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

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@3669
Revision 3669, 9.7 KB checked in by domlowe, 14 years ago (diff)

fixing time units

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                self.units='ISO8601'
181            else:
182                for val in domain[dimname]:
183                    self.extent.append(str(val))
184        else:
185            for val in domain[dimname]:
186                self.extent.append(str(val))
187           
188       
189class CSMLLayerSlab(object):
190    """
191    Implements LayerSlab
192    Represents a particular horizontal slice of a WMS layer.
193
194    ILayerSlab objects are designed to be convenient to cache.
195    They should be pickleable to enable memcached support in the future.
196
197    @ivar layer: The source ILayer instance.
198    @ivar crs: The coordinate reference system.
199    @ivar dimValues: A mapping of dimension values of this view.
200    @ivar renderOpts: The renderOpts used to create this view.
201    @ivar bbox: The bounding box as a 4-tuple.
202    """
203   
204    def __init__(self, netcdf, layer, crs, dimValues, renderOpts, bbox):
205        self._netcdf=netcdf
206        self.layer = layer
207        self.crs = crs
208        self.dimValues = dimValues
209        self.renderOpts=renderOpts
210        self.bbox=bbox
211       
212       
213    def getImage(self, bbox, width, height):
214        """
215        Create an image of a sub-bbox of a given size.
216
217        @ivar bbox: A bbox 4-tuple.
218        @ivar width: width in pixels.` 
219        @ivar height: height in pixels.
220        @return: A PIL Image object.
221
222        """
223        cmap=eval(config['colourmap']) # renderOpts is hook for colourmap, for now use config
224        grid=Grid(self.layer, self._netcdf, bbox, width, height)
225        #how to handle varmin,varmax? ..read array?
226        #minval, maxval=genutil.minmax(grid.value)
227        minval=min(min(l) for l in grid.value)
228        maxval=max(max(l) for l in grid.value)
229        renderer=RGBARenderer(minval, maxval)         
230        return renderer.renderGrid(grid, bbox, width, height, cmap)
231   
232class Grid(object):
233    """A class encapsulating a simple regularly spaced, rectilinear
234    grid.  This is the only type of grid pywms is expected to
235    understand and adaptors should be provided to connect to
236    underlying implementations such as cdms or csml.
237
238    @cvar crs: Coordinate reference system
239
240    @ivar x0: coordinate of the lower left corner.
241    @ivar y0: coordinate of the lower left corner.
242    @ivar dx: The x grid spacing.
243    @ivar dy: The y grid spacing.
244    @ivar nx: The number of x grid points.
245    @ivar ny: The number of y grid points.
246    @ivar value: A masked array of the grid values.
247    @ivar ix: The dimension index of longidude in value
248    @ivar iy: The dimension index of latitude in value
249    @ivar long_name: The name of the field.
250    @ivar units: The units of the field.
251    """
252    def __init__(self, layer, netcdf, bbox, width, height):
253        #we know the axes are called latitude and longitude as the CSML code has written it:
254
255        v=netcdf(layer.title)
256        tvar=v(latitude=(bbox[1], bbox[3]), longitude=(bbox[0],bbox[2]),squeeze=1)
257        order=tvar.getOrder()
258        #array of data
259        self.value=tvar.getValue()
260        #order of axes
261        if order == 'xy':
262            self.ix=0
263            self.iy=1
264        else:
265            self.ix=1
266            self.iy=0
267        lat = tvar.getLatitude()
268        lon = tvar.getLongitude()
269        self.x0=lon[0]
270        self.y0=lat[0]
271        self.dx=abs(lon[0]-lon[1])
272        self.dy=abs(lat[0]-lat[1])
273        self.nx=len(lon)
274        self.ny=len(lat)
275        self.long_name=tvar.id  #TODO, get long name from feature
276        self.units=tvar.units
Note: See TracBrowser for help on using the repository browser.