Terrain
Digital elevation models and terrain analysis.
Terrain Models and Visibility Analysis.
This module provides tools for working with digital elevation models (DEMs) and computing terrain-related effects:
Digital Elevation Model interface for elevation queries
GEBCO bathymetry/topography data loading
Earth2014 global terrain model loading
Terrain gradient and slope calculations
Line-of-sight and viewshed analysis
Terrain masking for radar/sensor coverage
Submodules
- dem
DEM data structures and elevation query interface.
- loaders
GEBCO and Earth2014 data loaders.
- visibility
Line-of-sight, viewshed, and terrain masking functions.
Examples
>>> from pytcl.terrain import DEMGrid, create_synthetic_terrain
>>> import numpy as np
>>> # Create synthetic terrain for testing
>>> dem = create_synthetic_terrain(
... lat_min=np.radians(35.0),
... lat_max=np.radians(36.0),
... lon_min=np.radians(-120.0),
... lon_max=np.radians(-119.0),
... amplitude=500.0
... )
>>> # Query elevation
>>> point = dem.get_elevation(np.radians(35.5), np.radians(-119.5))
>>> print(f"Elevation: {point.elevation:.1f} m")
>>> # Check line of sight
>>> from pytcl.terrain import line_of_sight
>>> los = line_of_sight(
... dem,
... obs_lat=np.radians(35.2), obs_lon=np.radians(-119.8), obs_height=10.0,
... tgt_lat=np.radians(35.8), tgt_lon=np.radians(-119.2), tgt_height=10.0
... )
>>> print(f"Visible: {los.visible}, Clearance: {los.clearance:.1f} m")
>>> # Load GEBCO/Earth2014 data (requires external files)
>>> from pytcl.terrain import load_gebco, load_earth2014, create_test_gebco_dem
>>> # Use test data for demonstration
>>> dem = create_test_gebco_dem()
- class pytcl.terrain.DEMPoint(latitude, longitude, elevation, valid)[source]
Bases:
NamedTupleSingle point elevation query result.
- Parameters:
- class pytcl.terrain.TerrainGradient(slope, aspect, dz_dx, dz_dy)[source]
Bases:
NamedTupleTerrain gradient at a point.
- Parameters:
- class pytcl.terrain.DEMMetadata(name, resolution, lat_min, lat_max, lon_min, lon_max, vertical_datum, horizontal_datum)[source]
Bases:
NamedTupleMetadata for a DEM dataset.
- Parameters:
name (str) – Name of the DEM dataset.
resolution (float) – Grid resolution in arc-seconds.
lat_min (float) – Minimum latitude in radians.
lat_max (float) – Maximum latitude in radians.
lon_min (float) – Minimum longitude in radians.
lon_max (float) – Maximum longitude in radians.
vertical_datum (str) – Vertical reference (e.g., ‘MSL’, ‘WGS84’).
horizontal_datum (str) – Horizontal reference (e.g., ‘WGS84’).
- class pytcl.terrain.DEMGrid(data, lat_min, lat_max, lon_min, lon_max, nodata_value=-9999.0, name='Custom DEM')[source]
Bases:
objectIn-memory DEM grid for elevation queries.
This class provides a simple in-memory representation of a DEM grid for efficient elevation queries. Data can be loaded from arrays or from external files.
- Parameters:
data (ndarray) – 2D array of elevation values (rows=latitude, cols=longitude). Latitude increases with row index (south to north).
lat_min (float) – Minimum latitude in radians.
lat_max (float) – Maximum latitude in radians.
lon_min (float) – Minimum longitude in radians.
lon_max (float) – Maximum longitude in radians.
nodata_value (float, optional) – Value representing no data. Default is -9999.
name (str, optional) – Name identifier for the DEM. Default is “Custom DEM”.
Examples
>>> import numpy as np >>> # Create a simple 10x10 DEM grid >>> data = np.random.rand(10, 10) * 1000 # 0-1000m elevation >>> dem = DEMGrid( ... data, ... lat_min=np.radians(35.0), ... lat_max=np.radians(36.0), ... lon_min=np.radians(-120.0), ... lon_max=np.radians(-119.0) ... ) >>> elev = dem.get_elevation(np.radians(35.5), np.radians(-119.5))
- __init__(data, lat_min, lat_max, lon_min, lon_max, nodata_value=-9999.0, name='Custom DEM')[source]
- get_elevation(lat, lon, interpolation='bilinear')[source]
Get elevation at a geographic coordinate.
- get_elevations(lats, lons, interpolation='bilinear')[source]
Get elevations at multiple geographic coordinates.
- Parameters:
lats (ndarray) – Latitudes in radians.
lons (ndarray) – Longitudes in radians.
interpolation (str, optional) – Interpolation method. Default is ‘bilinear’.
- Returns:
Array of elevation values. Invalid points contain nodata_value.
- Return type:
ndarray
- pytcl.terrain.get_elevation_profile(dem, lat_start, lon_start, lat_end, lon_end, n_points=100)[source]
Extract elevation profile along a path.
- Parameters:
dem (DEMGrid) – DEM grid to query.
lat_start (float) – Starting latitude in radians.
lon_start (float) – Starting longitude in radians.
lat_end (float) – Ending latitude in radians.
lon_end (float) – Ending longitude in radians.
n_points (int, optional) – Number of sample points along the path. Default is 100.
- Returns:
distances (ndarray) – Array of distances from start in meters.
elevations (ndarray) – Array of elevation values in meters.
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> import numpy as np >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), ... elevation=500) >>> dists, elevs = get_elevation_profile( ... dem, np.radians(35.2), np.radians(-119.8), ... np.radians(35.8), np.radians(-119.2), n_points=10) >>> len(dists) == 10 True
- pytcl.terrain.interpolate_dem(dem, new_lat_min, new_lat_max, new_lon_min, new_lon_max, new_n_lat, new_n_lon, method='bilinear')[source]
Interpolate DEM to a new grid.
- Parameters:
dem (DEMGrid) – Source DEM grid.
new_lat_min (float) – New minimum latitude in radians.
new_lat_max (float) – New maximum latitude in radians.
new_lon_min (float) – New minimum longitude in radians.
new_lon_max (float) – New maximum longitude in radians.
new_n_lat (int) – Number of latitude points in new grid.
new_n_lon (int) – Number of longitude points in new grid.
method (str, optional) – Interpolation method. Default is ‘bilinear’.
- Returns:
New interpolated DEM grid.
- Return type:
Examples
>>> import numpy as np >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), ... elevation=100) >>> new_dem = interpolate_dem( ... dem, ... np.radians(35.2), np.radians(35.8), ... np.radians(-119.8), np.radians(-119.2), ... new_n_lat=5, new_n_lon=5) >>> new_dem.data.shape (5, 5)
- pytcl.terrain.merge_dems(dems, lat_min, lat_max, lon_min, lon_max, resolution_arcsec=30.0)[source]
Merge multiple DEMs into a single grid.
- Parameters:
lat_min (float) – Output minimum latitude in radians.
lat_max (float) – Output maximum latitude in radians.
lon_min (float) – Output minimum longitude in radians.
lon_max (float) – Output maximum longitude in radians.
resolution_arcsec (float, optional) – Output resolution in arc-seconds. Default is 30.0.
- Returns:
Merged DEM grid.
- Return type:
Examples
>>> import numpy as np >>> dem1 = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), elevation=100) >>> dem2 = create_flat_dem( ... np.radians(36), np.radians(37), ... np.radians(-120), np.radians(-119), elevation=200) >>> merged = merge_dems( ... [dem1, dem2], ... np.radians(35), np.radians(37), ... np.radians(-120), np.radians(-119)) >>> merged.name 'Merged DEM'
- pytcl.terrain.create_flat_dem(lat_min, lat_max, lon_min, lon_max, elevation=0.0, resolution_arcsec=30.0)[source]
Create a flat DEM with constant elevation.
Useful for testing or as a placeholder when terrain data is unavailable.
- Parameters:
lat_min (float) – Minimum latitude in radians.
lat_max (float) – Maximum latitude in radians.
lon_min (float) – Minimum longitude in radians.
lon_max (float) – Maximum longitude in radians.
elevation (float, optional) – Constant elevation in meters. Default is 0.0.
resolution_arcsec (float, optional) – Grid resolution in arc-seconds. Default is 30.0.
- Returns:
Flat DEM grid.
- Return type:
Examples
>>> import numpy as np >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), ... elevation=500) >>> dem.name 'Flat DEM' >>> result = dem.get_elevation(np.radians(35.5), np.radians(-119.5)) >>> abs(result.elevation - 500) < 1 True
- pytcl.terrain.create_synthetic_terrain(lat_min, lat_max, lon_min, lon_max, base_elevation=0.0, amplitude=1000.0, wavelength_km=50.0, resolution_arcsec=30.0, seed=None)[source]
Create synthetic terrain for testing.
Generates terrain using a combination of sinusoidal functions and random noise to simulate realistic topography.
- Parameters:
lat_min (float) – Minimum latitude in radians.
lat_max (float) – Maximum latitude in radians.
lon_min (float) – Minimum longitude in radians.
lon_max (float) – Maximum longitude in radians.
base_elevation (float, optional) – Base elevation in meters. Default is 0.0.
amplitude (float, optional) – Amplitude of elevation variations in meters. Default is 1000.0.
wavelength_km (float, optional) – Characteristic wavelength of terrain features in km. Default is 50.0.
resolution_arcsec (float, optional) – Grid resolution in arc-seconds. Default is 30.0.
seed (int, optional) – Random seed for reproducibility.
- Returns:
Synthetic terrain DEM.
- Return type:
Examples
>>> import numpy as np >>> dem = create_synthetic_terrain( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), ... base_elevation=500, amplitude=200, seed=42) >>> dem.name 'Synthetic Terrain' >>> dem.data.min() < dem.data.max() # Has elevation variation True
- class pytcl.terrain.LOSResult(visible, grazing_angle, obstacle_distance, obstacle_elevation, clearance)[source]
Bases:
NamedTupleLine-of-sight analysis result.
- Parameters:
visible (bool) – Whether there is unobstructed line of sight between observer and target.
grazing_angle (float) – Grazing angle to terrain in radians (angle above/below obstacle). Positive means clearing terrain, negative means blocked.
obstacle_distance (float) – Distance to the blocking obstacle in meters (if blocked). 0 if line of sight is clear.
obstacle_elevation (float) – Elevation of blocking obstacle in meters (if blocked).
clearance (float) – Minimum clearance above terrain along path in meters. Negative if blocked.
- class pytcl.terrain.ViewshedResult(visible, observer_lat, observer_lon, observer_height, lat_min, lat_max, lon_min, lon_max)[source]
Bases:
NamedTupleViewshed computation result.
- Parameters:
visible (ndarray) – 2D boolean array indicating visibility from observer.
observer_lat (float) – Observer latitude in radians.
observer_lon (float) – Observer longitude in radians.
observer_height (float) – Observer height above terrain in meters.
lat_min (float) – Minimum latitude of viewshed grid in radians.
lat_max (float) – Maximum latitude of viewshed grid in radians.
lon_min (float) – Minimum longitude of viewshed grid in radians.
lon_max (float) – Maximum longitude of viewshed grid in radians.
- class pytcl.terrain.HorizonPoint(azimuth, elevation_angle, distance, terrain_elevation)[source]
Bases:
NamedTuplePoint on the terrain horizon.
- Parameters:
- pytcl.terrain.line_of_sight(dem, obs_lat, obs_lon, obs_height, tgt_lat, tgt_lon, tgt_height, n_samples=100, earth_radius=6371000.0, refraction_coeff=0.0)[source]
Compute line of sight between observer and target.
- Parameters:
dem (DEMGrid) – Digital elevation model.
obs_lat (float) – Observer latitude in radians.
obs_lon (float) – Observer longitude in radians.
obs_height (float) – Observer height above terrain in meters.
tgt_lat (float) – Target latitude in radians.
tgt_lon (float) – Target longitude in radians.
tgt_height (float) – Target height above terrain in meters.
n_samples (int, optional) – Number of sample points along path. Default is 100.
earth_radius (float, optional) – Earth radius in meters. Default is 6371000.0.
refraction_coeff (float, optional) – Atmospheric refraction coefficient (typically 0.13 for radio). Set to 0 for optical line of sight. Default is 0.0.
- Returns:
Line-of-sight analysis result.
- Return type:
Notes
The refraction coefficient models atmospheric bending of radio waves. A typical value for radio frequencies is 0.13 (4/3 Earth model). For optical line of sight, use 0.
Examples
>>> import numpy as np >>> from pytcl.terrain.dem import create_flat_dem >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), elevation=100) >>> result = line_of_sight( ... dem, ... np.radians(35.3), np.radians(-119.7), 10, ... np.radians(35.7), np.radians(-119.3), 10) >>> result.visible # Clear LOS over flat terrain True
- pytcl.terrain.viewshed(dem, obs_lat, obs_lon, obs_height, max_range=50000.0, target_height=0.0, n_radials=360, samples_per_radial=100, earth_radius=6371000.0, refraction_coeff=0.0)[source]
Compute viewshed (visible area) from an observer location.
- Parameters:
dem (DEMGrid) – Digital elevation model.
obs_lat (float) – Observer latitude in radians.
obs_lon (float) – Observer longitude in radians.
obs_height (float) – Observer height above terrain in meters.
max_range (float, optional) – Maximum analysis range in meters. Default is 50000.0.
target_height (float, optional) – Target height above terrain in meters. Default is 0.0.
n_radials (int, optional) – Number of radial directions to analyze. Default is 360.
samples_per_radial (int, optional) – Number of samples per radial. Default is 100.
earth_radius (float, optional) – Earth radius in meters. Default is 6371000.0.
refraction_coeff (float, optional) – Atmospheric refraction coefficient. Default is 0.0.
- Returns:
Viewshed computation result with visibility grid.
- Return type:
Examples
>>> import numpy as np >>> from pytcl.terrain.dem import create_flat_dem >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), elevation=100) >>> result = viewshed( ... dem, np.radians(35.5), np.radians(-119.5), 20, ... max_range=10000, n_radials=36, samples_per_radial=10) >>> result.visible.any() # Some cells visible True
- pytcl.terrain.compute_horizon(dem, obs_lat, obs_lon, obs_height, n_azimuths=360, max_range=50000.0, samples_per_radial=100, earth_radius=6371000.0)[source]
Compute terrain horizon profile from an observer location.
- Parameters:
dem (DEMGrid) – Digital elevation model.
obs_lat (float) – Observer latitude in radians.
obs_lon (float) – Observer longitude in radians.
obs_height (float) – Observer height above terrain in meters.
n_azimuths (int, optional) – Number of azimuth directions. Default is 360.
max_range (float, optional) – Maximum analysis range in meters. Default is 50000.0.
samples_per_radial (int, optional) – Number of samples per radial. Default is 100.
earth_radius (float, optional) – Earth radius in meters. Default is 6371000.0.
- Returns:
Horizon points for each azimuth direction.
- Return type:
list of HorizonPoint
Examples
>>> import numpy as np >>> from pytcl.terrain.dem import create_flat_dem >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), elevation=100) >>> horizon = compute_horizon( ... dem, np.radians(35.5), np.radians(-119.5), 10, ... n_azimuths=8, max_range=10000, samples_per_radial=10) >>> len(horizon) 8
- pytcl.terrain.terrain_masking_angle(dem, obs_lat, obs_lon, obs_height, azimuth, max_range=50000.0, n_samples=100, earth_radius=6371000.0)[source]
Compute terrain masking angle in a specific direction.
The masking angle is the minimum elevation angle at which a target would be visible (not blocked by terrain).
- Parameters:
dem (DEMGrid) – Digital elevation model.
obs_lat (float) – Observer latitude in radians.
obs_lon (float) – Observer longitude in radians.
obs_height (float) – Observer height above terrain in meters.
azimuth (float) – Azimuth direction in radians (clockwise from north).
max_range (float, optional) – Maximum analysis range in meters. Default is 50000.0.
n_samples (int, optional) – Number of sample points. Default is 100.
earth_radius (float, optional) – Earth radius in meters. Default is 6371000.0.
- Returns:
Masking angle in radians above horizontal.
- Return type:
Examples
>>> import numpy as np >>> from pytcl.terrain.dem import create_flat_dem >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), elevation=100) >>> angle = terrain_masking_angle( ... dem, np.radians(35.5), np.radians(-119.5), 10, azimuth=0) >>> -np.pi/2 <= angle <= np.pi/2 # Valid angle range True
- pytcl.terrain.radar_coverage_map(dem, radar_lat, radar_lon, radar_height, min_elevation=0.0, max_range=100000.0, target_height=1000.0, n_radials=360, samples_per_radial=200, earth_radius=6371000.0, refraction_coeff=0.13)[source]
Compute radar coverage map accounting for terrain masking.
Similar to viewshed but with radar-specific parameters including minimum elevation angle and atmospheric refraction.
- Parameters:
dem (DEMGrid) – Digital elevation model.
radar_lat (float) – Radar latitude in radians.
radar_lon (float) – Radar longitude in radians.
radar_height (float) – Radar antenna height above terrain in meters.
min_elevation (float, optional) – Minimum radar elevation angle in radians. Default is 0.0.
max_range (float, optional) – Maximum radar range in meters. Default is 100000.0.
target_height (float, optional) – Target altitude above terrain in meters. Default is 1000.0.
n_radials (int, optional) – Number of radial directions. Default is 360.
samples_per_radial (int, optional) – Samples per radial. Default is 200.
earth_radius (float, optional) – Earth radius in meters. Default is 6371000.0.
refraction_coeff (float, optional) – Atmospheric refraction coefficient (0.13 for 4/3 Earth). Default is 0.13.
- Returns:
Radar coverage map.
- Return type:
Examples
>>> import numpy as np >>> from pytcl.terrain.dem import create_flat_dem >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), elevation=100) >>> coverage = radar_coverage_map( ... dem, np.radians(35.5), np.radians(-119.5), 30, ... max_range=20000, n_radials=36, samples_per_radial=20) >>> coverage.visible.any() # Some coverage exists True
- class pytcl.terrain.GEBCOMetadata(version, resolution_arcsec, lat_min, lat_max, lon_min, lon_max, source)[source]
Bases:
NamedTupleGEBCO dataset metadata.
- class pytcl.terrain.Earth2014Metadata(layer, description, resolution_arcsec, lat_min, lat_max, lon_min, lon_max)[source]
Bases:
NamedTupleEarth2014 dataset metadata.
- pytcl.terrain.get_data_dir()[source]
Get the pytcl data directory for external data files.
The data directory is located at
~/.pytcl/data/by default. Can be overridden by setting thePYTCL_DATA_DIRenvironment variable.- Returns:
Path to the data directory.
- Return type:
Path
- pytcl.terrain.load_gebco(lat_min, lat_max, lon_min, lon_max, version='GEBCO2025')[source]
Load GEBCO bathymetry/topography data for a region.
GEBCO (General Bathymetric Chart of the Oceans) provides global bathymetry and land topography at 15 arc-second resolution.
- Parameters:
lat_min (float) – Minimum latitude in radians.
lat_max (float) – Maximum latitude in radians.
lon_min (float) – Minimum longitude in radians.
lon_max (float) – Maximum longitude in radians.
version (str, optional) – GEBCO version (“GEBCO2025”, “GEBCO2024”, “GEBCO2023”, “GEBCO2022”). Default is “GEBCO2025”.
- Returns:
DEM grid containing bathymetry/topography data.
- Return type:
- Raises:
FileNotFoundError – If the GEBCO file is not found.
DependencyError – If netCDF4 is not installed.
Examples
>>> import numpy as np >>> dem = load_gebco( ... lat_min=np.radians(35.0), ... lat_max=np.radians(40.0), ... lon_min=np.radians(-125.0), ... lon_max=np.radians(-120.0), ... version="GEBCO2025" ... ) >>> point = dem.get_elevation(np.radians(37.5), np.radians(-122.5)) >>> print(f"Elevation: {point.elevation:.1f} m")
Notes
GEBCO data files are not included in the package due to their size (~7.5 GB). Download from: https://www.gebco.net/data-products/gridded-bathymetry-data/ Save to: ~/.pytcl/data/GEBCO2025.nc
- pytcl.terrain.load_earth2014(lat_min, lat_max, lon_min, lon_max, layer='SUR')[source]
Load Earth2014 terrain data for a region.
Earth2014 provides global topography at 1 arc-minute resolution with multiple layer representations.
- Parameters:
lat_min (float) – Minimum latitude in radians.
lat_max (float) – Maximum latitude in radians.
lon_min (float) – Minimum longitude in radians.
lon_max (float) – Maximum longitude in radians.
layer (str, optional) – Layer type. Options: - “SUR”: Physical surface (topography, ice surface, 0 over oceans) - “BED”: Bedrock (topography minus ice/water) - “TBI”: Topography, bedrock, ice sheet surfaces - “RET”: Rock-equivalent topography - “ICE”: Ice sheet thickness Default is “SUR”.
- Returns:
DEM grid containing terrain data.
- Return type:
- Raises:
FileNotFoundError – If the Earth2014 file is not found.
ValueError – If an invalid layer is specified.
Examples
>>> import numpy as np >>> dem = load_earth2014( ... lat_min=np.radians(35.0), ... lat_max=np.radians(40.0), ... lon_min=np.radians(-125.0), ... lon_max=np.radians(-120.0), ... layer="SUR" ... ) >>> point = dem.get_elevation(np.radians(37.5), np.radians(-122.5)) >>> print(f"Elevation: {point.elevation:.1f} m")
Notes
Earth2014 data files are not included in the package (~455 MB per layer). Download from: https://ddfe.curtin.edu.au/models/Earth2014/ Save to: ~/.pytcl/data/Earth2014.SUR2014.1min.geod.bin (for SUR layer)
References
Hirt, C. and Rexer, M. (2015) Earth2014: 1 arc-min shape, topography, bedrock and ice-sheet models. Int. J. Appl. Earth Observ. Geoinform. 39.
- pytcl.terrain.create_test_gebco_dem(lat_min=np.float64(0.6108652381980153), lat_max=np.float64(0.6981317007977318), lon_min=np.float64(-2.181661564992912), lon_max=np.float64(-2.0943951023931953), resolution_arcsec=60.0, include_bathymetry=True, seed=42)[source]
Create synthetic GEBCO-like DEM for testing.
Generates a synthetic terrain with both land and ocean areas, useful for testing without requiring actual GEBCO data files.
- Parameters:
lat_min (float, optional) – Minimum latitude in radians. Default is 35°N.
lat_max (float, optional) – Maximum latitude in radians. Default is 40°N.
lon_min (float, optional) – Minimum longitude in radians. Default is 125°W.
lon_max (float, optional) – Maximum longitude in radians. Default is 120°W.
resolution_arcsec (float, optional) – Grid resolution in arc-seconds. Default is 60.0 (1 arc-min).
include_bathymetry (bool, optional) – If True, includes ocean bathymetry. Default is True.
seed (int, optional) – Random seed for reproducibility. Default is 42.
- Returns:
Synthetic DEM mimicking GEBCO characteristics.
- Return type:
- pytcl.terrain.create_test_earth2014_dem(lat_min=np.float64(0.6108652381980153), lat_max=np.float64(0.6981317007977318), lon_min=np.float64(-2.181661564992912), lon_max=np.float64(-2.0943951023931953), layer='SUR', resolution_arcsec=60.0, seed=42)[source]
Create synthetic Earth2014-like DEM for testing.
Generates a synthetic terrain mimicking Earth2014 characteristics, useful for testing without requiring actual data files.
- Parameters:
lat_min (float, optional) – Minimum latitude in radians. Default is 35°N.
lat_max (float, optional) – Maximum latitude in radians. Default is 40°N.
lon_min (float, optional) – Minimum longitude in radians. Default is 125°W.
lon_max (float, optional) – Maximum longitude in radians. Default is 120°W.
layer (str, optional) – Layer type to simulate. Default is “SUR”.
resolution_arcsec (float, optional) – Grid resolution in arc-seconds. Default is 60.0 (1 arc-min).
seed (int, optional) – Random seed for reproducibility. Default is 42.
- Returns:
Synthetic DEM mimicking Earth2014 characteristics.
- Return type:
- pytcl.terrain.get_gebco_metadata(version='GEBCO2025')[source]
Get metadata for a GEBCO version.
- Parameters:
version (str) – GEBCO version.
- Returns:
Metadata about the dataset.
- Return type:
- pytcl.terrain.get_earth2014_metadata(layer='SUR')[source]
Get metadata for an Earth2014 layer.
- Parameters:
layer (str) – Layer type.
- Returns:
Metadata about the dataset.
- Return type:
DEM Interface
Digital Elevation Model interface and operations.
Digital Elevation Model (DEM) Interface.
This module provides a generic interface for working with digital elevation models, including: - Elevation queries at geographic coordinates - Terrain gradient and slope calculations - Profile extraction along paths - Support for various DEM data formats
The interface is designed to work with common DEM formats including: - SRTM (Shuttle Radar Topography Mission) - ASTER GDEM - GEBCO (General Bathymetric Chart of the Oceans) - Earth2014 - ETOPO1/ETOPO2
References
- class pytcl.terrain.dem.DEMPoint(latitude, longitude, elevation, valid)[source]
Bases:
NamedTupleSingle point elevation query result.
- Parameters:
- class pytcl.terrain.dem.TerrainGradient(slope, aspect, dz_dx, dz_dy)[source]
Bases:
NamedTupleTerrain gradient at a point.
- Parameters:
- class pytcl.terrain.dem.DEMMetadata(name, resolution, lat_min, lat_max, lon_min, lon_max, vertical_datum, horizontal_datum)[source]
Bases:
NamedTupleMetadata for a DEM dataset.
- Parameters:
name (str) – Name of the DEM dataset.
resolution (float) – Grid resolution in arc-seconds.
lat_min (float) – Minimum latitude in radians.
lat_max (float) – Maximum latitude in radians.
lon_min (float) – Minimum longitude in radians.
lon_max (float) – Maximum longitude in radians.
vertical_datum (str) – Vertical reference (e.g., ‘MSL’, ‘WGS84’).
horizontal_datum (str) – Horizontal reference (e.g., ‘WGS84’).
- class pytcl.terrain.dem.DEMGrid(data, lat_min, lat_max, lon_min, lon_max, nodata_value=-9999.0, name='Custom DEM')[source]
Bases:
objectIn-memory DEM grid for elevation queries.
This class provides a simple in-memory representation of a DEM grid for efficient elevation queries. Data can be loaded from arrays or from external files.
- Parameters:
data (ndarray) – 2D array of elevation values (rows=latitude, cols=longitude). Latitude increases with row index (south to north).
lat_min (float) – Minimum latitude in radians.
lat_max (float) – Maximum latitude in radians.
lon_min (float) – Minimum longitude in radians.
lon_max (float) – Maximum longitude in radians.
nodata_value (float, optional) – Value representing no data. Default is -9999.
name (str, optional) – Name identifier for the DEM. Default is “Custom DEM”.
Examples
>>> import numpy as np >>> # Create a simple 10x10 DEM grid >>> data = np.random.rand(10, 10) * 1000 # 0-1000m elevation >>> dem = DEMGrid( ... data, ... lat_min=np.radians(35.0), ... lat_max=np.radians(36.0), ... lon_min=np.radians(-120.0), ... lon_max=np.radians(-119.0) ... ) >>> elev = dem.get_elevation(np.radians(35.5), np.radians(-119.5))
- __init__(data, lat_min, lat_max, lon_min, lon_max, nodata_value=-9999.0, name='Custom DEM')[source]
- get_elevation(lat, lon, interpolation='bilinear')[source]
Get elevation at a geographic coordinate.
- get_elevations(lats, lons, interpolation='bilinear')[source]
Get elevations at multiple geographic coordinates.
- Parameters:
lats (ndarray) – Latitudes in radians.
lons (ndarray) – Longitudes in radians.
interpolation (str, optional) – Interpolation method. Default is ‘bilinear’.
- Returns:
Array of elevation values. Invalid points contain nodata_value.
- Return type:
ndarray
- pytcl.terrain.dem.get_elevation_profile(dem, lat_start, lon_start, lat_end, lon_end, n_points=100)[source]
Extract elevation profile along a path.
- Parameters:
dem (DEMGrid) – DEM grid to query.
lat_start (float) – Starting latitude in radians.
lon_start (float) – Starting longitude in radians.
lat_end (float) – Ending latitude in radians.
lon_end (float) – Ending longitude in radians.
n_points (int, optional) – Number of sample points along the path. Default is 100.
- Returns:
distances (ndarray) – Array of distances from start in meters.
elevations (ndarray) – Array of elevation values in meters.
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> import numpy as np >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), ... elevation=500) >>> dists, elevs = get_elevation_profile( ... dem, np.radians(35.2), np.radians(-119.8), ... np.radians(35.8), np.radians(-119.2), n_points=10) >>> len(dists) == 10 True
- pytcl.terrain.dem.interpolate_dem(dem, new_lat_min, new_lat_max, new_lon_min, new_lon_max, new_n_lat, new_n_lon, method='bilinear')[source]
Interpolate DEM to a new grid.
- Parameters:
dem (DEMGrid) – Source DEM grid.
new_lat_min (float) – New minimum latitude in radians.
new_lat_max (float) – New maximum latitude in radians.
new_lon_min (float) – New minimum longitude in radians.
new_lon_max (float) – New maximum longitude in radians.
new_n_lat (int) – Number of latitude points in new grid.
new_n_lon (int) – Number of longitude points in new grid.
method (str, optional) – Interpolation method. Default is ‘bilinear’.
- Returns:
New interpolated DEM grid.
- Return type:
Examples
>>> import numpy as np >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), ... elevation=100) >>> new_dem = interpolate_dem( ... dem, ... np.radians(35.2), np.radians(35.8), ... np.radians(-119.8), np.radians(-119.2), ... new_n_lat=5, new_n_lon=5) >>> new_dem.data.shape (5, 5)
- pytcl.terrain.dem.merge_dems(dems, lat_min, lat_max, lon_min, lon_max, resolution_arcsec=30.0)[source]
Merge multiple DEMs into a single grid.
- Parameters:
lat_min (float) – Output minimum latitude in radians.
lat_max (float) – Output maximum latitude in radians.
lon_min (float) – Output minimum longitude in radians.
lon_max (float) – Output maximum longitude in radians.
resolution_arcsec (float, optional) – Output resolution in arc-seconds. Default is 30.0.
- Returns:
Merged DEM grid.
- Return type:
Examples
>>> import numpy as np >>> dem1 = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), elevation=100) >>> dem2 = create_flat_dem( ... np.radians(36), np.radians(37), ... np.radians(-120), np.radians(-119), elevation=200) >>> merged = merge_dems( ... [dem1, dem2], ... np.radians(35), np.radians(37), ... np.radians(-120), np.radians(-119)) >>> merged.name 'Merged DEM'
- pytcl.terrain.dem.create_flat_dem(lat_min, lat_max, lon_min, lon_max, elevation=0.0, resolution_arcsec=30.0)[source]
Create a flat DEM with constant elevation.
Useful for testing or as a placeholder when terrain data is unavailable.
- Parameters:
lat_min (float) – Minimum latitude in radians.
lat_max (float) – Maximum latitude in radians.
lon_min (float) – Minimum longitude in radians.
lon_max (float) – Maximum longitude in radians.
elevation (float, optional) – Constant elevation in meters. Default is 0.0.
resolution_arcsec (float, optional) – Grid resolution in arc-seconds. Default is 30.0.
- Returns:
Flat DEM grid.
- Return type:
Examples
>>> import numpy as np >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), ... elevation=500) >>> dem.name 'Flat DEM' >>> result = dem.get_elevation(np.radians(35.5), np.radians(-119.5)) >>> abs(result.elevation - 500) < 1 True
- pytcl.terrain.dem.create_synthetic_terrain(lat_min, lat_max, lon_min, lon_max, base_elevation=0.0, amplitude=1000.0, wavelength_km=50.0, resolution_arcsec=30.0, seed=None)[source]
Create synthetic terrain for testing.
Generates terrain using a combination of sinusoidal functions and random noise to simulate realistic topography.
- Parameters:
lat_min (float) – Minimum latitude in radians.
lat_max (float) – Maximum latitude in radians.
lon_min (float) – Minimum longitude in radians.
lon_max (float) – Maximum longitude in radians.
base_elevation (float, optional) – Base elevation in meters. Default is 0.0.
amplitude (float, optional) – Amplitude of elevation variations in meters. Default is 1000.0.
wavelength_km (float, optional) – Characteristic wavelength of terrain features in km. Default is 50.0.
resolution_arcsec (float, optional) – Grid resolution in arc-seconds. Default is 30.0.
seed (int, optional) – Random seed for reproducibility.
- Returns:
Synthetic terrain DEM.
- Return type:
Examples
>>> import numpy as np >>> dem = create_synthetic_terrain( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), ... base_elevation=500, amplitude=200, seed=42) >>> dem.name 'Synthetic Terrain' >>> dem.data.min() < dem.data.max() # Has elevation variation True
Data Loaders
GEBCO and Earth2014 terrain data loaders. The default GEBCO version is
GEBCO 2025. Requires pip install nrl-tracker[terrain] for NetCDF support.
GEBCO and Earth2014 terrain data loaders.
This module provides functions for loading elevation data from: - GEBCO (General Bathymetric Chart of the Oceans): Global bathymetry and topography - Earth2014: High-resolution global terrain model with multiple representations
Both datasets require external files that are too large to bundle with the package. Users should download files from the official sources and place them in ~/.pytcl/data/.
References
GEBCO Compilation Group (2025) GEBCO 2025 Grid. https://www.gebco.net/data-products/gridded-bathymetry-data/
Hirt, C. and Rexer, M. (2015) Earth2014: 1 arc-min shape, topography, bedrock and ice-sheet models - available as gridded data and degree-10,800 spherical harmonics. Int. J. Appl. Earth Observ. Geoinform. 39, 103-112. https://ddfe.curtin.edu.au/models/Earth2014/
- class pytcl.terrain.loaders.GEBCOMetadata(version, resolution_arcsec, lat_min, lat_max, lon_min, lon_max, source)[source]
Bases:
NamedTupleGEBCO dataset metadata.
- class pytcl.terrain.loaders.Earth2014Metadata(layer, description, resolution_arcsec, lat_min, lat_max, lon_min, lon_max)[source]
Bases:
NamedTupleEarth2014 dataset metadata.
- pytcl.terrain.loaders.get_data_dir()[source]
Get the pytcl data directory for external data files.
The data directory is located at
~/.pytcl/data/by default. Can be overridden by setting thePYTCL_DATA_DIRenvironment variable.- Returns:
Path to the data directory.
- Return type:
Path
- pytcl.terrain.loaders.load_gebco(lat_min, lat_max, lon_min, lon_max, version='GEBCO2025')[source]
Load GEBCO bathymetry/topography data for a region.
GEBCO (General Bathymetric Chart of the Oceans) provides global bathymetry and land topography at 15 arc-second resolution.
- Parameters:
lat_min (float) – Minimum latitude in radians.
lat_max (float) – Maximum latitude in radians.
lon_min (float) – Minimum longitude in radians.
lon_max (float) – Maximum longitude in radians.
version (str, optional) – GEBCO version (“GEBCO2025”, “GEBCO2024”, “GEBCO2023”, “GEBCO2022”). Default is “GEBCO2025”.
- Returns:
DEM grid containing bathymetry/topography data.
- Return type:
- Raises:
FileNotFoundError – If the GEBCO file is not found.
DependencyError – If netCDF4 is not installed.
Examples
>>> import numpy as np >>> dem = load_gebco( ... lat_min=np.radians(35.0), ... lat_max=np.radians(40.0), ... lon_min=np.radians(-125.0), ... lon_max=np.radians(-120.0), ... version="GEBCO2025" ... ) >>> point = dem.get_elevation(np.radians(37.5), np.radians(-122.5)) >>> print(f"Elevation: {point.elevation:.1f} m")
Notes
GEBCO data files are not included in the package due to their size (~7.5 GB). Download from: https://www.gebco.net/data-products/gridded-bathymetry-data/ Save to: ~/.pytcl/data/GEBCO2025.nc
- pytcl.terrain.loaders.load_earth2014(lat_min, lat_max, lon_min, lon_max, layer='SUR')[source]
Load Earth2014 terrain data for a region.
Earth2014 provides global topography at 1 arc-minute resolution with multiple layer representations.
- Parameters:
lat_min (float) – Minimum latitude in radians.
lat_max (float) – Maximum latitude in radians.
lon_min (float) – Minimum longitude in radians.
lon_max (float) – Maximum longitude in radians.
layer (str, optional) – Layer type. Options: - “SUR”: Physical surface (topography, ice surface, 0 over oceans) - “BED”: Bedrock (topography minus ice/water) - “TBI”: Topography, bedrock, ice sheet surfaces - “RET”: Rock-equivalent topography - “ICE”: Ice sheet thickness Default is “SUR”.
- Returns:
DEM grid containing terrain data.
- Return type:
- Raises:
FileNotFoundError – If the Earth2014 file is not found.
ValueError – If an invalid layer is specified.
Examples
>>> import numpy as np >>> dem = load_earth2014( ... lat_min=np.radians(35.0), ... lat_max=np.radians(40.0), ... lon_min=np.radians(-125.0), ... lon_max=np.radians(-120.0), ... layer="SUR" ... ) >>> point = dem.get_elevation(np.radians(37.5), np.radians(-122.5)) >>> print(f"Elevation: {point.elevation:.1f} m")
Notes
Earth2014 data files are not included in the package (~455 MB per layer). Download from: https://ddfe.curtin.edu.au/models/Earth2014/ Save to: ~/.pytcl/data/Earth2014.SUR2014.1min.geod.bin (for SUR layer)
References
Hirt, C. and Rexer, M. (2015) Earth2014: 1 arc-min shape, topography, bedrock and ice-sheet models. Int. J. Appl. Earth Observ. Geoinform. 39.
- pytcl.terrain.loaders.create_test_gebco_dem(lat_min=np.float64(0.6108652381980153), lat_max=np.float64(0.6981317007977318), lon_min=np.float64(-2.181661564992912), lon_max=np.float64(-2.0943951023931953), resolution_arcsec=60.0, include_bathymetry=True, seed=42)[source]
Create synthetic GEBCO-like DEM for testing.
Generates a synthetic terrain with both land and ocean areas, useful for testing without requiring actual GEBCO data files.
- Parameters:
lat_min (float, optional) – Minimum latitude in radians. Default is 35°N.
lat_max (float, optional) – Maximum latitude in radians. Default is 40°N.
lon_min (float, optional) – Minimum longitude in radians. Default is 125°W.
lon_max (float, optional) – Maximum longitude in radians. Default is 120°W.
resolution_arcsec (float, optional) – Grid resolution in arc-seconds. Default is 60.0 (1 arc-min).
include_bathymetry (bool, optional) – If True, includes ocean bathymetry. Default is True.
seed (int, optional) – Random seed for reproducibility. Default is 42.
- Returns:
Synthetic DEM mimicking GEBCO characteristics.
- Return type:
- pytcl.terrain.loaders.create_test_earth2014_dem(lat_min=np.float64(0.6108652381980153), lat_max=np.float64(0.6981317007977318), lon_min=np.float64(-2.181661564992912), lon_max=np.float64(-2.0943951023931953), layer='SUR', resolution_arcsec=60.0, seed=42)[source]
Create synthetic Earth2014-like DEM for testing.
Generates a synthetic terrain mimicking Earth2014 characteristics, useful for testing without requiring actual data files.
- Parameters:
lat_min (float, optional) – Minimum latitude in radians. Default is 35°N.
lat_max (float, optional) – Maximum latitude in radians. Default is 40°N.
lon_min (float, optional) – Minimum longitude in radians. Default is 125°W.
lon_max (float, optional) – Maximum longitude in radians. Default is 120°W.
layer (str, optional) – Layer type to simulate. Default is “SUR”.
resolution_arcsec (float, optional) – Grid resolution in arc-seconds. Default is 60.0 (1 arc-min).
seed (int, optional) – Random seed for reproducibility. Default is 42.
- Returns:
Synthetic DEM mimicking Earth2014 characteristics.
- Return type:
- pytcl.terrain.loaders.get_gebco_metadata(version='GEBCO2025')[source]
Get metadata for a GEBCO version.
- Parameters:
version (str) – GEBCO version.
- Returns:
Metadata about the dataset.
- Return type:
- pytcl.terrain.loaders.get_earth2014_metadata(layer='SUR')[source]
Get metadata for an Earth2014 layer.
- Parameters:
layer (str) – Layer type.
- Returns:
Metadata about the dataset.
- Return type:
- pytcl.terrain.loaders.parse_gebco_netcdf(filepath, lat_min=None, lat_max=None, lon_min=None, lon_max=None)[source]
Parse GEBCO NetCDF file and extract region.
- Parameters:
filepath (Path) – Path to GEBCO NetCDF file.
lat_min (float, optional) – Minimum latitude in radians (default: -90°).
lat_max (float, optional) – Maximum latitude in radians (default: +90°).
lon_min (float, optional) – Minimum longitude in radians (default: -180°).
lon_max (float, optional) – Maximum longitude in radians (default: +180°).
- Returns:
data (ndarray) – Elevation data array.
lat_min_actual (float) – Actual minimum latitude of extracted region.
lat_max_actual (float) – Actual maximum latitude of extracted region.
lon_min_actual (float) – Actual minimum longitude of extracted region.
lon_max_actual (float) – Actual maximum longitude of extracted region.
- Return type:
tuple[ndarray[tuple[Any, …], dtype[floating]], float, float, float, float]
- pytcl.terrain.loaders.parse_earth2014_binary(filepath, layer, lat_min=None, lat_max=None, lon_min=None, lon_max=None)[source]
Parse Earth2014 binary file and extract region.
Earth2014 files are stored as int16 big-endian binary data, organized as rows from south to north, columns from west to east.
- Parameters:
filepath (Path) – Path to Earth2014 binary file.
layer (str) – Layer type (SUR, BED, TBI, RET, ICE).
lat_min (float, optional) – Minimum latitude in radians (default: -90°).
lat_max (float, optional) – Maximum latitude in radians (default: +90°).
lon_min (float, optional) – Minimum longitude in radians (default: -180°).
lon_max (float, optional) – Maximum longitude in radians (default: +180°).
- Returns:
data (ndarray) – Elevation data array in meters.
lat_min_actual (float) – Actual minimum latitude of extracted region.
lat_max_actual (float) – Actual maximum latitude of extracted region.
lon_min_actual (float) – Actual minimum longitude of extracted region.
lon_max_actual (float) – Actual maximum longitude of extracted region.
- Return type:
tuple[ndarray[tuple[Any, …], dtype[floating]], float, float, float, float]
Visibility Analysis
Line-of-sight and viewshed computation.
Terrain Visibility and Masking Functions.
This module provides functions for computing terrain visibility and masking, including: - Line-of-sight (LOS) analysis between points - Viewshed computation (visible area from a point) - Terrain masking for radar/sensor coverage - Horizon computation
These functions are essential for: - Radar coverage analysis - Communication link budget calculations - Sensor placement optimization - Target detection range estimation
References
Wang, J., Robinson, G.J., and White, K. “A fast solution to local viewshed computation using grid-based digital elevation models.” Photogrammetric Engineering & Remote Sensing 62.10 (1996): 1157-1164.
De Floriani, L. and Magillo, P. “Algorithms for visibility computation on terrains: a survey.” Environment and Planning B 30.5 (2003): 709-728.
- class pytcl.terrain.visibility.LOSResult(visible, grazing_angle, obstacle_distance, obstacle_elevation, clearance)[source]
Bases:
NamedTupleLine-of-sight analysis result.
- Parameters:
visible (bool) – Whether there is unobstructed line of sight between observer and target.
grazing_angle (float) – Grazing angle to terrain in radians (angle above/below obstacle). Positive means clearing terrain, negative means blocked.
obstacle_distance (float) – Distance to the blocking obstacle in meters (if blocked). 0 if line of sight is clear.
obstacle_elevation (float) – Elevation of blocking obstacle in meters (if blocked).
clearance (float) – Minimum clearance above terrain along path in meters. Negative if blocked.
- class pytcl.terrain.visibility.ViewshedResult(visible, observer_lat, observer_lon, observer_height, lat_min, lat_max, lon_min, lon_max)[source]
Bases:
NamedTupleViewshed computation result.
- Parameters:
visible (ndarray) – 2D boolean array indicating visibility from observer.
observer_lat (float) – Observer latitude in radians.
observer_lon (float) – Observer longitude in radians.
observer_height (float) – Observer height above terrain in meters.
lat_min (float) – Minimum latitude of viewshed grid in radians.
lat_max (float) – Maximum latitude of viewshed grid in radians.
lon_min (float) – Minimum longitude of viewshed grid in radians.
lon_max (float) – Maximum longitude of viewshed grid in radians.
- class pytcl.terrain.visibility.HorizonPoint(azimuth, elevation_angle, distance, terrain_elevation)[source]
Bases:
NamedTuplePoint on the terrain horizon.
- Parameters:
- pytcl.terrain.visibility.line_of_sight(dem, obs_lat, obs_lon, obs_height, tgt_lat, tgt_lon, tgt_height, n_samples=100, earth_radius=6371000.0, refraction_coeff=0.0)[source]
Compute line of sight between observer and target.
- Parameters:
dem (DEMGrid) – Digital elevation model.
obs_lat (float) – Observer latitude in radians.
obs_lon (float) – Observer longitude in radians.
obs_height (float) – Observer height above terrain in meters.
tgt_lat (float) – Target latitude in radians.
tgt_lon (float) – Target longitude in radians.
tgt_height (float) – Target height above terrain in meters.
n_samples (int, optional) – Number of sample points along path. Default is 100.
earth_radius (float, optional) – Earth radius in meters. Default is 6371000.0.
refraction_coeff (float, optional) – Atmospheric refraction coefficient (typically 0.13 for radio). Set to 0 for optical line of sight. Default is 0.0.
- Returns:
Line-of-sight analysis result.
- Return type:
Notes
The refraction coefficient models atmospheric bending of radio waves. A typical value for radio frequencies is 0.13 (4/3 Earth model). For optical line of sight, use 0.
Examples
>>> import numpy as np >>> from pytcl.terrain.dem import create_flat_dem >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), elevation=100) >>> result = line_of_sight( ... dem, ... np.radians(35.3), np.radians(-119.7), 10, ... np.radians(35.7), np.radians(-119.3), 10) >>> result.visible # Clear LOS over flat terrain True
- pytcl.terrain.visibility.viewshed(dem, obs_lat, obs_lon, obs_height, max_range=50000.0, target_height=0.0, n_radials=360, samples_per_radial=100, earth_radius=6371000.0, refraction_coeff=0.0)[source]
Compute viewshed (visible area) from an observer location.
- Parameters:
dem (DEMGrid) – Digital elevation model.
obs_lat (float) – Observer latitude in radians.
obs_lon (float) – Observer longitude in radians.
obs_height (float) – Observer height above terrain in meters.
max_range (float, optional) – Maximum analysis range in meters. Default is 50000.0.
target_height (float, optional) – Target height above terrain in meters. Default is 0.0.
n_radials (int, optional) – Number of radial directions to analyze. Default is 360.
samples_per_radial (int, optional) – Number of samples per radial. Default is 100.
earth_radius (float, optional) – Earth radius in meters. Default is 6371000.0.
refraction_coeff (float, optional) – Atmospheric refraction coefficient. Default is 0.0.
- Returns:
Viewshed computation result with visibility grid.
- Return type:
Examples
>>> import numpy as np >>> from pytcl.terrain.dem import create_flat_dem >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), elevation=100) >>> result = viewshed( ... dem, np.radians(35.5), np.radians(-119.5), 20, ... max_range=10000, n_radials=36, samples_per_radial=10) >>> result.visible.any() # Some cells visible True
- pytcl.terrain.visibility.compute_horizon(dem, obs_lat, obs_lon, obs_height, n_azimuths=360, max_range=50000.0, samples_per_radial=100, earth_radius=6371000.0)[source]
Compute terrain horizon profile from an observer location.
- Parameters:
dem (DEMGrid) – Digital elevation model.
obs_lat (float) – Observer latitude in radians.
obs_lon (float) – Observer longitude in radians.
obs_height (float) – Observer height above terrain in meters.
n_azimuths (int, optional) – Number of azimuth directions. Default is 360.
max_range (float, optional) – Maximum analysis range in meters. Default is 50000.0.
samples_per_radial (int, optional) – Number of samples per radial. Default is 100.
earth_radius (float, optional) – Earth radius in meters. Default is 6371000.0.
- Returns:
Horizon points for each azimuth direction.
- Return type:
list of HorizonPoint
Examples
>>> import numpy as np >>> from pytcl.terrain.dem import create_flat_dem >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), elevation=100) >>> horizon = compute_horizon( ... dem, np.radians(35.5), np.radians(-119.5), 10, ... n_azimuths=8, max_range=10000, samples_per_radial=10) >>> len(horizon) 8
- pytcl.terrain.visibility.terrain_masking_angle(dem, obs_lat, obs_lon, obs_height, azimuth, max_range=50000.0, n_samples=100, earth_radius=6371000.0)[source]
Compute terrain masking angle in a specific direction.
The masking angle is the minimum elevation angle at which a target would be visible (not blocked by terrain).
- Parameters:
dem (DEMGrid) – Digital elevation model.
obs_lat (float) – Observer latitude in radians.
obs_lon (float) – Observer longitude in radians.
obs_height (float) – Observer height above terrain in meters.
azimuth (float) – Azimuth direction in radians (clockwise from north).
max_range (float, optional) – Maximum analysis range in meters. Default is 50000.0.
n_samples (int, optional) – Number of sample points. Default is 100.
earth_radius (float, optional) – Earth radius in meters. Default is 6371000.0.
- Returns:
Masking angle in radians above horizontal.
- Return type:
Examples
>>> import numpy as np >>> from pytcl.terrain.dem import create_flat_dem >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), elevation=100) >>> angle = terrain_masking_angle( ... dem, np.radians(35.5), np.radians(-119.5), 10, azimuth=0) >>> -np.pi/2 <= angle <= np.pi/2 # Valid angle range True
- pytcl.terrain.visibility.radar_coverage_map(dem, radar_lat, radar_lon, radar_height, min_elevation=0.0, max_range=100000.0, target_height=1000.0, n_radials=360, samples_per_radial=200, earth_radius=6371000.0, refraction_coeff=0.13)[source]
Compute radar coverage map accounting for terrain masking.
Similar to viewshed but with radar-specific parameters including minimum elevation angle and atmospheric refraction.
- Parameters:
dem (DEMGrid) – Digital elevation model.
radar_lat (float) – Radar latitude in radians.
radar_lon (float) – Radar longitude in radians.
radar_height (float) – Radar antenna height above terrain in meters.
min_elevation (float, optional) – Minimum radar elevation angle in radians. Default is 0.0.
max_range (float, optional) – Maximum radar range in meters. Default is 100000.0.
target_height (float, optional) – Target altitude above terrain in meters. Default is 1000.0.
n_radials (int, optional) – Number of radial directions. Default is 360.
samples_per_radial (int, optional) – Samples per radial. Default is 200.
earth_radius (float, optional) – Earth radius in meters. Default is 6371000.0.
refraction_coeff (float, optional) – Atmospheric refraction coefficient (0.13 for 4/3 Earth). Default is 0.13.
- Returns:
Radar coverage map.
- Return type:
Examples
>>> import numpy as np >>> from pytcl.terrain.dem import create_flat_dem >>> dem = create_flat_dem( ... np.radians(35), np.radians(36), ... np.radians(-120), np.radians(-119), elevation=100) >>> coverage = radar_coverage_map( ... dem, np.radians(35.5), np.radians(-119.5), 30, ... max_range=20000, n_radials=36, samples_per_radial=20) >>> coverage.visible.any() # Some coverage exists True