Merge pull request #1441 from Rohilalala/fix/e-cctv-heading-north

fix(cctv): show a camera facing 359.6° as HDG 0°, not HDG 360°
This commit is contained in:
Sameh Khamis authored and GitHub committed 2026-11-01 21:41:39 -08:00
commit 13a04a8957
6 files changed
+41 -3

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
- Show a CCTV camera facing 359.5° or more as HDG 0°. The HUD heading token
and the camera panel rounded the bearing without wrapping, so a camera
facing 359.6° read "HDG 360°" (Aditya, @Rohilalala, #1441).
+3 -1
View File
@@ -4930,7 +4930,9 @@ silently demoting every later lookup for the session.
wireframe dashed; colours, widths and active/idle emphasis are unchanged. A manual calibration wireframe dashed; colours, widths and active/idle emphasis are unchanged. A manual calibration
(`calSource: 'manual'`) or curated pose (`poseSource: 'curated'`) is never presented as (`calSource: 'manual'`) or curated pose (`poseSource: 'curated'`) is never presented as
estimated, matching the CAL badge. Public camera state carries `headingConfidence` and estimated, matching the CAL badge. Public camera state carries `headingConfidence` and
`headingEstimated` (`src/layers/cctv/headingConfidence.js`, #639). `headingEstimated` (`src/layers/cctv/headingConfidence.js`, #639). The HUD token and the
camera panel's meta line wrap the bearing after rounding to whole degrees, so a camera
facing 359.5° or more reads `HDG 0°` rather than `HDG 360°`.
- **CCTV identified hardware:** a camera whose source or seed names a hardware `model` that - **CCTV identified hardware:** a camera whose source or seed names a hardware `model` that
resolves in `src/layers/cctv/modelSpecs.js` (vendored CC0 records from the CCTV Camera resolves in `src/layers/cctv/modelSpecs.js` (vendored CC0 records from the CCTV Camera
Database) gets the model's specs appended to the HUD meta line. Only fixed single-lens Database) gets the model's specs appended to the HUD meta line. Only fixed single-lens
+2 -1
View File
@@ -44,7 +44,8 @@ export function isHeadingEstimated(camera) {
* @returns {string} * @returns {string}
*/ */
export function headingHudToken(camera) { export function headingHudToken(camera) {
const hdg = Math.round(Number(camera?.headingDeg) || 0); // 359.5° and above rounds to north; modulo keeps it from printing 360°.
const hdg = Math.round(Number(camera?.headingDeg) || 0) % 360;
return isHeadingEstimated(camera) return isHeadingEstimated(camera)
? t('HDG {value}° (ESTIMATED)', { value: hdg }) ? t('HDG {value}° (ESTIMATED)', { value: hdg })
: t('HDG {value}°', { value: hdg }); : t('HDG {value}°', { value: hdg });
@@ -44,6 +44,15 @@ test('HUD token tags a synthetic bearing and rounds the degrees', () => {
); );
}); });
test('HUD token reads a bearing that rounds to north as 0°, not 360°', () => {
assert.equal(headingHudToken({ headingDeg: 359.4 }), 'HDG 359°');
assert.equal(headingHudToken({ headingDeg: 359.6 }), 'HDG 0°');
assert.equal(
headingHudToken({ headingDeg: 359.5, headingConfidence: 'low' }),
'HDG 0° (ESTIMATED)',
);
});
test('HUD token stays untagged for surveyed and calibrated bearings', () => { test('HUD token stays untagged for surveyed and calibrated bearings', () => {
assert.equal( assert.equal(
headingHudToken({ headingDeg: 67.5, headingConfidence: 'high' }), headingHudToken({ headingDeg: 67.5, headingConfidence: 'high' }),
+23
View File
@@ -490,3 +490,26 @@ test('the scene summary types in, then holds the whole text', async (t) => {
await nextTick(); await nextTick();
assert.equal(byId('cctv-summary').textContent, text); assert.equal(byId('cctv-summary').textContent, text);
}); });
test('the panel meta line reads a bearing that rounds to north as HDG 0°', () => {
const meta = (headingDeg) => {
const camera = {
id: 'a',
city: 'Austin',
headingDeg,
fovDeg: 74,
rangeM: 700,
};
return presentCctvPanel(
{
enabled: true,
activeCameraId: camera.id,
activeCamera: camera,
cameras: [camera],
},
true,
).meta;
};
assert.match(meta(359.4), /· HDG 359° ·/);
assert.match(meta(359.6), /· HDG 0° ·/);
});
+1 -1
View File
@@ -107,7 +107,7 @@ function metaText(state, enabled, activeCamera, catalogCount) {
'{city} · HDG {heading} · FOV {fov} · RANGE {range}m · {projection}{calBadge} · {provider}{status}', '{city} · HDG {heading} · FOV {fov} · RANGE {range}m · {projection}{calBadge} · {provider}{status}',
{ {
city: activeCamera.city, city: activeCamera.city,
heading: `${Math.round(activeCamera.headingDeg)}°`, heading: `${Math.round(activeCamera.headingDeg) % 360}°`,
fov: `${Math.round(activeCamera.fovDeg)}°`, fov: `${Math.round(activeCamera.fovDeg)}°`,
range: Math.round(activeCamera.rangeM), range: Math.round(activeCamera.rangeM),
projection: projLabel, projection: projLabel,