1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
from .variable import Variable
# ====================================================================
#
# CoordinateBounds object
#
# ====================================================================
class CoordinateBounds(Variable):
'''
A CF coordinate's bounds object containing cell boundaries or
intervals of climatological time. The parent coordinate's
`!climatology` attribute indicates which type of bounds are present.
'''
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def lower_bounds(self):
'''
The lower coordinate bounds in a `cf.Data` object.
``b.lower_bounds`` is equivalent to ``b.data.min(axes=-1)``.
.. seealso:: `upper_bounds`
:Examples:
>>> print b.array
[[ 5 3]
[ 3 1]
[ 1 -1]]
>>> b.lower_bounds
<CF Data: [3, ..., -1]>
>>> print b.lower_bounds.array
[ 3 1 -1]
'''
if not self._hasData:
raise ValueError("Can't get lower bounds when there are no bounds")
return self.data.min(-1).squeeze(-1, i=True)
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def upper_bounds(self):
'''
The upper coordinate bounds in a `cf.Data` object.
``b.upper_bounds`` is equivalent to ``b.data.max(axes=-1)``.
.. seealso:: `lower_bounds`
:Examples:
>>> print b.array
[[ 5 3]
[ 3 1]
[ 1 -1]]
>>> b.upper_bounds
<CF Data: [5, ..., 1]>
>>> b.upper_bounds.array
array([5, 3, 1])
'''
if not self._hasData:
raise ValueError("Can't get upper bounds when there are no bounds")
return self.data.max(-1).squeeze(-1, i=True)
#--- End: def
def contiguous(self, overlap=True, direction=None):
'''
Return True if the bounds are contiguous.
Bounds are contiguous if the cell boundaries match up, or
overlap, with the boundaries of adjacent cells.
In general, it is only possible for 1 or 0 dimensional coordinates
with bounds to be contiguous, but size 1 coordinates with any number
of dimensions are always contiguous.
An exception occurs if the coordinate is multdimensional and has more
than one element.
'''
if not self._hasData:
return False
nbounds = self.shape[-1]
if self.size == nbounds:
return True
if nbounds == 4 and self.ndim ==3:
if overlap == True:
raise ValueError("Cannot tell if 2D coordinate bounds are" +
" contiguous if overlap is True.")
bnd = self.array
for j in xrange(self.shape[0] - 1):
for i in xrange(self.shape[1] - 1):
# check cells (j, i) and cells (j, i+1) are contiguous
if bnd[j,i,1] != bnd[j,i+1,0] or \
bnd[j,i,2] != bnd[j,i+1,3]:
return False
# check cells (j, i) and (j+1, i) are contiguous
if bnd[j,i,3] != bnd[j+1,i,0] or \
bnd[j,i,2] != bnd[j+1,i,1]:
return False
return True
if nbounds > 2 or self.ndim > 2:
raise ValueError(
"Can't tell if a multidimensional coordinate bounds are contiguous")
data = self.Data
if not overlap:
return data[1:, 0].equals(data[:-1, 1])
else:
if direction is None:
b = data[(0,)*(data.ndim-1)].array
direction = b.item(0,) < b.item(1,)
#--- End: if
if direction:
return (data[1:, 0] <= data[:-1, 1]).all()
else:
return (data[1:, 0] >= data[:-1, 1]).all()
#--- End: def
#--- End: class
|