Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add grid_bounds_to_polygons #478

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
3 changes: 3 additions & 0 deletions cf_xarray/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@
from .options import set_options # noqa
from .utils import _get_version

from . import geometry # noqa

#
__version__ = _get_version()
63 changes: 63 additions & 0 deletions cf_xarray/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,3 +438,66 @@
geoms = np.where(np.diff(offset2) == 1, lines[offset2[:-1]], multilines)

return xr.DataArray(geoms, dims=part_node_count.dims, coords=part_node_count.coords)


def grid_to_polygons(ds: xr.Dataset) -> xr.DataArray:
dcherian marked this conversation as resolved.
Show resolved Hide resolved
dcherian marked this conversation as resolved.
Show resolved Hide resolved
"""
Converts a regular 2D lat/lon grid to a 2D array of shapely polygons.
dcherian marked this conversation as resolved.
Show resolved Hide resolved

Modified from https://notebooksharing.space/view/c6c1f3a7d0c260724115eaa2bf78f3738b275f7f633c1558639e7bbd75b31456.

Parameters
----------
ds : xr.Dataset
Dataset with "latitude" and "longitude" variables as well as their bounds variables.
1D "latitude" and "longitude" variables are supported. This function will automatically
broadcast them against each other.
dcherian marked this conversation as resolved.
Show resolved Hide resolved
dcherian marked this conversation as resolved.
Show resolved Hide resolved

Returns
-------
DataArray
DataArray with shapely polygon per grid cell.
"""
import shapely

Check warning on line 461 in cf_xarray/geometry.py

View check run for this annotation

Codecov / codecov/patch

cf_xarray/geometry.py#L461

Added line #L461 was not covered by tests

grid = ds.cf[["latitude", "longitude"]].load()
bounds = grid.cf.bounds
dims = grid.cf.dims

Check warning on line 465 in cf_xarray/geometry.py

View check run for this annotation

Codecov / codecov/patch

cf_xarray/geometry.py#L463-L465

Added lines #L463 - L465 were not covered by tests

if "latitude" in dims or "longitude" in dims:

Check warning on line 467 in cf_xarray/geometry.py

View check run for this annotation

Codecov / codecov/patch

cf_xarray/geometry.py#L467

Added line #L467 was not covered by tests
# for 1D lat, lon, this allows them to be
# broadcast against each other
grid = grid.reset_coords()

Check warning on line 470 in cf_xarray/geometry.py

View check run for this annotation

Codecov / codecov/patch

cf_xarray/geometry.py#L470

Added line #L470 was not covered by tests

assert "latitude" in bounds
assert "longitude" in bounds
(lon_bounds,) = bounds["longitude"]
(lat_bounds,) = bounds["latitude"]

Check warning on line 475 in cf_xarray/geometry.py

View check run for this annotation

Codecov / codecov/patch

cf_xarray/geometry.py#L472-L475

Added lines #L472 - L475 were not covered by tests

with xr.set_options(keep_attrs=True):
(points,) = xr.broadcast(grid)

Check warning on line 478 in cf_xarray/geometry.py

View check run for this annotation

Codecov / codecov/patch

cf_xarray/geometry.py#L477-L478

Added lines #L477 - L478 were not covered by tests

bounds_dim = grid.cf.get_bounds_dim_name("latitude")
points = points.transpose(..., bounds_dim)
lonbnd = points[lon_bounds].data
latbnd = points[lat_bounds].data

Check warning on line 483 in cf_xarray/geometry.py

View check run for this annotation

Codecov / codecov/patch

cf_xarray/geometry.py#L480-L483

Added lines #L480 - L483 were not covered by tests

if points.sizes[bounds_dim] == 2:
lonbnd = lonbnd[..., [0, 0, 1, 1]]
latbnd = latbnd[..., [0, 1, 1, 0]]

Check warning on line 487 in cf_xarray/geometry.py

View check run for this annotation

Codecov / codecov/patch

cf_xarray/geometry.py#L485-L487

Added lines #L485 - L487 were not covered by tests

elif points.sizes[bounds_dim] != 4:

Check warning on line 489 in cf_xarray/geometry.py

View check run for this annotation

Codecov / codecov/patch

cf_xarray/geometry.py#L489

Added line #L489 was not covered by tests
raise ValueError(
f"The size of the detected bounds or vertex dimension {bounds_dim} is not 2 or 4."

Check warning on line 491 in cf_xarray/geometry.py

View check run for this annotation

Codecov / codecov/patch

cf_xarray/geometry.py#L491

Added line #L491 was not covered by tests
)

# geopandas needs this
mask = lonbnd[..., 0] >= 180
lonbnd[mask, :] = lonbnd[mask, :] - 360

Check warning on line 496 in cf_xarray/geometry.py

View check run for this annotation

Codecov / codecov/patch

cf_xarray/geometry.py#L495-L496

Added lines #L495 - L496 were not covered by tests
Comment on lines +1023 to +1025
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the reason for this is that most geographic data is given in WGS84 which is -180...180 (I may be wrong here). So if you want to compare it to regional polygons it is probably a good idea to wrap the data. Still, it may be confusing to users.

Maybe this could be optional. However, I am not sure what a good name is (In regionmask I call it wrap_lon with the options 180 (= what you do here), 360 and False. That works but there may be better names).


polyarray = shapely.polygons(shapely.linearrings(lonbnd, latbnd))

Check warning on line 498 in cf_xarray/geometry.py

View check run for this annotation

Codecov / codecov/patch

cf_xarray/geometry.py#L498

Added line #L498 was not covered by tests

# 'geometry' is a blessed name in geopandas.
boxes = points[lon_bounds][..., 0].copy(data=polyarray).rename("geometry")

Check warning on line 501 in cf_xarray/geometry.py

View check run for this annotation

Codecov / codecov/patch

cf_xarray/geometry.py#L501

Added line #L501 was not covered by tests

return boxes

Check warning on line 503 in cf_xarray/geometry.py

View check run for this annotation

Codecov / codecov/patch

cf_xarray/geometry.py#L503

Added line #L503 was not covered by tests
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,7 @@ module=[
"pint",
"matplotlib",
"pytest",
"shapely",
"shapely.geometry",
"shapely.*",
"xarray.core.pycompat",
]
ignore_missing_imports = true
Expand Down