Files
gods-eye-view/mobile/ui/stubSituations.js
T

687 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Offline stub situations, shaped like mobile/globe/API.md (band, archetype,
* telemetry, headline, media, series [{t,v}]). Everything here is labelled
* simulated; Eye cards point at the dev server's real public-camera frames
* (`/api/cctv/frame/:id`) and fall back to imagery when that is unreachable.
*/
import { esriThumb } from './dom.js';
export const SIM = 'Simulated · offline stub engine';
/** Public traffic cameras the dev server proxies (ids from /api/cctv/sources). */
export const STUB_CAMS = [
{
id: '354',
title: '5th St / Congress Ave',
city: 'Austin',
provider: 'Austin Transportation & Public Works',
lat: 30.267119,
lon: -97.743172,
headingDeg: 67.5,
},
{
id: 'tfl-00001.06502',
title: 'Trafalgar Square',
city: 'London',
provider: 'Transport for London',
lat: 51.5074,
lon: -0.12765,
headingDeg: 135,
},
{
id: 'wsdot-9520',
title: 'I-5 at 5th Ave / Columbia',
city: 'Seattle',
provider: 'WSDOT',
lat: 47.605166,
lon: -122.330316,
headingDeg: 202.5,
},
{
id: 'ca-d4-tv301',
title: 'US-101 at Octavia St',
city: 'San Francisco',
provider: 'Caltrans',
lat: 37.77146,
lon: -122.42323,
headingDeg: 180,
},
{
id: 'no-vegvesen-3000051_1',
title: 'F862 Sentrumstangenten',
city: 'Tromsø',
provider: 'Statens vegvesen',
lat: 69.64469,
lon: 18.947695,
headingDeg: 270,
},
{
id: 'ca-d11-x1248',
title: 'I-5 at First Avenue',
city: 'San Diego',
provider: 'Caltrans',
lat: 32.72285,
lon: -117.16356,
headingDeg: 180,
},
{
id: 'fi-c1455101',
title: 'vt4 Rovaniemi Revontuli',
city: 'Rovaniemi',
provider: 'Fintraffic',
lat: 66.503858,
lon: 25.725196,
headingDeg: 202.5,
},
];
export const camFrameUrl = (id) =>
`/api/cctv/frame/${encodeURIComponent(id)}?ts=${Math.floor(Date.now() / 10000)}`;
function series(values, stepMin, now) {
return values.map((v, i) => ({
t: now - (values.length - 1 - i) * stepMin * 60e3,
v,
}));
}
export function buildStubSituations(now) {
const min = 60e3;
const S = (o) => ({
live: true,
source: SIM,
section: 'now',
severity: 0,
updatedAt: now - (o.age ?? 2) * min,
time: o.time ?? { start: now - 6 * 3600e3, end: now + 24 * 3600e3 },
camera: o.camera ?? {
lon: o.lon,
lat: o.lat,
height: o.height ?? 400e3,
heading: 0,
pitch: -90,
},
thumbnail: esriThumb(o.lon, o.lat, o.span ?? 1, 480, o.th ?? 600),
...o,
});
const eye = (cam, o) =>
S({
kind: 'camera',
family: 'eyes',
band: 'ground',
archetype: 'eye',
lon: cam.lon,
lat: cam.lat,
span: 0.01,
height: 1500,
camera: {
lon: cam.lon,
lat: cam.lat,
height: 900,
heading: cam.headingDeg,
pitch: -30,
},
layers: ['eyes.public-cctv', 'ground.traffic-flow'],
media: {
kind: 'image',
url: camFrameUrl(cam.id),
refreshMs: 15000,
cameraId: cam.id,
headingDeg: cam.headingDeg,
provider: cam.provider,
city: cam.city,
},
telemetry: `GROUND · CAM ${cam.id.slice(-6).toUpperCase()} · ${cam.title.toUpperCase()}`,
source: `${cam.provider} public camera`,
...o,
});
const C = Object.fromEntries(STUB_CAMS.map((c) => [c.id, c]));
return [
S({
id: 'cyclone-wpac',
kind: 'cyclone',
family: 'hazards',
band: 'orbit',
archetype: 'sensor',
severity: 5,
title: 'Typhoon tracking toward Luzon',
subtitle: 'Category 4 · eyewall replacement under way',
place: 'Philippine Sea',
lon: 126.5,
lat: 15.4,
span: 9,
th: 640,
height: 2200e3,
layers: ['hazards.tropical-cyclones', 'weather.clouds', 'weather.wind'],
metrics: [
{ label: 'Wind', value: 185, unit: 'km/h' },
{ label: 'Pressure', value: 935, unit: 'hPa' },
{ label: 'Moving', value: 'WNW 18', unit: 'km/h' },
],
headline: { value: 185, unit: 'km/h', label: 'sustained wind' },
telemetry: 'ORBIT · GOES IR · 185 KM/H · 935 HPA · WNW 18',
series: series(
[120, 130, 140, 150, 165, 175, 185, 185, 180, 185],
360,
now,
),
seriesLabel: 'sustained wind, km/h',
age: 2,
time: { start: now - 72 * 3600e3, end: now + 120 * 3600e3 },
events: [{ at: now + 30 * 3600e3, label: 'Landfall (fcst)' }],
}),
eye(C['354'], {
id: 'eye-austin',
title: 'Saturday night on Congress Ave',
subtitle: 'Downtown crowds after the game',
place: 'Downtown Austin, Texas',
severity: 2,
age: 0,
}),
S({
id: 'squawk-7700',
kind: 'squawk',
family: 'sky',
band: 'sky',
archetype: 'track',
severity: 4,
title: 'Squawk 7700 · descending toward Halifax',
subtitle: 'Simulated emergency track, diverting',
place: 'Over Nova Scotia',
lon: -62.4,
lat: 45.1,
span: 2.4,
th: 520,
height: 350e3,
layers: ['sky.emergency-squawks', 'sky.commercial-flights'],
metrics: [
{ label: 'Altitude', value: 'FL100', unit: '' },
{ label: 'Speed', value: 312, unit: 'kt' },
{ label: 'Squawk', value: '7700', unit: '' },
],
headline: { value: '7700', unit: '', label: 'general emergency' },
telemetry: 'SKY · FL100 ↓ · 312 KT · SQ 7700',
track: { headingDeg: 310 },
age: 1,
}),
S({
id: 'lightning-gulf',
kind: 'lightning',
family: 'weather',
band: 'sky',
archetype: 'number',
severity: 3,
title: 'Lightning over the Gulf of Mexico',
subtitle: 'A squall line pushing toward the Louisiana coast',
place: 'Gulf of Mexico',
lon: -90.2,
lat: 27.4,
span: 5,
th: 460,
height: 900e3,
layers: ['weather.lightning', 'weather.radar'],
metrics: [
{ label: 'Strikes / h', value: 4812, unit: '' },
{ label: 'Peak density', value: 38, unit: '/km²' },
],
headline: { value: 4812, unit: '', label: 'strikes in the last hour' },
telemetry: 'SKY · GLM · 4,812 STR/H · 27.4N 90.2W',
series: series([620, 900, 1400, 2100, 2900, 3600, 4100, 4812], 15, now),
seriesLabel: 'strikes / 15 min',
age: 1,
}),
S({
id: 'quake-24h',
kind: 'earthquake',
family: 'hazards',
band: 'ground',
archetype: 'sensor',
severity: 4,
title: 'M6.4 · largest quake in 24 h',
subtitle: 'Shallow thrust event, aftershocks continuing',
place: 'Off Hokkaido, Japan',
lon: 144.2,
lat: 42.1,
span: 3.5,
th: 480,
height: 900e3,
layers: ['hazards.earthquakes'],
metrics: [
{ label: 'Magnitude', value: 6.4, unit: 'Mw' },
{ label: 'Depth', value: 38, unit: 'km' },
{ label: 'Aftershocks', value: 27, unit: '' },
],
headline: { value: 6.4, unit: 'Mw', label: 'magnitude' },
telemetry: 'GROUND · USGS · M6.4 · 38 KM DEEP',
age: 11,
events: [{ at: now - 14 * 3600e3, label: 'M6.4' }],
}),
S({
id: 'iss-pass',
kind: 'iss',
family: 'orbit',
band: 'orbit',
archetype: 'track',
severity: 2,
title: 'ISS crosses your sky at 21:42',
subtitle: 'Bright pass · max elevation 71°',
place: 'Low Earth orbit',
lon: -40,
lat: 30,
span: 30,
th: 520,
height: 9000e3,
layers: ['orbit.iss-crewed', 'orbit.ground-tracks'],
metrics: [
{ label: 'Altitude', value: 418, unit: 'km' },
{ label: 'Speed', value: 7.66, unit: 'km/s' },
{ label: 'Next pass', value: '21:42', unit: '' },
],
headline: { value: '21:42', unit: '', label: 'next pass · 71° max' },
telemetry: 'ORBIT · 418 KM · 7.66 KM/S · PASS 21:42',
track: { headingDeg: 52 },
age: 1,
}),
S({
id: 'fires-sierra',
kind: 'fires',
family: 'hazards',
band: 'ground',
archetype: 'sensor',
severity: 4,
title: 'Fire cluster in the Sierra foothills',
subtitle: 'Hotspots doubling since dawn, smoke drifting east',
place: 'Nevada County, California',
lon: -120.9,
lat: 39.05,
span: 1.6,
th: 560,
height: 260e3,
layers: [
'hazards.active-fires',
'hazards.fire-perimeters',
'weather.wind',
],
metrics: [
{ label: 'Hotspots', value: 214, unit: '' },
{ label: 'FRP', value: 1840, unit: 'MW' },
{ label: 'Wind', value: 32, unit: 'km/h' },
],
headline: { value: 214, unit: '', label: 'hotspots' },
telemetry: 'GROUND · FIRMS VIIRS · 214 HOTSPOTS · WIND W 32',
series: series([12, 18, 30, 44, 61, 88, 120, 160, 190, 214], 60, now),
seriesLabel: 'hotspots / h',
age: 4,
}),
S({
id: 'gnss-baltic',
kind: 'gnss',
family: 'infra',
band: 'sky',
archetype: 'number',
severity: 3,
title: 'GNSS jamming over the Baltic',
subtitle: 'Aircraft reporting degraded navigation integrity',
place: 'Baltic Sea',
lon: 21.0,
lat: 56.5,
span: 6,
th: 480,
height: 1500e3,
layers: ['infra.gnss-interference', 'sky.commercial-flights'],
metrics: [
{ label: 'Affected', value: 312, unit: 'aircraft' },
{ label: 'Area', value: 84, unit: 'k km²' },
],
headline: { value: 312, unit: '', label: 'aircraft affected' },
telemetry: 'SKY · ADS-B NIC<7 · 312 AC · 84K KM²',
series: series([120, 180, 240, 260, 300, 312], 60, now),
seriesLabel: 'aircraft with NIC < 7',
age: 9,
}),
eye(C['tfl-00001.06502'], {
id: 'eye-trafalgar',
title: 'Trafalgar Square, live',
subtitle: 'Looking south-east down Whitehall',
place: 'Westminster, London',
severity: 1,
age: 0,
}),
S({
id: 'circling-midtown',
kind: 'circling',
family: 'sky',
band: 'sky',
archetype: 'track',
severity: 4,
live: false,
title: '3 helicopters circled Midtown Manhattan',
subtitle: 'Detected from orbiting tracks · replay the Moment',
place: 'Empire State Building, New York',
lon: -73.98566,
lat: 40.74844,
span: 0.05,
th: 520,
height: 6000,
layers: ['sky.helicopters', 'sky.commercial-flights'],
metrics: [
{ label: 'Rotorcraft', value: 3, unit: '' },
{ label: 'Orbit radius', value: '450–650', unit: 'm' },
{ label: 'Altitude', value: '1,050–1,450', unit: 'ft' },
],
headline: { value: 3, unit: 'helis', label: 'circling one point' },
telemetry: 'SKY · 3 ROTORCRAFT · R 450–650 M · 1,050–1,450 FT',
momentId: 'empire-state',
illustrative: true,
track: { headingDeg: 0, circuit: true },
age: 76,
}),
S({
id: 'novosel',
kind: 'helicopters',
family: 'sky',
band: 'sky',
archetype: 'track',
title: 'Rotor school: 14th circuit of the day',
subtitle: 'A training helicopter flying the pattern',
place: 'Fort Novosel, Alabama',
lon: -85.72,
lat: 31.36,
span: 0.22,
th: 420,
height: 40e3,
layers: ['sky.helicopters'],
metrics: [
{ label: 'Airborne', value: 23, unit: 'rotorcraft' },
{ label: 'Pattern alt', value: 700, unit: 'ft' },
],
headline: { value: 14, unit: 'th', label: 'circuit today' },
telemetry: 'SKY · 700 FT · 88 KT · CIRCUIT 14',
track: { headingDeg: 140, circuit: true },
age: 3,
alwaysOn: true,
}),
S({
id: 'bhote-koshi-2026',
kind: 'reconstruction',
family: 'hazards',
band: 'inside',
archetype: 'sensor',
title: 'Bhote Koshi outburst flood',
subtitle: '16 sourced observations · 3 open hypotheses',
place: 'Rasuwa, Nepal ↔ Gyirong, Tibet',
section: 'reconstructions',
lon: 85.33,
lat: 28.15,
span: 0.3,
th: 640,
height: 30e3,
camera: {
lon: 85.3548,
lat: 28.207,
height: 14e3,
heading: 344,
pitch: -39,
},
layers: [
'hazards.floods',
'infra.recent-imagery',
'eyes.signed-captures',
],
metrics: [
{ label: 'Observations', value: 16, unit: '' },
{ label: 'Corridor', value: 145, unit: 'pts' },
{ label: 'Hypotheses', value: 3, unit: '' },
],
telemetry: 'INSIDE · 4D · 16 OBS · 22 KM CORRIDOR',
live: false,
age: 60 * 24 * 7,
source: 'GeoPera reconstruction · Vantor Open Data (CC BY-NC 4.0)',
time: { start: Date.UTC(2026, 7, 26, 0), end: Date.UTC(2026, 7, 27, 12) },
thread: 'bhote-koshi-2026',
thumbnail: '/events/bhote-koshi-2026/post.webp',
}),
S({
id: 'rain-houston',
kind: 'rain',
family: 'weather',
band: 'sky',
archetype: 'sensor',
severity: 3,
title: 'Heavy rain cell over Houston',
subtitle: '60 dBZ core over the Ship Channel, moving east',
place: 'Houston, Texas',
lon: -95.25,
lat: 29.74,
span: 0.9,
th: 540,
height: 120e3,
layers: ['weather.radar', 'weather.lightning'],
metrics: [
{ label: 'Peak', value: 60, unit: 'dBZ' },
{ label: 'Rate', value: 76, unit: 'mm/h' },
],
headline: { value: 76, unit: 'mm/h', label: 'rain rate' },
telemetry: 'SKY · MRMS · 60 DBZ · 76 MM/H · E 22 KM/H',
age: 2,
}),
S({
id: 'starbase',
kind: 'launch',
family: 'orbit',
band: 'orbit',
archetype: 'track',
title: 'Launch window opens at Starbase',
subtitle: 'Trajectory east over the Gulf',
place: 'Boca Chica, Texas',
lon: -97.157,
lat: 25.997,
span: 0.06,
th: 380,
height: 12e3,
layers: ['orbit.launches', 'orbit.ground-tracks'],
metrics: [
{ label: 'T−', value: '06:12:00', unit: '' },
{ label: 'Weather go', value: 80, unit: '%' },
],
headline: { value: 'T−06:12', unit: '', label: 'to window' },
telemetry: 'ORBIT · PAD B · AZ 097° · T−06:12:00',
track: { headingDeg: 97, launch: true },
age: 6,
events: [{ at: now + 6.2 * 3600e3, label: 'T-0' }],
}),
eye(C['wsdot-9520'], {
id: 'eye-seattle',
title: 'I-5 through downtown Seattle',
subtitle: 'Southbound into the convention center tunnel',
place: 'Seattle, Washington',
age: 0,
}),
S({
id: 'suez-queue',
kind: 'queue',
family: 'sea',
band: 'ground',
archetype: 'number',
title: '41 ships waiting at Suez',
subtitle: 'Southbound convoy holding in the anchorage',
place: 'Gulf of Suez, Egypt',
lon: 32.55,
lat: 29.88,
span: 0.35,
th: 560,
height: 60e3,
layers: ['sea.ais-vessels', 'sea.tankers-cargo'],
metrics: [
{ label: 'Waiting', value: 41, unit: 'vessels' },
{ label: 'Median wait', value: 19, unit: 'h' },
],
headline: { value: 41, unit: '', label: 'vessels at anchor' },
telemetry: 'GROUND · AIS · 41 AT ANCHOR · WAIT 19 H',
series: series([28, 30, 33, 35, 39, 41], 120, now),
seriesLabel: 'vessels at anchor',
age: 5,
}),
S({
id: 'north-sea-mil',
kind: 'military',
family: 'sky',
band: 'sky',
archetype: 'sensor',
title: 'Tanker orbits over the North Sea',
subtitle: 'Two refuelling tracks and a fighter CAP',
place: 'North Sea',
lon: 3.5,
lat: 55.5,
span: 5,
th: 520,
height: 900e3,
layers: ['sky.military-aircraft'],
metrics: [
{ label: 'Aircraft', value: 14, unit: '' },
{ label: 'Tankers', value: 3, unit: '' },
],
telemetry: 'SKY · FL260 · 3 TANKERS · 14 MIL',
age: 7,
}),
eye(C['no-vegvesen-3000051_1'], {
id: 'eye-tromso',
kind: 'camera',
title: 'Tromsø under a Kp 6 sky',
subtitle: 'Road camera on Sentrumstangenten, aurora nowcast high',
place: 'Tromsø, Norway',
age: 0,
}),
S({
id: 'jet-stream',
kind: 'jet',
family: 'weather',
band: 'sky',
archetype: 'sensor',
title: 'Jet stream at 210 kt over the Atlantic',
subtitle: 'Eastbound flights riding it, westbound avoiding it',
place: 'North Atlantic',
lon: -38,
lat: 49,
span: 22,
th: 460,
height: 4500e3,
layers: ['weather.wind', 'sky.commercial-flights'],
metrics: [{ label: 'Core', value: 210, unit: 'kt' }],
headline: { value: 210, unit: 'kt', label: 'jet core at FL340' },
telemetry: 'SKY · GFS 250 HPA · 210 KT · FL340',
age: 20,
}),
S({
id: 'atl-rush',
kind: 'airport',
family: 'sky',
band: 'sky',
archetype: 'number',
title: 'Busiest airport right now',
subtitle: 'Hartsfield–Jackson arrival bank',
place: 'Atlanta, Georgia',
lon: -84.428,
lat: 33.6407,
span: 0.09,
th: 600,
height: 18e3,
layers: [
'sky.commercial-flights',
'sky.holding-patterns',
'sky.airport-boards',
],
metrics: [
{ label: 'Movements', value: 182, unit: '/h' },
{ label: 'Holding', value: 6, unit: '' },
],
headline: { value: 182, unit: '/h', label: 'movements' },
telemetry: 'SKY · KATL · 182 MVT/H · 6 HOLDING',
series: series([120, 140, 166, 170, 182, 176, 181, 182], 30, now),
seriesLabel: 'movements / h',
age: 1,
alwaysOn: true,
}),
eye(C['ca-d4-tv301'], {
id: 'eye-sf',
title: 'US-101 at Octavia, San Francisco',
subtitle: 'Central Freeway merging into Mission',
place: 'San Francisco, California',
age: 0,
alwaysOn: true,
}),
S({
id: 'tokyo-rush',
kind: 'transit',
family: 'ground',
band: 'ground',
archetype: 'sensor',
title: 'Rush hour on the Yamanote line',
subtitle: 'Headways at 2 min 30 s through Shinjuku',
place: 'Shinjuku, Tokyo',
lon: 139.7,
lat: 35.69,
span: 0.05,
th: 600,
height: 9e3,
layers: ['ground.transit-vehicles', 'ground.rail'],
metrics: [
{ label: 'Trains moving', value: 1840, unit: '' },
{ label: 'On time', value: 98.6, unit: '%' },
],
telemetry: 'GROUND · GTFS-RT · 1,840 TRAINS · 98.6% OT',
age: 2,
alwaysOn: true,
}),
S({
id: 'aurora',
kind: 'aurora',
family: 'weather',
band: 'orbit',
archetype: 'sensor',
title: 'Aurora oval reaching 60°N tonight',
subtitle: 'Kp 6 storm, clear skies after 22:00',
place: 'Northern Scandinavia',
lon: 18.95,
lat: 69.65,
span: 8,
th: 600,
height: 1200e3,
layers: ['weather.clouds'],
metrics: [
{ label: 'Kp', value: 6, unit: '' },
{ label: 'Cloud', value: 20, unit: '%' },
],
headline: { value: 'Kp 6', unit: '', label: 'geomagnetic storm' },
telemetry: 'ORBIT · OVATION · KP 6 · OVAL 60N',
series: series([2, 3, 3, 4, 5, 6, 6], 180, now),
seriesLabel: 'Kp index',
age: 15,
}),
S({
id: 'singapore',
kind: 'strait',
family: 'sea',
band: 'ground',
archetype: 'sensor',
title: 'Singapore Strait at capacity',
subtitle: 'Eastbound lane full, 318 ships at anchor',
place: 'Singapore Strait',
lon: 103.85,
lat: 1.22,
span: 0.4,
th: 420,
height: 70e3,
layers: ['sea.ais-vessels'],
metrics: [
{ label: 'Transits', value: 22, unit: '/h' },
{ label: 'At anchor', value: 318, unit: '' },
],
telemetry: 'GROUND · AIS · 22 TRANSITS/H · 318 ANCHORED',
age: 3,
alwaysOn: true,
}),
];
}