Skip to content

Commit 1caf519

Browse files
committed
Replace rioxarray with xrspatial.geotiff in examples
Removes the rioxarray dependency from all example notebooks: - multispectral.ipynb: rioxarray.open_rasterio -> read_geotiff - classification-methods.ipynb: same - viewshed_gpu.ipynb: same - 25_GLCM_Texture.ipynb: rioxarray.open_rasterio COG read -> read_geotiff with window= and band= parameters. Also removes GDAL-specific env vars (AWS_NO_SIGN_REQUEST, etc.) since our reader doesn't use GDAL. Also updates reproject/_crs_utils.py to check attrs['crs'] and attrs['crs_wkt'] (xrspatial.geotiff convention) before falling back to .rio.crs (rioxarray). This means DataArrays from read_geotiff work directly with xrspatial.reproject without needing rioxarray installed. The rioxarray fallback is kept in _crs_utils.py for backwards compatibility with users who pass rioxarray-decorated DataArrays.
1 parent 4a3791c commit 1caf519

File tree

5 files changed

+30
-88
lines changed

5 files changed

+30
-88
lines changed

docs/source/user_guide/multispectral.ipynb

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,7 @@
4141
},
4242
"outputs": [],
4343
"source": [
44-
"import datashader as ds\n",
45-
"from datashader.colors import Elevation\n",
46-
"import datashader.transfer_functions as tf\n",
47-
"from datashader.transfer_functions import shade\n",
48-
"from datashader.transfer_functions import stack\n",
49-
"from datashader.transfer_functions import dynspread\n",
50-
"from datashader.transfer_functions import set_background\n",
51-
"from datashader.transfer_functions import Images, Image\n",
52-
"from datashader.utils import orient_array\n",
53-
"import numpy as np\n",
54-
"import xarray as xr\n",
55-
"import rioxarray"
44+
"import datashader as ds\nfrom datashader.colors import Elevation\nimport datashader.transfer_functions as tf\nfrom datashader.transfer_functions import shade\nfrom datashader.transfer_functions import stack\nfrom datashader.transfer_functions import dynspread\nfrom datashader.transfer_functions import set_background\nfrom datashader.transfer_functions import Images, Image\nfrom datashader.utils import orient_array\nimport numpy as np\nimport xarray as xr\nfrom xrspatial.geotiff import read_geotiff"
5645
]
5746
},
5847
{
@@ -143,23 +132,7 @@
143132
}
144133
],
145134
"source": [
146-
"SCENE_ID = \"LC80030172015001LGN00\"\n",
147-
"EXTS = {\n",
148-
" \"blue\": \"B2\",\n",
149-
" \"green\": \"B3\",\n",
150-
" \"red\": \"B4\",\n",
151-
" \"nir\": \"B5\",\n",
152-
"}\n",
153-
"\n",
154-
"cvs = ds.Canvas(plot_width=1024, plot_height=1024)\n",
155-
"layers = {}\n",
156-
"for name, ext in EXTS.items():\n",
157-
" layer = rioxarray.open_rasterio(f\"../../../xrspatial-examples/data/{SCENE_ID}_{ext}.tiff\").load()[0]\n",
158-
" layer.name = name\n",
159-
" layer = cvs.raster(layer, agg=\"mean\")\n",
160-
" layer.data = orient_array(layer)\n",
161-
" layers[name] = layer\n",
162-
"layers"
135+
"SCENE_ID = \"LC80030172015001LGN00\"\nEXTS = {\n \"blue\": \"B2\",\n \"green\": \"B3\",\n \"red\": \"B4\",\n \"nir\": \"B5\",\n}\n\ncvs = ds.Canvas(plot_width=1024, plot_height=1024)\nlayers = {}\nfor name, ext in EXTS.items():\n layer = read_geotiff(f\"../../../xrspatial-examples/data/{SCENE_ID}_{ext}.tiff\", band=0)\n layer.name = name\n layer = cvs.raster(layer, agg=\"mean\")\n layer.data = orient_array(layer)\n layers[name] = layer\nlayers"
163136
]
164137
},
165138
{
@@ -362,7 +335,7 @@
362335
"}\n",
363336
"\n",
364337
".xr-group-name::before {\n",
365-
" content: \"📁\";\n",
338+
" content: \"\ud83d\udcc1\";\n",
366339
" padding-right: 0.3em;\n",
367340
"}\n",
368341
"\n",
@@ -425,7 +398,7 @@
425398
"\n",
426399
".xr-section-summary-in + label:before {\n",
427400
" display: inline-block;\n",
428-
" content: \"\";\n",
401+
" content: \"\u25ba\";\n",
429402
" font-size: 11px;\n",
430403
" width: 15px;\n",
431404
" text-align: center;\n",
@@ -436,7 +409,7 @@
436409
"}\n",
437410
"\n",
438411
".xr-section-summary-in:checked + label:before {\n",
439-
" content: \"\";\n",
412+
" content: \"\u25bc\";\n",
440413
"}\n",
441414
"\n",
442415
".xr-section-summary-in:checked + label > span {\n",

examples/user_guide/25_GLCM_Texture.ipynb

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@
264264
"id": "ec79xdunce9",
265265
"metadata": {},
266266
"source": [
267-
"### Step 1 Download a Sentinel-2 NIR band\n",
267+
"### Step 1 \u2014 Download a Sentinel-2 NIR band\n",
268268
"\n",
269269
"We read a 500 x 500 pixel window (5 km x 5 km at 10 m resolution) straight from a\n",
270270
"Cloud-Optimized GeoTIFF hosted on AWS. The scene is\n",
@@ -282,47 +282,15 @@
282282
"metadata": {},
283283
"outputs": [],
284284
"source": [
285-
"import os\n",
286-
"import rioxarray\n",
287-
"\n",
288-
"os.environ['AWS_NO_SIGN_REQUEST'] = 'YES'\n",
289-
"os.environ['GDAL_DISABLE_READDIR_ON_OPEN'] = 'EMPTY_DIR'\n",
290-
"\n",
291-
"COG_URL = (\n",
292-
" 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/'\n",
293-
" 'sentinel-s2-l2a-cogs/10/S/EG/2023/9/'\n",
294-
" 'S2B_10SEG_20230921_0_L2A/B08.tif'\n",
295-
")\n",
296-
"\n",
297-
"try:\n",
298-
" nir_da = rioxarray.open_rasterio(COG_URL).isel(band=0, y=slice(2100, 2600), x=slice(5300, 5800))\n",
299-
" nir = nir_da.load().values.astype(np.float64)\n",
300-
" print(f'Downloaded NIR band: {nir.shape}, range {nir.min():.0f} to {nir.max():.0f}')\n",
301-
"except Exception as exc:\n",
302-
" print(f'Remote read failed ({exc}), using synthetic fallback')\n",
303-
" rng_sat = np.random.default_rng(99)\n",
304-
" nir = np.zeros((500, 500), dtype=np.float64)\n",
305-
" nir[:, 250:] = rng_sat.normal(80, 10, (500, 250)).clip(20, 200)\n",
306-
" nir[:, :250] = rng_sat.normal(1800, 400, (500, 250)).clip(300, 4000)\n",
307-
"\n",
308-
"satellite = xr.DataArray(nir, dims=['y', 'x'],\n",
309-
" coords={'y': np.arange(nir.shape[0], dtype=float),\n",
310-
" 'x': np.arange(nir.shape[1], dtype=float)})\n",
311-
"\n",
312-
"fig, ax = plt.subplots(figsize=(7, 7))\n",
313-
"satellite.plot.imshow(ax=ax, cmap='gray', vmax=float(np.percentile(nir, 98)),\n",
314-
" add_colorbar=False)\n",
315-
"ax.set_title('Sentinel-2 NIR band')\n",
316-
"ax.set_axis_off()\n",
317-
"plt.tight_layout()"
285+
"import os\nfrom xrspatial.geotiff import read_geotiff\n\n\nCOG_URL = (\n 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/'\n 'sentinel-s2-l2a-cogs/10/S/EG/2023/9/'\n 'S2B_10SEG_20230921_0_L2A/B08.tif'\n)\n\ntry:\n nir_da = read_geotiff(COG_URL, band=0, window=(2100, 5300, 2600, 5800))\n nir = nir_da.values.astype(np.float64)\n print(f'Downloaded NIR band: {nir.shape}, range {nir.min():.0f} to {nir.max():.0f}')\nexcept Exception as exc:\n print(f'Remote read failed ({exc}), using synthetic fallback')\n rng_sat = np.random.default_rng(99)\n nir = np.zeros((500, 500), dtype=np.float64)\n nir[:, 250:] = rng_sat.normal(80, 10, (500, 250)).clip(20, 200)\n nir[:, :250] = rng_sat.normal(1800, 400, (500, 250)).clip(300, 4000)\n\nsatellite = xr.DataArray(nir, dims=['y', 'x'],\n coords={'y': np.arange(nir.shape[0], dtype=float),\n 'x': np.arange(nir.shape[1], dtype=float)})\n\nfig, ax = plt.subplots(figsize=(7, 7))\nsatellite.plot.imshow(ax=ax, cmap='gray', vmax=float(np.percentile(nir, 98)),\n add_colorbar=False)\nax.set_title('Sentinel-2 NIR band')\nax.set_axis_off()\nplt.tight_layout()"
318286
]
319287
},
320288
{
321289
"cell_type": "markdown",
322290
"id": "joxz7n8olpc",
323291
"metadata": {},
324292
"source": [
325-
"### Step 2 Compute GLCM texture features\n",
293+
"### Step 2 \u2014 Compute GLCM texture features\n",
326294
"\n",
327295
"We pick four metrics that tend to separate water (uniform, high energy, high homogeneity) from land (rough, high contrast):\n",
328296
"\n",
@@ -485,4 +453,4 @@
485453
},
486454
"nbformat": 4,
487455
"nbformat_minor": 5
488-
}
456+
}

examples/viewshed_gpu.ipynb

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
}
3535
},
3636
"outputs": [],
37-
"source": "import pandas\nimport matplotlib.pyplot as plt\nimport geopandas as gpd\n\nimport xarray as xr\nimport numpy as np\nimport cupy\nimport rioxarray\n\nimport xrspatial"
37+
"source": [
38+
"import pandas\nimport matplotlib.pyplot as plt\nimport geopandas as gpd\n\nimport xarray as xr\nimport numpy as np\nimport cupy\nfrom xrspatial.geotiff import read_geotiff\n\nimport xrspatial"
39+
]
3840
},
3941
{
4042
"cell_type": "markdown",
@@ -64,15 +66,7 @@
6466
},
6567
"outputs": [],
6668
"source": [
67-
"file_name = '../xrspatial-examples/data/colorado_merge_3arc_resamp.tif'\n",
68-
"\n",
69-
"raster = rioxarray.open_rasterio(file_name).sel(band=1).drop_vars('band')\n",
70-
"raster.name = 'Colorado Elevation Raster'\n",
71-
"\n",
72-
"xmin, xmax = raster.x.data.min(), raster.x.data.max()\n",
73-
"ymin, ymax = raster.y.data.min(), raster.y.data.max()\n",
74-
"\n",
75-
"xmin, xmax, ymin, ymax"
69+
"file_name = '../xrspatial-examples/data/colorado_merge_3arc_resamp.tif'\n\nraster = read_geotiff(file_name, band=0)\nraster.name = 'Colorado Elevation Raster'\n\nxmin, xmax = raster.x.data.min(), raster.x.data.max()\nymin, ymax = raster.y.data.min(), raster.y.data.max()\n\nxmin, xmax, ymin, ymax"
7670
]
7771
},
7872
{

examples/xarray-spatial_classification-methods.ipynb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@
4646
}
4747
},
4848
"outputs": [],
49-
"source": "import xarray as xr\nimport rioxarray\nimport xrspatial\n\nfile_name = '../xrspatial-examples/data/colorado_merge_3arc_resamp.tif'\nraster = rioxarray.open_rasterio(file_name).sel(band=1).drop_vars('band')\nraster.name = 'Colorado Elevation Raster'\n\nxmin, xmax = raster.x.data.min(), raster.x.data.max()\nymin, ymax = raster.y.data.min(), raster.y.data.max()\n\nxmin, xmax, ymin, ymax"
49+
"source": [
50+
"import xarray as xr\nfrom xrspatial.geotiff import read_geotiff\nimport xrspatial\n\nfile_name = '../xrspatial-examples/data/colorado_merge_3arc_resamp.tif'\nraster = read_geotiff(file_name, band=0)\nraster.name = 'Colorado Elevation Raster'\n\nxmin, xmax = raster.x.data.min(), raster.x.data.max()\nymin, ymax = raster.y.data.min(), raster.y.data.max()\n\nxmin, xmax, ymin, ymax"
51+
]
5052
},
5153
{
5254
"cell_type": "code",

xrspatial/reproject/_crs_utils.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,28 @@ def _detect_source_crs(raster):
3535
"""Auto-detect the CRS of a DataArray.
3636
3737
Fallback chain:
38-
1. ``raster.rio.crs`` (rioxarray)
39-
2. ``raster.attrs['crs']``
40-
3. None
38+
1. ``raster.attrs['crs']`` (EPSG int from xrspatial.geotiff)
39+
2. ``raster.attrs['crs_wkt']`` (WKT string from xrspatial.geotiff)
40+
3. ``raster.rio.crs`` (rioxarray, if installed)
41+
4. None
4142
"""
42-
# rioxarray
43+
# attrs (xrspatial.geotiff convention)
44+
crs_attr = raster.attrs.get('crs')
45+
if crs_attr is not None:
46+
return _resolve_crs(crs_attr)
47+
48+
crs_wkt = raster.attrs.get('crs_wkt')
49+
if crs_wkt is not None:
50+
return _resolve_crs(crs_wkt)
51+
52+
# rioxarray fallback
4353
try:
4454
rio_crs = raster.rio.crs
4555
if rio_crs is not None:
4656
return _resolve_crs(rio_crs)
4757
except Exception:
4858
pass
4959

50-
# attrs
51-
crs_attr = raster.attrs.get('crs')
52-
if crs_attr is not None:
53-
return _resolve_crs(crs_attr)
54-
5560
return None
5661

5762

0 commit comments

Comments
 (0)