feat(map): add universal subject and station search

This commit is contained in:
Codex
2026-07-25 15:37:53 +03:00
parent eaecce9f56
commit a8c8bfd89a
13 changed files with 819 additions and 33 deletions
+46 -14
View File
@@ -135,6 +135,25 @@ export type MapGatewayHealth = {
lastFailure?: string | null;
lastFailureAt?: string | null;
};
referenceSources?: {
transportStations?: {
profileId?: string;
seedFactCount?: number;
fetchEnabled?: boolean;
cellDegrees?: number;
cachedCellCount?: number;
upstreamRequests?: number;
upstreamFailures?: number;
searchRequests?: number;
searchFailures?: number;
upstreamState?: "idle" | "ready" | "degraded";
activeFetches?: number;
queuedFetches?: number;
lastRefreshAt?: string | null;
lastFailure?: string | null;
lastFailureAt?: string | null;
};
};
ionConfigured?: boolean;
};
@@ -239,6 +258,7 @@ export type CesiumMapRendererHandle = {
getCameraView: () => MapCameraView | null;
fitRuntimeEntities: (entityIds?: string[]) => boolean;
focusRuntimeEntity: (entityId: string) => boolean;
focusCoordinates: (longitude: number, latitude: number) => boolean;
};
type TerrainRouteSample = {
@@ -1380,12 +1400,11 @@ export const CesiumMapRenderer = forwardRef<CesiumMapRendererHandle, {
return true;
}, [runtimeEntities]);
const focusRuntimeEntity = useCallback((entityId: string) => {
const focusCoordinates = useCallback((longitude: number, latitude: number) => {
const viewer = viewerRef.current;
if (!viewer || viewer.isDestroyed()) return false;
const entity = runtimeEntities([entityId])[0];
const position = entity?.position?.getValue(viewer.clock.currentTime);
if (!position) return false;
if (!viewer || viewer.isDestroyed()
|| !Number.isFinite(longitude) || longitude < -180 || longitude > 180
|| !Number.isFinite(latitude) || latitude < -90 || latitude > 90) return false;
// Preserve the observer's current composition exactly as the proven
// legacy MMAP/AIS interaction does: move the camera/viewport frame to the
@@ -1396,10 +1415,9 @@ export const CesiumMapRenderer = forwardRef<CesiumMapRendererHandle, {
new Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 2),
viewer.scene.globe.ellipsoid,
);
const cartographic = Cartographic.fromCartesian(position);
const groundTarget = Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
const groundTarget = Cartesian3.fromDegrees(
longitude,
latitude,
0,
viewer.scene.globe.ellipsoid,
);
@@ -1410,8 +1428,8 @@ export const CesiumMapRenderer = forwardRef<CesiumMapRendererHandle, {
new Cartesian3(),
)
: Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
CesiumMath.toRadians(longitude),
CesiumMath.toRadians(latitude),
camera.positionCartographic.height,
viewer.scene.globe.ellipsoid,
);
@@ -1423,10 +1441,23 @@ export const CesiumMapRenderer = forwardRef<CesiumMapRendererHandle, {
pitch: camera.pitch,
roll: camera.roll,
},
duration: 2.1,
duration: 0.45,
});
return true;
}, [runtimeEntities]);
}, []);
const focusRuntimeEntity = useCallback((entityId: string) => {
const viewer = viewerRef.current;
if (!viewer || viewer.isDestroyed()) return false;
const entity = runtimeEntities([entityId])[0];
const position = entity?.position?.getValue(viewer.clock.currentTime);
if (!position) return false;
const cartographic = Cartographic.fromCartesian(position);
return focusCoordinates(
CesiumMath.toDegrees(cartographic.longitude),
CesiumMath.toDegrees(cartographic.latitude),
);
}, [focusCoordinates, runtimeEntities]);
useImperativeHandle(ref, () => ({
startSpiralAnimation,
@@ -1436,8 +1467,9 @@ export const CesiumMapRenderer = forwardRef<CesiumMapRendererHandle, {
return viewer && !viewer.isDestroyed() ? getCameraView(viewer) : null;
},
fitRuntimeEntities,
focusCoordinates,
focusRuntimeEntity,
}), [fitRuntimeEntities, focusRuntimeEntity, startSpiralAnimation, stopSpiralAnimation]);
}), [fitRuntimeEntities, focusCoordinates, focusRuntimeEntity, startSpiralAnimation, stopSpiralAnimation]);
useEffect(() => {
const stopForPageLeave = () => stopSpiralAnimation("stopped");