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.
This commit is contained in:
bassem chagra committed 2026-09-25 12:35:06 +02:00
1 parent b210ab0fe4
commit 5b00dd0980
7 files changed
+341

No files matched your search

+6
View File
@@ -18,6 +18,7 @@ import {
loadNswSourcesFromOpenData,
loadCalgarySourcesFromOpenData,
loadDelDOTSourcesFromOpenData,
loadKingCountySourcesFromOpenData,
} from './sources.js';
/** Env kill switch: unset or anything but "0" means enabled. */
@@ -92,6 +93,11 @@ const LIVE_PACKS = [
enabled: () => envEnabled('CCTV_DELDOT_ENABLED'),
load: loadDelDOTSourcesFromOpenData,
},
{
name: 'kingcounty',
enabled: () => envEnabled('CCTV_KINGCOUNTY_ENABLED'),
load: loadKingCountySourcesFromOpenData,
},
];
/**
* Load CCTV sources from a local JSON file (CCTV_SOURCES_FILE env or default).
+19
View File
@@ -244,6 +244,25 @@ export const DELDOT_ANCHORS = [
{ lat: 39.1582, lon: -75.5244 }, // Dover (Kent)
{ lat: 38.6906, lon: -75.3877 }, // Georgetown (Sussex)
];
/** King County, WA road cameras: keyless public ArcGIS FeatureServer layer
* ("King County and WSDOT Traffic Cameras", King County DOT Road Services).
* GeoJSON with WGS84 point geometry. `outFields` is an explicit list on
* purpose: the layer also carries asset-administration columns (including
* staff names in `ModifiedBy`) that must never be fetched. Uniquely among the
* packs, the county publishes each camera's hardware `Manufacturer`/`Model`
* (Cohu 3950/3965, Bosch AUTODOME, Axis Q6135-LE, …), carried through as the
* source's `model`. */
export const KING_COUNTY_CAMERAS_URL =
'https://services.arcgis.com/Ej0PsM5Aw677QF1W/arcgis/rest/services/TRAFFICCAMERA_POINT_2029/FeatureServer/0/query?where=1%3D1&outFields=AssetID,Location,Description,ImageURL,CamRegion,Live,Model,Manufacturer,Owner,CurrentStatus&f=geojson';
/** Frames come only from the county's own image handler host. */
export const KING_COUNTY_IMAGE_ORIGIN = 'https://info.kingcounty.gov/';
/** The whole catalog is ~125 rows (~59 live county cameras); no thinning by
* default. */
export const DEFAULT_KING_COUNTY_MAX_SOURCES = 125;
/** Prioritization anchor: Renton, roughly central to the county road network. */
export const KING_COUNTY_ANCHORS = [{ lat: 47.4829, lon: -122.2171 }];
/** Camera CATALOGS change rarely; 15 min keeps multi-megabyte upstream list refetches (Austin rows.json + 4 Caltrans districts + TfL + Ontario 511) infrequent. Frames are fetched per-request and are unaffected. */
export const CCTV_SOURCE_CACHE_MS = 15 * 60 * 1000;
/** Per-provider catalog-fetch timeout. Bounds the worst-case refresh so one
+8
View File
@@ -516,5 +516,13 @@ export function normalizeSourceItem(item) {
// badge can distinguish them from raw automated priors (e.g. Austin Open
// Data, which never sets this field). Passed through as-is to the client.
poseSource: item.poseSource === 'curated' ? 'curated' : undefined,
// Optional hardware-model passthrough (additive, like poseSource): packs
// whose catalog publishes the camera's hardware model — King County's
// layer carries Manufacturer/Model per camera — pass it through so
// identified-hardware consumers can use it. Absent everywhere else.
model:
typeof item.model === 'string' && item.model.trim()
? item.model.trim()
: undefined,
};
}
+126
View File
@@ -59,6 +59,10 @@ import {
DELDOT_CCTV_URL,
DEFAULT_DELDOT_MAX_SOURCES,
DELDOT_ANCHORS,
KING_COUNTY_CAMERAS_URL,
KING_COUNTY_IMAGE_ORIGIN,
DEFAULT_KING_COUNTY_MAX_SOURCES,
KING_COUNTY_ANCHORS,
} from './constants.js';
import {
toFiniteNumber,
@@ -1701,3 +1705,125 @@ export async function loadDelDOTSourcesFromOpenData() {
return [];
}
}
/** Rough King County, WA bounding box. */
function isLikelyKingCountyCoordinate(lat, lon) {
return (
Number.isFinite(lat) &&
Number.isFinite(lon) &&
lat >= 47.0 &&
lat <= 48.0 &&
lon >= -123.1 &&
lon <= -121.0
);
}
/**
* Fetch King County road cameras (Washington State). Keyless: one public
* ArcGIS FeatureServer GeoJSON query (King County DOT Road Services), with an
* explicit `outFields` list so the layer's asset-administration columns are
* never fetched. Only cameras that are live and active (`Live`,
* `CurrentStatus`) with a positive integer id, finite in-county coordinates,
* and a frame on the county's own image handler are kept. WSDOT-owned rows in
* the layer are skipped — the statewide WSDOT pack is their home — while the
* handful of city-owned cameras the county hosts (Redmond, Sammamish,
* Newcastle, Snoqualmie) keep their owner as the per-camera credit,
* DriveBC-style. The layer publishes no facing, so every heading is the id
* hash at low confidence.
*
* Uniquely among the packs, the county publishes each camera's hardware
* `Manufacturer`/`Model`; the model string (whitespace-collapsed — the feed
* carries stray newlines) rides along as the source's `model` for
* identified-hardware consumers.
*
* @returns {Promise<Array<object>>} Normalized camera source objects.
*/
export async function loadKingCountySourcesFromOpenData() {
try {
const resp = await fetch(KING_COUNTY_CAMERAS_URL, {
headers: { Accept: 'application/json' },
signal: AbortSignal.timeout(CCTV_SOURCE_FETCH_TIMEOUT_MS),
});
if (!resp.ok) {
console.warn('[CCTV] King County camera download failed:', resp.status);
return [];
}
const payload = await resp.json();
const rows = Array.isArray(payload?.features) ? payload.features : [];
const cameras = [];
for (const row of rows) {
const attrs = row?.properties;
const rawId = attrs?.AssetID;
if (!Number.isSafeInteger(rawId) || rawId <= 0) continue;
if (attrs.Live !== 1) continue;
if (String(attrs.CurrentStatus || '').trim() !== 'Active') continue;
const owner = String(attrs.Owner || '').trim();
// WSDOT-owned rows belong to the statewide WSDOT pack; skipping them
// here keeps one camera from appearing twice on the globe.
if (owner === 'WSDOT') continue;
// The handler is served over HTTPS; the feed still writes http://.
const imageUrl = String(attrs.ImageURL || '')
.trim()
.replace(/^http:\/\/info\.kingcounty\.gov\//, KING_COUNTY_IMAGE_ORIGIN);
if (!imageUrl.startsWith(KING_COUNTY_IMAGE_ORIGIN)) continue;
const [lon, lat] = Array.isArray(row?.geometry?.coordinates)
? row.geometry.coordinates
: [];
if (!isLikelyKingCountyCoordinate(lat, lon)) continue;
const cameraId = `kingcounty-${rawId}`;
const model = String(attrs.Model || '')
.replace(/\s+/g, ' ')
.trim();
const camera = {
id: cameraId,
name:
String(attrs.Location || '').trim() || `King County camera ${rawId}`,
city: 'King County',
cityId: 'king-county',
provider: 'King County Road Services',
lat,
lon,
headingDeg: fallbackHeadingFromId(cameraId),
headingConfidence: 'low',
// The low-confidence pose personality shared by the other packs.
pitchDeg: -18,
fovDeg: 44,
rangeM: 145,
mountHeightM: 8,
feedType: 'image',
url: imageUrl,
snapshotUrl: imageUrl,
sourceKind: 'kingcounty-open-data',
license: 'King County GIS open data (KCGIS Center terms of use)',
credit: owner && owner !== 'King County' ? owner : '',
};
if (model) camera.model = model;
cameras.push(camera);
}
const maxRaw = Number(
process.env.CCTV_KINGCOUNTY_MAX_SOURCES ||
DEFAULT_KING_COUNTY_MAX_SOURCES,
);
const maxCount = Number.isFinite(maxRaw)
? Math.max(8, Math.min(1200, Math.floor(maxRaw)))
: DEFAULT_KING_COUNTY_MAX_SOURCES;
const prioritized = prioritizeSources(
cameras,
maxCount,
KING_COUNTY_ANCHORS,
);
console.log(
`[CCTV] Loaded King County camera sources: ${cameras.length} published (using nearest ${prioritized.length})`,
);
return prioritized;
} catch (error) {
console.warn(
'[CCTV] King County camera download error:',
error?.message || error,
);
return [];
}
}