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

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

Added a faster layer drawer for the grid, a new style of colour bar and colour schemes which now hold the normalisation and colour map instances.

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