Comparison with other GIS packages#
How pyramids relates to the established Python geospatial / scientific-array stack. This page compares pyramids against rasterio, xarray, and rioxarray, and is the place to add further comparisons over time.
How to read this — a fairness note#
A raw feature checklist is unfair to rasterio, xarray, and rioxarray, for different reasons:
- rasterio is deliberately a focused raster-core library (Unix-philosophy). Most things it does not
do itself are provided, often more maturely, by sibling packages —
fiona/geopandas(vector),rasterstats(zonal),rioxarray/stackstac(datacube),rio-cogeo(COG),shapely(geometry). - xarray is not a GIS library at all. It is the standard for N-dimensional labelled arrays and
datacubes (NetCDF / CF / Zarr / Dask). It has no native CRS, geotransform, or raster concept; its
geospatial powers come from extensions:
rioxarray,xrspatial,stackstac/odc-stac,uxarray,cfgrib. Comparing it on a GIS checklist understates it — it is the substrate datacube tools build on. - rioxarray is the rasterio-on-xarray bridge: it adds CRS, geotransform, reproject / clip / merge,
and GeoTIFF/COG read-write to xarray via the
.rioaccessor. It is xarray's "missing geospatial column" made concrete — the closest single-package peer to pyramids' raster + datacube combo — but it inherits xarray's vector / zonal / terrain / STAC blanks (filled by the ecosystem).
pyramids overlaps this stack on the datacube / NetCDF axis, but it is a different kind of tool: pyramids
is GDAL/GIS-first (a CRS-aware raster + vector + datacube library), whereas xarray/rioxarray are
array-first. So the tables compare what each single library ships, not what each ecosystem can
do; →pkg marks a capability supplied by an ecosystem package.
Legend: ✓ built-in · ✓✓ a strength · ✓✓✓ the de-facto standard · ◐ partial / needs wiring · ✗ not
provided · →pkg via an ecosystem package.
A ✓ means the capability is reachable through that library's own public API — a method, function, or
CLI command it ships — not merely that the underlying engine (GDAL/GEOS/PROJ) could do it if you dropped
down to it. pyramids is a GDAL/OGR wrapper, so nearly all of its raster ✓s are "a GDAL call wrapped in a
pyramids method" — which counts, exactly as rasterio's and rioxarray's GDAL wrappers count, because there
is a Pythonic entry point. Where pyramids has no wrapper and you would have to call osgeo.gdal
yourself, the cell is not a ✓ (see GRIB, GCP/RPC below). Every pyramids ✓ in these tables was checked
against a concrete public symbol in src/pyramids.
A ✓ is still not parity in maturity, performance, or edge-case robustness. rasterio, xarray, and rioxarray are years more battle-tested and far more widely deployed; several of pyramids' ✓s are comparatively new. Read the tables as surface coverage, with the maturity row as the counterweight.
pyramids vs rasterio vs xarray vs rioxarray#
Core raster I/O#
| Capability | pyramids | rasterio | xarray | rioxarray |
|---|---|---|---|---|
| GDAL raster formats (GeoTIFF, …) | ✓ Dataset |
✓✓ standard | ◐ →rioxarray |
✓ open_rasterio |
| Windowed read & write | ✓ read/write_array(window=) + Window math |
✓✓ Window |
◐ Dask chunks | ◐ Dask chunks |
| In-memory raster (bytes) | ✓ from_bytes/to_bytes |
✓ MemoryFile |
◐ →rioxarray |
◐ via MemoryFile |
| Overviews / pyramids | ✓ create_overviews, read_overview_array |
✓ build_overviews |
✗ →rio |
◐ →rasterio |
| COG write / validate / inspect | ✓✓ to_cog |
◐ →rio-cogeo |
✗ →rioxarray |
◐ to_raster(COG) |
| Decimated reads (preview / tile) | ✓✓ out_shape + preview |
◐ out_shape |
◐ →rioxarray |
◐ overview_level |
| No-data / masks / colour interp | ✓ mask_flags/read_masks/band_color |
✓ | ◐ .where |
✓ .rio.nodata |
CRS, warping & alignment#
| Capability | pyramids | rasterio | xarray | rioxarray |
|---|---|---|---|---|
| Reproject / warp | ✓ to_crs |
✓ warp.reproject |
✗ →rioxarray |
✓✓ .rio.reproject |
| Lazy / on-the-fly warp (VRT) | ✓ warped_view |
✓✓ WarpedVRT |
✗ | ◐ →rasterio |
| Resample (spatial) | ✓ resample |
✓ resampling enums | ✗ →rioxarray |
✓ reproject(resampling=) |
| Align / snap to target grid | ✓ align |
◐ manual | ◐ align (labels) |
✓✓ reproject_match |
| CRS / affine transforms | ✓ | ✓✓ Affine |
✗ →rioxarray |
✓✓ .rio.crs |
| GCP / RPC georeferencing | ✓ set_gcps/georeference/orthorectify |
✓✓ gcps/rpcs |
✗ | ◐ →rasterio |
Note: xarray's own .resample operates on a labelled dimension (e.g. time), not spatial reprojection —
spatial resample/warp is the rioxarray accessor's job.
Raster analysis#
| Capability | pyramids | rasterio | xarray | rioxarray |
|---|---|---|---|---|
| Crop by bbox / geometry | ✓ crop |
✓ mask.mask |
◐ .sel |
✓ .rio.clip |
| Mosaic / merge | ✓ merge |
✓ merge.merge |
✗ →stackstac |
✓ merge_arrays |
| Zonal statistics | ✓ zonal_stats |
✗ →rasterstats |
✗ →xrspatial |
✗ →xrspatial |
| Terrain (slope / aspect / hillshade) | ✓ slope/aspect/hillshade |
✗ →gdaldem |
✗ →xrspatial |
✗ →xrsp. |
| Proximity / sieve / contour | ✓ proximity/sieve/contour |
◐ features.sieve |
✗ →xrspatial |
✗ →scipy |
| Interpolation / gridding | ✓ from_points |
✗ →scipy |
◐ .interp |
◐ interpolate_na |
| Connected-component clustering | ✓ cluster |
✗ →scipy |
✗ →scipy |
✗ →scipy |
| Point sampling | ✓ sample / point |
✓ sample |
✓✓ .sel(method=) |
✓✓ .sel(method=) |
| Band statistics (min/max/mean/std) | ✓ stats |
◐ numpy | ✓✓ .mean |
✓✓ .mean |
| Histogram | ✓ get_histogram |
◐ numpy | ✓ .plot.hist |
✓ .plot.hist |
Raster ↔ vector#
| Capability | pyramids | rasterio | xarray | rioxarray |
|---|---|---|---|---|
| Rasterize vectors | ✓ from_features |
✓ features.rasterize |
✗ →geocube |
✗ →geocube |
| Vectorize / polygonize | ✓ to_feature_collection |
✓ features.shapes |
✗ →rasterio |
✗ →rasterio |
| Dataset footprint polygon | ✓ footprint |
◐ mask + shapes |
✗ →rioxarray |
◐ .rio.bounds |
Vector data (standalone)#
| Capability | pyramids | rasterio | xarray | rioxarray |
|---|---|---|---|---|
| Vector I/O (read/write) | ✓ FeatureCollection |
✗ →fiona |
✗ →geopandas |
✗ →geopandas |
| Geometry operations | ✓ | ✗ →shapely |
✗ →shapely |
✗ →shapely |
| GeoParquet | ✓ | ✗ →geopandas |
✗ →geopandas |
✗ →geopandas |
Multi-dimensional / datacube / formats#
| Capability | pyramids | rasterio | xarray | rioxarray |
|---|---|---|---|---|
| Time-series datacube | ✓ DatasetCollection |
✗ →rioxarray |
✓✓✓ the standard | ✓✓ xarray + CRS |
| NetCDF + CF conventions | ✓✓ first-class | ◐ subdatasets only | ✓✓✓ first-class | ✓✓ + CRS |
| UGRID unstructured grids | ✓ | ✗ | ◐ →uxarray |
✗ →uxarray |
| Zarr | ✓ to_zarr/from_zarr |
◐ GDAL driver | ✓✓ native | ✓ + CRS |
| GRIB (read) | ◐ via read_file (no GRIB-specific API) |
◐ via GDAL | ◐ →cfgrib |
◐ →cfgrib |
Cloud, STAC & lazy compute#
| Capability | pyramids | rasterio | xarray | rioxarray |
|---|---|---|---|---|
| Cloud VSI (s3 / gs / az) | ✓ remote |
✓ /vsi*/ |
◐ →fsspec |
◐ →fsspec |
| STAC search / load / mosaic | ✓✓ stac/ |
✗ →pystac-client |
✗ →stackstac |
✗ →stackstac |
| Requester-pays / signing | ✓ Signer |
◐ AWSSession |
✗ →fsspec |
◐ rasterio session |
| Lazy / Dask-backed arrays | ✓ Dask chunks= |
✗ →rioxarray |
✓✓✓ standard | ✓✓✓ standard |
| Concurrent windowed reads | ✓ read_array(threadsafe=), read_windows |
✓✓ | ✓ via Dask | ✓ via Dask |
Tooling & maturity#
| Capability | pyramids | rasterio | xarray | rioxarray |
|---|---|---|---|---|
| CLI | ✓ pyramids (incl. edit-info, calc, georeference, shapes, rasterize) |
✓ rio |
✗ | ✗ |
| Plotting | ◐ →cleopatra |
✓ rasterio.plot |
✓✓ xarray.plot |
✓✓ xarray.plot |
| Maturity / adoption / community | younger | ✓✓✓ standard | ✓✓✓ huge (Pangeo) | ✓✓ widely used |
| Stability / docs depth | growing | ✓✓✓ | ✓✓✓ | ✓✓ |
Honest summary#
- rasterio's real strengths: maturity, stability, enormous adoption, the most granular windowed-I/O
API, and a deep, composable raster ecosystem (
rio-cogeo,rio-tiler,rasterstats). Its narrow core is a feature. pyramids now matches it on boundless windowed reads, decimatedout_shapereads, in-memory byte round-trips, per-thread concurrent reads (read_windows), lazy on-the-fly warping (warped_view— itsWarpedVRTequivalent), per-band mask-flag introspection (mask_flags/read_masks), pixel↔map accessors (xy/rowcol), and — the last remaining capability gap, now closed — GCP / RPC georeferencing (set_gcps/georeference/set_rpcs/orthorectify). With that, there is no longer a raster capability rasterio has that pyramids lacks; rasterio's edge is now maturity, ecosystem depth, and ergonomic polish (the granularwindowsalgebra, therioplugin family), not missing features. Treat the surface ✓s as real, with the maturity rows as the honest counterweight. - xarray's real strengths: the de-facto standard for N-dimensional labelled arrays and datacubes —
NetCDF/CF, Zarr, Dask-backed lazy compute,
groupby/reductions, label-based selection, and faceted plotting. Outside its remit (CRS, warping, GIS raster ops) it leans onrioxarray/xrspatial. - rioxarray's real strengths: turns xarray into a CRS-aware raster engine —
reproject/reproject_match/clip/mergeand GeoTIFF/COG I/O on lazy Dask datacubes. It is the closest single-package peer to pyramids' raster + datacube combo, but inherits xarray's vector / zonal / terrain / STAC blanks (→ ecosystem). - pyramids' real strengths: breadth in one GDAL/GIS-first package, each capability behind its own Pythonic API — raster and vector and datacube; NetCDF/CF/UGRID, STAC, terrain / zonal / interpolation / clustering, COG tooling, and lazy/Dask, without stitching together several libraries.
- When to pick which:
- pyramids — an integrated, batteries-included, CRS-aware GDAL toolkit covering raster, vector, and datacubes together.
- rasterio (+ ecosystem) — a mature, minimal, highly-composable raster core; add the pieces you need.
- xarray + rioxarray — your problem is genuinely N-dimensional scientific arrays / large lazy datacubes, and you want a CRS-aware raster layer on top.
Scope reminder: pyramids stays a generic GDAL/OGR toolkit — the breadth above is generic primitives and format support, not domain logic. See Scope for the boundary.