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

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

Added the ability to draw a plot directly to a string buffer rather than to a file. There are still some problems with this method, currently the logo isn't included and it cannot cope with 'jpg' outputs.

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                                 dataMin=grid.values.min(),
155                                 dataMax=grid.values.max())
156
157    def drawToImage(self):
158        if self.format not in 'png':
159            raise Exception("format %s not yet supported for image retrieval" % (self.format,))
160       
161        self._drawToPlotArea()
162       
163        im = geoplot_utils.figureToImage(self._plotArea.figure)
164       
165        return im       
166   
167    def drawToBuffer(self):
168        self._drawToPlotArea()
169        return self._plotWriter.printFigureToBuffer(self._plotArea.figure)
170       
171       
172       
173    def _drawDiagram(self, *args, **kwargs):
174        raise NotImplementedError()
175
176
177    def __getattr__(self, name):
178        """
179        If this object dosen't have the desired attribute check the public attributes
180        of some of the interal objects. This allows properties (e.g. colourBarMin) to
181        be accesed without having to care about exactly where it is stored.
182        """
183
184        if name[0] == '_':
185            raise AttributeError("Attribute %s not found on %s" % (name, self))
186       
187        obj = self._findPrivateObjWithAttribute(name)
188           
189        if obj != None:
190            return getattr(obj, name)
191       
192        raise AttributeError("Attribute %s not found on %s" % (name, self))
193           
194    def __setattr__(self, name, value):
195        """
196        Allow the setting of some attributes on internal objects, these
197        are the attributes set in the defaults section of the config file
198        """
199               
200        if name[0] == '_':
201            object.__setattr__(self, name, value)
202            return
203       
204        obj = self._findPrivateObjWithAttribute(name)
205       
206        if obj != None:
207            #if obj found set it on that object
208            setattr(obj, name, value)
209        else:
210            #set it on self instead
211            object.__setattr__(self, name, value)
212
213    def _findPrivateObjWithAttribute(self, name):
214       
215        objFoundIn = None
216        for objName in self.__class__._accessableObjects:
217           
218   
219            #just in case this is called during initialisation
220            if objName not in self.__dict__.keys():
221                continue
222
223            obj = self.__dict__[objName]
224           
225            #use the defaults as a guide to what can be set
226            if name in config['Defaults'][obj.__class__.__name__].keys():
227                if objFoundIn != None:
228                    raise Exception("Attribute %s found in both %s and %s" % (name, objFoundIn, obj))
229                else:
230                    objFoundIn = obj
231                   
232        return objFoundIn
233   
234    def _setDynamicArgs(self):
235       
236        self._setColourBarPosition()
237        self._setColourBarLabel()
238   
239    def _setColourBarPosition(self):
240       
241        #if the colourbar position isn't already set, use the limits to work out
242        #the best fit
243        if self.colourBarPosition == None:
244            xRange = self.xLimits[1] - self.xLimits[0]
245            yRange = self.yLimits[1] - self.yLimits[0]
246            #log.debug("xRange:" + str(xRange) + "yRange:" + str(yRange))
247            if xRange > yRange:
248                self.colourBarPosition = 'horizontal'
249            else:
250                self.colourBarPosition = 'vertical'
251#            log.debug('new colour bar position:' + kwargs['colourBarPosition'])
252
253    def _setColourBarLabel(self):
254        #if no colourBarLabel is provided use the units insted
255        if self.colourBarLabel==None : 
256            self.colourBarLabel = self.units
257               
Note: See TracBrowser for help on using the repository browser.