curryer.compute.elevation ========================= .. py:module:: curryer.compute.elevation .. autoapi-nested-parse:: Elevation computations and DEM management. .. rubric:: References GMTED2010 https://pubs.usgs.gov/of/2011/1073/pdf/of2011-1073.pdf UNAVCO Geoid Height Calculator https://www.unavco.org/software/geodetic-utilities/geoid-height-calculator/geoid-height-calculator.html @author: Brandon Stone Attributes ---------- .. autoapisummary:: curryer.compute.elevation.logger curryer.compute.elevation.VDATUMS curryer.compute.elevation.VDATUM_TO_EPSG Classes ------- .. autoapisummary:: curryer.compute.elevation.Elevation curryer.compute.elevation.ElevationRegion Module Contents --------------- .. py:data:: logger .. py:data:: VDATUMS .. py:data:: VDATUM_TO_EPSG .. py:class:: Elevation(data_dir: str | pathlib.Path = None, meters=False, degrees=True) High-level class for accessing elevation data from individual files. .. py:attribute:: LON_STEP :value: 30 .. py:attribute:: LAT_STEP :value: 20 .. py:attribute:: LON_MID :value: 0 .. py:attribute:: LAT_MID :value: 10 .. py:attribute:: data_dir :value: None .. py:attribute:: meters :value: False .. py:attribute:: degrees :value: True .. py:attribute:: _files :value: None .. py:attribute:: _metadata :value: None .. py:attribute:: _cache .. py:attribute:: egm96_to_wgs84 .. py:method:: check_for_geoid_data() :staticmethod: Ensure that third-party geoid data is available. .. py:method:: latlon_to_pixel(lon: float, lat: float, transform: affine.Affine) -> (int, int) Convert lon/lat to raster row/col indices using the given Affine transform. :param lon: Longitude coordinate in degrees. :type lon: float :param lat: Latitude coordinate in degrees. :type lat: float :param transform: Affine transformation object for coordinate conversion. :type transform: affine.Affine :returns: Row and column indices (row, col) in the raster. :rtype: tuple of int .. rubric:: Notes This assumes pixel-center coordinates, so we don't need to add 0.5 offset since our coordinates already represent pixel centers. .. py:method:: _get_affine_from_tiff(tif: tifffile.TiffFile) -> affine.Affine :staticmethod: Extract affine transformation from GeoTIFF using tifffile. Uses standard GeoTIFF tags ModelPixelScaleTag (33550) and ModelTiepointTag (33922) as defined in GeoTIFF Specification v1.0. Assumes non-rotated, north-up images. For rotated images using ModelTransformationTag (34264), this method will need enhancement. :param tif: An open TiffFile object. :type tif: tifffile.TiffFile :returns: The affine transformation matrix. :rtype: Affine :raises ValueError: If required GeoTIFF tags are missing. .. rubric:: Notes Standard GeoTIFF structure (from GeoTIFF Specification Section 2.6.1): - ModelPixelScale = (ScaleX, ScaleY, ScaleZ) in map units per pixel - ModelTiepoint = (I, J, K, X, Y, Z) where pixel (I,J) maps to location (X,Y) - Y scale is negative for north-up images (GDAL convention) .. py:method:: _get_crs_from_tiff(tif: tifffile.TiffFile) -> int :staticmethod: Extract EPSG code from GeoTIFF using tifffile. Uses standard GeoTIFF GeoKeys as defined in GeoTIFF Specification v1.0: - ProjectedCSTypeGeoKey (3072): EPSG code for projected coordinate systems - GeographicTypeGeoKey (2048): EPSG code for geographic coordinate systems :param tif: An open TiffFile object. :type tif: tifffile.TiffFile :returns: The EPSG code for the coordinate reference system. :rtype: int :raises ValueError: If no valid EPSG code is found in the GeoTIFF metadata. .. rubric:: Notes tifffile 2025.x parses the GeoKeyDirectoryTag (34735) and converts numeric GeoKey IDs to descriptive names for readability. Values may be Enum objects (e.g., ) requiring .value extraction. These GeoKey names are from the official GeoTIFF specification, not specific to any test data. They will work with any compliant GeoTIFF file. .. rubric:: References GeoTIFF Specification v1.0, Section 6.3.3.1 (Projected CS Keys) GeoTIFF Specification v1.0, Section 6.3.2.1 (Geographic CS Keys) .. py:method:: locate_files(pattern: str = '*_gmted_*.tif') -> list[pathlib.Path] Locate DEM files on the filesystem (cached). :param pattern: File name pattern to filter DEM files with. :type pattern: str, optional :returns: List of found DEM files. Cached after first execution. :rtype: list[Path] .. py:method:: describe_files() -> pandas.DataFrame Compute the metadata for the available DEM files (cached). :returns: Metadata for the individual DEM files. Cached after first execution. :rtype: pd.DataFrame .. py:method:: lookup_file(lon: float, lat: float, arcsec: float = None, stat: str = 'mea', wrap_lon=False, degrees: bool | None = None) -> pandas.Series | None Find the DEM file for a given lon/lat point. :param lon: Longitude in degrees or radians, see `degrees`. :type lon: float :param lat: Latitude in degrees or radians, see `degrees`. :type lat: float :param arcsec: Select the DEM resolution. Default is the highest available. :type arcsec: float, optional :param stat: Select the type of DEM file. Default is "mea" (mean). :type stat: str, optional :param wrap_lon: Allow longitude values less than -180 and greater than 180, wrapping them around the globe (e.g., 190 = -170). Default is False. :type wrap_lon: bool, optional :param degrees: Assume in/out lon/lat are in degrees. If not specified or None (default), uses the instance value of `degrees` (default is True). :type degrees: None or bool, optional :returns: Matching DEM metadata entry or None if no match was found. :rtype: pd.Series or None .. py:method:: load_file(filepath: pathlib.Path) -> xarray.DataArray Load data from a DEM file (cached). :param filepath: Path to the DEM file to load. :type filepath: Path :returns: Loaded elevation data. Cached for each unique file path. :rtype: xr.DataArray .. py:method:: get_geoid_height(lon: numpy.ndarray | float, lat: numpy.ndarray | float, degrees: bool | None = None) -> numpy.ndarray | float Query the geoid height for a given lon/lat(s). :param lon: Longitude in degrees or radians, see `degrees`. :type lon: np.ndarray or float :param lat: Latitude in degrees or radians, see `degrees`. :type lat: np.ndarray or float :param degrees: Assume in/out lon/lat are in degrees. If not specified or None (default), uses the instance value of `degrees` (default is True). :type degrees: None or bool, optional :returns: Geoid height above the ellipsoid. Default is kilometers unless init variable `meters` was True. :rtype: np.ndarray or float .. py:method:: get_dem_height(lon: float, lat: float, filepath: pathlib.Path, degrees: bool | None = None) -> float Query the DEM height above the geoid for a given lon/lat. :param lon: Longitude in degrees or radians, see `degrees`. :type lon: float :param lat: Latitude in degrees or radians, see `degrees`. :type lat: float :param filepath: DEM file to query data from. :type filepath: Path :param degrees: Assume in/out lon/lat are in degrees. If not specified or None (default), uses the instance value of `degrees` (default is True). :type degrees: None or bool, optional :returns: DEM height above the geoid. Default is kilometers unless init variable `meters` was True. :rtype: float .. py:method:: query(lon, lat, orthometric=False) Query the surface (DEM + geoid) height above the ellipsoid a given lon/lat. :param lon: Longitude in degrees or radians, see init `degrees`. :type lon: float :param lat: Latitude in degrees or radians, see init `degrees`. :type lat: float :param orthometric: Exclude the geoid height as part of the surface height. Default is False. :type orthometric: bool, optional :returns: Surface (DEM and geoid (unless orthometric=True) height above the ellipsoid. Default is kilometers unless init variable `meters` was True. :rtype: float .. py:method:: _pad_lonlat(lon: float, lat: float, pad: float) -> tuple[float, float, float, float] Intelligently pad lon/lat values with a meter/kilometer distance. .. py:method:: _regional_files(min_lon: float, max_lon: float, min_lat: float, max_lat: float, allow_empty=False) -> set[pathlib.Path] Find all DEM files within a min/max lon/lat region. .. py:method:: _slice_file(rds: xarray.DataArray, min_lon: float, max_lon: float, min_lat: float, max_lat: float, orthometric: bool = False, fill_val: float = None) -> xarray.DataArray Subset a DEM file by a min/max lon/lat region. :param rds: DEM data array with 'x' (longitude) and 'y' (latitude) coordinates. :type rds: xr.DataArray :param min_lon: Longitude bounds in degrees (\[-180, 180\]). :type min_lon: float :param max_lon: Longitude bounds in degrees (\[-180, 180\]). :type max_lon: float :param min_lat: Latitude bounds in degrees (\[-90, 90\]). :type min_lat: float :param max_lat: Latitude bounds in degrees (\[-90, 90\]). :type max_lat: float :param orthometric: If True, return orthometric heights (DEM only). If False, return ellipsoid heights (DEM + geoid). :type orthometric: bool, optional :param fill_val: Fill value to check for consistency. :type fill_val: float, optional :returns: Subsetted DEM or ellipsoid heights for the region. :rtype: xr.DataArray .. py:method:: local_minmax(lon, lat, pad, orthometric=False) -> tuple[float, float] Compute the elevation min/max around a buffered point. :param lon: Longitude in degrees or radians, see init `degrees`. :type lon: float :param lat: Latitude in degrees or radians, see init `degrees`. :type lat: float :param pad: Padding to add to the point, in meters or kilometers, see init `meters`. :type pad: float :param orthometric: Exclude the geoid heights from the result. :type orthometric: bool, optional :returns: Min and max elevation around the specified padding point. :rtype: (float, float) .. py:method:: local_region(min_lon: float, max_lon: float, min_lat: float, max_lat: float, orthometric=False) Create a regional subset, supporting SIGNIFICANTLY faster queries. :param min_lon: Minimum longitude extent of the region, see init `degrees`. :type min_lon: float :param max_lon: Maximum longitude extent of the region, see init `degrees`. :type max_lon: float :param min_lat: Minimum latitude extent of the region, see init `degrees`. :type min_lat: float :param max_lat: Maximum latitude extent of the region, see init `degrees`. :type max_lat: float :param orthometric: Exclude the geoid heights from the result. :type orthometric: bool, optional :returns: Elevation subset that only supports queries within its region, but SIGNIFICANTLY faster within the region! :rtype: ElevationRegion .. py:class:: ElevationRegion(data: numpy.ndarray, ulx: float, uly: float, dx: float, dy: float, orthometric=None, meters: bool | None = None, degrees: bool | None = None) High-level class for accessing elevation data from an optimized pre-cached dataset. .. py:attribute:: data .. py:attribute:: ulx .. py:attribute:: uly .. py:attribute:: dx .. py:attribute:: dy .. py:attribute:: orthometric :value: None .. py:attribute:: meters :value: None .. py:attribute:: degrees :value: None .. py:attribute:: lrx .. py:attribute:: lry .. py:attribute:: _wrap :value: 360 .. py:method:: __repr__() .. py:method:: from_xarray(hts: xarray.DataArray, **kwargs) :classmethod: Create a region from an xarray data array. :param hts: Elevation data with lon/lat coordinates (x/y). :type hts: xr.DataArray :param kwargs: See init arguments. :type kwargs: dict :returns: Elevation region from an xarray data array. :rtype: ElevationRegion .. py:method:: query(lon: numpy.ndarray | float, lat: numpy.ndarray | float, orthometric=None) -> numpy.ndarray | float Query the surface (DEM + geoid) height above the ellipsoid a given lon/lat. :param lon: Longitude in degrees or radians, see init `degrees`. :type lon: np.ndarray or float :param lat: Latitude in degrees or radians, see init `degrees`. :type lat: np.ndarray or float :param orthometric: Included for backwards compatibility with the slower Elevation implementation. Ignored if not specified, otherwise it must match the value used to create this region. :type orthometric: bool, optional :returns: Surface (DEM and geoid (unless orthometric=True)) height above the ellipsoid. Default is kilometers unless init variable `meters` was True. :rtype: np.ndarray or float .. py:method:: local_minmax(lon=None, lat=None, pad=None) -> tuple[float, float] Compute local min/max elevation. Simply assumes the region is the same as the locality, returning its min/max. :param lon: Ignored. :param lat: Ignored. :param pad: Ignored. :returns: Min and max elevation for the region. :rtype: float, float