Skip to content

to_geotiff: write best-practice symbology sidecars for continuous rasters (QGIS color ramp) #3537

Description

@brendancol

Reason or Problem

When you write a categorical raster with arr.xrs.to_geotiff(), xarray-spatial drops a PAM .aux.xml sidecar holding the class names and colors, so QGIS opens the file already showing labelled, coloured classes. Continuous rasters (DEMs, slope, NDVI, and so on) get no such treatment. They open in QGIS as a flat grayscale stretch with whatever default min/max GDAL guesses, and the user has to set up a colour ramp by hand every time.

Proposal

Add an opt-in color_ramp parameter to to_geotiff that, for continuous single-band rasters, writes best-practice symbology sidecars so QGIS applies a sensible colour ramp on open. This is the continuous-data counterpart to the categorical RAT sidecar we already write.

Design:
Two sidecars are written when color_ramp is set and the raster is continuous (no category_names attr) and single-band:

  1. A QGIS .qml style file (dem.qml, replacing the extension, which is the name QGIS auto-loads) with a singlebandpseudocolor renderer and an interpolated colour ramp. This is what gives QGIS real colours.
  2. STATISTICS_MINIMUM/MAXIMUM/MEAN/STDDEV in the existing PAM .aux.xml (dem.tif.aux.xml), which GDAL tools and QGIS use for a min/max stretch even if the .qml is ignored.

Colour ramps ship as hardcoded control points (viridis default, plus a small curated set) so there is no matplotlib runtime dependency. Statistics are computed with a single backend-aware reduction pass covering numpy, cupy, dask+numpy and dask+cupy. A color_ramp_range=(min, max) escape hatch skips the reduction for large dask graphs.

Usage:

dem.xrs.to_geotiff('dem.tif', color_ramp='viridis')
# writes dem.tif, dem.qml, dem.tif.aux.xml

Open dem.tif in QGIS and it renders with the viridis ramp stretched across the data range.

Value: Continuous rasters written by xarray-spatial open in QGIS looking right without manual styling, matching the experience categorical rasters already get.

Stakeholders and Impacts

Anyone writing continuous rasters for use in QGIS. Touches the geotiff writer (to_geotiff wrapper) and a new _symbology module plus the existing _pam sidecar module. Default behaviour is unchanged because the feature is opt-in.

Drawbacks

Writes extra sidecar files. The .qml format is QGIS-specific (other GIS tools ignore it, though they still read the GDAL statistics). For the dask streaming write path, computing statistics adds a separate reduction pass unless color_ramp_range is supplied.

Alternatives

  • Statistics only, no .qml: works in all GDAL tools but only gives a grayscale stretch, no colour.
  • Automatic for every continuous write: rejected to avoid surprise side-files and a forced statistics pass on writes that do not want symbology.
  • A GDAL colour table embedded in the TIFF: discrete/paletted only, not suited to a continuous ramp.

Unresolved Questions

On a constant raster (min == max) the statistics sidecar is still written but the .qml is skipped, since a single-stop ramp is degenerate.

Additional Notes or Context

Follows the categorical sidecar work (#3482 / #3483).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions