diff --git a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/MeshGraphic.java b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/MeshGraphic.java index e510ddeb..1ce5ccd7 100644 --- a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/MeshGraphic.java +++ b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/MeshGraphic.java @@ -331,7 +331,11 @@ public class MeshGraphic extends GraphicCollection3D { vertexColor = new float[getVertexNumber() * 4]; float[] color; for (int i = 0; i < vertexValue.length; i++) { - color = legendScheme.findLegendBreak(vertexValue[i]).getColor().getRGBComponents(null); + if (Float.isNaN(vertexValue[i])) { + color = legendScheme.getLegendBreak(0).getColor().getRGBComponents(null); + } else { + color = legendScheme.findLegendBreak(vertexValue[i]).getColor().getRGBComponents(null); + } System.arraycopy(color, 0, vertexColor, i * 4, 4); } } diff --git a/meteoinfo-common/src/main/java/org/meteoinfo/common/util/GlobalUtil.java b/meteoinfo-common/src/main/java/org/meteoinfo/common/util/GlobalUtil.java index a0d13ed0..ba8b1783 100644 --- a/meteoinfo-common/src/main/java/org/meteoinfo/common/util/GlobalUtil.java +++ b/meteoinfo-common/src/main/java/org/meteoinfo/common/util/GlobalUtil.java @@ -67,7 +67,7 @@ import java.util.zip.ZipInputStream; public static String getVersion(){ String version = GlobalUtil.class.getPackage().getImplementationVersion(); if (version == null || version.equals("")) { - version = "3.6.5"; + version = "3.6.6"; } return version; } diff --git a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/CMARadarBaseDataInfo.java b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/CMARadarBaseDataInfo.java index bc1d8426..14c4e213 100644 --- a/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/CMARadarBaseDataInfo.java +++ b/meteoinfo-data/src/main/java/org/meteoinfo/data/meteodata/radar/CMARadarBaseDataInfo.java @@ -601,7 +601,7 @@ public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo { Section section = new Section(origin, size, stride); RadialRecord record = this.recordMap.get(varName); - Array dataArray = Array.factory(record.getDataType(), section.getShape()); + Array dataArray = Array.factory(DataType.FLOAT, section.getShape()); Range zRange = section.getRange(0); Range yRange = section.getRange(1); Range xRange = section.getRange(2); @@ -623,36 +623,36 @@ public class CMARadarBaseDataInfo extends DataInfo implements IGridDataInfo { } } } + } - Attribute aoAttr = variable.findAttribute("add_offset"); - Attribute sfAttr = variable.findAttribute("scale_factor"); - if (aoAttr != null || sfAttr != null) { - Number add_offset = 0.f; - Number scale_factor = 1.f; - if (aoAttr != null) { - switch (aoAttr.getDataType()) { - case DOUBLE: - add_offset = aoAttr.getValues().getDouble(0); - break; - case FLOAT: - case INT: - add_offset = aoAttr.getValues().getFloat(0); - break; - } + Attribute aoAttr = variable.findAttribute("add_offset"); + Attribute sfAttr = variable.findAttribute("scale_factor"); + if (aoAttr != null || sfAttr != null) { + Number add_offset = 0.f; + Number scale_factor = 1.f; + if (aoAttr != null) { + switch (aoAttr.getDataType()) { + case DOUBLE: + add_offset = aoAttr.getValues().getDouble(0); + break; + case FLOAT: + case INT: + add_offset = aoAttr.getValues().getFloat(0); + break; } - if (sfAttr != null) { - switch (sfAttr.getDataType()) { - case DOUBLE: - scale_factor = sfAttr.getValues().getDouble(0); - break; - case FLOAT: - case INT: - scale_factor = sfAttr.getValues().getFloat(0); - break; - } - } - dataArray = ArrayMath.div(ArrayMath.sub(dataArray, add_offset), scale_factor); } + if (sfAttr != null) { + switch (sfAttr.getDataType()) { + case DOUBLE: + scale_factor = sfAttr.getValues().getDouble(0); + break; + case FLOAT: + case INT: + scale_factor = sfAttr.getValues().getFloat(0); + break; + } + } + dataArray = ArrayMath.div(ArrayMath.sub(dataArray, add_offset), scale_factor); } return dataArray; diff --git a/meteoinfo-lab/milconfig.xml b/meteoinfo-lab/milconfig.xml index 063f604e..d53f7b24 100644 --- a/meteoinfo-lab/milconfig.xml +++ b/meteoinfo-lab/milconfig.xml @@ -8,27 +8,27 @@ - - + + - - + + - - + + @@ -36,5 +36,5 @@
- + diff --git a/meteoinfo-lab/pylib/mipylib/dataset/radardatafile.py b/meteoinfo-lab/pylib/mipylib/dataset/radardatafile.py index 5aae4ce1..7dee2641 100644 --- a/meteoinfo-lab/pylib/mipylib/dataset/radardatafile.py +++ b/meteoinfo-lab/pylib/mipylib/dataset/radardatafile.py @@ -26,9 +26,9 @@ class RadarDataFile(DimDataFile): :return: Scan elevation angles. """ if product is None: - return list(self.datainfo.getElevations()) + return np.array(self.datainfo.getElevations()) else: - return list(self.datainfo.getElevations(product)) + return np.array(self.datainfo.getElevations(product)) def get_vcs_data(self, product, start_point, end_point): """ diff --git a/meteoinfo-lab/pylib/mipylib/dataset/util/RadarUtil.py b/meteoinfo-lab/pylib/mipylib/dataset/util/RadarUtil.py index ed33f2c2..bf6c5e95 100644 --- a/meteoinfo-lab/pylib/mipylib/dataset/util/RadarUtil.py +++ b/meteoinfo-lab/pylib/mipylib/dataset/util/RadarUtil.py @@ -19,19 +19,43 @@ def antenna_to_cartesian(distance, azimuth, elevation, h=None): """ azimuth = np.deg2rad(azimuth) elevation = np.deg2rad(elevation) - nd = distance.shape[0] - na = azimuth.shape[0] - distance, azimuth = np.meshgrid(distance, azimuth) - if isinstance(elevation, np.NDArray): - elevation = elevation.reshape(na, 1) - elevation = elevation.repeat(nd, axis=1) - elevation = elevation._array - if h is None: - r = Transform.antennaToCartesian(distance._array, azimuth._array, elevation) - else: - r = Transform.antennaToCartesian(distance._array, azimuth._array, elevation, h) + if azimuth.ndim == 1: + nd = distance.shape[0] + na = azimuth.shape[0] + distance, azimuth = np.meshgrid(distance, azimuth) + if isinstance(elevation, np.NDArray): + elevation = elevation.reshape(na, 1) + elevation = elevation.repeat(nd, axis=1) + elevation = elevation._array + if h is None: + r = Transform.antennaToCartesian(distance._array, azimuth._array, elevation) + else: + r = Transform.antennaToCartesian(distance._array, azimuth._array, elevation, h) - return np.array(r[0]), np.array(r[1]), np.array(r[2]) + return np.array(r[0]), np.array(r[1]), np.array(r[2]) + else: + nd = distance.shape[0] + ns, na = azimuth.shape + x = np.empty((ns, na, nd)) + y = np.empty((ns, na, nd)) + z = np.empty((ns,na,nd)) + for i in range(ns): + dis, azi = np.meshgrid(distance, azimuth[i]) + if elevation.ndim == 1: + ele = elevation[i] + else: + ele = elevation[i].reshape(na, 1).copy() + ele = ele.repeat(nd, axis=1) + ele = ele._array + if h is None: + r = Transform.antennaToCartesian(dis._array, azi._array, ele) + else: + r = Transform.antennaToCartesian(dis._array, azi._array, ele, h) + x[i] = np.array(r[0]) + y[i] = np.array(r[1]) + z[i] = np.array(r[2]) + + return x, y, z def antenna_to_geographic(lon, lat, distance, azimuth, elevation, h=None): """ diff --git a/meteoinfo-lab/pylib/mipylib/numeric/core/_ndarray.py b/meteoinfo-lab/pylib/mipylib/numeric/core/_ndarray.py index 63e9671a..e75c20c7 100644 --- a/meteoinfo-lab/pylib/mipylib/numeric/core/_ndarray.py +++ b/meteoinfo-lab/pylib/mipylib/numeric/core/_ndarray.py @@ -80,7 +80,10 @@ class NDArray(object): return self.dtype.itemsize def __len__(self): - return self._shape[0] + if self.ndim == 0: + return 0 + else: + return self._shape[0] def __str__(self): return ArrayUtil.convertToString(self._array) @@ -296,10 +299,10 @@ class NDArray(object): step = 1 alllist = False elif isinstance(k, (list, tuple, NDArray)): - if isinstance(k, NDArray): - k = k.aslist() + if isinstance(k, (list, tuple)): + k = NDArray(k) onlyrange = False - ranges.append(k) + ranges.append(k._array) continue else: sidx = 0 if k.start is None else k.start @@ -517,6 +520,32 @@ class NDArray(object): else: raise StopIteration() + def item(self, *args): + """ + Copy an element of an array to a standard Python scalar and return it. + + :param args: none: in this case, the method only works for arrays with one element + (a.size == 1), which element is copied into a standard Python scalar object and returned. + int_type: this argument is interpreted as a flat index into the array, specifying which + element to copy and return. + tuple of int_types: functions as does a single int_type argument, except that the argument + is interpreted as a nd-index into the array. + + :return: A copy of the specified element of the array as a suitable Python scalar + """ + if self.ndim == 0: + return self._array.get() + else: + index = self._array.getIndex() + if len(args) == 1: + if isinstance(args[0], int): + index.setCurrentIndex(args[0]) + else: + index.set(args[0]) + elif len(args) > 1: + index.set(args) + return self._array.getObject(index) + def copy(self): """ Copy array values to a new array. diff --git a/meteoinfo-lab/pylib/mipylib/numeric/core/numeric$py.class b/meteoinfo-lab/pylib/mipylib/numeric/core/numeric$py.class index 5c4d87ea..87a34db8 100644 Binary files a/meteoinfo-lab/pylib/mipylib/numeric/core/numeric$py.class and b/meteoinfo-lab/pylib/mipylib/numeric/core/numeric$py.class differ diff --git a/meteoinfo-lab/pylib/mipylib/numeric/core/numeric.py b/meteoinfo-lab/pylib/mipylib/numeric/core/numeric.py index cfbd8bc8..36bb5ddd 100644 --- a/meteoinfo-lab/pylib/mipylib/numeric/core/numeric.py +++ b/meteoinfo-lab/pylib/mipylib/numeric/core/numeric.py @@ -40,7 +40,7 @@ __all__ = [ 'argmin','argmax','argsort','array','array_split','amax','amin','asanyarray','asarray','asgridarray', 'asgriddata','arcsin','asin','asmiarray','asstationdata','atleast_1d','atleast_2d','arctan','atan', 'arctan2','atan2','ave_month','average','histogram','broadcast_to','cdiff','ceil', - 'concatenate','conj','conjugate','corrcoef','cos','cosh','cylinder','degrees','delete','delnan','diag','diff', + 'concatenate','conj','conjugate','corrcoef','cos','cosh','cylinder','degrees','delnan','diag','diff', 'datatable','dot','empty','empty_like','exp','eye','flatnonzero','floor', 'fmax','fmin','full','hcurl','hdivg','hstack','hypot','identity','indices','interp2d','interpn','isarray', 'isclose','isfinite','isinf','isnan','isscalar','linspace','log','log10','logical_not','logspace', @@ -65,7 +65,7 @@ def array(object, dtype=None, copy=True, order='K', subok=False, ndmin=0): :param dtype: (*DataType*) Data type :param copy: (*bool*) If true (default), then the object is copied. :param order: (*str*) Specify the memory layout of the array. - :param subok: (*bool*) If True, then sub-classes will be passed-through, + :param subok: (*bool*) If True, then subclasses will be passed-through, otherwise the returned array will be forced to be a base-class array (default). :param ndmin: (*int*) Specifies the minimum number of dimensions that the resulting array should have. @@ -1753,8 +1753,8 @@ def argsort(a, axis=-1): :param axis: (*int or None*) Optional. Axis along which to sort. If None, the array is flattened after sorting. The default is ``-1`` , which sorts along the last axis. - :returns: (*NDArray*) Array of indices that sort a along the specified axis. If a is - one-dimensional, a[index_array] yields a sorted a. + :returns: (*NDArray*) Array of indices that sort `a` along the specified axis. If `a` is + one-dimensional, a[index_array] yields a sorted `a`. """ if isinstance(a, list): a = array(a) @@ -1992,7 +1992,7 @@ def concatenate(arrays, axis=0): """ ars = [] for a in arrays: - ars.append(a.asarray()) + ars.append(asanyarray(a).asarray()) r = ArrayUtil.concatenate(ars, axis) return NDArray(r) @@ -2801,7 +2801,7 @@ def smooth5(x): if isinstance(x, list): x = array(x) if x.ndim != 2: - print 'The array must be 2 dimension!' + print('The array must be 2 dimension!') raise ValueError() r = ArrayUtil.smooth5(x._array) if isinstance(x, DimArray): @@ -2829,7 +2829,7 @@ def smooth9(x): if isinstance(x, list): x = array(x) if x.ndim != 2: - print 'The array must be 2 dimension!' + print('The array must be 2 dimension!') raise ValueError() r = ArrayUtil.smooth9(x._array) if isinstance(x, DimArray): diff --git a/meteoinfo-lab/pylib/mipylib/numeric/lib/function_base.py b/meteoinfo-lab/pylib/mipylib/numeric/lib/function_base.py index e8a17e20..0f4427e3 100644 --- a/meteoinfo-lab/pylib/mipylib/numeric/lib/function_base.py +++ b/meteoinfo-lab/pylib/mipylib/numeric/lib/function_base.py @@ -4,8 +4,10 @@ from ..core import dtype from ..core._ndarray import NDArray from ..core.fromnumeric import (ravel, nonzero) from org.meteoinfo.ndarray.math import ArrayMath +import warnings -__all__ = ['angle','extract', 'place', 'grid_edge', 'gradient'] +__all__ = ['angle','extract', 'place', 'grid_edge', 'gradient', 'append', + 'delete', 'insert'] def extract(condition, arr): @@ -365,4 +367,402 @@ def gradient(f, *varargs, **kwargs): if len_axes == 1: return outvals[0] else: - return outvals \ No newline at end of file + return outvals + +def append(arr, values, axis=None): + """ + Append values to the end of an array. + + Parameters + ---------- + arr : array_like + Values are appended to a copy of this array. + values : array_like + These values are appended to a copy of `arr`. It must be of the + correct shape (the same shape as `arr`, excluding `axis`). If + `axis` is not specified, `values` can be any shape and will be + flattened before use. + axis : int, optional + The axis along which `values` are appended. If `axis` is not + given, both `arr` and `values` are flattened before use. + + Returns + ------- + append : ndarray + A copy of `arr` with `values` appended to `axis`. Note that + `append` does not occur in-place: a new array is allocated and + filled. If `axis` is None, `out` is a flattened array. + + See Also + -------- + insert : Insert elements into an array. + delete : Delete elements from an array. + + Examples + -------- + >>> np.append([1, 2, 3], [[4, 5, 6], [7, 8, 9]]) + array([1, 2, 3, ..., 7, 8, 9]) + + When `axis` is specified, `values` must have the correct shape. + + >>> np.append([[1, 2, 3], [4, 5, 6]], [[7, 8, 9]], axis=0) + array([[1, 2, 3], + [4, 5, 6], + [7, 8, 9]]) + >>> np.append([[1, 2, 3], [4, 5, 6]], [7, 8, 9], axis=0) + Traceback (most recent call last): + ... + ValueError: all the input arrays must have same number of dimensions, but + the array at index 0 has 2 dimension(s) and the array at index 1 has 1 + dimension(s) + + """ + arr = np.asanyarray(arr) + if axis is None: + if arr.ndim != 1: + arr = arr.ravel() + values = ravel(values) + axis = arr.ndim-1 + return np.concatenate((arr, values), axis=axis) + +def delete(arr, obj, axis=None): + """ + Return a new array with sub-arrays along an axis deleted. For a one + dimensional array, this returns those entries not returned by + `arr[obj]`. + + Parameters + ---------- + arr : array_like + Input array. + obj : slice, int or array of ints + Indicate indices of sub-arrays to remove along the specified axis. + + .. versionchanged:: 1.19.0 + Boolean indices are now treated as a mask of elements to remove, + rather than being cast to the integers 0 and 1. + + axis : int, optional + The axis along which to delete the subarray defined by `obj`. + If `axis` is None, `obj` is applied to the flattened array. + + Returns + ------- + out : ndarray + A copy of `arr` with the elements specified by `obj` removed. Note + that `delete` does not occur in-place. If `axis` is None, `out` is + a flattened array. + + See Also + -------- + insert : Insert elements into an array. + append : Append elements at the end of an array. + + Notes + ----- + Often it is preferable to use a boolean mask. For example: + + >>> arr = np.arange(12) + 1 + >>> mask = np.ones(len(arr), dtype=bool) + >>> mask[[0,2,4]] = False + >>> result = arr[mask,...] + + Is equivalent to ``np.delete(arr, [0,2,4], axis=0)``, but allows further + use of `mask`. + + Examples + -------- + >>> arr = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]]) + >>> arr + array([[ 1, 2, 3, 4], + [ 5, 6, 7, 8], + [ 9, 10, 11, 12]]) + >>> np.delete(arr, 1, 0) + array([[ 1, 2, 3, 4], + [ 9, 10, 11, 12]]) + + >>> np.delete(arr, np.s_[::2], 1) + array([[ 2, 4], + [ 6, 8], + [10, 12]]) + >>> np.delete(arr, [1,3,5], None) + array([ 1, 3, 5, 7, 8, 9, 10, 11, 12]) + + """ + arr = np.asarray(arr) + ndim = arr.ndim + if axis is None: + if ndim != 1: + arr = arr.ravel() + # needed for np.matrix, which is still not 1d after being ravelled + ndim = arr.ndim + axis = ndim - 1 + else: + axis = np.normalize_axis_index(axis, ndim) + + slobj = [slice(None)]*ndim + N = arr.shape[axis] + newshape = list(arr.shape) + + if isinstance(obj, slice): + start, stop, step = obj.indices(N) + xr = range(start, stop, step) + numtodel = len(xr) + + if numtodel <= 0: + return arr.copy() + + # Invert if step is negative: + if step < 0: + step = -step + start = xr[-1] + stop = xr[0] + 1 + + newshape[axis] -= numtodel + new = np.empty(newshape, arr.dtype) + # copy initial chunk + if start == 0: + pass + else: + slobj[axis] = slice(None, start) + new[tuple(slobj)] = arr[tuple(slobj)] + # copy end chunk + if stop == N: + pass + else: + slobj[axis] = slice(stop-numtodel, None) + slobj2 = [slice(None)]*ndim + slobj2[axis] = slice(stop, None) + new[tuple(slobj)] = arr[tuple(slobj2)] + # copy middle pieces + if step == 1: + pass + else: # use array indexing. + keep = np.ones(stop-start, dtype=np.dtype.bool) + keep[:stop-start:step] = False + slobj[axis] = slice(start, stop-numtodel) + slobj2 = [slice(None)]*ndim + slobj2[axis] = slice(start, stop) + arr = arr[tuple(slobj2)] + slobj2[axis] = keep + new[tuple(slobj)] = arr[tuple(slobj2)] + return new + + if isinstance(obj, int) and not isinstance(obj, bool): + single_value = True + else: + single_value = False + _obj = obj + obj = np.asarray(obj) + # `size == 0` to allow empty lists similar to indexing, but (as there) + # is really too generic: + if obj.size == 0 and not isinstance(_obj, np.NDArray): + obj = obj.astype(np.dtype.int) + elif obj.size == 1 and obj.dtype.kind in "ui": + # For a size 1 integer array we can use the single-value path + # (most dtypes, except boolean, should just fail later). + obj = obj.item() + single_value = True + + if single_value: + # optimization for a single value + if (obj < -N or obj >= N): + raise IndexError( + "index %i is out of bounds for axis %i with " + "size %i" % (obj, axis, N)) + if (obj < 0): + obj += N + newshape[axis] -= 1 + new = np.empty(newshape, arr.dtype,) + slobj[axis] = slice(None, obj) + new[tuple(slobj)] = arr[tuple(slobj)] + slobj[axis] = slice(obj, None) + slobj2 = [slice(None)]*ndim + slobj2[axis] = slice(obj+1, None) + new[tuple(slobj)] = arr[tuple(slobj2)] + else: + if obj.dtype == np.dtype.bool: + if obj.shape != (N,): + raise ValueError('boolean array argument obj to delete ' + 'must be one dimensional and match the axis ' + 'length of {}'.format(N)) + + # optimization, the other branch is slower + keep = ~obj + else: + keep = np.ones(N, dtype=np.dtype.bool) + keep[obj,] = False + + slobj[axis] = keep + new = arr[tuple(slobj)] + + return new + +def insert(arr, obj, values, axis=None): + """ + Insert values along the given axis before the given indices. + + Parameters + ---------- + arr : array_like + Input array. + obj : int, slice or sequence of ints + Object that defines the index or indices before which `values` is + inserted. + + Support for multiple insertions when `obj` is a single scalar or a + sequence with one element (similar to calling insert multiple + times). + values : array_like + Values to insert into `arr`. If the type of `values` is different + from that of `arr`, `values` is converted to the type of `arr`. + `values` should be shaped so that ``arr[...,obj,...] = values`` + is legal. + axis : int, optional + Axis along which to insert `values`. If `axis` is None then `arr` + is flattened first. + + Returns + ------- + out : ndarray + A copy of `arr` with `values` inserted. Note that `insert` + does not occur in-place: a new array is returned. If + `axis` is None, `out` is a flattened array. + + See Also + -------- + append : Append elements at the end of an array. + concatenate : Join a sequence of arrays along an existing axis. + delete : Delete elements from an array. + + Notes + ----- + Note that for higher dimensional inserts ``obj=0`` behaves very different + from ``obj=[0]`` just like ``arr[:,0,:] = values`` is different from + ``arr[:,[0],:] = values``. + + Examples + -------- + >>> a = np.array([[1, 1], [2, 2], [3, 3]]) + >>> a + array([[1, 1], + [2, 2], + [3, 3]]) + >>> np.insert(a, 1, 5) + array([1, 5, 1, ..., 2, 3, 3]) + >>> np.insert(a, 1, 5, axis=1) + array([[1, 5, 1], + [2, 5, 2], + [3, 5, 3]]) + + Difference between sequence and scalars: + + >>> np.insert(a, [1], [[1],[2],[3]], axis=1) + array([[1, 1, 1], + [2, 2, 2], + [3, 3, 3]]) + >>> np.array_equal(np.insert(a, 1, [1, 2, 3], axis=1), + ... np.insert(a, [1], [[1],[2],[3]], axis=1)) + True + + >>> b = a.flatten() + >>> b + array([1, 1, 2, 2, 3, 3]) + >>> np.insert(b, [2, 2], [5, 6]) + array([1, 1, 5, ..., 2, 3, 3]) + + >>> np.insert(b, slice(2, 4), [5, 6]) + array([1, 1, 5, ..., 2, 3, 3]) + + >>> np.insert(b, [2, 2], [7.13, False]) # type casting + array([1, 1, 7, ..., 2, 3, 3]) + + >>> x = np.arange(8).reshape(2, 4) + >>> idx = (1, 3) + >>> np.insert(x, idx, 999, axis=1) + array([[ 0, 999, 1, 2, 999, 3], + [ 4, 999, 5, 6, 999, 7]]) + + """ + arr = np.asarray(arr) + ndim = arr.ndim + if axis is None: + if ndim != 1: + arr = arr.ravel() + # needed for np.matrix, which is still not 1d after being ravelled + ndim = arr.ndim + axis = ndim - 1 + else: + axis = np.normalize_axis_index(axis, ndim) + slobj = [slice(None)]*ndim + N = arr.shape[axis] + newshape = list(arr.shape) + + if isinstance(obj, slice): + # turn it into a range object + indices = np.arange(*obj.indices(N), dtype=np.dtype.int) + else: + # need to copy obj, because indices will be changed in-place + indices = np.array(obj).copy() + if indices.dtype == np.dtype.bool: + # See also delete + warnings.warn( + "in the future insert will treat boolean arrays and " + "array-likes as a boolean index instead of casting it to " + "integer", FutureWarning, stacklevel=2) + indices = indices.astype(np.dtype.int) + elif indices.ndim > 1: + raise ValueError( + "index array argument obj to insert must be one dimensional " + "or scalar") + if indices.size == 1: + index = indices.item() + if index < -N or index > N: + raise IndexError("index {} is out of bounds for axis {} with size {}". + format(obj, axis, N)) + if (index < 0): + index += N + + # There are some object array corner cases here, but we cannot avoid + # that: + values = np.array(values, copy=False, ndmin=arr.ndim, dtype=arr.dtype) + if indices.ndim == 0: + # broadcasting is very different here, since a[:,0,:] = ... behaves + # very different from a[:,[0],:] = ...! This changes values so that + # it works likes the second case. (here a[:,0:1,:]) + values = np.moveaxis(values, 0, axis) + numnew = values.shape[axis] + newshape[axis] += numnew + new = np.empty(newshape, arr.dtype,) + slobj[axis] = slice(None, index) + new[tuple(slobj)] = arr[tuple(slobj)] + slobj[axis] = slice(index, index+numnew) + new[tuple(slobj)] = values + slobj[axis] = slice(index+numnew, None) + slobj2 = [slice(None)] * ndim + slobj2[axis] = slice(index, None) + new[tuple(slobj)] = arr[tuple(slobj2)] + return new + elif indices.size == 0 and not isinstance(obj, np.NDArray): + # Can safely cast the empty list to intp + indices = indices.astype(np.dtype.int) + + indices[indices < 0] += N + + numnew = len(indices) + #order = indices.argsort(kind='mergesort') # stable sort + order = np.argsort(indices) + indices[order] += np.arange(numnew) + + newshape[axis] += numnew + old_mask = np.ones(newshape[axis], dtype=np.dtype.bool) + old_mask[indices] = False + + new = np.empty(newshape, arr.dtype) + slobj2 = [slice(None)]*ndim + slobj[axis] = indices + slobj2[axis] = old_mask + new[tuple(slobj)] = values + new[tuple(slobj2)] = arr + + return new diff --git a/meteoinfo-lab/pylib/mipylib/plotlib/plotutil$py.class b/meteoinfo-lab/pylib/mipylib/plotlib/plotutil$py.class index 4ae7e94b..7631d86c 100644 Binary files a/meteoinfo-lab/pylib/mipylib/plotlib/plotutil$py.class and b/meteoinfo-lab/pylib/mipylib/plotlib/plotutil$py.class differ diff --git a/meteoinfo-lab/pylib/mipylib/plotlib/plotutil.py b/meteoinfo-lab/pylib/mipylib/plotlib/plotutil.py index c7cf4614..3ece26d2 100644 --- a/meteoinfo-lab/pylib/mipylib/plotlib/plotutil.py +++ b/meteoinfo-lab/pylib/mipylib/plotlib/plotutil.py @@ -183,8 +183,12 @@ def getcolors(cs, alpha=None): if isinstance(cs[0], int): colors.append(getcolor(cs, alpha)) else: - for c in cs: - colors.append(getcolor(c, alpha)) + if isinstance(alpha, (tuple, list)): + for c, a in zip(cs, alpha): + colors.append(getcolor(c, a)) + else: + for c in cs: + colors.append(getcolor(c, alpha)) else: colors.append(getcolor(cs, alpha)) return colors diff --git a/meteoinfo-math/src/main/java/org/meteoinfo/math/sort/MergeSort.java b/meteoinfo-math/src/main/java/org/meteoinfo/math/sort/MergeSort.java new file mode 100644 index 00000000..c68351f3 --- /dev/null +++ b/meteoinfo-math/src/main/java/org/meteoinfo/math/sort/MergeSort.java @@ -0,0 +1,53 @@ +package org.meteoinfo.math.sort; + +import java.util.Arrays; + +public class MergeSort { + + /** + * Merge sort + * @param sourceArray Source array + * @return Sorted array + * @throws Exception + */ + public int[] sort(int[] sourceArray) throws Exception { + // 对 arr 进行拷贝,不改变参数内容 + int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); + + if (arr.length < 2) { + return arr; + } + int middle = (int) Math.floor(arr.length / 2); + + int[] left = Arrays.copyOfRange(arr, 0, middle); + int[] right = Arrays.copyOfRange(arr, middle, arr.length); + + return merge(sort(left), sort(right)); + } + + protected int[] merge(int[] left, int[] right) { + int[] result = new int[left.length + right.length]; + int i = 0; + while (left.length > 0 && right.length > 0) { + if (left[0] <= right[0]) { + result[i++] = left[0]; + left = Arrays.copyOfRange(left, 1, left.length); + } else { + result[i++] = right[0]; + right = Arrays.copyOfRange(right, 1, right.length); + } + } + + while (left.length > 0) { + result[i++] = left[0]; + left = Arrays.copyOfRange(left, 1, left.length); + } + + while (right.length > 0) { + result[i++] = right[0]; + right = Arrays.copyOfRange(right, 1, right.length); + } + + return result; + } +} diff --git a/meteoinfo-math/src/main/java/org/meteoinfo/math/sort/QuickSort.java b/meteoinfo-math/src/main/java/org/meteoinfo/math/sort/QuickSort.java index a2265f64..5ba540df 100644 --- a/meteoinfo-math/src/main/java/org/meteoinfo/math/sort/QuickSort.java +++ b/meteoinfo-math/src/main/java/org/meteoinfo/math/sort/QuickSort.java @@ -45,7 +45,7 @@ import java.util.Comparator; * always appear before S in the sorted list. *

* For speed of execution, we implement it without recursion. Instead, - * we requires an auxiliary array (stack) of storage, of length + * we require an auxiliary array (stack) of storage, of length * 2 log2 n. When a subarray has gotten down to size 7, * we sort it by straight insertion. * diff --git a/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/ArrayBoolean.java b/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/ArrayBoolean.java index c9e252db..7a1907cd 100644 --- a/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/ArrayBoolean.java +++ b/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/ArrayBoolean.java @@ -305,7 +305,11 @@ public class ArrayBoolean extends Array { } public void setObject(Index i, Object value) { - storage[i.currentElement()] = (Boolean) value; + if (value instanceof Integer) { + storage[i.currentElement()] = ((Integer) value == 1); + } else { + storage[i.currentElement()] = (Boolean) value; + } } // package private : mostly for iterators diff --git a/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/ArrayDouble.java b/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/ArrayDouble.java index 9262c99d..a21c5a8d 100644 --- a/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/ArrayDouble.java +++ b/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/ArrayDouble.java @@ -275,7 +275,11 @@ public class ArrayDouble extends Array { } public void setObject(Index i, Object value) { - storageD[i.currentElement()] = ((Number) value).doubleValue(); + if (value instanceof Boolean) { + storageD[i.currentElement()] = ((Boolean) value) ? 1 : 0; + } else { + storageD[i.currentElement()] = ((Number) value).doubleValue(); + } } // trusted, assumes that individual dimension lengths have been checked @@ -368,7 +372,11 @@ public class ArrayDouble extends Array { } public void setObject(int index, Object value) { - storageD[index] = ((Number) value).doubleValue(); + if (value instanceof Boolean) { + storageD[index] = ((Boolean) value) ? 1 : 0; + } else { + storageD[index] = ((Number) value).doubleValue(); + } } /** diff --git a/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/ArrayInt.java b/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/ArrayInt.java index 294778f1..68e439ea 100644 --- a/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/ArrayInt.java +++ b/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/ArrayInt.java @@ -298,7 +298,11 @@ public class ArrayInt extends Array { } public void setObject(Index i, Object value) { - storage[i.currentElement()] = ((Number) value).intValue(); + if (value instanceof Boolean) { + storage[i.currentElement()] = ((Boolean) value) ? 1 : 0; + } else { + storage[i.currentElement()] = ((Number) value).intValue(); + } } // package private : mostly for iterators @@ -397,7 +401,11 @@ public class ArrayInt extends Array { } public void setObject(int index, Object value) { - storage[index] = ((Number) value).intValue(); + if (value instanceof Boolean) { + storage[index] = ((Boolean) value) ? 1 : 0; + } else { + storage[index] = ((Number) value).intValue(); + } } /** diff --git a/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/Index.java b/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/Index.java index 95fab67f..e896fd66 100644 --- a/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/Index.java +++ b/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/Index.java @@ -672,6 +672,18 @@ public class Index implements Cloneable { return this; } + /** + * Set the current element's index. General-rank case. + * + * @param index set current value to these values + * @return this, so you can use A.get(i.set(i)) + * @throws ArrayIndexOutOfBoundsException if index.length != rank. + */ + public Index set(List indexList) { + int[] index = indexList.stream().mapToInt(Integer::intValue).toArray(); + return set(index); + } + /** * set current element at dimension dim to v * diff --git a/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/MixIterator.java b/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/MixIterator.java index a3a58679..70e1dd7f 100644 --- a/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/MixIterator.java +++ b/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/MixIterator.java @@ -1,6 +1,9 @@ package org.meteoinfo.ndarray; +import org.meteoinfo.ndarray.math.ArrayUtil; + import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.List; public class MixIterator implements IndexIterator { @@ -29,7 +32,21 @@ public class MixIterator implements IndexIterator { if (range instanceof Range) { shape[i] = ((Range) range).length(); } else { - shape[i] = ((List) range).size(); + Array arr = (Array) range; + if (arr.getDataType() == DataType.BOOLEAN) { + List tList = new ArrayList<>(); + IndexIterator iter = arr.getIndexIterator(); + int j = 0; + while (iter.hasNext()) { + if (iter.getBooleanNext()) { + tList.add(j); + } + j += 1; + } + arr = ArrayUtil.array_list(tList, DataType.INT); + this.ranges.set(i, arr); + } + shape[i] = (int) arr.getSize(); } i += 1; } @@ -297,9 +314,9 @@ public class MixIterator implements IndexIterator { int c; for (Object range : this.ranges) { if (range instanceof Range) { - c = ((Range)range).elementNC(rangeCounter[i]); + c = ((Range) range).elementNC(rangeCounter[i]); } else { - c = ((List)range).get(rangeCounter[i]); + c = ((Array) range).getInt(rangeCounter[i]); } currentCounter[i] = c; i += 1; diff --git a/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/math/ArrayMath.java b/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/math/ArrayMath.java index eaceb98d..2a011aa2 100644 --- a/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/math/ArrayMath.java +++ b/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/math/ArrayMath.java @@ -6022,11 +6022,11 @@ public class ArrayMath { public static Array setSection(Array a, List ranges, Array v) throws InvalidRangeException { Array r = a.section(ranges); IndexIterator iter = r.getIndexIterator(); - if (r.getSize() != v.getSize()) { + /*if (r.getSize() != v.getSize()) { if (r.getShape() != v.getShape()) { v = ArrayUtil.broadcast(v, r.getShape()); } - } + }*/ Index index = v.getIndex(); while (iter.hasNext()) { iter.setObjectNext(v.getObject(index)); @@ -6207,18 +6207,34 @@ public class ArrayMath { * @param v Number value * @return Result array */ - public static Array setSection_List(Array a, List> ranges, Number v) { + public static Array setSection_List(Array a, List ranges, Number v) { //Array r = copy(a); int n = a.getRank(); int[] count = new int[n]; Index index = a.getIndex(); - int m = ranges.get(0).size(); - for (int i = 0; i < m; i++) { - for (int j = 0; j < n; j++) { - count[j] = ranges.get(j).get(i); + int m = (int) ranges.get(0).getSize(); + if (ranges.get(0).getDataType() == DataType.BOOLEAN) { + for (int i = 0; i < m; i++) { + boolean bool = true; + for (int j = 0; j < n; j++) { + if (!ranges.get(j).getBoolean(i)) { + bool = false; + break; + } + } + if (bool) { + a.setObject(index, v); + } + index.incr(); + } + } else { + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + count[j] = ranges.get(j).getInt(i); + } + index.set(count); + a.setObject(index, v); } - index.set(count); - a.setObject(index, v); } return a; @@ -6232,20 +6248,37 @@ public class ArrayMath { * @param v Array value * @return Result array */ - public static Array setSection_List(Array a, List> ranges, Array v) { + public static Array setSection_List(Array a, List ranges, Array v) { //Array r = copy(a); int n = a.getRank(); int[] count = new int[n]; Index index = a.getIndex(); Index vIndex = v.getIndex(); - int m = ranges.get(0).size(); - for (int i = 0; i < m; i++) { - for (int j = 0; j < n; j++) { - count[j] = ranges.get(j).get(i); + int m = (int) ranges.get(0).getSize(); + if (ranges.get(0).getDataType() == DataType.BOOLEAN) { + for (int i = 0; i < m; i++) { + boolean bool = true; + for (int j = 0; j < n; j++) { + if (!ranges.get(j).getBoolean(i)) { + bool = false; + break; + } + } + if (bool) { + a.setObject(index, v.getObject(vIndex)); + vIndex.incr(); + } + index.incr(); + } + } else { + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + count[j] = ranges.get(j).getInt(i); + } + index.set(count); + a.setObject(index, v.getObject(vIndex)); + vIndex.incr(); } - index.set(count); - a.setObject(index, v.getObject(vIndex)); - vIndex.incr(); } return a; diff --git a/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/math/ArrayUtil.java b/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/math/ArrayUtil.java index 91541f41..cb43b3a8 100644 --- a/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/math/ArrayUtil.java +++ b/meteoinfo-ndarray/src/main/java/org/meteoinfo/ndarray/math/ArrayUtil.java @@ -1633,6 +1633,12 @@ public class ArrayUtil { StringBuilder sbuff = new StringBuilder(); sbuff.append("array("); int ndim = a.getRank(); + if (ndim == 0) { + sbuff.append(a.getObject(0)); + sbuff.append(")"); + return sbuff.toString(); + } + if (ndim > 1) { for (int i = 0; i < ndim - 1; i++) sbuff.append("[");