mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
Keeps PR #595's Catalonia CCTV pack in sync with upstream's DelDOT CCTV pack (#699) and the wind/weather doc updates from #617. The sources.js conflict was a false positive from git's line-based diff interleaving two structurally similar but independent loader functions (loadCataloniaSourcesFromOpenData / loadDelDOTSourcesFromOpenData); resolved by keeping both as complete, separate functions. Full unit suite passes (4743/4743, 1 pre-existing Node-version skip).
307 lines
14 KiB
JavaScript
307 lines
14 KiB
JavaScript
export const DEFAULT_CCTV_SOURCE_FILE = 'config/cctv_sources.austin.json';
|
||
/** Austin Open Data portal endpoint for traffic camera records. */
|
||
export const DEFAULT_AUSTIN_ROWS_URL =
|
||
'https://data.austintexas.gov/api/views/b4k4-adkb/rows.json?accessType=DOWNLOAD';
|
||
/** Default cap on Austin cameras after distance-based prioritization. */
|
||
export const DEFAULT_AUSTIN_MAX_SOURCES = 250;
|
||
/**
|
||
* Catalog-wide safety ceiling on served cameras. Each pack already caps
|
||
* 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
|
||
* cap.js) so no region is silently dropped. Sized above the sum of the
|
||
* default per-pack caps so a default install never trims.
|
||
*/
|
||
export const DEFAULT_CCTV_MAX_SOURCES = 4000;
|
||
/** Hard upper bound for CCTV_MAX_SOURCES; also sizes the health map. */
|
||
export const CCTV_MAX_SOURCES_CEILING = 5000;
|
||
/** Reference point for Austin camera prioritization (Congress & 6th). */
|
||
export const AUSTIN_DOWNTOWN = { lat: 30.2672, lon: -97.7431 };
|
||
/** Caltrans CCTV: one JSON feed per district, identical schema statewide. */
|
||
/** TxDOT ITS: one keyless JSON catalog per district (25 districts statewide). */
|
||
export const TXDOT_ORIGIN = 'https://its.txdot.gov';
|
||
export const TXDOT_CCTV_STATUS_URL = (district) =>
|
||
`${TXDOT_ORIGIN}/its/DistrictIts/GetCctvStatusListByDistrict?districtCode=${encodeURIComponent(district)}`;
|
||
/** Per-camera frame. Returns JSON `{snippet:<base64 jpeg>}`, not an image body;
|
||
* media.js decodes it (fetchTxdotSnapshot) and only for this origin. */
|
||
export const TXDOT_CCTV_SNAPSHOT_URL = `${TXDOT_ORIGIN}/its/DistrictIts/GetCctvSnapshotByIcdId`;
|
||
/** Valid TxDOT district codes (the ITS map's own districtCodes list). */
|
||
export const TXDOT_DISTRICTS = new Set([
|
||
'ABL',
|
||
'AMA',
|
||
'ATL',
|
||
'AUS',
|
||
'BMT',
|
||
'BWD',
|
||
'BRY',
|
||
'CHS',
|
||
'CRP',
|
||
'DAL',
|
||
'ELP',
|
||
'FTW',
|
||
'HOU',
|
||
'LRD',
|
||
'LBB',
|
||
'LFK',
|
||
'ODA',
|
||
'PAR',
|
||
'PHR',
|
||
'SJT',
|
||
'SAT',
|
||
'TYL',
|
||
'WAC',
|
||
'WFS',
|
||
'YKM',
|
||
]);
|
||
/** Districts fetched by default: Austin (the reference camera city) and San
|
||
* Antonio. A statewide default was rejected: per-pack prioritization is
|
||
* nearest-to-any-anchor, so Dallas's dense core would take most slots.
|
||
* CCTV_TXDOT_DISTRICTS opens up the rest ("AUS,SAT,HOU,DAL,FTW" for the five
|
||
* big metros, or any of the 25 codes). */
|
||
export const DEFAULT_TXDOT_DISTRICTS = 'AUS,SAT';
|
||
export const DEFAULT_TXDOT_MAX_SOURCES = 500;
|
||
/** Prioritization anchors: downtown cores of the metro districts a user can
|
||
* select. Cameras rank by distance to the NEAREST anchor, so a widened
|
||
* CCTV_TXDOT_DISTRICTS still ranks sensibly instead of against Austin alone. */
|
||
export const TXDOT_ANCHORS = [
|
||
{ lat: 30.2672, lon: -97.7431 }, // Austin
|
||
{ lat: 29.4241, lon: -98.4936 }, // San Antonio
|
||
{ lat: 29.7604, lon: -95.3698 }, // Houston
|
||
{ lat: 32.7767, lon: -96.797 }, // Dallas
|
||
{ lat: 32.7555, lon: -97.3308 }, // Fort Worth
|
||
];
|
||
/** Ground-elevation priors in metres, by district. The TxDOT payload carries
|
||
* no elevation, and on a keyless (no-tileset) stack the client's ground snap
|
||
* never fires, so this prior is the only height a camera gets there. Texas
|
||
* spans sea level (Houston) to ~1,140 m (El Paso). */
|
||
export const TXDOT_DISTRICT_ELEVATION_M = Object.freeze({
|
||
ABL: 520,
|
||
AMA: 1099,
|
||
ATL: 105,
|
||
AUS: 149,
|
||
BMT: 5,
|
||
BWD: 425,
|
||
BRY: 111,
|
||
CHS: 250,
|
||
CRP: 7,
|
||
DAL: 131,
|
||
ELP: 1140,
|
||
FTW: 199,
|
||
HOU: 15,
|
||
LRD: 132,
|
||
LBB: 992,
|
||
LFK: 91,
|
||
ODA: 890,
|
||
PAR: 185,
|
||
PHR: 30,
|
||
SJT: 585,
|
||
SAT: 198,
|
||
TYL: 165,
|
||
WAC: 143,
|
||
WFS: 289,
|
||
YKM: 70,
|
||
});
|
||
export const TXDOT_DEFAULT_ELEVATION_M = 150;
|
||
export const CALTRANS_CCTV_URL = (district) =>
|
||
`https://cwwp2.dot.ca.gov/data/d${district}/cctv/cctvStatusD${String(district).padStart(2, '0')}.json`;
|
||
/** Districts fetched by default: SF Bay (4), LA (7), San Diego (11), Sacramento (3). */
|
||
export const DEFAULT_CALTRANS_DISTRICTS = '4,7,11,3';
|
||
export const DEFAULT_CALTRANS_MAX_SOURCES = 300;
|
||
/** Prioritization anchors: downtown cores of the four default metros. */
|
||
export const CALTRANS_ANCHORS = [
|
||
{ lat: 37.7793, lon: -122.4193 }, // San Francisco
|
||
{ lat: 34.0537, lon: -118.2428 }, // Los Angeles
|
||
{ lat: 32.7157, lon: -117.1611 }, // San Diego
|
||
{ lat: 38.5816, lon: -121.4944 }, // Sacramento
|
||
];
|
||
/** TfL JamCams: one keyless list endpoint; frames live on a public S3 bucket. */
|
||
export const TFL_JAMCAM_URL = 'https://api.tfl.gov.uk/Place/Type/JamCam';
|
||
export const TFL_IMAGE_ORIGIN =
|
||
'https://s3-eu-west-1.amazonaws.com/jamcams.tfl.gov.uk/';
|
||
export const DEFAULT_TFL_MAX_SOURCES = 250;
|
||
export const LONDON_CENTER = { lat: 51.5074, lon: -0.1278 };
|
||
/** Ontario 511: keyless CARS/511 camera catalog; frame URLs are still images. */
|
||
export const ONTARIO_511_CAMERAS_URL =
|
||
'https://511on.ca/api/v2/get/cameras?format=json&lang=en';
|
||
export const ONTARIO_511_IMAGE_ORIGIN = 'https://511on.ca/map/Cctv/';
|
||
export const DEFAULT_ONTARIO_MAX_SOURCES = 1000;
|
||
export const ONTARIO_ANCHORS = [
|
||
{ lat: 43.4516, lon: -80.4925 }, // Kitchener
|
||
{ lat: 43.6532, lon: -79.3832 }, // Toronto
|
||
{ lat: 45.4215, lon: -75.6972 }, // Ottawa
|
||
{ lat: 43.2557, lon: -79.8711 }, // Hamilton
|
||
{ lat: 42.9849, lon: -81.2453 }, // London, Ontario
|
||
{ lat: 42.3149, lon: -83.0364 }, // Windsor
|
||
];
|
||
/** Fintraffic road weather cameras (Digitraffic): one keyless GeoJSON list
|
||
* covering all of Finland. Each STATION carries N presets (fixed camera views)
|
||
* that share the station position; one preset is one camera here. */
|
||
export const FINTRAFFIC_STATIONS_URL =
|
||
'https://tie.digitraffic.fi/api/weathercam/v1/stations';
|
||
/** Frames: `<origin><presetId>.jpg`. Preset ids are synthesized into this
|
||
* origin rather than read from the payload, so no upstream field can steer the
|
||
* frame proxy off-host. */
|
||
export const FINTRAFFIC_IMAGE_ORIGIN = 'https://weathercam.digitraffic.fi/';
|
||
/** Digitraffic asks every client to identify itself on API calls. */
|
||
export const DIGITRAFFIC_USER = 'gods-eye-view';
|
||
export const DEFAULT_FINTRAFFIC_MAX_SOURCES = 300;
|
||
/** Ground-elevation prior, in metres, for stations that report no altitude.
|
||
* 228 of 809 stations carry a real metre value (median 94 m); the rest report
|
||
* 0, which means "not reported" rather than sea level — Kouvola (~80 m of real
|
||
* elevation) reports 0. The observed median stands in for those. */
|
||
export const FINTRAFFIC_GROUND_ELEVATION_M = 90;
|
||
/** Prioritization anchors: the population centres strung along Finland's main
|
||
* road spine (vt1 Turku, vt3 Tampere, vt4 Jyväskylä–Oulu–Rovaniemi, vt5
|
||
* Kuopio), so a cap keeps national coverage rather than just the capital. */
|
||
export const FINLAND_ANCHORS = [
|
||
{ lat: 60.1699, lon: 24.9384 }, // Helsinki
|
||
{ lat: 60.4518, lon: 22.2666 }, // Turku
|
||
{ lat: 61.4978, lon: 23.761 }, // Tampere
|
||
{ lat: 62.2426, lon: 25.7473 }, // Jyväskylä
|
||
{ lat: 62.8924, lon: 27.677 }, // Kuopio
|
||
{ lat: 65.0121, lon: 25.4651 }, // Oulu
|
||
{ lat: 66.5039, lon: 25.7294 }, // Rovaniemi
|
||
];
|
||
/** Global cap on total CCTV sources served by the proxy: the default per-pack
|
||
* caps summed (Austin 250 + Caltrans 300 + TfL 250 + DriveBC 250). */
|
||
/** DriveBC highway cameras (British Columbia): the keyless camera list served by
|
||
* the DriveBC.ca site (github.com/bcgov/DriveBC.ca). The DataBC HighwayCams CSV
|
||
* lists the same cameras but still carries retired images.drivebc.ca frame URLs,
|
||
* so frames are built from the numeric camera id on the current image host. */
|
||
export const DRIVEBC_WEBCAMS_URL = 'https://www.drivebc.ca/api/webcams/';
|
||
export const DRIVEBC_IMAGE_URL = (id) =>
|
||
`https://www.drivebc.ca/images/${id}.jpg`;
|
||
export const DEFAULT_DRIVEBC_MAX_SOURCES = 250;
|
||
/** Prioritization anchors: downtown Vancouver and Victoria. */
|
||
export const DRIVEBC_ANCHORS = [
|
||
{ lat: 49.2827, lon: -123.1207 }, // Vancouver
|
||
{ lat: 48.4284, lon: -123.3656 }, // Victoria
|
||
];
|
||
/** Tallinn intersection cameras: curated catalog + public stills on ristmikud.tallinn.ee. */
|
||
export const DEFAULT_TALLINN_SOURCE_FILE = 'config/cctv_sources.tallinn.json';
|
||
export const DEFAULT_TALLINN_MAX_SOURCES = 255;
|
||
export const TALLINN_IMAGE_ORIGIN = 'https://ristmikud.tallinn.ee/';
|
||
export const TALLINN_CENTER = { lat: 59.437, lon: 24.753 };
|
||
/** Transpordiamet / Tarktee road-weather cameras: keyless DATEX2 feeds. */
|
||
export const TARKTEE_LOCATIONS_URL =
|
||
'https://tarktee.transpordiamet.ee/api/v1/datex/roadCameraLocations';
|
||
export const TARKTEE_IMAGES_URL =
|
||
'https://tarktee.transpordiamet.ee/api/v1/datex/roadCameraImages';
|
||
export const TARKTEE_IMAGE_ORIGIN = 'https://tarktee.transpordiamet.ee/images/';
|
||
export const DEFAULT_TARKTEE_MAX_SOURCES = 179;
|
||
export const TARKTEE_ANCHORS = [
|
||
{ lat: 59.437, lon: 24.753 }, // Tallinn
|
||
{ lat: 58.378, lon: 26.729 }, // Tartu
|
||
{ lat: 58.3859, lon: 24.4971 }, // Pärnu
|
||
{ lat: 59.3797, lon: 28.1791 }, // Narva
|
||
];
|
||
/** Warendorf (Germany): the Marktplatz municipal webcam from a curated catalog file. */
|
||
export const DEFAULT_WARENDORF_SOURCE_FILE =
|
||
'config/cctv_sources.warendorf.json';
|
||
export const WARENDORF_IMAGE_ORIGINS = Object.freeze([
|
||
'http://webcam.warendorf.de/',
|
||
'https://www.kreis-warendorf.de/',
|
||
]);
|
||
/** Live Traffic NSW (Transport for NSW): keyless public camera catalog. */
|
||
export const NSW_CAMERAS_URL =
|
||
'https://data.livetraffic.com/cameras/traffic-cam.json';
|
||
export const NSW_IMAGE_ORIGIN = 'https://webcams.transport.nsw.gov.au/';
|
||
export const DEFAULT_NSW_MAX_SOURCES = 250;
|
||
export const SYDNEY_CENTER = { lat: -33.8688, lon: 151.2093 };
|
||
/**
|
||
* The NSW webcam host answers non-browser clients with HTTP 200 and a short
|
||
* HTML body instead of the frame (verified 2026-09-13), so the proxy
|
||
* identifies as a browser for that one host. See media.js.
|
||
*/
|
||
export const NSW_IMAGE_USER_AGENT =
|
||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
|
||
/**
|
||
* Longest NSW `view` sentence still usable as a label. NSW occasionally
|
||
* repurposes `view` for a multi-paragraph works notice; real descriptions top
|
||
* out around 120 characters.
|
||
*/
|
||
export const NSW_MAX_VIEW_LABEL = 140;
|
||
/** Open Calgary traffic cameras: one keyless Socrata endpoint for the whole
|
||
* city; frames are stills on a City of Calgary host. */
|
||
export const DEFAULT_CALGARY_ROWS_URL =
|
||
'https://data.calgary.ca/resource/k7p9-kppz.json?$limit=500';
|
||
/** The only origin Calgary camera frames may come from. The catalog publishes
|
||
* most rows as `http://`; that host serves HTTPS and 301-redirects to it, so
|
||
* URLs are upgraded and then pinned here before registration. */
|
||
export const CALGARY_IMAGE_ORIGIN = 'https://trafficcam.calgary.ca/';
|
||
export const DEFAULT_CALGARY_MAX_SOURCES = 220;
|
||
/** Centre Street / 7 Avenue: the prioritization anchor. */
|
||
export const CALGARY_DOWNTOWN = { lat: 51.0461, lon: -114.0626 };
|
||
/** Hard ceiling on the Calgary catalog body. The whole city is ~215 rows and
|
||
* under 100 KB; this only exists so an upstream that streams an unbounded
|
||
* body cannot be buffered without limit. */
|
||
export const CALGARY_MAX_CATALOG_BYTES = 4 * 1024 * 1024;
|
||
|
||
/**
|
||
* Catalonia traffic cameras (Servei Català de Trànsit): one keyless WFS/GML
|
||
* XML list (verified 2026-09-15, hourly catalog refresh). The feed carries a
|
||
* `font` field naming the actual publisher per camera — SCT's own highway
|
||
* cameras plus hotlinked Barcelona (IMI) and Terrassa municipal cameras are
|
||
* kept, each confirmed to return a real frame. Frame URLs are checked
|
||
* against a host allowlist rather than one pinned origin, since each
|
||
* publisher serves from its own host.
|
||
*/
|
||
export const CATALONIA_CAMERAS_URL =
|
||
'http://www.gencat.cat/transit/opendata/cameres.xml';
|
||
/** Allowed image hosts, one per kept `font` publisher in the feed. */
|
||
export const CATALONIA_IMAGE_HOSTS = Object.freeze(
|
||
new Set([
|
||
'mct.gencat.cat', // SCT
|
||
'www.bcn.cat', // IMI (Ajuntament de Barcelona)
|
||
'emap.terrassa.cat', // Ajuntament de Terrassa
|
||
]),
|
||
);
|
||
/** Per-`font` partner credit, shown beside the provider in the CCTV panel
|
||
* (same pattern as DriveBC's partner-supplied cameras). SCT is the feed's
|
||
* own publisher and carries no separate credit. */
|
||
export const CATALONIA_FONT_CREDIT = Object.freeze({
|
||
IMI: 'Ajuntament de Barcelona',
|
||
Terrassa: 'Ajuntament de Terrassa',
|
||
});
|
||
/** Ground-elevation priors in metres, by `font`: the feed carries no
|
||
* elevation, and this pack spans sea level (Barcelona) to Terrassa's plateau
|
||
* (~270 m). The client's ground snap corrects these on 3D-tile stacks. */
|
||
export const CATALONIA_FONT_ELEVATION_M = Object.freeze({
|
||
SCT: 80,
|
||
IMI: 20,
|
||
Terrassa: 270,
|
||
});
|
||
export const DEFAULT_CATALONIA_MAX_SOURCES = 150;
|
||
/** Prioritization anchors: Barcelona and Terrassa. */
|
||
export const CATALONIA_ANCHORS = [
|
||
{ lat: 41.3874, lon: 2.1686 }, // Barcelona
|
||
{ lat: 41.5636, lon: 2.0111 }, // Terrassa
|
||
];
|
||
|
||
/** DelDOT CCTV: one keyless statewide JSON catalog; live video via RTMP-over-HTTP (rtmpt:80). */
|
||
export const DELDOT_CCTV_URL = 'https://tmc.deldot.gov/json/videocamera.json';
|
||
export const DEFAULT_DELDOT_MAX_SOURCES = 300;
|
||
export const DELDOT_ANCHORS = [
|
||
{ lat: 39.7459, lon: -75.5466 }, // Wilmington (New Castle)
|
||
{ lat: 39.1582, lon: -75.5244 }, // Dover (Kent)
|
||
{ lat: 38.6906, lon: -75.3877 }, // Georgetown (Sussex)
|
||
];
|
||
/** 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
|
||
* stalled upstream can't leave getCctvSources (and thus every CCTV route)
|
||
* pending forever — a hung fetch aborts, the loader returns [], and
|
||
* serve-stale/other packs take over. */
|
||
export const CCTV_SOURCE_FETCH_TIMEOUT_MS = 15 * 1000;
|
||
/** Individual CCTV image fetches must settle before the active 10-second
|
||
* client refresh cadence. A bounded miss can fall through to Street View or
|
||
* the synthetic frame instead of leaving the browser preview pending. */
|
||
export const CCTV_FRAME_FETCH_TIMEOUT_MS = 8 * 1000;
|
||
|
||
/** Maximum buffered snapshot size. */
|
||
export const CCTV_FRAME_MAX_BODY_BYTES = 16 * 1024 * 1024;
|
||
|
||
/** Deadline for upstream response headers; live bodies keep streaming afterward. */
|
||
export const CCTV_MEDIA_FETCH_TIMEOUT_MS = 15 * 1000;
|
||
/** Declared size ceiling for fixed media responses. */
|
||
export const CCTV_MEDIA_MAX_BODY_BYTES = 64 * 1024 * 1024;
|