source: DPPP/kml/csml2kml/python/pylonsstack/pylonsstack/controllers/csmlGrapher.py @ 3567

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/DPPP/kml/csml2kml/python/pylonsstack/pylonsstack/controllers/csmlGrapher.py@3567
Revision 3567, 8.6 KB checked in by mkochan, 12 years ago (diff)

Changes to CSML grapher (unfinished)

Line 
1# Pylons-specific imports
2import logging
3from pylonsstack.lib.base import *
4
5# Other imports
6import csml
7import Image
8import pylab
9from pylab import *
10from datetime import datetime, timedelta
11import re
12import urllib
13from cStringIO import StringIO
14from tempfile import NamedTemporaryFile
15from cElementTree import Element, ElementTree, XML
16from utils import wget
17
18log = logging.getLogger(__name__)
19
20# [TODO] . Add security (especially checking of input parameters).
21#        . Add configurable URL for the data source web service (preferably explicitly on localhost)
22class CsmlgrapherController(BaseController):
23
24    def plot(self):
25        '''
26        Handler for plotting a specific PointSeries feature given by URL parameter "feature_id".
27        Retrieves the data from a web service, which should preferably run on the same machine.
28
29        Request parameters:    EITHER "feature_id" OR "station_id"
30        where:
31                               feature_id    Unique identifier of a requested CSML PointSeries feature.
32                               station_id    Unique identifier of a requested station.
33
34        Response:              If feature_id is supplied, an image/png of the time series for the CSML PointSeries feature;
35                               if station_id is supplied, nothing.
36        '''
37        def _plot_feature(feature):
38            '''
39            Plot a PointSeries feature using matplotlib, into a temporary file
40            '''
41           
42            # Make sure that time is considered to be UTC time. This is because all CSML data is supposed to be in UTC,
43            # but actually pylab is "time zone aware" and will interpret the time as local time zone time.
44            # Not quite sure if this would be a problem, but rather enforce it is UTC.
45            def _enforce_UTC_timezone(datestr):
46                if datestr[-1] == 'Z':
47                    return datestr
48                else:
49                    return datestr + 'Z'
50           
51            # Isolate time points   
52            times=feature.value.pointSeriesDomain.timePositionList.CONTENT.split()
53            times = map(_enforce_UTC_timezone, times)
54           
55            # Note the start and end date (they are actually date+time), and time span between them
56            start_date = dates.dateutil.parser.parse(times[0])
57            end_date = dates.dateutil.parser.parse(times[-1])
58            span = datestr2num(times[-1]) - datestr2num(times[0])
59
60            # Make a list of times as float numbers being days passed since start of epoch (here 01-01-0001)
61            elapsed_times = map(datestr2num, times)
62
63            # Generate tick locator and formatter; these determine the time axis: locator determines tick positions
64            # and formatter format of time
65            (tickLocator, tickFormatter) = dates.date_ticker_factory(span, numticks=8)
66
67            # We may need to do this for bodc data (i.e. not inline data)
68            # vals=feature.value.rangeSet.valueArray.valueComponent.insertedExtract.components.getDataFromChunks(0,19)
69
70            # Get values of the measured quantity
71            ql = feature.value.rangeSet.quantityList
72            vals = map(float, ql.CONTENT.split())
73
74            # Determine the units of measurement
75            uom=ql.uom.title()
76            if ql.uom.islower():
77                uom = uom.lower()
78            if ql.uom.isupper():
79                uom = uom.upper()
80
81            # Prepare the plot the figure (actual plotting actions are postponed until save)
82            fig = figure()
83            plot_date(elapsed_times, vals, '-o', xdate=True, lw=2)
84            ax = gca()
85            ax.xaxis.set_major_locator(tickLocator)
86            ax.xaxis.set_major_formatter(tickFormatter)
87            fig.autofmt_xdate()  # show times without overlaps
88            time_format = '%d-%b-%Y %H:%M:%S UTC'
89            xlabel('Times between %s and %s ' % (start_date.strftime(time_format), end_date.strftime(time_format)))
90            ylabel('Values [%s]' % uom)
91            title('"%s" (%s)' % (feature.id, feature.description.CONTENT))
92            grid(True)
93
94            # Save the figure to a temporary file
95            tempFile = NamedTemporaryFile(suffix='.png')
96            savefig(tempFile.name)
97           
98            return tempFile
99
100        def _set_response(tempFile):
101            '''
102            Set the WSGI response to an image, containing image read from a temporary location.
103            '''
104            img = Image.open(tempFile.name)
105            buf = StringIO()
106            img.save(buf, 'PNG')
107            response.content_type = 'image/png'
108            response.content = buf.getvalue()
109           
110        #----------------------
111
112        # Get parameters from the request object
113        try:
114            feature_id = request.params['feature_id']
115            station_name = request.params['station_name']
116        except KeyError:
117            raise HTTPBadRequest('Parameters "feature_id" and "station_id" must be supplied.')
118
119        # Get response from GeoServer and parse it into an ElementTree
120        lowerTimeBoundary = '1-JAN-2003'  # hard-coded for now
121        upperTimeBoundary = '1-FEB-2004'  # hard-coded for now
122        geoServerRequestUrl = 'http://130.246.76.98:8084/geoserver/wfs?request=getfeature&service=wfs&version=1.1.0&typename=%20csml:PointSeriesFeature&filter=<ogc:Filter%20xmlns:ogc="http://www.opengis.net/ogc"%20xmlns:gml="http://www.opengis.net/gml"><ogc:And><ogc:PropertyIsEqualTo><ogc:PropertyName>gml:description</ogc:PropertyName><ogc:Literal>'+ station_name +'</ogc:Literal></ogc:PropertyIsEqualTo><ogc:PropertyIsEqualTo><ogc:PropertyName>csml:parameter/swe:Phenomenon/gml:name</ogc:PropertyName><ogc:Literal>'+ feature_id +'</ogc:Literal></ogc:PropertyIsEqualTo><ogc:PropertyIsBetween><ogc:PropertyName>csml:value/csml:PointSeriesCoverage/csml:pointSeriesDomain/csml:TimeSeries/csml:timePositionList</ogc:PropertyName><ogc:LowerBoundary><ogc:Literal>'+ lowerTimeBoundary +'</ogc:Literal></ogc:LowerBoundary><ogc:UpperBoundary><ogc:Literal>'+ upperTimeBoundary +'</ogc:Literal></ogc:UpperBoundary></ogc:PropertyIsBetween></ogc:And></ogc:Filter>'
123
124        # [NOTE] Need to experiment with encoding for this request -- this one does not work. Maybe
125        #        return back to using URL escape characters, and play around with it?
126        #geoServerRequestUrl = 'http://130.246.76.98:8084/geoserver/wfs?request=getfeature&service=wfs&version=1.1.0&typename=%20csml:PointSeriesFeature&filter=%3Cogc:Filter%20xmlns:ogc=%22http://www.opengis.net/ogc%22%20xmlns:gml=%22http://www.opengis.net/gml%22%3E%3Cogc:And%3E%3Cogc:PropertyIsEqualTo%3E%3Cogc:PropertyName%3Egml:description%3C/ogc:PropertyName%3E%3Cogc:Literal%3E' + station_name + '%3C/ogc:Literal%3E%3C/ogc:PropertyIsEqualTo%3E%3Cogc:PropertyIsEqualTo%3E%3Cogc:PropertyName%3Ecsml:parameter/swe:Phenomenon/gml:name%3C/ogc:PropertyName%3E%3Cogc:Literal%3E' + feature_id + '%3C/ogc:Literal%3E%3C/ogc:PropertyIsEqualTo%3E%3Cogc:PropertyIsBetween%3E%3Cogc:PropertyName%3Ecsml:value/csml:PointSeriesCoverage/csml:pointSeriesDomain/csml:TimeSeries/csml:timePositionList%3C/ogc:PropertyName%3E%3Cogc:LowerBoundary%3E%3Cogc:Literal%3E' + lowerTimeBoundary + '%3C/ogc:Literal%3E%3C/ogc:LowerBoundary%3E%3Cogc:UpperBoundary%3E%3Cogc:Literal%3E' + upperTimeBoundary + '%3C/ogc:Literal%3E%3C/ogc:UpperBoundary%3E%3C/ogc:PropertyIsBetween%3E%3C/ogc:And%3E%3C/ogc:Filter%3E'
127
128        # Get the GeoServer response and parse it in as a CSML PointSeriesFeature
129        geoServerResponse = wget(geoServerRequestUrl)
130        fc = csml.parser.CSMLFeatureCollection()
131        fc.fromXML( XML(geoServerResponse) )
132        raise ValueError('Oops')
133
134        # If there was a parsing error, raise an exception
135        if not feature:
136            raise HTTPNotFound('Feature not found')
137           
138        # Try to plot the feature into a temporary file, and put the contents of that file into the response
139        try:
140            tempFile = _plot_feature(feature)              # plot the feature into a temporary file
141            _set_response(tempFile)                        # set the response as an image containing the plot
142        finally:
143            try:
144                tempFile.close()
145            except NameError:
146                pass  # tempFile undefined -- no need to close
147
148    def list(self):
149        '''Handler for generating a list of CSML features per station, with dynamic links to the plot service.'''
150
151        # Get parameters from the request object
152        try:
153            feature_id = request.params['feature_id']
154            station_name = request.params['station_name']
155        except KeyError:
156            raise HTTPBadRequest('Parameters "feature_id" and "station_id" must be supplied.')
157
158        # [NOTE] Should use: fc = csml.parser.CSMLFeatureCollection()
Note: See TracBrowser for help on using the repository browser.