2021-03-04 23:26:35 +08:00

1578 lines
53 KiB
Python

#-----------------------------------------------------
# Author: Yaqiang Wang
# Date: 2014-12-27
# Purpose: MeteoInfo dimarray module
# Note: Jython
#-----------------------------------------------------
from org.meteoinfo.projection import KnownCoordinateSystems, Reproject
from org.meteoinfo.data import GridData, GridArray
from org.meteoinfo.math import ArrayMath, ArrayUtil
from org.meteoinfo.geoprocess import GeometryUtil
from org.meteoinfo.geoprocess.analysis import ResampleMethods
from org.meteoinfo.common import PointD
from org.meteoinfo.ndarray import Array, Range, MAMath, DataType, Dimension, DimensionType
from multiarray import NDArray
import math
import datetime
import numbers
import mipylib.miutil as miutil
from java.lang import Double
from java.util import ArrayList
nan = Double.NaN
def dimension(value, name='null', type=None):
"""
Create a new Dimension.
:param value: (*array_like*) Dimension value.
:param name: (*string*) Dimension name.
:param type: (*string*) Dimension type ['X' | 'Y' | 'Z' | 'T'].
"""
if isinstance(value, NDArray):
value = value.aslist()
dtype = DimensionType.Other
if not type is None:
if type.upper() == 'X':
dtype = DimensionType.X
elif type.upper() == 'Y':
dtype = DimensionType.Y
elif type.upper() == 'Z':
dtype = DimensionType.Z
elif type.upper() == 'T':
dtype = DimensionType.T
dim = Dimension(dtype)
dim.setDimValues(value)
dim.setShortName(name)
return dim
# Dimension array
class DimArray(NDArray):
def __init__(self, array, dims=None, fill_value=-9999.0, proj=None):
if isinstance(array, NDArray):
array = array._array
super(DimArray, self).__init__(array)
self.dims = None
if not dims is None:
for dim in dims:
self.adddim(dim)
self.fill_value = fill_value
if math.isnan(self.fill_value):
self.fill_value = -9999.0
self.proj = proj
def __getitem__(self, indices):
if not isinstance(indices, tuple):
inds = []
inds.append(indices)
indices = inds
if len(indices) < self.ndim:
if isinstance(indices, tuple):
indices = list(indices)
for i in range(self.ndim - len(indices)):
indices.append(slice(None))
allint = True
aindex = self._array.getIndex()
i = 0
for ii in indices:
if isinstance(ii, int):
if ii < 0:
ii = self.shape[i] + ii
aindex.setDim(i, ii)
else:
allint = False
break
i += 1
if allint:
return self._array.getObject(aindex)
newaxis = []
if len(indices) > self.ndim:
nindices = []
i = 0
for ii in indices:
if ii is None:
newaxis.append(i)
else:
nindices.append(ii)
i += 1
indices = nindices
if len(indices) != self.ndim:
print 'indices must be ' + str(self.ndim) + ' dimensions!'
raise IndexError()
if not self.proj is None and not self.proj.isLonLat():
xlim = None
ylim = None
xidx = -1
yidx = -1
for i in range(0, self.ndim):
dim = self.dims[i]
if dim.getDimType() == DimensionType.X:
k = indices[i]
#if isinstance(k, (tuple, list)):
if isinstance(k, basestring):
xlims = k.split(':')
xlim = [float(xlims[0]), float(xlims[1])]
xidx = i
elif dim.getDimType() == DimensionType.Y:
k = indices[i]
#if isinstance(k, (tuple, list)):
if isinstance(k, basestring):
ylims = k.split(':')
ylim = [float(ylims[0]), float(ylims[1])]
yidx = i
if not xlim is None and not ylim is None:
fromproj=KnownCoordinateSystems.geographic.world.WGS1984
inpt = PointD(xlim[0], ylim[0])
outpt1 = Reproject.reprojectPoint(inpt, fromproj, self.proj)
inpt = PointD(xlim[1], ylim[1])
outpt2 = Reproject.reprojectPoint(inpt, fromproj, self.proj)
xlim = [outpt1.X, outpt2.X]
ylim = [outpt1.Y, outpt2.Y]
indices1 = []
for i in range(0, self.ndim):
if i == xidx:
#indices1.append(xlim
indices1.append(str(xlim[0]) + ':' + str(xlim[1]))
elif i == yidx:
#indices1.append(ylim)
indices1.append(str(ylim[0]) + ':' + str(ylim[1]))
else:
indices1.append(indices[i])
indices = indices1
ndims = []
ranges = []
flips = []
iszerodim = True
onlyrange = True
alllist = True
isempty = False
nshape = []
for i in range(0, self.ndim):
isrange = True
k = indices[i]
if isinstance(k, int):
if k < 0:
k = self.dims[i].getLength() + k
sidx = k
eidx = k
step = 1
alllist = False
elif isinstance(k, slice):
if isinstance(k.start, basestring):
sv = float(k.start)
sidx = self.dims[i].getValueIndex(sv)
elif isinstance(k.start, datetime.datetime):
sv = miutil.date2num(k.start)
sidx = self.dims[i].getValueIndex(sv)
else:
sidx = 0 if k.start is None else k.start
if sidx < 0:
sidx = self.dims[i].getLength() + sidx
if isinstance(k.stop, basestring):
ev = float(k.stop)
eidx = self.dims[i].getValueIndex(ev)
elif isinstance(k.stop, datetime.datetime):
ev = miutil.date2num(k.stop)
eidx = self.dims[i].getValueIndex(ev)
else:
eidx = self.dims[i].getLength() if k.stop is None else k.stop
if eidx < 0:
eidx = self.dims[i].getLength() + eidx
eidx -= 1
if isinstance(k.step, basestring):
nv = float(k.step) + self.dims[i].getDimValue()[0]
nidx = self.dims[i].getValueIndex(nv)
step = nidx - sidx
elif isinstance(k.step, datetime.timedelta):
nv = miutil.date2num(k.start + k.step)
nidx = self.dims[i].getValueIndex(nv)
step = nidx - sidx
else:
step = 1 if k.step is None else k.step
alllist = False
elif isinstance(k, (list, tuple, NDArray)):
onlyrange = False
isrange = False
if not isinstance(k[0], datetime.datetime):
if isinstance(k, NDArray):
k = k.aslist()
ranges.append(k)
else:
tlist = []
for tt in k:
sv = miutil.date2num(tt)
idx = self.dims[i].getValueIndex(sv)
tlist.append(idx)
ranges.append(tlist)
k = tlist
elif isinstance(k, basestring):
dim = self.dims[i]
kvalues = k.split(':')
sidx = dim.getValueIndex(float(kvalues[0]))
if len(kvalues) == 1:
eidx = sidx
step = 1
else:
eidx = dim.getValueIndex(float(kvalues[1]))
if len(kvalues) == 2:
step = 1
else:
step = int(float(kvalues[2]) / dim.getDeltaValue())
if sidx > eidx and step > 0:
step = -step
alllist = False
else:
print k
raise IndexError()
if isrange:
if sidx >= self.shape[i] or eidx >= self.shape[i]:
raise IndexError()
n = abs(eidx - sidx) + 1
if n > 1:
dim = self.dims[i]
ndims.append(dim.extract(sidx, eidx, step))
if step < 0:
#step = abs(step)
flips.append(i)
if sidx > eidx:
iidx = eidx
eidx = sidx
sidx = iidx
rr = Range(sidx, eidx, abs(step))
ranges.append(rr)
nshape.append(eidx - sidx + 1 if eidx - sidx >= 0 else 0)
else:
if len(k) > 1:
dim = self.dims[i]
ndims.append(dim.extract(k))
if isempty:
r = ArrayUtil.zeros(nshape, 'int')
return NDArray(r)
if onlyrange:
r = ArrayMath.section(self._array, ranges)
else:
if alllist:
r = ArrayMath.takeValues(self._array, ranges)
return NDArray(r)
else:
r = ArrayMath.take(self._array, ranges)
if newaxis:
for i in flips:
r = r.flip(i)
rr = Array.factory(r.getDataType(), r.getShape())
MAMath.copy(rr, r)
rr = NDArray(rr)
newshape = list(rr.shape)
for i in newaxis:
newshape.insert(i, 1)
rr = rr.reshape(newshape)
if onlyrange:
rr.base = self.get_base()
return rr
if r.getSize() == 1:
iter = r.getIndexIterator()
return iter.getObjectNext()
else:
for i in flips:
r = r.flip(i)
data = DimArray(r, ndims, self.fill_value, self.proj)
if onlyrange:
data.base = self.get_base()
return data
def __add__(self, other):
r = super(DimArray, self).__add__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __radd__(self, other):
return DimArray.__add__(self, other)
def __sub__(self, other):
r = super(DimArray, self).__sub__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __rsub__(self, other):
r = super(DimArray, self).__rsub__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __mul__(self, other):
r = super(DimArray, self).__mul__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __rmul__(self, other):
return DimArray.__mul__(self, other)
def __div__(self, other):
r = super(DimArray, self).__div__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __rdiv__(self, other):
r = super(DimArray, self).__rdiv__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __pow__(self, other):
r = super(DimArray, self).__pow__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __rpow__(self, other):
r = super(DimArray, self).__rpow__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __neg__(self):
r = super(DimArray, self).__neg__()
return DimArray(r, self.dims, self.fill_value, self.proj)
def __lt__(self, other):
r = super(DimArray, self).__lt__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __le__(self, other):
r = super(DimArray, self).__le__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __eq__(self, other):
r = super(DimArray, self).__eq__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __ne__(self, other):
r = super(DimArray, self).__ne__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __gt__(self, other):
r = super(DimArray, self).__gt__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __ge__(self, other):
r = super(DimArray, self).__ge__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __and__(self, other):
r = super(DimArray, self).__and__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __or__(self, other):
r = super(DimArray, self).__or__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __xor__(self, other):
r = super(DimArray, self).__xor__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __invert__(self, other):
r = super(DimArray, self).__invert__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __lshift__(self, other):
r = super(DimArray, self).__lshift__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def __rshift__(self, other):
r = super(DimArray, self).__rshift__(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def member_names(self):
'''
Get member names. Only valid for Structure data type.
:returns: (*list*) Member names
'''
if self._array.getDataType() != DataType.STRUCTURE:
print 'This method is only valid for structure array!'
return None
ms = self._array.getStructureMemberNames()
return list(ms)
def member_array(self, member, indices=None):
'''
Extract member array. Only valid for Structure data type.
:param member: (*string*) Member name.
:param indices: (*slice*) Indices.
:returns: (*array*) Extracted member array.
'''
if self._array.getDataType() != DataType.STRUCTURE:
print 'This method is only valid for structure array!'
return None
a = self._array.getArrayObject()
m = a.findMember(member)
if m is None:
raise KeyError('The member %s not exists!' % member)
a = a.extractMemberArray(m)
r = DimArray(a, self.dims, self.fill_value, self.proj)
if not indices is None:
r = r.__getitem__(indices)
return r
def in_values(self, other):
'''
The returned array element set 1 when the input array element is in other, otherwise the
element set 0.
:param other: (*array_like*) The array or list value.
:returns: (*array*) The array with element value of 1 or 0.
'''
r = super(DimArray, self).in_values(other)
return DimArray(r, self.dims, self.fill_value, self.proj)
def astype(self, dtype):
'''
Convert to another data type.
:param dtype: (*string*) Data type.
:returns: (*array*) Converted array.
'''
r = super(DimArray, self).astype(dtype)
return DimArray(r, self.dims, self.fill_value, self.proj)
def value(self, indices):
#print type(indices)
if not isinstance(indices, tuple):
inds = []
inds.append(indices)
indices = inds
if len(indices) != self.ndim:
print 'indices must be ' + str(self.ndim) + ' dimensions!'
return None
#origin = []
#size = []
#stride = []
dims = []
ranges = []
flips = []
for i in range(0, self.ndim):
k = indices[i]
if isinstance(indices[i], int):
sidx = k
eidx = k
step = 1
elif isinstance(k, slice):
sidx = 0 if k.start is None else k.start
eidx = self.dims[i].getLength()-1 if k.stop is None else k.stop
step = 1 if k.step is None else k.step
elif isinstance(k, tuple) or isinstance(k, list):
dim = self.dims[i]
sidx = dim.getValueIndex(k[0])
if len(k) == 1:
eidx = sidx
step = 1
else:
eidx = dim.getValueIndex(k[1])
if len(k) == 2:
step = 1
else:
step = int(k[2] / dim.getDeltaValue)
else:
print k
return None
if step < 0:
step = abs(step)
flips.append(i)
rr = Range(sidx, eidx, step)
ranges.append(rr)
#origin.append(sidx)
n = eidx - sidx + 1
#size.append(n)
#stride.append(step)
if n > 1:
dim = self.dims[i]
dims.append(dim.extract(sidx, eidx, step))
#r = ArrayMath.section(self._array, origin, size, stride)
r = ArrayMath.section(self._array, ranges)
for i in flips:
r = r.flip(i)
rr = Array.factory(r.getDataType(), r.getShape());
MAMath.copy(rr, r);
array = NDArray(rr)
data = DimArray(array, dims, self.fill_value, self.proj)
return data
def copy(self):
'''
Copy array vlaues to a new array.
'''
return DimArray(self._array.copy(), self.dims, self.fill_value, self.proj)
# get dimension length
def dimlen(self, idx=0):
'''
Get dimension length.
:param idx: (*int*) Dimension index.
:returns: (*int*) Dimension length.
'''
return self.dims[idx].getLength()
def dimvalue(self, idx=0, convert=False):
'''
Get dimension values.
:param idx: (*int*) Dimension index.
:param convert: (*boolean*) If convert to real values (i.e. datetime). Default
is ``False``.
:returns: (*array_like*) Dimension values
'''
dim = self.dims[idx]
if convert:
if dim.getDimType() == DimensionType.T:
return miutil.nums2dates(dim.getDimValue())
else:
return NDArray(ArrayUtil.array(self.dims[idx].getDimValue()))
else:
return NDArray(ArrayUtil.array(self.dims[idx].getDimValue()))
def setdimvalue(self, idx, dimvalue):
'''
Set dimension value.
:param idx: (*int*) Dimension index.
:param dimvalue: (*array_like*) Dimension value.
'''
if isinstance(dimvalue, NDArray):
dimvalue = dimvalue.aslist()
self.dims[idx].setDimValues(dimvalue)
def setdimtype(self, idx, dimtype):
'''
Set dimension type.
:param idx: (*int*) Dimension index.
:param dimtype: (*string*) Dimension type. [X | Y | Z | T].
'''
dtype = DimensionType.Other
if dimtype.upper() == 'X':
dtype = DimensionType.X
elif dimtype.upper() == 'Y':
dtype = DimensionType.Y
elif dimtype.upper() == 'Z':
dtype = DimensionType.Z
elif dimtype.upper() == 'T':
dtype = DimensionType.T
self.dims[idx].setDimType(dtype)
def adddim(self, dimvalue, dimtype=None, index=None):
'''
Add a dimension.
:param dimvalue: (*array_like*) Dimension value.
:param dimtype: (*string*) Dimension type.
:param index: (*int*) Index to be inserted.
'''
if isinstance(dimvalue, Dimension):
dim = dimvalue
else:
if isinstance(dimvalue, (NDArray, DimArray)):
dimvalue = dimvalue.aslist()
dtype = DimensionType.Other
if not dimtype is None:
if dimtype.upper() == 'X':
dtype = DimensionType.X
elif dimtype.upper() == 'Y':
dtype = DimensionType.Y
elif dimtype.upper() == 'Z':
dtype = DimensionType.Z
elif dimtype.upper() == 'T':
dtype = DimensionType.T
dim = Dimension(dtype)
dim.setDimValues(dimvalue)
if self.dims is None:
self.dims = [dim]
else:
if index is None:
self.dims.append(dim)
else:
self.dims.insert(index, dim)
self.ndim = len(self.dims)
def addtdim(self, t):
'''
Add a time dimension as first dimension.
:param t: (*array_like*) datetime array.
'''
if self.tdim() is None:
dim = Dimension(DimensionType.T)
t = miutil.date2num(t)
dim.setDimValues([t])
if self.dims is None:
self.dims = [dim]
else:
self.dims.insert(0, dim)
self.ndim = len(self.dims)
ss = list(self.shape)
ss.insert(0, 1)
ss = tuple(ss)
self._array = self._array.reshape(ss)
#self.shape = self._array.shape
def xdim(self):
'''
Get x dimension.
'''
for dim in self.dims:
if dim.getDimType() == DimensionType.X:
return dim
return None
def ydim(self):
'''
Get y dimension.
'''
for dim in self.dims:
if dim.getDimType() == DimensionType.Y:
return dim
return None
def zdim(self):
'''
Get z dimension.
'''
for dim in self.dims:
if dim.getDimType() == DimensionType.Z:
return dim
return None
def tdim(self):
'''
Get time dimension.
'''
if self.dims is None:
return None
for dim in self.dims:
if dim.getDimType() == DimensionType.T:
return dim
return None
def islondim(self, idx=0):
'''
Check a dimension is a longitude dimension or not.
:param idx: (*int*) Dimension index.
'''
dim = self.dims[idx]
if dim.getDimType() == DimensionType.X and self.proj.isLonLat():
return True
else:
return False
def islatdim(self, idx=0):
'''
Check a dimension is a latitude dimension or not.
:param idx: (*int*) Dimension index.
'''
dim = self.dims[idx]
if dim.getDimType() == DimensionType.Y and self.proj.isLonLat():
return True
else:
return False
def islonlatdim(self, idx=0):
'''
Check a dimension is a longitude or latitude dimension or not.
:param idx: (*int*) Dimension index.
'''
return self.islondim(idx) or self.islatdim(idx)
def istimedim(self, idx=0):
'''
Check a dimension is a time dimension or not.
:param idx: (*int*) Dimension index.
'''
dim = self.dims[idx]
if dim.getDimType() == DimensionType.T:
return True
else:
return False
def asgriddata(self):
xdata = self.dims[1].getDimValue()
ydata = self.dims[0].getDimValue()
gdata = GridData(self._array, xdata, ydata, self.fill_value, self.proj)
return PyGridData(gdata)
def asgridarray(self):
xdata = self.dims[1].getDimValue()
ydata = self.dims[0].getDimValue()
gdata = GridArray(self._array, xdata, ydata, self.fill_value, self.proj)
return gdata
def sum(self, axis=None):
'''
Sum of array elements over a given axis.
:param axis: (*int*) Axis along which the standard deviation is computed.
The default is to compute the standard deviation of the flattened array.
returns: (*array_like*) Sum result
'''
r = super(DimArray, self).sum(axis)
if axis is None:
return r
else:
dims = []
for i in range(0, self.ndim):
if i != axis:
dims.append(self.dims[i])
return DimArray(r, dims, self.fill_value, self.proj)
def mean(self, axis=None):
'''
Compute tha arithmetic mean along the specified axis.
:param axis: (*int*) Axis along which the standard deviation is computed.
The default is to compute the standard deviation of the flattened array.
returns: (*array_like*) Mean result
'''
r = super(DimArray, self).mean(axis)
if isinstance(r, numbers.Number):
return r
else:
dims = []
for i in range(0, self.ndim):
if isinstance(axis, (list, tuple)):
if not i in axis:
dims.append(self.dims[i])
else:
if i != axis:
dims.append(self.dims[i])
return DimArray(r, dims, self.fill_value, self.proj)
def median(self, axis=None):
'''
Compute tha median along the specified axis.
:param axis: (*int*) Axis along which the standard deviation is computed.
The default is to compute the standard deviation of the flattened array.
returns: (*array_like*) Median result
'''
r = super(DimArray, self).median(axis)
if isinstance(r, numbers.Number):
return r
else:
dims = []
for i in range(0, self.ndim):
if i != axis:
dims.append(self.dims[i])
return DimArray(r, dims, self.fill_value, self.proj)
def std(self, axis=None, ddof=0):
'''
Compute the standard deviation along the specified axis.
:param axis: (*int*) Axis along which the standard deviation is computed.
The default is to compute the standard deviation of the flattened array.
:param ddof: (*int*) Delta Degrees of Freedom: the divisor used in the calculation is
N - ddof, where N represents the number of elements. By default ddof is zero.
returns: (*array_like*) Standart deviation result.
'''
r = super(DimArray, self).std(axis, ddof)
if isinstance(r, numbers.Number):
return r
else:
dims = []
for i in range(0, self.ndim):
if i != axis:
dims.append(self.dims[i])
return DimArray(r, dims, self.fill_value, self.proj)
def var(self, axis=None, ddof=0):
'''
Compute the variance along the specified axis.
:param axis: (*int*) Axis along which the standard deviation is computed.
The default is to compute the standard deviation of the flattened array.
:param ddof: (*int*) Delta Degrees of Freedom: the divisor used in the calculation is
N - ddof, where N represents the number of elements. By default ddof is zero.
returns: (*array_like*) Variance result.
'''
r = super(DimArray, self).std(axis, ddof)
if isinstance(r, numbers.Number):
return r
else:
dims = []
for i in range(0, self.ndim):
if i != axis:
dims.append(self.dims[i])
return DimArray(r, dims, self.fill_value, self.proj)
def abs(self):
'''
Calculate the absolute value element-wise.
:returns: An array containing the absolute value of each element in x.
For complex input, a + ib, the absolute value is \sqrt{ a^2 + b^2 }.
'''
r = super(DimArray, self).abs()
return DimArray(r, self.dims, self.fill_value, self.proj)
def ceil(self):
'''
Return the ceiling of the input, element-wise.
:return: The ceiling of each element.
'''
r = super(DimArray, self).ceil()
return DimArray(r, self.dims, self.fill_value, self.proj)
def floor(self):
'''
Return the floor of the input, element-wise.
:return: The floor of each element.
'''
r = super(DimArray, self).floor()
return DimArray(r, self.dims, self.fill_value, self.proj)
def sqrt(self):
'''
Calculate sqrt value.
:returns: (*DimArray*) Sqrt value array.
'''
r = super(DimArray, self).sqrt()
return DimArray(r, self.dims, self.fill_value, self.proj)
def sin(self):
'''
Calculate sin value.
:returns: (*DimArray*) Sin value array.
'''
r = super(DimArray, self).sin()
return DimArray(r, self.dims, self.fill_value, self.proj)
def cos(self):
r = super(DimArray, self).cos()
return DimArray(r, self.dims, self.fill_value, self.proj)
def tan(self):
r = super(DimArray, self).tan()
return DimArray(r, self.dims, self.fill_value, self.proj)
def asin(self):
r = super(DimArray, self).asin()
return DimArray(r, self.dims, self.fill_value, self.proj)
def acos(self):
'''
Calculate acos value.
:returns: (*DimArray*) Acos value array.
'''
r = super(DimArray, self).acos()
return DimArray(r, self.dims, self.fill_value, self.proj)
def atan(self):
r = super(DimArray, self).atan()
return DimArray(r, self.dims, self.fill_value, self.proj)
def exp(self):
r = super(DimArray, self).exp()
return DimArray(r, self.dims, self.fill_value, self.proj)
def log(self):
r = super(DimArray, self).log()
return DimArray(r, self.dims, self.fill_value, self.proj)
def log10(self):
r = super(DimArray, self).log10()
return DimArray(r, self.dims, self.fill_value, self.proj)
def maskout(self, mask):
'''
Maskout data by polygons - the elements outside polygons will be set as NaN.
:param mask: (*list*) Polygon list as mask borders.
:returns: (*DimArray*) Maskouted data.
'''
if isinstance(mask, NDArray):
r = ArrayMath.maskout(self.asarray(), mask.asarray())
return DimArray(NDArray(r), self.dims, self.fill_value, self.proj)
else:
x = self.dims[1].getDimValue()
y = self.dims[0].getDimValue()
if not isinstance(mask, (list, ArrayList)):
mask = [mask]
r = GeometryUtil.maskout(self.asarray(), x, y, mask)
r = DimArray(NDArray(r), self.dims, self.fill_value, self.proj)
return r
def maskin(self, mask):
'''
Maskin data by polygons - the elements inside polygons will be set as NaN.
:param mask: (*list*) Polygon list as mask borders.
:returns: (*DimArray*) Maskined data.
'''
if isinstance(mask, NDArray):
r = ArrayMath.maskin(self.asarray(), mask.asarray())
return DimArray(r, self.dims, self.fill_value, self.proj)
else:
x = self.dimvalue(1)
y = self.dimvalue(0)
if not isinstance(mask, (list, ArrayList)):
mask = [mask]
r = GeometryUtil.maskin(self._array, x._array, y._array, mask)
r = DimArray(r, self.dims, self.fill_value, self.proj)
return r
def transpose(self, axes=None):
'''
Permute the dimensions of an array.
:param axes: (*list of int*) By default, reverse the dimensions, otherwise permute the axes according to the
values given.
:returns: Permuted array.
'''
if axes is None:
axes = [self.ndim-i-1 for i in range(self.ndim)]
r = super(DimArray, self).transpose(axes)
if self.ndim == 1:
dims = self.dims
else:
dims = []
for ax in axes:
dims.append(self.dims[ax])
return DimArray(r, dims, self.fill_value, self.proj)
T = property(transpose)
def swapaxes(self, axis1, axis2):
'''
Interchange two axes of an array.
:param axis1: (*int*) First axis.
:param axis2: (*int*) Second axis.
:returns: Axes swapped array.
'''
if self.ndim == 1:
return self
if axis1 < 0:
axis1 = self.ndim + axis1
if axis2 < 0:
axis2 = self.ndim + axis2
if axis1 == axis2:
return self
r = self._array.transpose(axis1, axis2)
dims = []
for i in range(self.ndim):
if i == axis1:
dims.append(self.dims[axis2])
elif i == axis2:
dims.append(self.dims[axis1])
else:
dims.append(self.dims[i])
return DimArray(r, dims, self.fill_value, self.proj)
def inv(self):
'''
Calculate inverse matrix array.
:returns: Inverse matrix array.
'''
r = super(DimArray, self).inv()
return DimArray(r, self.dims, self.fill_value, self.proj)
I = property(inv)
def lonflip(self):
'''
Reorder global array from 0 - 360 longitude to -180 - 180 longitude or vice versa.
:returns: Reordered array.
'''
lon = self.dimvalue(self.ndim - 1)
if lon.max() > 180:
return self.lonpivot(180)
else:
return self.lonpivot(0)
def lonpivot(self, pivot):
'''
Pivots an array about a user-specified longitude. The rightmost dimension must be the
longitude dimension, which must be global and without a cyclic point.
:param pivot: (*float*) The longitude value around which to pivot.
:returns: Result array after longitude pivot.
'''
lon = self.dimvalue(self.ndim - 1)
minlon = lon.min()
maxlon = lon.max()
dlon = lon[1] - lon[0]
if pivot < minlon:
pivot += 360
elif pivot > maxlon:
pivot -= 360
keys1 = []
keys2 = []
for i in range(self.ndim - 1):
keys1.append(slice(None,None,None))
keys2.append(slice(None,None,None))
keys1.append('%f:%f' % (pivot, maxlon))
keys2.append('%f:%f' % (minlon, pivot - dlon))
r1 = self.__getitem__(tuple(keys1))
r2 = self.__getitem__(tuple(keys2))
lon1 = r1.dimvalue(r1.ndim - 1)
lon2 = r2.dimvalue(r2.ndim - 1)
if maxlon > 180:
lon1 = lon1 - 360
else:
lon2 = lon2 + 360
r = r1.join(r2, self.ndim - 1)
lon1 = lon1.aslist()
lon1.extend(lon2.aslist())
r.setdimvalue(self.ndim - 1, lon1)
return r
def month_to_season(self, season):
'''
Computes a user-specified three-month seasonal mean
(DJF, JFM, FMA, MAM, AMJ, MJJ, JJA, JAS, ASO, SON, OND, NDJ).
The first average (DJF=JF) and the last average (NDJ=ND) are actually
two-month averages.
The time (leftmost) dimension must be divisible by 12. The data are assumed
to be monthly mean data and the first record is assumed to be January.
:param season: (*string*) A string representing the season to
calculate: e.g., "JFM", "JJA".
:returns: Season averaged data array.
'''
nmonth = self.dimlen(0)
nyear = nmonth / 12
seasons = ['DJF','JFM','FMA','MAM','AMJ','MJJ','JJA','JAS','ASO','SON','OND','NDJ']
season = season.upper()
if not season in seasons:
print 'Season string is not valid: "' + season + '"!'
raise KeyError()
idx = seasons.index(season) - 1
keys = []
keys.append(slice(0,nyear,1))
for i in range(1, self.ndim):
keys.append(slice(None,None,None))
r = self.__getitem__(tuple(keys))
si = idx
for i in range(nyear):
ei = si + 3
if si < 0:
si = 0
if ei > nmonth:
ei = nmonth
keys[0] = slice(si,ei,1)
sdata = self.__getitem__(tuple(keys))
sdata = ArrayMath.mean(sdata.asarray(), 0)
keys[0] = i
r.__setitem__(tuple(keys), sdata)
si += 12
return r
def interpn(self, xi):
"""
Multidimensional interpolation on regular grids.
:param xi: (*list*) The coordinates to sample the gridded data at.
:returns: (*float*) Interpolated value at input coordinates.
"""
points = []
for i in range(self.ndim):
points.append(ArrayUtil.array(self.dims[i].getDimValue()))
if isinstance(xi, (list, tuple)):
if isinstance(xi[0], NDArray):
nxi = []
for x in xi:
nxi.append(x._array)
else:
nxi = []
for x in xi:
if isinstance(x, datetime.datetime):
x = miutil.date2num(x)
nxi.append(x)
nxi = NDArray(nxi)._array
else:
nxi = nxi._array
r = ArrayUtil.interpn(points, self._array, nxi)
if isinstance(r, Array):
return NDArray(r)
else:
return r
def tostation(self, x, y):
gdata = self.asgriddata()
if isinstance(x, NDArray) or isinstance(x, DimArray):
r = gdata.data.toStation(x.aslist(), y.aslist())
return NDArray(ArrayUtil.array(r))
else:
return gdata.data.toStation(x, y)
def project(self, x=None, y=None, toproj=None, method='bilinear'):
"""
Project array
:param x: To x coordinates.
:param y: To y coordinates.
:param toproj: To projection.
:param method: Interpolation method: ``bilinear`` or ``neareast`` .
:returns: (*NDArray*) Projected array
"""
yy = self.dims[self.ndim - 2].getDimValue()
xx = self.dims[self.ndim - 1].getDimValue()
if toproj is None:
toproj = self.proj
if x is None or y is None:
pr = Reproject.reproject(self._array, xx, yy, self.proj, toproj)
r = pr[0]
x = pr[1]
y = pr[2]
dims = self.dims
ydim = Dimension(DimensionType.Y)
ydim.setDimValues(NDArray(y).aslist())
dims[-2] = ydim
xdim = Dimension(DimensionType.X)
xdim.setDimValues(NDArray(x).aslist())
dims[-1] = xdim
rr = DimArray(NDArray(r), dims, self.fill_value, toproj)
return rr
if method == 'bilinear':
method = ResampleMethods.Bilinear
else:
method = ResampleMethods.NearestNeighbor
if isinstance(x, (list, tuple)):
x = NDArray(x)
if isinstance(y, (list, tuple)):
y = NDArray(y)
if x.ndim == 1:
x, y = ArrayUtil.meshgrid(x.asarray(), y.asarray())
else:
x = x._array
y = y._array
r = Reproject.reproject(self._array, xx, yy, x, y, self.proj, toproj, method)
return NDArray(r)
def join(self, b, dimidx):
r = ArrayMath.join(self._array, b._array, dimidx)
dima = self.dimvalue(dimidx)
dimb = b.dimvalue(dimidx)
dimr = []
if dima[0] < dimb[0]:
for i in range(0, len(dima)):
dimr.append(dima[i])
for i in range(0, len(dimb)):
dimr.append(dimb[i])
else:
for i in range(0, len(dimb)):
dimr.append(dimb[i])
for i in range(0, len(dima)):
dimr.append(dima[i])
rdims = []
for i in range(0, len(self.dims)):
if i == dimidx:
ndim = Dimension()
ndim.setDimValues(dimr)
rdims.append(ndim)
else:
rdims.append(self.dims[i])
return DimArray(NDArray(r), rdims, self.fill_value, self.proj)
def savegrid(self, fname, format='surfer', **kwargs):
'''
Save the array data to an ASCII or binary file. The array must be 2 dimension.
:param fname: (*string*) File name.
:param format: (*string*) File format [surfer | bil | esri_ascii | micaps4].
:param description: (*string*) Data description - only used for ``micaps4`` file.
:param date: (*datetime*) Data datetime - only used for ``micaps4`` file.
:param hours: (*int*) Data forcasting hours - only used for ``micaps4`` file.
:param level: (*float*) Data vertical level - only used for ``micaps4`` file.
:param smooth: (*int*) 1 or 0 - only used for ``micaps4`` file.
:param boldvalue: (*int*) Bold contour value - only used for ``micaps4`` file.
:param proj: (*ProjectionInfo*) Data ProjectionInfo - only used for ``micaps4`` file.
:param float_format: (*string*) Float number format, such as '%.2f'.
'''
if self.ndim != 2:
print 'The array must be 2 dimensional!'
return
gdata = self.asgridarray()
float_format = kwargs.pop('float_format', None)
if format == 'surfer':
gdata.saveAsSurferASCIIFile(fname)
elif format == 'bil':
gdata.saveAsBILFile(fname)
elif format == 'esri_ascii':
gdata.saveAsESRIASCIIFile(fname)
elif format == 'micaps4':
desc = kwargs.pop('description', 'var')
date = kwargs.pop('date', datetime.datetime.now())
date = miutil.jdate(date)
hours = kwargs.pop('hours', 0)
level = kwargs.pop('level', 0)
smooth = kwargs.pop('smooth', 1)
boldvalue =kwargs.pop('boldvalue', 0)
proj = kwargs.pop('proj', self.proj)
if proj is None:
gdata.saveAsMICAPS4File(fname, desc, date, hours, level, smooth, boldvalue, float_format)
else:
if proj.isLonLat():
gdata.saveAsMICAPS4File(fname, desc, date, hours, level, smooth, boldvalue, float_format)
else:
gdata.saveAsMICAPS4File(fname, desc, date, hours, level, smooth, boldvalue, float_format, proj)
# The encapsulate class of GridData
class PyGridData():
# griddata must be a GridData object
def __init__(self, griddata=None):
if griddata != None:
self.data = griddata
else:
self.data = GridData()
def __getitem__(self, indices):
print type(indices)
if not isinstance(indices, tuple):
print 'indices must be tuple!'
return None
if len(indices) != 2:
print 'indices must be 2 dimension!'
return None
if isinstance(indices[0], int):
sxidx = indices[0]
exidx = indices[0]
xstep = 1
else:
sxidx = 0 if indices[0].start is None else indices[0].start
exidx = self.data.getXNum() if indices[0].stop is None else indices[0].stop
xstep = 1 if indices[0].step is None else indices[0].step
if isinstance(indices[1], int):
syidx = indices[1]
eyidx = indices[1]
ystep = 1
else:
syidx = 0 if indices[1].start is None else indices[1].start
eyidx = self.data.getYNum() if indices[1].stop is None else indices[1].stop
ystep = 1 if indices[1].step is None else indices[1].step
gdata = PyGridData(self.data.extract(sxidx, exidx, xstep, syidx, eyidx, ystep))
return gdata
def add(self, other):
gdata = None
if isinstance(other, PyGridData):
gdata = PyGridData(self.data.add(other.data))
else:
gdata = PyGridData(self.data.add(other))
return gdata
def __add__(self, other):
gdata = None
print isinstance(other, PyGridData)
if isinstance(other, PyGridData):
gdata = PyGridData(self.data.add(other.data))
else:
gdata = PyGridData(self.data.add(other))
return gdata
def __radd__(self, other):
return PyGridData.__add__(self, other)
def __sub__(self, other):
gdata = None
if isinstance(other, PyGridData):
gdata = PyGridData(self.data.sub(other.data))
else:
gdata = PyGridData(self.data.sub(other))
return gdata
def __rsub__(self, other):
gdata = None
if isinstance(other, PyGridData):
gdata = PyGridData(other.data.sub(self.data))
else:
gdata = PyGridData(DataMath.sub(other, self.data))
return gdata
def __mul__(self, other):
gdata = None
if isinstance(other, PyGridData):
gdata = PyGridData(self.data.mul(other.data))
else:
gdata = PyGridData(self.data.mul(other))
return gdata
def __rmul__(self, other):
return PyGridData.__mul__(self, other)
def __div__(self, other):
gdata = None
if isinstance(other, PyGridData):
gdata = PyGridData(self.data.div(other.data))
else:
gdata = PyGridData(self.data.div(other))
return gdata
def __rdiv__(self, other):
gdata = None
if isinstance(other, PyGridData):
gdata = PyGridData(other.data.div(self.data))
else:
gdata = PyGridData(DataMath.div(other, self))
return gdata
# other must be a numeric data
def __pow__(self, other):
gdata = PyGridData(self.data.pow(other))
return gdata
def min(self):
return self.data.getMinValue()
def max(self):
return self.data.getMaxValue()
def interpolate(self):
return PyGridData(self.data.interpolate())
def asdimarray(self):
a = self.data.getArray()
dims = self.data.getDimensions()
return DimArray(NDArray(a), dims, self.data.missingValue, self.data.projInfo)
def savedata(self, filename):
self.data.saveAsSurferASCIIFile(filename)
###############################################################
# The encapsulate class of StationData
class PyStationData():
# data must be a StationData object
def __init__(self, data=None):
self.data = data
def __len__(self):
return self.data.getStNum()
def __getitem__(self, indices):
if isinstance(indices, int): #Data index
idx = indices
stid = self.data.getStid(idx)
x = self.data.getX(idx)
y = self.data.getY(idx)
return stid, x, y
elif isinstance(indices, str): #Station identifer
stid = indices
idx = self.data.indexOf(stid)
x = self.data.getX(idx)
y = self.data.getY(idx)
return stid, x, y
else:
return None
def add(self, other):
gdata = None
if isinstance(other, PyStationData):
gdata = PyStationData(self.data.add(other.data))
else:
gdata = PyStationData(self.data.add(other))
return gdata
def __add__(self, other):
gdata = None
if isinstance(other, PyStationData):
gdata = PyStationData(self.data.add(other.data))
else:
gdata = PyStationData(self.data.add(other))
return gdata
def __radd__(self, other):
return PyStationData.__add__(self, other)
def __sub__(self, other):
gdata = None
if isinstance(other, PyStationData):
gdata = PyStationData(self.data.sub(other.data))
else:
gdata = PyStationData(self.data.sub(other))
return gdata
def __rsub__(self, other):
gdata = None
if isinstance(other, PyStationData):
gdata = PyStationData(other.data.sub(self.data))
else:
gdata = PyStationData(DataMath.sub(other, self.data))
return gdata
def __mul__(self, other):
gdata = None
if isinstance(other, PyStationData):
gdata = PyStationData(self.data.mul(other.data))
else:
gdata = PyStationData(self.data.mul(other))
return gdata
def __rmul__(self, other):
return PyStationData.__mul__(self, other)
def __div__(self, other):
gdata = None
if isinstance(other, PyStationData):
gdata = PyStationData(self.data.div(other.data))
else:
gdata = PyStationData(self.data.div(other))
return gdata
def __rdiv__(self, other):
gdata = None
if isinstance(other, PyStationData):
gdata = PyStationData(other.data.div(self.data))
else:
gdata = PyStationData(DataMath.div(other, self))
return gdata
# other must be a numeric data
def __pow__(self, other):
gdata = PyStationData(self.data.pow(other))
return gdata
def toarray(self):
r = ArrayUtil.getArraysFromStationData(self.data)
return NDArray(r[0]), NDArray(r[1]), NDArray(r[2])
def min(self):
return self.data.getMinValue()
def minloc(self):
minv = self.data.getMinValueIndex()
return minv[0], minv[1]
def max(self):
return self.data.getMaxValue()
def maxloc(self):
maxv = self.data.getMaxValueIndex()
return maxv[0], maxv[1]
def maskout(self, polygon):
if isinstance(polygon, MILayer):
polygon = polygon.layer
return PyStationData(self.data.maskout(polygon))
def maskin(self, polygon):
return PyStationData(self.data.maskin(polygon))
def filter(self, stations):
return PyStationData(self.data.filter(stations))
def join(self, other):
return PyStationData(self.data.join(other.data))
def ave(self):
return self.data.average()
def mean(self):
return self.data.average()
def sum(self):
return self.data.sum()
def griddata(self, xi=None, **kwargs):
method = kwargs.pop('method', 'idw')
fill_value = self.data.missingValue
x_s = NDArray(ArrayUtil.array(self.data.getXList()))
y_s = NDArray(ArrayUtil.array(self.data.getYList()))
if xi is None:
xn = int(math.sqrt(len(x_s)))
yn = xn
x_g = NDArray(ArrayUtil.lineSpace(x_s.min(), x_s.max(), xn, True))
y_g = NDArray(ArrayUtil.lineSpace(y_s.min(), y_s.max(), yn, True))
else:
x_g = xi[0]
y_g = xi[1]
if isinstance(x_s, NDArray):
x_s = x_s.aslist()
if isinstance(y_s, NDArray):
y_s = y_s.aslist()
if isinstance(x_g, NDArray):
x_g = x_g.aslist()
if isinstance(y_g, NDArray):
y_g = y_g.aslist()
if method == 'idw':
pnum = kwargs.pop('pointnum', 2)
radius = kwargs.pop('radius', None)
if radius is None:
r = self.data.interpolate_Neighbor(x_g, y_g, pnum, fill_value)
return PyGridData(r)
else:
r = self.data.interpolate_Radius(x_g, y_g, pnum, radius, fill_value)
return PyGridData(r)
elif method == 'cressman':
radius = kwargs.pop('radius', [10, 7, 4, 2, 1])
if isinstance(radius, NDArray):
radius = radius.aslist()
r = self.data.interpolate_Cressman(x_g, y_g, radius, fill_value)
return PyGridData(r)
elif method == 'neareast':
r = self.data.interpolate_Assign(x_g, y_g, fill_value)
return PyGridData(r)
else:
return None
def savedata(self, filename, fieldname='data', savemissingv=False):
self.data.saveAsCSVFile(filename, fieldname, savemissingv)