[Git][debian-gis-team/pyresample][master] 4 commits: New upstream version 1.27.1
Antonio Valentino (@antonio.valentino)
gitlab at salsa.debian.org
Fri Jun 23 07:19:46 BST 2023
Antonio Valentino pushed to branch master at Debian GIS Project / pyresample
Commits:
db4994c7 by Antonio Valentino at 2023-06-23T06:16:06+00:00
New upstream version 1.27.1
- - - - -
5ed6aefa by Antonio Valentino at 2023-06-23T06:17:14+00:00
Update upstream source from tag 'upstream/1.27.1'
Update to upstream version '1.27.1'
with Debian dir 5e581fd8811b020fa80d8da5ed2c1f09aacbf314
- - - - -
c14afe93 by Antonio Valentino at 2023-06-23T06:18:17+00:00
New upstream release
- - - - -
f75559e3 by Antonio Valentino at 2023-06-23T06:19:13+00:00
Set distribution to unstable
- - - - -
11 changed files:
- .github/workflows/ci.yaml
- .readthedocs.yml
- CHANGELOG.md
- debian/changelog
- pyresample/geometry.py
- pyresample/resampler.py
- pyresample/test/test_geometry/test_area.py
- pyresample/test/test_geometry_legacy.py
- pyresample/test/test_gradient.py
- pyresample/test/test_resamplers/test_resampler.py
- pyresample/version.py
Changes:
=====================================
.github/workflows/ci.yaml
=====================================
@@ -8,37 +8,6 @@ concurrency:
on: [push, pull_request]
jobs:
- website:
- name: build website
- runs-on: ubuntu-latest
- steps:
- - name: Checkout source
- uses: actions/checkout at v3
- with:
- fetch-depth: 0
-
- - name: Setup Conda Environment
- uses: conda-incubator/setup-miniconda at v2
- with:
- miniforge-variant: Mambaforge
- miniforge-version: latest
- use-mamba: true
- python-version: "3.11"
- environment-file: continuous_integration/environment.yaml
- activate-environment: test-environment
-
- - name: Install Satpy
- shell: bash -l {0}
- run: |
- pip install sphinx sphinx_rtd_theme sphinxcontrib-apidoc sphinx-reredirects
- pip install --no-deps -e .
-
- - name: Run Sphinx Build
- shell: bash -l {0}
- run: |
- cd docs
- make html SPHINXOPTS="-W"
-
test:
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
=====================================
.readthedocs.yml
=====================================
@@ -5,6 +5,11 @@ build:
tools:
python: "mambaforge-4.10"
+# Build documentation in the docs/ directory with Sphinx
+sphinx:
+ configuration: docs/source/conf.py
+ fail_on_warning: true
+
conda:
environment: docs/environment.yml
=====================================
CHANGELOG.md
=====================================
@@ -1,3 +1,26 @@
+## Version 1.27.1 (2023/06/21)
+
+### Issues Closed
+
+* [Issue 517](https://github.com/pytroll/pyresample/issues/517) - EWA resampling in 1.27 slows down four times than 1.26.1 ([PR 520](https://github.com/pytroll/pyresample/pull/520) by [@djhoese](https://github.com/djhoese))
+
+In this release 1 issue was closed.
+
+### Pull Requests Merged
+
+#### Bugs fixed
+
+* [PR 524](https://github.com/pytroll/pyresample/pull/524) - Preserve get_area_slices behavior when area to cover has an invalid boundary
+* [PR 523](https://github.com/pytroll/pyresample/pull/523) - Fix DynamicAreaDefinition not preserving user's requested resolution ([517](https://github.com/pytroll/pyresample/issues/517))
+* [PR 520](https://github.com/pytroll/pyresample/pull/520) - Fix performance regression in base resampler class when comparing geometries ([517](https://github.com/pytroll/pyresample/issues/517))
+
+#### Documentation changes
+
+* [PR 518](https://github.com/pytroll/pyresample/pull/518) - Add configuration for readthedocs to fail on warnings
+
+In this release 4 pull requests were closed.
+
+
## Version 1.27.0 (2023/05/17)
### Issues Closed
=====================================
debian/changelog
=====================================
@@ -1,3 +1,9 @@
+pyresample (1.27.1-1) unstable; urgency=medium
+
+ * New upstream release.
+
+ -- Antonio Valentino <antonio.valentino at tiscali.it> Fri, 23 Jun 2023 06:18:51 +0000
+
pyresample (1.27.0-1) unstable; urgency=medium
[ Bas Couwenberg ]
=====================================
pyresample/geometry.py
=====================================
@@ -1127,15 +1127,24 @@ class DynamicAreaDefinition(object):
height, width = shape
x_resolution = (corners[2] - corners[0]) * 1.0 / (width - 1)
y_resolution = (corners[3] - corners[1]) * 1.0 / (height - 1)
+ area_extent = (corners[0] - x_resolution / 2,
+ corners[1] - y_resolution / 2,
+ corners[2] + x_resolution / 2,
+ corners[3] + y_resolution / 2)
else:
x_resolution, y_resolution = resolution
- width = int(np.rint((corners[2] - corners[0]) * 1.0 / x_resolution + 1))
- height = int(np.rint((corners[3] - corners[1]) * 1.0 / y_resolution + 1))
+ half_x = x_resolution / 2
+ half_y = y_resolution / 2
+ # align extents with pixel resolution
+ area_extent = (
+ math.floor((corners[0] - half_x) / x_resolution) * x_resolution,
+ math.floor((corners[1] - half_y) / y_resolution) * y_resolution,
+ math.ceil((corners[2] + half_x) / x_resolution) * x_resolution,
+ math.ceil((corners[3] + half_y) / y_resolution) * y_resolution,
+ )
+ width = int(round((area_extent[2] - area_extent[0]) / x_resolution))
+ height = int(round((area_extent[3] - area_extent[1]) / y_resolution))
- area_extent = (corners[0] - x_resolution / 2,
- corners[1] - y_resolution / 2,
- corners[2] + x_resolution / 2,
- corners[3] + y_resolution / 2)
return area_extent, width, height
def _update_corners_for_full_extent(self, corners, shape, resolution, projection):
@@ -2592,14 +2601,8 @@ class AreaDefinition(_ProjectionDefinition):
"equal.")
data_boundary = Boundary(*get_geostationary_bounding_box_in_lonlats(self))
- if area_to_cover.is_geostationary:
- area_boundary = Boundary(
- *get_geostationary_bounding_box_in_lonlats(area_to_cover))
- else:
- area_boundary = AreaDefBoundary(area_to_cover, 100)
-
- intersection = data_boundary.contour_poly.intersection(
- area_boundary.contour_poly)
+ area_boundary = self._get_area_to_cover_boundary(area_to_cover)
+ intersection = data_boundary.contour_poly.intersection(area_boundary.contour_poly)
if intersection is None:
logger.debug('Cannot determine appropriate slicing. '
"Data and projection area do not overlap.")
@@ -2619,6 +2622,15 @@ class AreaDefinition(_ProjectionDefinition):
return (check_slice_orientation(x_slice),
check_slice_orientation(y_slice))
+ @staticmethod
+ def _get_area_to_cover_boundary(area_to_cover: AreaDefinition) -> Boundary:
+ try:
+ if area_to_cover.is_geostationary:
+ return Boundary(*get_geostationary_bounding_box_in_lonlats(area_to_cover))
+ return AreaDefBoundary(area_to_cover, 100)
+ except ValueError:
+ raise NotImplementedError("Can't determine boundary of area to cover")
+
def crop_around(self, other_area):
"""Crop this area around `other_area`."""
xslice, yslice = self.get_area_slices(other_area)
=====================================
pyresample/resampler.py
=====================================
@@ -121,7 +121,7 @@ class BaseResampler:
Returns (xarray.DataArray): Data resampled to the target area
"""
- if self.source_geo_def == self.target_geo_def:
+ if self._geometries_are_the_same():
return data
# default is to mask areas for SwathDefinitions
if mask_area is None and isinstance(
@@ -143,6 +143,42 @@ class BaseResampler:
cache_id = self.precompute(cache_dir=cache_dir, **kwargs)
return self.compute(data, cache_id=cache_id, **kwargs)
+ def _geometries_are_the_same(self):
+ """Check if two geometries are the same object and resampling isn't needed.
+
+ For area definitions this is a simple comparison using the ``==``.
+ When swaths are involved care is taken to not check coordinate equality
+ to avoid the expensive computation. A swath and an area are never
+ considered equal in this case even if they describe the same geographic
+ region.
+
+ Two swaths are only considered equal if the underlying arrays are the
+ exact same objects. Otherwise, they are considered not equal and
+ coordinate values are never checked. This has
+ the downside that if two SwathDefinitions have equal coordinates but
+ are loaded or created separately they will be considered not equal.
+
+ """
+ if self.source_geo_def is self.target_geo_def:
+ return True
+ if type(self.source_geo_def) is not type(self.target_geo_def): # noqa
+ # these aren't the exact same class
+ return False
+ if isinstance(self.source_geo_def, AreaDefinition):
+ return self.source_geo_def == self.target_geo_def
+ # swath or coordinate definitions
+ src_lons, src_lats = self.source_geo_def.get_lonlats()
+ dst_lons, dst_lats = self.target_geo_def.get_lonlats()
+ if (src_lons is dst_lons) and (src_lats is dst_lats):
+ return True
+
+ if not all(isinstance(arr, da.Array) for arr in (src_lons, src_lats, dst_lons, dst_lats)):
+ # they aren't the same object and they aren't dask arrays so not equal
+ return False
+ # if dask task names are the same then they are the same even if the
+ # dask Array instance itself is different
+ return src_lons.name == dst_lons.name and src_lats.name == dst_lats.name
+
def _create_cache_filename(self, cache_dir=None, prefix='',
fmt='.zarr', **kwargs):
"""Create filename for the cached resampling parameters."""
=====================================
pyresample/test/test_geometry/test_area.py
=====================================
@@ -1814,6 +1814,17 @@ class TestAreaDefGetAreaSlices:
assert slice_lines == expected_slice_lines
assert slice_cols == expected_slice_cols
+ def test_area_to_cover_all_nan_bounds(self, geos_src_area, create_test_area):
+ """Check area slicing when the target doesn't have a valid boundary."""
+ area_def = geos_src_area
+ # An area that is a subset of the original one
+ area_to_cover = create_test_area(
+ {"proj": "moll"},
+ 1000, 1000,
+ area_extent=(-18000000.0, -9000000.0, 18000000.0, 9000000.0))
+ with pytest.raises(NotImplementedError):
+ area_def.get_area_slices(area_to_cover)
+
class TestBoundary:
"""Test 'boundary' method for AreaDefinition classes."""
=====================================
pyresample/test/test_geometry_legacy.py
=====================================
@@ -291,29 +291,32 @@ class TestDynamicAreaDefinition:
resolution=3000,
proj_info={'lon_0': 16, 'lat_0': 58})
- np.testing.assert_allclose(result.area_extent, (-432079.38952,
- -872594.690447,
- 432079.38952,
- 904633.303964))
+ np.testing.assert_allclose(result.area_extent, (-435000.0,
+ -873000.0,
+ 435000.0,
+ 906000.0))
assert result.proj_dict['lon_0'] == 16
assert result.proj_dict['lat_0'] == 58
- assert result.width == 288
- assert result.height == 592
+ assert result.width == 290
+ assert result.height == 593
+ assert result.pixel_size_x == 3000
+ assert result.pixel_size_y == 3000
- # make sure that setting `proj_info` once doesn't
- # set it in the dynamic area
+ # make sure that setting `proj_info` once doesn't set it in the dynamic area
result = area.freeze((lons, lats),
resolution=3000,
proj_info={'lon_0': 0})
- np.testing.assert_allclose(result.area_extent, (538546.7274949469,
- 5380808.879250369,
- 1724415.6519203288,
- 6998895.701001488))
+ np.testing.assert_allclose(result.area_extent, (537000.0,
+ 5379000.0,
+ 1725000.0,
+ 6999000.0))
assert result.proj_dict['lon_0'] == 0
# lat_0 could be provided or not depending on version of pyproj
assert result.proj_dict.get('lat_0', 0) == 0
- assert result.width == 395
- assert result.height == 539
+ assert result.width == 396
+ assert result.height == 540
+ assert result.pixel_size_x == 3000
+ assert result.pixel_size_y == 3000
def test_freeze_when_area_is_optimized_and_has_a_resolution(self):
"""Test freezing an optimized area with a resolution."""
@@ -391,11 +394,11 @@ class TestDynamicAreaDefinition:
if is_pole:
assert extent[0] < -178
assert extent[2] > 178
- assert result.width == 64088
+ assert result.width == 64090
else:
assert extent[0] > 0
assert extent[2] > 0
- assert result.width == 1787
+ assert result.width == 1788
assert result.height == 2680
def test_freeze_with_bb(self):
@@ -454,10 +457,10 @@ class TestDynamicAreaDefinition:
"exclude_proj_components"
),
[
- (None, (21, 59), (164.75, 24.75, 194.25, 35.25), tuple(), ("+pm=180",)),
- ("modify_extents", (21, 59), (164.75, 24.75, 194.25, 35.25), tuple(), ("+pm=180",)),
- ("modify_crs", (21, 59), (164.75 - 180.0, 24.75, 194.25 - 180.0, 35.25), ("+pm=180",), tuple()),
- ("global_extents", (21, 720), (-180.0, 24.75, 180.0, 35.25), tuple(), ("+pm=180",)),
+ (None, (22, 60), (164.5, 24.5, 194.5, 35.5), tuple(), ("+pm=180",)),
+ ("modify_extents", (22, 60), (164.5, 24.5, 194.5, 35.5), tuple(), ("+pm=180",)),
+ ("modify_crs", (22, 60), (164.5 - 180.0, 24.5, 194.5 - 180.0, 35.5), ("+pm=180",), tuple()),
+ ("global_extents", (22, 720), (-180.0, 24.5, 180.0, 35.5), tuple(), ("+pm=180",)),
],
)
@pytest.mark.parametrize("use_dask", [False, True])
=====================================
pyresample/test/test_gradient.py
=====================================
@@ -485,7 +485,6 @@ class TestRBGradientSearchResamplerArea2Area:
res = self.resampler.compute(
data, method='nn',
fill_value=2.0).compute(scheduler='single-threaded').values
- print(res)
np.testing.assert_allclose(res, expected_resampled_data)
assert res.shape == dst_area.shape
=====================================
pyresample/test/test_resamplers/test_resampler.py
=====================================
@@ -20,12 +20,14 @@ from __future__ import annotations
from unittest import mock
+import dask.array as da
import numpy as np
import pytest
+import xarray as xr
from pytest_lazyfixture import lazy_fixture
from pyresample.future.resamplers.resampler import Resampler
-from pyresample.geometry import AreaDefinition
+from pyresample.geometry import AreaDefinition, SwathDefinition
from pyresample.resampler import BaseResampler
@@ -76,13 +78,68 @@ def test_resampler(src, dst):
assert resample_results.shape == dst.shape
-def test_base_resampler_does_nothing_when_src_and_dst_areas_are_equal():
+ at pytest.mark.parametrize(
+ ("use_swaths", "copy_dst_swath"),
+ [
+ (False, None),
+ (True, None), # same objects are equal
+ (True, "dask"), # same dask tasks are equal
+ (True, "swath_def"), # same underlying arrays are equal
+ ])
+def test_base_resampler_does_nothing_when_src_and_dst_areas_are_equal(_geos_area, use_swaths, copy_dst_swath):
"""Test that the BaseResampler does nothing when the source and target areas are the same."""
+ src_geom = _geos_area if not use_swaths else _xarray_swath_def_from_area(_geos_area)
+ dst_geom = src_geom
+ if copy_dst_swath == "dask":
+ dst_geom = _xarray_swath_def_from_area(_geos_area)
+ elif copy_dst_swath == "swath_def":
+ dst_geom = SwathDefinition(dst_geom.lons, dst_geom.lats)
+
+ resampler = BaseResampler(src_geom, dst_geom)
+ some_data = xr.DataArray(da.zeros(src_geom.shape, dtype=np.float64), dims=('y', 'x'))
+ assert resampler.resample(some_data) is some_data
+
+
+ at pytest.mark.parametrize(
+ ("src_area", "numpy_swath"),
+ [
+ (False, False),
+ (False, True),
+ (True, False),
+ ])
+ at pytest.mark.parametrize("dst_area", [False, True])
+def test_base_resampler_unequal_geometries(_geos_area, _geos_area2, src_area, numpy_swath, dst_area):
+ """Test cases where BaseResampler geometries are not considered equal."""
+ src_geom = _geos_area if src_area else _xarray_swath_def_from_area(_geos_area, numpy_swath)
+ dst_geom = _geos_area2 if dst_area else _xarray_swath_def_from_area(_geos_area2)
+ resampler = BaseResampler(src_geom, dst_geom)
+ some_data = xr.DataArray(da.zeros(src_geom.shape, dtype=np.float64), dims=('y', 'x'))
+ with pytest.raises(NotImplementedError):
+ resampler.resample(some_data)
+
+
+def _xarray_swath_def_from_area(area_def, use_numpy=False):
+ chunks = None if use_numpy else -1
+ lons_da, lats_da = area_def.get_lonlats(chunks=chunks)
+ lons = xr.DataArray(lons_da, dims=('y', 'x'))
+ lats = xr.DataArray(lats_da, dims=('y', 'x'))
+ swath_def = SwathDefinition(lons, lats)
+ return swath_def
+
+
+ at pytest.fixture
+def _geos_area():
src_area = AreaDefinition('src', 'src area', None,
{'ellps': 'WGS84', 'h': '35785831', 'proj': 'geos'},
100, 100,
(5550000.0, 5550000.0, -5550000.0, -5550000.0))
+ return src_area
- resampler = BaseResampler(src_area, src_area)
- some_data = np.zeros(src_area.shape, dtype=np.float64)
- assert resampler.resample(some_data) is some_data
+
+ at pytest.fixture
+def _geos_area2():
+ src_area = AreaDefinition('src', 'src area', None,
+ {'ellps': 'WGS84', 'h': '35785831', 'proj': 'geos'},
+ 200, 200,
+ (5550000.0, 5550000.0, -5550000.0, -5550000.0))
+ return src_area
=====================================
pyresample/version.py
=====================================
@@ -25,9 +25,9 @@ def get_keywords():
# setup.py/versioneer.py will grep for the variable names, so they must
# each be defined on a line of their own. _version.py will just call
# get_keywords().
- git_refnames = " (tag: v1.27.0)"
- git_full = "3fa952985b8b4a62f4407e1cdeef166719523e47"
- git_date = "2023-05-17 08:51:02 +0200"
+ git_refnames = " (HEAD -> main, tag: v1.27.1)"
+ git_full = "12892b875221846f0fbc7f68775dbdadd8b68584"
+ git_date = "2023-06-21 16:14:02 +0200"
keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
return keywords
View it on GitLab: https://salsa.debian.org/debian-gis-team/pyresample/-/compare/dabdfd077f9a4d31798aacfd8e89250e57ee43d2...f75559e324ed4d48e5a8b7dde330f919de16a92e
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/pyresample/-/compare/dabdfd077f9a4d31798aacfd8e89250e57ee43d2...f75559e324ed4d48e5a8b7dde330f919de16a92e
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/20230623/750f7d19/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list