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

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@6028
Revision 6028, 12.9 KB checked in by pnorton, 10 years ago (diff)

Fixed the legend code so that it won't try and set the colour bar maximum as infinity.

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