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

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

Made the user agent sniffing more specific so it dosen't trigger for IE 8. Also fixed the logging for non wms controllers.

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