Commit Graph
23 Commits
Author SHA1 Message Date
bassem chagra b7d25f4c69 Merge origin/main into feat/wsdot-camera-pack
Resolve the source-pack conflicts with Statens vegvesen (keep both packs).
The Norway pack's 900 default pushed the per-pack defaults to 5,204, past
the 4,500 catalog cap this PR enforces, so raise DEFAULT_CCTV_MAX_SOURCES
to 5,500 and CCTV_MAX_SOURCES_CEILING to 6,000.
2026-10-08 15:59:09 +02:00
bassem chagra 054f1cc198 Merge remote-tracking branch 'origin/main' into feat/king-county-camera-pack
# Conflicts:
#	.env.example
#	CHANGELOG.md
#	DATA_SOURCES.md
#	README.md
#	docs/CURRENT-STATE.md
#	server/providers/cctv/catalog.js
#	server/providers/cctv/sources.js
#	src/data/dataCredits.js
2026-10-08 15:52:38 +02:00
bassem chagra e55dcc90d3 Merge remote-tracking branch 'origin/main' into feat/cctv-model-spec-enrichment
# Conflicts:
#	CHANGELOG.md
2026-10-08 15:45:27 +02:00
bassem chagra 03929f1a00 docs(cctv): refresh the CCTV Camera Database size in the model-specs module
The dataset has grown from 12,000+ to 28,000+ camera models (v2.26.0, 75%
with a field of view). The 17 vendored records are unchanged in v2.26.0.
2026-10-01 12:34:07 +02:00
bassem chagra bfbaa88976 Merge remote-tracking branch 'origin/main' into feat/cctv-model-spec-enrichment
# Conflicts:
#	CHANGELOG.md
2026-10-01 12:24:42 +02:00
bassem chagra 0cfb795845 Merge remote-tracking branch 'origin/main' into feat/wsdot-camera-pack
# Conflicts:
#	CHANGELOG.md
#	docs/CURRENT-STATE.md
2026-10-01 12:23:41 +02:00
bassem chagra 031df60014 Merge remote-tracking branch 'origin/main' into feat/king-county-camera-pack
# Conflicts:
#	CHANGELOG.md
#	docs/CURRENT-STATE.md
2026-10-01 12:20:32 +02:00
bassem chagra 3273fb2431 Merge branch 'main' into feat/king-county-camera-pack 2026-09-28 10:19:25 +02:00
bassem chagra 3bcd5010a3 Merge branch 'main' into feat/wsdot-camera-pack 2026-09-28 10:19:15 +02:00
bassem chagra 6625b48d5a Merge branch 'main' into feat/cctv-model-spec-enrichment 2026-09-28 10:19:01 +02:00
bassem chagra cce159c93d docs(cctv): document identified-hardware specs and datasheet FOV in CHANGELOG and CURRENT-STATE
CONTRIBUTING requires runtime-behavior changes to update docs/CURRENT-STATE.md
and CHANGELOG.md in the same PR.
2026-09-27 12:24:13 +00:00
bassem chagra 5dfe3f0525 docs(cctv): add the King County pack to CHANGELOG, CURRENT-STATE, README and .env.example
CONTRIBUTING requires runtime-behavior changes to update docs/CURRENT-STATE.md
and CHANGELOG.md in the same PR, and new env controls belong in .env.example
with the other CCTV pack controls. The README and CURRENT-STATE table diffs
are column realignment from the widened source cell.
2026-09-27 12:23:23 +00:00
bassem chagra c4f18169eb 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.
2026-09-27 12:20:45 +00:00
bassem chagra 5b2e160e54 docs(cctv): add the WSDOT pack to CHANGELOG, CURRENT-STATE and README
CONTRIBUTING requires runtime-behavior changes to update docs/CURRENT-STATE.md
and CHANGELOG.md in the same PR. The README and CURRENT-STATE table diffs are
column realignment from the widened source cell.
2026-09-27 12:15:13 +00:00
bassem chagra ffa1891b01 fix(cctv): keep estimated FOV for PTZ/varifocal hardware; carry datasheet range as capability
A PTZ/varifocal datasheet FOV is a capability range, not the lens's
current zoom, so replacing the frustum's estimate with its wide end
presented unknown optical state as known. Parse the full horizontal
range (ignoring vertical, "combined" panoramic and lens-note figures),
carry it as specFovMinDeg/specFovMaxDeg, and substitute the datasheet
FOV only for fixed single-lens hardware. The HUD labels the range as
capability beside the estimate. This also stops the Q6000-E Mk II's
"360 combined" figure from being read as a 360-degree lens.
2026-09-27 12:09:27 +00:00
bassem chagra c3f197823e fix(cctv): serve King County model/manufacturer through /api/cctv/sources
The loader and normalizer carried the hardware model, but the /sources
response projection and the browser catalog both whitelist fields and
dropped it, so no client consumer ever saw it. Project model and
manufacturer (manufacturer disambiguates bare model strings like "3950")
at both boundaries, and add a test that drives the real middleware and
browser catalog end to end.
2026-09-27 12:09:27 +00:00
bassem chagra 481d0c327a fix(cctv): map WSDOT diagonal and BW compass codes; document WSDOT env
WSDOT's layer metadata defines BW (Bothways) plus the diagonals
NE/SE/SW/NW alongside N/E/S/W. Diagonal cameras were being demoted to the
low-confidence id-hash fallback; map them to 45/135/225/315 degrees and
treat BW (not B) as the no-single-facing code. Add CCTV_WSDOT_ENABLED and
CCTV_WSDOT_MAX_SOURCES to .env.example with the other pack controls.
2026-09-27 12:09:27 +00:00
bassem chagra 0533f78be4 feat(cctv): resolve verbose registry model strings via token fallback
Some catalogs publish a full product title rather than the bare model, e.g.
"AXIS Q6155-E PTZ Dome Network Camera" — which the exact model/alias match
misses. Add a fallback: after the exact match, scan for a record whose
DISTINCTIVE token (a >=4-char model/alias word containing a digit or hyphen,
so "dome"/"network"/"ptz"/"hd" never qualify) appears as a whole word in the
query. The most specific (longest) matched token wins; if two records tie on
the longest matched token the result is ambiguous and returns null, so the
lookup never guesses. Exact matches are unchanged and still take precedence.
2026-09-25 12:53:09 +02:00
bassem chagra 97118f4781 feat(cctv): vendor the model specs the integrated catalogs publish
Grow the enrichment table from the 4-model starter to the hardware the
currently integrated public catalogs actually publish, so identified
cameras render datasheet FOV cones and spec cards instead of estimates:

- King County (WA) road cameras: Cohu 3950 / 3960 (3965) / 3920 iDome
  (3925), Bosch AUTODOME IP 5000i.
- Sarasota County (FL) + Sioux Falls (SD) open data: Bosch AUTODOME IP
  starlight 7000i / starlight 7000 HD / dynamic 7000 HD, the VG4 AUTODOME
  (500i), Bosch AUTODOME 800 (VG5-836), and Axis Q6155-E, Q6054-E,
  Q6000-E Mk II, and P3707-PE.

All records are vendored verbatim from the CCTV Camera Database (CC0),
each transcribed from a manufacturer datasheet. Analog Cohu positioners
carry no fov_deg (their datasheets publish none), so they enrich the HUD
specs without a datasheet cone; every other model carries a real FOV.

Adds a test asserting the vendored table covers each integrated catalog's
published hardware strings.
2026-09-25 12:36:34 +02:00
bassem chagra ccdf8f0dc2 feat(cctv): enrich identified cameras with datasheet FOV + specs
When a CCTV source pack (or a seed) names its hardware `model`, resolve it
against a camera-model spec table: the coverage frustum then uses the
manufacturer's horizontal FOV instead of the pose estimate, and the HUD meta
line gains the model's resolution, night-vision, and PTZ specs. Cameras with
no identified model are unchanged — unknown never implies anything, matching
the layer's existing honesty about RAW PRIOR poses.

Spec records are vendored verbatim from the CCTV Camera Database
(https://github.com/ch-bas/cctv-camera-database, CC0-1.0), an open dataset of
12,000+ camera models transcribed from manufacturer datasheets. This lands a
small starter table plus the join + parsing helpers; source packs that publish
their hardware can grow it. Records describe camera products (optics,
resolution, night vision) only — no endpoints or access data.

- src/layers/cctv/modelSpecs.js: spec table, case-insensitive/alias lookup,
  wide-end FOV parser, HUD summary formatter
- src/layers/cctv/modelSpecs.test.mjs: lookup/alias/unknown, FOV parsing
  across the real fov string shapes, summary rendering
- src/layers/cctv/catalog.js: apply enrichment in both catalog builders
- src/layers/cctv/presentation.js: label a datasheet FOV, append the spec
  summary, and pass fovSource + spec through the public camera state
2026-09-25 12:36:34 +02:00
bassem chagra 5b00dd0980 feat(cctv): King County road camera source pack (Washington State)
Adds King County DOT Road Services' cameras as a keyless source pack, from
the county's public ArcGIS FeatureServer layer ("King County and WSDOT
Traffic Cameras"). 59 live cameras against the current feed.

- The `outFields` list is explicit on purpose: the layer also carries
  asset-administration columns (including staff names) that must never be
  fetched.
- Only live, active cameras with frames on the county's own
  info.kingcounty.gov image handler are registered; the feed writes http://
  and the handler serves HTTPS, so URLs are upgraded and pinned before
  registration. WSDOT-owned rows are skipped so a camera never appears twice
  when a statewide WSDOT pack is present; the few city-owned cameras the
  county hosts (Redmond, Sammamish, Newcastle, Snoqualmie) carry their owner
  as the per-camera credit, DriveBC-style.
- The layer publishes no facing: headings use the shared id-hash fallback at
  low confidence, correctable with the calibration gizmo.
- Uniquely among the packs, the county publishes each camera's hardware
  Manufacturer/Model (Cohu 3950/3965, Bosch AUTODOME IP 5000i, Axis
  Q6135-LE, Siqura PD910). The model string is whitespace-collapsed (the
  feed carries stray newlines) and carried on the source as `model`, with an
  additive passthrough in normalizeSourceItem (same pattern as poseSource)
  so identified-hardware consumers can use it. All 59 live cameras carry a
  model value.
- Attribution per the KCGIS Center terms, registered in the attribution
  popover and documented in DATA_SOURCES.md.
- Defaults: the whole catalog loads (125 cap); CCTV_KINGCOUNTY_MAX_SOURCES
  changes the cap, CCTV_KINGCOUNTY_ENABLED=0 turns the pack off.
- Tests: filtering (owner/live/status/host/geometry/bbox), HTTPS upgrade,
  model whitespace-collapse and passthrough, partner-city credit, env cap,
  upstream-failure fallback, and normalizeSourceItem model semantics.
2026-09-25 12:35:06 +02:00
bassem chagra ee3e3c7a41 feat(cctv): WSDOT highway camera source pack (Washington State)
Adds Washington State's highway cameras as a keyless source pack, from the
Esri JSON layer WSDOT's own Travel Center map consumes
(data.wsdot.wa.gov/travelcenter/Cameras.json, ~1,700 point features).
Closes #624.

- Geometry arrives in EPSG:3857; converted to WGS84 on load and bounded to
  a Washington bounding box.
- Only frames on WSDOT's own images.wsdot.wa.gov host are registered — the
  catalog also lists ~70 partner cameras (Oregon DOT, City of Seattle,
  lodges, airports) whose own terms apply; those are skipped rather than
  proxied. 1,630 cameras survive the filters against the live feed.
- CompassDirection letters (N/E/S/W) give a high-confidence heading; the
  majority `B` ("both directions") rows carry no single facing and use the
  shared id-hash fallback at low confidence, with the usual two pose
  personalities.
- The feed body carries Windows-1252 bytes (en-dashes in titles) with no
  charset declaration, so it is decoded explicitly before parsing.
- WSDOT's layer metadata marks the feed "low volume": the 15-minute catalog
  cache and per-request frame fetches respect that, frames are never stored
  or redistributed, and the WSDOT credit is registered in the attribution
  popover. A keyed WSDOT Traveler Information API serves the same population
  for higher-volume use and is deliberately not required.
- Defaults: 250 cameras nearest Seattle and Spokane; CCTV_WSDOT_MAX_SOURCES
  changes the cap, CCTV_WSDOT_ENABLED=0 turns the pack off.
- Tests: Mercator conversion against a real layer feature, official-host and
  malformed-row filtering, compass mapping and both pose personalities,
  cp1252 decoding, env cap, upstream-failure fallback.
2026-09-25 12:32:36 +02:00
bassem chagra 5f27f6edf4 fix(cctv): surface heading confidence — estimated bearings are now visible
Every pack sets headingConfidence on each camera, but nothing consumed it:
a bearing synthesized by fallbackHeadingFromId (a hash of the id string,
no geographic input) rendered exactly like a surveyed one — 2,827 of 3,997
cameras in a default catalog. Fixes #639.

Two consumers, plus a passthrough:

- HUD: the heading token is tagged when the bearing is synthetic —
  `HDG 194° (ESTIMATED)` — so a hash never reads as a measured facing.
- Wireframe: a low-confidence camera draws its coverage frustum with dashed
  polylines instead of solid ones. Colors, widths, and the active/idle
  emphasis are unchanged; the dash is the only signal.
- Public camera state gains `headingConfidence` (the raw pack flag) and
  `headingEstimated` (the derived bit) so UI consumers never re-derive it.

A human-vouched pose always wins over the pack flag: a manually saved
calibration (calSource:'manual') or a curated catalog entry
(poseSource:'curated') is never presented as estimated, mirroring the CAL
badge states in calibration.js.

- src/layers/cctv/headingConfidence.js: pure helpers (isHeadingEstimated,
  headingHudToken)
- src/layers/cctv/headingConfidence.test.mjs: confidence normalization,
  calibration/curation overrides, HUD token formatting
- src/layers/cctv/presentation.js: HUD token + public-state passthrough
- src/layers/cctv/rendering.js: dashed materials for estimated bearings
2026-09-17 10:20:26 +02:00