Elevation Models

Geolocating a pixel in an overhead image requires knowing the terrain height at that point. Without elevation data, the sensor model must assume a flat surface — and the resulting horizontal error grows with both the off-nadir angle and the true terrain relief.

Ellipsoidal vs. Orthometric Height

Sensor models in this library work in ellipsoidal heights — meters above the WGS84 reference ellipsoid. Most DEM sources (including SRTM) store orthometric heights — meters above mean sea level (the geoid). The difference between these datums (the geoid undulation) varies from about -106 m to +85 m globally.

Relationship between ellipsoidal height, orthometric height, and geoid undulation

Always apply a geoid correction when using orthometric DEM data. Failing to do so introduces the full geoid undulation as an elevation error — which, at high off-nadir angles, can produce horizontal errors exceeding 50 meters.

Quick Start

The simplest useful configuration: SRTM terrain tiles, EGM96 geoid correction, and a sea-level fallback for locations without DEM coverage.

from aws.osml.io import IO
from aws.osml.metadata import load_sensor_model
from aws.osml.elevation import ElevationModelBuilder, StoredDEMTileFactory
from aws.osml.photogrammetry import ImageCoordinate, SRTMTileSet

# Build the elevation model
elevation_model = (
    ElevationModelBuilder()
    .add_source(
        StoredDEMTileFactory("/path/to/srtm_tiles"),
        SRTMTileSet(),
    )
    .add_fallback(0.0)
    .with_geoid("/path/to/egm96_15.tif")
    .build()
)

# Use with a sensor model for accurate geolocation
with IO.open("image.ntf", "r") as reader:
    sensor_model = load_sensor_model(reader)
    image = reader.get_asset("image:0")
    width, height = image.num_columns, image.num_rows

world = sensor_model.image_to_world(
    ImageCoordinate([width / 2, height / 2]),
    elevation_model=elevation_model,
)

The ElevationModel Interface

All elevation models implement two methods:

  • set_elevation(world_coordinate) — updates the elevation component of a GeodeticWorldCoordinate in place. Returns True if the elevation was set, False if the model has no data at that location.

  • describe_region(world_coordinate) — returns an ElevationRegionSummary (min/max elevation, no-data value, post spacing) for the area near the coordinate, or None.

Composing Models Directly

The builder is convenient but not required. You can compose the elevation model classes directly for full control. The toolkit provides several implementations that compose together:

Class

Description

DigitalElevationModel

Reads DEM raster tiles and interpolates elevation values. Requires a DigitalElevationModelTileSet (maps coordinates to file paths) and a DigitalElevationModelTileFactory (loads raster data). The raster_cache_size parameter controls how many decoded tiles are kept in an LRU cache.

ConstantElevationModel

Returns a fixed elevation for all coordinates. Useful as a lowest-priority fallback or for testing.

MultiElevationModel

Calls set_elevation on each model in order and returns the first successful result. Models that return False are skipped, letting the next source try.

ConditionalElevationModel

Gates an inner model behind an ElevationModelCondition. If the condition returns False, set_elevation returns False and the coordinate is unchanged — allowing a MultiElevationModel to fall through to the next source.

OffsetElevationModel

Wraps an inner model and adds a datum offset after elevation is set. Use with RasterOffsetProvider (interpolates from a geoid undulation raster) or ConstantOffsetProvider (fixed offset everywhere).

NormalizedElevationModel

Normalizes longitude/latitude before delegating to an inner model.

Conditions

GeometryCondition connects an ElevationModelCondition to a GeometryQuery — it evaluates True when the query point falls inside a geometry returned by the query. ShapefileQuery is the built-in GeometryQuery implementation. Pass invert=True to match points outside the geometry instead. For non-spatial conditions, subclass ElevationModelCondition and implement is_true().

Example: Multi-Source with Shoreline Masking

High-res local DEM where coverage exists, global SRTM on land elsewhere, zero over water, with geoid correction applied globally:

from aws.osml.photogrammetry import (
    ConditionalElevationModel,
    ConstantElevationModel,
    DigitalElevationModel,
    MultiElevationModel,
    OffsetElevationModel,
    SRTMTileSet,
    GenericDEMTileSet,
)
from aws.osml.elevation import (
    GeometryCondition,
    RasterOffsetProvider,
    ShapefileQuery,
    StoredDEMTileFactory,
)

# Geometry condition: on land
land_condition = GeometryCondition(ShapefileQuery("/path/to/ne_10m_land.shp"))

# High-res local DEM (conditional on coverage area)
local_coverage = GeometryCondition(ShapefileQuery("/path/to/local_coverage.shp"))
local_dem = ConditionalElevationModel(
    inner_elevation_model=DigitalElevationModel(
        tile_set=GenericDEMTileSet(format_spec="%ld%lh_%od%oh.tif"),
        tile_factory=StoredDEMTileFactory("/path/to/local_dem"),
    ),
    em_condition=local_coverage,
)

# Global SRTM (conditional on land)
srtm_dem = ConditionalElevationModel(
    inner_elevation_model=DigitalElevationModel(
        tile_set=SRTMTileSet(),
        tile_factory=StoredDEMTileFactory("/path/to/srtm_tiles"),
    ),
    em_condition=land_condition,
)

# Compose: local → SRTM → sea level
terrain = MultiElevationModel([
    local_dem,
    srtm_dem,
    ConstantElevationModel(0.0),
])

# Apply geoid correction
elevation_model = OffsetElevationModel(
    inner_elevation_model=terrain,
    offset_provider=RasterOffsetProvider("/path/to/egm96_15.tif"),
)

Open-Source Elevation Data

Data

Source

Resolution

Format

SRTM

USGS Earth Explorer

1 or 3 arc-second

GeoTIFF

EGM96 geoid

NGA / ISG

15 arc-minute

GeoTIFF

EGM2008 geoid

NGA / ISG

5 or 1 arc-minute

GeoTIFF

Natural Earth land

naturalearthdata.com

1:10m

Shapefile