prop/lib/microwaveprop/rover/elevation.ex
Graham McIntire 40cbb40cb1
feat(rover): score per-station terrain clearance from SRTM path samples
For each (rover cell, fixed station) pair, sample SRTM along the
great-circle path and add a per-link dB bonus proportional to the
rover's elevation above the highest intermediate terrain. Rover spots
on hilltops with clear sight to multiple stations now rise to the top.

Also vendor Leaflet's layers control PNGs and copy them under
priv/static/assets/css/images/ on build so /rover stops 404'ing on
layers-2x.png.
2026-04-25 17:29:29 -05:00

54 lines
1.6 KiB
Elixir

defmodule Microwaveprop.Rover.Elevation do
@moduledoc """
Bulk SRTM elevation lookup for a list of `{lat, lon}` points.
Groups requested points by SRTM tile filename so each `.hgt` is opened
exactly once per call, then closed. Cells whose tile is missing or
whose value is the SRTM void sentinel are mapped to `nil`.
"""
alias Microwaveprop.Terrain.Srtm
@samples 3601
@void -32_768
@spec lookup_many([{float(), float()}]) :: %{{float(), float()} => integer() | nil}
def lookup_many([]), do: %{}
def lookup_many(points) when is_list(points) do
tiles_dir = Application.get_env(:microwaveprop, :srtm_tiles_dir, "/data/srtm")
unique = Enum.uniq(points)
grouped = Enum.group_by(unique, fn {lat, lon} -> Srtm.tile_filename(lat, lon) end)
grouped
|> Enum.flat_map(fn {filename, pts} -> read_tile(Path.join(tiles_dir, filename), pts) end)
|> Map.new()
end
defp read_tile(path, pts) do
case :file.open(path, [:read, :binary, :raw]) do
{:ok, fd} ->
try do
Enum.map(pts, fn {lat, lon} = key -> {key, read_point(fd, lat, lon)} end)
after
:file.close(fd)
end
{:error, _} ->
Enum.map(pts, fn key -> {key, nil} end)
end
end
defp read_point(fd, lat, lon) do
row = round((floor(lat) + 1 - lat) * (@samples - 1))
col = round((lon - floor(lon)) * (@samples - 1))
offset = (row * @samples + col) * 2
case :file.pread(fd, offset, 2) do
{:ok, <<elev::signed-big-integer-size(16)>>} when elev == @void -> nil
{:ok, <<elev::signed-big-integer-size(16)>>} -> elev
_ -> nil
end
end
end