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

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