source: qesdi/wms_ddc_vis/trunk/lib/wms_ddc_vis/controllers/coastwms.py @ 5535

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/qesdi/wms_ddc_vis/trunk/lib/wms_ddc_vis/controllers/coastwms.py@5535
Revision 5535, 12.1 KB checked in by pnorton, 10 years ago (diff)

Improved the coast WMS controller so that it now returns a getCapabilities response.

Line 
1import logging
2import Image
3import thread
4import re
5import simplejson as json
6
7from StringIO import StringIO
8from sets import Set
9from matplotlib.cm import get_cmap
10from pylons import request, response, c
11from routes import url_for
12from routes.util import GenerationException
13from genshi.template import NewTextTemplate
14from cows.pylons.wms_controller import WMSController
15from cows.model.wms import WmsDatasetSummary, Dimension, DataURL
16from cows.model import PossibleValues, WGS84BoundingBox, BoundingBox, Contents
17from cows.pylons import ows_controller
18from cows.exceptions import *
19from cows import bbox_util
20
21from geoplot.layer_drawer_coastline import LayerDrawerCoastlines
22
23from cows.model.wms import Style, LegendURL, FormattedURL, MetadataURL
24from cows.xml.iso19115_subset import OnlineResource
25
26log = logging.getLogger(__name__)
27
28class CoastwmsController(ows_controller.OWSController):
29
30    #layers = {}   
31    _pilImageFormats = {
32        'image/png': 'PNG',
33        'image/jpg': 'JPEG',
34        'image/gif': 'GIF',
35        'image/tiff': 'TIFF'
36        }
37   
38    _layerSlabCache = {}
39
40    #-------------------------------------------------------------------------
41    # Attributes required by OWSController
42
43    service = 'WMS'
44    owsOperations = (ows_controller.OWSController.owsOperations +
45        ['GetMap', 'GetContext', 'GetLegend', 'GetFeatureInfo', 'GetInfo', 'GetDisplayOptions'])
46   
47    validVersions = ['1.1.1', '1.3.0']
48
49
50
51    def _renderCapabilities(self, version, format):
52        if format == 'application/json':
53            t = ows_controller.templateLoader.load('wms_capabilities_json.txt',
54                                                   cls=NewTextTemplate)
55        elif version == '1.1.1':
56            t = ows_controller.templateLoader.load('wms_capabilities_1_1_1.xml')
57        elif version == '1.3.0':
58            t = ows_controller.templateLoader.load('wms_capabilities_1_3_0.xml')
59        else:
60            # We should never get here!  The framework should raise an exception before now.
61            raise RuntimeError("Version %s not supported" % version)
62       
63        return t.generate(c=c).render()
64
65    def _loadCapabilities(self):
66        """
67        @note: Assumes self.layers has already been created by __before__().
68
69        """
70        #!TODO: Add json format to GetCapabilities operation
71
72        ows_controller.addOperation('GetMap', formats=self._pilImageFormats.keys())
73        ows_controller.addOperation('GetContext', formats=['text/xml', 'application/json'])
74
75       
76        featureInfoFormats = Set()
77
78        log.debug('Loading capabilities contents')
79        c.capabilities.contents = Contents()
80       
81       
82        layers = ( ('c', 'coarse', 'coarse detail'), 
83                   ('l', 'low', 'low detail'), 
84                   ('i', 'intermediate', 'intermediate detail'),
85                   ('h', 'high', 'high detail'),
86                   ('f', 'full', 'full detail'),)
87       
88       
89        for layerName, title,  abstract in layers:
90            log.debug('LayerName: %s' % layerName)
91            log.debug('Loading layer %s' % layerName)
92
93            wgs84BBox = WGS84BoundingBox((-180,-90), (180,90))
94
95            # Get CRS/BBOX pairs
96            bboxObjs = []
97            for crs in ('EPSG:4326', 'CRS:84', 'WGS84'):
98                bbox = [-180,-90,180,90]
99                bboxObjs.append(BoundingBox(bbox[:2], bbox[2:], crs=crs))
100               
101
102               
103            #URL to WCS - uses named route 'wcsroute'
104            #TODO: Allow for a WCS blacklist to opt out of providing dataurls for certain datasets?
105            #TODO: How to make this more configurable - what if WCS is not coupled with WMS?
106#            try:
107#                version='1.0.0' #wcs version
108#                wcsbaseurl=url_for('wcsroute', fileoruri=c.fileoruri,qualified=True)+'?'
109#                dataURLs=[DataURL(format='WCS:CoverageDescription', onlineResource='%sService=WCS&Request=DescribeCoverage&Coverage=%s&Version=%s'%(wcsbaseurl, layerName, version))]
110#            except GenerationException:
111#                log.info("dataURLs not populated: could not generate WCS url with url_for('wcsroute', filedoruri=%s,qualified=True)"%c.fileoruri)
112#                dataURLs=[]
113           
114            onlineRes = OnlineResource(url_for(qualified=True, action='index') +\
115                                    "?request=GetDisplayOptions&layers=%s" % layerName)
116            metadataURL = MetadataURL(metadataType='display_options', format='application/json', onlineResource=onlineRes)
117       
118            # Create the cows object
119            ds = WmsDatasetSummary(identifier=layerName,
120                                   titles=[title],
121                                   CRSs=('EPSG:4326', 'CRS:84', 'WGS84'),
122                                   wgs84BoundingBoxes=[wgs84BBox],
123                                   boundingBoxes=bboxObjs,
124                                   abstracts=[abstract],
125                                   dimensions={},
126                                   queryable=False,
127                                   dataURLs=[],
128                                   styles=[],
129                                   metadataURLs=[metadataURL])
130
131            # Stuff that should go in the capabilities tree eventually
132            ds.legendSize = (630,80)
133            ds.legendFormats = ['image/png']
134
135            c.capabilities.contents.datasetSummaries.append(ds)
136
137        # Add this operation here after we have found all formats
138        ows_controller.addOperation('GetFeatureInfo',
139                                    formats = list(featureInfoFormats))
140
141
142    _escapedDimNames = ['width', 'height', 'version', 'request',
143                        'layers', 'styles', 'crs', 'srs', 'bbox',
144                        'format', 'transparent', 'bgcolor',
145                        'exceptions']
146
147
148    def _mapDimToParam(self, dimName):
149        """
150        Dimension names might clash with WMS parameter names, making
151        them inaccessible in WMS requests.  This method maps a
152        dimension name to a parameter name that appears in the
153        capabilities document and WMS requests.
154
155        """
156        if dimName.lower() in self._escapedDimNames:
157            return dimName+'_dim'
158        else:
159            return dimName
160       
161   
162    def GetMap(self):
163
164        # Housekeeping
165        version = self.getOwsParam('version', default=self.validVersions[0])
166        if version not in self.validVersions:
167            raise InvalidParameterValue('Version %s not supported' % version,
168                                        'version')
169        styles = self.getOwsParam('styles', default='')
170        transparent = self.getOwsParam('transparent', default='FALSE')
171        bgcolor = self.getOwsParam('bgcolor', default='0xFFFFFF')
172
173        # Layer handling
174        layerName = self.getOwsParam('layers')
175       
176        transparent = self.getOwsParam('transparent').lower() == 'true'
177       
178       
179        # Coordinate parameters
180        bbox = tuple(float(x) for x in self.getOwsParam('bbox').split(','))
181        width = int(self.getOwsParam('width'))
182        height = int(self.getOwsParam('height'))
183
184        if version == '1.1.1':
185            srs = self.getOwsParam('srs')
186        else:
187            srs = self.getOwsParam('crs')
188
189       # Get format
190        format = self.getOwsParam('format')
191        if format not in self._pilImageFormats:
192            raise InvalidParameterValue(
193                'Format %s not supported' % format, 'format')
194
195        ldg = LayerDrawerCoastlines(transparent=transparent,
196                                    resolution=layerName)
197
198        xLimits = (bbox[0], bbox[2])
199        yLimits = (bbox[1], bbox[3])       
200        finalImg = ldg.makeImage(xLimits, yLimits, width, height)     
201     
202       
203        # IE < 7 doesn't display the alpha layer right.  Here we sniff the
204        # user agent and remove the alpha layer if necessary.
205        try:
206            ua = request.headers['User-Agent']
207        except:
208            pass
209        else:
210            if 'MSIE' in ua and 'MSIE 7' not in ua:
211                finalImg = finalImg.convert('RGB')
212
213        buf = StringIO()
214        finalImg.save(buf, self._pilImageFormats[format])
215
216        response.headers['Content-Type'] = format
217        response.write(buf.getvalue())
218
219
220    def GetContext(self):
221        """
222        Return a WebMap Context document for a given set of layers.
223
224        """
225        # Parameters
226        layers = self.getOwsParam('layers', default=None)
227        format = self.getOwsParam('format', default='text/xml')
228
229        # Filter self.layers for selected layers
230        if layers is not None:
231            newLayerMap = {}
232            for layerName in layers.split(','):
233                try:
234                    newLayerMap[layerName] = self.layers[layerName]
235                except KeyError:
236                    raise InvalidParameterValue('Layer %s not found' % layerName,
237                                                'layers')
238                   
239            self.layers = newLayerMap
240
241        # Automatically select the first bbox/crs for the first layer
242        aLayer = self.layers.values()[0]
243        crs = aLayer.crss[0]
244        bb = aLayer.getBBox(crs)
245        c.bbox = BoundingBox(bb[:2], bb[2:], crs)
246
247        # Initialise as if doing GetCapabilities
248        ows_controller.initCapabilities()
249        self._loadCapabilities()
250
251        if format == 'text/xml':
252           
253            response.headers['Content-Type'] = format
254            t = ows_controller.templateLoader.load('wms_context_1_1_1.xml')
255            return t.generate(c=c).render()
256       
257        elif format == 'application/json':
258           
259            response.headers['Content-Type'] = format
260            t = ows_controller.templateLoader.load('wms_context_json.txt',
261                                                   cls=NewTextTemplate)
262            return t.generate(c=c).render()
263       
264        else:
265            raise InvalidParameterValue('Format %s not supported' % format)
266
267
268    def GetDisplayOptions(self):
269           
270        displayOptions = {
271#          "common": [
272#                        {
273#                            "type":"select",
274#                            "name":"cmap",
275#                            "options":["bone","jet", "copper", "gray", "winter"],
276#                            "title":"Colour Scheme",
277#                            "defaultVal":"jet",
278#                        },
279#                        {
280#                            "type":"value",
281#                            "name":"cmap_min",
282#                            "title":"Legend Min",
283#                            "defaultVal":None,
284#                        },
285#                        {
286#                            "type":"value",
287#                            "name":"cmap_max",
288#                            "title":"Legend Max",
289#                            "defaultVal":None,
290#                        },         
291#                     ],
292#          "grid": [
293#                        {
294#                            "type":"bool",
295#                            "name":"show_grid_lines",
296#                            "title":"Draw Grid Boxes",
297#                            "defaultVal":"False",
298#                        },   
299#                  ],
300#          "contour":[
301#                        {
302#                            "type":"value",
303#                            "name":"num_contour_lines",
304#                            "title":"Number of Contour Lines",
305#                            "defaultVal":10,
306#                        },
307#                        {
308#                            "type":"select",
309#                            "name":"contour_font_size",
310#                            "title":"Contour Label Size",
311#                            "options":["small","medium", "large",],
312#                            "defaultVal":"medium",
313#                        },   
314#                        {
315#                            "type":"value",
316#                            "name":"contour_label_interval",
317#                            "title":"Interval Between Labels",
318#                            "defaultVal":1,
319#                        },             
320#                    ],
321        }
322       
323       
324        request.headers['Content-Type'] = 'application/json'
325        response.write( json.dumps(displayOptions) )
326
Note: See TracBrowser for help on using the repository browser.