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

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

Added the land colour and the coastline width options to the coast wms controller.

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