Skip to content

terrain_utils

terrain_utils

PyAERMOD terrain utilities: datums, mosaic, reprojection, diagnostics, async downloads.

Companion to terrain.py. These helpers handle the operational gaps users hit when building AERMAP inputs for real sites:

  • DatumTransformer: NAD27 <-> NAD83 <-> WGS84 coordinate conversions for legacy DEM data (requires pyproj).
  • SRTMDownloader: fallback DEM source (SRTM v3.0 1-arc-second) for regions outside CONUS where USGS NED isn't available.
  • mosaic_dem_tiles: merge multiple DEM tiles into a single GeoTIFF (requires rasterio).
  • reproject_dem: reproject a DEM to a target UTM zone or EPSG.
  • async_fetch_tiles: parallel tile downloads via ThreadPoolExecutor.
  • hill_height_diagnostics: scan AERMAP output for implausible receptor elevations / hill heights.

DatumTransformer

Wrap pyproj to convert between common U.S. datums.

Typical use: older USGS DEMs (especially 7.5-minute quad series) are published in NAD27 with coordinates in arc-seconds. AERMOD expects UTM coordinates in a current datum (NAD83 or WGS84).

transform

transform(x: float, y: float) -> Tuple[float, float]

Transform a single (x, y) / (lon, lat) pair.

SRTMTileInfo dataclass

Metadata for a single SRTM v3.0 tile.

HillHeightAnomaly dataclass

Flagged AERMAP receptor/source elevation record.

utm_zone_for_lon

utm_zone_for_lon(lon: float) -> int

Return the UTM zone number (1-60) containing a given longitude.

utm_epsg

utm_epsg(lon: float, lat: float) -> int

Return the EPSG code for the WGS84 UTM zone covering (lon, lat).

srtm_tile_name

srtm_tile_name(lat: float, lon: float) -> str

Compute the SRTM v3.0 1x1-degree tile name for a coordinate.

Example: (35.5, -105.2) -> 'N35W106'

srtm_tiles_for_bbox

srtm_tiles_for_bbox(bounds: Tuple[float, float, float, float]) -> List[SRTMTileInfo]

Enumerate SRTM tile names covering a (west, south, east, north) bbox.

The download_url is a canonical USGS EarthData path — most users need NASA EarthData Login to actually fetch. For an unauthenticated alternative, consider OpenTopography's API.

mosaic_dem_tiles

mosaic_dem_tiles(tile_paths: Iterable[Union[str, Path]], output_path: Union[str, Path]) -> Path

Merge multiple DEM tiles into a single GeoTIFF.

Writes the mosaic to output_path and returns the path.

reproject_dem

reproject_dem(src_path: Union[str, Path], dst_path: Union[str, Path], dst_epsg: int, resampling: str = 'bilinear') -> Path

Reproject a DEM to a target EPSG (typically a UTM zone).

async_fetch_tiles

async_fetch_tiles(urls: Sequence[str], fetch_fn: Callable[[str], Any], max_workers: int = 8) -> List[Any]

Run fetch_fn(url) in a thread pool and return results in order.

fetch_fn is user-supplied so this module stays network-agnostic (tests can pass a pure function; production passes requests.get). Exceptions are returned in-place (do not raise) so partial failures don't lose successful results.

hill_height_diagnostics

hill_height_diagnostics(records: Sequence[Any], zelev_attr: str = 'zelev', zhill_attr: str = 'zhill', max_gradient_mperm: float = 10.0, flat_tolerance_m: float = 0.01) -> List[HillHeightAnomaly]

Scan AERMAP-derived receptor/source records for suspicious elevations.

Flags: - zhill < zelev (hill height below ground elevation: impossible) - huge receptor-to-receptor elevation gradients (> max_gradient_mperm) - large clusters of identical zelev values (flat patch signature of DEM stubs or fill — not necessarily wrong but worth surfacing)