source: cows/trunk/cows/service/imps/data_reader_geoplot_backend/data_reader_geoplot_wms_layer.py @ 6102

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/cows/trunk/cows/service/imps/data_reader_geoplot_backend/data_reader_geoplot_wms_layer.py@6102
Revision 6102, 13.6 KB checked in by pnorton, 11 years ago (diff)

Removed the old modis data reader class. Also modified COWS to pass the style parameter to the GetLegendImage? methods on the layer objects.

Line 
1'''
2Created on 9 Jun 2009
3
4@author: pnorton
5'''
6
7import logging
8
9import time
10import os
11import numpy
12
13from cows.service.imps.csmlbackend.config import config
14from cows.service.wms_iface import IwmsLayer
15
16
17log = logging.getLogger(__name__)
18
19import geoplot.colour_bar
20from geoplot.grid_builder_lat_lon import GridBuilderLatLon
21
22from cows.model.wms import Style, LegendURL, FormattedURL, MetadataURL
23from cows.xml.iso19115_subset import OnlineResource
24
25from routes import url_for
26
27from cows.service.imps.data_reader_geoplot_backend.geoplot_slabs.geoplot_slab_contour import GeoplotSlabContour
28from cows.service.imps.data_reader_geoplot_backend.geoplot_slabs.geoplot_slab_grid import GeoplotSlabGrid
29from cows.service.imps.data_reader_geoplot_backend.slab_options_parser import SlabOptionsParser
30
31
32class DRGeoplotWmsLayer(IwmsLayer):
33
34    slab_classes = [GeoplotSlabGrid, GeoplotSlabContour]
35    default_slab_class = GeoplotSlabGrid
36   
37    EnableDisplayOptions = False
38    EnableXMLAxisConfig = False
39   
40    def __init__(self, name, title, abstract, dimensions, units, crss, 
41                 boundingBox, dataReader):
42        self.featureInfoFormats=None #NotImplemented
43        self.title=title
44        self.abstract=abstract
45        self.dimensions=dimensions
46        self.units=units
47        self.crss=crss
48        self.legendSize=(630,120)
49       
50        # dummy values, will need to be set when the data is opened
51        self._minval = None
52        self._maxval = None 
53       
54        self.name = name
55        self.dataReader = dataReader
56       
57        self.styles = self._buildStyles()
58        self.metadataURLs = self._buildMetadataURL()
59       
60        bb = boundingBox
61           
62        #convert 0 - 360 to -180, 180 as per common WMS convention
63        if abs(bb[2]-bb[0]) >= 359 and abs(bb[2]-bb[0]) < 361:
64            bb[0], bb[2]=-180, 180
65           
66        self.wgs84BBox = bb
67        self.featureInfoFormats = ['text/html']
68       
69        try:
70            self.wgs84BBox = self.getBBox('EPSG:4326')
71        except:
72            raise ValueError("Layer must provide a bounding box in EPSG:4326 "
73                             "coordinates for compatibility with WMS-1.3.0")
74           
75        self.featureinfofilecache={} #used for caching netcdf file in getFeatureInfo
76   
77    def getBBox(self, crs):
78        """
79        @return: A 4-typle of the bounding box in the given coordinate
80            reference system.
81        """
82        #bb= self._feature.getCSMLBoundingBox().getBox()
83        #convert 0 - 360 to -180, 180 as per common WMS convention
84        #if abs(bb[2]-bb[0]) >= 359 and abs(bb[2]-bb[0]) < 361:
85        #    bb[0], bb[2]=-180, 180
86        #self.wgs84BBox = bb
87        return self.wgs84BBox
88        #raise NotImplementedError
89       
90    def getSlab(self, crs, style, dimValues, transparent, bgcolor, 
91                    additionalParams={}):
92        """
93        Creates a slab of the layer in a particular CRS and set of
94        dimensions.
95
96        @param crs: The coordinate reference system.
97        @param dimValues: A mapping of dimension names to dimension values
98            as specified in the IDimension.extent
99        @param renderOpts: A generic mapping object for passing rendering
100            options
101        @return: An object implementing ILayerSlab
102        #create netcdf for whole lat/lon for given dimValues, use to init slab
103        """
104        log.debug("additionalParams = %s" % (additionalParams,))
105        log.debug("bgcolor = %s" % (bgcolor,))
106        log.debug("transparent = %s" % (transparent,))
107        log.debug("dimValues = %s" % (dimValues,))
108       
109        #make the colour compatable with matplotlib
110        if bgcolor.find('0x') == 0:
111            bgcolor = '#' + bgcolor[2:]
112       
113        st = time.time()
114        netcdfVar = self.dataReader.getNetcdfVar(self.name, dimValues)
115        log.debug("got netcdf in %ss" % (time.time() - st,))
116       
117        slabClass = self._getSlabClass(style)
118       
119        bbox=self.getBBox(crs)
120       
121        slab = slabClass(netcdfVar, self.title, crs, dimValues, transparent, bgcolor, bbox, additionalParams)
122               
123        return slab
124 
125    def getCacheKey(self, crs, style, dimValues, transparent, bgcolor, 
126                    additionalParams={}): 
127        """
128        Create a unique key for use in caching a slab.
129
130        The intention here is that most of the work should be done when
131        instantiating an ILayerSlab object.  These can be cached by the
132        server for future use.  The server will first call getCacheKey()
133        for the slab creation arguments and if the key is in it's cache
134        it will use a pre-generated ILayerSlab object.
135
136        """
137
138        dimList = list(dimValues.items())
139        dimList.sort()
140
141        #set the default style if none provided
142        s = self._getActualStyle(style)
143           
144        return '%s:%s:%s:%s:%s:%s:%s' % (self.name, crs, s, dimList,
145                                      transparent, bgcolor, additionalParams)
146
147    def _getActualStyle(self, style=None):
148        actualStyle = None
149       
150        if style == 'default' or style == '' or style is None:
151            actualStyle = DRGeoplotWmsLayer.default_slab_class.style
152        else:
153            actualStyle = style
154       
155        if actualStyle not in [x.style for x in DRGeoplotWmsLayer.slab_classes]:
156            Exception("No slab class found for style = %s"  % (style,))
157             
158        return actualStyle
159   
160    def _getSlabClass(self, style):
161        slabClass = None
162       
163        s = self._getActualStyle(style)
164       
165        for klass in DRGeoplotWmsLayer.slab_classes:
166            if klass.style == s:
167                slabClass = klass
168                break
169       
170        if slabClass == None:
171            Exception("No slab class found for style = %s"  % (style,))
172       
173        return slabClass
174
175    def getFeatureInfo(self, format, crs, point, dimValues):
176        """
177        Return a response string descibing the feature at a given
178        point in a given CRS.
179
180        Currently only "html" is supported as output format
181
182        @param format: One of self.featureInfoFormats.  Defines which
183            format the response will be in.
184        @param crs: One of self.crss
185        @param point: a tuple (x, y) in the supplied crs of the point
186            being selected.
187        @param dimValues: A mapping of dimension names to dimension values.
188        @return: A string containing the response.
189
190        """
191       
192#        #cached netcdf is indexed by a tuple of feature id and dimvalues - i.e. typically a particular time and Z value for that feature.
193#        #look in dictionary for cached copy, and if so use that as the file object.
194#        dictindex=str((self._feature.id, dimValues))
195#       
196#        if dictindex in self.featureinfofilecache:
197#            log.debug('calling cache')
198#            f=self.featureinfofilecache[dictindex]
199#        else: #else, use the csml api to subset the feature afresh
200#            log.debug('not calling cache')
201#            randomname= csml.csmllibs.csmlextra.getRandomID() + '.nc'
202#            result= self._feature.subsetToGridSeries(config['tmpdir'], ncname=randomname, **dimValues)
203#            #for now have to read netcdf back from disk (limitation of CSML api)
204#            f=cdms.open(result[1])
205#            #append to cache:
206#            self.featureinfofilecache[dictindex]=f
207#            #and then delete the temporary file
208#            os.system('rm %s'%result[1])
209#       
210#        netcdf = f(self.title)  #netcdf here is a cdms transient variable
211       
212        netcdf = self.dataReader.getNetcdfVar(self.name, dimValues)
213       
214        #Now grab the netCDF object for the point specified.
215        #The reason for the 'cob' option is so that if the grid the data
216        #is defined on does not have a grid point at the point specified,
217        #we should  still get the nearest location
218       
219        t_point = netcdf(latitude=(point[1], point[1], 'cob'), longitude=(point[0], point[0], 'cob'))
220        #now get the value recorded at this location
221        value = t_point.getValue().tolist()
222        log.debug(value)
223        log.debug(t_point.fill_value())
224        #and the fill_value too
225        fill_value = t_point.fill_value()
226        #value is actually embedded in a multi dimensional list,
227        #so we need to extract the actual value from the list
228        while type(value) is list:
229                value = value[0]
230
231        #now check if the value is actually the fill_value rather than
232        #a value recorded at the point specified
233        log.debug('%s %s' % (value, fill_value))
234        if (2*fill_value) == value:
235                value = "No value found at position: "+str(point[1])+", "+str(point[0])
236        else:
237                value = "Value found at position: "+str(point[1])+", "+str(point[0])+" is: "+str(value)
238               
239        # finally return the value
240        return value
241
242    def getLegendImage(self, dimValues, width=None, height=None, 
243                       orientation='horizontal', 
244                       renderOpts={}, 
245                       style=None
246                       ):
247        """
248        Create an image of the colourbar for this layer.
249        @param orientation: Either 'vertical' or 'horizontal'
250        @return: A PIL image with labels
251
252        """
253        if width == None:
254            width = self.legendSize[0]
255           
256        if height == None:
257            height = self.legendSize[1]
258               
259        variable = self.dataReader.getNetcdfVar(self.name, dimValues)
260       
261        klass = self._getSlabClass(style)
262       
263        return klass.makeColourBar(width , height, orientation, self.units, renderOpts, variable)
264   
265       
266        parser = SlabOptionsParser(klass.renderingOptions, renderOpts)
267       
268        log.debug("klass.style = %s" % (klass.style,))
269       
270        minval = parser.getOption('cmap_min')
271        if minval == None:
272            minval = variable.min()
273           
274        maxval = parser.getOption('cmap_max')
275        if maxval == None:
276            maxval = variable.max()
277           
278            # can't have a colourbar with an infinite maximum, take the highest
279            # non-inf value.
280            if maxval == numpy.inf:
281                maxval = numpy.ma.masked_equal(variable, numpy.inf).max()
282       
283        log.debug("parser.getOption('intervals') = %s" % (parser.getOption('intervals'),))
284        log.debug("parser.getOption('intervalNames') = %s" % (parser.getOption('intervalNames'),))
285       
286        im = geoplot.colour_bar.getColourBarImage(width, height, 
287                                             label='Units of measure: %s' % str(self.units),
288                                             cmap=parser.getOption('cmap'), 
289                                             colourBarMin=minval,
290                                             colourBarMax=maxval,
291                                             colourBarScale=parser.getOption('cmap_scale'),
292                                             numIntervals=parser.getOption('num_intervals'), 
293                                             orientation=orientation,
294                                             intervals=parser.getOption('intervals'),
295                                             intervalNames=parser.getOption('intervalNames'),
296                                             colourBarStyle=parser.getOption('cbar_style'),
297                                             )
298       
299        return im
300   
301    def _buildStyles(self):
302        onlineRes = OnlineResource(self._getIndexActionURL() + "?request=GetLegend&layers=%s" % self.name)
303       
304        legendURL = LegendURL(630, 80, format='img/png', onlineResource=onlineRes )
305       
306        styles = []
307        for klass in DRGeoplotWmsLayer.slab_classes:
308           
309            styleName = klass.style
310           
311            title = getattr(klass, 'title', None)
312           
313            if title is None:
314                title = styleName
315           
316            s = Style(styleName, title, legendURLs=[legendURL] )
317           
318            styles.append(s)
319       
320        return styles
321   
322    def getAxisConfigFile(self):
323        xmlFile = None
324       
325        if hasattr(self.dataReader, 'getConfigAxisXMLFile'):
326           
327            xmlFile =  self.dataReader.getConfigAxisXMLFile()
328       
329        return xmlFile
330   
331    def _buildMetadataURL(self):
332       
333        metadataURLs = []
334       
335        if DRGeoplotWmsLayer.EnableDisplayOptions == True:
336            onlineRes = OnlineResource(self._getIndexActionURL() +\
337                       "?request=GetDisplayOptions&layers=%s" % self.name)
338           
339            metadataURLs.append( MetadataURL(metadataType='display_options', 
340                                          format='application/json',
341                                          onlineResource=onlineRes) )
342           
343        if DRGeoplotWmsLayer.EnableXMLAxisConfig:
344           
345            xmlFile =  self.getAxisConfigFile()
346           
347            if xmlFile != None:
348               
349                onlineRes = OnlineResource(self._getIndexActionURL() +\
350                                "?request=GetAxisConfig&layers=%s" % self.name)
351           
352                metadataURLs.append( MetadataURL(metadataType='axis_config', 
353                                                 format='text/xml',
354                                                 onlineResource=onlineRes) )     
355       
356        return metadataURLs
357   
358    def _getIndexActionURL(self):
359        """
360        Uses the pylons config to build a url for the index action of this contoller.
361        """
362               
363        indexURL = url_for(qualified=True, action='index')
364        return indexURL
Note: See TracBrowser for help on using the repository browser.