From c3798cb1690a960d5e13125567655a4e2975e4c9 Mon Sep 17 00:00:00 2001 From: Vincent Sarago Date: Thu, 23 Jan 2025 09:17:41 +0100 Subject: [PATCH] make sure transform is an Affine object (#781) --- CHANGES.md | 4 + rio_tiler/io/stac.py | 3 +- .../fixtures/stac-proj-titiler-issue1074.json | 177 ++++++++++++++++++ tests/test_io_stac.py | 9 + 4 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/stac-proj-titiler-issue1074.json diff --git a/CHANGES.md b/CHANGES.md index 54af61c0..4b4dcb4c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +# 7.3.1 (2025-01-23) + +* make sure `STACReader.transform` is an Affine object + # 7.3.0 (2025-01-07) * drop python 3.8 support diff --git a/rio_tiler/io/stac.py b/rio_tiler/io/stac.py index 00ef144c..5f7ad124 100644 --- a/rio_tiler/io/stac.py +++ b/rio_tiler/io/stac.py @@ -21,6 +21,7 @@ import httpx import pystac import rasterio +from affine import Affine from cachetools import LRUCache, cached from cachetools.keys import hashkey from morecantile import TileMatrixSet @@ -277,7 +278,7 @@ def __attrs_post_init__(self): ] ): self.height, self.width = self.item.ext.proj.shape - self.transform = self.item.ext.proj.transform + self.transform = Affine(*self.item.ext.proj.transform) self.bounds = array_bounds(self.height, self.width, self.transform) self.crs = rasterio.crs.CRS.from_string(self.item.ext.proj.crs_string) diff --git a/tests/fixtures/stac-proj-titiler-issue1074.json b/tests/fixtures/stac-proj-titiler-issue1074.json new file mode 100644 index 00000000..f605aa0a --- /dev/null +++ b/tests/fixtures/stac-proj-titiler-issue1074.json @@ -0,0 +1,177 @@ +{ + "type": "Feature", + "stac_version": "1.0.0", + "stac_extensions": [ + "https://stac-extensions.github.io/projection/v1.1.0/schema.json", + "https://stac-extensions.github.io/raster/v1.1.0/schema.json", + "https://stac-extensions.github.io/eo/v1.1.0/schema.json" + ], + "id": "2024-02-15-03-30-26_UMBRA-06_GEC.tif", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -79.61350819116612, + 8.941726366625119 + ], + [ + -79.54925740032105, + 8.941726366625119 + ], + [ + -79.54925740032105, + 9.005608046514665 + ], + [ + -79.61350819116612, + 9.005608046514665 + ], + [ + -79.61350819116612, + 8.941726366625119 + ] + ] + ] + }, + "bbox": [ + -79.61350819116612, + 8.941726366625119, + -79.54925740032105, + 9.005608046514665 + ], + "properties": { + "proj:epsg": 4326, + "proj:geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -79.61350819116612, + 8.941726366625119 + ], + [ + -79.54925740032105, + 8.941726366625119 + ], + [ + -79.54925740032105, + 9.005608046514665 + ], + [ + -79.61350819116612, + 9.005608046514665 + ], + [ + -79.61350819116612, + 8.941726366625119 + ] + ] + ] + }, + "proj:bbox": [ + -79.61350819116612, + 8.941726366625119, + -79.54925740032105, + 9.005608046514665 + ], + "proj:shape": [ + 16001, + 16001 + ], + "proj:transform": [ + 0.0000019242821881153906, + -0.0000020911412757346902, + -79.58004783961309, + -0.0000020791279755538317, + -0.0000019132274953258257, + 9.005608046514665, + 0, + 0, + 1 + ], + "proj:projjson": { + "$schema": "https://proj.org/schemas/v0.7/projjson.schema.json", + "type": "GeographicCRS", + "name": "WGS 84", + "datum": { + "type": "GeodeticReferenceFrame", + "name": "World Geodetic System 1984", + "ellipsoid": { + "name": "WGS 84", + "semi_major_axis": 6378137, + "inverse_flattening": 298.257223563 + } + }, + "coordinate_system": { + "subtype": "ellipsoidal", + "axis": [ + { + "name": "Geodetic latitude", + "abbreviation": "Lat", + "direction": "north", + "unit": "degree" + }, + { + "name": "Geodetic longitude", + "abbreviation": "Lon", + "direction": "east", + "unit": "degree" + } + ] + }, + "id": { + "authority": "EPSG", + "code": 4326 + } + }, + "datetime": "2025-01-22T14:44:29.445234Z" + }, + "links": [], + "assets": { + "data": { + "href": "https://umbra-open-data-catalog.s3.amazonaws.com/sar-data/tasks/Panama+Canal,+Panama/bb17c467-a6a8-41f0-80d8-2da88af9475b/2024-02-15-03-30-26_UMBRA-06/2024-02-15-03-30-26_UMBRA-06_GEC.tif", + "type": "image/tiff; application=geotiff", + "raster:bands": [ + { + "data_type": "uint8", + "scale": 1, + "offset": 0, + "sampling": "area", + "nodata": 0, + "statistics": { + "mean": 21.673484802246094, + "minimum": 1, + "maximum": 254, + "stddev": 21.97018807823054, + "valid_percent": 100 + }, + "histogram": { + "count": 11, + "min": 1, + "max": 254, + "buckets": [ + 701120, + 249619, + 69896, + 19634, + 5819, + 1773, + 536, + 125, + 45, + 9 + ] + } + } + ], + "eo:bands": [ + { + "name": "b1", + "description": "gray" + } + ], + "roles": [] + } + } +} diff --git a/tests/test_io_stac.py b/tests/test_io_stac.py index 7932dfcb..5dcc5313 100644 --- a/tests/test_io_stac.py +++ b/tests/test_io_stac.py @@ -40,6 +40,7 @@ STAC_ALTERNATE_PATH = os.path.join(PREFIX, "stac_alternate.json") STAC_GRIB_PATH = os.path.join(PREFIX, "stac_grib.json") STAC_NETCDF_PATH = os.path.join(PREFIX, "stac_netcdf.json") +STAC_ROTATED_AFFINE = os.path.join(PREFIX, "stac-proj-titiler-issue1074.json") with open(STAC_PATH) as f: item = json.loads(f.read()) @@ -1110,3 +1111,11 @@ def test_vrt_string_assets(): img = stac.preview(assets="vrt://asset?bands=1") assert img.count == 1 + + +def test_stac_with_rotate_affine(): + """Make sure we can handle rotated transform in proj""" + with STACReader(STAC_ROTATED_AFFINE) as stac: + assert stac.transform + assert stac.bounds + assert stac.get_geographic_bounds("epsg:4326")