feat: critical news pins on the osint map

This commit is contained in:
Sirius DevOps 2026-08-27 23:43:36 -04:00
parent 3b4dd49253
commit 496d13b8b0

View file

@ -264,6 +264,7 @@
.hud-chip .c.fires { background: #fb923c; box-shadow: 0 0 6px #fb923c; }
.hud-chip .c.cams { background: #4ade80; box-shadow: 0 0 6px #4ade80; }
.hud-chip .c.blips { background: var(--magenta); box-shadow: 0 0 6px var(--magenta); }
.hud-chip .c.news { background: var(--magenta); box-shadow: 0 0 6px var(--magenta); border-radius: 1px; transform: rotate(45deg); }
.hud-chip.off { opacity: 0.4; }
#dvr-bar {
position: absolute; left: 50%; bottom: 12px; transform: translateX(-50%);
@ -319,6 +320,7 @@
.lp-dot.fires { background: #fb923c; box-shadow: 0 0 7px #fb923c; }
.lp-dot.cams { background: #4ade80; box-shadow: 0 0 7px #4ade80; }
.lp-dot.blips { background: var(--magenta); box-shadow: 0 0 7px var(--magenta); }
.lp-dot.news { background: var(--magenta); box-shadow: 0 0 7px var(--magenta); border-radius: 1px; transform: rotate(45deg); }
.lp-dot.weather { background: var(--amber); box-shadow: 0 0 7px var(--amber); }
.lp-dot.flights { background: #7dd3fc; box-shadow: 0 0 7px #7dd3fc; }
.lp-dot.vessels { background: #2dd4bf; box-shadow: 0 0 7px #2dd4bf; }
@ -724,6 +726,13 @@
</div>
<div class="lp-note">Geolocated events from the ingest pipeline. Color = source type.</div>
</div>
<div class="lp-layer">
<div class="lp-row">
<label class="lp-name"><input type="checkbox" id="lp-news-on" checked onchange="toggleNewsPins()"> <span class="lp-dot news"></span> Critical News</label>
<span class="lp-count" id="lp-news-count">0</span>
</div>
<div class="lp-note">LLM-estimated locations from the hourly brief. Pins only for critical/high.</div>
</div>
<div class="lp-layer">
<div class="lp-row">
@ -804,6 +813,7 @@
<div class="hud-chip" id="hud-fires"><span class="c fires"></span>FIRMS <b id="hud-fires-n"></b></div>
<div class="hud-chip" id="hud-cams"><span class="c cams"></span>CAMS <b id="hud-cams-n"></b></div>
<div class="hud-chip" id="hud-blips"><span class="c blips"></span>BLIPS <b id="hud-blips-n"></b></div>
<div class="hud-chip" id="hud-news"><span class="c news"></span>NEWS <b id="hud-news-n"></b></div>
</div>
<div id="dvr-bar" class="live">
<button class="btn" id="dvr-live" type="button" onclick="dvrGoLive()">LIVE</button>
@ -1204,6 +1214,7 @@ function showView(name) {
if (firesOn) loadFires();
if (camsOn) loadCams();
if (blipsOn) loadBlips();
if (newsOn) loadNewsPins();
}
}
if (name === 'news') loadNews(false);
@ -1762,6 +1773,7 @@ async function saveSummaryModel() {
let map = null, mapLayer = null, mapInitStarted = false;
let firesHeat = null, camsGroup = null, firesOn = false, camsOn = false;
let blipsGroup = null, blipsOn = false, blipsSince = '24h';
let newsGroup = null, newsOn = false, newsReq = 0;
let firesOpacity = 0.65, firesColor = 'brightness', firesSince = '24h';
let camsOpacity = 1.0, baseOpacity = 1.0;
let camReq = 0, fireReq = 0, blipReq = 0; // stale-response guards (rapid zoom races)
@ -1954,6 +1966,7 @@ async function initMap() {
if (firesOn) loadFires();
if (camsOn) loadCams();
if (blipsOn) loadBlips();
if (newsOn) loadNewsPins();
refreshLiveOverlays();
sendLiveViewport();
}, 500);
@ -1965,6 +1978,7 @@ async function initMap() {
firesOn = document.getElementById('lp-fires-on').checked;
camsOn = document.getElementById('lp-cams-on').checked;
blipsOn = document.getElementById('lp-blips-on').checked;
newsOn = document.getElementById('lp-news-on').checked;
firesSince = document.getElementById('lp-fires-since').value;
blipsSince = document.getElementById('lp-blips-since').value;
radarOn = document.getElementById('lp-radar-on').checked;
@ -1979,6 +1993,7 @@ async function initMap() {
if (firesOn) loadFires();
if (camsOn) loadCams();
if (blipsOn) loadBlips();
if (newsOn) loadNewsPins();
refreshLiveOverlays();
connectLiveWs();
} catch(e) {
@ -2001,8 +2016,9 @@ function syncHud() {
set('fires', firesOn ? (hudFiresCount) : '—', firesOn);
set('cams', camsOn ? (hudCamsCount) : '—', camsOn);
set('blips', blipsOn ? (hudBlipsCount) : '—', blipsOn);
set('news', newsOn ? (hudNewsCount) : '—', newsOn);
}
let hudFiresCount = null, hudCamsCount = null, hudBlipsCount = null;
let hudFiresCount = null, hudCamsCount = null, hudBlipsCount = null, hudNewsCount = null;
function setMapAttribution(text) {
if (!map) return;
@ -2417,6 +2433,57 @@ async function loadBlips() {
}
}
/* ── CRITICAL NEWS (LLM-estimated locations, critical/high only) ── */
async function toggleNewsPins() {
newsOn = document.getElementById('lp-news-on').checked;
if (newsOn) {
await loadNewsPins();
return;
}
newsReq++;
if (newsGroup && map) { map.removeLayer(newsGroup); newsGroup = null; }
hudNewsCount = null;
const countEl = document.getElementById('lp-news-count');
if (countEl) countEl.textContent = '0';
syncHud();
}
async function loadNewsPins() {
if (!map) return;
const req = ++newsReq;
try {
const url = `${API}/api/news/map?bbox=${currentBBox()}&limit=200`;
const r = await overlayFetch(url);
const data = await r.json();
if (req !== newsReq) return; // superseded by a newer pan/zoom
const items = Array.isArray(data) ? data : [];
if (newsGroup) map.removeLayer(newsGroup);
const col = '#ff2e97';
const pins = items.filter(it => Number.isFinite(Number(it.lat)) && Number.isFinite(Number(it.lon)));
newsGroup = L.layerGroup(pins.map(it => {
const icon = L.divIcon({
className: '',
html: `<span style="display:inline-block;width:10px;height:10px;background:${col};box-shadow:0 0 8px ${col},0 0 2px #0f172a;border:1px solid #030609;transform:rotate(45deg);"></span>`,
iconSize: [14, 14], iconAnchor: [7, 7],
});
return L.marker([it.lat, it.lon], { icon })
.bindPopup(`<div class="blip-pop">` +
`<div class="blip-src">◈ ${esc(it.importance || '')}${it.category ? ' · ' + esc(it.category) : ''}</div>` +
`<b>${esc(it.headline || '')}</b>` +
`<div class="blip-time">${esc(it.location_name || '')}</div>` +
(it.url ? `<a href="${esc(it.url)}" target="_blank" rel="noopener">source ↗</a>` : '') +
`</div>`);
}));
newsGroup.addTo(map);
document.getElementById('lp-news-count').textContent = pins.length.toLocaleString();
hudNewsCount = pins.length;
syncHud();
} catch(e) {
if (isAbort(e)) return;
document.getElementById('map-hint').textContent = `News pins load failed: ${e.message || e}`;
console.error('News pins load failed', e);
}
}
/* ── Live overlays (radar / alerts / WFIGS / aircraft / trains / AIS / NHC) ── */
function refreshLiveOverlays() {
if (!map) return;