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

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@5848
Revision 5848, 13.2 KB checked in by pnorton, 11 years ago (diff)

First attempt at passing the axis config xml through to the metadata in the getcapabilities.

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,80)
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        intervalNames = parser.getOption('intervalNames')
267        if intervalNames != None:
268            intervalNames = intervalNames.split(',')
269       
270        intervalColourbar = parser.getOption('intervalColourbar')
271       
272        log.debug("intervalNames = %s" % (intervalNames,))
273        log.debug("intervalColourbar = %s" % (intervalColourbar,))
274       
275        #log.debug("dimValues = %s, minval = %s, maxval = %s" % (dimValues, minval , maxval,))
276       
277        if intervalColourbar:
278            builder = GridBuilderLatLon(variable)
279           
280            grid = builder.buildGrid()
281           
282            im = geoplot.colour_bar.getIntervalColourBarImage(grid, width, height, 
283                                             label='Units of measure: %s' % str(self.units), 
284                                             cmap=parser.getOption('cmap'), 
285                                             cmapRange=(minval, maxval),
286                                             intervalNames=intervalNames, 
287                                             orientation=orientation)
288           
289        else:
290            im = geoplot.colour_bar.getColourBarImage(width, height, 
291                                             label='Units of measure: %s' % str(self.units), 
292                                             cmap=parser.getOption('cmap'), 
293                                             cmapRange=(minval, maxval), 
294                                             orientation=orientation)
295       
296        return im
297   
298    def _buildStyles(self):
299        onlineRes = OnlineResource(url_for(qualified=True, action='index') + "?request=GetLegend&layers=%s" % self.name)
300       
301        legendURL = LegendURL(630, 80, format='img/png', onlineResource=onlineRes )
302       
303        styles = []
304        for klass in DRGeoplotWmsLayer.slab_classes:
305           
306            styleName = klass.style
307           
308            title = getattr(klass, 'title', None)
309           
310            if title is None:
311                title = styleName
312           
313            s = Style(styleName, title, legendURLs=[legendURL] )
314           
315            styles.append(s)
316       
317        return styles
318   
319    def getAxisConfigFile(self):
320        xmlFile = None
321       
322        if hasattr(self.dataReader, 'getConfigAxisXMLFile'):
323           
324            xmlFile =  self.dataReader.getConfigAxisXMLFile()
325       
326        return xmlFile
327   
328    def _buildMetadataURL(self):
329       
330        metadataURLs = []
331       
332        if DRGeoplotWmsLayer.EnableDisplayOptions == True:
333            onlineRes = OnlineResource(url_for(qualified=True, action='index') +\
334                                        "?request=GetDisplayOptions&layers=%s" % self.name)
335           
336            metadataURLs.append( MetadataURL(metadataType='display_options', 
337                                          format='application/json', 
338                                          onlineResource=onlineRes) )
339           
340        if DRGeoplotWmsLayer.EnableXMLAxisConfig:
341           
342            xmlFile =  self.getAxisConfigFile()
343           
344            if xmlFile != None:
345               
346                onlineRes = OnlineResource(url_for(qualified=True, action='index') +\
347                                        "?request=GetAxisConfig&layers=%s" % self.name)
348           
349                metadataURLs.append( MetadataURL(metadataType='axis_config', 
350                                                 format='text/xml', 
351                                          onlineResource=onlineRes) )     
352       
353        return metadataURLs
Note: See TracBrowser for help on using the repository browser.