- 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
63 lines
1.5 KiB
Elixir
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
|