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

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

Added some code to try and pass the axis config xml to the UI.

Also included a modified version of the beaker cache decorator that I've been using to cache the coastline map.

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()
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    def GetContext(self):
272        """
273        Return a WebMap Context document for a given set of layers.
274
275        """
276        # Parameters
277        layers = self.getOwsParam('layers', default=None)
278        format = self.getOwsParam('format', default='text/xml')
279
280        # Filter self.layers for selected layers
281        if layers is not None:
282            newLayerMap = {}
283            for layerName in layers.split(','):
284                try:
285                    newLayerMap[layerName] = self.layers[layerName]
286                except KeyError:
287                    raise InvalidParameterValue('Layer %s not found' % layerName,
288                                                'layers')
289                   
290            self.layers = newLayerMap
291
292        # Automatically select the first bbox/crs for the first layer
293        aLayer = self.layers.values()[0]
294        crs = aLayer.crss[0]
295        bb = aLayer.getBBox(crs)
296        c.bbox = BoundingBox(bb[:2], bb[2:], crs)
297
298        # Initialise as if doing GetCapabilities
299        ows_controller.initCapabilities()
300        self._loadCapabilities()
301
302        if format == 'text/xml':
303           
304            response.headers['Content-Type'] = format
305            t = ows_controller.templateLoader.load('wms_context_1_1_1.xml')
306            return t.generate(c=c).render()
307       
308        elif format == 'application/json':
309           
310            response.headers['Content-Type'] = format
311            t = ows_controller.templateLoader.load('wms_context_json.txt',
312                                                   cls=NewTextTemplate)
313            return t.generate(c=c).render()
314       
315        else:
316            raise InvalidParameterValue('Format %s not supported' % format)
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.