source: cows/trunk/cows/service/imps/data_reader_geoplot_backend/geoplot_slabs/geoplot_slab_base.py @ 6123

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/cows/trunk/cows/service/imps/data_reader_geoplot_backend/geoplot_slabs/geoplot_slab_base.py@6123
Revision 6123, 6.0 KB checked in by pnorton, 11 years ago (diff)

Added the interval geoplot renderer, also improved the slab option parser and moved the responsibility for generating the colour bars to the slabs.

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