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

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@3629
Revision 3629, 9.0 KB checked in by domlowe, 11 years ago (diff)

importing cdms2 as default

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