Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Patching — Field backends (RasterField, XarrayField, RioXarrayField, …)

Backends — one Patcher, five Field adapters on real heterogeneous data

geopatcher keeps the locality algebra (Geometry, Sampler, Window, Aggregation) decoupled from the substrate via the Field and Domain Protocols. The same SpatialPatcher therefore drives:

FieldDomainBackend
RasterFieldRasterDomain (GeoDataBase)RasterioReader, GeoTensor, AsyncGeoTIFFReader
RioXarrayFieldRasterDomainrioxarray DataArray
XarrayFieldGridDomainxarray.DataArray (non-raster)
GeoPandasFieldVectorDomain or PointDomaingeopandas.GeoDataFrame
XvecFieldPointDomainxvec.Dataset

Every section below uses a real, heterogeneous data source so the substrate diversity that motivates this module shows up in the substrates themselves:

<xarray.core.options.set_options at 0x11cd3e3c0>

1. RasterField — GeoTensor / RasterioReader

The canonical raster backend. Wraps anything satisfying georeader’s GeoData Protocol — GeoTensor (in-memory), RasterioReader (lazy file-backed), AsyncGeoTIFFReader (lazy async). The Patcher consumes them identically.

raster GeoTensor.shape: (512, 512)  crs: EPSG:32610
Loading...
raster_field.domain.shape: (512, 512)
raster_field.domain.crs:   EPSG:32610
raster: 16 patches
  first.indices: Window(col_off=0, row_off=0, width=128, height=128)
  first.data.shape: (128, 128)

2. RioXarrayField — rioxarray-flavoured DataArray

Same raster domain, accessed through the xarray surface (chunked Dask reads, unified xarray pipelines). The Patcher sees the affine + shape via the rioxarray accessor and treats it identically to a RasterioReader. We open the same Sentinel-2 B04 asset directly via rioxarray so the source is identical.

xr_raster shape: (1, 512, 512)  crs: EPSG:32610
Loading...
rio_xr_field.domain.shape: (1, 512, 512)
rio_xr_field.domain.transform: | 10.00, 0.00, 752180.00|
| 0.00,-10.00, 4345240.00|
| 0.00, 0.00, 1.00|

3. XarrayField — non-raster N-D grid

For dense, labeled cubes that aren’t necessarily raster — climate reanalyses, model output, DEM stacks. We open the Copernicus GLO-30 DEM tile over the same AOI as a (y, x) xarray.DataArray with native lat/lon coordinates.

DEM shape: (972, 360)  crs: EPSG:4326
Loading...
grid_field.domain.shape: (972, 360)
grid_field.domain.coords keys: ['lat', 'lon']
grid: 27 patches
  first.anchor: {'lat': 0, 'lon': 0}
  first.data.da.shape: (100, 100)

4. GeoPandasField — vector polygons

Real vector geometries: Natural Earth admin-1 polygons for the Pacific states. The SpatialRadiusGraph geometry dispatches on the polygon centroid to find all admin units within R degrees of an anchor point.

pacific.shape: (7, 2)
Loading...
vector_field.domain.crs: EPSG:32610
vector_field.domain.bounds: (374586.4578130039, 3529388.0180232613, 1833615.155247882, 5450822.428169321)
vector: 1 patches
  first.indices length: 6
  matching names: ['Arizona', 'California', 'Nevada', 'Utah', 'Oregon', 'Idaho']

5. GeoPandasField(as_points=True) — point cloud via geopandas

Real GBIF occurrence points (California live oak) lifted into a PointDomain with a cKDTree so KNNGraph / RadiusGraph queries are cheap.

oak shape: (300, 4)
Loading...
point_field.domain.coords.shape: (300, 2)
point_field.domain has kdtree: True
point: 1 patches
  k-NN neighbour indices: [127, 27, 281, 244, 257, 51, 222, 278]

6. XvecField — xvec data cubes for in-situ multivariate data

xvec puts a shapely.Point geometry coordinate on an xarray.Dataset, which is the modern pattern for stations / floats / swath samples with multiple variables and times. We lift the same GBIF points into a synthetic temperature + elevation cube — picture 24 hourly readings per occurrence record.

Loading...
xvec_field.domain.coords.shape: (300, 2)
xvec: 1 patches
  k-NN neighbour indices: [127, 27, 281, 244]
  data.ds.dims: {'geometry': 4, 'time': 24}

7. A common operator across substrates

Same SpatialPatcher shape, different backends. The composition algebra (GridSampler → ApplyToChips → Stitch) doesn’t care which substrate it is running on — the operator just sees patch.data in whatever shape the backend produces.

            RasterField (S2 NIR): {'anchor': '(0, 0)', 'data_type': 'GeoTensor', 'data_shape': '(128, 128)'}
               XarrayField (DEM): {'anchor': "{'lat': 0, 'lon': 0}", 'data_type': 'XarrayField', 'data_shape': '<da> (100, 100)'}
       GeoPandasField (polygons): {'anchor': '<POINT (744000 4325000)>', 'data_type': 'GeoPandasField', 'data_shape': '<gdf> (6, 2)'}
    GeoPandasField (GBIF points): {'anchor': 'array([ 744000., 4325000.])', 'data_type': 'GeoPandasField', 'data_shape': '<gdf> (8, 4)'}
     XvecField (oak + temp/elev): {'anchor': 'array([ 744000., 4325000.])', 'data_type': 'XvecField', 'data_shape': "<ds> dims={'geometry': 4, 'time': 24}"}

What this proves

  • The four-axis Patcher composition is substrate-agnostic. Same Sampler / Window / Aggregation triples work across every Field; only the Geometry dispatches on the Domain type.
  • Adding a new substrate is a single ~30-line Field adapter — implement domain, select(indexer), with_data(array), gate the optional dependency with a friendly import error.
  • Choice of backend is a deployment concern, not a modelling one. The same pipeline that runs against an in-memory GeoTensor in a notebook runs against a lazy RioXarrayField over a remote zarr archive in production with no code change beyond the field wrapper.