source: cows/trunk/cows/service/imps/wms_layers.py @ 5399

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/cows/trunk/cows/service/imps/wms_layers.py@5399
Revision 5399, 12.3 KB checked in by domlowe, 12 years ago (diff)

Changing PIL imports in cows

Line 
1# BSD Licence
2# Copyright (c) 2009, Science & Technology Facilities Council (STFC)
3# All rights reserved.
4#
5# See the LICENSE file in the source distribution of this software for
6# the full license text.
7
8"""
9implementation of ILayerMapper, ILayer, IDimension, ILayerSlab interfaces, as defined in wms_iface.py
10this implementation was written to read station data.
11"""
12import os
13import cdms2 as cdms
14try: 
15    from PIL import Image
16except ImportError:
17    import Image
18from copy import copy
19from pointrenderer import PointRenderer
20from matplotlib import cm
21import genutil
22#!FIXME: modules in the service package shouldn't depend on pylons!
23from pylons import config  #config must have tmpfilebuffer and csmlstore values
24from layers import LayerParser
25from csml2kml.utils import wget
26from StationCollection import StationCollection
27from xml.etree.ElementTree import ElementTree, Element, SubElement, XML
28import urllib
29from matplotlib import dates
30
31from cows.service.wxs_iface import ILayerMapper
32from cows.service.wms_iface import IwmsLayer, IwmsDimension, IwmsLayerSlab
33
34import logging
35log = logging.getLogger(__name__)
36
37class WMSLayerMapper(ILayerMapper):
38    """
39    Map keyword arguments to a collection of layers.
40    Supports the retrieval of sets of layers according to arbitary
41    keyword/value pairs.
42    Implements  ILayerMapper
43   
44    """
45    def __init__(self):
46   
47        """
48        Lists the names of all layers available as listed in the configuration file specified by the 'layer_config' parameter in the development.ini file.
49
50        @return: A mapping of layer names to ILayer implementations.
51        @raise ValueError: If no layers are available for these keywords.
52        """
53       
54        filename=config['layer_config']
55        if not os.path.exists(filename):
56            raise Exception(str('Config File could not be found: %s')%filename)
57        log.debug('Initialising WMS layermapper from %s' % filename)
58       
59        #instantiate LayerParser class with the value of 'layer_config' as parameter to read layer infromation
60        layerparser = LayerParser(filename)
61
62        layermap={}
63        layers = layerparser.getLayers()
64        for feature in layers:
65            # read information necessary to create a StationLayer object
66            title, abstract, crss,formats, serverURL, icon, featureName, dataSet, bbox, dataSetURL =self._getInfo(feature)
67            # URL required to query the relevant WFS server to acquire a list of station for the current layer in the loop
68            geoServerUrl =serverURL+'/wfs?request=getfeature&service=wfs&version=1.1.0&typename='+featureName+'&maxfeatures=100'
69            log.debug('Geoserver URL: %s' % geoServerUrl)
70            geoServerResponse = wget(geoServerUrl)
71            stationCollection=StationCollection(geoServerResponse)
72            # specify the filepath for the static image to be used for representing each station in the GetMap image
73            icon =config['csml_config']+'/img/'+icon
74            log.debug('icon: %s' % icon)
75            #instantiate a StationLayer object and store that in the layermap dictionary with the name of the layer as the key
76            layermap[feature.findtext("Name")]=StationLayer(title,abstract, crss, stationCollection,bbox, formats, icon, dataSet, dataSetURL)
77        if len(layermap) > 0:
78            self.layermap = layermap
79        else:
80            raise ValueError
81
82   
83    def _getInfo(self, feature):
84        ''' given a Station feature, return info about the layer/feature
85        @return:    title, abstract, crss, formats, serverURL, icon, featureName,dataSet, bbox, dataSetURL  '''
86
87        try:
88            title=feature.findtext("Title")
89        except:
90            title=''
91        try:
92            abstract=feature.findtext("Abstract")
93        except:
94            abstract=title
95       
96        crs=feature.findtext("SRS")
97        crss=[crs]
98        log.debug('crss: %s' % crss)
99        formats = []
100       
101        #read supported getFeatureInfo formats from the "SupportedFormats" element
102        sFElem = feature.getchildren()[4]
103        for format in sFElem.getchildren():
104                formats.append(format.text)
105       
106        # read orignal bbox info
107        bboxElem = feature.getchildren()[5]
108        bbox=[int(bboxElem.getchildren()[0].text),int(bboxElem.getchildren()[1].text),int(bboxElem.getchildren()[2].text),int(bboxElem.getchildren()[3].text)]
109        #static image to be used for GetMap response
110        icon = feature.getchildren()[6].text
111        #read WFS server information
112        wfsElem = feature.getchildren()[7]
113        serverURL = wfsElem.getchildren()[0].text
114        featureName = wfsElem.getchildren()[1].text
115        dataSet = wfsElem.getchildren()[2].text
116        dataSetURL =  wfsElem.getchildren()[3].text
117        return title, abstract, crss, formats, serverURL, icon, featureName,dataSet, bbox, dataSetURL
118
119
120
121   
122    def map(self, **kwargs):
123        '''this function is called by the wms_controller class to acquire information about all available layers'''
124        return self.layermap
125
126
127class StationLayer(IwmsLayer):
128    """
129    representing a WMS layer for MIDAS/ECN Stations.    Implements ILayer
130
131    @ivar title: The layer title.  As seen in the Capabilities document.
132    @ivar abstract:  Abstract as seen in the Capabilities document.
133    @ivar crss: A sequence of SRS/CRSs supported by this layer.
134    @ivar stationCollection: A list of NPStation objects to be rendered as part of GetMap response
135    @ivar formats: A list of output formats supported by the layer in question for GetFeatureInfo response
136    @ivar icon: A static image to be used for representing each station in the stationCollection in the GetMap response
137    @ivar dataSet: Name of the dataset to be used to construct an URL for the GetFeatureInfo response
138    @ivar dataSetURL: Server address part of the URL to be returned as a part of the GetFeatureInfo response
139
140    """
141
142    def __init__(self, title, abstract, crss, stationCollection, wgs84BBox, formats, icon,dataSet, dataSetURL):       
143        self.title=title
144        self.abstract=abstract
145        self.crss=crss
146        self.legendSize=(300,60)
147        self.dimensions ={}
148        self.stationCollection = stationCollection
149        self.featureInfoFormats= formats
150        self.wgs84BBox=wgs84BBox
151        self.dataSet = dataSet
152        self.dataSetURL = dataSetURL
153        self.icon = icon
154    def getBBox(self, crs):
155        """
156        @return: A 4-tuple of the bounding box in the given coordinate
157            reference system.
158        """
159        bb= self.wgs84BBox
160        #convert 0 - 360 to -180, 180 as per common WMS convention
161        if abs(bb[2]-bb[0]) >= 359 and abs(bb[2]-bb[0]) < 361:
162            bb[0], bb[2]=-180, 180
163        return bb
164        #raise NotImplementedError
165       
166    def getSlab(self, crs, dimValues=None, renderOpts={}):
167        """
168        Creates a slab of the layer in a particular CRS and set of
169        dimensions.
170
171        @param crs: The coordinate reference system.
172        @param dimValues: A mapping of dimension names to dimension values
173            as specified in the IDimension.extent
174        @param renderOpts: A generic mapping object for passing rendering
175            options
176        @return: An object implementing ILayerSlab
177       
178        """
179        bbox=self.getBBox(crs)
180        return StationLayerSlab(self, crs, dimValues, renderOpts, bbox,self.icon, self.stationCollection)
181               
182    def getCacheKey(self, crs, dimValues=None, renderOpts={}):
183        """
184        Create a unique key for use in caching a slab.
185
186        The intention here is that most of the work should be done when
187        instantiating an ILayerSlab object.  These can be cached by the
188        server for future use.  The server will first call getCacheKey()
189        for the slab creation arguments and if the key is in it's cache
190        it will use a pre-generated ILayerSlab object.
191
192        """
193        return None
194        #raise NotImplementedError
195       
196    def getFeatureInfo(self, format, crs, point, dimValues):
197        """
198        Return a response string descibing the feature at a given
199        point in a given CRS.
200       
201        Currently only "html" is supported as output format
202
203        @param format: One of self.featureInfoFormats.  Defines which
204            format the response will be in.
205        @param crs: One of self.crss
206        @param point: a tuple (x, y) in the supplied crs of the point
207            being selected.
208        @param dimValues: A mapping of dimension names to dimansion values.
209        @return: A string containing the response.
210       
211        """
212        log.debug('Point: %s' % point)
213        nearestStation = self.stationCollection.getNearestStation(point[1], point[0])
214       
215        log.debug('Nearest station: %s' % nearestStation.desc)
216       
217       
218        responseURL = self.dataSetURL+'/list?dataset_id='+self.dataSet+'&station_name='+nearestStation.desc
219        # replace space characters in the URL with %20 to avoid any URL validation error on the client side
220        responseURL = responseURL.replace(' ', '%20')
221        log.debug('Response URL: %s' % responseURL)
222        #finally construct the response, in this case it is in HTML with the responseURL represented as a hyperlink in it
223        response = "Description: <em>"+nearestStation.desc+"</em><br /><a href='"+responseURL+"'>"+responseURL+"</a>"
224        return response
225
226    def getLegendImage(self, orientation='vertical', renderOpts={}):
227        """
228        Create an image of the colourbar for this layer.
229
230        @param orientation: Either 'vertical' or 'horizontal'
231        @return: A PIL image
232
233        """
234        width = self.legendSize[0]
235        height = self.legendSize[1]
236        # if width and height specified in the GetLegend request parameters
237        # then use them instead of the default values
238        if 'width' in renderOpts:
239                width = renderOpts['width']
240        if 'height' in renderOpts:
241                height = renderOpts['height']
242        renderer=PointRenderer()
243        legImage = Image.new('RGBA', (width, height), (0,0,0,0))
244        # legend for stations without any associated dataset
245        withoutData = Image.open(self.icon)
246        # legend for stations that contain datasets
247        withData= Image.open(self.icon+'1')
248        legImage.paste(withoutData, (0,0))
249        legImage.paste(renderer.txt2img(self.title+' without dataset', "helvB08.pil"),(30, 5) )
250        legImage.paste(withData, (0, 30))
251        legImage.paste(renderer.txt2img(self.title+' with dataset', "helvB08.pil"),(30, 35) )       
252        return legImage
253
254class StationDimension(IwmsDimension):
255    """
256    implements IDimension
257    @ivar units: The units string.
258    @ivar extent: Sequence of extent values.
259
260    """
261   
262    def __init__(self, domain, dimname, unit):
263        self.units = unit
264        self.extent = []
265        for val in domain[dimname]:
266            self.extent.append(str(val))
267               
268       
269       
270       
271class StationLayerSlab(IwmsLayerSlab):
272    """
273    Implements LayerSlab
274    Represents a particular horizontal slice of a WMS layer.
275
276    ILayerSlab objects are designed to be convenient to cache.
277    They should be pickleable to enable memcached support in the future.
278
279    @ivar layer: The source ILayer instance.
280    @ivar crs: The coordinate reference system.
281    @ivar dimValues: A mapping of dimension values of this view.
282    @ivar renderOpts: The renderOpts used to create this view.
283    @ivar bbox: The bounding box as a 4-tuple.
284    """
285   
286    def __init__(self,  layer, crs, dimValues, renderOpts, bbox, icon,stationCollection):
287        self.layer = layer
288        self.crs = crs
289        self.dimValues = dimValues
290        self.renderOpts=renderOpts
291        self.bbox=bbox
292        self.stationCollection = stationCollection
293        self.icon = icon
294    def getImage(self, bbox, width, height):
295        """
296        Create an image of a sub-bbox of a given size.
297
298        @ivar bbox: A bbox 4-tuple.
299        @ivar width: width in pixels.` 
300        @ivar height: height in pixels.
301        @return: A PIL Image object.
302
303        """
304        log.debug('BBOX: %s' % bbox)
305        stationsInBbox = self.stationCollection.getStationsInBBox(bbox[1],bbox[0],bbox[3], bbox[2])
306       
307       
308        cmap=eval(config['colourmap']) 
309        renderer=PointRenderer()         
310        return renderer.renderPoint(bbox, stationsInBbox, width, height, cmap,self.icon)
Note: See TracBrowser for help on using the repository browser.