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:
Дмитрий Соловьев
2026-05-31 16:05:41 +03:00
parent 637a5e8848
commit 4ab765b0ee
7 changed files with 339 additions and 36 deletions
@@ -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 [];