Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/_static/style.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.sidebar-log {
max-width: 70%;
}

.xr-wrap {
font-size: 0.85em;
margin-left: 1.25em;
Expand Down
3 changes: 2 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
"font-size--small--2": "87.5%",
}
html_theme_options = dict(
sidebar_hide_name=True,
sidebar_hide_name=False,
light_css_variables=css_vars,
dark_css_variables=css_vars,
)
Expand All @@ -141,6 +141,7 @@
"pyproj": ("https://pyproj4.github.io/pyproj/stable/", None),
"exactextract": ("https://isciences.github.io/exactextract/", None),
"rasterio": ("https://rasterio.readthedocs.io/en/stable/", None),
"xvec": ("https://xvec.readthedocs.io/en/stable/", None),
}

autosummary_generate = True
Expand Down
3 changes: 3 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
caption: Raster Index
hidden:
---
raster_index/intro
raster_index/indexing
raster_index/crs
raster_index/aligning
raster_index/combining
raster_index/design_choices
Expand Down
2 changes: 1 addition & 1 deletion docs/raster_index/combining.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ xr.set_options(display_expand_indexes=True);

# Combining

{py:class}`RasterIndex` supports concatenation along a single axis through either {py:func}`xarray.concat` or {py:func}`xarray.combine_nested`.
{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`.
In all cases, a new {py:class}`RasterIndex` is created.

Cases (a) and (b) in the following image are supported, case (c) is not.
Expand Down
199 changes: 199 additions & 0 deletions docs/raster_index/crs.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "0",
"metadata": {},
"source": [
"# CRS handling\n",
"\n",
"RasterIndex composes with [xproj](https://xproj.readthedocs.io) to provide CRS handling."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": [
"hide-input",
"hide-output"
]
},
"outputs": [],
"source": [
"%xmode minimal\n",
"\n",
"import xarray as xr\n",
"\n",
"import rasterix\n",
"\n",
"xr.set_options(display_expand_indexes=True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [],
"source": [
"source = \"/vsicurl/https://noaadata.apps.nsidc.org/NOAA/G02135/south/daily/geotiff/2024/01_Jan/S_20240101_concentration_v3.0.tif\""
]
},
{
"cell_type": "markdown",
"id": "3",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"To enable CRS-handling, start by assigning a CRS using the `.proj.assign_crs` function from xproj."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [],
"source": [
"da = xr.open_dataarray(source, engine=\"rasterio\")\n",
"da = da.proj.assign_crs(spatial_ref=da.spatial_ref.attrs[\"crs_wkt\"])\n",
"da = da.pipe(rasterix.assign_index)\n",
"da"
]
},
{
"cell_type": "markdown",
"id": "5",
"metadata": {},
"source": [
"Note how the `x` variable has appropriate attributes!"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6",
"metadata": {},
"outputs": [],
"source": [
"da.x.attrs"
]
},
{
"cell_type": "markdown",
"id": "7",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"## Alignment\n",
"\n",
"For demo purposes we'll create a second copy with a different CRS."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [],
"source": [
"diffcrs = da.copy(deep=True).drop_vars(\"spatial_ref\").drop_indexes([\"x\", \"y\"])\n",
"diffcrs = diffcrs.proj.assign_crs(spatial_ref=\"epsg:4326\").pipe(rasterix.assign_index)\n",
"diffcrs"
]
},
{
"cell_type": "markdown",
"id": "9",
"metadata": {},
"source": [
"Again note that the attributes on `x` have changed appropriately. This is enabled by RasterIndex's integration with `xproj`"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10",
"metadata": {},
"outputs": [],
"source": [
"diffcrs.x.attrs"
]
},
{
"cell_type": "markdown",
"id": "11",
"metadata": {},
"source": [
"Attempting to add the two raises an error (as it should)!"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "12",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": [
"raises-exception"
]
},
"outputs": [],
"source": [
"diffcrs + da"
]
}
],
"metadata": {
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
40 changes: 40 additions & 0 deletions docs/raster_index/design_choices.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,43 @@

In designing {py:class}`RasterIndex`, we faced a few thorny questions. Below we discuss these considerations, and the approach we've taken.
Ultimately, there are no easy answers and tradeoffs to be made.

## CRS handling

Why do we want CRS handling?

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.
1. Support wraparound indexing along the `longitude` dimension ({issue}`26`)
1. Assign appropriate attributes to the created coordinate variables ({issue}`22`). (e.g. choose between `standard_name: latitude` and `standard_name: projection_y_coordinate`)
1. more?

{py:class}`xproj.CRSIndex` is an attempt at providing a building block for CRS handling in the Xarray ecosystem that solved problem (1).
Thus (1) can be handled by assigning {py:class}`xproj.CRSIndex` to the `spatial_ref` variable.
How might `RasterIndex` integrate with `xproj.CRSIndex`? Our options are:

1. fully encapsulate {py:class}`xproj.CRSIndex`, or
1. satisfy the ["CRS-aware" protocol](https://xproj.readthedocs.io/en/latest/integration.html) provided by `xproj`, or
1. simply handle the affine transform and ignore the CRS altogether.

### Why should `RasterIndex` be aware of the CRS?

RasterIndex handles indexing and the creation of coordinate variables. Thus it is the natural place to support (2) and (3) in the list above.

### Why not encapsulate CRSIndex?

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.
Thus, `RasterIndex` would be associated with 3 variables instead of 2: `x`, `y`, and `spatial_ref`, for example.

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`).
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.
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.

### CRS-aware Index

Therefore, we have chosen to experiment with the "CRS-aware" approach described in the [xproj docs](https://xproj.readthedocs.io/en/latest/integration.html).
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`.
The downside here is that CRS information is duplicated in two places explicitly, and requires explicit handling to ensure consistency

### Don't like it?

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.
Loading
Loading