source: qesdi/geoplot/trunk/lib/geoplot/layer_drawer.py @ 6089

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/qesdi/geoplot/trunk/lib/geoplot/layer_drawer.py@6089
Revision 6089, 9.3 KB checked in by pnorton, 10 years ago (diff)

Imroved the colour bar code so that a legend colour bar can be used without specifying any intervals.

Line 
1"""
2An object to draw just the layer (map + grid) from the plot,
3can draw to a file, a string or create an Image object.
4"""
5
6
7import logging
8import time
9import numpy
10
11import matplotlib.colors
12from matplotlib.figure import Figure
13
14import geoplot.utils as geoplot_utils
15from geoplot.colour_bar import COLOUR_BAR_STYLES
16from geoplot.grid_factory import GridFactory
17from geoplot.map_factory import MapFactory
18from geoplot.colour_scheme import ColourSchemeBuilder
19import geoplot.utils as utils
20
21from geoplot.colour_bar import COLOUR_SCHEME_SCALE
22
23log = logging.getLogger(__name__)
24
25VALID_GRID_TYPES = ['latlon', 'national', 'rotated']
26
27VALID_PROJECTIONS = ['latlon', 'national']
28
29class LayerDrawerBase(object):
30    "Draws only the layer section of the plot to create a PIL image object"
31   
32    def __init__(self, 
33                 gridType='latlon', 
34                 transparent=False,
35                 projection='latlon',
36                 resolution=None,
37                 cmap=None, 
38                 colourBarMin=None,
39                 colourBarMax=None, 
40                 colourBarScale='linear',
41                 colourBarStyle=COLOUR_BAR_STYLES.CONTINUOUS,
42                 intervals=None, 
43                 bgcolour='white'):
44       
45       
46        self._csBuilder = ColourSchemeBuilder()
47        self._bgcolour = 'white'
48       
49        self.colourBarStyle = colourBarStyle
50       
51        self.colourBarScale = colourBarScale
52        self.transparent = transparent
53        self.cmap = cmap
54       
55        log.debug("bgcolour = %s" % (bgcolour,))
56        self.bgcolour = bgcolour
57       
58        self._gridFactory = GridFactory(dataType=gridType)
59        self._mapFactory = MapFactory(projection, drawCoast=True, drawRivers=False, resolution=resolution)
60       
61        self.colourBarMin = colourBarMin
62        self.colourBarMax = colourBarMax
63        self.intervals = intervals
64       
65    def makeImage(self, xLimits=None, yLimits=None, width=800, height=600, dpi=200):
66        """
67        Creates a PIL image of the selected area of the layer.
68        """
69        st = time.time()
70
71        fig = self._getFigure(width, height, dpi)
72        figTime = time.time() 
73
74        axes = self._addAxes(fig)
75        axTime = time.time() 
76
77        self._drawToAxes(axes, xLimits, yLimits)
78        drawAxTime = time.time() 
79       
80        self._resetAxes(axes, xLimits, yLimits)
81        resetTime = time.time() 
82       
83        im = geoplot_utils.figureToImage(fig)
84        figToImTime = time.time() 
85       
86        log.debug("drawn layer in %.4fs : fig=%.2f ax=%.2f draw=%.2f res=%.2f im=%.2f" % 
87                  (time.time() -st , figTime - st, axTime - figTime, drawAxTime - axTime, resetTime - drawAxTime, figToImTime - resetTime))
88       
89        return im
90   
91   
92    def _drawToAxes(self, *args, **kwargs):
93        """
94        Draw the layer onto the axis, should be overidden by all subclasses.
95        """
96       
97        raise NotImplementedError()
98   
99    def _getFigure(self, width, height, dpi):
100        """
101        Returns a new figure object that is ready to be drawn on.
102        """
103
104        figsize=(width / float(dpi), height / float(dpi))
105
106        fig = Figure(figsize=figsize, dpi=dpi, facecolor=self.bgcolour, edgecolor=self.bgcolour,
107                     frameon=(not self.transparent))
108       
109        return fig       
110   
111    def _addAxes(self, figure):
112        """
113        Adds an axis to the figure object provided. The axes has no border and takes
114        up all the area on the figure so that anything drawn on the axis will
115        completly cover the figure.
116       
117        The axis background is transparent, if self.transparency is set to false
118        then the Figure's frameon should be set to true not the axis.
119        """
120       
121        axes = matplotlib.axes.Axes(figure, [0.0, 0.0, 1.0, 1.0], 
122                                    xticks=[], yticks=[], frameon=False)
123       
124        figure.add_axes(axes)
125       
126        #axes = figure.add_axes([0.0, 0.0, 1.0, 1.0],
127        #                       xticks=[], yticks=[], frameon=False)
128       
129        if self.transparent:
130            axes.set_alpha(0.0)
131            axes.patch.set_alpha(0.0)
132           
133       
134        return axes
135   
136    def _buildGrid(self, cdmsVar, xLimits, yLimits):
137        """
138        Builds a new grid object using the data found in the cdmsVar.
139        """
140       
141        self._gridFactory.cdmsVar = cdmsVar
142       
143        grid = self._gridFactory.getGrid(xLimits, yLimits)
144       
145        return grid
146   
147    def _resetAxes(self, axes, xLimits=None, yLimits=None):
148        """
149        resets the axis to the original limis and aspect after they have
150        been drawn on, this is needed as some methods of drawing to the axis
151        (notably basemap) change these properties.
152        """
153       
154        axes.set_aspect('auto')
155       
156        axes.set_xticks([])
157        axes.set_yticks([])
158       
159        if self.projection == 'latlon':
160            xLimitsMapUnits, yLimitsMapUnits = xLimits, yLimits
161        else:
162            map = self._getMap(xLimits, yLimits)
163            xLimitsMapUnits, yLimitsMapUnits = map.basemap(xLimits, yLimits)
164       
165        #reset the limits after drawing the grid
166        if xLimits != None:
167            axes.set_xlim(float(xLimitsMapUnits[0]), float(xLimitsMapUnits[1]))
168       
169        if yLimits != None:
170            axes.set_ylim(float(yLimitsMapUnits[0]), float(yLimitsMapUnits[1]))   
171       
172    def _getMap(self, xLimits, yLimits, ):
173        """
174        Returns a map object that corresponds to the current projection,
175        map objects can be used for transformation or drawing data.
176        """
177       
178        self._mapFactory.xLimits = xLimits
179        self._mapFactory.yLimits = yLimits
180       
181        map = self._mapFactory.buildMap()
182           
183        return map       
184
185    def _applyMask(self, grid, scheme, hideOutside):
186        """
187        Masks the data outside the scheme bounds if hideOutside is true, also
188        masks values less than zero if a log scale is being used
189        """
190       
191        if self._hideOutsideBounds:
192            grid.values = numpy.ma.masked_less(grid.values, scheme.norm.vmin)
193            grid.values = numpy.ma.masked_greater(grid.values, scheme.norm.vmax)
194       
195        if scheme.scale == COLOUR_SCHEME_SCALE.LOG:
196            grid.values = numpy.ma.masked_less_equal(grid.values, 0.0)
197           
198        return grid
199       
200    ### properties ###       
201   
202    def __set_gridType(self, value):
203        if value not in VALID_GRID_TYPES:
204           
205            raise Exception(\
206             "Invalid value of '%s' for LayerDrawer.gridType property, must be one of %s" 
207                % (value, VALID_GRID_TYPES,))
208           
209        self._gridFactory.dataType = value
210
211    def __get_gridType(self):
212        return self._gridFactory.dataType
213   
214    gridType = property(__get_gridType, __set_gridType, None, None)
215   
216    def __set_showGridLines(self, value):
217        self._gridDrawer.showGridLines = value
218   
219    def __get_showGridLines(self):
220        return self._gridDrawer.showGridLines
221   
222    showGridLines = property(__get_showGridLines, __set_showGridLines) 
223       
224    def __set_outline(self, value):
225        self._gridDrawer.outline = value
226       
227    def __get_outline(self):
228        return self._gridDrawer.outline
229   
230    outline = property(__get_outline, __set_outline)
231
232       
233    def __set_projection(self, value):
234        if value not in VALID_PROJECTIONS:
235           
236            raise Exception(\
237             "Invalid value of '%s' for projection property, must be one of %s" 
238                % (value, VALID_PROJECTIONS,))
239           
240        self._mapFactory.projection = value
241
242    def __get_projection(self):
243        return self._mapFactory.projection
244   
245    projection = property(__get_projection, __set_projection, None, None)
246
247    def __set_cmap(self, value):
248        self._csBuilder.cmap = value
249       
250    def __get_cmap(self):
251        return self._csBuilder.cmap
252   
253    cmap = property(__get_cmap, __set_cmap)
254
255
256    def __set_intervals(self, value):
257        self._csBuilder.intervals = value
258   
259    def __get_intervals(self):
260        return self._csBuilder.intervals
261   
262    intervals = property(__get_intervals, __set_intervals)
263   
264    def __set_colourBarMin(self, value):
265        self._csBuilder.colourBarMin = value
266   
267    def __get_colourBarMin(self):
268        return self._csBuilder.colourBarMin
269   
270    colourBarMin = property(__get_colourBarMin, __set_colourBarMin)
271   
272    def __set_colourBarMax(self, value):
273        self._csBuilder.colourBarMax = value
274   
275    def __get_colourBarMax(self):
276        return self._csBuilder.colourBarMax
277
278    colourBarMax = property(__get_colourBarMax, __set_colourBarMax)
279   
280    def __set_colourBarScale(self, value):
281        self._csBuilder.colourBarScale = value
282   
283    def __get_colourBarScale(self):
284        return self._csBuilder.colourBarScale
285
286    colourBarScale = property(__get_colourBarScale, __set_colourBarScale)
287           
288    def __set_bgcolour(self, value):
289       
290        try:
291            newVal = utils.getMatplotlibColour(value)
292        except ValueError, e:
293            log.warning("Error occurred getting matplotlib colour form %s, Exception:%s" % (value, e))
294        else:
295            self._bgcolour = newVal
296               
297    def __get_bgcolour(self):
298        return self._bgcolour
299   
300    bgcolour = property(__get_bgcolour, __set_bgcolour)   
301   
Note: See TracBrowser for help on using the repository browser.