Skip to content

Commit 74789e3

Browse files
authored
Merge branch 'main' into pre-commit-ci-update-config
2 parents 41d850a + 08fb286 commit 74789e3

File tree

4 files changed

+99
-5
lines changed

4 files changed

+99
-5
lines changed

.github/workflows/ci.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050
run: |
5151
pytest -n auto --cov=./ --cov-report=xml
5252
- name: Upload code coverage to Codecov
53-
uses: codecov/codecov-action@v4.1.0
53+
uses: codecov/codecov-action@v4.3.0
5454
with:
5555
file: ./coverage.xml
5656
flags: unittests
@@ -114,7 +114,7 @@ jobs:
114114
run: |
115115
python -m mypy --install-types --non-interactive --cobertura-xml-report mypy_report cf_xarray/
116116
- name: Upload mypy coverage to Codecov
117-
uses: codecov/codecov-action@v4.1.0
117+
uses: codecov/codecov-action@v4.3.0
118118
with:
119119
file: mypy_report/cobertura.xml
120120
flags: mypy

cf_xarray/geometry.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,10 @@ def shapely_to_cf(geometries: xr.DataArray | Sequence, grid_mapping: str | None
9494
A dataset with shapely geometry objects translated into CF-compliant variables :
9595
- 'x', 'y' : the node coordinates
9696
- 'crd_x', 'crd_y' : the feature coordinates (might have different names if `grid_mapping` is available).
97-
- 'node_count' : The number of nodes per feature. Absent if all instances are Points.
97+
- 'node_count' : The number of nodes per feature. Always present for Lines and Polygons. For Points: only present if there are multipart geometries.
98+
- 'part_node_count' : The number of nodes per individual geometry. Only for Lines with multipart geometries and for Polygons with multipart geometries or holes.
99+
- 'interior_ring' : Integer boolean indicating whether rings are interior or exterior. Only for Polygons with holes.
98100
- 'geometry_container' : Empty variable with attributes describing the geometry type.
99-
- Other variables are not implemented as only Points are currently understood.
100101
101102
References
102103
----------

ci/doc.yml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ dependencies:
1919
- pooch
2020
- pint
2121
- regex
22+
- shapely
2223
- furo
2324
- myst-nb
2425
- pip:

doc/geometry.md

+93-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,99 @@
1+
---
2+
jupytext:
3+
text_representation:
4+
format_name: myst
5+
kernelspec:
6+
display_name: Python 3
7+
name: python3
8+
---
9+
110
```{eval-rst}
211
.. currentmodule:: xarray
312
```
413

514
# Geometries
615

7-
See {py:func}`cf_xarray.shapely_to_cf`, {py:func}`cf_xarray.cf_to_shapely`
16+
```{seealso}
17+
1. [The CF conventions on Geometries](http://cfconventions.org/Data/cf-conventions/cf-conventions-1.11/cf-conventions.html#geometries)
18+
1. {py:func}`cf_xarray.shapely_to_cf`
19+
1. {py:func}`cf_xarray.cf_to_shapely`
20+
```
21+
22+
`cf_xarray` can convert between vector geometries represented as shapely objects
23+
and CF-compliant array representations of those geometries.
24+
25+
Let's start by creating an xarray object containing some shapely geometries. This example uses
26+
a `xr.DataArray` but these functions also work with a `xr.Dataset` where one of the data variables
27+
contains an array of shapes.
28+
29+
```{code-cell}
30+
import cf_xarray as cfxr
31+
import xarray as xr
32+
33+
from shapely.geometry import MultiPoint, Point
34+
35+
da = xr.DataArray(
36+
[
37+
MultiPoint([(1.0, 2.0), (2.0, 3.0)]),
38+
Point(3.0, 4.0),
39+
Point(4.0, 5.0),
40+
Point(3.0, 4.0),
41+
],
42+
dims=("index",),
43+
name="geometry"
44+
)
45+
```
46+
47+
```{warning}
48+
`cf_xarray` does not support handle multiple types of shapes (Point, Line, Polygon) in one
49+
`xr.DataArray`, but multipart geometries are supported and can be mixed with single-part
50+
geometries of the same type.
51+
```
52+
53+
Now we can take that `xr.DataArray` containing shapely geometries and convert it to cf:
54+
55+
```{code-cell}
56+
ds_cf = cfxr.shapely_to_cf(da)
57+
ds_cf
58+
```
59+
60+
This function returns a `xr.Dataset` containing the CF fields needed to reconstruct the
61+
geometries. In particular there are:
62+
63+
- `'x'`, `'y'` : the node coordinates
64+
- `'crd_x'`, `'crd_y'` : the feature coordinates (might have different names if `grid_mapping` is available).
65+
- `'node_count'` : The number of nodes per feature. Always present for Lines and Polygons. For
66+
Points: only present if there are multipart geometries.
67+
- `'part_node_count'` : The number of nodes per individual geometry. Only for Lines with multipart
68+
geometries and for Polygons with multipart geometries or holes.
69+
- `'interior_ring'` : Integer boolean indicating whether ring is interior or exterior. Only for
70+
Polygons with holes.
71+
- `'geometry_container`' : Empty variable with attributes describing the geometry type.
72+
73+
Here are the attributes on `geometry_container`. This pattern mimics the convention of
74+
specifying spatial reference information in the attrs of the empty array `spatial_ref`.
75+
76+
```{code-cell}
77+
ds_cf.geometry_container.attrs
78+
```
79+
80+
```{note}
81+
Z axis is not yet supported for any shapes.
82+
```
83+
84+
This `xr.Dataset` can be converted back into a `xr.DataArray` of shapely geometries:
85+
86+
```{code-cell}
87+
cfxr.cf_to_shapely(ds_cf)
88+
```
89+
90+
This conversion adds coordinates that aren't in the `xr.DataArray` that we started with.
91+
By default these are called `crd_x` and `crd_y` unless `grid_mapping` is specified.
92+
93+
## Gotchas
94+
95+
For MultiPolygons with holes the CF notation is slightly ambiguous on which hole is associated
96+
with which polygon. This is problematic because shapely stores holes within the polygon
97+
object that they are associated with. `cf_xarray` assumes that the the shapes are interleaved
98+
such that the holes (interior rings) are associated with the exteriors (exterior rings) that
99+
immediately precede them.

0 commit comments

Comments
 (0)