mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-09 04:44:01 +00:00
feat(satellites): find the satellites nearest a position
getNearestPositions makes one bounded pass over the shown points, keeps the nearest few in a short sorted list and reads latitude and longitude for those alone, leaving out dense extras, asked-for ids and classes such as debris. A caller that wants the nearest handful no longer builds and sorts an entry for every satellite.
This commit is contained in:
1 parent
569c7cfb1b
commit
fad62a4ba8
2 files changed
+129
No files matched your search
Vendored
+62
@@ -371,6 +371,68 @@ export function createControls({ state: layerState, services, parts, source }) {
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* The shown satellites nearest a position, nearest first, from per-point
|
||||
* state (no SGP4 re-propagation): a bounded pass that keeps only the
|
||||
* nearest `maxCount`, and reads latitude and longitude for those alone.
|
||||
* Dense extras are left out, as in `getAllPositions`.
|
||||
* @param {Cesium.Cartesian3} center Earth-fixed position.
|
||||
* @param {number} [maxCount=8] Most entries to return.
|
||||
* @param {{excludeIds?: Array<number|string>,
|
||||
* excludeClasses?: Array<string>}} [options] Satellites, and classes
|
||||
* (e.g. 'debris'), to leave out.
|
||||
* @returns {Array<{ id: number, label: string, position: Cesium.Cartesian3, latitude: number, longitude: number, altitudeM: number, satelliteClass: string|null, distanceM: number }>}
|
||||
*/
|
||||
getNearestPositions(
|
||||
center,
|
||||
maxCount = 8,
|
||||
{ excludeIds = [], excludeClasses = [] } = {},
|
||||
) {
|
||||
const nearest = [];
|
||||
if (!center || !layerState._points || layerState._points.size === 0)
|
||||
return nearest;
|
||||
const cap =
|
||||
Number.isFinite(maxCount) && maxCount > 0 ? Math.floor(maxCount) : 8;
|
||||
const excluded = new Set(excludeIds.map(String));
|
||||
const classes = new Set(excludeClasses);
|
||||
const dense = layerState._denseIds.length > 0;
|
||||
for (const [noradId, point] of layerState._points) {
|
||||
const p = point.position;
|
||||
if (!p || excluded.has(String(noradId))) continue;
|
||||
if (!_classVisible(noradId)) continue;
|
||||
const sat = layerState._catalog.get(noradId);
|
||||
if (dense && sat?.group === 'dense') continue;
|
||||
if (classes.size && classes.has(sat?.satelliteClass)) continue;
|
||||
const dx = p.x - center.x;
|
||||
const dy = p.y - center.y;
|
||||
const dz = p.z - center.z;
|
||||
const distanceSq = dx * dx + dy * dy + dz * dz;
|
||||
if (nearest.length === cap && distanceSq >= nearest[cap - 1].distanceSq)
|
||||
continue;
|
||||
// Insertion into a short sorted list: no copy or sort of the rest.
|
||||
let at = nearest.length;
|
||||
while (at > 0 && nearest[at - 1].distanceSq > distanceSq) at -= 1;
|
||||
nearest.splice(at, 0, { noradId, point, sat, distanceSq });
|
||||
if (nearest.length > cap) nearest.pop();
|
||||
}
|
||||
const result = [];
|
||||
for (const { noradId, point, sat, distanceSq } of nearest) {
|
||||
const carto = Cesium.Cartographic.fromCartesian(point.position);
|
||||
if (!carto) continue;
|
||||
result.push({
|
||||
id: noradId,
|
||||
label: sat ? sat.name.trim() : String(noradId),
|
||||
position: point.position,
|
||||
latitude: Cesium.Math.toDegrees(carto.latitude),
|
||||
longitude: Cesium.Math.toDegrees(carto.longitude),
|
||||
altitudeM: carto.height,
|
||||
satelliteClass: sat?.satelliteClass ?? null,
|
||||
distanceM: Math.sqrt(distanceSq),
|
||||
});
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Track a satellite by NORAD id (camera follow + orbit path + highlight),
|
||||
* same path as clicking its point.
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// src/layers/satellites/nearestPositions.test.mjs — the satellites nearest a
|
||||
// position: bounded to the nearest few, nearest first, hidden classes,
|
||||
// dense extras and asked-for exclusions left out.
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import * as Cesium from 'cesium';
|
||||
import { createControls } from './controls.js';
|
||||
|
||||
function controlsWith(satellites, { classes, dense = [] } = {}) {
|
||||
const state = {
|
||||
_points: new Map(),
|
||||
_catalog: new Map(),
|
||||
_denseIds: dense,
|
||||
_params: classes ? { classes } : {},
|
||||
};
|
||||
for (const sat of satellites) {
|
||||
state._points.set(sat.id, {
|
||||
position: Cesium.Cartesian3.fromDegrees(sat.lon, sat.lat, sat.alt),
|
||||
});
|
||||
state._catalog.set(sat.id, {
|
||||
name: `${sat.name} `,
|
||||
satelliteClass: sat.cls,
|
||||
group: sat.group,
|
||||
});
|
||||
}
|
||||
return createControls({
|
||||
state,
|
||||
services: { layerState: { isExplicitLayerStateOrigin: () => false } },
|
||||
parts: {},
|
||||
}).methods;
|
||||
}
|
||||
|
||||
const SATELLITES = [
|
||||
{ id: 1, name: 'NEAR', lat: 30.5, lon: -97, alt: 500_000, cls: 'imaging' },
|
||||
{ id: 2, name: 'MID', lat: 33, lon: -97, alt: 500_000, cls: 'stations' },
|
||||
{ id: 3, name: 'FAR', lat: -40, lon: 80, alt: 500_000, cls: 'imaging' },
|
||||
{ id: 4, name: 'DEB', lat: 30.1, lon: -97, alt: 500_000, cls: 'debris' },
|
||||
{ id: 5, name: 'TELECOM', lat: 30.2, lon: -97, alt: 500_000, cls: 'telecom' },
|
||||
{ id: 6, name: 'SELF', lat: 30, lon: -97, alt: 420_000, cls: 'stations' },
|
||||
{ id: 7, name: 'EXTRA', lat: 30.3, lon: -97, alt: 500_000, cls: 'imaging', group: 'dense' },
|
||||
];
|
||||
|
||||
const CENTRE = Cesium.Cartesian3.fromDegrees(-97, 30, 420_000);
|
||||
|
||||
test('the nearest shown satellites come first, bounded, with their distance', () => {
|
||||
const methods = controlsWith(SATELLITES, {
|
||||
classes: { stations: true, imaging: true, debris: true, telecom: false },
|
||||
dense: [7],
|
||||
});
|
||||
const nearest = methods.getNearestPositions(CENTRE, 3, {
|
||||
excludeIds: [6],
|
||||
excludeClasses: ['debris'],
|
||||
});
|
||||
assert.deepEqual(
|
||||
nearest.map((entry) => entry.label),
|
||||
['NEAR', 'MID', 'FAR'],
|
||||
'no self, debris, hidden telecom or dense extra',
|
||||
);
|
||||
for (let i = 1; i < nearest.length; i += 1)
|
||||
assert.ok(nearest[i].distanceM >= nearest[i - 1].distanceM);
|
||||
const exact = Cesium.Cartesian3.distance(CENTRE, nearest[0].position);
|
||||
assert.ok(Math.abs(nearest[0].distanceM - exact) < 1e-6);
|
||||
assert.ok(Math.abs(nearest[0].latitude - 30.5) < 1e-6);
|
||||
assert.equal(nearest[0].satelliteClass, 'imaging');
|
||||
assert.equal(methods.getNearestPositions(CENTRE, 1, { excludeIds: [6] })[0].label, 'DEB');
|
||||
assert.deepEqual(methods.getNearestPositions(null, 3), []);
|
||||
});
|
||||
Reference in new issue
Block a user