source: TI05-delivery/ows_framework/trunk/ows_server/ows_server/controllers/csml_wms.py @ 2989

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/ows_framework/trunk/ows_server/ows_server/controllers/csml_wms.py@2989
Revision 2989, 7.8 KB checked in by spascoe, 12 years ago (diff)

Implemented colourbar.

Line 
1# Copyright (C) 2007 STFC & NERC (Science and Technology Facilities Council).
2# This software may be distributed under the terms of the
3# Q Public License, version 1.0 or later.
4# http://ndg.nerc.ac.uk/public_docs/QPublic_license.txt
5"""
6Example WMS controller driven by CSML.
7
8@author: Stephen Pascoe
9
10"""
11from ows_server.lib.base import *
12from ows_server.lib.decorators import *
13from ows_server.lib import grid_util, render
14from ows_server.lib.csml_cache import csmlCache, extractCache
15import ows_server.lib.validators as V
16from ows_server.lib.ndgInterface import interface
17
18try: #python 2.5
19    from xml.etree import ElementTree as ET
20except ImportError:
21    try:
22        # if you've installed it yourself it comes this way
23        import ElementTree as ET
24    except ImportError:
25        # if you've egged it this is the way it comes
26        from elementtree import ElementTree as ET
27
28from ows_common import exceptions as OWS_E
29from ows_common.wms import *
30from ows_common.common import BoundingBox
31from ows_common.domain import ValuesUnit, PossibleValues
32
33import cdms
34import os, sys
35from cStringIO import StringIO
36import MA
37
38class CsmlWmsController(OwsController):
39
40    _ows_parameters = {
41        'Format': make_domain(['text/xml']),
42        'ExceptionFormat': make_domain(['text/xml']),
43        }
44
45    #_ows_constraints = {
46        # MaximumLayerLevels
47        # MaximumWidth
48        # MaximumWidth
49    #    }
50
51
52    def _iterDimensions(self, feature):
53        """
54        Retrieve the non-geospatial dimensions of a feature.
55
56        @return: generator of dimension names
57
58        """
59        # Waiting for this feature to be implemented
60        #lat = feature.getLatitude()
61        #lon = feature.getLongitude()
62        lat = 'latitude'; lon = 'longitude'
63       
64        for axis_name in feature.getDomain():
65            if axis_name not in [lat, lon]:
66                yield axis_name
67
68    def _loadFeatureDimensions(self, feature):
69        dims = {}
70        domain = feature.getDomain()
71        for axis_name in self._iterDimensions(feature):
72            axis = domain[axis_name]
73            dims[axis_name] = Dimension(possibleValues=PossibleValues.fromAllowedValues(axis),
74                                        #!TODO: see ticket:770 for how to populate this
75                                        valuesUnit=ValuesUnit(uoms=[''],
76                                                              referenceSystems=['']))
77        return dims
78
79    def _loadFeatureSummary(self, feature):
80        dims = self._loadFeatureDimensions(feature)
81       
82        return WmsDatasetSummary(identifier=feature.id,
83                                 titles=[feature.description.CONTENT],
84                                 boundingBoxes=[BoundingBox([-180,-90], [180,90],
85                                                            crs='CRS:84')],
86                                 dimensions=dims,               
87                                 )
88
89    def _loadCapabilities(self):
90        """
91        Overriding subclass to add layer capabilities
92
93        """
94        # Get default capabilities from superclass
95        sm = super(CsmlWmsController, self)._loadCapabilities()
96
97        # For WMS 1.3.0 compatibility we need a single root DatasetSummary
98        ds = WmsDatasetSummary(titles=['Root Dataset'], datasetSummaries=[],
99                               CRSs=['CRS:84'])
100        # Add a DatasetSummary for each feature       
101        for f_n in c.dataset.getFeatureList():
102            feature_ds = self._loadFeatureSummary(c.dataset.getFeature(f_n))
103            ds.datasetSummaries.append(feature_ds)
104
105        sm.contents = Contents(datasetSummaries=[ds])
106        return sm
107
108   
109    @operation
110    @parameter('Format', possibleValues=['text/xml'])
111    @parameter('Service', possibleValues=['WMS'], required=True)
112    @parameter('Version', possibleValues=['1.3.0'])
113    def GetCapabilities(self, uri, service=None, version=None):
114        """
115        @note: format and updatesequence parameters are not supported
116            by this WMS.
117
118        """
119        # Retrieve dataset and selected feature
120        rstatus, c.dataset = interface.GetParsedCSML(uri)
121
122        if not rstatus:
123            c.xml='<div class="error">%s</div>' % dataset
124            resp = render_response('error')
125            return resp
126
127        return self._renderCapabilities('ows/wms_capabilities')
128
129
130    @operation
131    @parameter('Layers', required=True, validator=V.single_layer)
132    @parameter('Width', required=True, validator=V.integer('Width'))
133    def GetColourbar(self, uri, layers, width):
134        feature = self._getFeature(uri, layers)
135        sel = self._makeSelector(feature)
136       
137        # Get the entire lat/lon slice from the extractCache
138        var = extractCache[feature, sel]
139
140        # Deduce min and max
141        #!TODO: How do we get the min/max?  This method produces different min/max for each time selection.
142        varmin = MA.minimum(var, None)
143        varmax = MA.maximum(var, None)
144        #varmin=0; varmax=100
145
146        img = render.render_colourbar(width, varmin, varmax)
147        return self._img2Response(img)
148       
149    @operation
150    @parameter('Version', possibleValues=['1.3.0'], required=True)
151    @parameter('Layers', required=True, validator=V.single_layer)
152    @parameter('Styles', required=True)
153    @parameter('CRS', possibleValues=['CRS:84'], required=True)
154    @parameter('Bbox', required=True, validator=V.bbox_2d)
155    @parameter('Width', required=True, validator=V.integer('Width'))
156    @parameter('Height', required=True, validator=V.integer('Height'))
157    @parameter('Format', required=True)
158    @parameter('Transparent', validator=V.boolean('Transparent'))
159    @parameter('Bgcolor')
160    @parameter('Exceptions')
161    # Dimension parameters Time, Elevation, etc. are handled separately
162    def GetMap(self, uri, version, layers, styles, crs, bbox, width, height, format,
163               transparent=False, bgcolor=None, exceptions=None):
164
165        feature = self._getFeature(uri, layers)
166        sel = self._makeSelector(feature)
167       
168        # Get the entire lat/lon slice from the extractCache
169        var = extractCache[feature, sel]
170
171        # Deduce min and max
172        #!TODO: How do we get the min/max?  This method produces different min/max for each time selection.
173        varmin = MA.minimum(var, None)
174        varmax = MA.maximum(var, None)
175        #varmin=0; varmax=100
176
177        # Do geographical subsetting in cdms
178        var = var(latitude=(bbox[1], bbox[3]), longitude=(bbox[0], bbox[2]), squeeze=1)
179
180        # Render variable to a PIL image
181        img = render.render_variable(var, bbox, width, height, varmin, varmax)
182
183        # Serialise it to PNG
184        return self._img2Response(img)
185
186    def _makeSelector(self, feature):
187        # Parse dimensions.
188        sel = {}
189        for dim in self._iterDimensions(feature):
190            # For the moment hard-code time in.  We don't support any other dimension yet.
191            if dim.lower() != 'time':
192                raise ValueError('Only lat, lon and time are supported domain axes')
193           
194            try:
195                sel[dim] = self.ows_params[dim]
196            except KeyError:
197                raise OWS_E.MissingParameterValue('%s dimension not specified' % dim, dim)
198
199        return sel
200
201    def _getFeature(self, uri, layers):
202        # Retrieve dataset and selected feature
203        rstatus, dataset = interface.GetParsedCSML(uri)
204        if not rstatus:
205            c.xml='<div class="error">%s</div>' % dataset
206            resp = render_response('error')
207            return resp
208           
209        # Retrieve feature from the layers parameter
210        feature = dataset.getFeature(layers)
211        if feature is None:
212            raise OWS_E.InvalidParameterValue('Layer not found', 'layers')
213
214        return feature
215
216    def _img2Response(self, img):
217        buf = StringIO()
218        img.save(buf, 'PNG')
219
220        return Response(content=buf.getvalue(), mimetype='image/png')
Note: See TracBrowser for help on using the repository browser.