Sidereon.Terrain (Sidereon v3.0.0)

Copy Markdown View Source

DTED terrain loading and elevation lookup.

Summary

Types

Horizontal datum a DTED tile's DSI record states: :wgs84, :wgs72, :unstated for a blank or zero-filled field (read as WGS84), or {:other, text} with the field as read.

Why a terrain lookup has no height although the query is valid.

Why a DTED tile could not be read or queried, one tag per core DtedTileError variant with its fields. {:null_posting, fields} is a posting holding the DTED null value, an unknown elevation rather than a height. The UHL metadata refusals (:coordinate_out_of_range, :wrong_hemisphere, :origin_not_whole_degree, :interval_count_mismatch, :profile_longitude_count_mismatch, :unsupported_partial_profile) name the metadata the reader places postings by.

Functions

Open a DTED terrain directory.

Open a DTED terrain directory or raise.

Look up terrain height at {longitude_deg, latitude_deg}.

Look up one ORTHOMETRIC terrain height in meters or raise.

Look up a batch of terrain heights.

Look up a batch of ORTHOMETRIC terrain heights in meters or raise.

Alias for height/4, matching the Rust/Python/WASM height_m name.

Look up terrain height with explicit lookup options.

Load one DTED tile from disk.

Load one DTED tile or raise.

Read the nearest posting elevation from a loaded DTED tile.

Read one ORTHOMETRIC tile posting height in meters or raise.

Return the horizontal datum a loaded DTED tile's DSI record states.

Types

horizontal_datum()

@type horizontal_datum() :: :wgs84 | :wgs72 | :unstated | {:other, String.t()}

Horizontal datum a DTED tile's DSI record states: :wgs84, :wgs72, :unstated for a blank or zero-filled field (read as WGS84), or {:other, text} with the field as read.

interpolation()

@type interpolation() :: :bilinear | :nearest_posting

lookup_error()

@type lookup_error() ::
  {:unknown_terrain_elevation,
   %{
     lat_index: integer(),
     lon_index: integer(),
     latitude_posting: non_neg_integer(),
     longitude_posting: non_neg_integer()
   }}
  | {:non_wgs84_terrain_tile,
     %{lat_index: integer(), lon_index: integer(), datum: horizontal_datum()}}
  | {:missing_terrain_tile, %{lat_index: integer(), lon_index: integer()}}
  | {:terrain_tile,
     %{lat_index: integer(), lon_index: integer(), error: tile_error()}}
  | {:terrain_tile_origin,
     %{
       path: String.t(),
       lat_index: integer(),
       lon_index: integer(),
       origin_latitude: integer(),
       origin_longitude: integer()
     }}
  | {:invalid_input, %{message: String.t()}}
  | {:parse, String.t()}

Why a terrain lookup has no height although the query is valid.

  • {:unknown_terrain_elevation, fields} - the lookup gives nonzero weight to a posting holding the DTED null value (MIL-PRF-89020B 3.11.3.1), and no neighbouring tile knows the height at the same place. fields names the tile (lat_index, lon_index) and the zero-based posting (latitude_posting, longitude_posting).
  • {:non_wgs84_terrain_tile, fields} - the tile states a horizontal datum other than WGS84 (datum), so it does not answer a WGS84 query; no datum transformation is performed.
  • {:missing_terrain_tile, fields} - the store holds no tile for the cell.
  • {:parse, message} - a DTED tile parse failure not represented by the typed :terrain_tile or :terrain_tile_origin cases below.
  • {:terrain_tile, fields} - a stored DTED tile could not be read; fields retains its indices and complete typed :error reason.
  • {:terrain_tile_origin, fields} - a tile's parsed origin disagrees with its indexed origin; fields retains the path and both tile identities.
  • {:invalid_input, fields} - a coordinate or other lookup input was refused; fields.message retains the core reason.

lookup_options()

@type lookup_options() :: keyword() | Sidereon.Terrain.DtedLookupOptions.t()

tile_error()

@type tile_error() ::
  {:io, %{path: String.t(), message: String.t()}}
  | {:too_short, %{path: String.t()}}
  | {:missing_uhl1, %{path: String.t()}}
  | {:invalid_encoding, String.t()}
  | {:invalid_field, String.t()}
  | {:invalid_dimensions,
     %{
       path: String.t(),
       lon_count: non_neg_integer(),
       lat_count: non_neg_integer()
     }}
  | {:truncated,
     %{path: String.t(), actual: non_neg_integer(), expected: non_neg_integer()}}
  | {:outside,
     %{
       longitude: float(),
       latitude: float(),
       origin_longitude: float(),
       origin_latitude: float()
     }}
  | {:posting_index_out_of_bounds,
     %{longitude_index: non_neg_integer(), latitude_index: non_neg_integer()}}
  | {:missing_data_sentinel, %{longitude_index: non_neg_integer()}}
  | {:checksum,
     %{longitude_index: non_neg_integer(), checksum: integer(), sum: integer()}}
  | :empty_coordinate
  | {:invalid_hemisphere, %{hemisphere: String.t()}}
  | {:negative_posting_index, %{index: integer()}}
  | {:coordinate_out_of_range, %{field: String.t(), text: String.t()}}
  | {:wrong_hemisphere,
     %{field: String.t(), hemisphere: String.t(), expected: String.t()}}
  | {:origin_not_whole_degree, %{field: String.t(), text: String.t()}}
  | {:interval_count_mismatch,
     %{
       field: String.t(),
       interval_tenths_arcsec: non_neg_integer(),
       count: non_neg_integer()
     }}
  | {:profile_longitude_count_mismatch,
     %{longitude_index: non_neg_integer(), declared: integer()}}
  | {:unsupported_partial_profile,
     %{longitude_index: non_neg_integer(), first_latitude_index: integer()}}
  | {:null_posting,
     %{longitude_index: non_neg_integer(), latitude_index: non_neg_integer()}}
  | {:other, String.t()}

Why a DTED tile could not be read or queried, one tag per core DtedTileError variant with its fields. {:null_posting, fields} is a posting holding the DTED null value, an unknown elevation rather than a height. The UHL metadata refusals (:coordinate_out_of_range, :wrong_hemisphere, :origin_not_whole_degree, :interval_count_mismatch, :profile_longitude_count_mismatch, :unsupported_partial_profile) name the metadata the reader places postings by.

Functions

dted(root)

@spec dted(String.t()) :: {:ok, Sidereon.Terrain.Dted.t()} | {:error, term()}

Open a DTED terrain directory.

The returned handle loads tiles lazily as lookups request them.

dted!(root)

Open a DTED terrain directory or raise.

height(dted, longitude_deg, latitude_deg, opts \\ [])

@spec height(Sidereon.Terrain.Dted.t(), number(), number(), lookup_options()) ::
  {:ok, float()} | {:error, lookup_error() | atom() | Sidereon.argument_error()}

Look up terrain height at {longitude_deg, latitude_deg}.

Returns {:ok, height_m} in meters above the DTED ORTHOMETRIC vertical datum, or {:error, reason}. Points outside cached DTED coverage return 0.0 from the core terrain model. Longitude is first by design.

A lookup that gives nonzero weight to a null posting returns {:error, {:unknown_terrain_elevation, fields}} unless a neighbouring tile knows the height at the same place, and a tile whose DSI names a horizontal datum other than WGS84 returns {:error, {:non_wgs84_terrain_tile, fields}}; see lookup_error/0. A query the lookup refuses, such as a non-finite coordinate, returns {:error, {:invalid_input, fields}} with the core reason in fields.message.

height!(terrain, longitude_deg, latitude_deg, opts \\ [])

Look up one ORTHOMETRIC terrain height in meters or raise.

height_batch(dted, points, opts \\ [])

@spec height_batch(
  Sidereon.Terrain.Dted.t(),
  [{number(), number()}],
  lookup_options()
) ::
  [ok: float(), error: lookup_error() | atom()] | {:error, term()}

Look up a batch of terrain heights.

points is a list of {longitude_deg, latitude_deg} pairs. The returned list has one {:ok, height_m} or {:error, reason} entry per input, preserving order, with the reasons height/4 returns. Heights are meters above the DTED ORTHOMETRIC vertical datum.

height_batch!(terrain, points, opts \\ [])

Look up a batch of ORTHOMETRIC terrain heights in meters or raise.

height_m(terrain, longitude_deg, latitude_deg, opts \\ [])

@spec height_m(Sidereon.Terrain.Dted.t(), number(), number(), lookup_options()) ::
  {:ok, float()} | {:error, lookup_error() | atom() | Sidereon.argument_error()}

Alias for height/4, matching the Rust/Python/WASM height_m name.

height_m_with_options(terrain, longitude_deg, latitude_deg, opts)

@spec height_m_with_options(
  Sidereon.Terrain.Dted.t(),
  number(),
  number(),
  lookup_options()
) ::
  {:ok, float()} | {:error, lookup_error() | atom() | Sidereon.argument_error()}

Look up terrain height with explicit lookup options.

load_tile(path)

@spec load_tile(String.t()) ::
  {:ok, Sidereon.Terrain.DtedTile.t()} | {:error, tile_error() | term()}

Load one DTED tile from disk.

Returns {:ok, %DtedTile{}} or {:error, reason} with a tile_error/0. A tile is checked against the metadata the reader places postings by: UHL origins must be whole degrees inside their axis with that axis's hemisphere letters, a stated UHL data interval must span one degree over the posting count, and each data record must declare the longitude count of its position and latitude count zero. A tile on any horizontal datum loads; see tile_horizontal_datum/1.

load_tile!(path)

Load one DTED tile or raise.

tile_elevation(dted_tile, longitude_deg, latitude_deg)

@spec tile_elevation(Sidereon.Terrain.DtedTile.t(), number(), number()) ::
  {:ok, integer()} | {:error, tile_error() | Sidereon.argument_error()}

Read the nearest posting elevation from a loaded DTED tile.

Returns an integer height in meters above the DTED ORTHOMETRIC vertical datum. Longitude is first. A posting holding the DTED null value is an unknown elevation, {:error, {:null_posting, %{longitude_index: i, latitude_index: j}}}; a point outside the tile is {:error, {:outside, fields}}.

tile_elevation!(tile, longitude_deg, latitude_deg)

Read one ORTHOMETRIC tile posting height in meters or raise.

tile_horizontal_datum(dted_tile)

@spec tile_horizontal_datum(Sidereon.Terrain.DtedTile.t()) :: horizontal_datum()

Return the horizontal datum a loaded DTED tile's DSI record states.

DtedTerrain lookups answer only from a tile whose datum is :wgs84 or :unstated, since queries are WGS84 positions.