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

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@3554
Revision 3554, 7.2 KB checked in by spascoe, 12 years ago (diff)

Changed the way we associate a layerMapper with WMSController.

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