From 0aae5f317780559631188ade7cc423288a11ed54 Mon Sep 17 00:00:00 2001 From: accius Date: Mon, 2 Feb 2026 18:43:28 -0500 Subject: [PATCH] lolngitude unwrapping --- src/components/WorldMap.jsx | 92 +++++++++++++------------------------ src/utils/geo.js | 33 ++----------- 2 files changed, 37 insertions(+), 88 deletions(-) diff --git a/src/components/WorldMap.jsx b/src/components/WorldMap.jsx index 0f0cf63..ccec3b9 100644 --- a/src/components/WorldMap.jsx +++ b/src/components/WorldMap.jsx @@ -267,21 +267,16 @@ export const WorldMap = ({ const isHovered = hoveredSpot && hoveredSpot.call === path.dxCall && Math.abs(parseFloat(hoveredSpot.freq) - parseFloat(path.freq)) < 0.01; - // Handle segments - const isSegmented = Array.isArray(pathPoints[0]) && pathPoints[0].length > 0 && Array.isArray(pathPoints[0][0]); - const segments = isSegmented ? pathPoints : [pathPoints]; - - segments.forEach(segment => { - if (segment && Array.isArray(segment) && segment.length > 1) { - const line = L.polyline(segment, { - color: isHovered ? '#ffffff' : color, - weight: isHovered ? 4 : 1.5, - opacity: isHovered ? 1 : 0.5 - }).addTo(map); - if (isHovered) line.bringToFront(); - dxPathsLinesRef.current.push(line); - } - }); + // Handle path rendering (single continuous array, unwrapped across antimeridian) + if (pathPoints && Array.isArray(pathPoints) && pathPoints.length > 1) { + const line = L.polyline(pathPoints, { + color: isHovered ? '#ffffff' : color, + weight: isHovered ? 4 : 1.5, + opacity: isHovered ? 1 : 0.5 + }).addTo(map); + if (isHovered) line.bringToFront(); + dxPathsLinesRef.current.push(line); + } // Add DX marker const dxCircle = L.circleMarker([path.dxLat, path.dxLon], { @@ -358,34 +353,20 @@ export const WorldMap = ({ // Draw orbit track if available if (sat.track && sat.track.length > 1) { - // Split track into segments to handle date line crossing - let segments = []; - let currentSegment = [sat.track[0]]; - - for (let i = 1; i < sat.track.length; i++) { - const prevLon = sat.track[i-1][1]; - const currLon = sat.track[i][1]; - // If longitude jumps more than 180 degrees, start new segment - if (Math.abs(currLon - prevLon) > 180) { - segments.push(currentSegment); - currentSegment = []; - } - currentSegment.push(sat.track[i]); + // Unwrap longitudes for continuous rendering across antimeridian + const unwrapped = sat.track.map(p => [...p]); + for (let i = 1; i < unwrapped.length; i++) { + while (unwrapped[i][1] - unwrapped[i-1][1] > 180) unwrapped[i][1] -= 360; + while (unwrapped[i][1] - unwrapped[i-1][1] < -180) unwrapped[i][1] += 360; } - segments.push(currentSegment); - // Draw each segment - segments.forEach(segment => { - if (segment.length > 1) { - const trackLine = L.polyline(segment, { - color: sat.visible ? satColor : satColorDark, - weight: 2, - opacity: sat.visible ? 0.8 : 0.4, - dashArray: sat.visible ? null : '5, 5' - }).addTo(map); - satTracksRef.current.push(trackLine); - } - }); + const trackLine = L.polyline(unwrapped, { + color: sat.visible ? satColor : satColorDark, + weight: 2, + opacity: sat.visible ? 0.8 : 0.4, + dashArray: sat.visible ? null : '5, 5' + }).addTo(map); + satTracksRef.current.push(trackLine); } // Draw footprint circle if available and satellite is visible @@ -537,25 +518,16 @@ export const WorldMap = ({ 50 ); - // Validate points before creating polyline - // getGreatCirclePoints returns array of segments (each segment is array of [lat,lon]) - if (points && Array.isArray(points) && points.length > 0) { - // Check if it's segmented (array of arrays of points) or flat (single segment) - const isSegmented = Array.isArray(points[0]) && points[0].length > 0 && Array.isArray(points[0][0]); - const segments = isSegmented ? points : [points]; - - segments.forEach(segment => { - if (segment && Array.isArray(segment) && segment.length > 1 && - segment.every(p => Array.isArray(p) && !isNaN(p[0]) && !isNaN(p[1]))) { - const line = L.polyline(segment, { - color: bandColor, - weight: 1.5, - opacity: 0.5, - dashArray: '4, 4' - }).addTo(map); - pskMarkersRef.current.push(line); - } - }); + // Validate points before creating polyline (single continuous array, unwrapped across antimeridian) + if (points && Array.isArray(points) && points.length > 1 && + points.every(p => Array.isArray(p) && !isNaN(p[0]) && !isNaN(p[1]))) { + const line = L.polyline(points, { + color: bandColor, + weight: 1.5, + opacity: 0.5, + dashArray: '4, 4' + }).addTo(map); + pskMarkersRef.current.push(line); } // Add small dot marker at spot location diff --git a/src/utils/geo.js b/src/utils/geo.js index 5b46e7d..8ce24e8 100644 --- a/src/utils/geo.js +++ b/src/utils/geo.js @@ -205,37 +205,14 @@ export const getGreatCirclePoints = (lat1, lon1, lat2, lon2, n = 100) => { rawPoints.push([toDeg(Math.atan2(z, Math.sqrt(x*x+y*y))), toDeg(Math.atan2(y, x))]); } - // Split path at antimeridian crossings for proper Leaflet rendering - const segments = []; - let currentSegment = [rawPoints[0]]; - + // Unwrap longitudes to be continuous (no jumps > 180°) + // This lets Leaflet draw smoothly across the antimeridian and world copies for (let i = 1; i < rawPoints.length; i++) { - const prevLat = rawPoints[i-1][0]; - const prevLon = rawPoints[i-1][1]; - const currLat = rawPoints[i][0]; - const currLon = rawPoints[i][1]; - - // Check if we crossed the antimeridian (lon jumps more than 180°) - if (Math.abs(currLon - prevLon) > 180) { - // Interpolate the crossing point - // Normalize longitudes so the crossing is smooth - const prevLonAdj = prevLon; - const currLonAdj = currLon > prevLon ? currLon - 360 : currLon + 360; - const frac = (prevLon > 0 ? 180 - prevLon : -180 - prevLon) / (currLonAdj - prevLonAdj); - const crossLat = prevLat + frac * (currLat - prevLat); - - // End current segment at the map edge - currentSegment.push([crossLat, prevLon > 0 ? 180 : -180]); - segments.push(currentSegment); - - // Start new segment from the opposite edge - currentSegment = [[crossLat, prevLon > 0 ? -180 : 180]]; - } - currentSegment.push(rawPoints[i]); + while (rawPoints[i][1] - rawPoints[i-1][1] > 180) rawPoints[i][1] -= 360; + while (rawPoints[i][1] - rawPoints[i-1][1] < -180) rawPoints[i][1] += 360; } - segments.push(currentSegment); - return segments; + return rawPoints; }; export default {