source: TI05-delivery/ows_framework/branches/ows_framework-refactor/ows_common/ows_common/service/imps/wms_csmllayer.py @ 3670

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/ows_framework/branches/ows_framework-refactor/ows_common/ows_common/service/imps/wms_csmllayer.py@3670
Revision 3670, 9.8 KB checked in by domlowe, 13 years ago (diff)

second attempt at fixing time string

Line 
1"""
2implementation of ILayerMapper, ILayer, IDimension, ILayerSlab interfaces, as defined in wms_iface.py
3
4"""
5import os, string
6import csml
7try:
8    import cdms2 as cdms
9except:
10    import cdms
11import Image
12from copy import copy
13from pywms.render_imp import RGBARenderer
14from matplotlib import cm
15import genutil
16from pylons import config  #config must have tmpfilebuffer and csmlstore values
17import cdtime
18
19
20class CSMLLayerMapper(object):
21    """
22    Map keyword arguments to a collection of layers.
23    Supports the retrieval of sets of layers according to arbitary
24    keyword/value pairs.
25    Implements  ILayerMapper
26   
27    """
28   
29    def _getInfo(self, feature):
30        ''' given a csml feature, return info about the layer/feature
31        @return:   title, abstract, dimensions, units, crss '''
32
33        try:
34            title=feature.name.CONTENT
35        except:
36            title=''
37        try:
38            abstract=feature.description.CONTENT
39        except:
40            abstract=title
41       
42        units=feature.getDomainUnits()
43        dimensions={}
44        tmpunits=copy(units)
45        tmpunits.reverse()
46        domain = feature.getDomain()
47        for dim in feature.getAxisLabels():
48            nextdim=CSMLDimension(domain, dim, tmpunits.pop())
49            if dim not in ['latitude', 'longitude']:
50                dimensions[dim]=nextdim
51        crs=feature.getNativeCRS()
52        crss=[self._crscat.getCRS(crs).twoD]
53        return title, abstract, dimensions, units, crss
54   
55    def map(self, **kwargs):
56        """
57        Given csml.parser.Dataset object list the names of
58        all layers available.
59       
60        @return: A mapping of layer names to ILayer implementations.
61        @raise ValueError: If no layers are available for these keywords.
62        """
63        fileoruri=kwargs['fileoruri']
64       
65        #TODO - handle file paths/directories URIs in config
66        #.xml or .csml extensions are supported:
67        filename='%s/%s.csml'%(config['csmlstore'],fileoruri)
68        if not os.path.exists(filename):
69           filename='%s/%s.xml'%(config['csmlstore'],fileoruri)
70        if not os.path.exists(filename):
71            raise Exception(str('CSML File could not be found: %s')%filename)
72           
73        ds=csml.parser.Dataset(filename)
74           
75        layermap={}
76        self._crscat=csml.csmllibs.csmlcrs.CRSCatalogue()
77        for feature in csml.csmllibs.csmlextra.listify(ds.featureCollection.featureMembers):
78            title, abstract, dimensions, units, crss=self._getInfo(feature)
79            layermap[feature.id]=CSMLLayer(title,abstract, dimensions, units, crss, feature)
80        if len(layermap) > 0:
81            return layermap
82        else:
83            raise ValueError
84
85
86class CSMLLayer(object):
87    """
88     representing a WMS layer.    Implements ILayer
89
90    @ivar title: The layer title.  As seen in the Capabilities document.
91    @ivar abstract:  Abstract as seen in the Capabilities document.
92    @ivar dimensions: A dictionary of IDimension objects.
93    @ivar units: A string describing the units.
94    @ivar crss: A sequence of SRS/CRSs supported by this layer.
95
96    @todo: Do we need minValue/maxValue?
97
98    """
99
100    def __init__(self, title, abstract, dimensions, units, crss, feature):
101        self.title=title
102        self.abstract=abstract
103        self.dimensions=dimensions
104        self.units=units
105        self.crss=crss
106        self._feature=feature
107        self.legendSize=(30,100)
108
109    def getBBox(self, crs):
110        """
111        @return: A 4-typle of the bounding box in the given coordinate
112            reference system.
113        """
114        bb= self._feature.getCSMLBoundingBox().getBox()
115        #convert 0 - 360 to -180, 180 as per common WMS convention
116        if abs(bb[2]-bb[0]) >= 359 and abs(bb[2]-bb[0]) < 361:
117            bb[0], bb[2]=-180, 180
118        return bb
119        #raise NotImplementedError
120       
121    def getSlab(self, crs, dimValues=None, renderOpts={}):
122        """
123        Creates a slab of the layer in a particular CRS and set of
124        dimensions.
125
126        @param crs: The coordinate reference system.
127        @param dimValues: A mapping of dimension names to dimension values
128            as specified in the IDimension.extent
129        @param renderOpts: A generic mapping object for passing rendering
130            options
131        @return: An object implementing ILayerSlab
132        #create netcdf for whole lat/lon for given dimValues, use to init slab
133        """
134        if type(self._feature) == csml.parser.GridSeriesFeature:
135            randomname= csml.csmllibs.csmlextra.getRandomID() + '.nc'
136            result= self._feature.subsetToGridSeries(config['tmpfilebuffer'], ncname=randomname, **dimValues)
137            #for now have to read netcdf back from disk (limitiation of CSML api)
138            netcdf=cdms.open(result[1])
139            #and then delete the temporary file
140            os.system('rm %s'%result[1])
141            bbox=self.getBBox(crs)
142            return CSMLLayerSlab(netcdf, self, crs, dimValues, renderOpts, bbox)
143        else:
144            raise NotImplementedError
145       
146    def getCacheKey(self, crs, dimValues=None, renderOpts={}):
147        """
148        Create a unique key for use in caching a slab.
149
150        The intention here is that most of the work should be done when
151        instantiating an ILayerSlab object.  These can be cached by the
152        server for future use.  The server will first call getCacheKey()
153        for the slab creation arguments and if the key is in it's cache
154        it will use a pre-generated ILayerSlab object.
155
156        """
157        return None
158        #raise NotImplementedError
159
160
161
162class CSMLDimension(object):
163    """
164    implements IDimension
165    @ivar units: The units string.
166    @ivar extent: Sequence of extent values.
167
168    """
169   
170    def __init__(self, domain, dimname, unit):
171        self.units = unit
172        self.extent = []
173        #patch to handle current limitations of multiple time dimension scanning in csml.
174        if string.lower(self.units)[:10] in ['days_since', 'seconds_si', 'minutes_si', 'hours_sinc','months _sin', 'years_sinc']:
175            if type(domain[dimname][0]) is not str   :
176                tunits=self.units.replace('_', ' ')
177                for val in domain[dimname]:
178                    csmltime= csml.csmllibs.csmltime.UDtimeToCSMLtime(cdtime.reltime(float(val), tunits).tocomp())
179                    self.extent.append(csmltime)
180            else:
181                for val in domain[dimname]:
182                    self.extent.append(str(val))
183        else:
184            for val in domain[dimname]:
185                self.extent.append(str(val))
186        #for time axis replace units with iso string
187        if dimname == 'time':
188            self.units='ISO8601'
189       
190class CSMLLayerSlab(object):
191    """
192    Implements LayerSlab
193    Represents a particular horizontal slice of a WMS layer.
194
195    ILayerSlab objects are designed to be convenient to cache.
196    They should be pickleable to enable memcached support in the future.
197
198    @ivar layer: The source ILayer instance.
199    @ivar crs: The coordinate reference system.
200    @ivar dimValues: A mapping of dimension values of this view.
201    @ivar renderOpts: The renderOpts used to create this view.
202    @ivar bbox: The bounding box as a 4-tuple.
203    """
204   
205    def __init__(self, netcdf, layer, crs, dimValues, renderOpts, bbox):
206        self._netcdf=netcdf
207        self.layer = layer
208        self.crs = crs
209        self.dimValues = dimValues
210        self.renderOpts=renderOpts
211        self.bbox=bbox
212       
213       
214    def getImage(self, bbox, width, height):
215        """
216        Create an image of a sub-bbox of a given size.
217
218        @ivar bbox: A bbox 4-tuple.
219        @ivar width: width in pixels.` 
220        @ivar height: height in pixels.
221        @return: A PIL Image object.
222
223        """
224        cmap=eval(config['colourmap']) # renderOpts is hook for colourmap, for now use config
225        grid=Grid(self.layer, self._netcdf, bbox, width, height)
226        #how to handle varmin,varmax? ..read array?
227        #minval, maxval=genutil.minmax(grid.value)
228        minval=min(min(l) for l in grid.value)
229        maxval=max(max(l) for l in grid.value)
230        renderer=RGBARenderer(minval, maxval)         
231        return renderer.renderGrid(grid, bbox, width, height, cmap)
232   
233class Grid(object):
234    """A class encapsulating a simple regularly spaced, rectilinear
235    grid.  This is the only type of grid pywms is expected to
236    understand and adaptors should be provided to connect to
237    underlying implementations such as cdms or csml.
238
239    @cvar crs: Coordinate reference system
240
241    @ivar x0: coordinate of the lower left corner.
242    @ivar y0: coordinate of the lower left corner.
243    @ivar dx: The x grid spacing.
244    @ivar dy: The y grid spacing.
245    @ivar nx: The number of x grid points.
246    @ivar ny: The number of y grid points.
247    @ivar value: A masked array of the grid values.
248    @ivar ix: The dimension index of longidude in value
249    @ivar iy: The dimension index of latitude in value
250    @ivar long_name: The name of the field.
251    @ivar units: The units of the field.
252    """
253    def __init__(self, layer, netcdf, bbox, width, height):
254        #we know the axes are called latitude and longitude as the CSML code has written it:
255
256        v=netcdf(layer.title)
257        tvar=v(latitude=(bbox[1], bbox[3]), longitude=(bbox[0],bbox[2]),squeeze=1)
258        order=tvar.getOrder()
259        #array of data
260        self.value=tvar.getValue()
261        #order of axes
262        if order == 'xy':
263            self.ix=0
264            self.iy=1
265        else:
266            self.ix=1
267            self.iy=0
268        lat = tvar.getLatitude()
269        lon = tvar.getLongitude()
270        self.x0=lon[0]
271        self.y0=lat[0]
272        self.dx=abs(lon[0]-lon[1])
273        self.dy=abs(lat[0]-lat[1])
274        self.nx=len(lon)
275        self.ny=len(lat)
276        self.long_name=tvar.id  #TODO, get long name from feature
277        self.units=tvar.units
Note: See TracBrowser for help on using the repository browser.