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

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

Improved the csml_data_reader so that it no longer assumes the feature id is the variable name.

Improved the data reader layer so that it now gets the dataset name from the csml file.

Added some code to the geoplot slabs that is used for selecting a log scale.

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