Skip to content

add RAT support to rasters. #25#122

Open
davemfish wants to merge 12 commits intonatcap:mainfrom
davemfish:feature/25-raster-attribute-table
Open

add RAT support to rasters. #25#122
davemfish wants to merge 12 commits intonatcap:mainfrom
davemfish:feature/25-raster-attribute-table

Conversation

@davemfish
Copy link
Collaborator

@davemfish davemfish commented Mar 20, 2026

  • Added read support for a GDAL Raster Attribute Table. If it exists, the
    table will be included as band metadata under the 'raster_attribute_table'
    key. It can be retrieved by the get_rat method of a RasterResource
    instance.

Fixes #25

@davemfish davemfish marked this pull request as ready for review March 24, 2026 16:41
Copy link
Member

@emilyanndavis emilyanndavis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great addition, @davemfish! I have a handful of suggestions, mainly around documentation.

Comment on lines +201 to +204
type: str
"""Datatype of the content of the column, see ``gdal.GFT_*``."""
usage: str
"""The intended use of the column, see ``gdal.GFU_*``."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it might be more helpful to refer to the GDAL enum names here? If nothing else, they're probably more reliable search terms for finding the relevant GDAL documentation.

e.g.,

Suggested change
type: str
"""Datatype of the content of the column, see ``gdal.GFT_*``."""
usage: str
"""The intended use of the column, see ``gdal.GFU_*``."""
type: str
"""Datatype of the content of the column, see ``gdal.GDALRATFieldType``."""
usage: str
"""The intended use of the column, see ``gdal.GDALRATFieldUsage``."""

or even

Suggested change
type: str
"""Datatype of the content of the column, see ``gdal.GFT_*``."""
usage: str
"""The intended use of the column, see ``gdal.GFU_*``."""
type: str
"""Datatype of the content of the column. String representation of one of ``gdal.GDALRATFieldType``."""
usage: str
"""The intended use of the column. String representation of one of ``gdal.GDALRATFieldUsage``."""

(though I'm not 100% sure I'm using "one of" properly there—I always get confused about the singular vs. plural nature of enums. 🙃 )

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be thorough, we could also cover the case where we're reading the RAT directly from a .vat.dbf file:

Suggested change
type: str
"""Datatype of the content of the column, see ``gdal.GFT_*``."""
usage: str
"""The intended use of the column, see ``gdal.GFU_*``."""
type: str
"""Datatype of the content of the column. String representation of one of ``gdal.GDALRATFieldType``, or a string returned by ``osgeo.ogr.FieldDefn.GetTypeName``."""
usage: str
"""The intended use of the column. String representation of one of ``gdal.GDALRATFieldUsage``."""

model_config = ConfigDict(frozen=True)

table_type: str
"""Thematic or Athematic, see ``gdal.GRTT_*``."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""Thematic or Athematic, see ``gdal.GRTT_*``."""
"""Thematic or Athematic. String representation of one of ``gdal.GDALRATTableType``."""

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, to be thorough, it might be worth mentioning 'Unknown' is a possibility here as well.

Suggested change
"""Thematic or Athematic, see ``gdal.GRTT_*``."""
"""Thematic, Athematic, or Unknown. String representation of one of ``gdal.GDALRATTableType``, or 'Unknown' if this information is unavailable."""

Comment on lines +223 to +231
for i in range(rat.GetColumnCount()):
columns.append(RATColumnDefn(
name=rat.GetNameOfCol(i),
type=utils._GFT_INT_TO_STR[rat.GetTypeOfCol(i)],
usage=utils._GFU_INT_TO_STR[rat.GetUsageOfCol(i)]))
rows = []
for i in range(rat.GetRowCount()):
row = {}
for j in range(rat.GetColumnCount()):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's probably very little overhead involved in the call to GetColumnCount, but it still might be worth defining a variable (e.g., num_cols) once, outside these two loops, rather than invoking the method once for every row.

Suggested change
for i in range(rat.GetColumnCount()):
columns.append(RATColumnDefn(
name=rat.GetNameOfCol(i),
type=utils._GFT_INT_TO_STR[rat.GetTypeOfCol(i)],
usage=utils._GFU_INT_TO_STR[rat.GetUsageOfCol(i)]))
rows = []
for i in range(rat.GetRowCount()):
row = {}
for j in range(rat.GetColumnCount()):
num_cols = rat.GetColumnCount()
for i in range(num_cols):
columns.append(RATColumnDefn(
name=rat.GetNameOfCol(i),
type=utils._GFT_INT_TO_STR[rat.GetTypeOfCol(i)],
usage=utils._GFU_INT_TO_STR[rat.GetUsageOfCol(i)]))
rows = []
for i in range(rat.GetRowCount()):
row = {}
for j in range(num_cols):

Comment on lines +234 to +247
case 'Integer':
row[columns[j].name] = rat.GetValueAsInt(i, j)
case 'String':
row[columns[j].name] = rat.GetValueAsString(i, j)
case 'Real':
row[columns[j].name] = rat.GetValueAsDouble(i, j)
case 'Boolean':
row[columns[j].name] = rat.GetValueAsBoolean(i, j)
case 'DateTime':
row[columns[j].name] = rat.GetValueAsDateTime(i, j)
case 'WKBGeometry':
row[columns[j].name] = rat.GetValueAsWKBGeometry(i, j)
case _:
row[columns[j].name] = rat.GetValueAsString(i, j)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May as well use col in all these case statements, since col is already defined as a reference to columns[j].

Suggested change
case 'Integer':
row[columns[j].name] = rat.GetValueAsInt(i, j)
case 'String':
row[columns[j].name] = rat.GetValueAsString(i, j)
case 'Real':
row[columns[j].name] = rat.GetValueAsDouble(i, j)
case 'Boolean':
row[columns[j].name] = rat.GetValueAsBoolean(i, j)
case 'DateTime':
row[columns[j].name] = rat.GetValueAsDateTime(i, j)
case 'WKBGeometry':
row[columns[j].name] = rat.GetValueAsWKBGeometry(i, j)
case _:
row[columns[j].name] = rat.GetValueAsString(i, j)
case 'Integer':
row[col.name] = rat.GetValueAsInt(i, j)
case 'String':
row[col.name] = rat.GetValueAsString(i, j)
case 'Real':
row[col.name] = rat.GetValueAsDouble(i, j)
case 'Boolean':
row[col.name] = rat.GetValueAsBoolean(i, j)
case 'DateTime':
row[col.name] = rat.GetValueAsDateTime(i, j)
case 'WKBGeometry':
row[col.name] = rat.GetValueAsWKBGeometry(i, j)
case _:
row[col.name] = rat.GetValueAsString(i, j)

Comment on lines +259 to +266
if name == 'VALUE':
usage = 'MinMax'
elif name == 'COUNT':
usage = 'PixelCount'
# I'm not sure how standard any other fields are, so just calling
# them all 'Generic' may be good enough.
else:
usage = 'Generic'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth referencing the _GFU_INT_TO_STR dictionary here to define these usage strings, just to make sure they don't get out of sync with that source of truth.

"""Unit of measurement for the pixel values."""
gdal_metadata: dict = {}
"""Metadata key:value pairs stored in the GDAL band object."""
raster_attribute_table: RasterAttributeTable | None = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably worth adding a short docstring here, for consistency?

@davemfish
Copy link
Collaborator Author

Thanks, @emilyanndavis , these are all clear improvements. Back to you.

@davemfish davemfish requested a review from emilyanndavis March 25, 2026 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

include raster attribute tables as band metadata

2 participants