def test_azimuth(self) -> None: view_item = pystac.Item.from_file(self.example_uri) # Get self.assertIn("view:azimuth", view_item.properties) view_azimuth = ViewExtension.ext(view_item).azimuth assert view_azimuth is not None self.assertEqual(view_azimuth, view_item.properties["view:azimuth"]) # Set ViewExtension.ext(view_item).azimuth = view_azimuth + 100 self.assertEqual(view_azimuth + 100, view_item.properties["view:azimuth"]) # Get from Asset asset_no_prop = view_item.assets["blue"] asset_prop = view_item.assets["red"] self.assertEqual( ViewExtension.ext(asset_no_prop).azimuth, ViewExtension.ext(view_item).azimuth, ) self.assertEqual(ViewExtension.ext(asset_prop).azimuth, 5.0) # Set to Asset asset_value = 15.0 ViewExtension.ext(asset_no_prop).azimuth = asset_value self.assertNotEqual( ViewExtension.ext(asset_no_prop).azimuth, ViewExtension.ext(view_item).azimuth, ) self.assertEqual(ViewExtension.ext(asset_no_prop).azimuth, asset_value) # Validate view_item.validate()
def process_view_geometry(item, granule): view_extension = ViewExtension.ext(item, add_if_missing=True) for attribute in granule.AdditionalAttributes.AdditionalAttribute: if attribute.Name == "MEAN_SUN_AZIMUTH_ANGLE": view_extension.sun_azimuth = float(attribute.Values.Value.cdata) if attribute.Name == "MEAN_VIEW_AZIMUTH_ANGLE": view_extension.azimuth = float(attribute.Values.Value.cdata)
def test_item_ext_add_to(self) -> None: item = pystac.Item.from_file(self.example_uri) item.stac_extensions.remove(ViewExtension.get_schema_uri()) self.assertNotIn(ViewExtension.get_schema_uri(), item.stac_extensions) _ = ViewExtension.ext(item, add_if_missing=True) self.assertIn(ViewExtension.get_schema_uri(), item.stac_extensions)
def test_extension_not_implemented(self) -> None: # Should raise exception if Item does not include extension URI item = pystac.Item.from_file(self.example_uri) item.stac_extensions.remove(ViewExtension.get_schema_uri()) with self.assertRaises(pystac.ExtensionNotImplemented): _ = ViewExtension.ext(item) # Should raise exception if owning Item does not include extension URI asset = item.assets["blue"] with self.assertRaises(pystac.ExtensionNotImplemented): _ = ViewExtension.ext(asset) # Should succeed if Asset has no owner ownerless_asset = pystac.Asset.from_dict(asset.to_dict()) _ = ViewExtension.ext(ownerless_asset)
def test_migrates_added_extension(self) -> None: item = pystac.Item.from_file( TestCases.get_path("data-files/examples/0.8.1/item-spec/" "examples/planet-sample.json")) self.assertTrue(ViewExtension.has_extension(item)) view_ext = ViewExtension.ext(item) self.assertEqual(view_ext.sun_azimuth, 101.8) self.assertEqual(view_ext.sun_elevation, 58.8) self.assertEqual(view_ext.off_nadir, 1)
def test_incidence_angle(self) -> None: view_item = pystac.Item.from_file(self.example_uri) # Get self.assertIn("view:incidence_angle", view_item.properties) view_incidence_angle = ViewExtension.ext(view_item).incidence_angle assert view_incidence_angle is not None self.assertEqual(view_incidence_angle, view_item.properties["view:incidence_angle"]) # Set ViewExtension.ext( view_item).incidence_angle = view_incidence_angle + 10 self.assertEqual(view_incidence_angle + 10, view_item.properties["view:incidence_angle"]) # Get from Asset asset_no_prop = view_item.assets["blue"] asset_prop = view_item.assets["red"] self.assertEqual( ViewExtension.ext(asset_no_prop).incidence_angle, ViewExtension.ext(view_item).incidence_angle, ) self.assertEqual(ViewExtension.ext(asset_prop).incidence_angle, 4.0) # Set to Asset asset_value = 14.0 ViewExtension.ext(asset_no_prop).incidence_angle = asset_value self.assertNotEqual( ViewExtension.ext(asset_no_prop).incidence_angle, ViewExtension.ext(view_item).incidence_angle, ) self.assertEqual( ViewExtension.ext(asset_no_prop).incidence_angle, asset_value) # Validate view_item.validate()
def test_off_nadir(self) -> None: view_item = pystac.Item.from_file(self.example_uri) # Get self.assertIn("view:off_nadir", view_item.properties) view_off_nadir = ViewExtension.ext(view_item).off_nadir assert view_off_nadir is not None self.assertEqual(view_off_nadir, view_item.properties["view:off_nadir"]) # Set ViewExtension.ext(view_item).off_nadir = view_off_nadir + 10 self.assertEqual(view_off_nadir + 10, view_item.properties["view:off_nadir"]) # Get from Asset asset_no_prop = view_item.assets["blue"] asset_prop = view_item.assets["red"] self.assertEqual( ViewExtension.ext(asset_no_prop).off_nadir, ViewExtension.ext(view_item).off_nadir, ) self.assertEqual(ViewExtension.ext(asset_prop).off_nadir, 3.0) # Set to Asset asset_value = 13.0 ViewExtension.ext(asset_no_prop).off_nadir = asset_value self.assertNotEqual( ViewExtension.ext(asset_no_prop).off_nadir, ViewExtension.ext(view_item).off_nadir, ) self.assertEqual( ViewExtension.ext(asset_no_prop).off_nadir, asset_value) # Validate view_item.validate()
def test_sun_elevation(self) -> None: view_item = pystac.Item.from_file(self.example_uri) # Get self.assertIn("view:sun_elevation", view_item.properties) view_sun_elevation = ViewExtension.ext(view_item).sun_elevation assert view_sun_elevation is not None self.assertEqual(view_sun_elevation, view_item.properties["view:sun_elevation"]) # Set ViewExtension.ext(view_item).sun_elevation = view_sun_elevation + 10 self.assertEqual(view_sun_elevation + 10, view_item.properties["view:sun_elevation"]) # Get from Asset asset_no_prop = view_item.assets["blue"] asset_prop = view_item.assets["red"] self.assertEqual( ViewExtension.ext(asset_no_prop).sun_elevation, ViewExtension.ext(view_item).sun_elevation, ) self.assertEqual(ViewExtension.ext(asset_prop).sun_elevation, 2.0) # Set to Asset asset_value = 12.0 ViewExtension.ext(asset_no_prop).sun_elevation = asset_value self.assertNotEqual( ViewExtension.ext(asset_no_prop).sun_elevation, ViewExtension.ext(view_item).sun_elevation, ) self.assertEqual( ViewExtension.ext(asset_no_prop).sun_elevation, asset_value) # Validate view_item.validate()
def test_apply(self) -> None: item = next(iter(TestCases.test_case_2().get_all_items())) self.assertFalse(ViewExtension.has_extension(item)) ViewExtension.add_to(item) ViewExtension.ext(item).apply( off_nadir=1.0, incidence_angle=2.0, azimuth=3.0, sun_azimuth=4.0, sun_elevation=5.0, ) self.assertEqual(ViewExtension.ext(item).off_nadir, 1.0) self.assertEqual(ViewExtension.ext(item).incidence_angle, 2.0) self.assertEqual(ViewExtension.ext(item).azimuth, 3.0) self.assertEqual(ViewExtension.ext(item).sun_azimuth, 4.0) self.assertEqual(ViewExtension.ext(item).sun_elevation, 5.0)
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