Fix satellite tracker polar plot and ground track issues

- Fix polar plot trajectory not displaying: backend returns 'el'/'az'
  properties but code expected 'elevation'/'azimuth'. Updated both
  drawPolarPlot() and drawPolarPlotPopout() to handle both formats.
- Fix ground track lines crossing entire map: added antimeridian
  crossing detection to split orbit tracks into segments, preventing
  lines from drawing across the map when crossing 180° longitude.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
James Smith
2025-12-30 15:10:13 +00:00
parent 7c215cf08c
commit 0cf9360fce
+104 -37
View File
@@ -8200,8 +8200,11 @@
ctx.beginPath(); ctx.beginPath();
pass.trajectory.forEach((point, i) => { pass.trajectory.forEach((point, i) => {
const r = radius * (90 - point.elevation) / 90; // Backend returns 'el' and 'az' properties
const rad = point.azimuth * Math.PI / 180; const el = point.el !== undefined ? point.el : point.elevation;
const az = point.az !== undefined ? point.az : point.azimuth;
const r = radius * (90 - el) / 90;
const rad = az * Math.PI / 180;
const x = cx + Math.sin(rad) * r; const x = cx + Math.sin(rad) * r;
const y = cy - Math.cos(rad) * r; const y = cy - Math.cos(rad) * r;
@@ -8212,9 +8215,15 @@
ctx.setLineDash([]); ctx.setLineDash([]);
// Draw max elevation point // Draw max elevation point
const maxPoint = pass.trajectory.reduce((max, p) => p.elevation > max.elevation ? p : max, { elevation: 0 }); const maxPoint = pass.trajectory.reduce((max, p) => {
const maxR = radius * (90 - maxPoint.elevation) / 90; const pEl = p.el !== undefined ? p.el : p.elevation;
const maxRad = maxPoint.azimuth * Math.PI / 180; const maxEl = max.el !== undefined ? max.el : max.elevation;
return pEl > maxEl ? p : max;
}, { el: 0, elevation: 0 });
const maxEl = maxPoint.el !== undefined ? maxPoint.el : maxPoint.elevation;
const maxAz = maxPoint.az !== undefined ? maxPoint.az : maxPoint.azimuth;
const maxR = radius * (90 - maxEl) / 90;
const maxRad = maxAz * Math.PI / 180;
const maxX = cx + Math.sin(maxRad) * maxR; const maxX = cx + Math.sin(maxRad) * maxR;
const maxY = cy - Math.cos(maxRad) * maxR; const maxY = cy - Math.cos(maxRad) * maxR;
@@ -8353,14 +8362,33 @@
if (groundTrackLine) groundTrackMap.removeLayer(groundTrackLine); if (groundTrackLine) groundTrackMap.removeLayer(groundTrackLine);
if (satMarker) groundTrackMap.removeLayer(satMarker); if (satMarker) groundTrackMap.removeLayer(satMarker);
// Draw ground track // Split ground track at antimeridian crossings
const coords = pass.groundTrack.map(p => [p.lat, p.lon]); const segments = [];
groundTrackLine = L.polyline(coords, { let currentSegment = [];
color: pass.color || '#00ff00', for (let i = 0; i < pass.groundTrack.length; i++) {
weight: 2, const p = pass.groundTrack[i];
opacity: 0.8, if (currentSegment.length > 0) {
dashArray: '5, 5' const prevLon = currentSegment[currentSegment.length - 1][1];
}).addTo(groundTrackMap); if (Math.abs(p.lon - prevLon) > 180) {
if (currentSegment.length > 1) segments.push(currentSegment);
currentSegment = [];
}
}
currentSegment.push([p.lat, p.lon]);
}
if (currentSegment.length > 1) segments.push(currentSegment);
// Draw ground track segments
groundTrackLine = L.layerGroup();
segments.forEach(seg => {
L.polyline(seg, {
color: pass.color || '#00ff00',
weight: 2,
opacity: 0.8,
dashArray: '5, 5'
}).addTo(groundTrackLine);
});
groundTrackLine.addTo(groundTrackMap);
// Add current position marker // Add current position marker
if (pass.currentPosition) { if (pass.currentPosition) {
@@ -8382,7 +8410,7 @@
} }
// Fit bounds to show track // Fit bounds to show track
if (coords.length > 0) { if (segments.length > 0) {
groundTrackMap.fitBounds(groundTrackLine.getBounds(), { padding: [20, 20] }); groundTrackMap.fitBounds(groundTrackLine.getBounds(), { padding: [20, 20] });
} }
} }
@@ -8448,31 +8476,61 @@
// Draw full orbit track from position endpoint // Draw full orbit track from position endpoint
if (pos.orbitTrack && pos.orbitTrack.length > 0 && groundTrackMap) { if (pos.orbitTrack && pos.orbitTrack.length > 0 && groundTrackMap) {
// Split into past and future segments // Split into past and future, handling antimeridian crossings
const pastCoords = pos.orbitTrack.filter(p => p.past).map(p => [p.lat, p.lon]); const pastPoints = pos.orbitTrack.filter(p => p.past);
const futureCoords = pos.orbitTrack.filter(p => !p.past).map(p => [p.lat, p.lon]); const futurePoints = pos.orbitTrack.filter(p => !p.past);
// Helper to split coords at antimeridian crossings
function splitAtAntimeridian(points) {
const segments = [];
let currentSegment = [];
for (let i = 0; i < points.length; i++) {
const p = points[i];
if (currentSegment.length > 0) {
const prevLon = currentSegment[currentSegment.length - 1][1];
// If longitude jumps more than 180°, start new segment
if (Math.abs(p.lon - prevLon) > 180) {
if (currentSegment.length > 1) segments.push(currentSegment);
currentSegment = [];
}
}
currentSegment.push([p.lat, p.lon]);
}
if (currentSegment.length > 1) segments.push(currentSegment);
return segments;
}
// Remove old lines // Remove old lines
if (orbitTrackLine) groundTrackMap.removeLayer(orbitTrackLine); if (orbitTrackLine) groundTrackMap.removeLayer(orbitTrackLine);
if (pastOrbitLine) groundTrackMap.removeLayer(pastOrbitLine); if (pastOrbitLine) groundTrackMap.removeLayer(pastOrbitLine);
// Draw past track (dimmer) // Draw past track segments (dimmer)
if (pastCoords.length > 1) { const pastSegments = splitAtAntimeridian(pastPoints);
pastOrbitLine = L.polyline(pastCoords, { if (pastSegments.length > 0) {
color: '#666666', pastOrbitLine = L.layerGroup();
weight: 2, pastSegments.forEach(seg => {
opacity: 0.5, L.polyline(seg, {
dashArray: '3, 6' color: '#666666',
}).addTo(groundTrackMap); weight: 2,
opacity: 0.5,
dashArray: '3, 6'
}).addTo(pastOrbitLine);
});
pastOrbitLine.addTo(groundTrackMap);
} }
// Draw future track (brighter) // Draw future track segments (brighter)
if (futureCoords.length > 1) { const futureSegments = splitAtAntimeridian(futurePoints);
orbitTrackLine = L.polyline(futureCoords, { if (futureSegments.length > 0) {
color: selectedPass.color || '#00ff00', orbitTrackLine = L.layerGroup();
weight: 3, futureSegments.forEach(seg => {
opacity: 0.8 L.polyline(seg, {
}).addTo(groundTrackMap); color: selectedPass.color || '#00ff00',
weight: 3,
opacity: 0.8
}).addTo(orbitTrackLine);
});
orbitTrackLine.addTo(groundTrackMap);
} }
} }
@@ -8813,8 +8871,11 @@
ctx.setLineDash([8, 4]); ctx.setLineDash([8, 4]);
ctx.beginPath(); ctx.beginPath();
pass.trajectory.forEach((point, i) => { pass.trajectory.forEach((point, i) => {
const r = radius * (90 - point.elevation) / 90; // Backend returns 'el' and 'az' properties
const rad = point.azimuth * Math.PI / 180; const el = point.el !== undefined ? point.el : point.elevation;
const az = point.az !== undefined ? point.az : point.azimuth;
const r = radius * (90 - el) / 90;
const rad = az * Math.PI / 180;
const x = cx + Math.sin(rad) * r; const x = cx + Math.sin(rad) * r;
const y = cy - Math.cos(rad) * r; const y = cy - Math.cos(rad) * r;
if (i === 0) ctx.moveTo(x, y); if (i === 0) ctx.moveTo(x, y);
@@ -8823,9 +8884,15 @@
ctx.stroke(); ctx.stroke();
ctx.setLineDash([]); ctx.setLineDash([]);
const maxPoint = pass.trajectory.reduce((max, p) => p.elevation > max.elevation ? p : max, { elevation: 0 }); const maxPoint = pass.trajectory.reduce((max, p) => {
const maxR = radius * (90 - maxPoint.elevation) / 90; const pEl = p.el !== undefined ? p.el : p.elevation;
const maxRad = maxPoint.azimuth * Math.PI / 180; const maxEl = max.el !== undefined ? max.el : max.elevation;
return pEl > maxEl ? p : max;
}, { el: 0, elevation: 0 });
const maxEl = maxPoint.el !== undefined ? maxPoint.el : maxPoint.elevation;
const maxAz = maxPoint.az !== undefined ? maxPoint.az : maxPoint.azimuth;
const maxR = radius * (90 - maxEl) / 90;
const maxRad = maxAz * Math.PI / 180;
ctx.fillStyle = pass.color || '#00ff00'; ctx.fillStyle = pass.color || '#00ff00';
ctx.beginPath(); ctx.beginPath();
ctx.arc(cx + Math.sin(maxRad) * maxR, cy - Math.cos(maxRad) * maxR, 8, 0, Math.PI * 2); ctx.arc(cx + Math.sin(maxRad) * maxR, cy - Math.cos(maxRad) * maxR, 8, 0, Math.PI * 2);