diff --git a/autotest/gdrivers/data/stacit/test_stac_1.1.json b/autotest/gdrivers/data/stacit/test_stac_1.1.json new file mode 100644 index 000000000000..bcf1dd31800f --- /dev/null +++ b/autotest/gdrivers/data/stacit/test_stac_1.1.json @@ -0,0 +1,53 @@ +{ + "type": "Feature", + "stac_version": "1.1.0", + "stac_extensions": [ + "https://stac-extensions.github.io/eo/v2.0.0/schema.json", + "https://stac-extensions.github.io/projection/v2.0.0/schema.json" + ], + "id": "byte", + "geometry": null, + "properties": { + "datetime": "2021-07-19T10:57:30Z", + "proj:code": "EPSG:26711", + "proj:bbox": [ + 440720.000, 3750120.000, + 441920.000, 3751320.000 + ], + "proj:transform": [ + 60, + 0, + 440720, + 0, + -60, + 3751320 + ] + }, + "collection": "my_collection", + "assets": { + "metadata": { + "title": "Original XML metadata", + "type": "application/xml", + "roles": [ + "metadata" + ], + "href": "https://example.com/metadata.xml" + }, + "B01": { + "title": "Band 1 (coastal)", + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "roles": [ + "data" + ], + "bands": [ + { + "name": "B01", + "eo:common_name": "coastal", + "eo:center_wavelength": 0.4439, + "eo:full_width_half_max": 0.027 + } + ], + "href": "data/byte.tif" + } + } +} diff --git a/autotest/gdrivers/stacit.py b/autotest/gdrivers/stacit.py index eaec895eb94a..ac609a92a598 100755 --- a/autotest/gdrivers/stacit.py +++ b/autotest/gdrivers/stacit.py @@ -361,3 +361,28 @@ def test_stacit_single_feature(tmp_vsimem): assert ds is not None assert ds.RasterXSize == 20 assert ds.GetRasterBand(1).Checksum() == 4672 + + +############################################################################### +# Test STAC 1.1 + + +def test_stacit_stac_1_1(tmp_vsimem): + + filename = str(tmp_vsimem / "feature.json") + with gdaltest.tempfile( + filename, open("data/stacit/test_stac_1.1.json", "rb").read() + ): + ds = gdal.Open(filename) + assert ds is not None + assert ds.RasterXSize == 20 + assert ds.GetSpatialRef().GetName() == "NAD27 / UTM zone 11N" + assert ds.GetGeoTransform() == pytest.approx( + [440720.0, 60.0, 0.0, 3751320.0, 0.0, -60.0], rel=1e-8 + ) + assert ds.GetRasterBand(1).GetMetadata() == { + "eo:center_wavelength": "0.4439", + "eo:full_width_half_max": "0.027", + } + assert ds.GetRasterBand(1).GetColorInterpretation() == gdal.GCI_CoastalBand + assert ds.GetRasterBand(1).Checksum() == 4672 diff --git a/frmts/stacit/stacitdataset.cpp b/frmts/stacit/stacitdataset.cpp index 993b67ca3dd8..ef888745a503 100644 --- a/frmts/stacit/stacitdataset.cpp +++ b/frmts/stacit/stacitdataset.cpp @@ -44,7 +44,7 @@ struct AssetSetByProjection struct Asset { std::string osName{}; - CPLJSONArray eoBands{}; + CPLJSONArray bands{}; std::map assets{}; }; @@ -123,10 +123,19 @@ int STACITDataset::Identify(GDALOpenInfo *poOpenInfo) return pszHeader[0] == '{'; } - if (strstr(pszHeader, "\"stac_version\"") != nullptr && - strstr(pszHeader, "\"proj:transform\"") != nullptr) + if (strstr(pszHeader, "\"stac_version\"") != nullptr) { - return true; + int nTransformBBOXShapeCount = 0; + for (const char *pszItem : + {"\"proj:transform\"", "\"proj:bbox\"", "\"proj:shape\""}) + { + if (strstr(pszHeader, pszItem)) + nTransformBBOXShapeCount++; + } + if (nTransformBBOXShapeCount >= 2) + { + return true; + } } if (i == 0) @@ -218,34 +227,44 @@ static void ParseAsset(const CPLJSONObject &jAsset, return oProperties[pszName]; }; - auto oProjEPSG = GetAssetOrFeatureProperty("proj:epsg"); std::string osProjUserString; - if (oProjEPSG.IsValid() && oProjEPSG.GetType() != CPLJSONObject::Type::Null) + const auto oProjCode = GetAssetOrFeatureProperty("proj:code"); + if (oProjCode.IsValid() && oProjCode.GetType() != CPLJSONObject::Type::Null) { - osProjUserString = "EPSG:" + oProjEPSG.ToString(); + osProjUserString = oProjCode.ToString(); } else { - auto oProjWKT2 = GetAssetOrFeatureProperty("proj:wkt2"); - if (oProjWKT2.IsValid() && - oProjWKT2.GetType() == CPLJSONObject::Type::String) + const auto oProjEPSG = GetAssetOrFeatureProperty("proj:epsg"); + if (oProjEPSG.IsValid() && + oProjEPSG.GetType() != CPLJSONObject::Type::Null) { - osProjUserString = oProjWKT2.ToString(); + osProjUserString = "EPSG:" + oProjEPSG.ToString(); } else { - auto oProjPROJJSON = GetAssetOrFeatureProperty("proj:projjson"); - if (oProjPROJJSON.IsValid() && - oProjPROJJSON.GetType() == CPLJSONObject::Type::Object) + const auto oProjWKT2 = GetAssetOrFeatureProperty("proj:wkt2"); + if (oProjWKT2.IsValid() && + oProjWKT2.GetType() == CPLJSONObject::Type::String) { - osProjUserString = oProjPROJJSON.ToString(); + osProjUserString = oProjWKT2.ToString(); } else { - CPLDebug("STACIT", - "Skipping asset %s that lacks a valid CRS member", - osAssetName.c_str()); - return; + const auto oProjPROJJSON = + GetAssetOrFeatureProperty("proj:projjson"); + if (oProjPROJJSON.IsValid() && + oProjPROJJSON.GetType() == CPLJSONObject::Type::Object) + { + osProjUserString = oProjPROJJSON.ToString(); + } + else + { + CPLDebug("STACIT", + "Skipping asset %s that lacks a valid CRS member", + osAssetName.c_str()); + return; + } } } } @@ -380,7 +399,9 @@ static void ParseAsset(const CPLJSONObject &jAsset, { Asset asset; asset.osName = osAssetName; - asset.eoBands = jAsset.GetArray("eo:bands"); + asset.bands = jAsset.GetArray("bands"); + if (!asset.bands.IsValid()) + asset.bands = jAsset.GetArray("eo:bands"); collection.assets[osAssetName] = std::move(asset); } @@ -584,15 +605,17 @@ bool STACITDataset::SetupDataset( poVRTBand->SetColorInterpretation(eInterp); // Set band properties - if (asset.eoBands.IsValid() && - asset.eoBands.Size() == poItemDS->GetRasterCount()) + if (asset.bands.IsValid() && + asset.bands.Size() == poItemDS->GetRasterCount()) { - const auto &eoBand = asset.eoBands[i]; - const auto osBandName = eoBand["name"].ToString(); + const auto &band = asset.bands[i]; + const auto osBandName = band["name"].ToString(); if (!osBandName.empty()) poVRTBand->SetDescription(osBandName.c_str()); - const auto osCommonName = eoBand["common_name"].ToString(); + auto osCommonName = band["eo:common_name"].ToString(); + if (osCommonName.empty()) + osCommonName = band["common_name"].ToString(); if (!osCommonName.empty()) { const auto eInterpFromCommonName = @@ -601,13 +624,14 @@ bool STACITDataset::SetupDataset( poVRTBand->SetColorInterpretation(eInterpFromCommonName); } - for (const auto &eoBandChild : eoBand.GetChildren()) + for (const auto &bandChild : band.GetChildren()) { - const auto osChildName = eoBandChild.GetName(); - if (osChildName != "name" && osChildName != "common_name") + const auto osChildName = bandChild.GetName(); + if (osChildName != "name" && osChildName != "common_name" && + osChildName != "eo:common_name") { poVRTBand->SetMetadataItem(osChildName.c_str(), - eoBandChild.ToString().c_str()); + bandChild.ToString().c_str()); } } }