Skip to content

Commit 6550084

Browse files
charlesgauthier-udmcharlesgauthier-udmpre-commit-ci[bot]huardaulemahal
authored
Warn of SpatialAverager error over large region and densify polygons (#293)
* Added warning for error over large regions and a function to densify polygons to prevent errors * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove unnecessary import * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove unnecessary import again, made a mistake last time * Moved densify_poly func to util.py and simplified it using shapely.segmentize. Simplified the length check for the warning in the SpatialAverager * Updated requirements to shapely>=2 so that shapely.segmentize can be used * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove unnecessary imports since change to shapely.segmentize * add note about segmentize in the nb * Added details in CHANGES.rst * Moved the poly length check to its own function and modified the warning * Added test to verify that a warning is raised for large polys * Removed densify_polys function * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Removed unnecessary test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Suggested changes to xesmf/frontend.py Co-authored-by: Pascal Bourgault <[email protected]> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update CHANGES.rst * Removed shapely>=2 dependencies --------- Co-authored-by: charlesgauthier-udm <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: David Huard <[email protected]> Co-authored-by: Pascal Bourgault <[email protected]>
1 parent 52cb33c commit 6550084

File tree

6 files changed

+37
-0
lines changed

6 files changed

+37
-0
lines changed

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ What's new
66

77
New features
88
~~~~~~~~~~~~
9+
* Added a check in SpatialAverager that warns user if they are using polygons with long segments that could cause errors (:pull:`293`). By `Charles Gauthier <https://github.com/charlesgauthier-udm>`_
910
* Added the ability to generate regridding weights in parallel. By `Charles Gauthier <https://github.com/charlesgauthier-udm>`_
1011
* Added the ability to chunk over horizontal/core dimensions. Added a `output_chunks` argument to the `Regridder`
1112
that allows the user to specify the chunking of the output data. By `Charles Gauthier <https://github.com/charlesgauthier-udm>`_

binder/environment.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ dependencies:
77
- dask
88
- numpy
99
- scipy
10+
- shapely
1011
- matplotlib
1112
- cartopy
1213
- cf_xarray>=0.3.1

ci/environment-upstream-dev.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dependencies:
1313
- pydap
1414
- pytest
1515
- pytest-cov
16+
- shapely
1617
- sparse>=0.8.0
1718
- pip:
1819
- git+https://github.com/pydata/xarray.git

doc/notebooks/Spatial_Averaging.ipynb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
"[regionmask](https://regionmask.readthedocs.io/) or\n",
2929
"[clisops](https://clisops.readthedocs.io).\n",
3030
"\n",
31+
"Also, note that low resolution polygons such as large boxes might get distorted\n",
32+
"when mapped on a sphere. Make sure polygon segments are at sufficiently high\n",
33+
"resolution, on the order of 1°. The `shapely` package (v2) has a\n",
34+
"[`segmentize` function](https://shapely.readthedocs.io/en/latest/reference/shapely.segmentize.html)\n",
35+
"to add vertices to polygon segments.\n",
36+
"\n",
3137
"The following example shows just how simple it is to compute the average over\n",
3238
"different countries. The notebook used `geopandas`, a simple and efficient\n",
3339
"container for geometries, and `descartes` for plotting maps. Make sure both\n",

xesmf/frontend.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import numpy as np
99
import sparse as sps
1010
import xarray as xr
11+
from shapely import LineString
1112
from xarray import DataArray, Dataset
1213

1314
from .backend import Grid, LocStream, Mesh, add_corner, esmf_regrid_build, esmf_regrid_finalize
@@ -1180,6 +1181,9 @@ def __init__(
11801181
self._lon_out_name = 'lon'
11811182
self._lat_out_name = 'lat'
11821183

1184+
# Check length of polys segments
1185+
self._check_polys_length(polys)
1186+
11831187
poly_centers = [poly.centroid.xy for poly in polys]
11841188
self._lon_out = np.asarray([c[0][0] for c in poly_centers])
11851189
self._lat_out = np.asarray([c[1][0] for c in poly_centers])
@@ -1202,6 +1206,23 @@ def __init__(
12021206
unmapped_to_nan=False,
12031207
)
12041208

1209+
@staticmethod
1210+
def _check_polys_length(polys, threshold=1):
1211+
# Check length of polys segments, issue warning if too long
1212+
check_polys, check_holes, _, _ = split_polygons_and_holes(polys)
1213+
check_polys.extend(check_holes)
1214+
poly_segments = []
1215+
for check_poly in check_polys:
1216+
b = check_poly.boundary.coords
1217+
# Length of each segment
1218+
poly_segments.extend([LineString(b[k : k + 2]).length for k in range(len(b) - 1)])
1219+
if np.any(np.array(poly_segments) > threshold):
1220+
warnings.warn(
1221+
f'`polys` contains large (> {threshold}°) segments. This could lead to errors over large regions. For a more accurate average, segmentize (densify) your shapes with `shapely.segmentize(polys, {threshold})`',
1222+
UserWarning,
1223+
stacklevel=2,
1224+
)
1225+
12051226
def _compute_weights_and_area(self, mesh_out):
12061227
"""Return the weights and the area of the destination mesh cells."""
12071228

xesmf/tests/test_frontend.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,3 +952,10 @@ def test_spatial_averager_mask():
952952
savg = xe.SpatialAverager(dsm, [poly], geom_dim_name='my_geom')
953953
out = savg(dsm.abc)
954954
assert_allclose(out, 2, rtol=1e-3)
955+
956+
957+
def test_densify_polys():
958+
# Check that using a large poly raises a warning
959+
poly = Polygon([(-80, -40), (80, -40), (80, 40), (-80, 40)]) # Large poly
960+
with pytest.warns(UserWarning):
961+
xe.SpatialAverager(ds_in, [poly])

0 commit comments

Comments
 (0)