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

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

Minor tidying up.

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