Esempio n. 1
0
    def tiles_from_geopolygon(
        self,
        geopolygon: geometry.Geometry,
        tile_buffer: Optional[Tuple[float, float]] = None,
        geobox_cache: Optional[dict] = None
    ) -> Iterator[Tuple[Tuple[int, int], geometry.GeoBox]]:
        """
        Returns an iterator of tile_index, :py:class:`GeoBox` tuples across
        the grid and overlapping with the specified `geopolygon`.

        .. note::

           Grid cells are referenced by coordinates `(x, y)`, which is the opposite to the usual CRS
           dimension order.

        :param geometry.Geometry geopolygon: Polygon to tile
        :param tile_buffer: Optional <float,float> tuple, (extra padding for the query
                            in native units of this GridSpec)
        :param dict geobox_cache: Optional cache to re-use geoboxes instead of creating new one each time
        :return: iterator of grid cells with :py:class:`GeoBox` tiles
        """
        geopolygon = geopolygon.to_crs(self.crs)
        bbox = geopolygon.boundingbox
        bbox = bbox.buffered(*tile_buffer) if tile_buffer else bbox

        for tile_index, tile_geobox in self.tiles(bbox, geobox_cache):
            tile_geobox = tile_geobox.buffered(
                *tile_buffer) if tile_buffer else tile_geobox

            if geometry.intersects(tile_geobox.extent, geopolygon):
                yield (tile_index, tile_geobox)
def transform_geojson_wgs_to_epsg(geojson, EPSG):
    """
    Takes a geojson dictionary and converts it from WGS84 (EPSG:4326) to desired EPSG

    Parameters
    ----------
    geojson: dict
        a geojson dictionary containing a 'geometry' key, in WGS84 coordinates
    EPSG: int
        numeric code for the EPSG coordinate referecnce system to transform into

    Returns
    -------
    transformed_geojson: dict
        a geojson dictionary containing a 'coordinates' key, in the desired CRS

    """
    gg = Geometry(geojson['geometry'], CRS('epsg:4326'))
    gg = gg.to_crs(CRS(f'epsg:{EPSG}'))
    return gg.__geo_interface__
Esempio n. 3
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
Esempio n. 4
0
def to_stac_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,
) -> dict:
    """
    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.
    """

    geom = Geometry(dataset.geometry, CRS(dataset.crs))
    wgs84_geometry = geom.to_crs(CRS("epsg:4326"), math.inf)

    properties = eo3_to_stac_properties(
        dataset.properties, dataset.crs, title=dataset.label
    )

    # 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
    )

    links = []
    if stac_item_destination_url:
        links.append(
            {
                "rel": "self",
                "type": "application/json",
                "href": stac_item_destination_url,
            }
        )
    if odc_dataset_metadata_url:
        links.append(
            {
                "title": "ODC Dataset YAML",
                "rel": "odc_yaml",
                "type": "text/yaml",
                "href": odc_dataset_metadata_url,
            }
        )
    links.extend(_odc_links(explorer_base_url, dataset, collection_url))

    item_doc = dict(
        stac_version="1.0.0-beta.2",
        stac_extensions=["eo", "projection"],
        type="Feature",
        id=dataset.id,
        bbox=wgs84_geometry.boundingbox,
        geometry=wgs84_geometry.json,
        properties={
            **properties,
            "odc:product": dataset.product.name,
            **(_proj_fields(dataset.grids) if dataset.grids else {}),
            **_lineage_fields(dataset.lineage),
        },
        # TODO: Currently assuming no name collisions.
        assets={
            **{
                name: (
                    {
                        "eo:bands": [{"name": name}],
                        **_media_fields(Path(m.path)),
                        "roles": ["data"],
                        "href": urljoin(dataset_location, m.path),
                        **(
                            _proj_fields(dataset.grids, m.grid) if dataset.grids else {}
                        ),
                    }
                )
                for name, m in dataset.measurements.items()
            },
            **{
                name: (
                    {
                        **_asset_title_fields(name),
                        **_media_fields(Path(m.path)),
                        **_asset_roles_fields(name),
                        "href": urljoin(dataset_location, m.path),
                    }
                )
                for name, m in dataset.accessories.items()
            },
        },
        links=links,
    )

    # To pass validation, only add 'view' extension when we're using it somewhere.
    if any(k.startswith("view:") for k in item_doc["properties"].keys()):
        item_doc["stac_extensions"].append("view")

    return item_doc