fix(alpr): show a 359.6° camera bearing as 0°, not 360°

The selected ALPR card rounded a mapped camera:direction without
wrapping, so a camera facing 359.5° or more read "DIRECTION 360°". The
rounded bearing now wraps to 0°.
This commit is contained in:
Aditya committed 2026-10-28 15:15:16 +05:30
1 parent a4d3701c3c
commit 065d7f4f8b
3 files changed
+23 -2

No files matched your search

+3 -1
View File
@@ -894,7 +894,9 @@ change. Once the camera is still and tiles have settled, each overlay anchor is
replaced once by the rendered surface under it (two depth samples per paint, replaced once by the rendered surface under it (two depth samples per paint,
accepted only within -30/+40 m of the floor cell), so badges do not float or accepted only within -30/+40 m of the floor cell), so badges do not float or
sink on slopes; wedge corners follow their own floor cells instead of staying sink on slopes; wedge corners follow their own floor cells instead of staying
level with the apex. Unknown bearings never acquire a wedge. The row reads level with the apex. Unknown bearings never acquire a wedge. The selected
card's `DIRECTION` wraps after rounding to whole degrees, so a mapped bearing
of 359.5° or more reads `0°` rather than `360°`. The row reads
`N nearby · M on screen`; when every loaded camera is outside the view it adds `N nearby · M on screen`; when every loaded camera is outside the view it adds
"None on screen — nearby cameras are outside the view" instead of looking "None on screen — nearby cameras are outside the view" instead of looking
unloaded. `scripts/qa-alpr-journey.mjs` drives an Austin journey with ALPR off, unloaded. `scripts/qa-alpr-journey.mjs` drives an Austin journey with ALPR off,
+18
View File
@@ -2,6 +2,7 @@ import test from 'node:test';
import assert from 'node:assert/strict'; import assert from 'node:assert/strict';
import * as Cesium from 'cesium'; import * as Cesium from 'cesium';
import { createAlprOverlay } from './overlay.js'; import { createAlprOverlay } from './overlay.js';
import { normalizeAlprNode } from './records.js';
import { import {
directionWedgePositions, directionWedgePositions,
alprLabelDetails, alprLabelDetails,
@@ -218,6 +219,23 @@ test('adapter labels never claim a custom source is public OSM data', () => {
); );
}); });
test('a mapped bearing that rounds up to a whole turn reads 0°, not 360°', () => {
const direction = (tag) =>
alprLabelDetails(
normalizeAlprNode({
type: 'node',
id: 1,
lat: 30,
lon: -97,
tags: { 'surveillance:type': 'ALPR', 'camera:direction': tag },
}),
{ label: 'Custom directory' },
).find((line) => line.startsWith('DIRECTION'));
assert.equal(direction('359.6'), 'DIRECTION 0°');
assert.equal(direction('359.4'), 'DIRECTION 359°');
assert.equal(direction('0.4'), 'DIRECTION 0°');
});
test('overlay glyphs and native badges shrink alike with distance', () => { test('overlay glyphs and native badges shrink alike with distance', () => {
assert.equal(markerScale(100), 1.15); assert.equal(markerScale(100), 1.15);
assert.equal(markerScale(40_000), 0.45); assert.equal(markerScale(40_000), 0.45);
+2 -1
View File
@@ -49,8 +49,9 @@ export function alprLabelDetails(record, source) {
const details = [ const details = [
sourceName === 'OpenStreetMap' ? 'OSM MAPPED' : `Source: ${sourceName}`, sourceName === 'OpenStreetMap' ? 'OSM MAPPED' : `Source: ${sourceName}`,
]; ];
// Wrap after rounding: 359.6° rounds to 360°, which is north.
if (Number.isFinite(record.directionDeg)) if (Number.isFinite(record.directionDeg))
details.push(`DIRECTION ${Math.round(record.directionDeg)}°`); details.push(`DIRECTION ${Math.round(record.directionDeg) % 360}°`);
const equipment = [ const equipment = [
...new Set( ...new Set(
[record.manufacturer, record.operator, record.cameraType] [record.manufacturer, record.operator, record.cameraType]