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

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@5761
Revision 5761, 12.3 KB checked in by pnorton, 12 years ago (diff)

Added the ability to set background colours and some other options for the data renderer implementation.

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