prop/lib/microwaveprop_web/live/contact_map_live.ex
Graham McIntire cd8d7a0f52
Add contact detail map, elevation profile chart, and UI improvements
- Leaflet map on contact detail pages showing both station locations
- SRTM elevation profile with Chart.js: terrain, LOS, Fresnel zone
- Path info: distance, azimuth, elevation angle in both directions
- Compact 4-column grid layout for contact details
- Contact map page with all contacts plotted
- Rename QSOs to Contacts in page header
- Wrap algo page in standard layout
2026-04-01 12:34:03 -05:00

63 lines
1.5 KiB
Elixir

defmodule MicrowavepropWeb.ContactMapLive do
@moduledoc false
use MicrowavepropWeb, :live_view
import Ecto.Query
alias Microwaveprop.Radio.Contact
alias Microwaveprop.Repo
@impl true
def mount(_params, _session, socket) do
contacts = load_contacts()
{:ok,
assign(socket,
page_title: "Contact Map",
contacts_json: Jason.encode!(contacts),
contact_count: length(contacts)
)}
end
defp load_contacts do
from(c in Contact,
where: not is_nil(c.pos1) and not is_nil(c.pos2),
select: %{
pos1: c.pos1,
pos2: c.pos2,
band: c.band
}
)
|> Repo.all()
|> Enum.map(fn c ->
lat1 = c.pos1["lat"]
lon1 = c.pos1["lon"] || c.pos1["lng"]
lat2 = c.pos2["lat"]
lon2 = c.pos2["lon"] || c.pos2["lng"]
band = if c.band, do: Decimal.to_integer(c.band), else: 0
if lat1 && lon1 && lat2 && lon2 do
[lat1, lon1, lat2, lon2, band]
end
end)
|> Enum.reject(&is_nil/1)
end
@impl true
def render(assigns) do
~H"""
<div
id="contacts-map"
phx-hook="ContactsMap"
phx-update="ignore"
class="fixed inset-0 z-0"
data-contacts={@contacts_json}
>
</div>
<div class="fixed top-3 left-14 z-[1000] bg-base-100/90 shadow rounded-box border border-base-300 px-3 py-2">
<div class="font-bold text-sm">Contact Map</div>
<div class="text-xs opacity-70">{Integer.to_string(@contact_count)} contacts</div>
</div>
"""
end
end