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

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

Upgrade the server code to make consistent with newer pylons codebase - v0.9.6.1.
This mainly involves the replacement of the Response object, and associated methods,
with the inbuild, default response object. Typical changes include:

render_response -> render - with required settings on the response object
made before the render call against the response object

Response(...) -> response.write() - for content + response.headers.. = .. for headers info

  • also included the replacement of depricated functions, as highlighted by

the server logging

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