[Git][debian-gis-team/pyresample][upstream] New upstream version 1.10.1
Antonio Valentino
gitlab at salsa.debian.org
Sun Jul 8 21:22:06 BST 2018
Antonio Valentino pushed to branch upstream at Debian GIS Project / pyresample
Commits:
5f4f8007 by Antonio Valentino at 2018-07-08T19:42:55+00:00
New upstream version 1.10.1
- - - - -
11 changed files:
- .bumpversion.cfg
- CHANGELOG.md
- docs/source/installation.rst
- pyresample/__init__.py
- pyresample/geometry.py
- pyresample/kd_tree.py
- pyresample/test/test_geometry.py
- pyresample/test/test_kd_tree.py
- pyresample/test/test_spherical.py
- pyresample/version.py
- setup.py
Changes:
=====================================
.bumpversion.cfg
=====================================
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,9 +1,9 @@
[bumpversion]
-current_version = 1.9.3.gamma0
+current_version = 1.10.1
commit = True
tag = False
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)((?P<pre>[a-z]+)(?P<num>\d+))?(\.(?P<release>dev)(?P<dev>\d+))?
-serialize =
+serialize =
{major}.{minor}.{patch}{pre}{num}.{release}{dev}
{major}.{minor}.{patch}{pre}{num}
{major}.{minor}.{patch}.{release}{dev}
@@ -13,12 +13,12 @@ serialize =
[bumpversion:part:release]
optional_value = gamma
-values =
+values =
dev
gamma
[bumpversion:part:dev]
-values =
+values =
0
1
2
@@ -28,9 +28,8 @@ optional_value = 4
[bumpversion:part:pre]
optional_value = final
-values =
+values =
a
b
rc
final
-
=====================================
CHANGELOG.md
=====================================
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,30 @@
+
+
###############################################################################
+## Version 1.10.1 (2018/07/03)
+
+
+### Pull Requests Merged
+
+#### Bugs fixed
+
+* [PR 130](https://github.com/pytroll/pyresample/pull/130) - Fix log message not to rely on the proj key
+* [PR 129](https://github.com/pytroll/pyresample/pull/129) - Ignore proj dicts with no key for slicing
+
+In this release 2 pull requests were closed.
+
+
+## Version 1.10.0 (2018/06/25)
+
+### Pull Requests Merged
+
+#### Features added
+
+* [PR 128](https://github.com/pytroll/pyresample/pull/128) - Add option to provide KDTree's 'mask' argument when querying
+
+In this release 1 pull request was closed.
+
+
## Version 1.9.3 (2018/06/08)
### Issues Closed
@@ -15,12 +41,11 @@ In this release 3 issues were closed.
* [PR 125](https://github.com/pytroll/pyresample/pull/125) - Fix area slices not working for non-geos projections
* [PR 119](https://github.com/pytroll/pyresample/pull/119) - Add hashing to StackedAreaDefinitions
-In this release 1 pull requests were closed.
+In this release 1 pull request was closed.
## Version 1.9.2 (2018/05/13)
-
### Pull Requests Merged
#### Bugs fixed
@@ -36,7 +61,6 @@ In this release 2 pull requests were closed.
## Version 1.9.1 (2018/05/03)
-
### Pull Requests Merged
#### Features added
=====================================
docs/source/installation.rst
=====================================
--- a/docs/source/installation.rst
+++ b/docs/source/installation.rst
@@ -65,14 +65,5 @@ Using numexpr
As of pyresample v1.0.0 numexpr_ will be used for minor bottleneck optimization if available
-Show active plugins
-*******************
-The active drop-in plugins can be show using:
-
- >>> import pyresample as pr
- >>> pr.get_capabilities()
-
.. _pykdtree: https://github.com/storpipfugl/pykdtree
.. _numexpr: https://code.google.com/p/numexpr/
-
-
\ No newline at end of file
=====================================
pyresample/__init__.py
=====================================
--- a/pyresample/__init__.py
+++ b/pyresample/__init__.py
@@ -38,21 +38,3 @@ from pyresample.plot import save_quicklook, area_def2basemap # noqa
__all__ = ['grid', 'image', 'kd_tree',
'utils', 'plot', 'geo_filter', 'geometry', 'CHUNK_SIZE']
-
-
-def get_capabilities():
- cap = {}
-
- try:
- from pykdtree.kdtree import KDTree # noqa
- cap['pykdtree'] = True
- except ImportError:
- cap['pykdtree'] = False
-
- try:
- import numexpr # noqa
- cap['numexpr'] = True
- except ImportError:
- cap['numexpr'] = False
-
- return cap
=====================================
pyresample/geometry.py
=====================================
--- a/pyresample/geometry.py
+++ b/pyresample/geometry.py
@@ -195,6 +195,20 @@ class BaseDefinition(object):
else:
return self.lons[data_slice], self.lats[data_slice]
+ def get_lonlats_dask(self, chunks=CHUNK_SIZE):
+ """Get the lon lats as a single dask array."""
+ import dask.array as da
+ lons, lats = self.get_lonlats()
+
+ if isinstance(lons.data, da.Array):
+ return lons.data, lats.data
+ else:
+ lons = da.from_array(np.asanyarray(lons),
+ chunks=chunks)
+ lats = da.from_array(np.asanyarray(lats),
+ chunks=chunks)
+ return lons, lats
+
def get_boundary_lonlats(self):
"""Return Boundary objects."""
s1_lon, s1_lat = self.get_lonlats(data_slice=(0, slice(None)))
@@ -529,20 +543,6 @@ class SwathDefinition(CoordinateDefinition):
pass
return the_hash
- def get_lonlats_dask(self, chunks=CHUNK_SIZE):
- """Get the lon lats as a single dask array."""
- import dask.array as da
- lons, lats = self.get_lonlats()
-
- if isinstance(lons.data, da.Array):
- return lons.data, lats.data
- else:
- lons = da.from_array(np.asanyarray(lons),
- chunks=chunks)
- lats = da.from_array(np.asanyarray(lats),
- chunks=chunks)
- return lons, lats
-
def _compute_omerc_parameters(self, ellipsoid):
"""Compute the oblique mercator projection bouding box parameters."""
lines, cols = self.lons.shape
@@ -1344,7 +1344,8 @@ class AreaDefinition(BaseDefinition):
# Intersection only required for two different projections
if area_to_cover.proj_str == self.proj_str:
logger.debug('Projections for data and slice areas are'
- ' identical: %s', area_to_cover.proj_dict['proj'])
+ ' identical: %s',
+ area_to_cover.proj_dict.get('proj', area_to_cover.proj_dict.get('init')))
# Get xy coordinates
llx, lly, urx, ury = area_to_cover.area_extent
x, y = self.get_xy_from_proj_coords([llx, urx], [lly, ury])
@@ -1362,7 +1363,7 @@ class AreaDefinition(BaseDefinition):
"equal.")
data_boundary = Boundary(*get_geostationary_bounding_box(self))
- if area_to_cover.proj_dict['proj'] == 'geos':
+ if area_to_cover.proj_dict.get('proj') == 'geos':
area_boundary = Boundary(
*get_geostationary_bounding_box(area_to_cover))
else:
=====================================
pyresample/kd_tree.py
=====================================
--- a/pyresample/kd_tree.py
+++ b/pyresample/kd_tree.py
@@ -26,44 +26,29 @@ import warnings
from logging import getLogger
import numpy as np
-
+from pykdtree.kdtree import KDTree
from pyresample import _spatial_mp, data_reduce, geometry
+from pyresample import CHUNK_SIZE
logger = getLogger(__name__)
try:
from xarray import DataArray
import dask.array as da
+ import dask
except ImportError:
DataArray = None
da = None
+ dask = None
if sys.version >= '3':
long = int
-kd_tree_name = None
-try:
- from pykdtree.kdtree import KDTree
- kd_tree_name = 'pykdtree'
-except ImportError:
- try:
- import scipy.spatial as sp
- kd_tree_name = 'scipy.spatial'
- except ImportError:
- raise ImportError('Either pykdtree or scipy must be available')
-
class EmptyResult(ValueError):
pass
-def which_kdtree():
- """Returns the name of the kdtree used for resampling
- """
-
- return kd_tree_name
-
-
def resample_nearest(source_geo_def,
data,
target_geo_def,
@@ -510,12 +495,10 @@ def _create_resample_kdtree(source_lons,
raise EmptyResult('No valid data points in input data')
# Build kd-tree on input
- if kd_tree_name == 'pykdtree':
- resample_kdtree = KDTree(input_coords)
- elif nprocs > 1:
+ if nprocs > 1:
resample_kdtree = _spatial_mp.cKDTree_MP(input_coords, nprocs=nprocs)
else:
- resample_kdtree = sp.cKDTree(input_coords)
+ resample_kdtree = KDTree(input_coords)
return resample_kdtree
@@ -914,12 +897,21 @@ def lonlat2xyz(lons, lats):
(x_coords.ravel(), y_coords.ravel(), z_coords.ravel()), axis=-1)
-def query_no_distance(target_lons, target_lats,
- valid_output_index, kdtree, neighbours, epsilon, radius):
- """Query the kdtree. No distances are returned."""
+def query_no_distance(target_lons, target_lats, valid_output_index,
+ mask=None, valid_input_index=None,
+ neighbours=None, epsilon=None, radius=None,
+ kdtree=None):
+ """Query the kdtree. No distances are returned.
+
+ NOTE: Dask array arguments must always come before other keyword arguments
+ for `da.atop` arguments to work.
+
+ """
voi = valid_output_index
- shape = voi.shape
+ shape = voi.shape + (neighbours,)
voir = voi.ravel()
+ if mask is not None:
+ mask = mask.ravel()[valid_input_index.ravel()]
target_lons_valid = target_lons.ravel()[voir]
target_lats_valid = target_lats.ravel()[voir]
@@ -928,30 +920,36 @@ def query_no_distance(target_lons, target_lats,
coords.compute(),
k=neighbours,
eps=epsilon,
- distance_upper_bound=radius)
+ distance_upper_bound=radius,
+ mask=mask)
+
+ if index_array.ndim == 1:
+ index_array = index_array[:, None]
# KDTree query returns out-of-bounds neighbors as `len(arr)`
# which is an invalid index, we mask those out so -1 represents
# invalid values
- # voi is 2D, index_array is 1D
+ # voi is 2D (trows, tcols)
+ # index_array is 2D (valid output pixels, neighbors)
+ # there are as many Trues in voi as rows in index_array
good_pixels = index_array < kdtree.n
- voi[voi] = good_pixels
- res_ia = np.full(shape, fill_value=-1, dtype=np.int)
- res_ia[voi] = index_array[good_pixels]
+ res_ia = np.empty(shape, dtype=np.int)
+ mask = np.zeros(shape, dtype=np.bool)
+ mask[voi, :] = good_pixels
+ res_ia[mask] = index_array[good_pixels]
+ res_ia[~mask] = -1
return res_ia
-class XArrayResamplerNN():
+class XArrayResamplerNN(object):
def __init__(self,
source_geo_def,
target_geo_def,
radius_of_influence,
- neighbours=8,
- epsilon=0,
- reduce_data=True,
- nprocs=1,
- segments=None):
+ neighbours=1,
+ epsilon=0):
"""
+
Parameters
----------
source_geo_def : object
@@ -961,18 +959,12 @@ class XArrayResamplerNN():
radius_of_influence : float
Cut off distance in meters
neighbours : int, optional
- The number of neigbours to consider for each grid point
+ The number of neigbours to consider for each grid point.
+ Default 1. Currently 1 is the only supported number.
epsilon : float, optional
Allowed uncertainty in meters. Increasing uncertainty
reduces execution time
- reduce_data : bool, optional
- Perform initial coarse reduction of source dataset in order
- to reduce execution time
- nprocs : int, optional
- Number of processor cores to be used
- segments : int or None
- Number of segments to use when resampling.
- If set to None an estimate will be calculated
+
"""
if DataArray is None:
raise ImportError("Missing 'xarray' and 'dask' dependencies")
@@ -981,52 +973,52 @@ class XArrayResamplerNN():
self.valid_output_index = None
self.index_array = None
self.distance_array = None
+ self.delayed_kdtree = None
self.neighbours = neighbours
self.epsilon = epsilon
- self.reduce_data = reduce_data
- self.nprocs = nprocs
- self.segments = segments
self.source_geo_def = source_geo_def
self.target_geo_def = target_geo_def
self.radius_of_influence = radius_of_influence
+ assert (self.target_geo_def.ndim == 2), \
+ "Target area definition must be 2 dimensions"
- def _create_resample_kdtree(self):
+ def _create_resample_kdtree(self, chunks=CHUNK_SIZE):
"""Set up kd tree on input"""
- source_lons, source_lats = self.source_geo_def.get_lonlats_dask()
+ source_lons, source_lats = self.source_geo_def.get_lonlats_dask(
+ chunks=chunks)
valid_input_idx = ((source_lons >= -180) & (source_lons <= 180) &
(source_lats <= 90) & (source_lats >= -90))
-
- # FIXME: Is dask smart enough to only compute the pixels we end up
- # using even with this complicated indexing
input_coords = lonlat2xyz(source_lons, source_lats)
input_coords = input_coords[valid_input_idx.ravel(), :]
# Build kd-tree on input
input_coords = input_coords.astype(np.float)
- valid_input_idx, input_coords = da.compute(valid_input_idx,
- input_coords)
- if kd_tree_name == 'pykdtree':
- resample_kdtree = KDTree(input_coords)
- else:
- resample_kdtree = sp.cKDTree(input_coords)
-
- return valid_input_idx, resample_kdtree
-
- def _query_resample_kdtree(self,
- resample_kdtree,
- tlons,
- tlats,
- valid_oi,
- reduce_data=True):
+ delayed_kdtree = dask.delayed(KDTree, pure=True)(input_coords)
+ return valid_input_idx, delayed_kdtree
+
+ def query_resample_kdtree(self,
+ resample_kdtree,
+ tlons,
+ tlats,
+ valid_oi,
+ mask):
"""Query kd-tree on slice of target coordinates."""
-
- res = da.map_blocks(query_no_distance, tlons, tlats,
- valid_oi, dtype=np.int, kdtree=resample_kdtree,
- neighbours=self.neighbours, epsilon=self.epsilon,
- radius=self.radius_of_influence)
+ if mask is None:
+ args = tuple()
+ else:
+ ndims = self.source_geo_def.ndim
+ dims = 'mn'[:ndims]
+ args = (mask, dims, self.valid_input_index, dims)
+ # res.shape = rows, cols, neighbors
+ # j=rows, i=cols, k=neighbors, m=source rows, n=source cols
+ res = da.atop(query_no_distance, 'jik', tlons, 'ji', tlats, 'ji',
+ valid_oi, 'ji', *args, kdtree=resample_kdtree,
+ neighbours=self.neighbours, epsilon=self.epsilon,
+ radius=self.radius_of_influence, dtype=np.int,
+ new_axes={'k': self.neighbours}, concatenate=True)
return res, None
- def get_neighbour_info(self):
+ def get_neighbour_info(self, mask=None):
"""Return neighbour info.
Returns
@@ -1041,27 +1033,22 @@ class XArrayResamplerNN():
(self.neighbours, self.source_geo_def.size))
# Create kd-tree
- valid_input_idx, resample_kdtree = self._create_resample_kdtree()
- # This is a numpy array
+ chunks = mask.chunks if mask is not None else CHUNK_SIZE
+ valid_input_idx, resample_kdtree = self._create_resample_kdtree(
+ chunks=chunks)
self.valid_input_index = valid_input_idx
-
- if resample_kdtree.n == 0:
- # Handle if all input data is reduced away
- valid_output_idx, index_arr, distance_arr = \
- _create_empty_info(self.source_geo_def,
- self.target_geo_def, self.neighbours)
-
- self.valid_output_index = valid_output_idx
- self.index_array = index_arr
- self.distance_array = distance_arr
- return valid_input_idx, valid_output_idx, index_arr, distance_arr
+ self.delayed_kdtree = resample_kdtree
target_lons, target_lats = self.target_geo_def.get_lonlats_dask()
valid_output_idx = ((target_lons >= -180) & (target_lons <= 180) &
(target_lats <= 90) & (target_lats >= -90))
- index_arr, distance_arr = self._query_resample_kdtree(
- resample_kdtree, target_lons, target_lats, valid_output_idx)
+ if mask is not None:
+ assert (mask.shape == self.source_geo_def.shape), \
+ "'mask' must be the same shape as the source geo definition"
+ mask = mask.data
+ index_arr, distance_arr = self.query_resample_kdtree(
+ resample_kdtree, target_lons, target_lats, valid_output_idx, mask)
self.valid_output_index, self.index_array = valid_output_idx, index_arr
self.distance_array = distance_arr
@@ -1072,54 +1059,151 @@ class XArrayResamplerNN():
self.distance_array)
def get_sample_from_neighbour_info(self, data, fill_value=np.nan):
+ """Get the pixels matching the target area.
+
+ This method should work for any dimensionality of the provided data
+ array as long as the geolocation dimensions match in size and name in
+ ``data.dims``. Where source area definition are `AreaDefinition`
+ objects the corresponding dimensions in the data should be
+ ``('y', 'x')``.
+
+ This method also attempts to preserve chunk sizes of dask arrays,
+ but does require loading/sharing the fully computed source data before
+ it can actually compute the values to write to the destination array.
+ This can result in large memory usage for large source data arrays,
+ but is a necessary evil until fancier indexing is supported by dask
+ and/or pykdtree.
+
+ Args:
+ data (dask.array.Array): Source data pixels to sample
+ fill_value (float): Output fill value when no source data is
+ near the target pixel. If the input data
+ is a integer array then the minimum value
+ for that integer type is used. Otherwise,
+ NaN is used and can be detected in the result
+ with ``res.isnull()``.
+ """
+ if fill_value is not None and np.isnan(fill_value) and \
+ np.issubdtype(data.dtype, np.integer):
+ fill_value = _get_fill_mask_value(data.dtype)
+ logger.warning("Fill value incompatible with integer data "
+ "using {:d} instead.".format(fill_value))
+
+ # Convert back to 1 neighbor
+ if self.neighbours > 1:
+ raise NotImplementedError("Nearest neighbor resampling can not "
+ "handle more than 1 neighbor yet.")
+ # Convert from multiple neighbor shape to 1 neighbor
+ ia = self.index_array[:, :, 0]
+ vii = self.valid_input_index
+
+ if isinstance(self.source_geo_def, geometry.SwathDefinition):
+ # could be 1D or 2D
+ src_geo_dims = self.source_geo_def.lons.dims
+ else:
+ # assume AreaDefinitions and everything else are 2D with 'y', 'x'
+ src_geo_dims = ('y', 'x')
+ dst_geo_dims = ('y', 'x')
+ # verify that source dims are the same between geo and data
+ data_geo_dims = tuple(d for d in data.dims if d in src_geo_dims)
+ assert (data_geo_dims == src_geo_dims), \
+ "Data dimensions do not match source area dimensions"
+ # verify that the dims are next to each other
+ first_dim_idx = data.dims.index(src_geo_dims[0])
+ num_dims = len(src_geo_dims)
+ assert (data.dims[first_dim_idx:first_dim_idx + num_dims] ==
+ data_geo_dims), "Data's geolocation dimensions are not " \
+ "consecutive."
+
+ # FIXME: Can't include coordinates whose dimensions depend on the geo
+ # dims either
+ def contain_coords(var, coord_list):
+ return bool(set(coord_list).intersection(set(var.dims)))
+
+ coords = {c: c_var for c, c_var in data.coords.items()
+ if not contain_coords(c_var, src_geo_dims + dst_geo_dims)}
try:
- self.index_array = self.index_array.compute()
+ coord_x, coord_y = self.target_geo_def.get_proj_vectors_dask()
+ coords['y'] = coord_y
+ coords['x'] = coord_x
except AttributeError:
- pass
+ logger.debug("No geo coordinates created")
+ # shape of the source data after we flatten the geo dimensions
flat_src_shape = []
- dst_shape = []
- dst_2d_shape = self.index_array.shape
+ # slice objects to index in to the source data
vii_slices = []
ia_slices = []
- where_slices = []
- flat_done = False
- coords = {}
- ia = self.index_array
-
- try:
- coord_x, coord_y = self.target_geo_def.get_proj_vectors_dask()
- except AttributeError:
- coord_x, coord_y = None, None
-
+ # whether we have seen the geo dims in our analysis
+ geo_handled = False
+ # dimension indexes for da.atop
+ src_adims = []
+ flat_adim = []
+ # map source dimension name to dimension number for da.atop
+ src_dim_to_ind = {}
+ # destination array dimension indexes for da.atop
+ dst_dims = []
for i, dim in enumerate(data.dims):
- if dim in ['y', 'x']:
- if not flat_done:
- flat_src_shape.append(-1)
- vii_slices.append(self.valid_input_index.ravel())
- ia_slices.append(ia.ravel())
- flat_done = True
- dst_shape.append(dst_2d_shape[0])
- where_slices.append(slice(None))
- dst_2d_shape = dst_2d_shape[1:]
- coords[dim] = coord_x if dim == 'x' else coord_y
- else:
+ src_dim_to_ind[dim] = i
+ if dim in src_geo_dims and not geo_handled:
+ flat_src_shape.append(-1)
+ vii_slices.append(None) # mark for replacement
+ ia_slices.append(None) # mark for replacement
+ flat_adim.append(i)
+ src_adims.append(i)
+ dst_dims.extend(dst_geo_dims)
+ geo_handled = True
+ elif dim not in src_geo_dims:
flat_src_shape.append(data.sizes[dim])
vii_slices.append(slice(None))
ia_slices.append(slice(None))
- where_slices.append(np.newaxis)
- dst_shape.append(data.sizes[dim])
- coords[dim] = data.coords[dim]
-
- new_data = data.data.reshape(*flat_src_shape)[tuple(vii_slices)]
- res = new_data[tuple(ia_slices)].reshape(dst_shape)
-
- res = DataArray(res, dims=data.dims, coords=coords)
- if fill_value is None:
- fill_value = np.nan
- res = res.where(ia[tuple(where_slices)] != -1, fill_value)
-
+ src_adims.append(i)
+ dst_dims.append(dim)
+ # map destination dimension names to atop dimension indexes
+ dst_dim_to_ind = src_dim_to_ind.copy()
+ dst_dim_to_ind['y'] = i + 1
+ dst_dim_to_ind['x'] = i + 2
+ # FUTURE: when we allow more than one neighbor
+ # neighbors_dim = i + 3
+
+ def _my_index(index_arr, vii, data_arr, vii_slices=None,
+ ia_slices=None, fill_value=np.nan):
+ vii_slices = tuple(
+ x if x is not None else vii.ravel() for x in vii_slices)
+ mask_slices = tuple(
+ x if x is not None else (index_arr == -1) for x in ia_slices)
+ ia_slices = tuple(
+ x if x is not None else index_arr for x in ia_slices)
+ res = data_arr[vii_slices][ia_slices]
+ res[mask_slices] = fill_value
+ return res
+
+ new_data = data.data.reshape(flat_src_shape)
+ vii = vii.ravel()
+ dst_adims = [dst_dim_to_ind[dim] for dim in dst_dims]
+ ia_adims = [dst_dim_to_ind[dim] for dim in dst_geo_dims]
+ # FUTURE: when we allow more than one neighbor add neighbors dimension
+ # dst_adims.append(neighbors_dim)
+ # ia_adims.append(neighbors_dim)
+ # FUTURE: when we allow more than one neighbor we need to add
+ # the new axis to atop:
+ # `new_axes={neighbor_dim: self.neighbors}`
+ # FUTURE: if/when dask can handle index arrays that are dask arrays
+ # then we can avoid all of this complicated atop stuff
+ res = da.atop(_my_index, dst_adims,
+ ia, ia_adims,
+ vii, flat_adim,
+ new_data, src_adims,
+ vii_slices=vii_slices, ia_slices=ia_slices,
+ fill_value=fill_value,
+ dtype=new_data.dtype, concatenate=True)
+ res = DataArray(res, dims=dst_dims, coords=coords,
+ attrs=data.attrs.copy())
+ res.attrs['_FillValue'] = fill_value
+ # if fill_value isn't NaN then we have to tell xarray what null is
+ if not np.isnan(fill_value):
+ res = res.where(res != fill_value)
return res
=====================================
pyresample/test/test_geometry.py
=====================================
--- a/pyresample/test/test_geometry.py
+++ b/pyresample/test/test_geometry.py
@@ -788,7 +788,6 @@ class Test(unittest.TestCase):
self.assertEqual(slice(46, 3667, None), slice_x)
self.assertEqual(slice(52, 3663, None), slice_y)
-
area_to_cover = geometry.AreaDefinition('areaD', 'Europe (3km, HRV, VTC)', 'areaD',
{'a': 6378144.0,
'b': 6356759.0,
@@ -806,6 +805,19 @@ class Test(unittest.TestCase):
self.assertEqual(slice_x, slice(1610, 2343))
self.assertEqual(slice_y, slice(158, 515, None))
+ # totally different area
+ area_to_cover = geometry.AreaDefinition('epsg4326', 'Global equal latitude/longitude grid for global sphere',
+ 'epsg4326',
+ {"init": 'EPSG:4326',
+ 'units': 'degrees'},
+ 8192,
+ 4096,
+ [-180.0, -90.0, 180.0, 90.0])
+
+ slice_x, slice_y = area_def.get_area_slices(area_to_cover)
+ self.assertEqual(slice_x, slice(46, 3667, None))
+ self.assertEqual(slice_y, slice(52, 3663, None))
+
def test_get_area_slices_nongeos(self):
"""Check area slicing for non-geos projections."""
from pyresample import utils
=====================================
pyresample/test/test_kd_tree.py
=====================================
--- a/pyresample/test/test_kd_tree.py
+++ b/pyresample/test/test_kd_tree.py
@@ -2,8 +2,9 @@ from __future__ import with_statement
import os
import sys
+import six
-import numpy
+import numpy as np
from pyresample import geometry, kd_tree, utils
from pyresample.test.utils import catch_warnings
@@ -34,12 +35,12 @@ class Test(unittest.TestCase):
1029087.28,
1490031.3600000001])
- cls.tdata = numpy.array([1, 2, 3])
- cls.tlons = numpy.array([11.280789, 12.649354, 12.080402])
- cls.tlats = numpy.array([56.011037, 55.629675, 55.641535])
+ cls.tdata = np.array([1, 2, 3])
+ cls.tlons = np.array([11.280789, 12.649354, 12.080402])
+ cls.tlats = np.array([56.011037, 55.629675, 55.641535])
cls.tswath = geometry.SwathDefinition(lons=cls.tlons, lats=cls.tlats)
cls.tgrid = geometry.CoordinateDefinition(
- lons=numpy.array([12.562036]), lats=numpy.array([55.715613]))
+ lons=np.array([12.562036]), lats=np.array([55.715613]))
def test_nearest_base(self):
res = kd_tree.resample_nearest(self.tswath,
@@ -100,9 +101,9 @@ class Test(unittest.TestCase):
self.assertEqual(counts[0], 3)
def test_nearest(self):
- data = numpy.fromfunction(lambda y, x: y * x, (50, 10))
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ data = np.fromfunction(lambda y, x: y * x, (50, 10))
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
res = kd_tree.resample_nearest(swath_def, data.ravel(),
self.area_def, 50000, segments=1)
@@ -112,14 +113,14 @@ class Test(unittest.TestCase):
def test_nearest_masked_swath_target(self):
"""Test that a masked array works as a target."""
- data = numpy.fromfunction(lambda y, x: y * x, (50, 10))
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
- mask = numpy.ones_like(lons, dtype=numpy.bool)
+ data = np.fromfunction(lambda y, x: y * x, (50, 10))
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
+ mask = np.ones_like(lons, dtype=np.bool)
mask[::2, ::2] = False
swath_def = geometry.SwathDefinition(
- lons=numpy.ma.masked_array(lons, mask=mask),
- lats=numpy.ma.masked_array(lats, mask=False)
+ lons=np.ma.masked_array(lons, mask=mask),
+ lats=np.ma.masked_array(lats, mask=False)
)
res = kd_tree.resample_nearest(swath_def, data.ravel(),
swath_def, 50000, segments=3)
@@ -129,9 +130,9 @@ class Test(unittest.TestCase):
self.assertEqual(cross_sum, expected)
def test_nearest_1d(self):
- data = numpy.fromfunction(lambda x, y: x * y, (800, 800))
- lons = numpy.fromfunction(lambda x: 3 + x / 100., (500,))
- lats = numpy.fromfunction(lambda x: 75 - x / 10., (500,))
+ data = np.fromfunction(lambda x, y: x * y, (800, 800))
+ lons = np.fromfunction(lambda x: 3 + x / 100., (500,))
+ lats = np.fromfunction(lambda x: 75 - x / 10., (500,))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
res = kd_tree.resample_nearest(self.area_def, data.ravel(),
swath_def, 50000, segments=1)
@@ -141,9 +142,9 @@ class Test(unittest.TestCase):
self.assertEqual(cross_sum, expected)
def test_nearest_empty(self):
- data = numpy.fromfunction(lambda y, x: y * x, (50, 10))
- lons = numpy.fromfunction(lambda y, x: 165 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ data = np.fromfunction(lambda y, x: y * x, (50, 10))
+ lons = np.fromfunction(lambda y, x: 165 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
res = kd_tree.resample_nearest(swath_def, data.ravel(),
self.area_def, 50000, segments=1)
@@ -152,11 +153,11 @@ class Test(unittest.TestCase):
self.assertEqual(cross_sum, expected)
def test_nearest_empty_multi(self):
- data = numpy.fromfunction(lambda y, x: y * x, (50, 10))
- lons = numpy.fromfunction(lambda y, x: 165 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
- data_multi = numpy.column_stack((data.ravel(), data.ravel(),
- data.ravel()))
+ data = np.fromfunction(lambda y, x: y * x, (50, 10))
+ lons = np.fromfunction(lambda y, x: 165 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
+ data_multi = np.column_stack((data.ravel(), data.ravel(),
+ data.ravel()))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
res = kd_tree.resample_nearest(swath_def, data_multi,
self.area_def, 50000, segments=1)
@@ -164,11 +165,11 @@ class Test(unittest.TestCase):
msg='Swath resampling nearest empty multi failed')
def test_nearest_empty_multi_masked(self):
- data = numpy.fromfunction(lambda y, x: y * x, (50, 10))
- lons = numpy.fromfunction(lambda y, x: 165 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
- data_multi = numpy.column_stack((data.ravel(), data.ravel(),
- data.ravel()))
+ data = np.fromfunction(lambda y, x: y * x, (50, 10))
+ lons = np.fromfunction(lambda y, x: 165 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
+ data_multi = np.column_stack((data.ravel(), data.ravel(),
+ data.ravel()))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
res = kd_tree.resample_nearest(swath_def, data_multi,
self.area_def, 50000, segments=1,
@@ -176,9 +177,9 @@ class Test(unittest.TestCase):
self.assertEqual(res.shape, (800, 800, 3))
def test_nearest_empty_masked(self):
- data = numpy.fromfunction(lambda y, x: y * x, (50, 10))
- lons = numpy.fromfunction(lambda y, x: 165 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ data = np.fromfunction(lambda y, x: y * x, (50, 10))
+ lons = np.fromfunction(lambda y, x: 165 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
res = kd_tree.resample_nearest(swath_def, data.ravel(),
self.area_def, 50000, segments=1,
@@ -188,9 +189,9 @@ class Test(unittest.TestCase):
self.assertTrue(cross_sum == expected)
def test_nearest_segments(self):
- data = numpy.fromfunction(lambda y, x: y * x, (50, 10))
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ data = np.fromfunction(lambda y, x: y * x, (50, 10))
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
res = kd_tree.resample_nearest(swath_def, data.ravel(),
self.area_def, 50000, segments=2)
@@ -199,9 +200,9 @@ class Test(unittest.TestCase):
self.assertEqual(cross_sum, expected)
def test_nearest_remap(self):
- data = numpy.fromfunction(lambda y, x: y * x, (50, 10))
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ data = np.fromfunction(lambda y, x: y * x, (50, 10))
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
res = kd_tree.resample_nearest(swath_def, data.ravel(),
self.area_def, 50000, segments=1)
@@ -212,9 +213,9 @@ class Test(unittest.TestCase):
self.assertEqual(cross_sum, expected)
def test_nearest_mp(self):
- data = numpy.fromfunction(lambda y, x: y * x, (50, 10))
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ data = np.fromfunction(lambda y, x: y * x, (50, 10))
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
res = kd_tree.resample_nearest(swath_def, data.ravel(),
self.area_def, 50000, nprocs=2, segments=1)
@@ -223,12 +224,12 @@ class Test(unittest.TestCase):
self.assertEqual(cross_sum, expected)
def test_nearest_multi(self):
- data = numpy.fromfunction(lambda y, x: y * x, (50, 10))
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ data = np.fromfunction(lambda y, x: y * x, (50, 10))
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
- data_multi = numpy.column_stack((data.ravel(), data.ravel(),
- data.ravel()))
+ data_multi = np.column_stack((data.ravel(), data.ravel(),
+ data.ravel()))
res = kd_tree.resample_nearest(swath_def, data_multi,
self.area_def, 50000, segments=1)
cross_sum = res.sum()
@@ -236,11 +237,11 @@ class Test(unittest.TestCase):
self.assertEqual(cross_sum, expected)
def test_nearest_multi_unraveled(self):
- data = numpy.fromfunction(lambda y, x: y * x, (50, 10))
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ data = np.fromfunction(lambda y, x: y * x, (50, 10))
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
- data_multi = numpy.dstack((data, data, data))
+ data_multi = np.dstack((data, data, data))
res = kd_tree.resample_nearest(swath_def, data_multi,
self.area_def, 50000, segments=1)
cross_sum = res.sum()
@@ -248,9 +249,9 @@ class Test(unittest.TestCase):
self.assertEqual(cross_sum, expected)
def test_gauss_sparse(self):
- data = numpy.fromfunction(lambda y, x: y * x, (50, 10))
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ data = np.fromfunction(lambda y, x: y * x, (50, 10))
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
res = kd_tree.resample_gauss(swath_def, data.ravel(),
self.area_def, 50000, 25000, fill_value=-1, segments=1)
@@ -259,10 +260,10 @@ class Test(unittest.TestCase):
self.assertAlmostEqual(cross_sum, expected, places=3)
def test_gauss(self):
- data = numpy.fromfunction(lambda y, x: (y + x) * 10 ** -5, (5000, 100))
- lons = numpy.fromfunction(
+ data = np.fromfunction(lambda y, x: (y + x) * 10 ** -5, (5000, 100))
+ lons = np.fromfunction(
lambda y, x: 3 + (10.0 / 100) * x, (5000, 100))
- lats = numpy.fromfunction(
+ lats = np.fromfunction(
lambda y, x: 75 - (50.0 / 5000) * y, (5000, 100))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
with catch_warnings() as w:
@@ -275,10 +276,10 @@ class Test(unittest.TestCase):
self.assertAlmostEqual(cross_sum, expected)
def test_gauss_fwhm(self):
- data = numpy.fromfunction(lambda y, x: (y + x) * 10 ** -5, (5000, 100))
- lons = numpy.fromfunction(
+ data = np.fromfunction(lambda y, x: (y + x) * 10 ** -5, (5000, 100))
+ lons = np.fromfunction(
lambda y, x: 3 + (10.0 / 100) * x, (5000, 100))
- lats = numpy.fromfunction(
+ lats = np.fromfunction(
lambda y, x: 75 - (50.0 / 5000) * y, (5000, 100))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
with catch_warnings() as w:
@@ -291,14 +292,14 @@ class Test(unittest.TestCase):
self.assertAlmostEqual(cross_sum, expected)
def test_gauss_multi(self):
- data = numpy.fromfunction(lambda y, x: (y + x) * 10 ** -6, (5000, 100))
- lons = numpy.fromfunction(
+ data = np.fromfunction(lambda y, x: (y + x) * 10 ** -6, (5000, 100))
+ lons = np.fromfunction(
lambda y, x: 3 + (10.0 / 100) * x, (5000, 100))
- lats = numpy.fromfunction(
+ lats = np.fromfunction(
lambda y, x: 75 - (50.0 / 5000) * y, (5000, 100))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
- data_multi = numpy.column_stack((data.ravel(), data.ravel(),
- data.ravel()))
+ data_multi = np.column_stack((data.ravel(), data.ravel(),
+ data.ravel()))
with catch_warnings() as w:
res = kd_tree.resample_gauss(swath_def, data_multi,
self.area_def, 50000, [25000, 15000, 10000], segments=1)
@@ -309,14 +310,14 @@ class Test(unittest.TestCase):
self.assertAlmostEqual(cross_sum, expected)
def test_gauss_multi_uncert(self):
- data = numpy.fromfunction(lambda y, x: (y + x) * 10 ** -6, (5000, 100))
- lons = numpy.fromfunction(
+ data = np.fromfunction(lambda y, x: (y + x) * 10 ** -6, (5000, 100))
+ lons = np.fromfunction(
lambda y, x: 3 + (10.0 / 100) * x, (5000, 100))
- lats = numpy.fromfunction(
+ lats = np.fromfunction(
lambda y, x: 75 - (50.0 / 5000) * y, (5000, 100))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
- data_multi = numpy.column_stack((data.ravel(), data.ravel(),
- data.ravel()))
+ data_multi = np.column_stack((data.ravel(), data.ravel(),
+ data.ravel()))
with catch_warnings() as w:
# The assertion below checks if there is only one warning raised
# and whether it contains a specific message from pyresample
@@ -345,14 +346,14 @@ class Test(unittest.TestCase):
self.assertAlmostEqual(cross_sum_counts, expected_counts)
def test_gauss_multi_mp(self):
- data = numpy.fromfunction(lambda y, x: (y + x) * 10 ** -6, (5000, 100))
- lons = numpy.fromfunction(
+ data = np.fromfunction(lambda y, x: (y + x) * 10 ** -6, (5000, 100))
+ lons = np.fromfunction(
lambda y, x: 3 + (10.0 / 100) * x, (5000, 100))
- lats = numpy.fromfunction(
+ lats = np.fromfunction(
lambda y, x: 75 - (50.0 / 5000) * y, (5000, 100))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
- data_multi = numpy.column_stack((data.ravel(), data.ravel(),
- data.ravel()))
+ data_multi = np.column_stack((data.ravel(), data.ravel(),
+ data.ravel()))
with catch_warnings() as w:
res = kd_tree.resample_gauss(swath_def, data_multi,
self.area_def, 50000, [
@@ -365,14 +366,14 @@ class Test(unittest.TestCase):
self.assertAlmostEqual(cross_sum, expected)
def test_gauss_multi_mp_segments(self):
- data = numpy.fromfunction(lambda y, x: (y + x) * 10 ** -6, (5000, 100))
- lons = numpy.fromfunction(
+ data = np.fromfunction(lambda y, x: (y + x) * 10 ** -6, (5000, 100))
+ lons = np.fromfunction(
lambda y, x: 3 + (10.0 / 100) * x, (5000, 100))
- lats = numpy.fromfunction(
+ lats = np.fromfunction(
lambda y, x: 75 - (50.0 / 5000) * y, (5000, 100))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
- data_multi = numpy.column_stack((data.ravel(), data.ravel(),
- data.ravel()))
+ data_multi = np.column_stack((data.ravel(), data.ravel(),
+ data.ravel()))
with catch_warnings() as w:
res = kd_tree.resample_gauss(swath_def, data_multi,
self.area_def, 50000, [
@@ -385,14 +386,14 @@ class Test(unittest.TestCase):
self.assertAlmostEqual(cross_sum, expected)
def test_gauss_multi_mp_segments_empty(self):
- data = numpy.fromfunction(lambda y, x: (y + x) * 10 ** -6, (5000, 100))
- lons = numpy.fromfunction(
+ data = np.fromfunction(lambda y, x: (y + x) * 10 ** -6, (5000, 100))
+ lons = np.fromfunction(
lambda y, x: 165 + (10.0 / 100) * x, (5000, 100))
- lats = numpy.fromfunction(
+ lats = np.fromfunction(
lambda y, x: 75 - (50.0 / 5000) * y, (5000, 100))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
- data_multi = numpy.column_stack((data.ravel(), data.ravel(),
- data.ravel()))
+ data_multi = np.column_stack((data.ravel(), data.ravel(),
+ data.ravel()))
res = kd_tree.resample_gauss(swath_def, data_multi,
self.area_def, 50000, [
25000, 15000, 10000],
@@ -404,10 +405,10 @@ class Test(unittest.TestCase):
def wf(dist):
return 1 - dist / 100000.0
- data = numpy.fromfunction(lambda y, x: (y + x) * 10 ** -5, (5000, 100))
- lons = numpy.fromfunction(
+ data = np.fromfunction(lambda y, x: (y + x) * 10 ** -5, (5000, 100))
+ lons = np.fromfunction(
lambda y, x: 3 + (10.0 / 100) * x, (5000, 100))
- lats = numpy.fromfunction(
+ lats = np.fromfunction(
lambda y, x: 75 - (50.0 / 5000) * y, (5000, 100))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
with catch_warnings() as w:
@@ -427,16 +428,16 @@ class Test(unittest.TestCase):
return 1
def wf3(dist):
- return numpy.cos(dist) ** 2
+ return np.cos(dist) ** 2
- data = numpy.fromfunction(lambda y, x: (y + x) * 10 ** -6, (5000, 100))
- lons = numpy.fromfunction(
+ data = np.fromfunction(lambda y, x: (y + x) * 10 ** -6, (5000, 100))
+ lons = np.fromfunction(
lambda y, x: 3 + (10.0 / 100) * x, (5000, 100))
- lats = numpy.fromfunction(
+ lats = np.fromfunction(
lambda y, x: 75 - (50.0 / 5000) * y, (5000, 100))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
- data_multi = numpy.column_stack((data.ravel(), data.ravel(),
- data.ravel()))
+ data_multi = np.column_stack((data.ravel(), data.ravel(),
+ data.ravel()))
with catch_warnings() as w:
res = kd_tree.resample_custom(swath_def, data_multi,
self.area_def, 50000, [wf1, wf2, wf3], segments=1)
@@ -447,150 +448,150 @@ class Test(unittest.TestCase):
self.assertAlmostEqual(cross_sum, expected)
def test_masked_nearest(self):
- data = numpy.ones((50, 10))
+ data = np.ones((50, 10))
data[:, 5:] = 2
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
- mask = numpy.ones((50, 10))
+ mask = np.ones((50, 10))
mask[:, :5] = 0
- masked_data = numpy.ma.array(data, mask=mask)
+ masked_data = np.ma.array(data, mask=mask)
res = kd_tree.resample_nearest(swath_def, masked_data.ravel(),
self.area_def, 50000, segments=1)
- expected_mask = numpy.fromfile(os.path.join(os.path.dirname(__file__),
- 'test_files',
- 'mask_test_nearest_mask.dat'),
- sep=' ').reshape((800, 800))
- expected_data = numpy.fromfile(os.path.join(os.path.dirname(__file__),
- 'test_files',
- 'mask_test_nearest_data.dat'),
- sep=' ').reshape((800, 800))
- self.assertTrue(numpy.array_equal(expected_mask, res.mask))
- self.assertTrue(numpy.array_equal(expected_data, res.data))
+ expected_mask = np.fromfile(os.path.join(os.path.dirname(__file__),
+ 'test_files',
+ 'mask_test_nearest_mask.dat'),
+ sep=' ').reshape((800, 800))
+ expected_data = np.fromfile(os.path.join(os.path.dirname(__file__),
+ 'test_files',
+ 'mask_test_nearest_data.dat'),
+ sep=' ').reshape((800, 800))
+ self.assertTrue(np.array_equal(expected_mask, res.mask))
+ self.assertTrue(np.array_equal(expected_data, res.data))
def test_masked_nearest_1d(self):
- data = numpy.ones((800, 800))
+ data = np.ones((800, 800))
data[:400, :] = 2
- lons = numpy.fromfunction(lambda x: 3 + x / 100., (500,))
- lats = numpy.fromfunction(lambda x: 75 - x / 10., (500,))
+ lons = np.fromfunction(lambda x: 3 + x / 100., (500,))
+ lats = np.fromfunction(lambda x: 75 - x / 10., (500,))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
- mask = numpy.ones((800, 800))
+ mask = np.ones((800, 800))
mask[400:, :] = 0
- masked_data = numpy.ma.array(data, mask=mask)
+ masked_data = np.ma.array(data, mask=mask)
res = kd_tree.resample_nearest(self.area_def, masked_data.ravel(),
swath_def, 50000, segments=1)
self.assertEqual(res.mask.sum(), 112)
def test_masked_gauss(self):
- data = numpy.ones((50, 10))
+ data = np.ones((50, 10))
data[:, 5:] = 2
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
- mask = numpy.ones((50, 10))
+ mask = np.ones((50, 10))
mask[:, :5] = 0
- masked_data = numpy.ma.array(data, mask=mask)
+ masked_data = np.ma.array(data, mask=mask)
res = kd_tree.resample_gauss(swath_def, masked_data.ravel(),
self.area_def, 50000, 25000, segments=1)
- expected_mask = numpy.fromfile(os.path.join(os.path.dirname(__file__),
- 'test_files',
- 'mask_test_mask.dat'),
- sep=' ').reshape((800, 800))
- expected_data = numpy.fromfile(os.path.join(os.path.dirname(__file__),
- 'test_files',
- 'mask_test_data.dat'),
- sep=' ').reshape((800, 800))
+ expected_mask = np.fromfile(os.path.join(os.path.dirname(__file__),
+ 'test_files',
+ 'mask_test_mask.dat'),
+ sep=' ').reshape((800, 800))
+ expected_data = np.fromfile(os.path.join(os.path.dirname(__file__),
+ 'test_files',
+ 'mask_test_data.dat'),
+ sep=' ').reshape((800, 800))
expected = expected_data.sum()
cross_sum = res.data.sum()
- self.assertTrue(numpy.array_equal(expected_mask, res.mask))
+ self.assertTrue(np.array_equal(expected_mask, res.mask))
self.assertAlmostEqual(cross_sum, expected, places=3)
def test_masked_fill_float(self):
- data = numpy.ones((50, 10))
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ data = np.ones((50, 10))
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
res = kd_tree.resample_nearest(swath_def, data.ravel(),
self.area_def, 50000, fill_value=None, segments=1)
- expected_fill_mask = numpy.fromfile(os.path.join(os.path.dirname(__file__),
- 'test_files',
- 'mask_test_fill_value.dat'),
- sep=' ').reshape((800, 800))
+ expected_fill_mask = np.fromfile(os.path.join(os.path.dirname(__file__),
+ 'test_files',
+ 'mask_test_fill_value.dat'),
+ sep=' ').reshape((800, 800))
fill_mask = res.mask
- self.assertTrue(numpy.array_equal(fill_mask, expected_fill_mask))
+ self.assertTrue(np.array_equal(fill_mask, expected_fill_mask))
def test_masked_fill_int(self):
- data = numpy.ones((50, 10)).astype('int')
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ data = np.ones((50, 10)).astype('int')
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
res = kd_tree.resample_nearest(swath_def, data.ravel(),
self.area_def, 50000, fill_value=None, segments=1)
- expected_fill_mask = numpy.fromfile(os.path.join(os.path.dirname(__file__),
- 'test_files',
- 'mask_test_fill_value.dat'),
- sep=' ').reshape((800, 800))
+ expected_fill_mask = np.fromfile(os.path.join(os.path.dirname(__file__),
+ 'test_files',
+ 'mask_test_fill_value.dat'),
+ sep=' ').reshape((800, 800))
fill_mask = res.mask
- self.assertTrue(numpy.array_equal(fill_mask, expected_fill_mask))
+ self.assertTrue(np.array_equal(fill_mask, expected_fill_mask))
def test_masked_full(self):
- data = numpy.ones((50, 10))
+ data = np.ones((50, 10))
data[:, 5:] = 2
- mask = numpy.ones((50, 10))
+ mask = np.ones((50, 10))
mask[:, :5] = 0
- masked_data = numpy.ma.array(data, mask=mask)
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ masked_data = np.ma.array(data, mask=mask)
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
res = kd_tree.resample_nearest(swath_def,
masked_data.ravel(
), self.area_def, 50000,
fill_value=None, segments=1)
- expected_fill_mask = numpy.fromfile(os.path.join(os.path.dirname(__file__),
- 'test_files',
- 'mask_test_full_fill.dat'),
- sep=' ').reshape((800, 800))
+ expected_fill_mask = np.fromfile(os.path.join(os.path.dirname(__file__),
+ 'test_files',
+ 'mask_test_full_fill.dat'),
+ sep=' ').reshape((800, 800))
fill_mask = res.mask
- self.assertTrue(numpy.array_equal(fill_mask, expected_fill_mask))
+ self.assertTrue(np.array_equal(fill_mask, expected_fill_mask))
def test_masked_full_multi(self):
- data = numpy.ones((50, 10))
+ data = np.ones((50, 10))
data[:, 5:] = 2
- mask1 = numpy.ones((50, 10))
+ mask1 = np.ones((50, 10))
mask1[:, :5] = 0
- mask2 = numpy.ones((50, 10))
+ mask2 = np.ones((50, 10))
mask2[:, 5:] = 0
- mask3 = numpy.ones((50, 10))
+ mask3 = np.ones((50, 10))
mask3[:25, :] = 0
- data_multi = numpy.column_stack(
+ data_multi = np.column_stack(
(data.ravel(), data.ravel(), data.ravel()))
- mask_multi = numpy.column_stack(
+ mask_multi = np.column_stack(
(mask1.ravel(), mask2.ravel(), mask3.ravel()))
- masked_data = numpy.ma.array(data_multi, mask=mask_multi)
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ masked_data = np.ma.array(data_multi, mask=mask_multi)
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
res = kd_tree.resample_nearest(swath_def,
masked_data, self.area_def, 50000,
fill_value=None, segments=1)
- expected_fill_mask = numpy.fromfile(os.path.join(os.path.dirname(__file__),
- 'test_files',
- 'mask_test_full_fill_multi.dat'),
- sep=' ').reshape((800, 800, 3))
+ expected_fill_mask = np.fromfile(os.path.join(os.path.dirname(__file__),
+ 'test_files',
+ 'mask_test_full_fill_multi.dat'),
+ sep=' ').reshape((800, 800, 3))
fill_mask = res.mask
cross_sum = res.sum()
expected = 357140.0
self.assertAlmostEqual(cross_sum, expected)
- self.assertTrue(numpy.array_equal(fill_mask, expected_fill_mask))
+ self.assertTrue(np.array_equal(fill_mask, expected_fill_mask))
def test_dtype(self):
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
grid_def = geometry.GridDefinition(lons, lats)
- lons = numpy.asarray(lons, dtype='f4')
- lats = numpy.asarray(lats, dtype='f4')
+ lons = np.asarray(lons, dtype='f4')
+ lats = np.asarray(lats, dtype='f4')
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
valid_input_index, valid_output_index, index_array, distance_array = \
kd_tree.get_neighbour_info(swath_def,
@@ -598,9 +599,9 @@ class Test(unittest.TestCase):
50000, neighbours=1, segments=1)
def test_nearest_from_sample(self):
- data = numpy.fromfunction(lambda y, x: y * x, (50, 10))
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ data = np.fromfunction(lambda y, x: y * x, (50, 10))
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
valid_input_index, valid_output_index, index_array, distance_array = \
kd_tree.get_neighbour_info(swath_def,
@@ -621,16 +622,16 @@ class Test(unittest.TestCase):
return 1
def wf3(dist):
- return numpy.cos(dist) ** 2
+ return np.cos(dist) ** 2
- data = numpy.fromfunction(lambda y, x: (y + x) * 10 ** -6, (5000, 100))
- lons = numpy.fromfunction(
+ data = np.fromfunction(lambda y, x: (y + x) * 10 ** -6, (5000, 100))
+ lons = np.fromfunction(
lambda y, x: 3 + (10.0 / 100) * x, (5000, 100))
- lats = numpy.fromfunction(
+ lats = np.fromfunction(
lambda y, x: 75 - (50.0 / 5000) * y, (5000, 100))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
- data_multi = numpy.column_stack((data.ravel(), data.ravel(),
- data.ravel()))
+ data_multi = np.column_stack((data.ravel(), data.ravel(),
+ data.ravel()))
with catch_warnings() as w:
valid_input_index, valid_output_index, index_array, distance_array = \
@@ -662,21 +663,21 @@ class Test(unittest.TestCase):
self.assertAlmostEqual(cross_sum, expected)
def test_masked_multi_from_sample(self):
- data = numpy.ones((50, 10))
+ data = np.ones((50, 10))
data[:, 5:] = 2
- mask1 = numpy.ones((50, 10))
+ mask1 = np.ones((50, 10))
mask1[:, :5] = 0
- mask2 = numpy.ones((50, 10))
+ mask2 = np.ones((50, 10))
mask2[:, 5:] = 0
- mask3 = numpy.ones((50, 10))
+ mask3 = np.ones((50, 10))
mask3[:25, :] = 0
- data_multi = numpy.column_stack(
+ data_multi = np.column_stack(
(data.ravel(), data.ravel(), data.ravel()))
- mask_multi = numpy.column_stack(
+ mask_multi = np.column_stack(
(mask1.ravel(), mask2.ravel(), mask3.ravel()))
- masked_data = numpy.ma.array(data_multi, mask=mask_multi)
- lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
- lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ masked_data = np.ma.array(data_multi, mask=mask_multi)
+ lons = np.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = np.fromfunction(lambda y, x: 75 - y, (50, 10))
swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
valid_input_index, valid_output_index, index_array, distance_array = \
kd_tree.get_neighbour_info(swath_def,
@@ -687,20 +688,218 @@ class Test(unittest.TestCase):
valid_input_index,
valid_output_index, index_array,
fill_value=None)
- expected_fill_mask = numpy.fromfile(os.path.join(os.path.dirname(__file__),
- 'test_files',
- 'mask_test_full_fill_multi.dat'),
- sep=' ').reshape((800, 800, 3))
+ expected_fill_mask = np.fromfile(os.path.join(os.path.dirname(__file__),
+ 'test_files',
+ 'mask_test_full_fill_multi.dat'),
+ sep=' ').reshape((800, 800, 3))
fill_mask = res.mask
- self.assertTrue(numpy.array_equal(fill_mask, expected_fill_mask))
+ self.assertTrue(np.array_equal(fill_mask, expected_fill_mask))
+
+
+class TestXArrayResamplerNN(unittest.TestCase):
+ """Test the XArrayResamplerNN class."""
+
+ @classmethod
+ def setUpClass(cls):
+ import xarray as xr
+ import dask.array as da
+ cls.area_def = geometry.AreaDefinition('areaD',
+ 'Europe (3km, HRV, VTC)',
+ 'areaD',
+ {'a': '6378144.0',
+ 'b': '6356759.0',
+ 'lat_0': '50.00',
+ 'lat_ts': '50.00',
+ 'lon_0': '8.00',
+ 'proj': 'stere'},
+ 800,
+ 800,
+ [-1370912.72,
+ -909968.64000000001,
+ 1029087.28,
+ 1490031.3600000001])
+
+ dfa = da.from_array # shortcut
+ cls.chunks = chunks = 5
+ cls.tgrid = geometry.CoordinateDefinition(
+ lons=dfa(np.array([
+ [11.5, 12.562036, 12.9],
+ [11.5, 12.562036, 12.9],
+ [11.5, 12.562036, 12.9],
+ [11.5, 12.562036, 12.9],
+ ]), chunks=chunks),
+ lats=dfa(np.array([
+ [55.715613, 55.715613, 55.715613],
+ [55.715613, 55.715613, 55.715613],
+ [55.715613, np.nan, 55.715613],
+ [55.715613, 55.715613, 55.715613],
+ ]), chunks=chunks))
+
+ cls.tdata_1d = xr.DataArray(
+ dfa(np.array([1., 2., 3.]), chunks=chunks), dims=('my_dim1',))
+ cls.tlons_1d = xr.DataArray(
+ dfa(np.array([11.280789, 12.649354, 12.080402]), chunks=chunks),
+ dims=('my_dim1',))
+ cls.tlats_1d = xr.DataArray(
+ dfa(np.array([56.011037, 55.629675, 55.641535]), chunks=chunks),
+ dims=('my_dim1',))
+ cls.tswath_1d = geometry.SwathDefinition(lons=cls.tlons_1d,
+ lats=cls.tlats_1d)
+
+ cls.data_2d = xr.DataArray(
+ da.from_array(np.fromfunction(lambda y, x: y * x, (50, 10)),
+ chunks=5),
+ dims=('my_dim_y', 'my_dim_x'))
+ cls.data_3d = xr.DataArray(
+ da.from_array(np.fromfunction(lambda y, x, b: y * x * b, (50, 10, 3)),
+ chunks=5),
+ dims=('my_dim_y', 'my_dim_x', 'bands'),
+ coords={'bands': ['r', 'g', 'b']})
+ cls.lons_2d = xr.DataArray(
+ da.from_array(np.fromfunction(lambda y, x: 3 + x, (50, 10)),
+ chunks=5),
+ dims=('my_dim_y', 'my_dim_x'))
+ cls.lats_2d = xr.DataArray(
+ da.from_array(np.fromfunction(lambda y, x: 75 - y, (50, 10)),
+ chunks=5),
+ dims=('my_dim_y', 'my_dim_x'))
+ cls.swath_def_2d = geometry.SwathDefinition(lons=cls.lons_2d,
+ lats=cls.lats_2d)
+ cls.src_area_2d = geometry.AreaDefinition(
+ 'areaD_src', 'Europe (3km, HRV, VTC)', 'areaD',
+ {'a': '6378144.0', 'b': '6356759.0', 'lat_0': '52.00',
+ 'lat_ts': '52.00', 'lon_0': '5.00', 'proj': 'stere'}, 50, 10,
+ [-1370912.72, -909968.64000000001, 1029087.28,
+ 1490031.3600000001])
+
+ def test_nearest_swath_1d_mask_to_grid_1n(self):
+ """Test 1D swath definition to 2D grid definition; 1 neighbor."""
+ from pyresample.kd_tree import XArrayResamplerNN
+ import xarray as xr
+ import dask.array as da
+ resampler = XArrayResamplerNN(self.tswath_1d, self.tgrid,
+ radius_of_influence=100000,
+ neighbours=1)
+ data = self.tdata_1d
+ ninfo = resampler.get_neighbour_info(mask=data.isnull())
+ for val in ninfo[:3]:
+ # vii, ia, voi
+ self.assertIsInstance(val, da.Array)
+ res = resampler.get_sample_from_neighbour_info(data)
+ self.assertIsInstance(res, xr.DataArray)
+ self.assertIsInstance(res.data, da.Array)
+ actual = res.values
+ expected = np.array([
+ [1., 2., 2.],
+ [1., 2., 2.],
+ [1., np.nan, 2.],
+ [1., 2., 2.],
+ ])
+ np.testing.assert_allclose(actual, expected)
+
+ def test_nearest_swath_2d_mask_to_area_1n(self):
+ """Test 2D swath definition to 2D area definition; 1 neighbor."""
+ from pyresample.kd_tree import XArrayResamplerNN
+ import xarray as xr
+ import dask.array as da
+ swath_def = self.swath_def_2d
+ data = self.data_2d
+ resampler = XArrayResamplerNN(swath_def, self.area_def,
+ radius_of_influence=50000,
+ neighbours=1)
+ ninfo = resampler.get_neighbour_info(mask=data.isnull())
+ for val in ninfo[:3]:
+ # vii, ia, voi
+ self.assertIsInstance(val, da.Array)
+ res = resampler.get_sample_from_neighbour_info(data)
+ self.assertIsInstance(res, xr.DataArray)
+ self.assertIsInstance(res.data, da.Array)
+ res = res.values
+ cross_sum = np.nansum(res)
+ expected = 15874591.0
+ self.assertEqual(cross_sum, expected)
+
+ def test_nearest_area_2d_to_area_1n(self):
+ """Test 2D area definition to 2D area definition; 1 neighbor."""
+ from pyresample.kd_tree import XArrayResamplerNN
+ import xarray as xr
+ import dask.array as da
+ data = self.data_2d
+ resampler = XArrayResamplerNN(self.src_area_2d, self.area_def,
+ radius_of_influence=50000,
+ neighbours=1)
+ ninfo = resampler.get_neighbour_info()
+ for val in ninfo[:3]:
+ # vii, ia, voi
+ self.assertIsInstance(val, da.Array)
+ self.assertRaises(AssertionError,
+ resampler.get_sample_from_neighbour_info, data)
+
+ # rename data dimensions to match the expected area dimensions
+ data = data.rename({'my_dim_y': 'y', 'my_dim_x': 'x'})
+ res = resampler.get_sample_from_neighbour_info(data)
+ self.assertIsInstance(res, xr.DataArray)
+ self.assertIsInstance(res.data, da.Array)
+ res = res.values
+ cross_sum = np.nansum(res)
+ expected = 27706753.0
+ self.assertEqual(cross_sum, expected)
+
+ def test_nearest_area_2d_to_area_1n_3d_data(self):
+ """Test 2D area definition to 2D area definition; 1 neighbor, 3d data."""
+ from pyresample.kd_tree import XArrayResamplerNN
+ import xarray as xr
+ import dask.array as da
+ data = self.data_3d
+ resampler = XArrayResamplerNN(self.src_area_2d, self.area_def,
+ radius_of_influence=50000,
+ neighbours=1)
+ ninfo = resampler.get_neighbour_info()
+ for val in ninfo[:3]:
+ # vii, ia, voi
+ self.assertIsInstance(val, da.Array)
+ self.assertRaises(AssertionError,
+ resampler.get_sample_from_neighbour_info, data)
+
+ # rename data dimensions to match the expected area dimensions
+ data = data.rename({'my_dim_y': 'y', 'my_dim_x': 'x'})
+ res = resampler.get_sample_from_neighbour_info(data)
+ self.assertIsInstance(res, xr.DataArray)
+ self.assertIsInstance(res.data, da.Array)
+ six.assertCountEqual(self, res.coords['bands'], ['r', 'g', 'b'])
+ res = res.values
+ cross_sum = np.nansum(res)
+ expected = 83120259.0
+ self.assertEqual(cross_sum, expected)
+
+ @unittest.skipIf(True, "Multiple neighbors not supported yet")
+ def test_nearest_swath_1d_mask_to_grid_8n(self):
+ """Test 1D swath definition to 2D grid definition; 8 neighbors."""
+ from pyresample.kd_tree import XArrayResamplerNN
+ import xarray as xr
+ import dask.array as da
+ resampler = XArrayResamplerNN(self.tswath_1d, self.tgrid,
+ radius_of_influence=100000,
+ neighbours=8)
+ data = self.tdata_1d
+ ninfo = resampler.get_neighbour_info(mask=data.isnull())
+ for val in ninfo[:3]:
+ # vii, ia, voi
+ self.assertIsInstance(val, da.Array)
+ res = resampler.get_sample_from_neighbour_info(data)
+ self.assertIsInstance(res, xr.DataArray)
+ self.assertIsInstance(res.data, da.Array)
+ # actual = res.values
+ # expected = TODO
+ # np.testing.assert_allclose(actual, expected)
def suite():
- """The test suite.
- """
+ """The test suite."""
loader = unittest.TestLoader()
mysuite = unittest.TestSuite()
mysuite.addTest(loader.loadTestsFromTestCase(Test))
+ mysuite.addTest(loader.loadTestsFromTestCase(TestXArrayResamplerNN))
return mysuite
=====================================
pyresample/test/test_spherical.py
=====================================
--- a/pyresample/test/test_spherical.py
+++ b/pyresample/test/test_spherical.py
@@ -185,7 +185,7 @@ class TestArc(unittest.TestCase):
lon, lat = arc1.intersection(arc2)
self.assertTrue(np.allclose(np.rad2deg(lon), 5))
- self.assertEquals(np.rad2deg(lat), 5.0575148968282093)
+ self.assertEquals(np.rad2deg(lat).round(7), round(5.0575148968282093, 7))
arc1 = Arc(SCoordinate(0, 0),
SCoordinate(np.deg2rad(10), np.deg2rad(10)))
=====================================
pyresample/version.py
=====================================
--- a/pyresample/version.py
+++ b/pyresample/version.py
@@ -16,4 +16,4 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-__version__ = '1.9.3'
+__version__ = '1.10.1'
=====================================
setup.py
=====================================
--- a/setup.py
+++ b/setup.py
@@ -27,7 +27,7 @@ from setuptools.command.build_ext import build_ext as _build_ext
version = imp.load_source('pyresample.version', 'pyresample/version.py')
requirements = ['setuptools>=3.2', 'pyproj>=1.9.5.1', 'numpy>=1.10.0', 'configobj',
- 'pykdtree>=1.1.1', 'pyyaml', 'six']
+ 'pykdtree>=1.3.1', 'pyyaml', 'six']
extras_require = {'pykdtree': ['pykdtree>=1.1.1'],
'numexpr': ['numexpr'],
'quicklook': ['matplotlib', 'cartopy', 'pillow'],
View it on GitLab: https://salsa.debian.org/debian-gis-team/pyresample/commit/5f4f8007f7a92cb8683c8843bc5d50dd63633019
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/pyresample/commit/5f4f8007f7a92cb8683c8843bc5d50dd63633019
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20180708/dde6212c/attachment-0001.html>
More information about the Pkg-grass-devel
mailing list