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

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

colourmap now in config

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