@zendir/ui 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/index.js +0 -137
  2. package/dist/index.js.map +1 -1
  3. package/dist/react/context/DisplaySettingsContext.js +0 -12
  4. package/dist/react/context/DisplaySettingsContext.js.map +1 -1
  5. package/dist/react/index.d.ts +0 -30
  6. package/dist/react/utils/index.js +0 -8
  7. package/dist/react/utils/index.js.map +1 -1
  8. package/dist/react.js +0 -137
  9. package/dist/react.js.map +1 -1
  10. package/package.json +1 -1
  11. package/dist/react/3d/EarthViewer.js +0 -836
  12. package/dist/react/3d/EarthViewer.js.map +0 -1
  13. package/dist/react/3d/SolarSystemViewer.js +0 -372
  14. package/dist/react/3d/SolarSystemViewer.js.map +0 -1
  15. package/dist/react/3d/ZenSpace3D.js +0 -1253
  16. package/dist/react/3d/ZenSpace3D.js.map +0 -1
  17. package/dist/react/3d/ZenSpace3DCesium.js +0 -186
  18. package/dist/react/3d/ZenSpace3DCesium.js.map +0 -1
  19. package/dist/react/3d/ZenSpace3DShaders.js +0 -94
  20. package/dist/react/3d/ZenSpace3DShaders.js.map +0 -1
  21. package/dist/react/3d/ZenSpace3DUtils.js +0 -213
  22. package/dist/react/3d/ZenSpace3DUtils.js.map +0 -1
  23. package/dist/react/3d/threeLoader.js +0 -18
  24. package/dist/react/3d/threeLoader.js.map +0 -1
  25. package/dist/react/cards/AccessCard.js +0 -410
  26. package/dist/react/cards/AccessCard.js.map +0 -1
  27. package/dist/react/cards/OrbitCard.js +0 -372
  28. package/dist/react/cards/OrbitCard.js.map +0 -1
  29. package/dist/react/cards/SpacecraftCard.js +0 -941
  30. package/dist/react/cards/SpacecraftCard.js.map +0 -1
  31. package/dist/react/cards/TelemetryCard.js +0 -742
  32. package/dist/react/cards/TelemetryCard.js.map +0 -1
  33. package/dist/react/cards/TelemetryStreamCard.js +0 -309
  34. package/dist/react/cards/TelemetryStreamCard.js.map +0 -1
  35. package/dist/react/charts/GroundTrackMap.js +0 -1123
  36. package/dist/react/charts/GroundTrackMap.js.map +0 -1
  37. package/dist/react/charts/GroundTrackMapLeaflet.js +0 -571
  38. package/dist/react/charts/GroundTrackMapLeaflet.js.map +0 -1
  39. package/dist/react/charts/groundTrackMapLeafletTiles.js +0 -11
  40. package/dist/react/charts/groundTrackMapLeafletTiles.js.map +0 -1
  41. package/dist/react/charts/groundTrackMapLeafletUtils.js +0 -109
  42. package/dist/react/charts/groundTrackMapLeafletUtils.js.map +0 -1
  43. package/dist/react/charts/unified/AstroChart.js +0 -1405
  44. package/dist/react/charts/unified/AstroChart.js.map +0 -1
  45. package/dist/react/charts/unified/PowerOverviewChart.js +0 -488
  46. package/dist/react/charts/unified/PowerOverviewChart.js.map +0 -1
  47. package/dist/react/charts/unified/domain.js +0 -3168
  48. package/dist/react/charts/unified/domain.js.map +0 -1
  49. package/dist/react/charts/unified/generators.js +0 -518
  50. package/dist/react/charts/unified/generators.js.map +0 -1
  51. package/dist/react/charts/unified/presets.js +0 -999
  52. package/dist/react/charts/unified/presets.js.map +0 -1
  53. package/dist/react/charts/unified/sync.js +0 -219
  54. package/dist/react/charts/unified/sync.js.map +0 -1
  55. package/dist/react/charts/unified/theme.js +0 -562
  56. package/dist/react/charts/unified/theme.js.map +0 -1
  57. package/dist/react/charts/unified/useChartStream.js +0 -226
  58. package/dist/react/charts/unified/useChartStream.js.map +0 -1
  59. package/dist/react/visualizations/EclipseTimerCard.js +0 -250
  60. package/dist/react/visualizations/EclipseTimerCard.js.map +0 -1
  61. package/dist/react/visualizations/LinkBudgetCard.js +0 -444
  62. package/dist/react/visualizations/LinkBudgetCard.js.map +0 -1
  63. package/dist/react/visualizations/NavBallCard.js +0 -243
  64. package/dist/react/visualizations/NavBallCard.js.map +0 -1
  65. package/dist/react/visualizations/PropulsionCard.js +0 -298
  66. package/dist/react/visualizations/PropulsionCard.js.map +0 -1
  67. package/dist/react/visualizations/SensorFootprintCard.js +0 -326
  68. package/dist/react/visualizations/SensorFootprintCard.js.map +0 -1
  69. package/dist/react/visualizations/ThermalHeatmapCard.js +0 -372
  70. package/dist/react/visualizations/ThermalHeatmapCard.js.map +0 -1
  71. package/dist/shaders/atmosphere.frag.js +0 -5
  72. package/dist/shaders/atmosphere.frag.js.map +0 -1
  73. package/dist/shaders/atmosphere.vert.js +0 -5
  74. package/dist/shaders/atmosphere.vert.js.map +0 -1
  75. package/dist/shaders/stars.frag.js +0 -5
  76. package/dist/shaders/stars.frag.js.map +0 -1
  77. package/dist/shaders/stars.vert.js +0 -5
  78. package/dist/shaders/stars.vert.js.map +0 -1
  79. package/dist/style.css +0 -143
@@ -1,1123 +0,0 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import { useRef, useState, useMemo, useEffect, useCallback } from "react";
3
- import { AstroIcon } from "../core/AstroIcon.js";
4
- import { useTheme } from "../theme/ThemeProvider.js";
5
- const STATUS_COLOR_MAP = {
6
- normal: "#56f000",
7
- // Green — Astro UXDS "Normal"
8
- standby: "#2dccff",
9
- // Cyan — Astro UXDS "Standby"
10
- caution: "#fce83a",
11
- // Yellow — Astro UXDS "Caution"
12
- serious: "#ffb302",
13
- // Orange — Astro UXDS "Serious"
14
- critical: "#ff3838",
15
- // Red — Astro UXDS "Critical"
16
- off: "#a4abb6"
17
- // Grey — Astro UXDS "Off"
18
- };
19
- const DEFAULT_TRACK_COLORS = [
20
- "#2dccff",
21
- "#3E3CFF",
22
- "#9D70FF",
23
- "#56f000",
24
- "#fce83a",
25
- "#ff7849",
26
- "#2dd4bf",
27
- "#ff3838"
28
- ];
29
- const WORLD_MAP_PATHS = [
30
- // North America
31
- "M-168,70 L-168,60 L-145,63 L-140,58 L-130,55 L-125,49 L-122,48 L-117,33 L-115,28 L-105,22 L-97,18 L-88,20 L-82,25 L-82,30 L-80,32 L-67,47 L-60,46 L-55,52 L-56,60 L-65,70 L-85,73 L-100,72 L-130,70 L-168,70",
32
- // South America
33
- "M-82,12 L-77,8 L-73,11 L-65,5 L-60,5 L-50,0 L-45,-5 L-43,-10 L-39,-15 L-38,-22 L-40,-25 L-43,-30 L-48,-33 L-50,-40 L-55,-48 L-57,-52 L-68,-55 L-73,-50 L-78,-42 L-80,-20 L-78,-5 L-82,12",
34
- // Europe + Africa
35
- "M-10,72 L-5,60 L-5,48 L0,44 L3,44 L5,48 L8,44 L3,37 L-5,35 L-17,28 L-17,15 L-8,10 L-4,5 L5,5 L10,2 L15,-3 L22,-12 L28,-22 L33,-30 L30,-35 L22,-35 L18,-27 L12,-20 L8,-10 L3,0 L-5,10 L-12,15 L-15,20 L-13,32 L-10,38 L0,48 L8,55 L15,58 L20,60 L25,65 L30,70 L-10,72",
36
- // Asia
37
- "M30,70 L40,65 L50,60 L60,55 L70,50 L80,45 L90,45 L100,40 L105,35 L110,30 L115,25 L120,30 L125,35 L130,40 L135,45 L140,50 L145,55 L150,60 L155,65 L160,68 L170,70 L180,70 L180,72 L30,72 L30,70",
38
- // Australia
39
- "M112,-12 L118,-14 L125,-15 L132,-13 L138,-15 L143,-17 L148,-20 L152,-23 L153,-28 L150,-33 L147,-37 L140,-38 L134,-36 L130,-33 L126,-30 L120,-30 L116,-28 L114,-24 L113,-18 L112,-12",
40
- // Antarctica
41
- "M-180,-72 L180,-72 L180,-90 L-180,-90 Z"
42
- ];
43
- function drawCoverageEllipse(ctx, centerLat, centerLon, radiusDeg, W, H, lonToX, latToY, numSegments = 72) {
44
- const lat0 = centerLat * Math.PI / 180;
45
- const lon0 = centerLon * Math.PI / 180;
46
- const d = radiusDeg * Math.PI / 180;
47
- const cx = lonToX(centerLon, W);
48
- const cy = latToY(centerLat, H);
49
- const pxPerDegLon = W / 360;
50
- const pxPerDegLat = H / 180;
51
- ctx.beginPath();
52
- for (let i = 0; i <= numSegments; i++) {
53
- const bearing = i / numSegments * 2 * Math.PI;
54
- const sinLat = Math.sin(lat0) * Math.cos(d) + Math.cos(lat0) * Math.sin(d) * Math.cos(bearing);
55
- const lat = Math.asin(Math.max(-1, Math.min(1, sinLat)));
56
- const lon = lon0 + Math.atan2(
57
- Math.sin(bearing) * Math.sin(d) * Math.cos(lat0),
58
- Math.cos(d) - Math.sin(lat0) * sinLat
59
- );
60
- const pLatDeg = lat * 180 / Math.PI;
61
- const pLonDeg = lon * 180 / Math.PI;
62
- const dLon = pLonDeg - centerLon;
63
- const dLat = pLatDeg - centerLat;
64
- const px = cx + dLon * pxPerDegLon;
65
- const py = cy - dLat * pxPerDegLat;
66
- if (i === 0) ctx.moveTo(px, py);
67
- else ctx.lineTo(px, py);
68
- }
69
- ctx.closePath();
70
- }
71
- function calculateTerminator(date, numPoints = 72) {
72
- const dayOfYear = Math.floor((date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 864e5);
73
- const declination = -23.44 * Math.cos(2 * Math.PI / 365 * (dayOfYear + 10));
74
- const decRad = declination * Math.PI / 180;
75
- const hourAngle = (date.getUTCHours() + date.getUTCMinutes() / 60) / 24 * 360 - 180;
76
- const points = [];
77
- for (let i = 0; i <= numPoints; i++) {
78
- const latRad = (i / numPoints * 180 - 90) * (Math.PI / 180);
79
- const cosH = -Math.tan(latRad) * Math.tan(decRad);
80
- let lon;
81
- if (cosH < -1) lon = hourAngle + 180;
82
- else if (cosH > 1) lon = hourAngle;
83
- else lon = hourAngle + Math.acos(cosH) * 180 / Math.PI;
84
- while (lon > 180) lon -= 360;
85
- while (lon < -180) lon += 360;
86
- points.push({ lat: i / numPoints * 180 - 90, lon });
87
- }
88
- return points;
89
- }
90
- function GroundTrackMap({
91
- groundTrack,
92
- satellites = [],
93
- groundStations = [],
94
- accessMask,
95
- teamPaths,
96
- showTerminator = true,
97
- showGrid = true,
98
- showLegend = true,
99
- showEquator = true,
100
- showRecenterButton = false,
101
- isLoading = false,
102
- emptyMessage = "No orbital data available",
103
- width = "100%",
104
- height = "100%",
105
- minHeight = "400px",
106
- defaultCenter,
107
- defaultZoom,
108
- className = "",
109
- onSatelliteClick,
110
- onStationClick,
111
- mapProvider = "leaflet",
112
- tileUrl
113
- }) {
114
- const { tokens } = useTheme();
115
- const canvasRef = useRef(null);
116
- const containerRef = useRef(null);
117
- const [tooltip, setTooltip] = useState(null);
118
- const [_viewOffset, setViewOffset] = useState({ x: 0, y: 0 });
119
- const allSatellites = useMemo(() => {
120
- if (satellites.length > 0) return satellites;
121
- if (teamPaths && teamPaths.length > 0) {
122
- return teamPaths.map((tp, idx) => ({
123
- id: `team-${idx}`,
124
- name: tp.name,
125
- color: tp.color,
126
- status: "normal",
127
- groundTrack: tp.positions.filter((p) => p.lat != null && p.lng != null).map((p) => ({
128
- latitude: p.lat,
129
- longitude: p.lng,
130
- altitude: p.altitude ?? 0,
131
- timestamp: p.time != null ? new Date(p.time * 1e3).toISOString() : (/* @__PURE__ */ new Date()).toISOString()
132
- }))
133
- }));
134
- }
135
- if (groundTrack && groundTrack.length > 0) {
136
- return [{
137
- id: "primary",
138
- name: "Satellite",
139
- groundTrack,
140
- status: "normal",
141
- accessMask
142
- }];
143
- }
144
- return [];
145
- }, [satellites, groundTrack, accessMask, teamPaths]);
146
- const leafletDefaultCenter = useMemo(() => defaultCenter ?? [20, 0], [defaultCenter]);
147
- const leafletDefaultZoom = useMemo(() => defaultZoom ?? 2, [defaultZoom]);
148
- const [LeafletMap, setLeafletMap] = useState(null);
149
- const [leafletFailed, setLeafletFailed] = useState(false);
150
- useEffect(() => {
151
- if (mapProvider !== "leaflet") return;
152
- import("./GroundTrackMapLeaflet.js").then((m) => setLeafletMap(() => m.GroundTrackMapLeaflet)).catch((err) => {
153
- console.warn('[GroundTrackMap] Leaflet backend failed to load. Install "leaflet" and ensure it is resolvable. Falling back to static canvas.', err);
154
- setLeafletFailed(true);
155
- });
156
- }, [mapProvider]);
157
- const handleRecenter = useCallback(() => {
158
- setViewOffset({ x: 0, y: 0 });
159
- }, []);
160
- const COLORS = useMemo(() => ({
161
- background: tokens.colors.background.base,
162
- grid: tokens.colors.border.muted,
163
- text: tokens.colors.text.secondary,
164
- accent: tokens.colors.accent.primary,
165
- land: "rgba(30, 40, 58, 0.92)",
166
- ocean: tokens.colors.background.base,
167
- night: "rgba(0, 10, 40, 0.35)",
168
- equator: "rgba(88, 166, 255, 0.25)"
169
- }), [tokens]);
170
- const lonToX = useCallback((lon, w) => {
171
- let normalizedLon = lon;
172
- while (normalizedLon > 180) normalizedLon -= 360;
173
- while (normalizedLon < -180) normalizedLon += 360;
174
- return (normalizedLon + 180) / 360 * w;
175
- }, []);
176
- const latToY = useCallback((lat, h) => {
177
- return (90 - lat) / 180 * h;
178
- }, []);
179
- const draw = useCallback(() => {
180
- const canvas = canvasRef.current;
181
- const container = containerRef.current;
182
- if (!canvas || !container) return;
183
- const ctx = canvas.getContext("2d");
184
- if (!ctx) return;
185
- const dpr = window.devicePixelRatio || 1;
186
- const rect = container.getBoundingClientRect();
187
- const W = rect.width;
188
- const H = typeof height === "number" ? height : rect.height || 400;
189
- canvas.width = W * dpr;
190
- canvas.height = H * dpr;
191
- canvas.style.width = `${W}px`;
192
- canvas.style.height = `${H}px`;
193
- ctx.scale(dpr, dpr);
194
- ctx.fillStyle = COLORS.background;
195
- ctx.fillRect(0, 0, W, H);
196
- if (showTerminator) {
197
- const terminator = calculateTerminator(/* @__PURE__ */ new Date());
198
- ctx.fillStyle = COLORS.night;
199
- ctx.beginPath();
200
- ctx.moveTo(W, 0);
201
- ctx.lineTo(W, H);
202
- let prevRX = W;
203
- for (let i = terminator.length - 1; i >= 0; i--) {
204
- const x = lonToX(terminator[i].lon, W);
205
- const y = latToY(terminator[i].lat, H);
206
- if (Math.abs(x - prevRX) > W * 0.5 && i < terminator.length - 1) {
207
- const edgeX = prevRX > W / 2 ? W : 0;
208
- ctx.lineTo(edgeX, y);
209
- ctx.closePath();
210
- ctx.fill();
211
- ctx.beginPath();
212
- ctx.moveTo(W, y);
213
- }
214
- ctx.lineTo(x, y);
215
- prevRX = x;
216
- }
217
- ctx.closePath();
218
- ctx.fill();
219
- const normLon = (l) => {
220
- let r = l;
221
- while (r > 180) r -= 360;
222
- while (r < -180) r += 360;
223
- return r;
224
- };
225
- ctx.beginPath();
226
- ctx.moveTo(0, 0);
227
- ctx.lineTo(0, H);
228
- let prevLX = 0;
229
- for (let i = terminator.length - 1; i >= 0; i--) {
230
- const sunriseLon = normLon(terminator[i].lon - 180);
231
- const x = lonToX(sunriseLon, W);
232
- const y = latToY(terminator[i].lat, H);
233
- if (Math.abs(x - prevLX) > W * 0.5 && i < terminator.length - 1) {
234
- const edgeX = prevLX < W / 2 ? 0 : W;
235
- ctx.lineTo(edgeX, y);
236
- ctx.closePath();
237
- ctx.fill();
238
- ctx.beginPath();
239
- ctx.moveTo(0, y);
240
- }
241
- ctx.lineTo(x, y);
242
- prevLX = x;
243
- }
244
- ctx.closePath();
245
- ctx.fill();
246
- }
247
- ctx.fillStyle = COLORS.land;
248
- ctx.strokeStyle = "rgba(100, 120, 160, 0.25)";
249
- ctx.lineWidth = 1;
250
- for (const pathStr of WORLD_MAP_PATHS) {
251
- const commands = pathStr.split(" ");
252
- ctx.beginPath();
253
- for (const cmd of commands) {
254
- if (cmd.startsWith("M") || cmd.startsWith("L")) {
255
- const coords = cmd.substring(1).split(",");
256
- const lon = parseFloat(coords[0]);
257
- const lat = parseFloat(coords[1]);
258
- const x = lonToX(lon, W);
259
- const y = latToY(lat, H);
260
- if (cmd.startsWith("M")) ctx.moveTo(x, y);
261
- else ctx.lineTo(x, y);
262
- } else if (cmd === "Z") {
263
- ctx.closePath();
264
- }
265
- }
266
- ctx.fill();
267
- ctx.stroke();
268
- }
269
- if (showGrid) {
270
- ctx.strokeStyle = COLORS.grid;
271
- ctx.lineWidth = 0.4;
272
- for (let lon = -180; lon <= 180; lon += 30) {
273
- const x = lonToX(lon, W);
274
- ctx.beginPath();
275
- ctx.moveTo(x, 0);
276
- ctx.lineTo(x, H);
277
- ctx.stroke();
278
- }
279
- for (let lat = -90; lat <= 90; lat += 30) {
280
- const y = latToY(lat, H);
281
- ctx.beginPath();
282
- ctx.moveTo(0, y);
283
- ctx.lineTo(W, y);
284
- ctx.stroke();
285
- }
286
- }
287
- if (showEquator) {
288
- const eqY = latToY(0, H);
289
- ctx.strokeStyle = COLORS.equator;
290
- ctx.lineWidth = 1;
291
- ctx.setLineDash([6, 4]);
292
- ctx.beginPath();
293
- ctx.moveTo(0, eqY);
294
- ctx.lineTo(W, eqY);
295
- ctx.stroke();
296
- ctx.setLineDash([]);
297
- }
298
- for (const gs of groundStations) {
299
- const enhanced = gs;
300
- if (enhanced.showCoverage && enhanced.coverageRadius) {
301
- const statusColor = STATUS_COLOR_MAP[enhanced.status || "standby"] || STATUS_COLOR_MAP.standby;
302
- drawCoverageEllipse(ctx, gs.latitude, gs.longitude, enhanced.coverageRadius, W, H, lonToX, latToY);
303
- ctx.fillStyle = `${statusColor}15`;
304
- ctx.fill();
305
- ctx.strokeStyle = `${statusColor}40`;
306
- ctx.lineWidth = 1;
307
- ctx.setLineDash([4, 3]);
308
- ctx.stroke();
309
- ctx.setLineDash([]);
310
- }
311
- }
312
- for (const gs of groundStations) {
313
- const enhanced = gs;
314
- const x = lonToX(gs.longitude, W);
315
- const y = latToY(gs.latitude, H);
316
- const status = enhanced.status || "standby";
317
- const statusColor = STATUS_COLOR_MAP[status] || STATUS_COLOR_MAP.standby;
318
- const stationType = enhanced.type || "dish";
319
- const iconSize = 20;
320
- const halfIcon = iconSize / 2;
321
- ctx.save();
322
- ctx.translate(x, y);
323
- ctx.beginPath();
324
- ctx.arc(0, 0, halfIcon + 2, 0, Math.PI * 2);
325
- ctx.fillStyle = `${statusColor}18`;
326
- ctx.fill();
327
- ctx.beginPath();
328
- ctx.arc(0, 0, halfIcon, 0, Math.PI * 2);
329
- ctx.fillStyle = "rgba(15, 23, 42, 0.85)";
330
- ctx.fill();
331
- ctx.strokeStyle = `${statusColor}66`;
332
- ctx.lineWidth = 1.5;
333
- ctx.stroke();
334
- ctx.save();
335
- const iconScale = iconSize / 24;
336
- ctx.translate(-halfIcon, -halfIcon);
337
- ctx.scale(iconScale, iconScale);
338
- if (stationType === "dish" || stationType === "omni") {
339
- ctx.strokeStyle = statusColor;
340
- ctx.fillStyle = statusColor;
341
- ctx.lineWidth = 2 / iconScale;
342
- ctx.beginPath();
343
- ctx.arc(12, 12, 10, Math.PI * 1.25, Math.PI * 1.75);
344
- ctx.stroke();
345
- ctx.beginPath();
346
- ctx.arc(12, 12, 6.5, Math.PI * 1.2, Math.PI * 1.8);
347
- ctx.stroke();
348
- ctx.beginPath();
349
- ctx.arc(12, 12, 3, Math.PI * 1.15, Math.PI * 1.85);
350
- ctx.stroke();
351
- ctx.beginPath();
352
- ctx.arc(12, 12, 1.5, 0, Math.PI * 2);
353
- ctx.fill();
354
- ctx.lineWidth = 1.5 / iconScale;
355
- ctx.beginPath();
356
- ctx.moveTo(12, 14);
357
- ctx.lineTo(12, 20);
358
- ctx.stroke();
359
- ctx.beginPath();
360
- ctx.moveTo(8, 20);
361
- ctx.lineTo(16, 20);
362
- ctx.stroke();
363
- } else if (stationType === "phased-array") {
364
- ctx.strokeStyle = statusColor;
365
- ctx.fillStyle = statusColor;
366
- ctx.lineWidth = 1.5 / iconScale;
367
- ctx.strokeRect(5, 4, 14, 12);
368
- for (let gx = 8; gx <= 16; gx += 4) {
369
- ctx.beginPath();
370
- ctx.moveTo(gx, 4);
371
- ctx.lineTo(gx, 16);
372
- ctx.stroke();
373
- }
374
- for (let gy = 8; gy <= 12; gy += 4) {
375
- ctx.beginPath();
376
- ctx.moveTo(5, gy);
377
- ctx.lineTo(19, gy);
378
- ctx.stroke();
379
- }
380
- ctx.beginPath();
381
- ctx.moveTo(12, 16);
382
- ctx.lineTo(12, 21);
383
- ctx.stroke();
384
- ctx.beginPath();
385
- ctx.moveTo(8, 21);
386
- ctx.lineTo(16, 21);
387
- ctx.stroke();
388
- } else {
389
- ctx.strokeStyle = statusColor;
390
- ctx.fillStyle = statusColor;
391
- ctx.lineWidth = 2 / iconScale;
392
- ctx.beginPath();
393
- ctx.arc(10, 12, 7, Math.PI * 0.8, Math.PI * 1.7);
394
- ctx.stroke();
395
- ctx.lineWidth = 1.5 / iconScale;
396
- ctx.beginPath();
397
- ctx.moveTo(10, 12);
398
- ctx.lineTo(16, 6);
399
- ctx.stroke();
400
- ctx.lineWidth = 1.2 / iconScale;
401
- ctx.beginPath();
402
- ctx.arc(17, 5, 2.5, Math.PI * 1.2, Math.PI * 1.7);
403
- ctx.stroke();
404
- ctx.beginPath();
405
- ctx.arc(17, 5, 4.5, Math.PI * 1.15, Math.PI * 1.75);
406
- ctx.stroke();
407
- ctx.lineWidth = 1.5 / iconScale;
408
- ctx.beginPath();
409
- ctx.moveTo(10, 14);
410
- ctx.lineTo(10, 20);
411
- ctx.stroke();
412
- ctx.beginPath();
413
- ctx.moveTo(6, 20);
414
- ctx.lineTo(14, 20);
415
- ctx.stroke();
416
- }
417
- ctx.restore();
418
- const badgeX = -halfIcon + 1;
419
- const badgeY = -halfIcon + 1;
420
- const badgeSize = 7;
421
- ctx.beginPath();
422
- ctx.arc(badgeX, badgeY, badgeSize / 2 + 2, 0, Math.PI * 2);
423
- ctx.fillStyle = "rgba(15, 23, 42, 0.9)";
424
- ctx.fill();
425
- ctx.fillStyle = statusColor;
426
- ctx.strokeStyle = statusColor;
427
- ctx.lineWidth = 1;
428
- if (status === "normal") {
429
- ctx.beginPath();
430
- ctx.arc(badgeX, badgeY, badgeSize / 2, 0, Math.PI * 2);
431
- ctx.fill();
432
- } else if (status === "standby") {
433
- ctx.beginPath();
434
- ctx.arc(badgeX, badgeY, badgeSize / 2 - 0.5, 0, Math.PI * 2);
435
- ctx.lineWidth = 2;
436
- ctx.stroke();
437
- } else if (status === "caution") {
438
- const hs = badgeSize / 2;
439
- ctx.fillRect(badgeX - hs, badgeY - hs, badgeSize, badgeSize);
440
- } else if (status === "serious") {
441
- const hs = badgeSize / 2;
442
- ctx.beginPath();
443
- ctx.moveTo(badgeX, badgeY - hs);
444
- ctx.lineTo(badgeX + hs, badgeY);
445
- ctx.lineTo(badgeX, badgeY + hs);
446
- ctx.lineTo(badgeX - hs, badgeY);
447
- ctx.closePath();
448
- ctx.fill();
449
- } else if (status === "critical") {
450
- const hs = badgeSize / 2;
451
- ctx.beginPath();
452
- ctx.moveTo(badgeX, badgeY + hs);
453
- ctx.lineTo(badgeX + hs, badgeY - hs);
454
- ctx.lineTo(badgeX - hs, badgeY - hs);
455
- ctx.closePath();
456
- ctx.fill();
457
- } else {
458
- ctx.beginPath();
459
- ctx.arc(badgeX, badgeY, badgeSize / 3, 0, Math.PI * 2);
460
- ctx.fill();
461
- }
462
- ctx.restore();
463
- ctx.fillStyle = COLORS.text;
464
- ctx.font = '9px "Inter", system-ui, sans-serif';
465
- ctx.textAlign = "center";
466
- ctx.fillText(gs.name.length > 14 ? gs.name.slice(0, 14) + "…" : gs.name, x, y + halfIcon + 12);
467
- }
468
- allSatellites.forEach((sat, satIdx) => {
469
- const track = sat.groundTrack;
470
- if (!track || track.length === 0) return;
471
- const trackColor = sat.color || DEFAULT_TRACK_COLORS[satIdx % DEFAULT_TRACK_COLORS.length];
472
- const futureIdx = sat.futureTrackIndex ?? track.length;
473
- ctx.strokeStyle = trackColor;
474
- ctx.lineWidth = 2;
475
- ctx.lineCap = "round";
476
- ctx.lineJoin = "round";
477
- ctx.setLineDash([]);
478
- ctx.beginPath();
479
- let prevX = lonToX(track[0].longitude, W);
480
- let prevY = latToY(track[0].latitude, H);
481
- ctx.moveTo(prevX, prevY);
482
- for (let i = 1; i < Math.min(futureIdx, track.length); i++) {
483
- const x = lonToX(track[i].longitude, W);
484
- const y = latToY(track[i].latitude, H);
485
- const crossesDateLine = Math.abs(x - prevX) > W / 2;
486
- if (crossesDateLine) {
487
- ctx.stroke();
488
- ctx.beginPath();
489
- ctx.moveTo(x, y);
490
- } else {
491
- ctx.lineTo(x, y);
492
- }
493
- prevX = x;
494
- prevY = y;
495
- }
496
- ctx.stroke();
497
- if (futureIdx < track.length) {
498
- ctx.strokeStyle = `${trackColor}88`;
499
- ctx.lineWidth = 1.5;
500
- ctx.setLineDash([6, 4]);
501
- ctx.beginPath();
502
- ctx.moveTo(lonToX(track[futureIdx].longitude, W), latToY(track[futureIdx].latitude, H));
503
- let fpx = lonToX(track[futureIdx].longitude, W);
504
- for (let i = futureIdx + 1; i < track.length; i++) {
505
- const x = lonToX(track[i].longitude, W);
506
- const y = latToY(track[i].latitude, H);
507
- if (Math.abs(x - fpx) > W / 2) {
508
- ctx.stroke();
509
- ctx.beginPath();
510
- ctx.moveTo(x, y);
511
- } else {
512
- ctx.lineTo(x, y);
513
- }
514
- fpx = x;
515
- }
516
- ctx.stroke();
517
- ctx.setLineDash([]);
518
- }
519
- if (sat.accessMask && sat.accessMask.some(Boolean)) {
520
- ctx.strokeStyle = STATUS_COLOR_MAP.normal;
521
- ctx.lineWidth = 3;
522
- ctx.setLineDash([]);
523
- ctx.beginPath();
524
- let drawing = false;
525
- let apx = lonToX(track[0].longitude, W);
526
- for (let i = 0; i < track.length; i++) {
527
- const x = lonToX(track[i].longitude, W);
528
- const y = latToY(track[i].latitude, H);
529
- const active = sat.accessMask[i];
530
- const crossesDateLine = i > 0 && Math.abs(x - apx) > W / 2;
531
- if (active && !crossesDateLine) {
532
- if (!drawing) {
533
- ctx.moveTo(x, y);
534
- drawing = true;
535
- } else ctx.lineTo(x, y);
536
- } else {
537
- if (drawing) {
538
- ctx.stroke();
539
- ctx.beginPath();
540
- drawing = false;
541
- }
542
- }
543
- apx = x;
544
- }
545
- if (drawing) ctx.stroke();
546
- }
547
- if (sat.passMarkers) {
548
- for (const marker of sat.passMarkers) {
549
- const mx = lonToX(marker.longitude, W);
550
- const my = latToY(marker.latitude, H);
551
- const markerColor = marker.type === "aos" ? STATUS_COLOR_MAP.normal : STATUS_COLOR_MAP.critical;
552
- ctx.save();
553
- ctx.translate(mx, my);
554
- ctx.beginPath();
555
- if (marker.type === "aos") {
556
- ctx.moveTo(0, -6);
557
- ctx.lineTo(4, 2);
558
- ctx.lineTo(-4, 2);
559
- } else {
560
- ctx.moveTo(0, 6);
561
- ctx.lineTo(4, -2);
562
- ctx.lineTo(-4, -2);
563
- }
564
- ctx.closePath();
565
- ctx.fillStyle = markerColor;
566
- ctx.fill();
567
- ctx.restore();
568
- if (marker.label) {
569
- ctx.fillStyle = markerColor;
570
- ctx.font = '8px "Inter", system-ui, sans-serif';
571
- ctx.textAlign = "center";
572
- ctx.fillText(marker.label, mx, marker.type === "aos" ? my - 9 : my + 13);
573
- }
574
- }
575
- }
576
- if (sat.showFootprint && sat.footprintRadius) {
577
- const lastPt = futureIdx > 0 ? track[Math.min(futureIdx - 1, track.length - 1)] : track[track.length - 1];
578
- drawCoverageEllipse(ctx, lastPt.latitude, lastPt.longitude, sat.footprintRadius, W, H, lonToX, latToY);
579
- ctx.fillStyle = `${trackColor}12`;
580
- ctx.fill();
581
- ctx.strokeStyle = `${trackColor}50`;
582
- ctx.lineWidth = 1;
583
- ctx.setLineDash([3, 3]);
584
- ctx.stroke();
585
- ctx.setLineDash([]);
586
- }
587
- const currentIdx = futureIdx > 0 ? Math.min(futureIdx - 1, track.length - 1) : track.length - 1;
588
- const current = track[currentIdx];
589
- const cx = lonToX(current.longitude, W);
590
- const cy = latToY(current.latitude, H);
591
- const satStatus = sat.status || "normal";
592
- const statusColor = STATUS_COLOR_MAP[satStatus] || STATUS_COLOR_MAP.normal;
593
- const satIconSize = 22;
594
- const satHalf = satIconSize / 2;
595
- ctx.beginPath();
596
- ctx.arc(cx, cy, satHalf + 6, 0, Math.PI * 2);
597
- ctx.fillStyle = `${statusColor}12`;
598
- ctx.fill();
599
- ctx.beginPath();
600
- ctx.arc(cx, cy, satHalf + 3, 0, Math.PI * 2);
601
- ctx.fillStyle = `${statusColor}20`;
602
- ctx.fill();
603
- ctx.save();
604
- ctx.translate(cx, cy);
605
- ctx.beginPath();
606
- ctx.arc(0, 0, satHalf, 0, Math.PI * 2);
607
- ctx.fillStyle = "rgba(15, 23, 42, 0.88)";
608
- ctx.fill();
609
- ctx.strokeStyle = `${statusColor}88`;
610
- ctx.lineWidth = 1.5;
611
- ctx.stroke();
612
- ctx.fillStyle = statusColor;
613
- const bw = 5, bh = 3.5;
614
- ctx.beginPath();
615
- ctx.moveTo(-bw / 2 + 0.5, -bh / 2);
616
- ctx.lineTo(bw / 2 - 0.5, -bh / 2);
617
- ctx.arcTo(bw / 2, -bh / 2, bw / 2, -bh / 2 + 0.5, 0.5);
618
- ctx.lineTo(bw / 2, bh / 2 - 0.5);
619
- ctx.arcTo(bw / 2, bh / 2, bw / 2 - 0.5, bh / 2, 0.5);
620
- ctx.lineTo(-bw / 2 + 0.5, bh / 2);
621
- ctx.arcTo(-bw / 2, bh / 2, -bw / 2, bh / 2 - 0.5, 0.5);
622
- ctx.lineTo(-bw / 2, -bh / 2 + 0.5);
623
- ctx.arcTo(-bw / 2, -bh / 2, -bw / 2 + 0.5, -bh / 2, 0.5);
624
- ctx.closePath();
625
- ctx.fill();
626
- ctx.fillStyle = `${statusColor}BB`;
627
- ctx.fillRect(-satHalf + 2, -2, 5, 4);
628
- ctx.strokeStyle = `${statusColor}55`;
629
- ctx.lineWidth = 0.5;
630
- ctx.beginPath();
631
- ctx.moveTo(-satHalf + 4.5, -2);
632
- ctx.lineTo(-satHalf + 4.5, 2);
633
- ctx.stroke();
634
- ctx.fillStyle = `${statusColor}BB`;
635
- ctx.fillRect(satHalf - 7, -2, 5, 4);
636
- ctx.strokeStyle = `${statusColor}55`;
637
- ctx.beginPath();
638
- ctx.moveTo(satHalf - 4.5, -2);
639
- ctx.lineTo(satHalf - 4.5, 2);
640
- ctx.stroke();
641
- ctx.strokeStyle = statusColor;
642
- ctx.lineWidth = 1;
643
- ctx.beginPath();
644
- ctx.moveTo(-bw / 2, 0);
645
- ctx.lineTo(-satHalf + 7, 0);
646
- ctx.moveTo(bw / 2, 0);
647
- ctx.lineTo(satHalf - 7, 0);
648
- ctx.stroke();
649
- const satBadgeX = -satHalf + 2;
650
- const satBadgeY = -satHalf + 2;
651
- const satBadgeSize = 7;
652
- ctx.beginPath();
653
- ctx.arc(satBadgeX, satBadgeY, satBadgeSize / 2 + 2, 0, Math.PI * 2);
654
- ctx.fillStyle = "rgba(15, 23, 42, 0.9)";
655
- ctx.fill();
656
- ctx.fillStyle = statusColor;
657
- ctx.strokeStyle = statusColor;
658
- ctx.lineWidth = 1;
659
- if (satStatus === "normal") {
660
- ctx.beginPath();
661
- ctx.arc(satBadgeX, satBadgeY, satBadgeSize / 2, 0, Math.PI * 2);
662
- ctx.fill();
663
- } else if (satStatus === "standby") {
664
- ctx.beginPath();
665
- ctx.arc(satBadgeX, satBadgeY, satBadgeSize / 2 - 0.5, 0, Math.PI * 2);
666
- ctx.lineWidth = 2;
667
- ctx.stroke();
668
- } else if (satStatus === "caution") {
669
- const hs = satBadgeSize / 2;
670
- ctx.fillRect(satBadgeX - hs, satBadgeY - hs, satBadgeSize, satBadgeSize);
671
- } else if (satStatus === "serious") {
672
- const hs = satBadgeSize / 2;
673
- ctx.beginPath();
674
- ctx.moveTo(satBadgeX, satBadgeY - hs);
675
- ctx.lineTo(satBadgeX + hs, satBadgeY);
676
- ctx.lineTo(satBadgeX, satBadgeY + hs);
677
- ctx.lineTo(satBadgeX - hs, satBadgeY);
678
- ctx.closePath();
679
- ctx.fill();
680
- } else if (satStatus === "critical") {
681
- const hs = satBadgeSize / 2;
682
- ctx.beginPath();
683
- ctx.moveTo(satBadgeX, satBadgeY + hs);
684
- ctx.lineTo(satBadgeX + hs, satBadgeY - hs);
685
- ctx.lineTo(satBadgeX - hs, satBadgeY - hs);
686
- ctx.closePath();
687
- ctx.fill();
688
- } else {
689
- ctx.beginPath();
690
- ctx.arc(satBadgeX, satBadgeY, satBadgeSize / 3, 0, Math.PI * 2);
691
- ctx.fill();
692
- }
693
- ctx.restore();
694
- ctx.fillStyle = "#fff";
695
- ctx.font = 'bold 10px "Inter", system-ui, sans-serif';
696
- ctx.textAlign = "center";
697
- ctx.fillText(sat.name, cx, cy - satHalf - 6);
698
- });
699
- if (showLegend && (allSatellites.length > 0 || groundStations.length > 0)) {
700
- const legendX = 10;
701
- const legendY = 10;
702
- const lineHeight = 14;
703
- const items = [];
704
- allSatellites.forEach((sat, idx) => {
705
- const c = sat.color || DEFAULT_TRACK_COLORS[idx % DEFAULT_TRACK_COLORS.length];
706
- items.push({ color: c, label: sat.name });
707
- });
708
- if (groundStations.length > 0) {
709
- items.push({ color: STATUS_COLOR_MAP.standby, label: `${groundStations.length} Ground Station${groundStations.length > 1 ? "s" : ""}` });
710
- }
711
- const maxLabelWidth = Math.max(...items.map((item) => ctx.measureText(item.label).width)) + 28;
712
- const legendH = items.length * lineHeight + 10;
713
- ctx.fillStyle = "rgba(0, 0, 0, 0.6)";
714
- ctx.strokeStyle = "rgba(100, 116, 139, 0.3)";
715
- ctx.lineWidth = 1;
716
- const r = 4;
717
- ctx.beginPath();
718
- ctx.moveTo(legendX + r, legendY);
719
- ctx.lineTo(legendX + maxLabelWidth - r, legendY);
720
- ctx.arcTo(legendX + maxLabelWidth, legendY, legendX + maxLabelWidth, legendY + r, r);
721
- ctx.lineTo(legendX + maxLabelWidth, legendY + legendH - r);
722
- ctx.arcTo(legendX + maxLabelWidth, legendY + legendH, legendX + maxLabelWidth - r, legendY + legendH, r);
723
- ctx.lineTo(legendX + r, legendY + legendH);
724
- ctx.arcTo(legendX, legendY + legendH, legendX, legendY + legendH - r, r);
725
- ctx.lineTo(legendX, legendY + r);
726
- ctx.arcTo(legendX, legendY, legendX + r, legendY, r);
727
- ctx.closePath();
728
- ctx.fill();
729
- ctx.stroke();
730
- items.forEach((item, i) => {
731
- const iy = legendY + 8 + i * lineHeight;
732
- ctx.fillStyle = item.color;
733
- ctx.beginPath();
734
- ctx.arc(legendX + 10, iy + 3, 4, 0, Math.PI * 2);
735
- ctx.fill();
736
- ctx.fillStyle = "rgba(255, 255, 255, 0.85)";
737
- ctx.font = '10px "Inter", system-ui, sans-serif';
738
- ctx.textAlign = "left";
739
- ctx.fillText(item.label, legendX + 20, iy + 7);
740
- });
741
- }
742
- if (allSatellites.length === 0 && groundStations.length === 0) {
743
- ctx.fillStyle = COLORS.text;
744
- ctx.font = '12px "Inter", system-ui, sans-serif';
745
- ctx.textAlign = "center";
746
- ctx.fillText(emptyMessage, W / 2, H / 2);
747
- }
748
- }, [allSatellites, groundStations, height, showTerminator, showGrid, showLegend, showEquator, lonToX, latToY, COLORS, emptyMessage]);
749
- const handleMouseMove = useCallback((e) => {
750
- const canvas = canvasRef.current;
751
- if (!canvas) return;
752
- const rect = canvas.getBoundingClientRect();
753
- const mx = e.clientX - rect.left;
754
- const my = e.clientY - rect.top;
755
- const W = rect.width;
756
- const H = typeof height === "number" ? height : rect.height;
757
- for (const sat of allSatellites) {
758
- if (!sat.groundTrack.length) continue;
759
- const futureIdx = sat.futureTrackIndex ?? sat.groundTrack.length;
760
- const currentIdx = futureIdx > 0 ? Math.min(futureIdx - 1, sat.groundTrack.length - 1) : sat.groundTrack.length - 1;
761
- const lastPt = sat.groundTrack[currentIdx];
762
- const sx = lonToX(lastPt.longitude, W);
763
- const sy = latToY(lastPt.latitude, H);
764
- const dist = Math.sqrt((mx - sx) ** 2 + (my - sy) ** 2);
765
- if (dist < 18) {
766
- const satStatus = sat.status || "normal";
767
- setTooltip({
768
- x: e.clientX - rect.left + 15,
769
- y: e.clientY - rect.top - 10,
770
- type: "satellite",
771
- name: sat.name,
772
- status: satStatus,
773
- statusColor: STATUS_COLOR_MAP[satStatus] || STATUS_COLOR_MAP.normal,
774
- lat: lastPt.latitude,
775
- lon: lastPt.longitude,
776
- alt: lastPt.altitude,
777
- trackColor: sat.color || DEFAULT_TRACK_COLORS[allSatellites.indexOf(sat) % DEFAULT_TRACK_COLORS.length],
778
- hasFootprint: sat.showFootprint,
779
- futurePoints: sat.groundTrack.length - futureIdx,
780
- pastPoints: futureIdx
781
- });
782
- return;
783
- }
784
- }
785
- for (const gs of groundStations) {
786
- const gx = lonToX(gs.longitude, W);
787
- const gy = latToY(gs.latitude, H);
788
- const dist = Math.sqrt((mx - gx) ** 2 + (my - gy) ** 2);
789
- if (dist < 16) {
790
- const enhanced = gs;
791
- const gsStatus = enhanced.status || "standby";
792
- setTooltip({
793
- x: e.clientX - rect.left + 15,
794
- y: e.clientY - rect.top - 10,
795
- type: "station",
796
- name: gs.name,
797
- status: gsStatus,
798
- statusColor: STATUS_COLOR_MAP[gsStatus] || STATUS_COLOR_MAP.standby,
799
- lat: gs.latitude,
800
- lon: gs.longitude,
801
- stationType: enhanced.type || "dish",
802
- network: "network" in gs && gs.network ? String(gs.network) : void 0,
803
- coverageRadius: enhanced.coverageRadius
804
- });
805
- return;
806
- }
807
- }
808
- setTooltip(null);
809
- }, [allSatellites, groundStations, height, lonToX, latToY]);
810
- const handleClick = useCallback((e) => {
811
- const canvas = canvasRef.current;
812
- if (!canvas) return;
813
- const rect = canvas.getBoundingClientRect();
814
- const mx = e.clientX - rect.left;
815
- const my = e.clientY - rect.top;
816
- const W = rect.width;
817
- const H = typeof height === "number" ? height : rect.height;
818
- for (const sat of allSatellites) {
819
- if (!sat.groundTrack.length) continue;
820
- const futureIdx = sat.futureTrackIndex ?? sat.groundTrack.length;
821
- const currentIdx = futureIdx > 0 ? Math.min(futureIdx - 1, sat.groundTrack.length - 1) : sat.groundTrack.length - 1;
822
- const currentPt = sat.groundTrack[currentIdx];
823
- const sx = lonToX(currentPt.longitude, W);
824
- const sy = latToY(currentPt.latitude, H);
825
- if (Math.sqrt((mx - sx) ** 2 + (my - sy) ** 2) < 15) {
826
- onSatelliteClick == null ? void 0 : onSatelliteClick(sat.id);
827
- return;
828
- }
829
- }
830
- for (const gs of groundStations) {
831
- const gx = lonToX(gs.longitude, W);
832
- const gy = latToY(gs.latitude, H);
833
- if (Math.sqrt((mx - gx) ** 2 + (my - gy) ** 2) < 12) {
834
- onStationClick == null ? void 0 : onStationClick("id" in gs ? gs.id : gs.name);
835
- return;
836
- }
837
- }
838
- }, [allSatellites, groundStations, height, lonToX, latToY, onSatelliteClick, onStationClick]);
839
- useEffect(() => {
840
- draw();
841
- window.addEventListener("resize", draw);
842
- return () => window.removeEventListener("resize", draw);
843
- }, [draw]);
844
- const resolvedMinHeightForLeaflet = minHeight || (typeof height === "number" ? `${height}px` : "400px");
845
- if (mapProvider === "leaflet") {
846
- if (LeafletMap) {
847
- return /* @__PURE__ */ jsx(
848
- "div",
849
- {
850
- role: "img",
851
- "aria-label": allSatellites.length > 0 || groundStations.length > 0 ? "Ground track map showing satellite tracks and ground stations" : "Ground track map",
852
- style: {
853
- display: "block",
854
- width: width ?? "100%",
855
- height: typeof height === "string" ? height : void 0,
856
- minHeight: resolvedMinHeightForLeaflet
857
- },
858
- children: /* @__PURE__ */ jsx(
859
- LeafletMap,
860
- {
861
- allSatellites,
862
- groundStations,
863
- showTerminator,
864
- showGrid,
865
- showLegend,
866
- showEquator,
867
- showRecenterButton,
868
- defaultCenter: leafletDefaultCenter,
869
- defaultZoom: leafletDefaultZoom,
870
- height,
871
- width,
872
- minHeight,
873
- emptyMessage,
874
- tileUrl,
875
- className,
876
- onSatelliteClick,
877
- onStationClick
878
- }
879
- )
880
- }
881
- );
882
- }
883
- if (!leafletFailed) {
884
- return /* @__PURE__ */ jsx(
885
- "div",
886
- {
887
- className: `zendir-ground-track-map ${className}`,
888
- style: {
889
- width,
890
- height: typeof height === "string" ? height : void 0,
891
- minHeight: resolvedMinHeightForLeaflet,
892
- backgroundColor: tokens.colors.background.base,
893
- borderRadius: 8,
894
- display: "flex",
895
- alignItems: "center",
896
- justifyContent: "center"
897
- },
898
- children: /* @__PURE__ */ jsx("span", { style: { color: tokens.colors.text.secondary, fontSize: 14 }, children: "Loading map…" })
899
- }
900
- );
901
- }
902
- }
903
- const resolvedMinHeight = minHeight || (typeof height === "number" ? `${height}px` : "400px");
904
- if (isLoading) {
905
- return /* @__PURE__ */ jsx(
906
- "div",
907
- {
908
- className: `zendir-ground-track-map ${className}`,
909
- style: {
910
- width,
911
- minHeight: resolvedMinHeight,
912
- backgroundColor: COLORS.background,
913
- borderRadius: "8px",
914
- display: "flex",
915
- alignItems: "center",
916
- justifyContent: "center"
917
- },
918
- children: /* @__PURE__ */ jsx("span", { style: { color: COLORS.text, fontSize: "14px", letterSpacing: "1px" }, children: "Loading…" })
919
- }
920
- );
921
- }
922
- return /* @__PURE__ */ jsxs(
923
- "div",
924
- {
925
- ref: containerRef,
926
- role: "img",
927
- "aria-label": allSatellites.length > 0 || groundStations.length > 0 ? "Ground track map showing satellite tracks and ground stations" : "Ground track map",
928
- className: `zendir-ground-track-map ${className}`,
929
- style: { width, height: typeof height === "string" ? height : void 0, minHeight: resolvedMinHeight, backgroundColor: COLORS.background, borderRadius: "8px", overflow: "hidden", position: "relative" },
930
- "data-map-backend": "canvas",
931
- children: [
932
- mapProvider === "leaflet" && leafletFailed && /* @__PURE__ */ jsxs(
933
- "div",
934
- {
935
- style: {
936
- position: "absolute",
937
- bottom: 8,
938
- left: 8,
939
- zIndex: 100,
940
- padding: "4px 8px",
941
- background: "rgba(0,0,0,0.75)",
942
- color: "#fce83a",
943
- fontSize: 11,
944
- borderRadius: 4
945
- },
946
- children: [
947
- "Static view (install ",
948
- /* @__PURE__ */ jsx("code", { style: { fontFamily: "monospace" }, children: "leaflet" }),
949
- " for zoom/pan)"
950
- ]
951
- }
952
- ),
953
- showRecenterButton && /* @__PURE__ */ jsx(
954
- "button",
955
- {
956
- type: "button",
957
- onClick: handleRecenter,
958
- title: "Recenter Map",
959
- "aria-label": "Recenter map",
960
- style: {
961
- position: "absolute",
962
- top: "8px",
963
- right: "8px",
964
- zIndex: 20,
965
- background: "rgba(30, 41, 59, 0.85)",
966
- border: "1px solid rgba(100, 116, 139, 0.4)",
967
- borderRadius: "4px",
968
- color: "#fff",
969
- cursor: "pointer",
970
- padding: "4px 8px",
971
- fontSize: "12px",
972
- lineHeight: "1",
973
- display: "flex",
974
- alignItems: "center",
975
- gap: "4px"
976
- },
977
- children: /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
978
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" }),
979
- /* @__PURE__ */ jsx("path", { d: "M12 2v4M12 18v4M2 12h4M18 12h4" })
980
- ] })
981
- }
982
- ),
983
- /* @__PURE__ */ jsx(
984
- "canvas",
985
- {
986
- ref: canvasRef,
987
- style: { display: "block", cursor: tooltip ? "pointer" : "default" },
988
- onMouseMove: handleMouseMove,
989
- onMouseLeave: () => setTooltip(null),
990
- onClick: handleClick
991
- }
992
- ),
993
- tooltip && /* @__PURE__ */ jsxs(
994
- "div",
995
- {
996
- style: {
997
- position: "absolute",
998
- left: tooltip.x,
999
- top: tooltip.y,
1000
- background: "rgba(15, 23, 42, 0.95)",
1001
- backdropFilter: "blur(8px)",
1002
- border: `1px solid ${tooltip.statusColor}40`,
1003
- borderRadius: "8px",
1004
- padding: 0,
1005
- pointerEvents: "none",
1006
- zIndex: 10,
1007
- color: "rgba(255, 255, 255, 0.9)",
1008
- fontSize: "11px",
1009
- fontFamily: '"Inter", system-ui, sans-serif',
1010
- lineHeight: "1.5",
1011
- boxShadow: `0 4px 16px rgba(0, 0, 0, 0.5), 0 0 12px ${tooltip.statusColor}15`,
1012
- minWidth: 180,
1013
- overflow: "hidden"
1014
- },
1015
- children: [
1016
- /* @__PURE__ */ jsxs("div", { style: {
1017
- padding: "6px 10px",
1018
- borderBottom: `1px solid ${tooltip.statusColor}25`,
1019
- background: `${tooltip.statusColor}10`,
1020
- display: "flex",
1021
- alignItems: "center",
1022
- gap: 6
1023
- }, children: [
1024
- /* @__PURE__ */ jsx("span", { style: {
1025
- width: 8,
1026
- height: 8,
1027
- borderRadius: tooltip.status === "caution" ? 1 : "50%",
1028
- background: tooltip.statusColor,
1029
- display: "inline-block",
1030
- flexShrink: 0,
1031
- boxShadow: `0 0 4px ${tooltip.statusColor}88`
1032
- } }),
1033
- /* @__PURE__ */ jsx("span", { style: { fontWeight: 600, fontSize: 12 }, children: tooltip.name }),
1034
- /* @__PURE__ */ jsx("span", { style: {
1035
- marginLeft: "auto",
1036
- fontSize: 9,
1037
- fontWeight: 600,
1038
- textTransform: "uppercase",
1039
- color: tooltip.statusColor,
1040
- letterSpacing: "0.5px"
1041
- }, children: tooltip.status })
1042
- ] }),
1043
- /* @__PURE__ */ jsxs("div", { style: { padding: "6px 10px", display: "flex", flexDirection: "column", gap: 3 }, children: [
1044
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: 2 }, children: [
1045
- /* @__PURE__ */ jsx("span", { style: {
1046
- fontSize: 9,
1047
- fontWeight: 600,
1048
- padding: "1px 5px",
1049
- borderRadius: 3,
1050
- textTransform: "uppercase",
1051
- letterSpacing: "0.3px",
1052
- background: tooltip.type === "satellite" ? "rgba(45, 204, 255, 0.15)" : "rgba(86, 240, 0, 0.15)",
1053
- color: tooltip.type === "satellite" ? "#2dccff" : "#56f000"
1054
- }, children: tooltip.type === "satellite" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1055
- /* @__PURE__ */ jsx(AstroIcon, { name: "satellite", size: "extra-small", label: "Satellite", style: { marginRight: 4, verticalAlign: "middle" } }),
1056
- "Satellite"
1057
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1058
- /* @__PURE__ */ jsx(AstroIcon, { name: "antenna", size: "extra-small", label: tooltip.stationType || "Ground station", style: { marginRight: 4, verticalAlign: "middle" } }),
1059
- tooltip.stationType || "dish"
1060
- ] }) }),
1061
- tooltip.network && /* @__PURE__ */ jsx("span", { style: {
1062
- fontSize: 9,
1063
- padding: "1px 5px",
1064
- borderRadius: 3,
1065
- background: "rgba(157, 112, 255, 0.15)",
1066
- color: "#9D70FF"
1067
- }, children: tooltip.network })
1068
- ] }),
1069
- /* @__PURE__ */ jsxs("div", { style: { display: "grid", gridTemplateColumns: "52px 1fr", gap: "2px 8px", fontSize: 10 }, children: [
1070
- /* @__PURE__ */ jsx("span", { style: { color: "rgba(255,255,255,0.5)" }, children: "Lat" }),
1071
- /* @__PURE__ */ jsxs("span", { style: { fontFamily: '"Roboto Mono", monospace', fontWeight: 500 }, children: [
1072
- tooltip.lat >= 0 ? "+" : "",
1073
- tooltip.lat.toFixed(4),
1074
- "°"
1075
- ] }),
1076
- /* @__PURE__ */ jsx("span", { style: { color: "rgba(255,255,255,0.5)" }, children: "Lon" }),
1077
- /* @__PURE__ */ jsxs("span", { style: { fontFamily: '"Roboto Mono", monospace', fontWeight: 500 }, children: [
1078
- tooltip.lon >= 0 ? "+" : "",
1079
- tooltip.lon.toFixed(4),
1080
- "°"
1081
- ] }),
1082
- tooltip.alt !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1083
- /* @__PURE__ */ jsx("span", { style: { color: "rgba(255,255,255,0.5)" }, children: "Altitude" }),
1084
- /* @__PURE__ */ jsxs("span", { style: { fontFamily: '"Roboto Mono", monospace', fontWeight: 500 }, children: [
1085
- tooltip.alt.toFixed(1),
1086
- " km"
1087
- ] })
1088
- ] }),
1089
- tooltip.coverageRadius !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1090
- /* @__PURE__ */ jsx("span", { style: { color: "rgba(255,255,255,0.5)" }, children: "Coverage" }),
1091
- /* @__PURE__ */ jsxs("span", { style: { fontFamily: '"Roboto Mono", monospace', fontWeight: 500 }, children: [
1092
- tooltip.coverageRadius.toFixed(1),
1093
- "° radius"
1094
- ] })
1095
- ] })
1096
- ] }),
1097
- tooltip.type === "satellite" && (tooltip.pastPoints !== void 0 || tooltip.futurePoints !== void 0) && /* @__PURE__ */ jsxs("div", { style: { marginTop: 3, paddingTop: 3, borderTop: "1px solid rgba(255,255,255,0.08)", display: "flex", gap: 10, fontSize: 9 }, children: [
1098
- tooltip.pastPoints !== void 0 && /* @__PURE__ */ jsxs("span", { children: [
1099
- /* @__PURE__ */ jsx("span", { style: { color: tooltip.trackColor }, children: "━" }),
1100
- " ",
1101
- tooltip.pastPoints,
1102
- " past pts"
1103
- ] }),
1104
- tooltip.futurePoints !== void 0 && tooltip.futurePoints > 0 && /* @__PURE__ */ jsxs("span", { children: [
1105
- /* @__PURE__ */ jsx("span", { style: { color: `${tooltip.trackColor}88` }, children: "╌" }),
1106
- " ",
1107
- tooltip.futurePoints,
1108
- " predicted"
1109
- ] }),
1110
- tooltip.hasFootprint && /* @__PURE__ */ jsx("span", { style: { color: "#9D70FF" }, children: "◎ Footprint" })
1111
- ] })
1112
- ] })
1113
- ]
1114
- }
1115
- )
1116
- ]
1117
- }
1118
- );
1119
- }
1120
- export {
1121
- GroundTrackMap
1122
- };
1123
- //# sourceMappingURL=GroundTrackMap.js.map