Merge branch 'main' into fix/b-vessel-course-north

This commit is contained in:
Sameh Khamis committed 2026-11-01 21:16:27 -08:00
commit 87e2a2eaa8
7 files changed
+47 -4

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).
@@ -0,0 +1,3 @@
- Show a transit vehicle's reported bearing of 359.5° or more as 0°. The
selected-vehicle card rounded it without wrapping, so a bus heading 359.6°
read "reported hdg 360°" (Aditya, @Rohilalala, #1452).
+6 -2
View File
@@ -960,7 +960,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,
@@ -4490,7 +4492,9 @@ to 0.45 at two minutes, 0.20 at ten minutes and 0.08 at fifteen minutes.
display delay and newest-report age, reported speed/stop, vehicle ID, and display delay and newest-report age, reported speed/stop, vehicle ID, and
available trail duration. Report age is rounded to whole seconds and stays in seconds while that rounded available trail duration. Report age is rounded to whole seconds and stays in seconds while that rounded
count is below 90; otherwise it shows completed minutes calculated from the count is below 90; otherwise it shows completed minutes calculated from the
rounded second count. Its anchor follows the rendered marker; text is rounded second count. The reported heading wraps after rounding to whole
degrees, so a bearing of 359.5° or more reads `hdg 0°` rather than `hdg 360°`.
Its anchor follows the rendered marker; text is
checked at 4 Hz and published when it changes. DETECT labels retain route, checked at 4 Hz and published when it changes. DETECT labels retain route,
mode and motion, with the selected label suppressed beside its fuller card. mode and motion, with the selected label suppressed beside its fuller card.
Candidate membership changes on revisions, coalesced at 4 Hz; paints read Candidate membership changes on revisions, coalesced at 4 Hz; paints read
+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]
+4 -1
View File
@@ -405,9 +405,12 @@ export function buildTransitSelectionCopy(
reportedMotion.push( reportedMotion.push(
t('reported {speed} km/h', { speed: Math.round(record.speedMps * 3.6) }), t('reported {speed} km/h', { speed: Math.round(record.speedMps * 3.6) }),
); );
// 359.5° and above rounds to north; modulo keeps it from printing 360°.
if (Number.isFinite(record.bearing)) if (Number.isFinite(record.bearing))
reportedMotion.push( reportedMotion.push(
t('reported hdg {heading}°', { heading: Math.round(record.bearing) }), t('reported hdg {heading}°', {
heading: Math.round(record.bearing) % 360,
}),
); );
// The operator's own claim, kept as a REPORT rather than as the live state. // The operator's own claim, kept as a REPORT rather than as the live state.
// It describes the moment of the report, which the display may not have // It describes the moment of the report, which the display may not have
+12
View File
@@ -43,6 +43,18 @@ test('the selection card calls a Toronto 501 a streetcar', () => {
assert.doesNotMatch(copy.details.join(' | '), /Tram/); assert.doesNotMatch(copy.details.join(' | '), /Tram/);
}); });
test('the selection card reads a bearing that rounds to north as hdg 0°', () => {
const hdg = (bearing) =>
buildTransitSelectionCopy(
ttc(),
{ id: '4400', routeId: '501', timestamp: 0, bearing },
'tram',
0,
).details.find((line) => line.includes('hdg'));
assert.equal(hdg(359.4), 'reported hdg 359°');
assert.equal(hdg(359.6), 'reported hdg 0°');
});
test('detection labels carry the operator vocabulary within the field width', () => { test('detection labels carry the operator vocabulary within the field width', () => {
const klass = transitDetectionClass('tram', ttc()); const klass = transitDetectionClass('tram', ttc());
assert.equal(klass, 'STREETCAR TTC'); assert.equal(klass, 'STREETCAR TTC');