Source code for stactools.core.utils.raster_footprint

"""Generate convex hulls of valid raster data for use in STAC Item
geometries."""

import logging
import warnings
from enum import Enum, auto
from typing import Any, Dict, Iterator, List, Optional, Tuple, Type, TypeVar, Union

import numpy as np
import numpy.typing as npt
import rasterio
import rasterio.features
from pystac import Item
from rasterio import Affine, DatasetReader
from rasterio.crs import CRS
from shapely.geometry import mapping, shape
from shapely.geometry.multipolygon import MultiPolygon
from shapely.geometry.polygon import Polygon, orient
from shapely.ops import unary_union
from stactools.core.geometry import mutual_intersection

from ..projection import reproject_shape

logger = logging.getLogger(__name__)

# Roughly 1 centimeter in geodetic coordinates
DEFAULT_PRECISION = 7

T = TypeVar("T", bound="RasterFootprint")


[docs] class FootprintMergeStrategy(Enum): """Strategy for handling the aggregation of differing asset footprints.""" FIRST = auto() """Use the footprint of the first matching asset.""" UNION = auto() """Union the geometries of all matching assets.""" INTERSECTION = auto() """Use the mutual intersection of all matching asset footprints."""
[docs] def densify_by_factor( point_list: List[Tuple[float, float]], factor: int ) -> List[Tuple[float, float]]: """Densifies the number of points in a list of points by a ``factor``. For example, a list of 5 points and a factor of 2 will result in 10 points (one new point between each original adjacent points). Derived from code found at https://stackoverflow.com/questions/64995977/generating-equidistance-points-along-the-boundary-of-a-polygon-but-cw-ccw Args: point_list (List[Tuple[float, float]]): The list of points to be densified. factor (int): The factor by which to densify the points. A larger densification factor should be used when reprojection results in greater curvature from the original geometry. Returns: List[Tuple[float, float]]: A list of the densified points. """ # noqa: E501 points: Any = np.asarray(point_list) densified_number = len(points) * factor existing_indices = np.arange(0, densified_number, factor) interp_indices = np.arange(existing_indices[-1] + 1) interp_x = np.interp(interp_indices, existing_indices, points[:, 0]) interp_y = np.interp(interp_indices, existing_indices, points[:, 1]) return [(x, y) for x, y in zip(interp_x, interp_y)]
[docs] def densify_by_distance( point_list: List[Tuple[float, float]], distance: float ) -> List[Tuple[float, float]]: """Densifies the number of points in a list of points by inserting new points at intervals between each set of successive points. For example, if two successive points in the list are separated by 10 units and a ``distance`` of 2 is provided, 4 new points will be added between the two original points (one new point every 2 units of ``distance``). Derived from code found at https://stackoverflow.com/questions/64995977/generating-equidistance-points-along-the-boundary-of-a-polygon-but-cw-ccw Args: point_list (List[Tuple[float, float]]): The list of points to be densified. distance (float): The interval at which to insert additional points. A smaller densification distance should be used when reprojection results in greater curvature from the original geometry. Returns: List[Tuple[float, float]]: A list of the densified points. """ points: Any = np.asarray(point_list) dxdy = points[1:, :] - points[:-1, :] segment_lengths = np.sqrt(np.sum(np.square(dxdy), axis=1)) steps = segment_lengths / distance coordinate_steps = dxdy / steps.reshape(-1, 1) densified_points = np.empty((len(point_list) - 1,), dtype="O") for index in range(len(point_list) - 1): step = np.arange(steps[index]) densified_points[index] = ( np.array((step, step)).T * coordinate_steps[index] + points[index] ) final_point = points[-1].reshape(1, -1) densified_array = np.concatenate((*densified_points, final_point), axis=0) return [(float(row[0]), float(row[1])) for row in densified_array]
[docs] def reproject_polygon( polygon: Polygon, crs: CRS, precision: Optional[int] = DEFAULT_PRECISION, dst_crs: CRS = "EPSG:4326", ) -> Polygon: """DEPRECATED. .. deprecated:: 0.5.0 Use :func:`projection.reproject_shape` instead. Projects a polygon and rounds the projected vertex coordinates to ``precision``. Duplicate points caused by rounding are removed. Args: polygon (Polygon): The polygon to reproject. crs (CRS): The CRS of the input polygon. precision (int): The number of decimal places to include in the final polygon vertex coordinates. dst_crs (CRS): The CRS of the output polygon. Defaults to EPSG 4326 Returns: Polygon: Polygon in 'dst_crs'. Default to EPSG 4326 """ warnings.warn( "``utils.reproject_polygon`` is deprecated and will be removed in v0.6.0. " "Use ``projection.reproject_shape`` instead.", DeprecationWarning, ) return reproject_shape( src_crs=crs, dst_crs=dst_crs, geom=polygon, precision=precision )
[docs] class RasterFootprint: """An object for creating a convex hull polygon around all areas within an raster that have data values (i.e., they do not have the nodata value). This convex hull is termed the "footprint" of the raster data and is returned by the :meth:`footprint` method as a polygon in a GeoJSON dictionary for use as the geometry attribute of a STAC Item. Two important operations during this calculation are the densification of the footprint in the native CRS and simplification of the footprint after reprojection. If the initial low-vertex polygon in the native CRS is not densified, this can result in a reprojected polygon that does not accurately represent the data footprint. For example, a MODIS asset represented by a rectangular 5 point Polygon in a sinusoidal projection will reproject to a parallelogram in EPSG 4326, when it would be more accurately represented by a polygon with two parallel sides and two curved sides. The difference between these representations is greater the further away from the meridian and equator the asset is located. After reprojection, the footprint may have more points than desired. This can be simplified to a polygon with fewer points that maintain a maximum distance to the original geometry. Args: data_array (numpy.NDArray[Any]): The raster data used for the footprint calculation. crs (CRS): Coordinate reference system of the raster data. dst_crs (CRS): Coordinate reference system of the footprint data. Defaults to EPSG 4236. transform (Affine): Matrix defining the transformation from pixel to CRS coordinates. precision (int): The number of decimal places to include in the final footprint coordinates. densification_factor (Optional[int]): The factor by which to increase point density within the footprint polygon before projection. A factor of 2 would double the density of points (placing one new point between each existing pair of points), a factor of 3 would place two points between each point, etc. Higher densities produce higher fidelity footprints in areas of high projection distortion. Mutually exclusive with ``densification_distance``. densification_distance (Optional[float]): The distance by which to increase point density within the footprint polygon before projection. If the distance is set to 2 and the segment length between two polygon vertices is 10, 4 new vertices would be created along the segment. Higher densities produce higher fidelity footprints in areas of high projection distortion. Mutually exclusive with ``densification_factor``. simplify_tolerance (Optional[float]): Distance, in degrees, within which all locations on the simplified polygon will be to the original polygon. no_data (Optional[Union[int, float]]): The nodata value in ``data_array``. If set to None, this will return a footprint including nodata values. """ crs: CRS """Coordinate reference system of the raster data.""" dst_crs: CRS = "EPSG:4326" """Coordinate reference system of the footprint.""" data_array: npt.NDArray[Any] """2D or 3D array of raster data.""" densification_distance: Optional[float] """Optional distance for densifying polygon vertices before reprojection.""" densification_factor: Optional[int] """Optional factor for densifying polygon vertices before reprojection.""" no_data: Optional[Union[int, float]] """Optional value defining pixels to exclude from the footprint.""" precision: int """Number of decimal places in the final footprint coordinates.""" simplify_tolerance: Optional[float] """Optional maximum allowable error when simplifying the reprojected polygon.""" transform: Affine """Transformation matrix from pixel to CRS coordinates.""" def __init__( self, data_array: npt.NDArray[Any], crs: CRS, transform: Affine, *, dst_crs: CRS = "EPSG:4326", precision: int = DEFAULT_PRECISION, densification_factor: Optional[int] = None, densification_distance: Optional[float] = None, simplify_tolerance: Optional[float] = None, no_data: Optional[Union[int, float]] = None, ) -> None: if data_array.ndim == 2: data_array = data_array[np.newaxis, :] self.data_array = data_array self.crs = crs self.dst_crs = dst_crs self.transform = transform self.precision = precision if densification_factor is not None and densification_distance is not None: raise ValueError( "Only one of 'densification_factor' or 'densification_distance' " "can be specified." ) self.densification_factor = densification_factor self.densification_distance = densification_distance self.simplify_tolerance = simplify_tolerance self.no_data = no_data
[docs] def footprint(self) -> Optional[Dict[str, Any]]: """Produces the footprint surrounding data (not nodata) pixels in the source image. If the footprint is unable to be computed, None is returned. Returns: Optional[Dict[str, Any]]: A GeoJSON dictionary containing the footprint polygon. """ mask = self.data_mask() polygon = self.data_extent(mask) if polygon is None: return None polygon = self.densify_polygon(polygon) polygon = self.reproject_polygon(polygon) polygon = self.simplify_polygon(polygon) return mapping(polygon) # type: ignore
[docs] def data_mask(self) -> npt.NDArray[np.uint8]: """Produces a mask of valid data in the source image. Nodata pixels values are set to 0, data pixels are set to 1. Returns: numpy.NDArray[numpy.uint8]: A 2D array containing 0s and 1s for nodata/data pixels. """ assert self.data_array.ndim == 3 shape = self.data_array.shape if self.no_data is not None: mask: npt.NDArray[np.uint8] = np.full(shape, 0, dtype=np.uint8) if np.isnan(self.no_data): mask[~np.isnan(self.data_array)] = 1 else: mask[self.data_array != self.no_data] = 1 mask = np.sum(mask, axis=0, dtype=np.uint8) mask[mask > 0] = 1 else: mask = np.full(shape, 1, dtype=np.uint8) return mask
[docs] def data_extent(self, mask: npt.NDArray[np.uint8]) -> Optional[Polygon]: """Produces the data footprint in the native CRS. Args: mask (numpy.NDArray[numpy.uint8]): A 2D array containing 0s and 1s for nodata/data pixels. Returns: Optional[Polygon]: A native CRS polygon of the convex hull of data pixels. """ data_polygons = [ shape(polygon_dict) for polygon_dict, region_value in rasterio.features.shapes( mask, transform=self.transform ) if region_value == 1 ] if not data_polygons: return None elif len(data_polygons) == 1: polygon = data_polygons[0] else: polygon = MultiPolygon(data_polygons).convex_hull return orient(polygon)
[docs] def densify_polygon(self, polygon: Polygon) -> Polygon: """Adds vertices to the footprint polygon in the native CRS using either ``self.densification_factor`` or ``self.densification_distance``. Args: polygon (Polygon): Footprint polygon in the native CRS. Returns: Polygon: Densified footprint polygon in the native CRS. """ assert not (self.densification_factor and self.densification_distance) if self.densification_factor is not None: return Polygon( densify_by_factor(polygon.exterior.coords, self.densification_factor) ) elif self.densification_distance is not None: return Polygon( densify_by_distance( polygon.exterior.coords, self.densification_distance ) ) else: return polygon
[docs] def reproject_polygon(self, polygon: Polygon) -> Polygon: """Projects a polygon and rounds the projected vertex coordinates to ``self.precision``. Duplicate points caused by rounding are removed. Args: polygon (Polygon): Footprint polygon in the native CRS. Returns: Polygon: Footprint polygon in 'dst_crs'. """ return reproject_shape( src_crs=self.crs, dst_crs=self.dst_crs, geom=polygon, precision=self.precision, )
[docs] def simplify_polygon(self, polygon: Polygon) -> Polygon: """Reduces the number of polygon vertices such that the simplified polygon shape is no further away than the original polygon vertices than ``self.simplify_tolerance``. Args: polygon (Polygon): Polygon to be simplified. Returns: Polygon: Reduced vertex polygon. """ if self.simplify_tolerance is not None: return orient( polygon.simplify( tolerance=self.simplify_tolerance, preserve_topology=False ) ) return polygon
[docs] @classmethod def from_href( cls: Type[T], href: str, *, dst_crs: CRS = "EPSG:4326", precision: int = DEFAULT_PRECISION, densification_factor: Optional[int] = None, densification_distance: Optional[float] = None, simplify_tolerance: Optional[float] = None, no_data: Optional[Union[int, float]] = None, bands: List[int] = [1], ) -> T: """Produces a :class:`RasterFootprint` instance from an image href. The href can point to any file that is openable by rasterio. Args: href (str): The href of the image to process. dst_crs (CRS): Coordinate reference system of the footprint data. Defaults to EPSG 4236. precision (int): The number of decimal places to include in the final footprint coordinates. densification_factor (Optional[int]): The factor by which to increase point density within the footprint polygon before projection. A factor of 2 would double the density of points (placing one new point between each existing pair of points), a factor of 3 would place two points between each point, etc. Higher densities produce higher fidelity footprints in areas of high projection distortion. Mutually exclusive with ``densification_distance``. densification_distance (Optional[float]): The distance by which to increase point density within the footprint polygon before projection. If the distance is set to 2 and the segment length between two polygon vertices is 10, 4 new vertices would be created along the segment. Higher densities produce higher fidelity footprints in areas of high projection distortion. Mutually exclusive with ``densification_factor``. simplify_tolerance (Optional[float]): Distance, in degrees, within which all locations on the simplified polygon will be to the original polygon. no_data (Optional[Union[int, float]]): Explicitly sets the nodata value if not in source image metadata. If set to None, this will return a footprint including nodata values. bands (List[int]): The bands to use to compute the footprint. Defaults to [1]. If an empty list is provided, the bands will be ORd together; e.g., for a pixel to be outside of the footprint, all bands must have nodata in that pixel. Returns: RasterFootprint: A :class:`RasterFootprint` instance. """ with rasterio.open(href) as source: return cls.from_rasterio_dataset_reader( reader=source, dst_crs=dst_crs, no_data=no_data, bands=bands, precision=precision, densification_factor=densification_factor, densification_distance=densification_distance, simplify_tolerance=simplify_tolerance, )
[docs] @classmethod def from_rasterio_dataset_reader( cls: Type[T], reader: DatasetReader, *, dst_crs: CRS = "EPSG:4326", precision: int = DEFAULT_PRECISION, densification_factor: Optional[int] = None, densification_distance: Optional[float] = None, simplify_tolerance: Optional[float] = None, no_data: Optional[Union[int, float]] = None, bands: List[int] = [1], ) -> T: """Produces a :class:`RasterFootprint` instance from a :class:`rasterio.io.DatasetReader` object, i.e., an opened dataset object returned by a :func:`rasterio.open` call. Args: reader (DatasetReader): A rasterio dataset reader object for the image to process. dst_crs (CRS): Coordinate reference system of the footprint data. Defaults to EPSG 4236. precision (int): The number of decimal places to include in the final footprint coordinates. densification_factor (Optional[int]): The factor by which to increase point density within the footprint polygon before projection. A factor of 2 would double the density of points (placing one new point between each existing pair of points), a factor of 3 would place two points between each point, etc. Higher densities produce higher fidelity footprints in areas of high projection distortion. Mutually exclusive with ``densification_distance``. densification_distance (Optional[float]): The distance by which to increase point density within the footprint polygon before projection. If the distance is set to 2 and the segment length between two polygon vertices is 10, 4 new vertices would be created along the segment. Higher densities produce higher fidelity footprints in areas of high projection distortion. Mutually exclusive with ``densification_factor``. simplify_tolerance (Optional[float]): Distance, in degrees, within which all locations on the simplified polygon will be to the original polygon. no_data (Optional[Union[int, float]]): Explicitly sets the nodata value if not in source image metadata. If set to None, this will return a footprint including nodata values. bands (List[int]): The bands to use to compute the footprint. Defaults to [1]. If an empty list is provided, the bands will be ORd together; e.g., for a pixel to be outside of the footprint, all bands must have nodata in that pixel. Returns: RasterFootprint: A :class:`RasterFootprint` instance. """ if not reader.indexes: raise ValueError( "Raster footprint cannot be computed for an asset with no bands." ) if len(set(reader.nodatavals)) != 1: raise ValueError("All raster bands must have the same 'nodata' value.") if not bands: bands = reader.indexes if no_data is None: no_data = reader.nodata band_data = [] for index in bands: band_data.append(reader.read(index)) return cls( data_array=np.asarray(band_data), crs=reader.crs, dst_crs=dst_crs, transform=reader.transform, no_data=no_data, precision=precision, densification_factor=densification_factor, densification_distance=densification_distance, simplify_tolerance=simplify_tolerance, )
[docs] @classmethod def update_geometry_from_asset_footprint( cls, item: Item, *, asset_names: List[str] = [], dst_crs: CRS = "EPSG:4326", precision: int = DEFAULT_PRECISION, densification_factor: Optional[int] = None, densification_distance: Optional[float] = None, simplify_tolerance: Optional[float] = None, no_data: Optional[Union[int, float]] = None, bands: List[int] = [1], skip_errors: bool = True, footprint_merge_strategy: FootprintMergeStrategy = FootprintMergeStrategy.FIRST, ) -> bool: """Accepts an Item and an optional list of asset names within that Item, and updates the geometry of that Item in-place with the data footprint derived from the first of the assets that exists in the Item. The Item bbox is also updated in-place to bound the new footprint extents. See :class:`RasterFootprint` for details on the data footprint calculation. Args: item (Item): The PySTAC Item to update. asset_names (List[str]): The names of the assets for which to attempt to extract footprints. The first successful footprint will be used. If the list is empty, all assets will be tried until one is successful. dst_crs (CRS): Coordinate reference system of the footprint data. Defaults to EPSG 4236. precision (int): The number of decimal places to include in the final footprint coordinates. densification_factor (Optional[int]): The factor by which to increase point density within the footprint polygon before projection. A factor of 2 would double the density of points (placing one new point between each existing pair of points), a factor of 3 would place two points between each point, etc. Higher densities produce higher fidelity footprints in areas of high projection distortion. Mutually exclusive with ``densification_distance``. densification_distance (Optional[float]): The distance by which to increase point density within the footprint polygon before projection. If the distance is set to 2 and the segment length between two polygon vertices is 10, 4 new vertices would be created along the segment. Higher densities produce higher fidelity footprints in areas of high projection distortion. Mutually exclusive with ``densification_factor``. simplify_tolerance (Optional[float]): Distance, in degrees, within which all locations on the simplified polygon will be to the original polygon. no_data (Optional[Union[int, float]]): Explicitly sets the nodata value if not in source image metadata. If set to None, this will return a footprint including nodata values. bands (List[int]): The bands to use to compute the footprint. Defaults to [1]. If an empty list is provided, the bands will be ORd together; e.g., for a pixel to be outside of the footprint, all bands must have nodata in that pixel. skip_errors (bool): If False, raise an error for a missing href or footprint calculation failure. footprint_aggregator (FootprintMergeStrategy): Provides a means to control how the footprints of assets are aggregated; see :class:`FootprintMergeStrategy` for details; defaults to using the `FIRST` strategy Returns: bool: True if the Item geometry was successfully updated, False if not. """ asset_extent_iterator = cls.data_footprints_for_data_assets( item, asset_names=asset_names, dst_crs=dst_crs, precision=precision, densification_factor=densification_factor, densification_distance=densification_distance, simplify_tolerance=simplify_tolerance, no_data=no_data, bands=bands, skip_errors=skip_errors, ) if footprint_merge_strategy == FootprintMergeStrategy.FIRST: asset_name_extent = next(asset_extent_iterator, None) if asset_name_extent is None: return False _, extent = asset_name_extent else: extents = [shape(extent) for _, extent in asset_extent_iterator] if extents == []: return False if footprint_merge_strategy == FootprintMergeStrategy.INTERSECTION: extent = mutual_intersection(extents) elif footprint_merge_strategy == FootprintMergeStrategy.UNION: extent = unary_union(extents) else: raise Exception( f"Unrecognized aggregation strategy: {footprint_merge_strategy}" ) item.geometry = extent item.bbox = list(shape(extent).bounds) return True
[docs] @classmethod def data_footprints_for_data_assets( cls, item: Item, *, asset_names: List[str] = [], dst_crs: CRS = "EPSG:4326", precision: int = DEFAULT_PRECISION, densification_factor: Optional[int] = None, densification_distance: Optional[float] = None, simplify_tolerance: Optional[float] = None, no_data: Optional[Union[int, float]] = None, bands: List[int] = [1], skip_errors: bool = True, ) -> Iterator[Tuple[str, Dict[str, Any]]]: """Accepts an Item and an optional list of asset names within that Item, and produces an iterator over the same asset names (if they exist) and dictionaries representing GeoJSON Polygons of the data footprints of the assets. See :class:`RasterFootprint` for details on the data footprint calculation. Args: item (Item): The PySTAC Item to update. asset_names (List[str]): The names of the assets for which to attempt to extract footprints. The first successful footprint will be used. If the list is empty, all assets will be tried until one is successful. dst_crs (CRS): Coordinate reference system of the footprint data. Defaults to EPSG 4236. precision (int): The number of decimal places to include in the final footprint coordinates. densification_factor (Optional[int]): The factor by which to increase point density within the footprint polygon before projection. A factor of 2 would double the density of points (placing one new point between each existing pair of points), a factor of 3 would place two points between each point, etc. Higher densities produce higher fidelity footprints in areas of high projection distortion. Mutually exclusive with ``densification_distance``. densification_distance (Optional[float]): The distance by which to increase point density within the footprint polygon before projection. If the distance is set to 2 and the segment length between two polygon vertices is 10, 4 new vertices would be created along the segment. Higher densities produce higher fidelity footprints in areas of high projection distortion. Mutually exclusive with ``densification_factor``. simplify_tolerance (Optional[float]): Distance, in degrees, within which all locations on the simplified polygon will be to the original polygon. no_data (Optional[Union[int, float]]): Explicitly sets the nodata value if not in source image metadata. If set to None, this will return a footprint including nodata values. bands (List[int]): The bands to use to compute the footprint. Defaults to [1]. If an empty list is provided, the bands will be ORd together; e.g., for a pixel to be outside of the footprint, all bands must have nodata in that pixel. skip_errors (bool): If False, raise an error for a missing href or footprint calculation failure. Returns: Iterator[Tuple[str, Dict[str, Any]]]: Iterator of the asset name and dictionary representing a GeoJSON Polygon of the data footprint for each asset. """ def handle_error(message: str) -> None: if skip_errors: logger.error(message) else: raise Exception(message) for name, asset in item.assets.items(): if not asset_names or name in asset_names: href = asset.get_absolute_href() if href is None: handle_error( f"Could not determine extent for asset '{name}', missing href" ) else: extent = cls.from_href( href=href, dst_crs=dst_crs, no_data=no_data, bands=bands, precision=precision, densification_factor=densification_factor, densification_distance=densification_distance, simplify_tolerance=simplify_tolerance, ).footprint() if extent: yield name, extent else: handle_error(f"Could not determine extent for asset '{name}'")
[docs] def update_geometry_from_asset_footprint( item: Item, *, asset_names: List[str] = [], precision: int = DEFAULT_PRECISION, densification_factor: Optional[int] = None, simplify_tolerance: Optional[float] = None, no_data: Optional[Union[int, float]] = None, bands: List[int] = [1], skip_errors: bool = True, ) -> bool: """DEPRECATED. .. deprecated:: 0.4.4 Use :meth:`RasterFootprint.update_geometry_from_asset_footprint` instead. Accepts an Item and an optional list of asset names within that Item, and updates the geometry of that Item in-place with the data footprint derived from the first of the assets that exists in the Item. The Item bbox is also updated in-place to bound the new footprint extents. See :class:`RasterFootprint` for details on the data footprint calculation. Args: item (Item): The PySTAC Item to update. asset_names (List[str]): The names of the assets for which to attempt to extract footprints. The first successful footprint will be used. If the list is empty, all assets will be tried until one is successful. precision (int): The number of decimal places to include in the final footprint coordinates. densification_factor (Optional[int]): The factor by which to increase point density within the polygon before projection to EPSG 4326. A factor of 2 would double the density of points (placing one new point between each existing pair of points), a factor of 3 would place two points between each point, etc. Higher densities produce higher fidelity footprints in areas of high projection distortion. simplify_tolerance (Optional[float]): Distance, in degrees, within which all locations on the simplified polygon will be to the original polygon. no_data (Optional[Union[int, float]]): Explicitly sets the nodata value if not in source image metadata. If set to None, this will return a footprint including nodata values. bands (List[int]): The bands to use to compute the footprint. Defaults to [1]. If an empty list is provided, the bands will be ORd together; e.g., for a pixel to be outside of the footprint, all bands must have nodata in that pixel. skip_errors (bool): If False, raise an error for a missing href or footprint calculation failure. Returns: bool: True if the Item geometry was successfully updated, False if not. """ warnings.warn( "update_geometry_from_asset_footprint() is deprecated, use " "RasterFootprint.update_geometry_from_asset_footprint() instead", DeprecationWarning, ) return RasterFootprint.update_geometry_from_asset_footprint( item, asset_names=asset_names, precision=precision, densification_factor=densification_factor, simplify_tolerance=simplify_tolerance, no_data=no_data, bands=bands, skip_errors=skip_errors, )
[docs] def data_footprints_for_data_assets( item: Item, *, asset_names: List[str] = [], precision: int = DEFAULT_PRECISION, densification_factor: Optional[int] = None, simplify_tolerance: Optional[float] = None, no_data: Optional[Union[int, float]] = None, bands: List[int] = [1], skip_errors: bool = True, ) -> Iterator[Tuple[str, Dict[str, Any]]]: """DEPRECATED. .. deprecated:: 0.4.4 Use :meth:`RasterFootprint.data_footprints_for_data_assets` instead. Accepts an Item and an optional list of asset names within that Item, and produces an iterator over the same asset names (if they exist) and dictionaries representing GeoJSON Polygons of the data footprints of the assets. See :class:`RasterFootprint` for details on the data footprint calculation. Args: item (Item): The PySTAC Item to update. asset_names (List[str]): The names of the assets for which to attempt to extract footprints. The first successful footprint will be used. If the list is empty, all assets will be tried until one is successful. precision (int): The number of decimal places to include in the final footprint coordinates. densification_factor (Optional[int]): The factor by which to increase point density within the polygon before projection to EPSG 4326. A factor of 2 would double the density of points (placing one new point between each existing pair of points), a factor of 3 would place two points between each point, etc. Higher densities produce higher fidelity footprints in areas of high projection distortion. simplify_tolerance (Optional[float]): Distance, in degrees, within which all locations on the simplified polygon will be to the original polygon. no_data (Optional[Union[int, float]]): Explicitly sets the nodata value if not in source image metadata. If set to None, this will return a footprint including nodata values. bands (List[int]): The bands to use to compute the footprint. Defaults to [1]. If an empty list is provided, the bands will be ORd together; e.g., for a pixel to be outside of the footprint, all bands must have nodata in that pixel. skip_errors (bool): If False, raise an error for a missing href or footprint calculation failure. Returns: Iterator[Tuple[str, Dict[str, Any]]]: Iterator of the asset name and dictionary representing a GeoJSON Polygon of the data footprint for each asset. """ warnings.warn( "data_footprints_for_data_assets() is deprecated, use " "RasterFootprint.data_footprints_for_data_assets() instead", DeprecationWarning, ) return RasterFootprint.data_footprints_for_data_assets( item, asset_names=asset_names, precision=precision, densification_factor=densification_factor, simplify_tolerance=simplify_tolerance, no_data=no_data, bands=bands, skip_errors=skip_errors, )
[docs] def data_footprint( href: str, *, precision: int = DEFAULT_PRECISION, densification_factor: Optional[int] = None, simplify_tolerance: Optional[float] = None, no_data: Optional[Union[int, float]] = None, bands: List[int] = [1], ) -> Optional[Dict[str, Any]]: """DEPRECATED. .. deprecated:: 0.4.4 Call :meth:`~RasterFootprint.footprint` on a :class:`RasterFootprint` instance created with :meth:`RasterFootprint.from_href` instead. Produces a data footprint from the href of a raster file. See :class:`RasterFootprint` for details on the data footprint calculation. Args: href (str): The href of the image to process. precision (int): The number of decimal places to include in the final footprint coordinates. densification_factor (Optional[int]): The factor by which to increase point density within the polygon before projection to EPSG 4326. A factor of 2 would double the density of points (placing one new point between each existing pair of points), a factor of 3 would place two points between each point, etc. Higher densities produce higher fidelity footprints in areas of high projection distortion. simplify_tolerance (Optional[float]): Distance, in degrees, within which all locations on the simplified polygon will be to the original polygon. no_data (Optional[Union[int, float]]): Explicitly sets the nodata value if not in source image metadata. If set to None, this will return a footprint including nodata values. bands (List[int]): The bands to use to compute the footprint. Defaults to [1]. If an empty list is provided, the bands will be ORd together; e.g. for a pixel to be outside of the footprint, all bands must have nodata in that pixel. Returns: Optional[Dict[str, Any]]: A dictionary representing a GeoJSON Polygon of the data footprint of the raster data retrieved from the given ``href``. """ warnings.warn( "data_footprint() is deprecated, use RasterFootprint.data_footprint() " "on a RasterFootprint instance created with " "RasterFootprint.from_href() instead", DeprecationWarning, ) return RasterFootprint.from_href( href=href, no_data=no_data, bands=bands, precision=precision, densification_factor=densification_factor, simplify_tolerance=simplify_tolerance, ).footprint()
[docs] def densify_reproject_simplify( polygon: Polygon, crs: CRS, *, dst_crs: CRS = "EPSG:4326", densification_factor: Optional[int] = None, precision: int = DEFAULT_PRECISION, simplify_tolerance: Optional[float] = None, ) -> Polygon: """Densifies the input polygon, reprojects it, and simplifies the resulting polygon. See :class:`RasterFootprint` for details on densification and simplification. Args: polygon (Polygon): The input polygon. crs (CRS): The CRS of the input polygon. dst_crs (CRS): The CRS of the output polygon. Defaults to EPSG 4326 densification_factor (Optional[int]): The factor by which to increase point density within the polygon before projection. A factor of 2 would double the density of points (placing one new point between each existing pair of points), a factor of 3 would place two points between each point, etc. Higher densities produce higher fidelity footprints in areas of high projection distortion. precision (int): The number of decimal places to include in the final polygon vertex coordinates. simplify_tolerance (Optional[float]): Distance, in degrees, within which all locations on the simplified polygon will be to the original polygon. Returns: Polygon: The reprojected Polygon. """ if densification_factor is not None: polygon = Polygon( densify_by_factor(polygon.exterior.coords, factor=densification_factor) ) polygon = reproject_shape( geom=polygon, src_crs=crs, dst_crs=dst_crs, precision=precision ) if simplify_tolerance is not None: polygon = polygon.simplify( tolerance=simplify_tolerance, preserve_topology=False ) return polygon