misc fixes
This commit is contained in:
parent
01ee6eada5
commit
e7214f5064
8 changed files with 197 additions and 153 deletions
137
assets/js/map.ts
137
assets/js/map.ts
|
|
@ -64,6 +64,7 @@ interface MarkerState {
|
|||
historical?: boolean;
|
||||
is_most_recent_for_callsign?: boolean;
|
||||
callsign_group?: string;
|
||||
callsign?: string;
|
||||
}
|
||||
|
||||
interface MapEventData {
|
||||
|
|
@ -503,69 +504,60 @@ let MapAPRSMap = {
|
|||
const incomingCallsign = data.callsign_group || data.callsign || data.id;
|
||||
|
||||
if (incomingCallsign) {
|
||||
// Find existing marker for this callsign - check both live and historical markers
|
||||
let existingMarkerId: string | null = null;
|
||||
let existingCallsign: string | null = null;
|
||||
// Find existing live markers for this callsign and convert them to historical dots
|
||||
const markersToConvert: string[] = [];
|
||||
|
||||
self.markerStates!.forEach((state, id) => {
|
||||
// Extract callsign from various sources
|
||||
const stateCallsign =
|
||||
state.callsign_group ||
|
||||
(typeof id === "string" && !id.startsWith("hist_") ? id : null) ||
|
||||
(typeof id === "string" && id.startsWith("hist_")
|
||||
? id.replace(/^hist_/, "").replace(/_\d+$/, "")
|
||||
: null);
|
||||
|
||||
// Match callsigns (only consider non-historical markers for conversion)
|
||||
if (stateCallsign === incomingCallsign && !state.historical) {
|
||||
existingMarkerId = id;
|
||||
existingCallsign = stateCallsign;
|
||||
const stateCallsign = state.callsign_group || state.callsign;
|
||||
// Only convert non-historical markers for the same callsign, but exclude the incoming packet's ID
|
||||
if (
|
||||
stateCallsign === incomingCallsign &&
|
||||
!state.historical &&
|
||||
String(id) !== String(data.id)
|
||||
) {
|
||||
markersToConvert.push(String(id));
|
||||
}
|
||||
});
|
||||
|
||||
// If we found an existing live marker, convert it to a historical dot
|
||||
if (existingMarkerId && existingCallsign) {
|
||||
const existingMarker = self.markers!.get(existingMarkerId);
|
||||
const existingState = self.markerStates!.get(existingMarkerId);
|
||||
// Convert existing live markers to historical dots by updating their icon
|
||||
markersToConvert.forEach((id) => {
|
||||
const existingMarker = self.markers!.get(id);
|
||||
const existingState = self.markerStates!.get(id);
|
||||
|
||||
if (existingMarker && existingState) {
|
||||
// Create historical dot data from existing marker
|
||||
const historicalData = {
|
||||
id: `hist_${existingCallsign}_${Date.now()}`,
|
||||
// Update the state to historical
|
||||
existingState.historical = true;
|
||||
existingState.is_most_recent_for_callsign = false;
|
||||
|
||||
// Create new historical dot icon
|
||||
const historicalIconData = {
|
||||
id: String(id),
|
||||
lat: existingState.lat,
|
||||
lng: existingState.lng,
|
||||
callsign: existingCallsign,
|
||||
callsign_group: existingCallsign,
|
||||
callsign: existingState.callsign || incomingCallsign,
|
||||
callsign_group: existingState.callsign_group || incomingCallsign,
|
||||
symbol_table_id: existingState.symbol_table,
|
||||
symbol_code: existingState.symbol_code,
|
||||
historical: true,
|
||||
is_most_recent_for_callsign: false,
|
||||
timestamp: data.timestamp
|
||||
? typeof data.timestamp === "string"
|
||||
? new Date(data.timestamp).getTime() - 1000
|
||||
: data.timestamp - 1000
|
||||
: Date.now() - 1000,
|
||||
popup: existingState.popup,
|
||||
};
|
||||
|
||||
// Remove the existing marker (this will also clean up its trail reference)
|
||||
self.removeMarker(existingMarkerId);
|
||||
// Update the marker's icon to show as a dot
|
||||
existingMarker.setIcon(self.createMarkerIcon(historicalIconData));
|
||||
|
||||
// Add the historical dot marker
|
||||
self.addMarker({
|
||||
...historicalData,
|
||||
popup: self.buildPopupContent(historicalData),
|
||||
});
|
||||
// Mark as historical
|
||||
(existingMarker as any)._isHistorical = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add the new marker with proper callsign grouping
|
||||
// Add the new marker as the most recent for this callsign
|
||||
self.addMarker({
|
||||
...data,
|
||||
historical: false,
|
||||
is_most_recent_for_callsign: true,
|
||||
callsign_group: data.callsign_group || data.callsign || incomingCallsign, // Ensure callsign_group is set for trail grouping
|
||||
callsign_group: data.callsign_group || data.callsign || incomingCallsign,
|
||||
popup: self.buildPopupContent(data),
|
||||
openPopup: true,
|
||||
});
|
||||
|
|
@ -655,14 +647,46 @@ let MapAPRSMap = {
|
|||
|
||||
// Handle clearing historical packets
|
||||
self.handleEvent("clear_historical_packets", () => {
|
||||
// Remove all historical markers
|
||||
// Remove only historical markers (preserve live markers and their trails)
|
||||
const markersToRemove: string[] = [];
|
||||
self.markers!.forEach((marker: any, id: any) => {
|
||||
if ((marker as any)._isHistorical) {
|
||||
const markerState = self.markerStates!.get(String(id));
|
||||
// Only remove markers that are explicitly historical
|
||||
if ((marker as any)._isHistorical || (markerState && markerState.historical)) {
|
||||
markersToRemove.push(String(id));
|
||||
}
|
||||
});
|
||||
markersToRemove.forEach((id) => self.removeMarker(id));
|
||||
|
||||
// Remove historical markers without affecting trails of live markers
|
||||
markersToRemove.forEach((id) => {
|
||||
const marker = self.markers!.get(id);
|
||||
const markerState = self.markerStates!.get(id);
|
||||
|
||||
if (marker) {
|
||||
// Remove from map and storage
|
||||
self.markerLayer!.removeLayer(marker);
|
||||
self.markers!.delete(id);
|
||||
self.markerStates!.delete(id);
|
||||
|
||||
// Only remove trail if this was the only marker for this callsign
|
||||
if (self.trailManager && markerState) {
|
||||
const callsign = markerState.callsign_group || markerState.callsign || id;
|
||||
// Check if there are any live markers left for this callsign
|
||||
let hasLiveMarkers = false;
|
||||
self.markerStates!.forEach((state) => {
|
||||
const stateCallsign = state.callsign_group || state.callsign;
|
||||
if (stateCallsign === callsign && !state.historical) {
|
||||
hasLiveMarkers = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Only remove trail if no live markers remain
|
||||
if (!hasLiveMarkers) {
|
||||
self.trailManager.removeTrail(callsign);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Handle bounds-based marker filtering
|
||||
|
|
@ -681,6 +705,11 @@ let MapAPRSMap = {
|
|||
self.handleEvent("clear_and_reload_markers", () => {
|
||||
// This event is just a trigger - the server will handle clearing and adding markers
|
||||
});
|
||||
|
||||
// Handle clear_all_markers event
|
||||
self.handleEvent("clear_all_markers", () => {
|
||||
self.clearAllMarkers();
|
||||
});
|
||||
},
|
||||
|
||||
sendBoundsToServer() {
|
||||
|
|
@ -763,8 +792,8 @@ let MapAPRSMap = {
|
|||
}
|
||||
}
|
||||
|
||||
// Remove existing marker if it exists
|
||||
self.removeMarker(data.id);
|
||||
// Remove existing marker if it exists (without affecting trails since we're just replacing it)
|
||||
self.removeMarkerWithoutTrail(data.id);
|
||||
|
||||
// Create marker - use simple dot for older historical positions, APRS icon for most recent
|
||||
const marker = L.marker([lat, lng], {
|
||||
|
|
@ -934,8 +963,24 @@ let MapAPRSMap = {
|
|||
}
|
||||
});
|
||||
|
||||
// Remove out-of-bounds markers
|
||||
markersToRemove.forEach((id) => self.removeMarker(String(id)));
|
||||
// Remove out-of-bounds markers without affecting trails
|
||||
markersToRemove.forEach((id) => self.removeMarkerWithoutTrail(String(id)));
|
||||
},
|
||||
|
||||
removeMarkerWithoutTrail(id: string | any) {
|
||||
const self = this as unknown as LiveViewHookContext;
|
||||
// Ensure id is a string
|
||||
const markerId = typeof id === "string" ? id : String(id);
|
||||
|
||||
const marker = self.markers!.get(markerId);
|
||||
|
||||
if (marker) {
|
||||
// Remove marker from map but don't touch trails
|
||||
self.markerLayer!.removeLayer(marker);
|
||||
self.markers!.delete(markerId);
|
||||
self.markerStates!.delete(markerId);
|
||||
// Note: We intentionally don't remove trails here
|
||||
}
|
||||
},
|
||||
|
||||
createMarkerIcon(data: MarkerData): L.DivIcon {
|
||||
|
|
|
|||
|
|
@ -394,20 +394,8 @@ defmodule Aprs.Packets do
|
|||
defp filter_by_region(query, _), do: query
|
||||
|
||||
defp filter_by_callsign(query, %{callsign: callsign}) do
|
||||
# Support both exact match and partial match
|
||||
# For exact match, check if callsign contains a hyphen (has SSID)
|
||||
if String.contains?(callsign, "-") do
|
||||
# Exact match for callsign with SSID
|
||||
[base_call, ssid] = String.split(callsign, "-", parts: 2)
|
||||
|
||||
from p in query,
|
||||
where:
|
||||
ilike(p.base_callsign, ^base_call) and
|
||||
((is_nil(p.ssid) and ^ssid == "0") or p.ssid == ^ssid)
|
||||
else
|
||||
# Match base callsign exactly, regardless of SSID
|
||||
from p in query, where: ilike(p.base_callsign, ^callsign)
|
||||
end
|
||||
# Use sender field for exact callsign matching
|
||||
from p in query, where: ilike(p.sender, ^callsign)
|
||||
end
|
||||
|
||||
defp filter_by_callsign(query, _), do: query
|
||||
|
|
|
|||
|
|
@ -26,13 +26,8 @@ defmodule AprsWeb.MapLive.CallsignView do
|
|||
page_title: "APRS Map - #{normalized_callsign}",
|
||||
# Track visible packets by callsign
|
||||
visible_packets: %{},
|
||||
# Default bounds - will be updated based on packet locations
|
||||
map_bounds: %{
|
||||
north: 49.0,
|
||||
south: 24.0,
|
||||
east: -66.0,
|
||||
west: -125.0
|
||||
},
|
||||
# Map bounds - will be set when first bounds update is received
|
||||
map_bounds: nil,
|
||||
map_center: @default_center,
|
||||
map_zoom: @default_zoom,
|
||||
default_center: @default_center,
|
||||
|
|
@ -62,7 +57,9 @@ defmodule AprsWeb.MapLive.CallsignView do
|
|||
packets_loaded: false,
|
||||
# Latest symbol table ID and code
|
||||
latest_symbol_table_id: "/",
|
||||
latest_symbol_code: ">"
|
||||
latest_symbol_code: ">",
|
||||
# Flag to track if latest marker was already pushed
|
||||
latest_marker_pushed: false
|
||||
)
|
||||
|
||||
if connected?(socket) do
|
||||
|
|
@ -164,14 +161,7 @@ defmodule AprsWeb.MapLive.CallsignView do
|
|||
|> assign(packets_loaded: true)
|
||||
end
|
||||
|
||||
# Auto-start replay if it hasn't been started yet
|
||||
socket =
|
||||
if socket.assigns.replay_started do
|
||||
socket
|
||||
else
|
||||
socket = start_historical_replay(socket)
|
||||
assign(socket, replay_started: true, replay_active: true)
|
||||
end
|
||||
# Don't start historical replay yet - wait for bounds to be available
|
||||
|
||||
# If we have a pending geolocation, zoom to it after a delay
|
||||
socket =
|
||||
|
|
@ -208,21 +198,32 @@ defmodule AprsWeb.MapLive.CallsignView do
|
|||
|> Enum.filter(fn {_k, packet} -> MapHelpers.within_bounds?(packet, normalized_bounds) end)
|
||||
|> Map.new()
|
||||
|
||||
markers_to_remove =
|
||||
packets_to_remove =
|
||||
socket.assigns.visible_packets
|
||||
|> Enum.reject(fn {_k, packet} -> MapHelpers.within_bounds?(packet, normalized_bounds) end)
|
||||
|> Enum.map(fn {k, _} -> k end)
|
||||
|
||||
socket =
|
||||
if markers_to_remove == [] do
|
||||
if packets_to_remove == [] do
|
||||
socket
|
||||
else
|
||||
Enum.reduce(markers_to_remove, socket, fn k, acc ->
|
||||
Enum.reduce(packets_to_remove, socket, fn k, acc ->
|
||||
push_event(acc, "remove_marker", %{id: k})
|
||||
end)
|
||||
end
|
||||
|
||||
socket = assign(socket, map_bounds: normalized_bounds, visible_packets: new_visible_packets)
|
||||
|
||||
# Start historical replay now that we have bounds (if not already started)
|
||||
socket =
|
||||
if socket.assigns.map_ready and not socket.assigns.replay_started and
|
||||
not is_nil(socket.assigns.map_bounds) do
|
||||
socket = start_historical_replay(socket)
|
||||
assign(socket, replay_started: true, replay_active: true)
|
||||
else
|
||||
socket
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
|
|
@ -249,11 +250,12 @@ defmodule AprsWeb.MapLive.CallsignView do
|
|||
end
|
||||
|
||||
def handle_info(:auto_start_replay, socket) do
|
||||
if not socket.assigns.replay_started and socket.assigns.map_ready do
|
||||
if not socket.assigns.replay_started and socket.assigns.map_ready and
|
||||
not is_nil(socket.assigns.map_bounds) do
|
||||
socket = start_historical_replay(socket)
|
||||
{:noreply, assign(socket, replay_started: true, replay_active: true)}
|
||||
else
|
||||
if not socket.assigns.map_ready do
|
||||
if not socket.assigns.map_ready or is_nil(socket.assigns.map_bounds) do
|
||||
Process.send_after(self(), :auto_start_replay, 1000)
|
||||
end
|
||||
|
||||
|
|
@ -266,7 +268,9 @@ defmodule AprsWeb.MapLive.CallsignView do
|
|||
|
||||
def handle_info(%Phoenix.Socket.Broadcast{topic: "aprs_messages", event: "packet", payload: packet}, socket) do
|
||||
# Only process packets for the specific callsign being viewed
|
||||
if PacketUtils.generate_callsign(packet) == socket.assigns.callsign do
|
||||
packet_sender = Map.get(packet, :sender, "")
|
||||
|
||||
if String.upcase(packet_sender) == String.upcase(socket.assigns.callsign) do
|
||||
handle_info_postgres_packet(packet, socket)
|
||||
else
|
||||
{:noreply, socket}
|
||||
|
|
@ -280,11 +284,12 @@ defmodule AprsWeb.MapLive.CallsignView do
|
|||
updated_visible_packets = Map.put(socket.assigns.visible_packets, key, packet)
|
||||
socket = assign(socket, visible_packets: updated_visible_packets)
|
||||
|
||||
packet_data = PacketUtils.build_packet_data(packet)
|
||||
# Live packets are always the most recent for their callsign
|
||||
packet_data = PacketUtils.build_packet_data(packet, true)
|
||||
|
||||
socket =
|
||||
if packet_data,
|
||||
do: push_event(socket, "add_markers", %{markers: [packet_data]}),
|
||||
do: push_event(socket, "new_packet", packet_data),
|
||||
else: socket
|
||||
|
||||
{:noreply, socket}
|
||||
|
|
@ -672,7 +677,8 @@ defmodule AprsWeb.MapLive.CallsignView do
|
|||
fetch_historical_packets_for_callsign(
|
||||
socket.assigns.callsign,
|
||||
one_hour_ago,
|
||||
now
|
||||
now,
|
||||
socket.assigns.map_bounds
|
||||
)
|
||||
|
||||
# Always fetch the latest packet with a position, regardless of age
|
||||
|
|
@ -752,23 +758,23 @@ defmodule AprsWeb.MapLive.CallsignView do
|
|||
|
||||
historical_callsigns = MapSet.new(unique_position_packets, &PacketUtils.generate_callsign/1)
|
||||
|
||||
socket =
|
||||
{socket, latest_marker_pushed} =
|
||||
if latest_packet && !MapSet.member?(historical_callsigns, latest_packet_id) do
|
||||
# Push the latest marker as a regular marker (not historical)
|
||||
packet_data = PacketUtils.build_packet_data(latest_packet)
|
||||
packet_data = PacketUtils.build_packet_data(latest_packet, true)
|
||||
|
||||
if packet_data do
|
||||
push_event(socket, "add_markers", %{markers: [packet_data]})
|
||||
{push_event(socket, "new_packet", packet_data), true}
|
||||
else
|
||||
socket
|
||||
{socket, false}
|
||||
end
|
||||
else
|
||||
socket
|
||||
{socket, false}
|
||||
end
|
||||
|
||||
if Enum.empty?(packet_data_list) do
|
||||
# No historical packets found (but latest marker may have been pushed above)
|
||||
socket
|
||||
assign(socket, latest_marker_pushed: latest_marker_pushed)
|
||||
else
|
||||
# Clear any previous historical packets from the map
|
||||
socket = push_event(socket, "clear_historical_packets", %{})
|
||||
|
|
@ -787,18 +793,28 @@ defmodule AprsWeb.MapLive.CallsignView do
|
|||
replay_packets: [],
|
||||
replay_index: 0,
|
||||
replay_start_time: one_hour_ago,
|
||||
replay_end_time: now
|
||||
replay_end_time: now,
|
||||
latest_marker_pushed: latest_marker_pushed
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_historical_packets_for_callsign(callsign, start_time, end_time) do
|
||||
Packets.get_packets_for_replay(%{
|
||||
defp fetch_historical_packets_for_callsign(callsign, start_time, end_time, bounds) do
|
||||
params = %{
|
||||
callsign: callsign,
|
||||
start_time: start_time,
|
||||
end_time: end_time,
|
||||
limit: 1000
|
||||
})
|
||||
}
|
||||
|
||||
params =
|
||||
if bounds do
|
||||
Map.put(params, :bounds, [bounds.west, bounds.south, bounds.east, bounds.north])
|
||||
else
|
||||
params
|
||||
end
|
||||
|
||||
Packets.get_packets_for_replay(params)
|
||||
end
|
||||
|
||||
# Filter packets to only include those with unique positions (lat/lon changed)
|
||||
|
|
@ -886,7 +902,12 @@ defmodule AprsWeb.MapLive.CallsignView do
|
|||
defp maybe_push_latest_marker(socket, nil), do: socket
|
||||
|
||||
defp maybe_push_latest_marker(socket, packet) do
|
||||
packet_data = PacketUtils.build_packet_data(packet)
|
||||
if packet_data, do: push_event(socket, "add_markers", %{markers: [packet_data]}), else: socket
|
||||
# Only push if we haven't already pushed it during historical replay
|
||||
if Map.get(socket.assigns, :latest_marker_pushed, false) do
|
||||
socket
|
||||
else
|
||||
packet_data = PacketUtils.build_packet_data(packet, true)
|
||||
if packet_data, do: push_event(socket, "new_packet", packet_data), else: socket
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -361,10 +361,10 @@ defmodule AprsWeb.MapLive.Index do
|
|||
end)
|
||||
end
|
||||
|
||||
# Clear existing historical packets
|
||||
socket = push_event(socket, "clear_historical_packets", %{})
|
||||
# Remove only out-of-bounds historical packets instead of clearing all
|
||||
socket = push_event(socket, "filter_markers_by_bounds", %{bounds: map_bounds})
|
||||
|
||||
# Load historical packets for the new bounds
|
||||
# Load additional historical packets for the new bounds if needed
|
||||
socket = load_historical_packets_for_bounds(socket, map_bounds)
|
||||
|
||||
# Update map bounds and visible packets
|
||||
|
|
@ -496,11 +496,13 @@ defmodule AprsWeb.MapLive.Index do
|
|||
else: System.unique_integer([:positive])
|
||||
|
||||
new_visible_packets = Map.put(socket.assigns.visible_packets, callsign_key, packet)
|
||||
marker_data = PacketUtils.build_packet_data(packet)
|
||||
|
||||
# Live packets are always the most recent for their callsign
|
||||
marker_data = PacketUtils.build_packet_data(packet, true)
|
||||
|
||||
socket =
|
||||
if marker_data do
|
||||
push_event(socket, "add_markers", %{markers: [marker_data]})
|
||||
push_event(socket, "new_packet", marker_data)
|
||||
else
|
||||
socket
|
||||
end
|
||||
|
|
@ -965,8 +967,8 @@ defmodule AprsWeb.MapLive.Index do
|
|||
# Clear existing historical packets
|
||||
socket = assign(socket, historical_packets: %{})
|
||||
|
||||
# Clear all markers on the client
|
||||
socket = push_event(socket, "clear_all_markers", %{})
|
||||
# Clear only historical markers on the client (preserve live markers)
|
||||
socket = push_event(socket, "clear_historical_packets", %{})
|
||||
|
||||
# Load historical packets with new time range
|
||||
socket = load_historical_packets_for_bounds(socket, socket.assigns.map_bounds)
|
||||
|
|
|
|||
|
|
@ -81,9 +81,9 @@ defmodule AprsWeb.MapLive.PacketUtils do
|
|||
@spec generate_callsign(map()) :: String.t()
|
||||
def generate_callsign(packet) do
|
||||
base_callsign = get_packet_field(packet, :base_callsign, "")
|
||||
ssid = get_packet_field(packet, :ssid, 0)
|
||||
ssid = get_packet_field(packet, :ssid, nil)
|
||||
|
||||
if ssid != 0 and ssid != "" and ssid != nil do
|
||||
if ssid != nil and ssid != "" do
|
||||
"#{base_callsign}-#{ssid}"
|
||||
else
|
||||
base_callsign
|
||||
|
|
@ -138,15 +138,20 @@ defmodule AprsWeb.MapLive.PacketUtils do
|
|||
Map.get(data_extended, to_string(key)) || "N/A"
|
||||
end
|
||||
|
||||
def build_packet_data(packet) do
|
||||
def build_packet_data(packet, is_most_recent_for_callsign) when is_boolean(is_most_recent_for_callsign) do
|
||||
{lat, lon, data_extended} = MapHelpers.get_coordinates(packet)
|
||||
callsign = generate_callsign(packet)
|
||||
|
||||
if lat && lon && callsign != "" && callsign != nil do
|
||||
build_packet_map(packet, lat, lon, data_extended)
|
||||
packet_data = build_packet_map(packet, lat, lon, data_extended)
|
||||
Map.put(packet_data, "is_most_recent_for_callsign", is_most_recent_for_callsign)
|
||||
end
|
||||
end
|
||||
|
||||
def build_packet_data(packet) do
|
||||
build_packet_data(packet, false)
|
||||
end
|
||||
|
||||
defp build_packet_map(packet, lat, lon, data_extended) do
|
||||
data_extended = data_extended || %{}
|
||||
packet_info = extract_packet_info(packet, data_extended)
|
||||
|
|
@ -157,7 +162,7 @@ defmodule AprsWeb.MapLive.PacketUtils do
|
|||
|
||||
defp extract_packet_info(packet, data_extended) do
|
||||
%{
|
||||
callsign: generate_callsign(packet),
|
||||
callsign: get_packet_field(packet, :sender, ""),
|
||||
symbol_table_id: get_packet_field(packet, :symbol_table_id, "/"),
|
||||
symbol_code: get_packet_field(packet, :symbol_code, ">"),
|
||||
timestamp: get_timestamp(packet),
|
||||
|
|
@ -197,7 +202,7 @@ defmodule AprsWeb.MapLive.PacketUtils do
|
|||
|
||||
"""
|
||||
<div class="aprs-popup">
|
||||
<div class="aprs-callsign"><strong><a href="/map/callsign/#{packet_info.callsign}">#{packet_info.callsign}</a></strong></div>
|
||||
<div class="aprs-callsign"><strong><a href="/#{packet_info.callsign}">#{packet_info.callsign}</a></strong></div>
|
||||
#{comment_html}
|
||||
<div class="aprs-coords">#{Float.round(to_float(lat), 4)}, #{Float.round(to_float(lon), 4)}</div>
|
||||
#{timestamp_html}
|
||||
|
|
@ -205,7 +210,7 @@ defmodule AprsWeb.MapLive.PacketUtils do
|
|||
"""
|
||||
end
|
||||
|
||||
defp build_weather_popup_html(packet, callsign) do
|
||||
defp build_weather_popup_html(packet, sender) do
|
||||
received_at = get_received_at(packet)
|
||||
timestamp_dt = TimeHelpers.to_datetime(received_at)
|
||||
|
||||
|
|
@ -222,7 +227,7 @@ defmodule AprsWeb.MapLive.PacketUtils do
|
|||
end
|
||||
|
||||
"""
|
||||
<strong>#{callsign} - Weather Report</strong>
|
||||
<strong>#{sender} - Weather Report</strong>
|
||||
#{timestamp_html}
|
||||
<hr style="margin-top: 4px; margin-bottom: 4px;">
|
||||
Temperature: #{get_weather_field(packet, :temperature)}°F<br>
|
||||
|
|
@ -236,11 +241,19 @@ defmodule AprsWeb.MapLive.PacketUtils do
|
|||
end
|
||||
|
||||
defp build_packet_result(packet, packet_info, lat, lon, popup) do
|
||||
# Generate unique ID for live packets to prevent conflicts
|
||||
packet_id =
|
||||
if Map.has_key?(packet, :id) do
|
||||
"live_#{packet.id}_#{System.unique_integer([:positive])}"
|
||||
else
|
||||
"live_#{packet_info.callsign}_#{System.unique_integer([:positive])}"
|
||||
end
|
||||
|
||||
%{
|
||||
"id" => packet_info.callsign,
|
||||
"id" => packet_id,
|
||||
"callsign" => packet_info.callsign,
|
||||
"base_callsign" => get_packet_field(packet, :base_callsign, ""),
|
||||
"ssid" => get_packet_field(packet, :ssid, 0),
|
||||
"ssid" => get_packet_field(packet, :ssid, nil),
|
||||
"lat" => to_float(lat),
|
||||
"lng" => to_float(lon),
|
||||
"data_type" => to_string(get_packet_field(packet, :data_type, "unknown")),
|
||||
|
|
|
|||
|
|
@ -95,20 +95,8 @@ defmodule AprsWeb.PacketsLive.CallsignView do
|
|||
order_by: [desc: p.received_at],
|
||||
limit: ^limit
|
||||
|
||||
# Apply callsign filter
|
||||
filtered_query =
|
||||
if String.contains?(callsign, "-") do
|
||||
# Exact match for callsign with SSID
|
||||
[base_call, ssid] = String.split(callsign, "-", parts: 2)
|
||||
|
||||
from p in query,
|
||||
where:
|
||||
ilike(p.base_callsign, ^base_call) and
|
||||
((is_nil(p.ssid) and ^ssid == "0") or p.ssid == ^ssid)
|
||||
else
|
||||
# Match base callsign exactly, regardless of SSID
|
||||
from p in query, where: ilike(p.base_callsign, ^callsign)
|
||||
end
|
||||
# Apply callsign filter using sender field for exact matching
|
||||
filtered_query = from p in query, where: ilike(p.sender, ^callsign)
|
||||
|
||||
filtered_query
|
||||
|> Repo.all()
|
||||
|
|
@ -122,20 +110,15 @@ defmodule AprsWeb.PacketsLive.CallsignView do
|
|||
end
|
||||
|
||||
defp packet_matches_callsign?(packet, target_callsign) do
|
||||
# Check if packet sender or base_callsign matches the target callsign
|
||||
# Supports both exact matches and SSID variants (e.g., "N0CALL" matches "N0CALL-1")
|
||||
# Check exact match for sender field only
|
||||
sender = packet.sender || ""
|
||||
base_callsign = packet.base_callsign || ""
|
||||
|
||||
# Convert to uppercase for case-insensitive comparison
|
||||
sender_upper = String.upcase(sender)
|
||||
base_upper = String.upcase(base_callsign)
|
||||
target_upper = String.upcase(target_callsign)
|
||||
|
||||
# Check exact match for sender or base_callsign
|
||||
# Also check if the sender starts with the target callsign (for SSID variants)
|
||||
sender_upper == target_upper || base_upper == target_upper ||
|
||||
String.starts_with?(sender_upper, target_upper <> "-")
|
||||
# Exact match only
|
||||
sender_upper == target_upper
|
||||
end
|
||||
|
||||
# Helper to get all packets (stored + live) in chronological order
|
||||
|
|
|
|||
|
|
@ -74,19 +74,16 @@ defmodule Parser do
|
|||
ssid =
|
||||
case List.last(callsign_parts) do
|
||||
nil ->
|
||||
0
|
||||
nil
|
||||
|
||||
s when is_binary(s) ->
|
||||
case Integer.parse(s) do
|
||||
{i, _} -> i
|
||||
:error -> 0
|
||||
end
|
||||
s
|
||||
|
||||
i when is_integer(i) ->
|
||||
i
|
||||
to_string(i)
|
||||
|
||||
_ ->
|
||||
0
|
||||
nil
|
||||
end
|
||||
|
||||
{:ok,
|
||||
|
|
@ -1182,17 +1179,12 @@ defmodule Parser do
|
|||
end
|
||||
end
|
||||
|
||||
defp extract_ssid(nil), do: 0
|
||||
defp extract_ssid(nil), do: nil
|
||||
|
||||
defp extract_ssid(s) when is_binary(s) do
|
||||
case Integer.parse(s) do
|
||||
{i, _} -> i
|
||||
:error -> 0
|
||||
end
|
||||
end
|
||||
defp extract_ssid(s) when is_binary(s), do: s
|
||||
|
||||
defp extract_ssid(i) when is_integer(i), do: i
|
||||
defp extract_ssid(_), do: 0
|
||||
defp extract_ssid(i) when is_integer(i), do: to_string(i)
|
||||
defp extract_ssid(_), do: nil
|
||||
|
||||
defp split_path_for_tunnel(path) do
|
||||
split_path(path)
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ defmodule Aprs.Integration.HistoricalPacketsTest do
|
|||
test "only loads packets within map bounds", %{conn: conn, packets: mock_packets} do
|
||||
# Mock to return only packets within the specified bounds
|
||||
# TEST2 is at lat: 38.8, lon: -97.5, which is outside these bounds
|
||||
test1_packets = Enum.filter(mock_packets, fn packet -> packet.base_callsign == "TEST1" end)
|
||||
test1_packets = Enum.filter(mock_packets, fn packet -> packet.sender == "TEST1" end)
|
||||
expect_packets_for_replay_with_bounds(test1_packets)
|
||||
|
||||
{:ok, view, _html} = live(conn, "/")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue