updates
This commit is contained in:
parent
c4bf50284b
commit
f5049a6e2d
4 changed files with 145 additions and 49 deletions
|
|
@ -1748,6 +1748,46 @@ let MapAPRSMap = {
|
|||
// Remove existing marker if it exists (without affecting trails since we're just replacing it)
|
||||
self.removeMarkerWithoutTrail(data.id);
|
||||
|
||||
// When adding a "most recent" marker, convert any existing "current" markers
|
||||
// for the same callsign_group to historical dots. This prevents duplicate labels
|
||||
// when multiple senders report the same object (e.g. two stations tracking one radiosonde).
|
||||
if (data.is_most_recent_for_callsign && self.callsignIndex) {
|
||||
const csGroup = data.callsign_group || data.callsign;
|
||||
if (csGroup) {
|
||||
const existingIds = self.callsignIndex.get(csGroup);
|
||||
if (existingIds) {
|
||||
for (const eid of existingIds) {
|
||||
if (String(eid) === String(data.id)) continue;
|
||||
const eState = self.markerStates.get(eid);
|
||||
const eMarker = self.markers.get(eid);
|
||||
if (
|
||||
eState &&
|
||||
eMarker &&
|
||||
eState.is_most_recent_for_callsign
|
||||
) {
|
||||
eState.historical = true;
|
||||
eState.is_most_recent_for_callsign = false;
|
||||
eMarker.setIcon(
|
||||
self.createMarkerIcon({
|
||||
id: String(eid),
|
||||
lat: eState.lat,
|
||||
lng: eState.lng,
|
||||
callsign: eState.callsign || csGroup,
|
||||
callsign_group: eState.callsign_group || csGroup,
|
||||
symbol_table_id: eState.symbol_table,
|
||||
symbol_code: eState.symbol_code,
|
||||
historical: true,
|
||||
is_most_recent_for_callsign: false,
|
||||
popup: eState.popup,
|
||||
}),
|
||||
);
|
||||
(eMarker as APRSMarker)._isHistorical = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create marker - use simple dot for older historical positions, APRS icon for most recent
|
||||
const marker = L.marker([lat, lng], {
|
||||
icon: self.createMarkerIcon(data),
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ defmodule AprsmeWeb.MapLive.DataBuilder do
|
|||
build_packet_data(packet, is_most_recent, locale)
|
||||
end)
|
||||
|> Enum.filter(& &1)
|
||||
|> deduplicate_by_callsign_group()
|
||||
|
||||
Process.delete(:weather_callsigns_cache)
|
||||
result
|
||||
|
|
@ -111,6 +112,7 @@ defmodule AprsmeWeb.MapLive.DataBuilder do
|
|||
symbol_code = get_packet_field(packet, :symbol_code, ">")
|
||||
|
||||
callsign = display_name(packet)
|
||||
label = map_label(packet)
|
||||
|
||||
# For historical (non-most-recent) packets, use a simple red dot HTML
|
||||
# instead of the full APRS symbol — matches what the JS createMarkerIcon
|
||||
|
|
@ -120,19 +122,16 @@ defmodule AprsmeWeb.MapLive.DataBuilder do
|
|||
AprsmeWeb.SymbolRenderer.render_marker_symbol(
|
||||
symbol_table_id,
|
||||
symbol_code,
|
||||
callsign,
|
||||
label,
|
||||
32
|
||||
)
|
||||
else
|
||||
historical_dot_html(callsign)
|
||||
historical_dot_html(label)
|
||||
end
|
||||
|
||||
raw_comment = get_packet_field(packet, :comment, "")
|
||||
clean_comment = Aprsme.EncodingUtils.sanitize_comment(raw_comment)
|
||||
|
||||
# Use map_label for display, but keep display_name for grouping
|
||||
label = map_label(packet)
|
||||
|
||||
%{
|
||||
"id" => if(is_most_recent, do: "current_#{get_packet_id(packet)}", else: "hist_#{get_packet_id(packet)}"),
|
||||
"lat" => to_float(lat),
|
||||
|
|
@ -229,7 +228,7 @@ defmodule AprsmeWeb.MapLive.DataBuilder do
|
|||
@spec build_simple_popup(map(), boolean()) :: String.t()
|
||||
def build_simple_popup(packet, has_weather) do
|
||||
# Build popup HTML directly without database queries
|
||||
callsign = display_name(packet)
|
||||
callsign = map_label(packet)
|
||||
timestamp_dt = get_packet_received_at(packet)
|
||||
cache_buster = System.system_time(:millisecond)
|
||||
|
||||
|
|
@ -685,4 +684,15 @@ defmodule AprsmeWeb.MapLive.DataBuilder do
|
|||
|> Enum.map(fn packet -> build_minimal_packet_data(packet, false, has_weather) end)
|
||||
|> Enum.filter(& &1)
|
||||
end
|
||||
|
||||
# When multiple senders track the same object, keep only one "current" marker
|
||||
# per callsign_group to avoid duplicate labels on the map.
|
||||
defp deduplicate_by_callsign_group(marker_list) do
|
||||
marker_list
|
||||
|> Enum.reduce(%{}, fn marker, acc ->
|
||||
group = marker["callsign_group"] || marker["callsign"]
|
||||
Map.put(acc, group, marker)
|
||||
end)
|
||||
|> Map.values()
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -849,51 +849,13 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
end
|
||||
|
||||
def handle_info({:packet_batch, packets}, socket) do
|
||||
# Process batch: collect marker data and removal IDs without pushing events
|
||||
{socket, marker_data_list, removal_ids} =
|
||||
Enum.reduce(packets, {socket, [], []}, fn packet, {acc_socket, markers, removals} ->
|
||||
{new_socket, marker_data, removed_id} = process_packet_for_batch(packet, acc_socket)
|
||||
markers = if marker_data, do: [marker_data | markers], else: markers
|
||||
removals = if removed_id, do: [removed_id | removals], else: removals
|
||||
{new_socket, markers, removals}
|
||||
end)
|
||||
{socket, marker_data_list, removal_ids} = process_packet_batch(packets, socket)
|
||||
|
||||
# Push a single batched remove event for all removals
|
||||
socket =
|
||||
if removal_ids == [] do
|
||||
socket
|
||||
else
|
||||
DisplayManager.remove_markers_batch(socket, removal_ids)
|
||||
end
|
||||
|
||||
socket = assign(socket, :last_update_at, DateTime.utc_now())
|
||||
|
||||
# Send heat map update once if in heat map mode, otherwise batch markers
|
||||
socket =
|
||||
cond do
|
||||
socket.assigns.map_zoom <= 8 and marker_data_list != [] ->
|
||||
DisplayManager.send_heat_map_for_current_bounds(socket)
|
||||
|
||||
marker_data_list != [] ->
|
||||
reversed = Enum.reverse(marker_data_list)
|
||||
|
||||
# Extract convert_to_historical callsign keys from markers that replaced existing ones
|
||||
convert_to_historical =
|
||||
reversed
|
||||
|> Enum.map(&Map.get(&1, "convert_from"))
|
||||
|> Enum.filter(& &1)
|
||||
|> Enum.uniq()
|
||||
|
||||
markers = prepare_markers_for_push(reversed, socket.assigns.station_popup_open)
|
||||
|
||||
push_event(socket, "new_packets", %{
|
||||
packets: markers,
|
||||
convert_to_historical: convert_to_historical
|
||||
})
|
||||
|
||||
true ->
|
||||
socket
|
||||
end
|
||||
socket
|
||||
|> remove_markers_if_needed(removal_ids)
|
||||
|> assign(:last_update_at, DateTime.utc_now())
|
||||
|> update_display_for_batch(marker_data_list)
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
|
@ -1100,6 +1062,61 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
end
|
||||
end
|
||||
|
||||
defp process_packet_batch(packets, socket) do
|
||||
Enum.reduce(packets, {socket, [], []}, fn packet, {acc_socket, markers, removals} ->
|
||||
{new_socket, marker_data, removed_id} = process_packet_for_batch(packet, acc_socket)
|
||||
markers = if marker_data, do: [marker_data | markers], else: markers
|
||||
removals = if removed_id, do: [removed_id | removals], else: removals
|
||||
{new_socket, markers, removals}
|
||||
end)
|
||||
end
|
||||
|
||||
defp remove_markers_if_needed(socket, []), do: socket
|
||||
|
||||
defp remove_markers_if_needed(socket, removal_ids) do
|
||||
DisplayManager.remove_markers_batch(socket, removal_ids)
|
||||
end
|
||||
|
||||
defp update_display_for_batch(socket, []), do: socket
|
||||
|
||||
defp update_display_for_batch(socket, marker_data_list) do
|
||||
if socket.assigns.map_zoom <= 8 do
|
||||
DisplayManager.send_heat_map_for_current_bounds(socket)
|
||||
else
|
||||
send_marker_batch(socket, marker_data_list)
|
||||
end
|
||||
end
|
||||
|
||||
defp send_marker_batch(socket, marker_data_list) do
|
||||
reversed = Enum.reverse(marker_data_list)
|
||||
|
||||
# Deduplicate by callsign_group — when multiple senders track the same
|
||||
# object (e.g. two stations reporting the same radiosonde), keep only
|
||||
# the most recent packet per group to avoid duplicate "current" markers.
|
||||
deduped =
|
||||
reversed
|
||||
|> Enum.reduce(%{}, fn marker, acc ->
|
||||
group = marker["callsign_group"] || marker["callsign"]
|
||||
Map.put(acc, group, marker)
|
||||
end)
|
||||
|> Map.values()
|
||||
|
||||
# Send callsign_groups so the frontend can look up existing markers
|
||||
# via its callsignIndex (keyed by callsign_group, not by packet UUID)
|
||||
convert_to_historical =
|
||||
deduped
|
||||
|> Enum.map(fn m -> m["callsign_group"] || m["callsign"] end)
|
||||
|> Enum.filter(& &1)
|
||||
|> Enum.uniq()
|
||||
|
||||
markers = prepare_markers_for_push(deduped, socket.assigns.station_popup_open)
|
||||
|
||||
push_event(socket, "new_packets", %{
|
||||
packets: markers,
|
||||
convert_to_historical: convert_to_historical
|
||||
})
|
||||
end
|
||||
|
||||
# Handle replaying the next historical packet
|
||||
|
||||
@impl true
|
||||
|
|
|
|||
|
|
@ -143,6 +143,9 @@ defmodule AprsmeWeb.MapLive.DataBuilderTest do
|
|||
assert result["callsign"] == "DB0SDA"
|
||||
# But grouping uses object name
|
||||
assert result["callsign_group"] == "P-K5SGD"
|
||||
# Symbol HTML should show sender, not object name
|
||||
assert result["symbol_html"] =~ "DB0SDA"
|
||||
refute result["symbol_html"] =~ "P-K5SGD"
|
||||
end
|
||||
|
||||
test "build_minimal_packet_data uses red dot HTML for historical (non-most-recent) packets" do
|
||||
|
|
@ -343,5 +346,31 @@ defmodule AprsmeWeb.MapLive.DataBuilderTest do
|
|||
groups = Enum.map(results, & &1["callsign_group"])
|
||||
assert Enum.all?(groups, &(&1 == "P-K5SGD"))
|
||||
end
|
||||
|
||||
test "build_simple_popup shows sender for object packets" do
|
||||
packet = %{
|
||||
id: Ecto.UUID.generate(),
|
||||
sender: "WA0YMH-2",
|
||||
base_callsign: "WA0YMH",
|
||||
ssid: "2",
|
||||
object_name: "X3234025",
|
||||
is_object: true,
|
||||
is_item: false,
|
||||
lat: 33.251833,
|
||||
lon: -95.719833,
|
||||
data_type: "object",
|
||||
received_at: DateTime.utc_now(),
|
||||
symbol_table_id: "/",
|
||||
symbol_code: "O",
|
||||
comment: "Clb=5.9m/s t=-58.5C 404.800MHz Type=RS41-NG rdzTTGOsonde",
|
||||
has_position: true,
|
||||
path: "TCPIP*,qAR,WA0YMH-2"
|
||||
}
|
||||
|
||||
popup = DataBuilder.build_simple_popup(packet, false)
|
||||
|
||||
assert popup =~ "WA0YMH-2"
|
||||
refute popup =~ "X3234025"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue