Give Local ADS-B the selectById hook the other selectable layers expose

It takes the ICAO a position row carries, in any case, or the marker id, and selects through the same path as a click; selectAircraft now delegates to it.
This commit is contained in:
German Khokhlov committed 2026-11-03 20:36:10 +04:00
1 parent 60a230b2ff
commit dea10cac4f
3 files changed
+60 -9

No files matched your search

+6 -1
View File
@@ -97,7 +97,12 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
await p.waitForFunction(
() => window.qaAdsb.mod.getAllPositions().length === 1,
);
assert.ok(await p.evaluate(() => window.qaAdsb.mod.selectAircraft('abc123')));
// The selection hook the selectable layers share, by the row's own id.
assert.ok(
await p.evaluate(() =>
window.qaAdsb.mod.selectById(window.qaAdsb.mod.getAllPositions()[0].id),
),
);
for (const kind of ['cesium', 'navara']) {
await p.evaluate(() => {
window.qaSelections = [];
+18 -6
View File
@@ -529,6 +529,14 @@ export function createLocalAdsbLayer({
return true;
}
function selectById(id) {
if (!enabled || id === null || id === undefined) return false;
let icao = String(id).trim().toLowerCase();
if (icao.startsWith(ENTITY_PREFIX)) icao = icao.slice(ENTITY_PREFIX.length);
if (!icao) return false;
return selectMarker(`${ENTITY_PREFIX}${icao}`);
}
/**
* Join the engine's selection dispatcher with this layer's own click rule
* (`lookUnder`): a click on a local aircraft — on top, or under picks no
@@ -653,7 +661,8 @@ export function createLocalAdsbLayer({
installFrame();
sync();
const selected = getSelectedEntityContext();
if (restore && (!selected || selected.id === restore)) selectMarker(restore);
if (restore && (!selected || selected.id === restore))
selectMarker(restore);
}
return true;
},
@@ -777,13 +786,16 @@ export function createLocalAdsbLayer({
/**
* Select a heard aircraft's marker and publish its readout card, exactly
* as a click does. The camera does not follow it.
* @param {string} icao Lowercase ICAO hex.
* as a click does; the selection hook other selectable layers expose
* (vessels' selectById). The camera does not follow it.
* @param {string} id The ICAO hex of a getAllPositions row, in any case,
* or the marker id (`local-adsb:<icao>`).
* @returns {boolean} Whether a fresh marker was selected.
*/
selectAircraft(icao) {
return enabled && selectMarker(`${ENTITY_PREFIX}${icao}`);
},
selectById,
/** The same as selectById, by ICAO hex. */
selectAircraft: (icao) => selectById(icao),
/**
* Snapshot loaded locally received aircraft as plain JSON-safe records for the analyst
+36 -2
View File
@@ -612,6 +612,30 @@ test('markers drop when the position is 60 s old and records when silent 60 s',
assert.deepEqual(layer.getDetectableObjects(), []);
});
test('selectById selects a heard aircraft by the id its position rows carry, like a click', async (t) => {
const receiver = fakeReceiver({ mode: 'adsb', connected: true });
const clock = { now: 100_000 };
const { layer, calls } = await enabledLayer(receiver, clock);
t.after(() => layer.destroy());
receiver.set({ aircraft: [record()] });
await layer.update();
const [row] = layer.getAllPositions();
assert.equal(row.id, 'abc123');
assert.equal(layer.selectById(row.id), true);
assert.equal(layer.selectById(' ABC123 '), true, 'any case, trimmed');
assert.equal(layer.selectById('local-adsb:abc123'), true, 'the marker id');
assert.deepEqual(calls.selected, [
'local-adsb:abc123',
'local-adsb:abc123',
'local-adsb:abc123',
]);
for (const missing of ['ffffff', '', null, undefined, 'local-adsb:'])
assert.equal(layer.selectById(missing), false, String(missing));
await layer.disable();
assert.equal(layer.selectById('abc123'), false, 'nothing while off');
assert.equal(calls.selected.length, 3);
});
test('selecting a marker publishes the readout card and eviction clears it', async (t) => {
const receiver = fakeReceiver({ mode: 'adsb', connected: true });
const clock = { now: 100_000 };
@@ -1170,14 +1194,24 @@ test('globe handoff retains the receiver, feed, trail fixes and selection', asyn
const receiver = fakeReceiver({ aircraft: [record()] });
const feeds = fakeFeeds();
let closes = 0;
receiver.destroy = () => { closes++; };
receiver.destroy = () => {
closes++;
};
const clock = { now: 100_000 };
const { layer, calls } = await enabledLayer(receiver, clock, feeds);
t.after(() => layer.destroy());
assert.ok(layer.selectAircraft('abc123'));
layer.detachRenderer();
clock.now += 1000;
receiver.set({ aircraft: [record({ lon: 0.001, lastPositionAt: clock.now, lastMessageAt: clock.now })] });
receiver.set({
aircraft: [
record({
lon: 0.001,
lastPositionAt: clock.now,
lastMessageAt: clock.now,
}),
],
});
await layer.update();
assert.equal(layer.getAnalystRecords()[0].icao24, 'abc123');
layer.attachRenderer(fakeViewer().viewer);