Merge pull request #1451 from Rohilalala/fix/e-alpr-direction-360

fix(alpr): show a 359.6° camera bearing as 0°, not 360°
This commit is contained in:
Sameh Khamis authored and GitHub committed 2026-11-01 21:16:18 -08:00
commit 87e5cf3f93
4 files changed
+25 -2

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
- Show an ALPR camera's mapped bearing of 359.5° or more as DIRECTION 0°
rather than DIRECTION 360° (Aditya, @Rohilalala, #1451).
+3 -1
View File
@@ -958,7 +958,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]