From a476084d44eb20f5af7691cdbab04c5df9256830 Mon Sep 17 00:00:00 2001 From: Preston Hartzell Date: Sun, 19 Jun 2022 14:59:36 -0400 Subject: [PATCH] update raster extension to v1.1.0 (#809) * adds raster extension v1.1.0 - update schema uri - align nodata type with new schema: change nodata type from float to union of float and a StringEnum, where the StringEnum contains "nan", "inf", and "-inf" - update tests * update CHANGELOG Co-authored-by: Jon Duckworth --- CHANGELOG.md | 2 ++ pystac/extensions/raster.py | 16 +++++++++----- .../raster/raster-planet-example.json | 2 +- .../raster/raster-sentinel2-example.json | 9 +++++++- tests/extensions/test_raster.py | 21 ++++++++++++++++--- 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cb0f079c..c1168b10a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ ### Changed +- Updated Raster Extension from v1.0.0 to v1.1.0 ([#809](https://github.com/stac-utils/pystac/pull/809)) + ### Fixed - "How to create STAC catalogs" tutorial ([#775](https://github.com/stac-utils/pystac/pull/775)) diff --git a/pystac/extensions/raster.py b/pystac/extensions/raster.py index 6119b61e7..0c7623c24 100644 --- a/pystac/extensions/raster.py +++ b/pystac/extensions/raster.py @@ -10,7 +10,7 @@ ) from pystac.utils import StringEnum, get_opt, get_required, map_opt -SCHEMA_URI = "https://stac-extensions.github.io/raster/v1.0.0/schema.json" +SCHEMA_URI = "https://stac-extensions.github.io/raster/v1.1.0/schema.json" BANDS_PROP = "raster:bands" @@ -39,6 +39,12 @@ class DataType(StringEnum): OTHER = "other" +class NoDataStrings(StringEnum): + INF = "inf" + NINF = "-inf" + NAN = "nan" + + class Statistics: """Represents statistics information attached to a band in the raster extension. @@ -350,7 +356,7 @@ def __init__(self, properties: Dict[str, Any]) -> None: def apply( self, - nodata: Optional[float] = None, + nodata: Optional[Union[float, NoDataStrings]] = None, sampling: Optional[Sampling] = None, data_type: Optional[DataType] = None, bits_per_sample: Optional[float] = None, @@ -400,7 +406,7 @@ def apply( @classmethod def create( cls, - nodata: Optional[float] = None, + nodata: Optional[Union[float, NoDataStrings]] = None, sampling: Optional[Sampling] = None, data_type: Optional[DataType] = None, bits_per_sample: Optional[float] = None, @@ -452,7 +458,7 @@ def create( return b @property - def nodata(self) -> Optional[float]: + def nodata(self) -> Optional[Union[float, NoDataStrings]]: """Get or sets the nodata pixel value Returns: @@ -461,7 +467,7 @@ def nodata(self) -> Optional[float]: return self.properties.get("nodata") @nodata.setter - def nodata(self, v: Optional[float]) -> None: + def nodata(self, v: Optional[Union[float, NoDataStrings]]) -> None: if v is not None: self.properties["nodata"] = v else: diff --git a/tests/data-files/raster/raster-planet-example.json b/tests/data-files/raster/raster-planet-example.json index b85aea962..66fb39d3c 100644 --- a/tests/data-files/raster/raster-planet-example.json +++ b/tests/data-files/raster/raster-planet-example.json @@ -1245,6 +1245,6 @@ "https://stac-extensions.github.io/projection/v1.0.0/schema.json", "https://stac-extensions.github.io/eo/v1.0.0/schema.json", "https://stac-extensions.github.io/processing/v1.0.0/schema.json", - "https://stac-extensions.github.io/raster/v1.0.0/schema.json" + "https://stac-extensions.github.io/raster/v1.1.0/schema.json" ] } \ No newline at end of file diff --git a/tests/data-files/raster/raster-sentinel2-example.json b/tests/data-files/raster/raster-sentinel2-example.json index dbaaef625..807270404 100644 --- a/tests/data-files/raster/raster-sentinel2-example.json +++ b/tests/data-files/raster/raster-sentinel2-example.json @@ -548,6 +548,13 @@ "full_width_half_max": 0.026 } ], + "raster:bands": [ + { + "data_type": "uint16", + "spatial_resolution": 60, + "nodata": "nan" + } + ], "proj:shape": [ 1830, 1830 @@ -711,7 +718,7 @@ "https://stac-extensions.github.io/eo/v1.0.0/schema.json", "https://stac-extensions.github.io/view/v1.0.0/schema.json", "https://stac-extensions.github.io/projection/v1.0.0/schema.json", - "https://stac-extensions.github.io/raster/v1.0.0/schema.json" + "https://stac-extensions.github.io/raster/v1.1.0/schema.json" ], "virtual:assets": { "SIR": { diff --git a/tests/extensions/test_raster.py b/tests/extensions/test_raster.py index 677929c6b..423bba66e 100644 --- a/tests/extensions/test_raster.py +++ b/tests/extensions/test_raster.py @@ -6,6 +6,7 @@ from pystac.utils import get_opt from pystac.extensions.raster import ( Histogram, + NoDataStrings, RasterExtension, RasterBand, Sampling, @@ -41,6 +42,7 @@ def test_validate_raster(self) -> None: def test_asset_bands(self) -> None: item = pystac.Item.from_file(self.PLANET_EXAMPLE_URI) + item2 = pystac.Item.from_file(self.SENTINEL2_EXAMPLE_URI) # Get data_asset = item.assets["data"] @@ -74,8 +76,12 @@ def test_asset_bands(self) -> None: asset_bands = RasterExtension.ext(index_asset).bands self.assertIs(None, asset_bands) + b09_asset = item2.assets["B09"] + b09_bands = RasterExtension.ext(b09_asset).bands + assert b09_bands is not None + self.assertEqual(b09_bands[0].nodata, "nan") + # Set - item2 = pystac.Item.from_file(self.SENTINEL2_EXAMPLE_URI) b2_asset = item2.assets["B02"] self.assertEqual( get_opt(get_opt(RasterExtension.ext(b2_asset).bands)[0].statistics).maximum, @@ -90,6 +96,9 @@ def test_asset_bands(self) -> None: get_opt(get_opt(new_b2_asset_bands)[0].statistics).maximum, 20567 ) + new_b2_asset_band0 = get_opt(new_b2_asset_bands)[0] + new_b2_asset_band0.nodata = NoDataStrings.INF + item2.validate() # Check adding a new asset @@ -127,7 +136,7 @@ def test_asset_bands(self) -> None: histogram=new_histograms[1], ), RasterBand.create( - nodata=3, + nodata=NoDataStrings.NINF, unit="test3", statistics=new_stats[2], histogram=new_histograms[2], @@ -148,6 +157,9 @@ def test_asset_bands(self) -> None: item.assets["test"].extra_fields["raster:bands"][1]["histogram"]["min"], 3848.354901960784, ) + self.assertEqual( + item.assets["test"].extra_fields["raster:bands"][2]["nodata"], "-inf" + ) for s in new_stats: s.minimum = None @@ -190,7 +202,7 @@ def test_asset_bands(self) -> None: histogram=new_histograms[1], ) new_bands[0].apply( - nodata=3, + nodata=NoDataStrings.NAN, unit="test3", statistics=new_stats[0], histogram=new_histograms[2], @@ -202,6 +214,9 @@ def test_asset_bands(self) -> None: ], 1, ) + self.assertEqual( + item.assets["test"].extra_fields["raster:bands"][0]["nodata"], "nan" + ) def test_extension_not_implemented(self) -> None: # Should raise exception if Item does not include extension URI