source: qesdi/geoplot/trunk/lib/geoplot/grid_builder_base.py @ 5403

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/qesdi/geoplot/trunk/lib/geoplot/grid_builder_base.py@5403
Revision 5403, 12.4 KB checked in by pnorton, 12 years ago (diff)

Moved QESDI tree from DCIP repository
 http://proj.badc.rl.ac.uk/svn/dcip/qesdi@3900.

Line 
1"""
2grid_builder_base.py
3====================
4
5Holds the GridBuilderBase class. A GridBuilder is an object that knows how to
6extract a lat-lon Grid object from a given cdmsVariable. This is an abstract
7baseclass and can't be used directly.
8
9"""
10
11#python modules
12import logging
13
14#third party modules
15
16#internal modules
17from geoplot.grid import Grid
18import cdms2 as cdms
19
20import numpy as N
21import numpy.ma as MA
22
23#set the log
24log = logging.getLogger(__name__)
25log.setLevel(logging.DEBUG)
26
27class GridBuilderBase(object):
28    """
29    Impliments the common GridBuilder functionality.
30
31    This is an abstract class. Any methods that iherit from this class need to
32    impliment the _resizeVar, _buildGridBounds, _buildGridMidpoints and the
33    _buildGridValues methods.
34    """
35
36    def __init__(self, cdmsVar):
37        """
38        constructs the grid builder object
39       
40        @param cdmsVar: the cdms variable that conains the grid data.
41        @type cdmsVar:a cdms.variable object 
42        """
43       
44        self.cdmsVar = cdmsVar
45        self._checkVariable()
46       
47#        for axis in self.cdmsVar.getAxisList():
48#            _logAxis(axis)
49
50    def buildGrid(self, xLimits=None, yLimits=None):
51        """
52        builds a grid object from the data in the cdmsVar ascociated with the
53        grid builder object.
54       
55        If xLimits or yLimits values are given then the resulting grid will
56        contain the portion of the data in the cdms variable that falls within
57        the limits given.
58       
59        @keyword xLimits: (optional) longitude limits of the resulting grid
60        @type xLimits: a tuple of (MinLongitude, MaxLongitude)
61        @keyword yLimits: (optional) latitude limits of the resulting grid
62        @type yLimits: a tuple of (MinLatitude, MaxLatitude)
63        @return: a grid built using the cdms variable data
64        @rtype: geoplot.Grid
65        """
66
67#        log.debug("building grid with cdms variable id = %s" % (self.cdmsVar.id,))
68#        log.debug("self.cdmsVar.getAxisIds() = %s" % (self.cdmsVar.getAxisIds(),))
69#        log.debug("self.cdmsVar.shape = %s" % (self.cdmsVar.shape,))
70
71        xmid, ymid = self._buildGridMidpoints(self.cdmsVar)
72       
73#        log.debug("y midpoints min =[" + str(ymid.min()) + \
74#                "] max =[" + str(ymid.max()) + "]")
75#        log.debug("x midpoints min =[" + str(xmid.min()) + \
76#                "] max =[" + str(xmid.max()) + "]")
77#       
78        reducedVar = self._getResizedVar(xLimits, yLimits)
79           
80        (gridBoundsX, gridBoundsY) = self._buildGridBounds(reducedVar)
81        (gridMidpointX, gridMidpointY) = self._buildGridMidpoints(reducedVar)
82
83        gridValues = self._buildGridValues(reducedVar)
84               
85#        log.debug("After resize:")
86#        log.debug("y midpoints min =[" + str(gridMidpointY.min()) + \
87#                "] max =[" + str(gridMidpointY.max()) + "]")
88#        log.debug("x midpoints min =[" + str(gridMidpointX.min()) + \
89#                "] max =[" + str(gridMidpointX.max()) + "]")
90#       
91#        log.debug("Diff: y [" + str(gridMidpointY.min() - ymid.min()) + \
92#                  "][" + str(gridMidpointY.max() - ymid.max()) )
93#        log.debug("Diff: x [" + str(gridMidpointX.min() - xmid.min()) + \
94#                  "][" + str(gridMidpointX.max() - xmid.max()) +"]")
95       
96
97#        log.debug("gridMidpointX = %s" % (gridMidpointX,))
98#        log.debug("gridMidpointY = %s" % (gridMidpointY,))
99#        log.debug("gridBoundsX = %s" % (gridBoundsX,))
100#        log.debug("gridBoundsY = %s" % (gridBoundsY,))
101
102        return Grid(gridBoundsX, gridBoundsY, gridMidpointX, gridMidpointY, gridValues)
103
104    def _getResizedVar(self, xLimits, yLimits):
105        """
106        Replaces any None's in the limits with the max/min midpoint values then
107        returns the reduced variable from self.resizeVar().
108       
109        If both of these limits are None then the original self.cdmsVar will be
110        returned. If any part of these limits is None then it will be replaced
111        with the maximum or minimum midpoint value from the self.cdms variable.
112       
113        After filling any Nones in the limits this method calls the
114        self._resizeVar method to do the resizing.
115       
116        @param xLimits: (optional) longitude limits of the resulting grid
117        @type xLimits: a tuple of (MinLongitude, MaxLongitude)
118        @param yLimits: (optional) latitude limits of the resulting grid
119        @type yLimits: a tuple of (MinLatitude, MaxLatitude)
120        @return: A variable containing the subset of data
121        @rtype: cdms.variable
122        """
123       
124        if xLimits == None: xLimits = (None, None)
125        if yLimits == None: yLimits = (None, None)
126       
127        if xLimits == (None, None) and yLimits == (None, None):
128            reducedVar = self.cdmsVar
129        else:
130            if None in xLimits or None in yLimits:
131                xLimits, yLimits = self._replaceNoneInLimitsWithMaxMin(xLimits, yLimits)
132           
133#            log.debug("limits:" + str(xLimits) + ", " + str(yLimits))
134            reducedVar = self._resizeVar(xLimits, yLimits)
135           
136        return reducedVar
137       
138    def _resizeVar(self, xLimits, yLimits):
139        """
140        Returns a cdms variable that contains the subset of self.cdmsVar that is between
141        the limits.
142       
143        @param xLimits: (optional) longitude limits of the resulting grid
144        @type xLimits: a tuple of (MinLongitude, MaxLongitude)
145        @param yLimits: (optional) latitude limits of the resulting grid
146        @type yLimits: a tuple of (MinLatitude, MaxLatitude)
147        @return: A variable containing the subset of data
148        @rtype: cdms.variable
149        """
150        raise NotImplementedError
151
152    def _buildGridBounds(self, cdmsVar):
153        """
154        Builds two 2d Numpy array of the x and y positions of the grid
155        boundaries.
156       
157        Returns one array for the x bounds and one for the y bound. These
158        arrays contain the lower boundary for a given grid box.
159       
160        e.g. xBounds[x,y] will give the lower x boundary for the gridbox x,y.
161       
162        @param cdmsVar: the variable to extract the boundry data from
163        @type cdmsVar: cdms.variable
164        @return: the position data as (xPositions, yPositions)
165        @rtype: 2 Numpy Arrays
166       
167        """
168        raise NotImplementedError
169
170    def _buildGridMidpoints(self, cdmsVar):
171        """
172        Builds two 2d Numpy arrays for the x and y midpoints of a given grid
173        box, this position corresponds to the location of the grid box value.
174       
175        Returns one array for the midpoint x position and another for the y
176        postion.
177       
178        e.g. xMidpoints[x,y] will give the midpoint of the gridbox x,y and the
179        position of the ascociated measurment (value[x,y]).
180       
181        @param cdmsVar: the variable to extract the midpoint data from
182        @type cdmsVar: cdms.variable
183        @return: the midpoint data  as (xMidpoints, yMidpoints)
184        @rtype: 2 Numpy Arrays
185        """
186        raise NotImplementedError
187
188    def _buildGridValues(self,cdmsVar):
189        """
190        Builds a numpy array of values for each of the grid boxes.
191       
192        e.g. values[x,y] is the value for grid box x,y.
193       
194        @param cdmsVar: the variable to extract the values from
195        @type cdmsVar: cdms.variable
196        @return: the value data
197        @rtype: numpy.array
198        """
199        raise NotImplementedError
200   
201    @staticmethod
202    def _getBoundsFromAxis(axis):
203        """
204        returns the bounding grid for the values in an axis, These bounds will
205        be retrieved form the cdms variable if present or generated.
206
207        The bounds array is
208
209        @param axis: the axis to get bounds for
210        @type axis: cdms.axis.Axis
211        @return: the axis bounds
212        @rtype: numpy.aarray
213        """
214        if axis.getBounds() == None:
215            axisBounds = GridBuilderBase._createBoundsFormList(axis.getValue())
216        else:
217            axisBounds = GridBuilderBase._mergeBounds(axis.getBounds())
218
219        return axisBounds
220   
221    @staticmethod
222    def _createBoundsFormList(values):
223        """
224        Creates a list of boundries from a given list of vlaues.
225
226        These bounding values are created by halfing the distance between the first
227        two items in the list and then adding this value to every other item in the list.
228        The first bounding value is the first item in the list minus this shift.
229
230        @params values: a list of values to create bounds from
231        @type values: a list of int or float
232        """
233        bounds = []
234        shift = (values[1] - values[0])/2
235        bounds.append(values[0] - shift)
236        for item in values:
237            bounds.append(item + shift)
238
239        return N.array(bounds)
240
241    @staticmethod
242    def _mergeBounds(bounds):
243        """
244        Folds a bounds array of shape (x, 2) into a 1D array of shape (x + 1,).
245   
246        We assume that grid boxes are contiguous.  I.e. the
247        right-hand edge of grid box (x, y) is the same as the left-hand
248        edge of grid box (x + 1, y) and similarly in y.
249   
250        @param lonBounds: The longitude bounds array
251        @param latBounds: the latitude bounds array
252        """
253   
254        # Get grid dimensions
255        n = bounds.shape[0]
256   
257        # Take the lower bounds as the mesh point
258        # except for the last index where the upper bounds is taken
259        merged = N.resize(bounds[:, 0], (n + 1,))
260        merged[-1] = bounds[-1, 1]
261   
262        return merged
263   
264   
265   
266   
267    @staticmethod
268    def _fillMissingLimitsFromArray(limits, array):
269       
270        newLimits = [limits[0], limits[1]]
271
272        if newLimits[0] == None:
273            newLimits[0] = array.min()
274           
275        if newLimits[1] == None:
276            newLimits[1] = array.max()
277
278        return tuple(newLimits)
279
280    def _replaceNoneInLimitsWithMaxMin(self, xLimits, yLimits):
281       
282        (newXLimits, newYLimits) = (xLimits, yLimits)
283        xBounds, yBounds = self._buildGridMidpoints(self.cdmsVar)
284        #log.debug("bounds" + str(yBounds))
285       
286        if None in xLimits:
287            axis = self.cdmsVar.getAxisList()[1]
288            newXLimits = GridBuilderBase._fillMissingLimitsFromArray(xLimits, xBounds)
289           
290        if None in yLimits:
291            axis = self.cdmsVar.getAxisList()[0]
292            newYLimits = GridBuilderBase._fillMissingLimitsFromArray(yLimits, yBounds)
293       
294        return (newXLimits, newYLimits)
295
296    def _checkVariable(self):
297        """
298        checks the cdms variable to make sure that it is suitable for plotting
299        """
300
301        self._checkVariableAxis()
302
303    def _checkVariableAxis(self):
304        """
305        Checks the axis on a given variable, if axes that arn't expected are
306        found then a warning is written to the log.
307        """
308
309        if len(self.cdmsVar.getAxisList()) > 2:
310            log.warning('cdms variable contains ' \
311                        +str(len(self.cdmsVar.getAxisList())) + ' axes.')
312
313        if self.cdmsVar.getTime() != None:
314            log.warning('cdms variable contains a time axes.')
315            #firstTime = self.cdmsVar.getTime().getValue()[0]
316            #self.cdmsVar = self.cdmsVar(time = firstTime, squeeze = 1)
317            #log.warning('using first time variable of ' + str(firstTime))
318
319        if self.cdmsVar.getLevel() != None:
320            log.warning('cdms variable contains a level axes.')
321            #firstLevel = self.cdmsVar.getLevel().getValue()[0]
322            #self.cdmsVar = self.cdmsVar(level = float(firstLevel), squeeze = 1)
323            #log.warning('using first level variable of ' + str(firstLevel))
324
325    def _buildGridValues(self,cdmsVar):
326        """
327        Builds a numpy array of values for each of the grid boxes.
328       
329        e.g. values[x,y] is the value for grid box x,y.
330        """
331        data = cdmsVar.getValue()
332        missing = cdmsVar.getMissing()
333       
334        if missing == None:
335            missing = 1e20
336       
337        return MA.masked_values(data, missing)
338       
339def _logAxis(axis):
340    """
341    A function that writes the details of a particular axis to log.debug.
342    """
343
344    if axis.isLongitude() :
345        msg = 'Longitude Axis'
346    elif axis.isLatitude():
347        msg = 'Latitude Axis'
348    elif axis.isLevel():
349        msg = 'Level Axis'
350    elif axis.isTime():
351        msg = 'Time Axis'
352    else:
353        msg = 'Unknown axis'
354
355    log.debug(msg + ' ' + str(axis.id) + '(' + str(len(axis.getValue())) +') :' +\
356                str(axis.getValue()[0]) + ' - ' + str(axis.getValue()[-1]))
Note: See TracBrowser for help on using the repository browser.