From 57f243d5b686e820fdf047dbbe10031ceb7a2672 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Sun, 22 Mar 2026 13:15:49 -0500 Subject: [PATCH] Fix coordinate normalization bug Added proper latitude/longitude normalization to prevent invalid coordinates: - Longitude now wraps to -180 to 180 range (e.g., 199.0 -> -161.0) - Latitude now clamps to -90 to 90 range - Applied normalization before creating geometry in Packet module Fixed 7 existing packets with invalid coordinates: - WB6WWJ-N: 199.0 -> -161.0 - BI7MCK-1: 320.3 -> -39.7 This prevents map display issues and ensures PostGIS spatial queries work correctly. --- lib/aprsme/packet.ex | 8 +++-- .../live/shared/coordinate_utils.ex | 30 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/aprsme/packet.ex b/lib/aprsme/packet.ex index b8eb6d6..d100639 100644 --- a/lib/aprsme/packet.ex +++ b/lib/aprsme/packet.ex @@ -177,8 +177,12 @@ defmodule Aprsme.Packet do defp extract_coordinates_from_data_extended(_), do: {nil, nil} defp create_geometry_from_coordinates(changeset, lat, lon) do - if valid_coordinates?(lat, lon) do - create_and_set_location(changeset, lat, lon) + # Normalize coordinates to valid ranges + normalized_lat = CoordinateUtils.normalize_latitude(lat) + normalized_lon = CoordinateUtils.normalize_longitude(lon) + + if valid_coordinates?(normalized_lat, normalized_lon) do + create_and_set_location(changeset, normalized_lat, normalized_lon) else changeset end diff --git a/lib/aprsme_web/live/shared/coordinate_utils.ex b/lib/aprsme_web/live/shared/coordinate_utils.ex index 57aa710..62ed338 100644 --- a/lib/aprsme_web/live/shared/coordinate_utils.ex +++ b/lib/aprsme_web/live/shared/coordinate_utils.ex @@ -86,6 +86,36 @@ defmodule AprsmeWeb.Live.Shared.CoordinateUtils do def normalize_coordinate(%Decimal{} = decimal), do: Decimal.to_float(decimal) def normalize_coordinate(coord), do: coord + @doc """ + Normalize longitude to the -180 to 180 range. + """ + @spec normalize_longitude(number()) :: number() + def normalize_longitude(lon) when is_number(lon) do + # Wrap longitude to -180 to 180 range + cond do + lon > 180 -> normalize_longitude(lon - 360) + lon < -180 -> normalize_longitude(lon + 360) + true -> lon + end + end + + def normalize_longitude(lon), do: lon + + @doc """ + Clamp latitude to the -90 to 90 range. + Latitude cannot wrap, so we clamp to valid range. + """ + @spec normalize_latitude(number()) :: number() + def normalize_latitude(lat) when is_number(lat) do + cond do + lat > 90 -> 90.0 + lat < -90 -> -90.0 + true -> lat + end + end + + def normalize_latitude(lat), do: lat + @doc """ Calculate distance between two lat/lon points in meters using Haversine formula. """