mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
Seven contributed CCTV camera packs (Ontario 511, Fintraffic Finland, DriveBC, TxDOT Texas, Estonia Tallinn + Tarktee, Warendorf, Live Traffic NSW) on a fair per-pack catalog cap; semantic camera labels; monitor planes lifted clear of the terrain from a precomputed, bundled ground-height dataset. Default catalog 800 → 3,446 cameras. Full context in PR #433. Co-authored-by: sendmebits <[email protected]> Co-authored-by: avocadoAgent <[email protected]> Co-authored-by: Alex Harvey <[email protected]> Co-authored-by: Jason Meredith <[email protected]> Co-authored-by: Daniel Smith <[email protected]> Co-authored-by: Vladimir Sobolev <[email protected]> Co-authored-by: Matthias Schneider <[email protected]> Co-authored-by: Cyber Knight <[email protected]> Co-authored-by: NovaSoft Australia <[email protected]>
257 lines
8.2 KiB
JavaScript
257 lines
8.2 KiB
JavaScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { loadFintrafficSourcesFromOpenData } from '../../server/providers/cctv/sources.js';
|
|
import {
|
|
fintrafficCameraName,
|
|
isLikelyFinlandCoordinate,
|
|
} from '../../server/providers/cctv/normalize.js';
|
|
import {
|
|
FINTRAFFIC_GROUND_ELEVATION_M,
|
|
FINTRAFFIC_IMAGE_ORIGIN,
|
|
FINTRAFFIC_STATIONS_URL,
|
|
} from '../../server/providers/cctv/constants.js';
|
|
|
|
/** One station feature in the shape tie.digitraffic.fi actually returns. */
|
|
function station(
|
|
id,
|
|
{
|
|
name = `vt4_${id}`,
|
|
status = 'GATHERING',
|
|
lon = 24.9384,
|
|
lat = 60.1699,
|
|
altitude = 0,
|
|
presets = [],
|
|
} = {},
|
|
) {
|
|
return {
|
|
type: 'Feature',
|
|
id,
|
|
geometry: { type: 'Point', coordinates: [lon, lat, altitude] },
|
|
properties: {
|
|
id,
|
|
name,
|
|
collectionStatus: status,
|
|
dataUpdatedTime: '2026-09-13T03:25:36Z',
|
|
presets,
|
|
state: null,
|
|
},
|
|
};
|
|
}
|
|
|
|
/** Run the loader against a canned station list, restoring global fetch after. */
|
|
async function loadWith(features, { env = {}, capture = {} } = {}) {
|
|
const originalFetch = globalThis.fetch;
|
|
const originalEnv = {};
|
|
for (const [key, value] of Object.entries(env)) {
|
|
originalEnv[key] = process.env[key];
|
|
if (value === undefined) delete process.env[key];
|
|
else process.env[key] = value;
|
|
}
|
|
globalThis.fetch = async (url, options) => {
|
|
capture.url = String(url);
|
|
capture.options = options;
|
|
return new Response(
|
|
JSON.stringify({
|
|
type: 'FeatureCollection',
|
|
dataUpdatedTime: '2026-09-13T05:31:58Z',
|
|
features,
|
|
}),
|
|
{
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
},
|
|
);
|
|
};
|
|
try {
|
|
return await loadFintrafficSourcesFromOpenData();
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
for (const [key, value] of Object.entries(originalEnv)) {
|
|
if (value === undefined) delete process.env[key];
|
|
else process.env[key] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
test('Fintraffic loader turns every in-collection preset into one camera', async () => {
|
|
const cameras = await loadWith([
|
|
station('C01503', {
|
|
name: 'kt51_Inkoo',
|
|
lon: 23.99616,
|
|
lat: 60.05374,
|
|
presets: [
|
|
{ id: 'C0150301', inCollection: true },
|
|
{ id: 'C0150302', inCollection: true },
|
|
],
|
|
}),
|
|
]);
|
|
|
|
assert.equal(cameras.length, 2);
|
|
const [first, second] = cameras;
|
|
assert.equal(first.id, 'fi-c0150301');
|
|
assert.equal(first.name, 'kt51 Inkoo (view 01)');
|
|
assert.equal(second.name, 'kt51 Inkoo (view 02)');
|
|
assert.equal(first.provider, 'Fintraffic');
|
|
assert.equal(first.city, 'Finland');
|
|
assert.equal(first.cityId, 'finland');
|
|
assert.equal(first.sourceKind, 'fintraffic-open-data');
|
|
assert.equal(first.license, 'Fintraffic / digitraffic.fi (CC BY 4.0)');
|
|
assert.equal(first.feedType, 'image');
|
|
// Presets of one station share the station position...
|
|
assert.equal(first.lat, 60.05374);
|
|
assert.equal(second.lat, first.lat);
|
|
assert.equal(second.lon, first.lon);
|
|
// ...so the id-hash fallback heading is what keeps their gizmos apart.
|
|
assert.notEqual(first.headingDeg, second.headingDeg);
|
|
assert.equal(first.headingConfidence, 'low');
|
|
});
|
|
|
|
test('Fintraffic frame URLs are built on the official image origin', async () => {
|
|
const cameras = await loadWith([
|
|
station('C01503', { presets: [{ id: 'C0150301', inCollection: true }] }),
|
|
]);
|
|
|
|
assert.equal(cameras[0].url, `${FINTRAFFIC_IMAGE_ORIGIN}C0150301.jpg`);
|
|
assert.equal(cameras[0].snapshotUrl, cameras[0].url);
|
|
assert.ok(cameras[0].url.startsWith('https://weathercam.digitraffic.fi/'));
|
|
});
|
|
|
|
test('Fintraffic loader drops dead stations, uncollected presets and bad ids', async () => {
|
|
const cameras = await loadWith([
|
|
// Station off collection: every preset goes, live or not.
|
|
station('C01601', {
|
|
status: 'REMOVED_TEMPORARILY',
|
|
presets: [{ id: 'C0160101', inCollection: true }],
|
|
}),
|
|
station('C01503', {
|
|
presets: [
|
|
{ id: 'C0150301', inCollection: true },
|
|
{ id: 'C0150302', inCollection: false },
|
|
{ id: 'C0150303' },
|
|
// Ids that would escape the synthesized frame path, or belong to
|
|
// another station, never become a camera.
|
|
{ id: '../../etc/passwd', inCollection: true },
|
|
{ id: 'C9999901', inCollection: true },
|
|
],
|
|
}),
|
|
// Coordinates nowhere near Finland (swapped lat/lon).
|
|
station('C01504', {
|
|
lon: 60.1699,
|
|
lat: 24.9384,
|
|
presets: [{ id: 'C0150401', inCollection: true }],
|
|
}),
|
|
]);
|
|
|
|
assert.deepEqual(
|
|
cameras.map((camera) => camera.id),
|
|
['fi-c0150301'],
|
|
);
|
|
});
|
|
|
|
test('Fintraffic ground elevation uses a reported altitude and falls back otherwise', async () => {
|
|
const cameras = await loadWith([
|
|
station('C08508', {
|
|
altitude: 136,
|
|
presets: [{ id: 'C0850801', inCollection: true }],
|
|
}),
|
|
station('C01503', {
|
|
altitude: 0,
|
|
presets: [{ id: 'C0150301', inCollection: true }],
|
|
}),
|
|
station('C03506', {
|
|
altitude: 99_000,
|
|
presets: [{ id: 'C0350601', inCollection: true }],
|
|
}),
|
|
]);
|
|
|
|
const byId = new Map(cameras.map((camera) => [camera.id, camera]));
|
|
assert.equal(byId.get('fi-c0850801').groundElevationM, 136);
|
|
// 0 is "not reported", not sea level.
|
|
assert.equal(
|
|
byId.get('fi-c0150301').groundElevationM,
|
|
FINTRAFFIC_GROUND_ELEVATION_M,
|
|
);
|
|
// A garbage upstream value cannot fling a camera kilometres up.
|
|
assert.equal(byId.get('fi-c0350601').groundElevationM, 1400);
|
|
});
|
|
|
|
test('Fintraffic loader identifies itself to Digitraffic and refuses redirects', async () => {
|
|
const capture = {};
|
|
await loadWith(
|
|
[station('C01503', { presets: [{ id: 'C0150301', inCollection: true }] })],
|
|
{ capture },
|
|
);
|
|
|
|
assert.equal(capture.url, FINTRAFFIC_STATIONS_URL);
|
|
assert.equal(capture.options.headers['Digitraffic-User'], 'gods-eye-view');
|
|
assert.equal(capture.options.headers['Accept-Encoding'], 'gzip');
|
|
assert.equal(capture.options.redirect, 'manual');
|
|
assert.ok(capture.options.signal instanceof AbortSignal);
|
|
|
|
const originalFetch = globalThis.fetch;
|
|
globalThis.fetch = async () =>
|
|
new Response(null, {
|
|
status: 302,
|
|
headers: { Location: 'https://example.com/stations' },
|
|
});
|
|
try {
|
|
assert.deepEqual(await loadFintrafficSourcesFromOpenData(), []);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test('Fintraffic pack caps itself and fails to an empty list, never a throw', async () => {
|
|
const presets = Array.from({ length: 40 }, (_, index) => ({
|
|
id: `C01503${String(index).padStart(2, '0')}`,
|
|
inCollection: true,
|
|
}));
|
|
const capped = await loadWith([station('C01503', { presets })], {
|
|
env: { CCTV_FINTRAFFIC_MAX_SOURCES: '8' },
|
|
});
|
|
assert.equal(capped.length, 8);
|
|
|
|
const originalFetch = globalThis.fetch;
|
|
globalThis.fetch = async () => {
|
|
throw new Error('network down');
|
|
};
|
|
try {
|
|
assert.deepEqual(await loadFintrafficSourcesFromOpenData(), []);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
|
|
globalThis.fetch = async () => new Response('nope', { status: 503 });
|
|
try {
|
|
assert.deepEqual(await loadFintrafficSourcesFromOpenData(), []);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test('fintrafficCameraName unpacks the machine station name and the view number', () => {
|
|
assert.equal(
|
|
fintrafficCameraName('vt3_Hyvinkää_Noppo', 'C01234', 'C0123402'),
|
|
'vt3 Hyvinkää Noppo (view 02)',
|
|
);
|
|
// Nameless station still gets a usable label.
|
|
assert.equal(
|
|
fintrafficCameraName('', 'C01234', 'C0123409'),
|
|
'Fintraffic C01234 (view 09)',
|
|
);
|
|
// A preset id that is just the station id has no view suffix to add.
|
|
assert.equal(
|
|
fintrafficCameraName('vt4_Mäntsälä', 'C01234', 'C01234'),
|
|
'vt4 Mäntsälä',
|
|
);
|
|
});
|
|
|
|
test('isLikelyFinlandCoordinate spans the catalog extent and rejects the rest', () => {
|
|
assert.equal(isLikelyFinlandCoordinate(60.1699, 24.9384), true); // Helsinki
|
|
assert.equal(isLikelyFinlandCoordinate(70.088512, 27.0), true); // Utsjoki, the northern extreme
|
|
assert.equal(isLikelyFinlandCoordinate(59.856454, 19.618722), true); // the southwestern extreme
|
|
assert.equal(isLikelyFinlandCoordinate(51.5074, -0.1278), false); // London
|
|
assert.equal(isLikelyFinlandCoordinate(0, 0), false); // null island
|
|
assert.equal(isLikelyFinlandCoordinate(NaN, 24.9384), false);
|
|
});
|