source: cows/trunk/cows/service/imps/csml_geoplot_backend/csml_geoplot_wms_layer_slab_base.py @ 5641

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/cows/trunk/cows/service/imps/csml_geoplot_backend/csml_geoplot_wms_layer_slab_base.py@5641
Revision 5641, 5.6 KB checked in by pnorton, 11 years ago (diff)

Added the EnableDisplayOptions? to the CsmlGeoplotWmsLayer? class that controls if the display options metadata URL is added to the response, by default it is not.

Also improved the handling of requests outside the boundaries of the data in the csml geoplot slab.

Line 
1import logging
2import time
3
4from cows.service.imps.csml_geoplot_backend.csml_geoplot_render_options_parser import CSMLGeoplotRenderOptionsParser
5
6
7import numpy
8from geoplot.utils import isRangeInLimits
9from cows.service.wms_iface import IwmsLayerSlab
10import Image
11
12log = logging.getLogger(__name__)
13
14class CSMLGeoplotWmsLayerSlabBase(IwmsLayerSlab):
15    """
16    A layer slab that implements the IwmsLayerSlab interface and uses geoplot
17    to render the required images.
18   
19    This is an abstract base class and should not be used directly.
20    """
21
22    """
23    contstructor
24   
25    @param netcdf: the netcdf variable that contains the data for this slab
26    @param title: the title of the variable that is to be used
27    @param crs: the coordinate refrence system the data is stored in
28    @param dimValues: the dimension values for this slab
29    @param transparent: indicates if the produced image should be transparent or
30        not.
31    @param bbox: the bounds of the data in lat/lon
32    @param renderOpts: the additional parameters recieved by the WMS, may include
33        some custom rendering options.
34    """
35    def __init__(self, netcdf, title, crs, dimValues, transparent, bbox, renderOpts):
36        self._netcdf=netcdf
37        self.title = title
38        self.crs = crs
39        self.dimValues = dimValues
40        self.renderOpts = renderOpts
41        self.transparent = transparent
42        self.bbox=bbox
43       
44        self.variable =  self._netcdf(self.title, squeeze=1) 
45       
46        self.minval = self.variable.min()
47        self.maxval = self.variable.max()
48 
49        #log.debug("renderOpts = %s" % (renderOpts,))
50       
51        self.parser = CSMLGeoplotRenderOptionsParser(renderOpts, self.minval, self.maxval)
52        self.ld = self._setupLayerDrawer()
53   
54    """
55    Creates the layer drawer object so that it can be used in getImage
56    """
57    def _setupLayerDrawer(self):
58        raise NotImplementedError()
59   
60    """
61    returns an image of the data constructed using the layer drawer
62   
63    @param bbox: the limits of the image requested
64    @param width: the width in px of the image
65    @param height: the height in px of the image
66    """
67    def getImage(self, bbox, width, height):
68        """
69        Create an image of a sub-bbox of a given size.
70
71        @ivar bbox: A bbox 4-tuple.
72        @ivar width: width in pixels.` 
73        @ivar height: height in pixels.
74        @return: A PIL Image object.
75
76        """
77        #log.debug("GetImage called with bbox=%s, width=%s, height = %s" % (bbox, width, height,))
78        xLimits = (bbox[0], bbox[2])
79        yLimits = (bbox[1], bbox[3])
80               
81        if self.variable.getAxisIds().sort() == ['latlitude','longitude'].sort():
82           
83            if not self._areBoundsInLimits(bbox, xLimits, yLimits):
84               
85                img = numpy.zeros((height,width,4), numpy.uint8)
86                pilImage = Image.fromarray(img, 'RGBA')
87               
88                log.debug("empty image used as no data found for id=%s (%sx%s), lon=%s, lat=%s " % \
89                  (self.variable.id, width, height, xLimits, yLimits))
90               
91                return pilImage
92                       
93        st = time.time()
94        im = self.ld.makeImage(xLimits, yLimits, width, height)
95       
96        log.debug("generated contour image id=%s (%sx%s, lon=%s, lat=%s in %.2fs" % \
97                  (self.variable.id, width, height, xLimits, yLimits,  time.time() - st,))
98       
99        return im
100
101    def _areBoundsInLimits(self, bbox, xLimits, yLimits):
102       
103        if self.variable.getAxisIds()[0] == 'longitude':
104            lonAx, latAx = self.variable.getAxisList()
105        else:
106            latAx, lonAx = self.variable.getAxisList()
107           
108        xRange = [ lonAx.getBounds().min(), lonAx.getBounds().max()]
109        yRange = [ latAx.getBounds().min(), latAx.getBounds().max()]
110        log.debug("xLimits = %s" % (xLimits,))
111        log.debug("yLimits = %s" % (yLimits,))
112        log.debug("xRange = %s" % (xRange,))
113        log.debug("yRange = %s" % (yRange,))
114       
115        xRange = self._fixLongitudeRange(xLimits, xRange)
116       
117        isInLimits = isRangeInLimits(xRange, xLimits) and \
118                     isRangeInLimits(yRange, yLimits)
119                     
120        return isInLimits
121   
122    def _fixLongitudeRange(self, xLimits, xRange):
123        """
124        Attemts to match the upper and lower bounds of the longiude range
125        to that of the xLimits (either -180,180 or 0,360)
126        """
127       
128        returnRange = xRange
129       
130        range_180to0 = False
131        range180to360 = False 
132             
133        #assuming the xLimits are given in range -180 to 180
134        for i in [0,1]:
135            if -180.0 <= xLimits[i] < 0.0:
136                range_180to0 = True
137           
138            elif 180 < xLimits[i] <= 360:
139                range180to360 = True
140       
141        log.debug("range180to360 = %s, range_180to0 = %s" % (range180to360, range_180to0,))
142        assert not (range_180to0 and range180to360), "confusing bounds found on longitude range %s " % (xLimits,)
143       
144        if range_180to0:
145            #if the xRange is 0-360 need to adjust it
146            for x in returnRange:
147               
148                if x > 180.0:
149                    returnRange = [x - 180 for x in returnRange]
150                    break
151                   
152        if range180to360:
153            #if the range is -180-0 need to adjust it
154            for x in returnRange:
155                if x < 0.0:
156                    returnRange =  [x + 180 for x in returnRange]
157                    break
158               
159        log.debug("returnRange = %s" % (returnRange,))
160        return returnRange
161       
Note: See TracBrowser for help on using the repository browser.