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.
54 lines
1.6 KiB
Elixir
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
|