def test_bands(self) -> None: item = pystac.Item.from_file(self.BANDS_IN_ITEM_URI) # Get self.assertIn("eo:bands", item.properties) bands = EOExtension.ext(item).bands assert bands is not None self.assertEqual( list(map(lambda x: x.name, bands)), ["band1", "band2", "band3", "band4"] ) # Set new_bands = [ Band.create(name="red", description=Band.band_description("red")), Band.create(name="green", description=Band.band_description("green")), Band.create(name="blue", description=Band.band_description("blue")), ] EOExtension.ext(item).bands = new_bands self.assertEqual( "Common name: red, Range: 0.6 to 0.7", item.properties["eo:bands"][0]["description"], ) self.assertEqual(len(EOExtension.ext(item).bands or []), 3) item.validate()
def test_item_ext_add_to(self) -> None: item = pystac.Item.from_file(self.PLAIN_ITEM) self.assertNotIn(EOExtension.get_schema_uri(), item.stac_extensions) _ = EOExtension.ext(item, add_if_missing=True) self.assertIn(EOExtension.get_schema_uri(), item.stac_extensions)
def test_asset_ext_add_to(self) -> None: item = pystac.Item.from_file(self.PLAIN_ITEM) self.assertNotIn(EOExtension.get_schema_uri(), item.stac_extensions) asset = item.assets["thumbnail"] _ = EOExtension.ext(asset, add_if_missing=True) self.assertIn(EOExtension.get_schema_uri(), item.stac_extensions)
def test_summaries_adds_uri(self) -> None: col = pystac.Collection.from_file(self.EO_COLLECTION_URI) col.stac_extensions = [] self.assertRaisesRegex( pystac.ExtensionNotImplemented, r"Could not find extension schema URI.*", EOExtension.summaries, col, False, ) _ = EOExtension.summaries(col, add_if_missing=True) self.assertIn(EOExtension.get_schema_uri(), col.stac_extensions) EOExtension.remove_from(col) self.assertNotIn(EOExtension.get_schema_uri(), col.stac_extensions)
def add_assets(item, granule, endpoint, version): item_id = granule.GranuleUR.cdata product = item_id.split(".")[1] if product == "S30": band_info = sentinel_band_info url = f"https://{endpoint}/lp-prod-protected/HLSS30.{version}/{item_id}/" public_url = f"https://{endpoint}/lp-prod-public/HLSS30.{version}/{item_id}/" if product == "L30": band_info = landsat_band_info url = f"https://{endpoint}/lp-prod-protected/HLSL30.{version}/{item_id}/" public_url = f"https://{endpoint}/lp-prod-public/HLSL30.{version}/{item_id}/" url_template = url + "{}.{}.tif" for band_id, band_info in band_info.items(): band_url = url_template.format(item_id, band_id) asset = pystac.Asset(href=band_url, media_type=pystac.MediaType.COG, roles=["data"]) bands = [band_info["band"]] EOExtension.ext(asset).bands = bands item.add_asset(band_id, asset) thumbnail_url = f"{public_url}{item_id}.jpg" thumbnail_asset = pystac.Asset(href=thumbnail_url, media_type=pystac.MediaType.JPEG, roles=["thumbnail"]) item.add_asset("thumbnail", thumbnail_asset) item.set_self_href(f"{public_url}{item_id}_stac.json")
def test_extension_not_implemented(self) -> None: # Should raise exception if Item does not include extension URI item = pystac.Item.from_file(self.PLAIN_ITEM) with self.assertRaises(pystac.ExtensionNotImplemented): _ = EOExtension.ext(item) # Should raise exception if owning Item does not include extension URI asset = item.assets["thumbnail"] with self.assertRaises(pystac.ExtensionNotImplemented): _ = EOExtension.ext(asset) # Should succeed if Asset has no owner ownerless_asset = pystac.Asset.from_dict(asset.to_dict()) _ = EOExtension.ext(ownerless_asset)
def test_asset_ext_add_to_ownerless_asset(self) -> None: item = pystac.Item.from_file(self.PLAIN_ITEM) asset_dict = item.assets["thumbnail"].to_dict() asset = pystac.Asset.from_dict(asset_dict) with self.assertRaises(pystac.STACError): _ = EOExtension.ext(asset, add_if_missing=True)
def test_cloud_cover(self) -> None: item = pystac.Item.from_file(self.LANDSAT_EXAMPLE_URI) # Get self.assertIn("eo:cloud_cover", item.properties) cloud_cover = EOExtension.ext(item).cloud_cover self.assertEqual(cloud_cover, 78) # Set EOExtension.ext(item).cloud_cover = 50 self.assertEqual(item.properties["eo:cloud_cover"], 50) # Get from Asset b2_asset = item.assets["B2"] self.assertEqual( EOExtension.ext(b2_asset).cloud_cover, EOExtension.ext(item).cloud_cover ) b3_asset = item.assets["B3"] self.assertEqual(EOExtension.ext(b3_asset).cloud_cover, 20) # Set on Asset EOExtension.ext(b2_asset).cloud_cover = 10 self.assertEqual(EOExtension.ext(b2_asset).cloud_cover, 10) item.validate()
def test_asset_bands(self) -> None: item = pystac.Item.from_file(self.LANDSAT_EXAMPLE_URI) # Get b1_asset = item.assets["B1"] asset_bands = EOExtension.ext(b1_asset).bands assert asset_bands is not None self.assertEqual(len(asset_bands), 1) self.assertEqual(asset_bands[0].name, "B1") self.assertEqual(asset_bands[0].solar_illumination, 2000) index_asset = item.assets["index"] asset_bands = EOExtension.ext(index_asset).bands self.assertIs(None, asset_bands) # No asset specified item_bands = EOExtension.ext(item).bands self.assertIsNot(None, item_bands) # Set b2_asset = item.assets["B2"] self.assertEqual(get_opt(EOExtension.ext(b2_asset).bands)[0].name, "B2") EOExtension.ext(b2_asset).bands = EOExtension.ext(b1_asset).bands new_b2_asset_bands = EOExtension.ext(item.assets["B2"]).bands self.assertEqual(get_opt(new_b2_asset_bands)[0].name, "B1") item.validate() # Check adding a new asset new_bands = [ Band.create( name="red", description=Band.band_description("red"), solar_illumination=1900, ), Band.create( name="green", description=Band.band_description("green"), solar_illumination=1950, ), Band.create( name="blue", description=Band.band_description("blue"), solar_illumination=2000, ), ] asset = pystac.Asset(href="some/path.tif", media_type=pystac.MediaType.GEOTIFF) EOExtension.ext(asset).bands = new_bands item.add_asset("test", asset) self.assertEqual(len(item.assets["test"].extra_fields["eo:bands"]), 3)
def test_reads_asset_bands_in_pre_1_0_version(self) -> None: item = pystac.Item.from_file( TestCases.get_path( "data-files/examples/0.9.0/item-spec/examples/" "landsat8-sample.json" ) ) bands = EOExtension.ext(item.assets["B9"]).bands self.assertEqual(len(bands or []), 1) self.assertEqual(get_opt(bands)[0].common_name, "cirrus")
def test_add_to(self) -> None: item = Item.from_file(self.PLAIN_ITEM) self.assertNotIn(EOExtension.get_schema_uri(), item.stac_extensions) # Check that the URI gets added to stac_extensions EOExtension.add_to(item) self.assertIn(EOExtension.get_schema_uri(), item.stac_extensions) # Check that the URI only gets added once, regardless of how many times add_to # is called. EOExtension.add_to(item) EOExtension.add_to(item) eo_uris = [ uri for uri in item.stac_extensions if uri == EOExtension.get_schema_uri() ] self.assertEqual(len(eo_uris), 1)
def test_item_apply(self) -> None: item = pystac.Item.from_file(self.LANDSAT_EXAMPLE_URI) eo_ext = EOExtension.ext(item) test_band = Band.create(name="test") self.assertEqual(eo_ext.cloud_cover, 78) self.assertNotIn(test_band, eo_ext.bands or []) eo_ext.apply(bands=[test_band], cloud_cover=15) assert eo_ext.bands is not None self.assertEqual(test_band.to_dict(), eo_ext.bands[0].to_dict()) self.assertEqual(eo_ext.cloud_cover, 15)
def test_summaries(self) -> None: col = pystac.Collection.from_file(self.EO_COLLECTION_URI) eo_summaries = EOExtension.summaries(col) # Get cloud_cover_summaries = eo_summaries.cloud_cover assert cloud_cover_summaries is not None self.assertEqual(cloud_cover_summaries.minimum, 0.0) self.assertEqual(cloud_cover_summaries.maximum, 80.0) bands = eo_summaries.bands assert bands is not None self.assertEqual(len(bands), 11) # Set eo_summaries.cloud_cover = RangeSummary(1.0, 2.0) eo_summaries.bands = [Band.create(name="test")] col_dict = col.to_dict() self.assertEqual(len(col_dict["summaries"]["eo:bands"]), 1) self.assertEqual(col_dict["summaries"]["eo:cloud_cover"]["minimum"], 1.0)
def test_asset_bands_s2(self) -> None: item = pystac.Item.from_file(self.S2_ITEM_URI) mtd_asset = item.get_assets()["mtd"] self.assertIsNone(EOExtension.ext(mtd_asset).bands)
def process_eo(item, granule): eo_extension = EOExtension.ext(item, add_if_missing=True) for attribute in granule.AdditionalAttributes.AdditionalAttribute: if attribute.Name == "CLOUD_COVERAGE": eo_extension.cloud_cover = float(attribute.Values.Value.cdata)
def test_read_eo_items_are_heritable(self) -> None: cat = TestCases.test_case_5() item = next(iter(cat.get_all_items())) self.assertTrue(EOExtension.has_extension(item))
def to_pystac_item( dataset: DatasetDoc, stac_item_destination_url: str, dataset_location: Optional[str] = None, odc_dataset_metadata_url: Optional[str] = None, explorer_base_url: Optional[str] = None, collection_url: Optional[str] = None, ) -> pystac.Item: """ Convert the given ODC Dataset into a Stac Item document. Note: You may want to call `validate_item(doc)` on the outputs to find any incomplete properties. :param collection_url: URL to the Stac Collection. Either this or an explorer_base_url should be specified for Stac compliance. :param stac_item_destination_url: Public 'self' URL where the stac document will be findable. :param dataset_location: Use this location instead of picking from dataset.locations (for calculating relative band paths) :param odc_dataset_metadata_url: Public URL for the original ODC dataset yaml document :param explorer_base_url: An Explorer instance that contains this dataset. Will allow links to things such as the product definition. """ if dataset.geometry is not None: geom = Geometry(dataset.geometry, CRS(dataset.crs)) wgs84_geometry = geom.to_crs(CRS("epsg:4326"), math.inf) geometry = wgs84_geometry.json bbox = wgs84_geometry.boundingbox else: geometry = None bbox = None properties = eo3_to_stac_properties(dataset, title=dataset.label) properties.update(_lineage_fields(dataset.lineage)) dt = properties["datetime"] del properties["datetime"] # TODO: choose remote if there's multiple locations? # Without a dataset location, all paths will be relative. dataset_location = dataset_location or (dataset.locations[0] if dataset.locations else None) item = Item( id=str(dataset.id), datetime=dt, properties=properties, geometry=geometry, bbox=bbox, collection=dataset.product.name, ) # Add links if stac_item_destination_url: item.links.append( Link( rel="self", media_type=MediaType.JSON, target=stac_item_destination_url, )) if odc_dataset_metadata_url: item.links.append( Link( title="ODC Dataset YAML", rel="odc_yaml", media_type="text/yaml", target=odc_dataset_metadata_url, )) for link in _odc_links(explorer_base_url, dataset, collection_url): item.links.append(link) EOExtension.ext(item, add_if_missing=True) if dataset.geometry: proj = ProjectionExtension.ext(item, add_if_missing=True) epsg, wkt = _get_projection(dataset) if epsg is not None: proj.apply(epsg=epsg, **_proj_fields(dataset.grids)) elif wkt is not None: proj.apply(wkt2=wkt, **_proj_fields(dataset.grids)) else: raise STACError( "Projection extension requires either epsg or wkt for crs.") # To pass validation, only add 'view' extension when we're using it somewhere. if any(k.startswith("view:") for k in properties.keys()): ViewExtension.ext(item, add_if_missing=True) # Add assets that are data for name, measurement in dataset.measurements.items(): if not dataset_location and not measurement.path: # No URL to link to. URL is mandatory for Stac validation. continue asset = Asset( href=_uri_resolve(dataset_location, measurement.path), media_type=_media_type(Path(measurement.path)), title=name, roles=["data"], ) eo = EOExtension.ext(asset) # TODO: pull out more information about the band band = Band.create(name) eo.apply(bands=[band]) if dataset.grids: proj_fields = _proj_fields(dataset.grids, measurement.grid) if proj_fields is not None: proj = ProjectionExtension.ext(asset) # Not sure how this handles None for an EPSG code proj.apply( shape=proj_fields["shape"], transform=proj_fields["transform"], epsg=epsg, ) item.add_asset(name, asset=asset) # Add assets that are accessories for name, measurement in dataset.accessories.items(): if not dataset_location and not measurement.path: # No URL to link to. URL is mandatory for Stac validation. continue asset = Asset( href=_uri_resolve(dataset_location, measurement.path), media_type=_media_type(Path(measurement.path)), title=_asset_title_fields(name), roles=_asset_roles_fields(name), ) item.add_asset(name, asset=asset) return item
def test_extend_invalid_object(self) -> None: link = pystac.Link("child", "https://some-domain.com/some/path/to.json") with self.assertRaises(pystac.ExtensionTypeError): EOExtension.ext(link) # type: ignore