Show installation and ALPR camera labels on Navara the way Cesium does

An installation card on Navara now names an unnamed feature and its
class in the UI language, and an ALPR camera keeps its context label as
English data that is translated when shown.
This commit is contained in:
German Khokhlov committed 2026-11-05 12:29:45 +04:00
1 parent 350a7f5abb
commit c502404e1d
4 files changed
+98 -6

No files matched your search

+2 -2
View File
@@ -1,4 +1,4 @@
import { t } from '../../../i18n/index.js';
import { msg } from '../../../i18n/index.js';
import * as geo from '../../../geo/index.js';
import { distanceScale } from '../../../globe/cpuPicks.js';
import { selectionDispatcherFor } from '../../../data/selectionDispatcher.js';
@@ -341,7 +341,7 @@ export function createAlprNavaraRenderer(engine, { state, services, source }) {
layerName: 'ALPR Cameras',
source:
source.attribution?.description || source.label || 'Camera source',
label: t('ALPR camera'),
label: msg('ALPR camera'),
latitude: record.latitude,
longitude: record.longitude,
properties: {
+12
View File
@@ -1,6 +1,8 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import * as geo from '../../../geo/index.js';
import { setLocale } from '../../../i18n/index.js';
import { shownEntityLabel } from '../../../voice/speech.js';
import { createAlprNavaraRenderer } from './navara.js';
function event() {
@@ -311,6 +313,16 @@ test('ALPR preserves selected metadata and releases an evicted or replaced selec
assert.equal(h.contexts.has('alpr:1'), false);
});
test('the camera label stays English data in a translated interface, as on Cesium', (t) => {
const h = harness(t);
setLocale('ru');
t.after(() => setLocale('en'));
h.load(records(1));
const { label } = h.contexts.get('alpr:0');
assert.equal(label, 'ALPR camera');
assert.equal(shownEntityLabel(label), 'Камера ALPR');
});
test('ten ALPR toggles clear contexts, picks and credits without allocating new sets', (t) => {
const h = harness(t);
for (let i = 0; i < 10; i++) {
+3 -4
View File
@@ -7,6 +7,7 @@ import {
MILITARY_LABEL_CAP,
MILITARY_POINT_CAP,
} from '../../../data/militaryNames.js';
import { installationClassLabel, installationTitle } from '../classLabel.js';
import { COLOR_BY_CLASS, LAYER_ID, MAX_RENDERED } from '../policy.js';
import {
createInstallationDrapedMesh,
@@ -278,11 +279,9 @@ export function createInstallationsNavaraRenderer(
}
context.gevContextId = record.id;
context.gevLabelModel = {
title: record.name || t('MAPPED INSTALLATION'),
title: installationTitle(record.name) || t('MAPPED INSTALLATION'),
details: [
String(record.class || 'installation')
.replaceAll('_', ' ')
.toUpperCase(),
installationClassLabel(record.class),
...(record.memberNames || []).slice(0, 3),
],
accent: COLOR_BY_CLASS[record.class] || '#9ca6b0',
@@ -1,6 +1,8 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { Mesh } from 'three';
import { setLocale } from '../../../i18n/index.js';
import { installationClassLabel, installationTitle } from '../classLabel.js';
import { createInstallationsNavaraRenderer } from './navara.js';
test('selected installation fill is reused and removed with its scene resources', () => {
@@ -157,3 +159,82 @@ test('selected installation fill is reused and removed with its scene resources'
assert.equal(deletedEffects, 1, 'destroy deletes the public effect handle');
assert.equal(effectConfig.gevInstallationsDrape.owner.meshes.size, 0);
});
test('the card label reads in the UI language, as on Cesium', () => {
const record = {
id: 'osm:node:4',
longitude: 10,
latitude: 50,
class: 'naval_base',
name: 'Firing range',
};
const state = {
enabled: true,
records: [record],
recordById: new Map([[record.id, record]]),
selectedId: null,
};
const contexts = new Map();
const set = { setCount() {}, set() {}, commit() {}, destroy() {} };
const renderer = createInstallationsNavaraRenderer(
{
movers: { createSet: () => set },
camera: { height: () => 1_000 },
render: { request() {} },
},
{
state,
services: {
context: {
getSelectedEntityContext: () => null,
registerEntityContext: (entity, value) =>
contexts.set(value.id, entity),
selectEntityContext() {},
removeEntityContextsForLayer() {},
clearSelectedEntityContextForLayer() {},
},
picking: { registerPickOwner() {}, unregisterPickOwner() {} },
},
parts: {
model: {
colorFor: () => ({
red: 0.6,
green: 0.6,
blue: 0.6,
alpha: 1,
toCssColorString: () => '#9ca6b0',
}),
installationSurfaceHeightM: () => 0,
installationSourceLabel: () => 'OpenStreetMap',
},
namedMarkers: { sync() {}, enable() {}, hide() {}, destroy() {} },
viewport: { clearUnavailableRetry() {} },
ingestion: { setInstallationStatus() {} },
},
source: {},
overlayHost: { setVisible() {}, setEntries() {}, clearSource() {} },
view: {
canvas: { clientHeight: 800 },
camera: { raw: { fov: 60 } },
registerEffect() {},
addEffect: () => ({ update() {}, delete() {} }),
},
EffectDesc: class {},
NavaraEffect: class {},
DrapedMesh: Mesh,
setupMaterialForDrape() {},
},
);
try {
setLocale('ru');
renderer.lifecycle.methods.enable();
const label = contexts.get(record.id).gevLabelModel;
assert.equal(label.title, installationTitle('Firing range'));
assert.notEqual(label.title, 'Firing range');
assert.equal(label.details[0], installationClassLabel('naval_base'));
assert.notEqual(label.details[0], 'NAVAL BASE');
} finally {
setLocale('en');
renderer.lifecycle.methods.destroy();
}
});