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

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

Improved geoplot's behaviour when dealing with variables with axis in the order of lon/lat instead of lat/lon.

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