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.

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, 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       
257        klass = self._getSlabClass(None)
258        parser = SlabOptionsParser(klass.renderingOptions, renderOpts)
259       
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()
267            if maxval == numpy.inf:
268                maxval = numpy.ma.masked_equal(variable, numpy.inf).max()
269       
270        log.debug("parser.getOption('intervals') = %s" % (parser.getOption('intervals'),))
271        log.debug("parser.getOption('intervalNames') = %s" % (parser.getOption('intervalNames'),))
272       
273        im = geoplot.colour_bar.getColourBarImage(width, height, 
274                                             label='Units of measure: %s' % str(self.units),
275                                             cmap=parser.getOption('cmap'), 
276                                             colourBarMin=minval,
277                                             colourBarMax=maxval,
278                                             colourBarScale=parser.getOption('cmap_scale'), 
279                                             orientation=orientation,
280                                             intervals=parser.getOption('intervals'),
281                                             intervalNames=parser.getOption('intervalNames'),
282                                             colourBarStyle=parser.getOption('cbar_style'),
283                                             )
284       
285        return im
286   
287    def _buildStyles(self):
288        onlineRes = OnlineResource(self._getIndexActionURL() + "?request=GetLegend&layers=%s" % self.name)
289       
290        legendURL = LegendURL(630, 80, format='img/png', onlineResource=onlineRes )
291       
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)
305       
306        return styles
307   
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   
317    def _buildMetadataURL(self):
318       
319        metadataURLs = []
320       
321        if DRGeoplotWmsLayer.EnableDisplayOptions == True:
322            onlineRes = OnlineResource(self._getIndexActionURL() +\
323                       "?request=GetDisplayOptions&layers=%s" % self.name)
324           
325            metadataURLs.append( MetadataURL(metadataType='display_options', 
326                                          format='application/json',
327                                          onlineResource=onlineRes) )
328           
329        if DRGeoplotWmsLayer.EnableXMLAxisConfig:
330           
331            xmlFile =  self.getAxisConfigFile()
332           
333            if xmlFile != None:
334               
335                onlineRes = OnlineResource(self._getIndexActionURL() +\
336                                "?request=GetAxisConfig&layers=%s" % self.name)
337           
338                metadataURLs.append( MetadataURL(metadataType='axis_config', 
339                                                 format='text/xml',
340                                                 onlineResource=onlineRes) )     
341       
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.