- Local prominence: each cell's elevation vs. its 8-neighbor ring (~1.3 km radius) feeds a small additive bonus so broad hilltops rank above isolated SRTM voxels - Road proximity: one Overpass call per Calculate fetches drivable ways inside the bbox; cells beyond ~0.5 km of any road get a light dB penalty, gated off in tests
67 lines
1.9 KiB
Elixir
67 lines
1.9 KiB
Elixir
defmodule Microwaveprop.Rover.Prominence do
|
|
@moduledoc """
|
|
Local terrain prominence per rover cell: how far the cell sits above
|
|
the average elevation of its 8 immediate neighbors at SRTM tile scale.
|
|
|
|
Used by the rover scorer to favor *broad* hilltops over isolated SRTM
|
|
voxels that just happen to be tall — a hill big enough to actually
|
|
park on and run a station, not a tree poking through the data.
|
|
"""
|
|
|
|
alias Microwaveprop.Rover.Elevation
|
|
|
|
@ring_offset_deg 0.012
|
|
@ring_offsets [
|
|
{@ring_offset_deg, 0.0},
|
|
{-@ring_offset_deg, 0.0},
|
|
{0.0, @ring_offset_deg},
|
|
{0.0, -@ring_offset_deg},
|
|
{@ring_offset_deg, @ring_offset_deg},
|
|
{@ring_offset_deg, -@ring_offset_deg},
|
|
{-@ring_offset_deg, @ring_offset_deg},
|
|
{-@ring_offset_deg, -@ring_offset_deg}
|
|
]
|
|
|
|
@type latlon :: {float(), float()}
|
|
|
|
@doc """
|
|
Returns `%{ {lat, lon} => prominence_m | nil }` for each cell.
|
|
|
|
`prominence_m = cell_elev - mean(ring_elevs)`. ~1.3 km ring radius
|
|
matches the scale of the kind of hill a rover wants to park on.
|
|
"""
|
|
@spec prominence_map([map()], keyword()) :: %{latlon() => integer() | nil}
|
|
def prominence_map(cells, opts \\ []) do
|
|
elev_lookup = Keyword.get(opts, :elev_lookup, &Elevation.lookup_many/1)
|
|
|
|
cell_points = Enum.map(cells, fn c -> {c.lat, c.lon} end)
|
|
|
|
ring_points =
|
|
cells
|
|
|> Enum.flat_map(&ring_for/1)
|
|
|> Enum.uniq()
|
|
|
|
elev = elev_lookup.(cell_points ++ ring_points)
|
|
|
|
Map.new(cells, fn cell ->
|
|
key = {cell.lat, cell.lon}
|
|
cell_elev = Map.get(elev, key)
|
|
|
|
ring =
|
|
cell
|
|
|> ring_for()
|
|
|> Enum.map(&Map.get(elev, &1))
|
|
|> Enum.reject(&is_nil/1)
|
|
|
|
prominence =
|
|
if is_integer(cell_elev) and ring != [],
|
|
do: cell_elev - round(Enum.sum(ring) / length(ring))
|
|
|
|
{key, prominence}
|
|
end)
|
|
end
|
|
|
|
defp ring_for(%{lat: lat, lon: lon}) do
|
|
Enum.map(@ring_offsets, fn {dlat, dlon} -> {lat + dlat, lon + dlon} end)
|
|
end
|
|
end
|