Example #1
0
    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)
Example #2
0
 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)
Example #3
0
    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()
Example #4
0
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)
Example #5
0
    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)
Example #6
0
    def test_set_sun_elevation_summaries(self) -> None:
        collection = self.collection.clone()
        view_summaries = ViewExtension.summaries(collection, True)

        view_summaries.sun_elevation = RangeSummary(-10, 38)
        self.assertDictEqual({
            "minimum": -10,
            "maximum": 38
        }, view_summaries.sun_elevation.to_dict())
Example #7
0
    def test_set_incidence_angle_summaries(self) -> None:
        collection = self.collection.clone()
        view_summaries = ViewExtension.summaries(collection, True)

        view_summaries.incidence_angle = RangeSummary(5, 15)
        self.assertDictEqual({
            "minimum": 5,
            "maximum": 15
        }, view_summaries.incidence_angle.to_dict())
Example #8
0
    def test_set_off_nadir_summaries(self) -> None:
        collection = self.collection.clone()
        view_summaries = ViewExtension.summaries(collection, True)

        view_summaries.off_nadir = RangeSummary(0, 10)
        self.assertDictEqual({
            "minimum": 0,
            "maximum": 10
        }, view_summaries.off_nadir.to_dict())
Example #9
0
    def test_get_azimuth_summaries(self) -> None:
        azimuths = ViewExtension.summaries(self.collection, True).azimuth

        assert azimuths is not None

        self.assertDictEqual({
            "minimum": 20,
            "maximum": 186
        }, azimuths.to_dict())
Example #10
0
    def test_get_off_nadir_summaries(self) -> None:
        off_nadirs = ViewExtension.summaries(self.collection, True).off_nadir

        assert off_nadirs is not None

        self.assertDictEqual({
            "minimum": 0.5,
            "maximum": 7.3
        }, off_nadirs.to_dict())
Example #11
0
    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)
Example #12
0
    def test_summaries_adds_uri(self) -> None:
        collection = self.collection.clone()
        collection.stac_extensions = []
        self.assertRaisesRegex(
            pystac.ExtensionNotImplemented,
            r"Could not find extension schema URI.*",
            ViewExtension.summaries,
            collection,
            False,
        )
        _ = ViewExtension.summaries(collection, True)

        self.assertIn(ViewExtension.get_schema_uri(),
                      collection.stac_extensions)

        ViewExtension.remove_from(collection)
        self.assertNotIn(ViewExtension.get_schema_uri(),
                         collection.stac_extensions)
Example #13
0
    def test_set_sun_azimuth_summaries(self) -> None:
        collection = self.collection.clone()
        view_summaries = ViewExtension.summaries(collection, True)

        view_summaries.sun_azimuth = RangeSummary(210, 275)
        self.assertDictEqual({
            "minimum": 210,
            "maximum": 275
        }, view_summaries.sun_azimuth.to_dict())
Example #14
0
    def test_get_sun_azimuth_summaries(self) -> None:
        sun_azimuths = ViewExtension.summaries(self.collection,
                                               True).sun_azimuth

        assert sun_azimuths is not None

        self.assertDictEqual({
            "minimum": 48,
            "maximum": 78
        }, sun_azimuths.to_dict())
Example #15
0
    def test_get_incidence_angle_summaries(self) -> None:
        incidence_angles = ViewExtension.summaries(self.collection,
                                                   True).incidence_angle

        assert incidence_angles is not None

        self.assertDictEqual({
            "minimum": 23,
            "maximum": 35
        }, incidence_angles.to_dict())
Example #16
0
    def test_get_sun_elevation_summaries(self) -> None:
        sun_elevations = ViewExtension.summaries(self.collection,
                                                 True).sun_elevation

        assert sun_elevations is not None

        self.assertDictEqual({
            "minimum": 10,
            "maximum": 45
        }, sun_elevations.to_dict())
Example #17
0
    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()
Example #18
0
    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()
Example #19
0
    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()
Example #20
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
Example #21
0
 def test_validate_view(self) -> None:
     item = pystac.Item.from_file(self.example_uri)
     self.assertTrue(ViewExtension.has_extension(item))
     item.validate()
Example #22
0
    def test_set_azimuth_summaries(self) -> None:
        collection = self.collection.clone()
        view_summaries = ViewExtension.summaries(collection, True)

        view_summaries.azimuth = None
        self.assertIsNone(view_summaries.azimuth)