source: qesdi/geoplot/trunk/lib/geoplot/plot_base.py @ 5688

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/qesdi/geoplot/trunk/lib/geoplot/plot_base.py@5688
Revision 5688, 9.2 KB checked in by pnorton, 11 years ago (diff)

Improved the colour bar code so it now accepts the data array instead of just a minimum + maximum. It also now defaults to creating a Normalize instance with vmin=None and vmax=None instead of 0 and 1.

Also modified the layer_drawer objects to use the ColourBar? class instead of implementing it themselves.

Line 
1import logging
2
3from geoplot.colour_bar import ColourBar
4from geoplot.map_factory import MapFactory
5from geoplot.grid_factory import GridFactory
6from geoplot.plotarea import PlotArea
7from geoplot.plot_writer import PlotWriter
8from geoplot.metadata_box import MetadataBox
9
10from geoplot.config import getConfig
11import geoplot.utils as geoplot_utils 
12
13config = getConfig()
14log = logging.getLogger(__name__)
15
16_construtorDocString = """
17Constructor for the abstract class PlotBase, should be called by any class inheriting
18from PlotBase.
19
20@param cdmsVar:the CDMS variable being plotted
21@type cdmsVar:
22@keyword format:the format of the output file
23@type format: a string , one of ('jpg','pdf','ps','png')
24@keyword width:the width of the drawn image in pixels, the actual width of 'pdf' and 'ps'
25    files will be altered by the dpi.
26@type width: integer
27@keyword height:the height of the drawn map in pixels, the actual width of 'pdf' and 'ps'
28    files will be altered by the dpi.
29@type height: integer
30@keyword xLimits:a tuple representing the (longitude) limits of the map
31@type xLimits: a tuple of 2 floats, corresponding to (min, max)
32@keyword yLimits:a tuple representing the (latitude) limits of the map
33@type yLimits: a tuple of 2 floats , corresponding to (min, max)
34@keyword dip: the resolution of the produced image
35@type dpi: integer
36@keyword cmap: the colour map to be used to draw the grid
37@type cmap: an instance of matplotlib.colors.LinearSegmentedColormap
38
39@keyword drawColourBar: indicates if the colour bar should be drawn
40@type drawColourBar: boolean
41@keyword colourBarLabel: the label for the colour bar
42@type colourBarLabel: string
43@keyword colourBarPosition: the orientation and position the colour bar is drawn on
44    the plot. The default is chosen based on the lat-lon limits of the plot.
45@type colourBarPosition: string, one of ['horizontal', 'vertical']
46@keyword colourBarMax: the minimum value for the colour bar
47@type colourBarMax: float
48@keyword colourBarMin: the maximum value for the colour bar
49@type colourBarMin: float
50@keyword drawLogo: indicates wheather to draw the UKCIP08 logo
51@type drawLogo: boolean
52@keyword drawCoast: indicates if coaslines should be drawn
53@type drawCoast: boolean
54@keyword drawRivers: indicates if rivers should be drawn
55@type drawRivers: boolean
56@keyword plotTitle: the title of the plot
57@type plotTitle: string
58@keyword units: the units of the value being plotted
59@type units: string
60@keyword metadataList: the metadata to be displayed along with the plot, if None no
61    metadata box will be drawn
62@type metadataList: a list of (key, value) tuples. The keys and values should both be
63    strings.
64@keyword fontSize: a general indication of the size of the fonts used on the plot
65@type fontSize: a string, one of ["small","medium" ,"large", 'xl', 'xxl']
66@keyword units: the units the cdmsVar data is in, if no colourBarLabel is specified
67    the units will be used instead.
68@type units: string
69
70@keyword addShapefile: location of shapefile to overlay
71@type addShapefile: string
72"""
73
74class PlotBase(object):
75    '''
76    Creates a contour plot form the data stored in a netCDF file
77    '''
78   
79    _accessableObjects = ['_colourBar', '_plotArea', '_metadataBox', 
80                          '_plotWriter', '_mapFactory', '_gridFactory']
81   
82    def __init__(self, **kwargs):
83        # doc string a top of file
84       
85        self.units = kwargs.pop('units', None)
86       
87        self._colourBar   = ColourBar  (**self._getArgs(kwargs, ColourBar.__name__))
88       
89        self._plotArea    = PlotArea   (**self._getArgs(kwargs, PlotArea.__name__))
90       
91        self._metadataBox = MetadataBox(**self._getArgs(kwargs, MetadataBox.__name__))
92       
93        self._plotWriter  = PlotWriter (**self._getArgs(kwargs, PlotWriter.__name__))
94       
95        self._mapFactory  = MapFactory (**self._getArgs(kwargs, MapFactory.__name__))
96       
97        self._gridFactory = GridFactory(**self._getArgs(kwargs, GridFactory.__name__))
98       
99        if len(kwargs.keys()) > 0:
100            log.warning("Unused keyword arguments = %s" % (kwargs,))
101   
102    __init__.__doc__ = _construtorDocString
103     
104    def _getArgs(self, kwargs, className):
105        args = {}
106       
107        # all of the keyword arguments will have a value (even if it is None).
108        for k, defaultValue in config['Defaults'][className].items():
109           
110            # use the value from the keywords if one is there, otherwise use the
111            # default value
112            args[k] = kwargs.pop(k, defaultValue)
113               
114        return args
115
116    def drawMap(self, filename):
117        """
118        Outputs the map to the filename.
119
120        @param filename: the name of the file the image should be written to,
121            the extension of the filename given should match the intended format
122            of the image, i.e. 'test.jpg' for an image with format 'jpg'.
123        @type filename: string
124        """
125
126        log.info("Drawing map to " + filename)
127
128        self._plotWriter.checkFileExtension(filename)
129
130        self._drawToPlotArea()
131
132        self._plotWriter.writeFile(self._plotArea.figure, filename, self.drawLogo, self._plotArea.logoAxes)
133   
134    def _drawToPlotArea(self):
135       
136        self._setDynamicArgs()
137       
138        self._plotArea.setupArea(self.colourBarPosition)
139
140        map = self._mapFactory.buildMap()
141       
142        grid = self._gridFactory.getGrid(self.xLimits, self.yLimits)
143       
144        self._drawDiagram(map, grid, self._plotArea.axes)
145
146        #the map has to be drawn after the grid, otherwise it distorts the axis
147        map.drawMap(self._plotArea.axes)
148       
149        if self.drawMetadata:
150            self._metadataBox.draw(self._plotArea.metadataAxes, self.fontSize, self.height)
151
152        if self.drawColourBar:
153            self._colourBar.draw(self._plotArea.cbAxes, self.fontSize, 
154                                 data=grid.values)
155
156    def drawToImage(self):
157        if self.format not in 'png':
158            raise Exception("format %s not yet supported for image retrieval" % (self.format,))
159       
160        self._drawToPlotArea()
161       
162        im = geoplot_utils.figureToImage(self._plotArea.figure)
163       
164        return im       
165   
166    def drawToBuffer(self):
167        self._drawToPlotArea()
168        return self._plotWriter.printFigureToBuffer(self._plotArea.figure)
169       
170       
171       
172    def _drawDiagram(self, *args, **kwargs):
173        raise NotImplementedError()
174
175
176    def __getattr__(self, name):
177        """
178        If this object dosen't have the desired attribute check the public attributes
179        of some of the interal objects. This allows properties (e.g. colourBarMin) to
180        be accesed without having to care about exactly where it is stored.
181        """
182
183        if name[0] == '_':
184            raise AttributeError("Attribute %s not found on %s" % (name, self))
185       
186        obj = self._findPrivateObjWithAttribute(name)
187           
188        if obj != None:
189            return getattr(obj, name)
190       
191        raise AttributeError("Attribute %s not found on %s" % (name, self))
192           
193    def __setattr__(self, name, value):
194        """
195        Allow the setting of some attributes on internal objects, these
196        are the attributes set in the defaults section of the config file
197        """
198               
199        if name[0] == '_':
200            object.__setattr__(self, name, value)
201            return
202       
203        obj = self._findPrivateObjWithAttribute(name)
204       
205        if obj != None:
206            #if obj found set it on that object
207            setattr(obj, name, value)
208        else:
209            #set it on self instead
210            object.__setattr__(self, name, value)
211
212    def _findPrivateObjWithAttribute(self, name):
213       
214        objFoundIn = None
215        for objName in self.__class__._accessableObjects:
216           
217   
218            #just in case this is called during initialisation
219            if objName not in self.__dict__.keys():
220                continue
221
222            obj = self.__dict__[objName]
223           
224            #use the defaults as a guide to what can be set
225            if name in config['Defaults'][obj.__class__.__name__].keys():
226                if objFoundIn != None:
227                    raise Exception("Attribute %s found in both %s and %s" % (name, objFoundIn, obj))
228                else:
229                    objFoundIn = obj
230                   
231        return objFoundIn
232   
233    def _setDynamicArgs(self):
234       
235        self._setColourBarPosition()
236        self._setColourBarLabel()
237   
238    def _setColourBarPosition(self):
239       
240        #if the colourbar position isn't already set, use the limits to work out
241        #the best fit
242        if self.colourBarPosition == None:
243            xRange = self.xLimits[1] - self.xLimits[0]
244            yRange = self.yLimits[1] - self.yLimits[0]
245            #log.debug("xRange:" + str(xRange) + "yRange:" + str(yRange))
246            if xRange > yRange:
247                self.colourBarPosition = 'horizontal'
248            else:
249                self.colourBarPosition = 'vertical'
250#            log.debug('new colour bar position:' + kwargs['colourBarPosition'])
251
252    def _setColourBarLabel(self):
253        #if no colourBarLabel is provided use the units insted
254        if self.colourBarLabel==None : 
255            self.colourBarLabel = self.units
256               
Note: See TracBrowser for help on using the repository browser.