fix(cctv): raise the default catalog cap to 4,500 and enforce it over every pack

The per-pack defaults already summed to 4,054 before WSDOT (4,304 with
it), past the documented 4,000 cap, so a default install was trimming.
The existing sum check in the DriveBC test listed packs by hand and
missed Calgary and DelDOT. Replace it with a check in
cctvCatalogCap.test.mjs that sums every exported DEFAULT_*_MAX_SOURCES,
and drop the Calgary test that pinned the cap at exactly 4,000.
This commit is contained in:
bassem chagra committed 2026-09-27 12:20:45 +00:00
1 parent 5b2e160e54
commit c4f18169eb
7 files changed
+37 -53

No files matched your search

+1 -1
View File
@@ -182,6 +182,6 @@ AISSTREAM_API_KEY=
# CCTV_CALGARY_ROWS_URL= # override the Open Calgary catalog URL # CCTV_CALGARY_ROWS_URL= # override the Open Calgary catalog URL
# CCTV_WSDOT_ENABLED=1 # set 0 to disable the WSDOT (Washington State) highway cameras # CCTV_WSDOT_ENABLED=1 # set 0 to disable the WSDOT (Washington State) highway cameras
# CCTV_WSDOT_MAX_SOURCES=250 # cap cameras loaded from the WSDOT catalog, nearest Seattle/Spokane first # CCTV_WSDOT_MAX_SOURCES=250 # cap cameras loaded from the WSDOT catalog, nearest Seattle/Spokane first
# CCTV_MAX_SOURCES=4000 # catalog-wide ceiling, shared round-robin across packs # CCTV_MAX_SOURCES=4500 # catalog-wide ceiling, shared round-robin across packs
# CCTV_PREFER_AUSTIN=1 # prioritize Austin cameras in the registry # CCTV_PREFER_AUSTIN=1 # prioritize Austin cameras in the registry
# CCTV_FORCE_AUSTIN= # force live default sources when a source-pack JSON is configured # CCTV_FORCE_AUSTIN= # force live default sources when a source-pack JSON is configured
+5 -1
View File
@@ -7,7 +7,11 @@
diagonal, get a high-confidence heading; `BW` (Bothways) cameras use the diagonal, get a high-confidence heading; `BW` (Bothways) cameras use the
shared id-hash fallback at low confidence. By default the 250 cameras nearest shared id-hash fallback at low confidence. By default the 250 cameras nearest
Seattle and Spokane load; `CCTV_WSDOT_MAX_SOURCES` sets the cap and Seattle and Spokane load; `CCTV_WSDOT_MAX_SOURCES` sets the cap and
`CCTV_WSDOT_ENABLED=0` turns the pack off (bassem chagra, #644). `CCTV_WSDOT_ENABLED=0` turns the pack off. The default catalog cap
(`CCTV_MAX_SOURCES`) rises from 4,000 to 4,500: the per-pack defaults already
summed to 4,054 before WSDOT, so a default install was being trimmed. A test
now sums every pack's default and fails if they exceed the cap
(bassem chagra, #644).
- CCTV cameras whose bearing is a guess now say so. Packs mark bearings derived - CCTV cameras whose bearing is a guess now say so. Packs mark bearings derived
from a hash of the camera id as `headingConfidence: 'low'`, but nothing read the from a hash of the camera id as `headingConfidence: 'low'`, but nothing read the
+1 -1
View File
@@ -3422,7 +3422,7 @@ silently demoting every later lookup for the session.
kill switch `CCTV_FINTRAFFIC_ENABLED=0`) are the fourth pack: one keyless GeoJSON station list kill switch `CCTV_FINTRAFFIC_ENABLED=0`) are the fourth pack: one keyless GeoJSON station list
covering the whole country, where one _preset_ (a station's fixed view) is one camera — 806 covering the whole country, where one _preset_ (a station's fixed view) is one camera — 806
GATHERING stations carry 2,256 in-collection presets, prioritized to 300 against seven anchors GATHERING stations carry 2,256 in-collection presets, prioritized to 300 against seven anchors
on the main road spine. `CCTV_MAX_SOURCES` is a 4,000 catalog-wide ceiling shared round-robin on the main road spine. `CCTV_MAX_SOURCES` is a 4,500 catalog-wide ceiling (4,000 until the WSDOT pack) shared round-robin
across packs, so a lower global cap thins every region instead of starving the last pack. across packs, so a lower global cap thins every region instead of starving the last pack.
~1,100 cameras total, all RAW PRIOR poses, stills-first. ~1,100 cameras total, all RAW PRIOR poses, stills-first.
- **CCTV v3 UX — viewshed + calibration gizmo** (built 2026-07-05 and field - **CCTV v3 UX — viewshed + calibration gizmo** (built 2026-07-05 and field
+5 -2
View File
@@ -9,9 +9,12 @@ export const DEFAULT_AUSTIN_MAX_SOURCES = 250;
* itself (nearest-to-anchor first); this bound only matters when the packs * itself (nearest-to-anchor first); this bound only matters when the packs
* together exceed it, and it is then filled round-robin across packs (see * together exceed it, and it is then filled round-robin across packs (see
* cap.js) so no region is silently dropped. Sized above the sum of the * cap.js) so no region is silently dropped. Sized above the sum of the
* default per-pack caps so a default install never trims. * default per-pack caps so a default install never trims (enforced by
* cctvCatalogCap.test.mjs). The per-pack defaults reached 4,304 with WSDOT,
* so this is 4,500: room for one more small pack while staying under
* CCTV_MAX_SOURCES_CEILING, which also sizes the health map.
*/ */
export const DEFAULT_CCTV_MAX_SOURCES = 4000; export const DEFAULT_CCTV_MAX_SOURCES = 4500;
/** Hard upper bound for CCTV_MAX_SOURCES; also sizes the health map. */ /** Hard upper bound for CCTV_MAX_SOURCES; also sizes the health map. */
export const CCTV_MAX_SOURCES_CEILING = 5000; export const CCTV_MAX_SOURCES_CEILING = 5000;
/** Reference point for Austin camera prioritization (Congress & 6th). */ /** Reference point for Austin camera prioritization (Congress & 6th). */
-5
View File
@@ -12,7 +12,6 @@ import {
CALGARY_IMAGE_ORIGIN, CALGARY_IMAGE_ORIGIN,
CALGARY_MAX_CATALOG_BYTES, CALGARY_MAX_CATALOG_BYTES,
DEFAULT_CALGARY_ROWS_URL, DEFAULT_CALGARY_ROWS_URL,
DEFAULT_CCTV_MAX_SOURCES,
} from '../../server/providers/cctv/constants.js'; } from '../../server/providers/cctv/constants.js';
import { CAMERA_CODE_MAX_CHARS } from '../../server/providers/cctv/normalize.js'; import { CAMERA_CODE_MAX_CHARS } from '../../server/providers/cctv/normalize.js';
import { allocateSourceCap } from '../../server/providers/cctv/cap.js'; import { allocateSourceCap } from '../../server/providers/cctv/cap.js';
@@ -383,7 +382,3 @@ test('CCTV_CALGARY_ENABLED=0 keeps the lane from being loaded at all', async (t)
Object.assign(process.env, saved); Object.assign(process.env, saved);
} }
}); });
test('the shipped catalog ceiling is not raised to make room for this pack', () => {
assert.equal(DEFAULT_CCTV_MAX_SOURCES, 4000);
});
+24 -4
View File
@@ -4,10 +4,9 @@ import {
allocateSourceCap, allocateSourceCap,
resolveCatalogCap, resolveCatalogCap,
} from '../../server/providers/cctv/cap.js'; } from '../../server/providers/cctv/cap.js';
import { import * as constants from '../../server/providers/cctv/constants.js';
CCTV_MAX_SOURCES_CEILING,
DEFAULT_CCTV_MAX_SOURCES, const { CCTV_MAX_SOURCES_CEILING, DEFAULT_CCTV_MAX_SOURCES } = constants;
} from '../../server/providers/cctv/constants.js';
import { createCctvCatalog } from '../../server/providers/cctv/catalog.js'; import { createCctvCatalog } from '../../server/providers/cctv/catalog.js';
const pack = (name, count, prefix = name) => ({ const pack = (name, count, prefix = name) => ({
@@ -135,3 +134,24 @@ test('the catalog applies the fair cap to configured packs end to end', async ()
Object.assign(process.env, saved); Object.assign(process.env, saved);
} }
}); });
test('default per-pack camera caps fit inside the default catalog cap', () => {
// Every exported DEFAULT_<PACK>_MAX_SOURCES counts, so a new pack cannot
// slip past this check by not being listed (Warendorf ships 1 curated
// camera and has no cap constant).
const packCaps = Object.entries(constants).filter(
([name]) =>
/^DEFAULT_[A-Z_]+_MAX_SOURCES$/.test(name) &&
name !== 'DEFAULT_CCTV_MAX_SOURCES',
);
assert.ok(packCaps.length >= 13, 'every pack cap constant is found');
const total = packCaps.reduce((sum, [, cap]) => sum + cap, 0) + 1;
assert.ok(
total <= DEFAULT_CCTV_MAX_SOURCES,
`default packs (${total}) would be thinned by the catalog cap (${DEFAULT_CCTV_MAX_SOURCES})`,
);
assert.ok(
DEFAULT_CCTV_MAX_SOURCES <= CCTV_MAX_SOURCES_CEILING,
'the default cap must sit inside the CCTV_MAX_SOURCES ceiling',
);
});
+1 -39
View File
@@ -4,21 +4,7 @@ import fs from 'node:fs';
import os from 'node:os'; import os from 'node:os';
import path from 'node:path'; import path from 'node:path';
import { createCctvCatalog } from '../../server/providers/cctv/catalog.js'; import { createCctvCatalog } from '../../server/providers/cctv/catalog.js';
import { import { DRIVEBC_WEBCAMS_URL } from '../../server/providers/cctv/constants.js';
DEFAULT_AUSTIN_MAX_SOURCES,
DEFAULT_CALTRANS_MAX_SOURCES,
DEFAULT_CCTV_MAX_SOURCES,
CCTV_MAX_SOURCES_CEILING,
DEFAULT_DRIVEBC_MAX_SOURCES,
DEFAULT_TFL_MAX_SOURCES,
DEFAULT_ONTARIO_MAX_SOURCES,
DEFAULT_FINTRAFFIC_MAX_SOURCES,
DEFAULT_TXDOT_MAX_SOURCES,
DEFAULT_TALLINN_MAX_SOURCES,
DEFAULT_TARKTEE_MAX_SOURCES,
DEFAULT_NSW_MAX_SOURCES,
DRIVEBC_WEBCAMS_URL,
} from '../../server/providers/cctv/constants.js';
import { import {
driveBcImageCredit, driveBcImageCredit,
loadDriveBcSourcesFromOpenData, loadDriveBcSourcesFromOpenData,
@@ -221,30 +207,6 @@ test('CCTV catalog merges DriveBC cameras and CCTV_DRIVEBC_ENABLED=0 skips the r
); );
}); });
test('default per-pack camera caps fit inside the default catalog cap', () => {
// Every default pack at its own cap (Warendorf ships 1 curated camera).
const packs =
DEFAULT_AUSTIN_MAX_SOURCES +
DEFAULT_CALTRANS_MAX_SOURCES +
DEFAULT_TFL_MAX_SOURCES +
DEFAULT_ONTARIO_MAX_SOURCES +
DEFAULT_FINTRAFFIC_MAX_SOURCES +
DEFAULT_DRIVEBC_MAX_SOURCES +
DEFAULT_TXDOT_MAX_SOURCES +
DEFAULT_TALLINN_MAX_SOURCES +
DEFAULT_TARKTEE_MAX_SOURCES +
DEFAULT_NSW_MAX_SOURCES +
1;
assert.ok(
packs <= DEFAULT_CCTV_MAX_SOURCES,
`default packs (${packs}) would be thinned by the catalog cap (${DEFAULT_CCTV_MAX_SOURCES})`,
);
assert.ok(
DEFAULT_CCTV_MAX_SOURCES <= CCTV_MAX_SOURCES_CEILING,
'the default cap must sit inside the CCTV_MAX_SOURCES ceiling',
);
});
test('DriveBC credit keeps partner attribution and drops operational notes', () => { test('DriveBC credit keeps partner attribution and drops operational notes', () => {
assert.equal( assert.equal(
driveBcImageCredit( driveBcImageCredit(