mirror of
https://github.com/smittix/intercept.git
synced 2026-07-01 14:28:59 -07:00
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:
+104
-37
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user