From a5f98701792513dbd48d417937ee332340da0d5d Mon Sep 17 00:00:00 2001 From: Denis Groshev Date: Thu, 5 Nov 2026 19:45:19 +0100 Subject: [PATCH] Document the News layer's own sources, keys and limits DATA_SOURCES, the README's layer and key tables, CURRENT-STATE and the changelog describe the News layer as it now is: a configured news server, or Core's own reading of GDELT (no key), Liveuamap (optional) and World News API (Local, free plan backlink credited). .env.example gives the download sizes and the daily read budget; KNOWN-ISSUES lists what machine coding gets wrong and which disputed areas are named as themselves. --- .env.example | 25 +++++++++++++++++--- DATA_SOURCES.md | 6 ++++- README.md | 15 ++++++++---- changelog.d/0-regional-news-freshness.md | 2 +- changelog.d/1-news-layer.md | 30 ++++++++++++++++-------- docs/CURRENT-STATE.md | 10 ++++---- docs/KNOWN-ISSUES.md | 22 ++++++++++++++++- src/tooling/transitQa.test.mjs | 2 +- 8 files changed, 86 insertions(+), 26 deletions(-) diff --git a/.env.example b/.env.example index d026e35e2..2b62f8871 100644 --- a/.env.example +++ b/.env.example @@ -318,7 +318,26 @@ GEMINI_CHAT_MODEL=gemini-3.5-flash-lite # Restart after changing. Leave blank for the existing global selection. # RADIO_PRIORITY_COUNTRIES=IN,PL # Optional: a news server for the News layer (its /api/news/hotspots, /feed -# and /outline routes), such as https://news.example. The dev server forwards -# only the layer's own GET requests there, with a 15 s timeout. Unset, Core -# serves no news and the layer shows itself unavailable. SERVER-SIDE ONLY. +# and /outline routes), such as https://news.example. The local server +# forwards only the layer's own GET requests there, with a 15 s timeout. Set, +# it wins over the settings below. SERVER-SIDE ONLY. # GEV_NEWS_ORIGIN= +# +# Without a news server Core reads the News layer itself. SERVER-SIDE ONLY. +# Conflicts need no key: the last 24 h of GDELT events (its 15-minute export +# files, about 100 KB each, about 10 MB a day; violence only, one report per +# story at GDELT's place for it), titled from GDELT's GKG files (about 6 MB +# each, about 23 MB an hour: the newest 3 h are read at start, then each new +# one). Optional: Liveuamap's mapped reports beside them (paid API, +# https://liveuamap.com/promo/api), read once every 5 minutes. +# LIVEUAMAP_API_KEY= +# Local: the day's news from the outlets of the country in view (free plan +# of 50 points a day, https://worldnewsapi.com/console/), read once per +# country every 30 minutes, about 1.5 points a read. Without it Local says +# "key required". The free plan asks for a visible "World News API" link; +# the app's Data attribution popover carries it. +# WORLDNEWSAPI_KEY= +# The most World News API reads a day (default 30, about 45 points, inside +# the free plan). After that Local says the daily limit is reached. +# Raise it on a paid plan. +# WORLDNEWSAPI_DAILY_READS=30 diff --git a/DATA_SOURCES.md b/DATA_SOURCES.md index 5bddb2349..3fdfda97b 100644 --- a/DATA_SOURCES.md +++ b/DATA_SOURCES.md @@ -39,7 +39,10 @@ How to read this: | **NASA GIBS** (WMTS tiles + Worldview Snapshots) and **NASA CMR** (granule catalog) | Recent Imagery layer: HLS S30/L30 (Sentinel-2, Landsat 8/9) and VIIRS true-colour tiles draped on the map, per-day thumbnails and PNG exports; the HLS granule catalog (dates, cloud, footprints) for a selected box | U.S. public domain (NASA data); keyless, browser-direct (CORS `*`); no bulk use — one 30-day catalog query per box, tiles bounded to the box, six tile requests in flight | "We acknowledge the use of imagery provided by services from NASA's Global Imagery Browse Services (GIBS), part of NASA's Earth Science Data and Information System (ESDIS)." — [gibs.earthdata.nasa.gov](https://gibs.earthdata.nasa.gov), [HLS](https://lpdaac.usgs.gov/products/hlss30v002/) | | **NIFC WFIGS** (Wildland Fire Interagency Geospatial Services, ArcGIS feature service) | Fire Perimeters layer: current interagency wildfire incident perimeters, containment, and incident facts | U.S. public domain (interagency wildland-fire data published through the [NIFC Open Data portal](https://data-nifc.opendata.arcgis.com/)); fetched keyless through `/api/fire-perimeters` (5-minute server cache) with `exceededTransferLimit` paging | "Wildfire perimeters: National Interagency Fire Center (WFIGS)" | | **S2 Underground GhostMaps** (`github.com/s2underground/GhostMaps`) | S2 GhostMaps layer: the current edition of S2's KMZ intel products (Common Intelligence Picture, 30-day kinetic pull, Border Crisis Map, Iran and Yemen products, newest incident drop). Keyless. `/api/ghostmaps` reads the branch head from git's ref advertisement every 15 s (`GHOSTMAPS_POLL_SECONDS`, minimum 5 s), lists the tree through the GitHub REST API only when the head changes (optional `GITHUB_TOKEN`), downloads files by commit-pinned raw URLs into `.gev-cache/ghostmaps/` and pushes new catalogs to browsers over server-sent events | [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) (repository README: "S2 Underground © 2025 by S2A1"); non-commercial, attribution required; files are fetched at runtime, never stored in this repository | "S2 GhostMaps intel products: S2 Underground © by S2A1, CC BY-NC-SA 4.0" — shown in-app | -| **News server** (`GEV_NEWS_ORIGIN`, optional; none by default) | News layer: curated hotspots and the last 24 h of reports from many publishers, merged into events. Core serves no news itself; the local server forwards the layer's `/api/news/hotspots`, `/feed` and `/outline` GET requests to the configured server | Each report stays its publisher's: headlines and summaries link to the original article, and pictures are hotlinked from the publisher's site (no referrer), never stored | Per report: publisher name and article link on every card; picture cards name the picture's publisher. No single provider is credited for the feed | +| **News server** (`GEV_NEWS_ORIGIN`, optional; none by default) | News layer, both sections: the last 24 h of reports from many publishers. Set, the local server forwards the layer's `/api/news/hotspots`, `/feed` and `/outline` GET requests to it and Core reads none of the three sources below. Which providers stand behind its feed is the news server's to state | Each report stays its publisher's: headlines and summaries link to the original article, and pictures are hotlinked from the publisher's site (no referrer), never stored | Per report: publisher name and article link on every card; picture cards name the picture's publisher. The Data attribution popover states that places, categories, severity and English headlines are derived automatically and can be wrong | +| **GDELT 2.0 events and GKG** (`data.gdeltproject.org/gdeltv2/`; no key; used only without a news server) | News layer, Conflicts: the last 24 h of GDELT's 15-minute event exports (`*.export.CSV.zip`, about 100 KB each), read server-side as they appear. Physical violence only (CAMEO roots 18–20); one report per story, copies of one agency story merged; placed at GDELT's location for it (a town the article's words name beats a bare country). Titled by the article's page title from the GKG export of the same quarter hour (`*.gkg.csv.zip`, about 6 MB each) for the newest 3 h, else by the words of the article's address. GDELT's actor coding is never shown. Categories, relevance and severity by bundled rules | [GDELT Terms of Use](https://www.gdeltproject.org/about.html#termsofuse): unrestricted academic, commercial and governmental use, with citation and a link to the GDELT Project. Coded events, page titles and article links only; linked articles keep their publishers' terms | "GDELT Project" with a link to gdeltproject.org in the Data attribution popover; "via GDELT Project", the publisher's domain and the article link on every card | +| **Liveuamap** (`a.liveuamap.com/api`, `LIVEUAMAP_API_KEY`, optional, paid; used only without a news server) | News layer, Conflicts, beside GDELT: Liveuamap's worldwide mapped items of the last 24 h (`a=mpts`), read server-side every 5 minutes; categories and severity are read from the headline by bundled rules | Liveuamap API terms of the operator's own subscription. Headlines, places and links only; item pictures and clips are hotlinked from Liveuamap, never stored | "Liveuamap" with a link in the Data attribution popover once its items show; "Liveuamap" and the item's Liveuamap link on every card | +| **World News API** (`api.worldnewsapi.com/search-news`, `WORLDNEWSAPI_KEY`, optional; used only without a news server) | News layer, Local: the last 24 h of news by the source country at the view centre (country from the bundled Natural Earth admin-0 pack; none in a disputed area), read server-side once per country every 30 minutes, about 1.5 points a read; at most `WORLDNEWSAPI_DAILY_READS` reads a day (default 30), after which Local says the daily limit is reached. Sport, entertainment, lifestyle and travel left out | [World News API terms](https://worldnewsapi.com/terms/) of the operator's own plan; the free plan (50 points a day) requires a visible backlink to worldnewsapi.com in the app, paid plans do not. Titles and links only; article text and summaries are not kept or shown; pictures are hotlinked from the publisher | The "World News API" link to worldnewsapi.com, always in the Data attribution popover (the free plan's required backlink); "via World News API", the publisher's domain and the article link on every card | | **InciWeb** (inciweb.wildfire.gov) | Per-incident "InciWeb ↗" card links to official incident pages (narratives, evacuation/closure notices) | U.S. government public incident information. Links are matched best-effort by incident name and state, with incident page origin and update times checked on selection. Same-origin `/api/fire-perimeters/inciweb` routes cache the catalog for 1 hour and publication metadata for 30 minutes; linked pages remain InciWeb content | "Incident information: InciWeb (inciweb.wildfire.gov)" | | **FAA Special Use Airspace** (`services6.arcgis.com` FeatureServer `Special_Use_Airspace`) | Prohibited & Restricted Airspace layer: US prohibited (P) and restricted (R) special-use airspace polygons (not the UAS Facility Map or UAS flight restrictions), altitude limits from the `*_VAL`/`*_UOM`/`*_CODE` fields, times of use, and controlling agency | U.S. public domain (FAA aeronautical data published through the [FAA AIS Open Data portal](https://adds-faa.opendata.arcgis.com/)); fetched keyless through `/api/drone-airspace` (1-hour server cache). Informational only — not a substitute for official UAS flight-planning tools | "Prohibited & restricted airspace: FAA Special Use Airspace" | | **OpenFreeMap / OpenMapTiles** | Vector tiles | OpenStreetMap data under ODbL 1.0 | OpenFreeMap © OpenMapTiles | @@ -220,6 +223,7 @@ The [Bhote Koshi event pack](public/events/bhote-koshi-2026/README.md), under `p | **Natural Earth physical regions** (1,046 land + 292 marine named polygons) | `natural_earth/` | **Public domain** | ✅ (no restrictions) | "Made with Natural Earth" (courtesy credit — not legally required) | | **DataSF Analysis Neighborhoods** (41 SF neighborhood polygons) | `neighborhoods/` | **PDDL 1.0** (public domain) | ✅ (no restrictions) | "City & County of San Francisco — DataSF" (courtesy — not legally required) | | **Natural Earth countries, states and provinces** (260 admin-0/map units + 4,587 admin-1 polygons) | `natural_earth/` | **Public domain** | ✅ (no restrictions) | "Made with Natural Earth" (courtesy credit — not legally required) | +| **Natural Earth disputed areas** (11 of the source's 99 features, hand-picked; [list](src/data/local_data/natural_earth/README.md#disputed-areas-disputed_areasjson)) | `natural_earth/` | **Public domain** | ✅ (no restrictions) | "Made with Natural Earth" (courtesy credit — not legally required) | | **US Census Bureau counties** (3,235 county polygons) | `us_census_counties/` | **Public domain** | ✅ (no restrictions) | "U.S. Census Bureau" (courtesy — not legally required) | | **US Census Bureau places** (32,629 city, town and CDP polygons) | `us_census_places/` | **Public domain** | ✅ (no restrictions) | "U.S. Census Bureau" (courtesy — not legally required) | | **Who's On First neighborhoods** (61,441 polygons) | `wof_neighborhoods/` | Per source: CC BY, ODC-By, Open Government licences, CC0, PDDL, public domain (folder `ATTRIBUTION.md`) | ✅ (attribution) | "Neighborhood boundaries: Who's On First and contributors"; per-source credits in the folder | diff --git a/README.md b/README.md index 6212f3991..d955ee57c 100644 --- a/README.md +++ b/README.md @@ -426,7 +426,7 @@ _Ask for radio near anywhere and the globe starts broadcasting — every station ## 🛰️ What's on the Globe -Twenty-one layers and map sources. **Nineteen have a keyless path.** Some offer additional capabilities with a provider key. (🟢 no key · 🟡 free key · 🔴 metered.) +Twenty-two layers and map sources. **Twenty have a keyless path.** Some offer additional capabilities with a provider key. (🟢 no key · 🟡 free key · 🔴 metered.) | Layer | What you get | Source | Auth | | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------- | @@ -438,6 +438,7 @@ Twenty-one layers and map sources. **Nineteen have a keyless path.** Some offer | 🚢 **Live Vessels** | Thousands of ships worldwide | AISStream | 🟡 | | 🛰️ **Satellites** | 838-object catalog, color-coded by class with a live legend — the **DENSE** chip drops in the whole Starlink shell | CelesTrak | 🟢 | | 🌍 **Earthquakes** | Global seismic activity, last 24h | USGS | 🟢 | +| 📰 **News** | The last 24 hours of news in the right-rail **NEWS** panel, narrowed to 12, 6, 3 or 1 hours, with picture cards and clips. **Conflicts**: physical violence worldwide from GDELT's event files, one report per story at GDELT's place for it, plus Liveuamap's mapped items with a key. **Local**: the news of the country at the view centre, its outline and name on the globe; a disputed area of the bundled list is named as itself. Places and categories are machine-coded and can be wrong | GDELT · Liveuamap · World News API, or your own news server | 🟢 Conflicts · 🟡 Local (World News API) · 🔴 Liveuamap items | | ◍ **Air quality** | Live AQHI on the published 1–10+ health-risk scale, with a Weather-panel card showing the band legend and the highest readings in view. **Canada only** — ECCC is the first registered provider | ECCC MSC GeoMet | 🟢 | | 🚗 **Traffic** | Simulated vehicles on OSM roads. With TomTom, live flow speeds drive the simulation and congestion colors below ~8 km; individual vehicle positions are not live observations | TomTom + OSM | 🟢 simulation · 🟡 live flow speeds | | 📹 **CCTV Mesh** | ~4,000 public cameras (shared cap) projected _into_ the 3D space — Austin · Texas (TxDOT) · California (Caltrans) · London (TfL) · Ontario (511, free developer key) · Finland (Fintraffic) · British Columbia (DriveBC) · Estonia (Tallinn, Tarktee) · Delaware (DelDOT live video) · New South Wales (Live Traffic NSW) · Calgary · Norway (Statens vegvesen) · Toronto · King County (WA) · Washington (WSDOT) · Catalonia (Servei Català de Trànsit) · Hong Kong (Transport Department) · Taiwan (Freeway Bureau, MOTC) · Italy (A22 Brennero motorway), plus opt-in New York City (NYC DOT TMC, needs your own NYC DOT agreement), opt-in Lithuania (Via Lietuva / Eismoinfo, photo licence unconfirmed), opt-in Florida (FL511, individual personal use only; other use needs FDOT's written consent) and opt-in Houston (TranStar, enable only with TxDOT's written permission per TranStar's Content Usage terms). Positions are published; poses are estimated priors **you calibrate by dragging a gizmo on the camera itself** | City APIs | 🟢 | @@ -566,12 +567,14 @@ and configuration details. No key needed for Wind, Rain radar, Satellite clouds, Lightning density, Cyclone advisories, Mapped ALPR Cameras, Transit, Directions, Delaware live -video, and coordinate or bundled-place search. A layer row waiting on a key +video, the News layer's Conflicts section, and coordinate or bundled-place +search. A layer row waiting on a key names it. ### Choose the capabilities you want -Seven keys. Five have a free tier, and the two 🔴 ones are metered: +Nine keys. Six have a free tier. Of the three 🔴 ones, Google Maps and OpenAI are +metered and Liveuamap is a paid subscription: | | Key | Why | Get it | | --- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -582,6 +585,8 @@ Seven keys. Five have a free tier, and the two 🔴 ones are metered: | 🟡 | **NASA FIRMS** | 🔥 Live active fires | [firms.modaps.eosdis.nasa.gov](https://firms.modaps.eosdis.nasa.gov/api/map_key/) — free | | 🟡 | **TomTom** | 🚦 Live flow speeds and congestion colors for the simulated traffic layer | [my.tomtom.com](https://my.tomtom.com/keys) — free tier available | | 🟡 | **Mapillary** | 📷 Street Level coverage and photos. A public client token by design (see SECURITY.md) | [mapillary.com/dashboard/developers](https://www.mapillary.com/dashboard/developers) — register an app, copy its Client Token | +| 🟡 | **World News API** | 📰 News layer, Local: the news of the country in view. The free plan (50 points a day) needs the in-app "World News API" link, which the Data attribution popover carries; `WORLDNEWSAPI_DAILY_READS` (default 30) keeps Core inside it | [worldnewsapi.com/console](https://worldnewsapi.com/console/) — free plan | +| 🔴 | **Liveuamap** | 📰 News layer, Conflicts: Liveuamap's mapped items beside GDELT's reports (🟢 Conflicts works without) | [liveuamap.com/promo/api](https://liveuamap.com/promo/api) — paid API | ![Diving from city-scale live congestion straight into an intersection's public camera](docs/media/05-traffic-to-cctv.gif) @@ -655,8 +660,8 @@ Honest numbers, roughly, as of mid-2026 — always check the provider pricing pa | | Cost reality | | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **🟢 Most layers** | **$0, no signup.** OpenSky anon, USGS, CelesTrak, adsb.lol, city CCTV, Radio Browser, GBFS, Launch Library 2, transit feeds, OSRM routing, NOAA and ECMWF weather, OpenStreetMap ALPR mapping, bundled datasets. | -| **🟡 The free-key tier** | **$0 with a signup.** AISStream, FIRMS, TomTom, OpenSky, plus Cesium ion for eligible personal/non-commercial use. Provider quotas and eligibility still apply. | +| **🟢 Most layers** | **$0, no signup.** OpenSky anon, USGS, CelesTrak, adsb.lol, city CCTV, Radio Browser, GBFS, Launch Library 2, transit feeds, OSRM routing, NOAA and ECMWF weather, OpenStreetMap ALPR mapping, GDELT news events, bundled datasets. | +| **🟡 The free-key tier** | **$0 with a signup.** AISStream, FIRMS, TomTom, OpenSky, World News API, plus Cesium ion for eligible personal/non-commercial use. Provider quotas and eligibility still apply. | | **🗺️ Google 3D tiles** | **Free through an eligible Cesium ion Community account within its quota; metered through a direct Google key.** Use the direct route for Google place search or commercial deployment, verify current provider terms, and set budget alerts where billing is enabled. | | **🔴 OpenAI voice** | API-key sessions keep the live spend readout, STD/MINI model toggle, $2 warning, and **$5 hard cap**. ChatGPT OAuth is an explicit choice in Provider Settings. Its usage is tracked without an API-dollar estimate or spend cap. The voice context window stays deliberately short. | diff --git a/changelog.d/0-regional-news-freshness.md b/changelog.d/0-regional-news-freshness.md index b0d6d831f..7ab1e3032 100644 --- a/changelog.d/0-regional-news-freshness.md +++ b/changelog.d/0-regional-news-freshness.md @@ -4,4 +4,4 @@ a failed refresh as `CACHED · REFRESH FAILED`, `NO NEWS IN THE LAST 24 H`, and `REGIONAL NEWS UNAVAILABLE`. The banner is now really hidden over a populated list, and the footer no longer reverts to a static `RECENT` on a - page change. + page change (AUTHOR, #0). diff --git a/changelog.d/1-news-layer.md b/changelog.d/1-news-layer.md index d8925eaeb..3448367df 100644 --- a/changelog.d/1-news-layer.md +++ b/changelog.d/1-news-layer.md @@ -1,11 +1,21 @@ - Add the News layer (Events ▸ News) and its right-rail NEWS panel: the last - 24 hours from curated hotspots (or the fewer hours a server names), narrowed - in the panel to the last 12, 6, 3 or 1 hours with the hotspot counts read for - that window, coloured by severity only, with hotspot - beacons, outlines with disputed areas, picture cards for major events and a - continuous feed whose cards fly the globe to the event. A feed card shows its - event's picture even when one source reports it, and an open card plays the - post's clip on request. The panel's Local - section lists every report within 500 km of the view below 3,000 km, and the - map shows those instead of the hotspots' news while it is chosen. Core serves no news itself: set `GEV_NEWS_ORIGIN` to a news - server and the local server forwards the layer's three read routes there. + 24 hours of news, narrowed in the panel to the last 12, 6, 3 or 1 hours with + the hotspot counts read for that window. Markers are coloured by severity + only, with hotspot beacons, outlines with disputed areas, picture cards for + major events and a continuous feed whose cards fly the globe to the event. + An open card plays the post's clip on request. The panel has two sections. + Conflicts lists the hotspots' news. Local, below 3,000 km, lists every + report in the area at the view centre, names and outlines that area on the + globe, and shows those reports on the map instead while it is chosen; a + disputed area is named as itself and read from no country's outlets. Set + `GEV_NEWS_ORIGIN` to a news server and the local server forwards the + layer's three read routes there. Unset, Core answers them itself: + Conflicts from the last 24 hours of GDELT 2.0 event exports with no key + (physical violence only, one report per story at GDELT's place for it, + titled from GDELT's GKG files for the newest 3 hours), plus Liveuamap's + mapped items with `LIVEUAMAP_API_KEY`; Local from World News API with + `WORLDNEWSAPI_KEY`, at most `WORLDNEWSAPI_DAILY_READS` reads a day + (default 30). A section says when its key is required, refused or out of + its daily allowance. The Data attribution popover credits the GDELT + Project, World News API and, once its items show, Liveuamap + (AUTHOR, #1). diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 4a52c0d74..ea0210440 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -4216,7 +4216,7 @@ its criteria cannot be silently ignored. | Ski Pistes ⛷ (`ski-pistes`) | OpenSnowMap piste raster tiles (keyless; CC BY-SA tiles, OSM data ODbL; no bulk download) | `src/data/osmOverlays.js` | — (browser-direct tiles) | on view (Cesium tile requests; no polling) | | Fire Perimeters 🔥 | NIFC WFIGS current interagency perimeters (keyless, paged past the 2000-record cap); InciWeb catalog + incident page origin and update time checks for verified incident-page links | `src/layers/perimeters/` via `src/app/layers/perimeters.js` | `/api/fire-perimeters` + `/api/fire-perimeters/inciweb/*` | 5 min (server caches: catalog 1 h; publication 30 min) | | Prohibited & Restricted Airspace 🚫 | FAA Special Use Airspace prohibited (P) and restricted (R) zones (keyless FeatureServer, simplified geometry) | `src/layers/droneAirspace/` via `src/app/layers/droneAirspace.js` | `/api/drone-airspace` | 1 h (server cache 1 h; stale-on-error) | -| News ◆ (`news`) | A news server's `/api/news` read routes (curated hotspots, last 24 h of reports merged into events, outlines); Core serves none itself | `src/layers/news/` via `src/app/layers/news.js`; panel `src/ui/newsPanel.js` | `/api/news/hotspots`, `/api/news/feed`, `/api/news/outline` (local dev proxies `GEV_NEWS_ORIGIN`; unset answers "not configured") | 2 min map refresh; In view re-read when the camera settles 0.8 s in a new half-degree cell below 3,000 km | +| News ◆ (`news`) | The last 24 h of reports in two sections, Conflicts and Local, with hotspots, outlines and disputed areas: from a news server (`GEV_NEWS_ORIGIN`), else Core's own reading of GDELT 2.0 events (no key), Liveuamap (`LIVEUAMAP_API_KEY`) and World News API (`WORLDNEWSAPI_KEY`) | `src/layers/news/` via `src/app/layers/news.js`; panel `src/ui/newsPanel.js`, `src/ui/news/`; server `server/providers/news.js`, `server/providers/news/` | `/api/news/hotspots`, `/api/news/feed`, `/api/news/outline` (forwarded to `GEV_NEWS_ORIGIN` when set, else answered by Core) | 2 min map refresh; Local re-read when the camera settles 0.8 s in a new half-degree cell below 3,000 km | GNSS Integrity samples a 250 nm circle around the Earth point under the screen centre. Camera reads debounce for 600 ms with a monotonic 2.5 s minimum gap; @@ -5849,9 +5849,11 @@ request viewbox and continues searching without that geographic preference. ### News (November 2026) -- **What it shows.** Events ▸ **News**: reports from curated hotspots (Ukraine–Russia, Israel–Gaza–Lebanon, Iran–Hormuz, Yemen–Red Sea, Sudan, Sahel, and developing situations elsewhere), the last 24 hours only. Zoomed out, one beacon per hotspot with its daily count and trend, and a breathing beacon per developing situation (still under reduced motion). Below 2,500 km (7,500 km with a hotspot chosen), the hotspot outlines with disputed areas dotted and named, and every geocoded report as a ground-clamped marker: fill is severity (0 grey no physical event, 1 yellow minor, 2 orange significant, 3 red major), the icon is the category, the rim is attestation (solid confirmed, dashed claim, thin single), size grows with sources and the marker fades over the day. Colour never marks a side. Up to ten picture cards (physical events with two or more sources, major or pressing) stand over their markers without overlapping; "Show event cards" off leaves only the selected one. A selected report's card reads "Source: host ↗" and opens the article. -- **Panel.** The right-rail NEWS panel (`src/ui/newsPanel.js`, `src/ui/news/`) shows "Last 24 h", hotspot chips, severity (significant and above by default), category, "Include minor news" (off) and "Show event cards" (on), "In view" (reports within 500 km of the view centre while the camera is below 3,000 km) and a continuous feed of event cards: the publisher's picture when the event has one (hotlinked, no referrer, publisher credited), the headline (the model's English headline marked as such), a mono meta line (severity marker, category, place, sources, age), "Reported by UA sources · RU sources", attestation and media chips. A click flies the camera straight down from 150 km and selects the report. -- **Source.** `sources.news` (`createNewsSource`, export `./layers/news/source`) reads same-origin `/api/news/*` with a 15 s timeout, a 4 MiB cap and record-by-record validation; `newsQueryAllowed` lets a host admit exactly the client's requests. Core's local server (`server/providers/news.js`) forwards only those GET requests to `GEV_NEWS_ORIGIN`; unset, every route answers 503 `news_not_configured` and the layer reads unavailable without retrying until it is turned off and on. The feed has no area query, so In view filters the day's geocoded reports by distance. +- **What it shows.** Events ▸ **News**: the last 24 hours only, or the fewer hours a server names. Zoomed out, one beacon per hotspot with its count for the window and its trend, and a breathing beacon per developing situation (still under reduced motion). Below 2,500 km (7,500 km with a hotspot chosen), the hotspot outlines with disputed areas dotted and named, and every placed report as a ground-clamped marker: fill is severity (0 grey no physical event, 1 yellow minor, 2 orange significant, 3 red major), the icon is the category, the rim is attestation (solid confirmed, dashed claim, thin single), size grows with sources and the marker fades over the day. Colour never marks a side. Up to ten picture cards (physical events with two or more sources, major or pressing) stand over their markers without overlapping. A selected report's card reads "Source: host ↗" and opens the article. +- **Panel.** The right-rail NEWS panel (`src/ui/newsPanel.js`, `src/ui/news/`) has a time window at the top (the server's 24 h, or 12, 6, 3 or 1 h; the hotspot counts are read for the chosen window) and two sections. **Conflicts**: hotspot chips with counts, a severity floor (significant and above by default), a category filter, the chosen event, and a continuous feed, newest first. **Local**, while the camera is below 3,000 km: every report in the area at the view centre, named at the top ("News from …") and outlined on the globe, which then shows those reports instead of the hotspots' news; at sea, reports within 500 km. A disputed area of the bundled list (11, see `src/data/local_data/natural_earth/README.md`) is named as itself and read from no country's outlets; elsewhere Natural Earth's default country lines apply. Each card shows the publisher's picture when the event has one (hotlinked, no referrer, publisher credited), the headline (a model's English headline marked as such), a mono meta line (severity marker, category, place, sources, age), who reported it, attestation and media chips, and the service it came through ("via GDELT Project"). Open, a card plays the post's clip on request. A click flies the camera straight down from 150 km and selects the report. +- **Notices.** A section whose key the server lacks says "{provider} key required" and names the variable; a refused key says "{provider} refused its key"; a spent daily allowance says "{provider} limit reached for today". The other section keeps working. +- **Source.** `sources.news` (`createNewsSource`, export `./layers/news/source`) reads same-origin `/api/news/*` with a 15 s timeout, a 4 MiB cap and record-by-record validation; `newsQueryAllowed` lets a host admit exactly the client's requests. Core's local server (`server/providers/news.js`) forwards only those GET requests to `GEV_NEWS_ORIGIN` when it is set. +- **Built-in backend.** With `GEV_NEWS_ORIGIN` unset, Core answers the same routes itself (`server/providers/news/keyed.js`). **Conflicts** needs no key: the last 24 h of GDELT 2.0 event exports (15-minute files on `data.gdeltproject.org`, about 100 KB each), physical violence only (CAMEO roots 18–20), one report per story with copies of one agency story merged, placed at GDELT's location for it (a town the article's words name beats a bare country). Titles are the article's page title from GDELT's GKG export of the same quarter hour (about 6 MB each) for the newest 3 h, else the words of the article's address; GDELT's actor coding is never shown. With `LIVEUAMAP_API_KEY`, Liveuamap's mapped items stand beside GDELT's. **Local** reads World News API (`WORLDNEWSAPI_KEY`) by the source country at the view centre, once per country every 30 minutes (about 1.5 points a read, at most `WORLDNEWSAPI_DAILY_READS` reads a day, default 30), plus the Conflicts reports placed inside the area. Areas come from the bundled Natural Earth countries and disputed areas (`server/providers/news/areas.js`). Categories, relevance and severity are bundled rules (`categorize.js`); a news server may replace them. - **State.** Share/restore token `6`, enabled-only. Voice is off for this layer (`VOICE_OFF_LAYERS`). ### S2 GhostMaps (October 2026) diff --git a/docs/KNOWN-ISSUES.md b/docs/KNOWN-ISSUES.md index 53c7949c6..3a87eb1b5 100644 --- a/docs/KNOWN-ISSUES.md +++ b/docs/KNOWN-ISSUES.md @@ -1,6 +1,6 @@ # KNOWN ISSUES -Updated: September 22, 2026 +Updated: November 5, 2026 This file tracks active runtime issues only. @@ -127,6 +127,26 @@ Status: Open (limits) --- +### News layer without a news server +Status: Open (limits) + +- GDELT's places and categories are machine-coded and approximate. A report can + be placed in the wrong town of the same name, or at a country an article only + names as a nationality ("Syrian refugees" placed in Syria). +- Copies of one agency story are merged, but the same event under different + wording stays as separate reports. +- Only the 11 disputed areas in the bundled list + (`src/data/local_data/natural_earth/README.md`) are named as themselves; + everywhere else Natural Earth's default country lines apply, so a place in + Western Sahara or Kashmir, for example, is named with the country those + lines put it in. GDELT places a whole city at one point: Jerusalem's falls + inside East Jerusalem, so it is named "Jerusalem" alone. +- Local needs a World News API key. Its free plan allows 50 points a day; Core + stops at `WORLDNEWSAPI_DAILY_READS` reads (default 30) and Local then says the + daily limit is reached. + +--- + ## Closed / Intentional (for clarity) ### Proxy SSRF and error-surface hardening gaps diff --git a/src/tooling/transitQa.test.mjs b/src/tooling/transitQa.test.mjs index a879eac5c..0b4af4a16 100644 --- a/src/tooling/transitQa.test.mjs +++ b/src/tooling/transitQa.test.mjs @@ -695,7 +695,7 @@ test('public transit documentation uses courtesy attribution and the approved gr ); assert.ok( readme.includes( - 'Twenty-one layers and map sources. **Nineteen have a keyless path.**', + 'Twenty-two layers and map sources. **Twenty have a keyless path.**', ), ); assert.ok(