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

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

Version works with latest ukcip02_server. About to reorganise.

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