Skip to content

Commit c6bb13a

Browse files
dcherianscottyhq
andauthored
Add docs for raster-index (#41)
Co-authored-by: Scott Henderson <3924836+scottyhq@users.noreply.github.com>
1 parent aa59ec0 commit c6bb13a

File tree

9 files changed

+958
-4
lines changed

9 files changed

+958
-4
lines changed

docs/_static/style.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
.sidebar-log {
2+
max-width: 70%;
3+
}
4+
15
.xr-wrap {
26
font-size: 0.85em;
37
margin-left: 1.25em;

docs/conf.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@
116116
"font-size--small--2": "87.5%",
117117
}
118118
html_theme_options = dict(
119-
sidebar_hide_name=True,
119+
sidebar_hide_name=False,
120120
light_css_variables=css_vars,
121121
dark_css_variables=css_vars,
122122
)
@@ -141,6 +141,7 @@
141141
"pyproj": ("https://pyproj4.github.io/pyproj/stable/", None),
142142
"exactextract": ("https://isciences.github.io/exactextract/", None),
143143
"rasterio": ("https://rasterio.readthedocs.io/en/stable/", None),
144+
"xvec": ("https://xvec.readthedocs.io/en/stable/", None),
144145
}
145146

146147
autosummary_generate = True

docs/index.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
caption: Raster Index
88
hidden:
99
---
10+
raster_index/intro
11+
raster_index/indexing
12+
raster_index/crs
1013
raster_index/aligning
1114
raster_index/combining
1215
raster_index/design_choices

docs/raster_index/combining.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ xr.set_options(display_expand_indexes=True);
2121

2222
# Combining
2323

24-
{py:class}`RasterIndex` supports concatenation along a single axis through either {py:func}`xarray.concat` or {py:func}`xarray.combine_nested`.
24+
{py:class}`RasterIndex` supports concatenation along a single axis through either {py:func}`xarray.concat` or across multiple axes using {py:func}`xarray.combine_nested`.
2525
In all cases, a new {py:class}`RasterIndex` is created.
2626

2727
Cases (a) and (b) in the following image are supported, case (c) is not.

docs/raster_index/crs.ipynb

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "0",
6+
"metadata": {},
7+
"source": [
8+
"# CRS handling\n",
9+
"\n",
10+
"RasterIndex composes with [xproj](https://xproj.readthedocs.io) to provide CRS handling."
11+
]
12+
},
13+
{
14+
"cell_type": "code",
15+
"execution_count": null,
16+
"id": "1",
17+
"metadata": {
18+
"editable": true,
19+
"slideshow": {
20+
"slide_type": ""
21+
},
22+
"tags": [
23+
"hide-input",
24+
"hide-output"
25+
]
26+
},
27+
"outputs": [],
28+
"source": [
29+
"%xmode minimal\n",
30+
"\n",
31+
"import xarray as xr\n",
32+
"\n",
33+
"import rasterix\n",
34+
"\n",
35+
"xr.set_options(display_expand_indexes=True)"
36+
]
37+
},
38+
{
39+
"cell_type": "code",
40+
"execution_count": null,
41+
"id": "2",
42+
"metadata": {
43+
"editable": true,
44+
"slideshow": {
45+
"slide_type": ""
46+
},
47+
"tags": []
48+
},
49+
"outputs": [],
50+
"source": [
51+
"source = \"/vsicurl/https://noaadata.apps.nsidc.org/NOAA/G02135/south/daily/geotiff/2024/01_Jan/S_20240101_concentration_v3.0.tif\""
52+
]
53+
},
54+
{
55+
"cell_type": "markdown",
56+
"id": "3",
57+
"metadata": {
58+
"editable": true,
59+
"slideshow": {
60+
"slide_type": ""
61+
},
62+
"tags": []
63+
},
64+
"source": [
65+
"To enable CRS-handling, start by assigning a CRS using the `.proj.assign_crs` function from xproj."
66+
]
67+
},
68+
{
69+
"cell_type": "code",
70+
"execution_count": null,
71+
"id": "4",
72+
"metadata": {
73+
"editable": true,
74+
"slideshow": {
75+
"slide_type": ""
76+
},
77+
"tags": []
78+
},
79+
"outputs": [],
80+
"source": [
81+
"da = xr.open_dataarray(source, engine=\"rasterio\")\n",
82+
"da = da.proj.assign_crs(spatial_ref=da.spatial_ref.attrs[\"crs_wkt\"])\n",
83+
"da = da.pipe(rasterix.assign_index)\n",
84+
"da"
85+
]
86+
},
87+
{
88+
"cell_type": "markdown",
89+
"id": "5",
90+
"metadata": {},
91+
"source": [
92+
"Note how the `x` variable has appropriate attributes!"
93+
]
94+
},
95+
{
96+
"cell_type": "code",
97+
"execution_count": null,
98+
"id": "6",
99+
"metadata": {},
100+
"outputs": [],
101+
"source": [
102+
"da.x.attrs"
103+
]
104+
},
105+
{
106+
"cell_type": "markdown",
107+
"id": "7",
108+
"metadata": {
109+
"editable": true,
110+
"slideshow": {
111+
"slide_type": ""
112+
},
113+
"tags": []
114+
},
115+
"source": [
116+
"## Alignment\n",
117+
"\n",
118+
"For demo purposes we'll create a second copy with a different CRS."
119+
]
120+
},
121+
{
122+
"cell_type": "code",
123+
"execution_count": null,
124+
"id": "8",
125+
"metadata": {
126+
"editable": true,
127+
"slideshow": {
128+
"slide_type": ""
129+
},
130+
"tags": []
131+
},
132+
"outputs": [],
133+
"source": [
134+
"diffcrs = da.copy(deep=True).drop_vars(\"spatial_ref\").drop_indexes([\"x\", \"y\"])\n",
135+
"diffcrs = diffcrs.proj.assign_crs(spatial_ref=\"epsg:4326\").pipe(rasterix.assign_index)\n",
136+
"diffcrs"
137+
]
138+
},
139+
{
140+
"cell_type": "markdown",
141+
"id": "9",
142+
"metadata": {},
143+
"source": [
144+
"Again note that the attributes on `x` have changed appropriately. This is enabled by RasterIndex's integration with `xproj`"
145+
]
146+
},
147+
{
148+
"cell_type": "code",
149+
"execution_count": null,
150+
"id": "10",
151+
"metadata": {},
152+
"outputs": [],
153+
"source": [
154+
"diffcrs.x.attrs"
155+
]
156+
},
157+
{
158+
"cell_type": "markdown",
159+
"id": "11",
160+
"metadata": {},
161+
"source": [
162+
"Attempting to add the two raises an error (as it should)!"
163+
]
164+
},
165+
{
166+
"cell_type": "code",
167+
"execution_count": null,
168+
"id": "12",
169+
"metadata": {
170+
"editable": true,
171+
"slideshow": {
172+
"slide_type": ""
173+
},
174+
"tags": [
175+
"raises-exception"
176+
]
177+
},
178+
"outputs": [],
179+
"source": [
180+
"diffcrs + da"
181+
]
182+
}
183+
],
184+
"metadata": {
185+
"language_info": {
186+
"codemirror_mode": {
187+
"name": "ipython",
188+
"version": 3
189+
},
190+
"file_extension": ".py",
191+
"mimetype": "text/x-python",
192+
"name": "python",
193+
"nbconvert_exporter": "python",
194+
"pygments_lexer": "ipython3"
195+
}
196+
},
197+
"nbformat": 4,
198+
"nbformat_minor": 5
199+
}

docs/raster_index/design_choices.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,43 @@
66

77
In designing {py:class}`RasterIndex`, we faced a few thorny questions. Below we discuss these considerations, and the approach we've taken.
88
Ultimately, there are no easy answers and tradeoffs to be made.
9+
10+
## CRS handling
11+
12+
Why do we want CRS handling?
13+
14+
1. We want Xarray to disallow alignment of two Xarray objects with different CRS e.g. `da1 + da2` should fail if `da1`, and `da2` have different CRS.
15+
1. Support wraparound indexing along the `longitude` dimension ({issue}`26`)
16+
1. Assign appropriate attributes to the created coordinate variables ({issue}`22`). (e.g. choose between `standard_name: latitude` and `standard_name: projection_y_coordinate`)
17+
1. more?
18+
19+
{py:class}`xproj.CRSIndex` is an attempt at providing a building block for CRS handling in the Xarray ecosystem that solved problem (1).
20+
Thus (1) can be handled by assigning {py:class}`xproj.CRSIndex` to the `spatial_ref` variable.
21+
How might `RasterIndex` integrate with `xproj.CRSIndex`? Our options are:
22+
23+
1. fully encapsulate {py:class}`xproj.CRSIndex`, or
24+
1. satisfy the ["CRS-aware" protocol](https://xproj.readthedocs.io/en/latest/integration.html) provided by `xproj`, or
25+
1. simply handle the affine transform and ignore the CRS altogether.
26+
27+
### Why should `RasterIndex` be aware of the CRS?
28+
29+
RasterIndex handles indexing and the creation of coordinate variables. Thus it is the natural place to support (2) and (3) in the list above.
30+
31+
### Why not encapsulate CRSIndex?
32+
33+
If RasterIndex must track CRS in some form, one way to do that would be to have RasterIndex internally build a `CRSIndex` for the `spatial_ref` variable.
34+
Thus, `RasterIndex` would be associated with 3 variables instead of 2: `x`, `y`, and `spatial_ref`, for example.
35+
36+
The downside of this approach is that it doesn't compose well with any other Index that would also like to handle the CRS (e.g. {py:class}`xvec.GeometryIndex`).
37+
For example, `xr.merge([geometries, raster])` where `geometries` has `xvec.GeometryIndex[geometry, spatial_ref]` (square brackets list associated coordinate variable names) and `raster` has `RasterIndex[x, y, spatial_ref]`, would fail because the variable `spatial_ref` is associated with two Indexes of different types.
38+
This fails because the Xarray model enforces that _one Variable is only associated with only one Index_, in order to prevent different Indexes modifying the same Variable.
39+
40+
### CRS-aware Index
41+
42+
Therefore, we have chosen to experiment with the "CRS-aware" approach described in the [xproj docs](https://xproj.readthedocs.io/en/latest/integration.html).
43+
Here `RasterIndex` tracks it's own _optional_ copy of a CRS object (not an Index) and defines the hooks needed for `CRSIndex` to communicate with `RasterIndex`.
44+
The downside here is that CRS information is duplicated in two places explicitly, and requires explicit handling to ensure consistency
45+
46+
### Don't like it?
47+
48+
We chose this approach to enable experimentation. It is entirely possible to experiment with other approaches. Please reach out if you have opinions on this topic.

0 commit comments

Comments
 (0)