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

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

Modified the beaker cache decorator that comes with pylons so that it includes response content written to response.write(). Added this decorator to a number of controllers to try and improve the performacne.

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