feat(tgu-ops-ui): display real ground track and swath from ballistics service
Replace synthetic orbit calculation (Kepler + spacecraft ID hash) with real
data from pcp-ballistics-service via a new proxy endpoint in pcp-ui-service.
Backend:
- TguPlanningController: add GET /api/tgu-planning/spacecraft/{noradId}/flight-line
that proxies FlightLineDTO[] from BallisticsService for a given time interval
Frontend:
- Fetch FlightLineDTO[] (revolution, time, lat/long, swath boundaries) using
the spacecraft's noradId for the current 24h map window
- Group points by real revolution number to build OrbitPassInfo[] with actual
orbit numbers and UTC times instead of synthetic Kepler-based estimates
- Build ground track MapLines and swath MapPolygons from real coordinates;
swath polygon = outer-left edge forward + outer-right edge reversed
- Enable swath layer in the editor map; draw with teal fill/stroke matching
the track colour
- MapPassSelector now shows real revolution numbers in header and handle labels
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import type { MapProjection } from "../geometry/mapProjection";
|
||||
import type { MapPolygon } from "../model/mapTypes";
|
||||
import { drawPlanWorks } from "./drawPlanWorks";
|
||||
|
||||
export function drawSwath(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
@@ -8,6 +7,24 @@ export function drawSwath(
|
||||
projection: MapProjection,
|
||||
viewport: { width: number; height: number }
|
||||
) {
|
||||
drawPlanWorks(ctx, polygons, projection, viewport);
|
||||
ctx.save();
|
||||
ctx.fillStyle = "rgba(104, 195, 189, 0.10)";
|
||||
ctx.strokeStyle = "rgba(104, 195, 189, 0.40)";
|
||||
ctx.lineWidth = 1;
|
||||
for (const polygon of polygons) {
|
||||
if (polygon.points.length < 3) continue;
|
||||
const screenPoints = polygon.points.map((point) => projection.project(point));
|
||||
if (!screenPoints.some((p) => p.x >= -32 && p.x <= viewport.width + 32 && p.y >= -32 && p.y <= viewport.height + 32)) {
|
||||
continue;
|
||||
}
|
||||
ctx.beginPath();
|
||||
screenPoints.forEach((p, i) => {
|
||||
if (i === 0) ctx.moveTo(p.x, p.y);
|
||||
else ctx.lineTo(p.x, p.y);
|
||||
});
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,31 @@ export function nearestStation(point: GroundPoint, stations: GroundStationPoint[
|
||||
return bestStation;
|
||||
}
|
||||
|
||||
export type OrbitPassInfo = {
|
||||
index: number;
|
||||
startMs: number;
|
||||
endMs: number;
|
||||
revolution?: number;
|
||||
};
|
||||
|
||||
export function orbitPeriodMs(orbit: PassOrbit): number {
|
||||
return orbitParams(orbit).periodSeconds * 1000;
|
||||
}
|
||||
|
||||
export function orbitPassList(orbit: PassOrbit, range: PassRange): OrbitPassInfo[] {
|
||||
if (range.toMs <= range.fromMs) return [];
|
||||
const periodMs = orbitPeriodMs(orbit);
|
||||
const passes: OrbitPassInfo[] = [];
|
||||
let t = range.fromMs;
|
||||
let index = 1;
|
||||
while (t < range.toMs) {
|
||||
passes.push({ index, startMs: t, endMs: Math.min(t + periodMs, range.toMs) });
|
||||
t += periodMs;
|
||||
index++;
|
||||
}
|
||||
return passes;
|
||||
}
|
||||
|
||||
export function groundTrack(orbit: PassOrbit, range: PassRange, maxPoints = 240): GroundPoint[][] {
|
||||
if (range.toMs <= range.fromMs) {
|
||||
return [];
|
||||
|
||||
@@ -40,6 +40,7 @@ export type MapLine = {
|
||||
layer: "tracks";
|
||||
points: GeoPoint[];
|
||||
spacecraftId?: string;
|
||||
passIndex?: number;
|
||||
zIndex: number;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user