_pam._parse_rat sorts thematic RAT rows by their Value column and returns the names densely packed. The attrs contract in _attrs.py says category_names is an "ordered list of class label strings (index == pixel value)". Any sidecar whose Value column isn't exactly 0..N-1 comes back misaligned.
Repro:
from xrspatial.geotiff._pam import _parse_rat
from xrspatial.geotiff._safe_xml import safe_fromstring
xml = """<PAMDataset><PAMRasterBand band="1">
<GDALRasterAttributeTable tableType="thematic">
<FieldDefn index="0"><Name>Value</Name><Type>0</Type><Usage>5</Usage></FieldDefn>
<FieldDefn index="1"><Name>Class</Name><Type>2</Type><Usage>2</Usage></FieldDefn>
<Row index="0"><F>1</F><F>water</F></Row>
<Row index="1"><F>2</F><F>forest</F></Row>
<Row index="2"><F>5</F><F>urban</F></Row>
</GDALRasterAttributeTable>
</PAMRasterBand></PAMDataset>"""
names, colors = _parse_rat(safe_fromstring(xml).find('.//GDALRasterAttributeTable'))
print(names) # ['water', 'forest', 'urban']
Under index==value, pixel 1 now reads as "forest" (should be "water") and pixel 5 has no entry. category_colors shifts the same way, so QGIS colors land on the wrong classes.
Our own writer (build_pam_xml) always emits a dense 0-based Value column, so xrspatial-to-xrspatial round trips are fine. This only bites when the .aux.xml was authored by GDAL or QGIS, and those commonly write 1-based or sparse Value columns (classified rasters where 0 is nodata, NLCD's 11/21/22/...).
Possible fix: pad gaps with empty strings (matching GDAL's CategoryNames semantics) and (0,0,0,0) colors. Should also fail closed on negative values or a huge max value so a weird RAT can't allocate an enormous list.
Found during the 2026-07-01 accuracy sweep. Filing rather than fixing since this is label metadata, not pixel values.
_pam._parse_ratsorts thematic RAT rows by their Value column and returns the names densely packed. The attrs contract in_attrs.pysayscategory_namesis an "ordered list of class label strings (index == pixel value)". Any sidecar whose Value column isn't exactly 0..N-1 comes back misaligned.Repro:
Under index==value, pixel 1 now reads as "forest" (should be "water") and pixel 5 has no entry.
category_colorsshifts the same way, so QGIS colors land on the wrong classes.Our own writer (
build_pam_xml) always emits a dense 0-based Value column, so xrspatial-to-xrspatial round trips are fine. This only bites when the.aux.xmlwas authored by GDAL or QGIS, and those commonly write 1-based or sparse Value columns (classified rasters where 0 is nodata, NLCD's 11/21/22/...).Possible fix: pad gaps with empty strings (matching GDAL's CategoryNames semantics) and (0,0,0,0) colors. Should also fail closed on negative values or a huge max value so a weird RAT can't allocate an enormous list.
Found during the 2026-07-01 accuracy sweep. Filing rather than fixing since this is label metadata, not pixel values.