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

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

Add functionality to produce XML files for use with the Con Terra service.

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            resp = render_response('error')
114            return resp
115
116        return self._renderCapabilities('ows/wms_capabilities')
117
118
119    @operation
120    @parameter('Layers', required=True, validator=V.single_layer)
121    @parameter('Width', required=True, validator=V.integer('Width'))
122    def GetColourbar(self, uri, layers, width):
123        feature = self._getFeature(uri, layers)
124        sel = self._makeSelector(feature)
125       
126        # Get the entire lat/lon slice from the extractCache
127        var = extractCache[feature, sel]
128
129        # Deduce min and max
130        #!TODO: How do we get the min/max?  This method produces different min/max for each time selection.
131        varmin = MA.minimum(var, None)
132        varmax = MA.maximum(var, None)
133        #varmin=0; varmax=100
134
135        img = render.render_colourbar(width, varmin, varmax)
136        return self._img2Response(img)
137       
138    @operation
139    @parameter('Version', possibleValues=['1.3.0'], required=True)
140    @parameter('Layers', required=True, validator=V.single_layer)
141    @parameter('Styles', required=True)
142    @parameter('CRS', possibleValues=['CRS:84'], required=True)
143    @parameter('Bbox', required=True, validator=V.bbox_2d)
144    @parameter('Width', required=True, validator=V.integer('Width'))
145    @parameter('Height', required=True, validator=V.integer('Height'))
146    @parameter('Format', required=True)
147    @parameter('Transparent', validator=V.boolean('Transparent'))
148    @parameter('Bgcolor')
149    @parameter('Exceptions')
150    # Dimension parameters Time, Elevation, etc. are handled separately
151    def GetMap(self, uri, version, layers, styles, crs, bbox, width, height, format,
152               transparent=False, bgcolor=None, exceptions=None):
153
154        feature = self._getFeature(uri, layers)
155        sel = self._makeSelector(feature)
156       
157        # Get the entire lat/lon slice from the extractCache
158        var = extractCache[feature, sel]
159
160        # Deduce min and max
161        #!TODO: How do we get the min/max?  This method produces different min/max for each time selection.
162        varmin = MA.minimum(var, None)
163        varmax = MA.maximum(var, None)
164        #varmin=0; varmax=100
165
166        # Do geographical subsetting in cdms
167        var = var(latitude=(bbox[1], bbox[3]), longitude=(bbox[0], bbox[2]), squeeze=1)
168
169        # Render variable to a PIL image
170        img = render.render_variable(var, bbox, width, height, varmin, varmax)
171
172        # Serialise it to PNG
173        return self._img2Response(img)
174
175    def _makeSelector(self, feature):
176        # Parse dimensions.
177        sel = {}
178        for dim in self._iterDimensions(feature):
179            # For the moment hard-code time in.  We don't support any other dimension yet.
180            if dim.lower() != 'time':
181                raise ValueError('Only lat, lon and time are supported domain axes')
182           
183            try:
184                sel[dim] = self.ows_params[dim]
185            except KeyError:
186                raise OWS_E.MissingParameterValue('%s dimension not specified' % dim, dim)
187
188        return sel
189
190    def _getFeature(self, uri, layers):
191        # Retrieve dataset and selected feature
192        rstatus, dataset = interface.GetParsedCSML(uri)
193        if not rstatus:
194            c.xml='<div class="error">%s</div>' % dataset
195            resp = render_response('error')
196            return resp
197           
198        # Retrieve feature from the layers parameter
199        feature = dataset.getFeature(layers)
200        if feature is None:
201            raise OWS_E.InvalidParameterValue('Layer not found', 'layers')
202
203        return feature
204
205    def _img2Response(self, img):
206        buf = StringIO()
207        img.save(buf, 'PNG')
208
209        return Response(content=buf.getvalue(), mimetype='image/png')
Note: See TracBrowser for help on using the repository browser.