source: TI05-delivery/ows_framework/branches/ows_framework-refactor/ows_common/ows_common/pylons/wms_controller.py @ 3569

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/ows_framework/branches/ows_framework-refactor/ows_common/ows_common/pylons/wms_controller.py@3569
Revision 3569, 7.7 KB checked in by spascoe, 12 years ago (diff)

Slight changing to caching code. tweeking capabilities to test
compatibility with mapClient.

RevLine 
[3548]1"""
2WMS controller for OGC Web Services (OWS).
3
4@author: Stephen Pascoe
5"""
6
7from cStringIO import StringIO
8
9from matplotlib.cm import get_cmap
[3552]10from pylons import request, response, c
[3548]11
12import logging
13log = logging.getLogger(__name__)
14
[3552]15import Image
16
[3553]17from ows_common.model.wms import WmsDatasetSummary, Dimension
18from ows_common.model import PossibleValues, BoundingBox, Contents
[3554]19from ows_common.pylons import ows_controller
[3548]20from ows_common.exceptions import *
[3552]21from ows_common import bbox_util
[3548]22
23class WMSController(ows_controller.OWSController):
[3554]24    """
25    Subclass this controller in a pylons application and set the layerMapper
26    class attribute to implement a WMS.
27
28    @cvar layerMapper: an ows_common.service.wms_iface.ILayerMapper object.
29
30    """
31    layerMapper = None
[3552]32   
[3548]33    _pilImageFormats = {
34        'image/png': 'PNG',
35        'image/jpg': 'JPEG',
36        'image/gif': 'GIF',
37        'image/tiff': 'TIFF'
38        }
[3561]39    _layerSlabCache = {}
[3548]40
[3552]41    #-------------------------------------------------------------------------
42    # Attributes required by OWSController
43
44    service = 'WMS'
45    owsOperations = ows_controller.OWSController.owsOperations + ['GetMap', 'GetInfo']
46    validVersions = ['1.1.1']
47
48    #-------------------------------------------------------------------------
49
[3548]50    def __before__(self, **kwargs):
51        """
52        This default implementation of __before__() will pass all routes
53        arguments to the layer mapper to retrieve a list of layers for
54        this WMS.
55
56        It will be called automatically by pylons before each action method.
57
58        @todo: The layer mapper needs to come from somewhere.
59
60        """
[3554]61        self.layers = self.layerMapper.map(**kwargs)
[3548]62
63    #-------------------------------------------------------------------------
64    # Methods implementing stubs in OWSController
65
66    def _renderCapabilities(self, version, format):
67        t = ows_controller.templateLoader.load('wms_capabilities_1_1_1.xml')
68        return t.generate(c=c).render()
69
70    def _loadCapabilities(self):
71        """
72        @note: Assumes self.layers has already been created by __before__().
73
74        """
75        ows_controller.addOperation('GetMap', formats=self._pilImageFormats.keys())
76        ows_controller.addOperation('GetInfo')
77        log.debug('Loading capabilities contents')
[3552]78        c.capabilities.contents = Contents()
[3548]79        for layerName, layer in self.layers.items():
80            log.debug('Loading layer %s' % layerName)
81
82            # Get CRS/BBOX pairs
83            bboxObjs = []
84            for crs in layer.crss:
[3552]85                bbox = layer.getBBox(crs)
[3548]86                bboxObjs.append(BoundingBox(bbox[:2], bbox[2:], crs=crs))
87            # Get dimensions
88            dims = {}
89            for dimName, dim in layer.dimensions.items():
90                dims[dimName] = Dimension(valuesUnit=dim.units,
91                                          unitSymbol=dim.units,
92                                          possibleValues=
93                                            PossibleValues.fromAllowedValues(dim.extent))
94            # Create the ows_common object
95            ds = WmsDatasetSummary(identifier=layerName,
96                                   titles=[layer.title],
[3561]97                                   CRSs=layer.crss,
[3548]98                                   boundingBoxes=bboxObjs,
[3561]99                                   abstracts=[layer.abstract],
100                                   dimensions=dims)
[3548]101
102            c.capabilities.contents.datasetSummaries.append(ds)
103
104    #-------------------------------------------------------------------------
105    # OWS Operation methods
106   
107    def GetMap(self):
108
109        # Housekeeping
110        version = self.getOwsParam('version', default=self.validVersions[0])
111        if version not in self.validVersions:
112            raise InvalidParameterValue('Version %s not supported' % version,
113                                        'version')
114        styles = self.getOwsParam('styles', default='')
115        transparent = self.getOwsParam('transparent', default='FALSE')
116        bgcolor = self.getOwsParam('bgcolor', default='0xFFFFFF')
117
118        # Layer handling
119        layerName = self.getOwsParam('layers')
[3552]120        if ',' in layerName:
[3548]121            raise InvalidParameterValue(
122                'Multi-layer GetMap requests are not supported', 'layers')
123        try:
124            layerObj = self.layers[layerName]
125        except KeyError:
126            raise InvalidParameterValue('Layer %s not found' % layerName, 'layers')
127
[3561]128       
[3548]129        # Coordinate parameters
130        bbox = tuple(float(x) for x in self.getOwsParam('bbox').split(','))
131        width = int(self.getOwsParam('width'))
132        height = int(self.getOwsParam('height'))
133        srs = self.getOwsParam('srs')
134        if srs not in layerObj.crss:
135            raise InvalidParameterValue('Layer %s does not support SRS %s' % (layerName, srs))
136
137        # Get format
138        format = self.getOwsParam('format')
139        if format not in self._pilImageFormats:
140            raise InvalidParameterValue(
141                'Format %s not supported' % format, 'format')
142
143        # Dimension handling
144        dimValues = {}
[3552]145        for dimName, dim in layerObj.dimensions.items():
[3548]146            defaultValue = dim.extent[0]
[3561]147            dimValues[dimName] = self.getOwsParam(dimName, default=defaultValue)
148           
149        #---------------------------------------------------------------------
[3548]150        # The real work
151        #!TODO: Minimum and maximum values
[3561]152       
153        # Find the slab in the cache first
154        cacheKey = layerObj.getCacheKey(srs, dimValues)
[3569]155
156        slab = self._layerSlabCache.get(cacheKey)
157        if slab is None:
[3561]158            slab = layerObj.getSlab(srs, dimValues, dict(minValue=0, maxValue=100))
[3569]159            if cacheKey is not None:
160                self._layerSlabCache[cacheKey] = slab
[3548]161
[3552]162        # We must request a bbox within the layer's bbox.
163        lbbox = layerObj.getBBox(srs)
164        ibbox = bbox_util.intersection(bbox, lbbox)
165
166        log.debug('bbox = %s' % (bbox,))
167        log.debug('lbbox = %s' % (lbbox,))
168        log.debug('ibbox = %s' % (ibbox,))
169
[3561]170        # If bbox is not within layerObj.bbox then we need to calculate the
171        # pixel offset of the inner bbox, request the right width/height
172        # and paste the image into a blank background
[3552]173        if bbox == ibbox:
174            img = slab.getImage(bbox, width, height)
175            log.debug('slab image.size = %s' % (img.size,))
176        else:
177            sx, sy = bbox_util.relativeSize(ibbox, bbox)
178            log.debug('scaling: %s,%s' % (sx, sy))
179            img1 = slab.getImage(ibbox, int(width*sx), int(height*sy))
180            log.debug('inner image.size = %s' % (img1.size,))
181
182            img = Image.new('RGBA', (width, height))
183            pxOrigin = bbox_util.geoToPixel(ibbox[0], ibbox[3], bbox, width, height)
184            log.debug('pxOrigin = %s' % (pxOrigin,))
185            img.paste(img1, pxOrigin)
186           
187
[3548]188        # IE < 7 doesn't display the alpha layer right.  Here we sniff the
189        # user agent and remove the alpha layer if necessary.
190        try:
191            ua = request.headers['User-Agent']
192        except:
193            pass
194        else:
195            if 'MSIE' in ua and 'MSIE 7' not in ua:
196                img = img.convert('RGB')
197
198        buf = StringIO()
199        img.save(buf, self._pilImageFormats[format])
200
201        response.headers['Content-Type'] = format
202        response.write(buf.getvalue())
203
204        return request
205
206    def GetInfo(self):
207        from pprint import pformat
208        request.headers['Content-Type'] = 'text/ascii'
209        response.write('Some info about this service\n')
210        for layer in model.ukcip02.layers:
211            response.write('Layer %s: %s\n' % (layer, pformat(g.ukcip02_layers[layer].__dict__)))
212
213           
Note: See TracBrowser for help on using the repository browser.