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

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

Changed print statements to loggging calls where appropriate.

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