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, 11 years ago (diff)

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

Line 
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
10from pylons import request, response, c
11
12import logging
13log = logging.getLogger(__name__)
14
15import Image
16
17from ows_common.model.wms import WmsDatasetSummary, Dimension
18from ows_common.model import PossibleValues, BoundingBox, Contents
19from ows_common.pylons import ows_controller
20from ows_common.exceptions import *
21from ows_common import bbox_util
22
23class WMSController(ows_controller.OWSController):
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
32   
33    _pilImageFormats = {
34        'image/png': 'PNG',
35        'image/jpg': 'JPEG',
36        'image/gif': 'GIF',
37        'image/tiff': 'TIFF'
38        }
39    _layerSlabCache = {}
40
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
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        """
61        self.layers = self.layerMapper.map(**kwargs)
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')
78        c.capabilities.contents = Contents()
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:
85                bbox = layer.getBBox(crs)
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],
97                                   CRSs=layer.crss,
98                                   boundingBoxes=bboxObjs,
99                                   abstracts=[layer.abstract],
100                                   dimensions=dims)
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')
120        if ',' in layerName:
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
128       
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 = {}
145        for dimName, dim in layerObj.dimensions.items():
146            defaultValue = dim.extent[0]
147            dimValues[dimName] = self.getOwsParam(dimName, default=defaultValue)
148           
149        #---------------------------------------------------------------------
150        # The real work
151        #!TODO: Minimum and maximum values
152       
153        # Find the slab in the cache first
154        cacheKey = layerObj.getCacheKey(srs, dimValues)
155
156        slab = self._layerSlabCache.get(cacheKey)
157        if slab is None:
158            slab = layerObj.getSlab(srs, dimValues, dict(minValue=0, maxValue=100))
159            if cacheKey is not None:
160                self._layerSlabCache[cacheKey] = slab
161
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
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
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
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.