[Git][debian-gis-team/pyresample][master] 6 commits: New upstream version 1.10.1

Antonio Valentino gitlab at salsa.debian.org
Sun Jul 8 21:21:59 BST 2018


Antonio Valentino pushed to branch master at Debian GIS Project / pyresample


Commits:
5f4f8007 by Antonio Valentino at 2018-07-08T19:42:55+00:00
New upstream version 1.10.1
- - - - -
3943a345 by Antonio Valentino at 2018-07-08T19:43:09+00:00
Update upstream source from tag 'upstream/1.10.1'

Update to upstream version '1.10.1'
with Debian dir 504f82aa9e54a3b5741de087eaa93ad3a0570db5
- - - - -
f7bcd2d9 by Antonio Valentino at 2018-07-08T19:45:32+00:00
New upstream release

- - - - -
4d674d84 by Antonio Valentino at 2018-07-08T19:48:56+00:00
Require pykdtree >= 1.3.1

- - - - -
f54281a6 by Antonio Valentino at 2018-07-08T20:08:56+00:00
Drop test_spherical.patch

- - - - -
e8d6c9df by Antonio Valentino at 2018-07-08T22:14:45+02:00
Refresh all patches.

- - - - -


16 changed files:

- .bumpversion.cfg
- CHANGELOG.md
- debian/changelog
- debian/control
- debian/patches/0002-fix-proj4-initialization.patch
- debian/patches/series
- − debian/patches/test_spherical.patch
- 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


=====================================
debian/changelog
=====================================
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,7 +1,16 @@
-pyresample (1.9.3-3) UNRELEASED; urgency=medium
+pyresample (1.10.1-1) UNRELEASED; urgency=medium
 
+  [ Bas Couwenberg ]
   * Bump Standards-Version to 4.1.5, no changes.
 
+  [ Antonio Valentino ]
+  * New upstream release.
+  * debian/control
+    - require pykdtree >= 1.3.1.
+  * debian/patches
+    - drop test_spherical.patch, applied upstrem.
+    - refresh remaining patches.
+
  -- Bas Couwenberg <sebastic at debian.org>  Thu, 05 Jul 2018 11:02:39 +0200
 
 pyresample (1.9.3-2) unstable; urgency=medium


=====================================
debian/control
=====================================
--- a/debian/control
+++ b/debian/control
@@ -11,8 +11,8 @@ Build-Depends: debhelper (>= 11.0.0),
                python3-setuptools,
                python-numpy,
                python3-numpy,
-               python-pykdtree,
-               python3-pykdtree,
+               python-pykdtree (>= 1.3.1),
+               python3-pykdtree (>= 1.3.1),
                python-pyproj,
                python3-pyproj,
                python-configobj,
@@ -49,7 +49,7 @@ Depends: ${misc:Depends},
          python-numpy,
          python-pyproj,
          python-yaml,
-         python-pykdtree,
+         python-pykdtree (>= 1.3.1),
          python-six,
          python-pyresample-test
 Recommends: python-numexpr,
@@ -78,7 +78,7 @@ Depends: ${misc:Depends},
          python3-numpy,
          python3-pyproj,
          python3-yaml,
-         python3-pykdtree,
+         python3-pykdtree (>= 1.3.1),
          python3-six,
          python-pyresample-test
 Recommends: python3-numexpr,


=====================================
debian/patches/0002-fix-proj4-initialization.patch
=====================================
--- a/debian/patches/0002-fix-proj4-initialization.patch
+++ b/debian/patches/0002-fix-proj4-initialization.patch
@@ -21,7 +21,7 @@ index 3c6ef3c..5346cb4 100644
    YSIZE: 480
    AREA_EXTENT:  (-20037508.342789244, -10018754.171394622, 20037508.342789244, 10018754.171394622)
 diff --git a/pyresample/test/test_geometry.py b/pyresample/test/test_geometry.py
-index d90ae97..449eb2c 100644
+index 15c0813..0077a31 100644
 --- a/pyresample/test/test_geometry.py
 +++ b/pyresample/test/test_geometry.py
 @@ -401,7 +401,7 @@ class Test(unittest.TestCase):


=====================================
debian/patches/series
=====================================
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,3 +1,2 @@
 0001-fix-doc-build.patch
 0002-fix-proj4-initialization.patch
-test_spherical.patch


=====================================
debian/patches/test_spherical.patch deleted
=====================================
--- a/debian/patches/test_spherical.patch
+++ /dev/null
@@ -1,17 +0,0 @@
-Description: Fix TestArc failure on some architectures.
- AssertionError: array([5.0575149]) != 5.057514896828209
-Author: Bas Couwenberg <sebastic at debian.org>
-Forwarded: https://github.com/pytroll/pyresample/pull/127
-Applied-Upstream: https://github.com/pytroll/pyresample/commit/80567dbf832c7e45174cca911a192c16cc8b3f58
-
---- 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)))


=====================================
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/compare/29e9d89b8da0930889c462d6cc2fc4a23326902b...e8d6c9df52e50797623b9f542cf9c07c5e266509

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/pyresample/compare/29e9d89b8da0930889c462d6cc2fc4a23326902b...e8d6c9df52e50797623b9f542cf9c07c5e266509
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/1de7aa3c/attachment-0001.html>


More information about the Pkg-grass-devel mailing list