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

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

Moved all the url_for requests into the same place in the geoplot data reader layer.

RevLine 
[5676]1'''
2Created on 9 Jun 2009
3
4@author: pnorton
5'''
6
7import logging
8
9import time
10import os
[6028]11import numpy
[5676]12
13from cows.service.imps.csmlbackend.config import config
14from cows.service.wms_iface import IwmsLayer
15
[6042]16
[5676]17log = logging.getLogger(__name__)
18
19import geoplot.colour_bar
[5697]20from geoplot.grid_builder_lat_lon import GridBuilderLatLon
[5676]21
22from cows.model.wms import Style, LegendURL, FormattedURL, MetadataURL
23from cows.xml.iso19115_subset import OnlineResource
24
25from routes import url_for
26
[5692]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
[5693]29from cows.service.imps.data_reader_geoplot_backend.slab_options_parser import SlabOptionsParser
[5692]30
31
[5676]32class DRGeoplotWmsLayer(IwmsLayer):
[5692]33
34    slab_classes = [GeoplotSlabGrid, GeoplotSlabContour]
35    default_slab_class = GeoplotSlabGrid
[5676]36   
37    EnableDisplayOptions = False
[5848]38    EnableXMLAxisConfig = False
[5676]39   
[5848]40    def __init__(self, name, title, abstract, dimensions, units, crss, 
41                 boundingBox, dataReader):
[5676]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
[5877]48        self.legendSize=(630,120)
[5676]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       
[5761]109        #make the colour compatable with matplotlib
110        if bgcolor.find('0x') == 0:
111            bgcolor = '#' + bgcolor[2:]
112       
[5676]113        st = time.time()
114        netcdfVar = self.dataReader.getNetcdfVar(self.name, dimValues)
115        log.debug("got netcdf in %ss" % (time.time() - st,))
116       
[5692]117        slabClass = self._getSlabClass(style)
118       
[5676]119        bbox=self.getBBox(crs)
120       
[5761]121        slab = slabClass(netcdfVar, self.title, crs, dimValues, transparent, bgcolor, bbox, additionalParams)
[5676]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
[5692]142        s = self._getActualStyle(style)
[5676]143           
144        return '%s:%s:%s:%s:%s:%s:%s' % (self.name, crs, s, dimList,
145                                      transparent, bgcolor, additionalParams)
146
[5692]147    def _getActualStyle(self, style=None):
148        actualStyle = None
149       
[5693]150        if style == 'default' or style == '' or style is None:
[5692]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]:
[5848]156            Exception("No slab class found for style = %s"  % (style,))
[5692]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:
[5848]171            Exception("No slab class found for style = %s"  % (style,))
[5692]172       
173        return slabClass
174
[5676]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, orientation='horizontal', renderOpts={}):
243        """
244        Create an image of the colourbar for this layer.
245        @param orientation: Either 'vertical' or 'horizontal'
246        @return: A PIL image with labels
247
248        """
249        if width == None:
250            width = self.legendSize[0]
251           
252        if height == None:
253            height = self.legendSize[1]
254               
255        variable = self.dataReader.getNetcdfVar(self.name, dimValues)
256       
[5693]257        klass = self._getSlabClass(None)
258        parser = SlabOptionsParser(klass.renderingOptions, renderOpts)
[5676]259       
[5693]260        minval = parser.getOption('cmap_min')
261        if minval == None:
262            minval = variable.min()
263           
264        maxval = parser.getOption('cmap_max')
265        if maxval == None:
266            maxval = variable.max()
[6028]267            if maxval == numpy.inf:
268                maxval = numpy.ma.masked_equal(variable, numpy.inf).max()
[5697]269       
[5877]270        log.debug("parser.getOption('intervals') = %s" % (parser.getOption('intervals'),))
271        log.debug("parser.getOption('intervalNames') = %s" % (parser.getOption('intervalNames'),))
[5945]272       
[5877]273        im = geoplot.colour_bar.getColourBarImage(width, height, 
274                                             label='Units of measure: %s' % str(self.units),
[5676]275                                             cmap=parser.getOption('cmap'), 
[5877]276                                             colourBarMin=minval,
[5945]277                                             colourBarMax=maxval,
278                                             colourBarScale=parser.getOption('cmap_scale'), 
[5877]279                                             orientation=orientation,
280                                             intervals=parser.getOption('intervals'),
281                                             intervalNames=parser.getOption('intervalNames'),
282                                             colourBarStyle=parser.getOption('cbar_style'),
283                                             )
[5676]284       
285        return im
286   
287    def _buildStyles(self):
[6042]288        onlineRes = OnlineResource(self._getIndexActionURL() + "?request=GetLegend&layers=%s" % self.name)
[5676]289       
290        legendURL = LegendURL(630, 80, format='img/png', onlineResource=onlineRes )
291       
[5692]292        styles = []
293        for klass in DRGeoplotWmsLayer.slab_classes:
294           
295            styleName = klass.style
296           
297            title = getattr(klass, 'title', None)
298           
299            if title is None:
300                title = styleName
301           
302            s = Style(styleName, title, legendURLs=[legendURL] )
303           
304            styles.append(s)
[5676]305       
[5692]306        return styles
[5676]307   
[5848]308    def getAxisConfigFile(self):
309        xmlFile = None
310       
311        if hasattr(self.dataReader, 'getConfigAxisXMLFile'):
312           
313            xmlFile =  self.dataReader.getConfigAxisXMLFile()
314       
315        return xmlFile
316   
[5676]317    def _buildMetadataURL(self):
318       
[5848]319        metadataURLs = []
320       
[5676]321        if DRGeoplotWmsLayer.EnableDisplayOptions == True:
[6042]322            onlineRes = OnlineResource(self._getIndexActionURL() +\
323                       "?request=GetDisplayOptions&layers=%s" % self.name)
[5676]324           
[5848]325            metadataURLs.append( MetadataURL(metadataType='display_options', 
[6042]326                                          format='application/json',
[5848]327                                          onlineResource=onlineRes) )
[5676]328           
[5848]329        if DRGeoplotWmsLayer.EnableXMLAxisConfig:
330           
331            xmlFile =  self.getAxisConfigFile()
332           
333            if xmlFile != None:
334               
[6042]335                onlineRes = OnlineResource(self._getIndexActionURL() +\
336                                "?request=GetAxisConfig&layers=%s" % self.name)
[5848]337           
338                metadataURLs.append( MetadataURL(metadataType='axis_config', 
[6042]339                                                 format='text/xml',
340                                                 onlineResource=onlineRes) )     
[5848]341       
[6042]342        return metadataURLs
343   
344    def _getIndexActionURL(self):
345        """
346        Uses the pylons config to build a url for the index action of this contoller.
347        """
348               
349        indexURL = url_for(qualified=True, action='index')
350        return indexURL
Note: See TracBrowser for help on using the repository browser.