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

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

Splitting wms_iface into wxs_iface and wms_iface interfaces.
Modifying implemenations of wms_iface to inherit from new interfaces.

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