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

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@3749
Revision 3749, 10.2 KB checked in by domlowe, 13 years ago (diff)

fix to unicode problem in multidimensional subsetting

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.featureInfoFormats=None #NotImplemented
102        self.title=title
103        self.abstract=abstract
104        self.dimensions=dimensions
105        self.units=units
106        self.crss=crss
107        self._feature=feature
108        self.legendSize=(30,100)
109        try:
110            self.wgs84BBox = self.getBBox('EPSG:4326')
111        except:
112            raise ValueError("Layer must provide a bounding box in EPSG:4326 "
113                             "coordinates for compatibility with WMS-1.3.0")
114
115    def getBBox(self, crs):
116        """
117        @return: A 4-typle of the bounding box in the given coordinate
118            reference system.
119        """
120        bb= self._feature.getCSMLBoundingBox().getBox()
121        #convert 0 - 360 to -180, 180 as per common WMS convention
122        if abs(bb[2]-bb[0]) >= 359 and abs(bb[2]-bb[0]) < 361:
123            bb[0], bb[2]=-180, 180
124        return bb
125        #raise NotImplementedError
126       
127    def getSlab(self, crs, dimValues=None, renderOpts={}):
128        """
129        Creates a slab of the layer in a particular CRS and set of
130        dimensions.
131
132        @param crs: The coordinate reference system.
133        @param dimValues: A mapping of dimension names to dimension values
134            as specified in the IDimension.extent
135        @param renderOpts: A generic mapping object for passing rendering
136            options
137        @return: An object implementing ILayerSlab
138        #create netcdf for whole lat/lon for given dimValues, use to init slab
139        """
140        #unicode conversion
141        for dimval in dimValues:
142            if dimval != 'time':
143                dimValues[dimval]=float(dimValues[dimval])
144        if type(self._feature) == csml.parser.GridSeriesFeature:
145            randomname= csml.csmllibs.csmlextra.getRandomID() + '.nc'
146            result= self._feature.subsetToGridSeries(config['tmpfilebuffer'], ncname=randomname, **dimValues)
147            #for now have to read netcdf back from disk (limitiation of CSML api)
148            netcdf=cdms.open(result[1])
149            #and then delete the temporary file
150            os.system('rm %s'%result[1])
151            bbox=self.getBBox(crs)
152            return CSMLLayerSlab(netcdf, self, crs, dimValues, renderOpts, bbox)
153        else:
154            raise NotImplementedError
155       
156    def getCacheKey(self, crs, dimValues=None, renderOpts={}):
157        """
158        Create a unique key for use in caching a slab.
159
160        The intention here is that most of the work should be done when
161        instantiating an ILayerSlab object.  These can be cached by the
162        server for future use.  The server will first call getCacheKey()
163        for the slab creation arguments and if the key is in it's cache
164        it will use a pre-generated ILayerSlab object.
165
166        """
167        return None
168        #raise NotImplementedError
169
170
171
172class CSMLDimension(object):
173    """
174    implements IDimension
175    @ivar units: The units string.
176    @ivar extent: Sequence of extent values.
177
178    """
179   
180    def __init__(self, domain, dimname, unit):
181        self.units = unit
182        self.extent = []
183        #patch to handle current limitations of multiple time dimension scanning in csml.
184        if string.lower(self.units)[:10] in ['days_since', 'seconds_si', 'minutes_si', 'hours_sinc','months _sin', 'years_sinc']:
185            if type(domain[dimname][0]) is not str   :
186                tunits=self.units.replace('_', ' ')
187                for val in domain[dimname]:
188                    csmltime= csml.csmllibs.csmltime.UDtimeToCSMLtime(cdtime.reltime(float(val), tunits).tocomp())
189                    self.extent.append(csmltime)
190            else:
191                for val in domain[dimname]:
192                    self.extent.append(str(val))
193        else:
194            for val in domain[dimname]:
195                self.extent.append(str(val))
196        #for time axis replace units with iso string
197        if dimname == 'time':
198            self.units='ISO8601'
199       
200class CSMLLayerSlab(object):
201    """
202    Implements LayerSlab
203    Represents a particular horizontal slice of a WMS layer.
204
205    ILayerSlab objects are designed to be convenient to cache.
206    They should be pickleable to enable memcached support in the future.
207
208    @ivar layer: The source ILayer instance.
209    @ivar crs: The coordinate reference system.
210    @ivar dimValues: A mapping of dimension values of this view.
211    @ivar renderOpts: The renderOpts used to create this view.
212    @ivar bbox: The bounding box as a 4-tuple.
213    """
214   
215    def __init__(self, netcdf, layer, crs, dimValues, renderOpts, bbox):
216        self._netcdf=netcdf
217        self.layer = layer
218        self.crs = crs
219        self.dimValues = dimValues
220        self.renderOpts=renderOpts
221        self.bbox=bbox
222       
223       
224    def getImage(self, bbox, width, height):
225        """
226        Create an image of a sub-bbox of a given size.
227
228        @ivar bbox: A bbox 4-tuple.
229        @ivar width: width in pixels.` 
230        @ivar height: height in pixels.
231        @return: A PIL Image object.
232
233        """
234        cmap=eval(config['colourmap']) # renderOpts is hook for colourmap, for now use config
235        grid=Grid(self.layer, self._netcdf, bbox, width, height)
236        #how to handle varmin,varmax? ..read array?
237        #minval, maxval=genutil.minmax(grid.value)
238        minval=min(min(l) for l in grid.value)
239        maxval=max(max(l) for l in grid.value)
240        renderer=RGBARenderer(minval, maxval)         
241        return renderer.renderGrid(grid, bbox, width, height, cmap)
242   
243class Grid(object):
244    """A class encapsulating a simple regularly spaced, rectilinear
245    grid.  This is the only type of grid pywms is expected to
246    understand and adaptors should be provided to connect to
247    underlying implementations such as cdms or csml.
248
249    @cvar crs: Coordinate reference system
250
251    @ivar x0: coordinate of the lower left corner.
252    @ivar y0: coordinate of the lower left corner.
253    @ivar dx: The x grid spacing.
254    @ivar dy: The y grid spacing.
255    @ivar nx: The number of x grid points.
256    @ivar ny: The number of y grid points.
257    @ivar value: A masked array of the grid values.
258    @ivar ix: The dimension index of longidude in value
259    @ivar iy: The dimension index of latitude in value
260    @ivar long_name: The name of the field.
261    @ivar units: The units of the field.
262    """
263    def __init__(self, layer, netcdf, bbox, width, height):
264        #we know the axes are called latitude and longitude as the CSML code has written it:
265
266        v=netcdf(layer.title)
267        tvar=v(latitude=(bbox[1], bbox[3]), longitude=(bbox[0],bbox[2]),squeeze=1)
268        order=tvar.getOrder()
269        #array of data
270        self.value=tvar.getValue()
271        #order of axes
272        if order == 'xy':
273            self.ix=0
274            self.iy=1
275        else:
276            self.ix=1
277            self.iy=0
278        lat = tvar.getLatitude()
279        lon = tvar.getLongitude()
280        self.x0=lon[0]
281        self.y0=lat[0]
282        self.dx=abs(lon[0]-lon[1])
283        self.dy=abs(lat[0]-lat[1])
284        self.nx=len(lon)
285        self.ny=len(lat)
286        self.long_name=tvar.id  #TODO, get long name from feature
287        self.units=tvar.units
Note: See TracBrowser for help on using the repository browser.