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

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

Added enforcement of interpretation of times as UTC times -- in CSML dataset all times should be UTC, and pylab uses local timezone as default. Therefore, to make this code deployable out of UK, we must enforce this.

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
16
17log = logging.getLogger(__name__)
18
19# [TODO] . Add correct labeling of time axis
20#        . Comments
21#        . Add security (especially checking of input parameters).
22#        . Add configurable URL for the data source web service (preferably explicitly on localhost)
23class CsmlgrapherController(BaseController):
24
25    def plot(self):
26        '''
27        Handler for plotting a specific PointSeries feature given by URL parameter "feature_id".
28        Retrieves the data from a web service, which should preferably run on the same machine.
29
30        Request parameters:    EITHER "feature_id" OR "station_id"
31        where:
32                               feature_id    Unique identifier of a requested CSML PointSeries feature.
33                               station_id    Unique identifier of a requested station.
34
35        Response:              If feature_id is supplied, an image/png of the time series for the CSML PointSeries feature;
36                               if station_id is supplied, nothing.
37        '''
38        def _plot_feature(feature):
39            '''
40            Plot a PointSeries feature using matplotlib, into a temporary file
41            '''
42            (lon, lat) = map(float, feature.location.CONTENT.split())
43            print 'Feature "%s" (%s) at: (%f,%f)' % (feature.id, feature.description.CONTENT, lon, lat)
44
45            def _enforce_UTC_timezone(datestr):
46                if datestr[-1] == 'Z':
47                    return datestr
48                else:
49                    return datestr + 'Z'
50            times=feature.value.pointSeriesDomain.timePositionList.CONTENT.split()
51            times = map(_enforce_UTC_timezone, times)
52               
53            print 'All times (%s of them): '% len(times)
54            print times
55           
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            print 'The span is ' + str(span) + ' days.'
60
61            elapsed_times = map(datestr2num, times)
62
63            (tickLocator, tickFormatter) = dates.date_ticker_factory(span, numticks=8)
64            print 'tickLocator:%s\ntickFormatter:%s' % (tickLocator, tickFormatter)
65
66            print 'All times (%s of them) as days since 01-01-0001 UTC: '% len(elapsed_times)
67            print elapsed_times
68
69            # We may need to do this for bodc data (i.e. not inline data)
70            # vals=feature.value.rangeSet.valueArray.valueComponent.insertedExtract.components.getDataFromChunks(0,19)
71
72            # We use this for inline data:
73            ql = feature.value.rangeSet.quantityList
74            vals = map(float, ql.CONTENT.split())
75            print 'Measurement values (%s of them):\n%s' % (len(vals), repr(vals))
76
77            # Determine the units of measurement
78            uom=ql.uom.title()
79            if ql.uom.islower():
80                uom = uom.lower()
81            if ql.uom.isupper():
82                uom = uom.upper()
83            print 'Units of measurement: %s' % uom
84
85            # Plot the figure
86            fig = figure()
87            plot_date(elapsed_times, vals, '-', xdate=True, lw=2)
88            ax = gca()
89            ml = ax.xaxis.get_major_locator()
90            ax.xaxis.set_major_locator(tickLocator)
91            ax.xaxis.set_major_formatter(tickFormatter)
92            fig.autofmt_xdate()
93            time_format = '%d-%b-%Y %H:%M:%S UTC'
94            xlabel('Times between %s and %s ' % (start_date.strftime(time_format), end_date.strftime(time_format)))
95            ylabel('Values [%s]' % uom)
96            title('"%s" (%s)' % (feature.id, feature.description.CONTENT))
97            grid(True)
98
99            print start_date
100            print end_date
101
102            # Save the figure to a temporary file
103            tempFile = NamedTemporaryFile(suffix='.png')
104            savefig(tempFile.name)
105           
106            return tempFile
107
108        def _set_response(tempFile):
109            '''
110            Set the WSGI response to an image, containing image read from a temporary location.
111            '''
112            img = Image.open(tempFile.name)
113            buf = StringIO()
114            img.save(buf, 'PNG')
115            response.content_type = 'image/png'
116            response.content = buf.getvalue()
117           
118        def _wget(url):
119            '''Auxiliary function, returns data read from an URL'''
120            content = None
121            try:
122                socket = urllib.urlopen(url)               # open a socket (actually a file-like object)
123                content = socket.read()                    # read the text in
124            finally:
125                try:
126                    socket.close()
127                except NameError:
128                    pass  # socket undefined -- no need to close
129            return content
130
131        #-------------------------------------------------------------------------------------------------------------------
132
133        # get feature ID from HTTP request
134        feature_id = None; station_id = None
135        try:
136            feature_id = request.params['feature_id']
137            gml_id = feature_id
138        except KeyError:
139            try:
140                station_id = request.params['station_id']
141                gml_id = station_id
142            except KeyError:
143                raise HTTPBadRequest('exactly one of the parameters "feature_id" and "station_id" must be supplied.')
144
145        # Get response from GeoServer and parse it into an ElementTree
146        geoServerURL = 'http://bond.badc.rl.ac.uk:8089/dummyGeoServer/GetStationCSMLFeatures?gml_id=' + gml_id
147        geoSrResponse = _wget(geoServerURL)
148        tree = ElementTree(XML(geoSrResponse))
149
150        if station_id:
151
152            # --- [NOTE] Keeping until ruled as obsolete ---
153
154            # Parse in the feature collection
155            fc = csml.parser.CSMLFeatureCollection()
156            fc.fromXML(tree.getroot())
157
158        else:
159           
160            # Parse in the feature directly
161            feature = csml.parser.PointSeriesFeature()
162            feature.fromXML(tree.getroot())
163
164        if not feature:
165            raise HTTPNotFound('Feature not found')
166           
167        # Try to plot the feature into a temporary file, and put the contents of that file into the WSGI response
168        try:
169            tempFile = _plot_feature(feature)              # plot the feature into a temporary file
170            _set_response(tempFile)                        # set WSGI response as an image containing the plot
171        finally:
172            try:
173                tempFile.close()
174            except NameError:
175                pass  # tempFile undefined -- no need to close
Note: See TracBrowser for help on using the repository browser.