0eebcb3aec
Pipeline web data-only complet : Cote feeds, OSC target :57124 ajoute au profil data-only.toml pour que le bridge.py diffuse vers : SC (57121) + oF (57123) + Web (57124). Cote web (data_only_viz/web/) : - server.js : Express :3211 + WS broadcast + osc UDP :57124 parse les paths /data/<feed>/<sub> en JSON et diffuse - public/dashboard.html : grille de cards live avec sparklines SVG vanilla (USGS, SWPC Kp/wind/Bz/X-ray, Blitz, OpenSky, Bluesky, meteo, air, ISS, volcans, social_buzz, grid) - public/map.html : Leaflet dark fullscreen avec markers ephemeres (60s TTL) pour seismes/foudre/avions/volcans + marker ISS persistant qui suit la position Cote launcher : - ProcessManager : startDataWeb / stopDataWeb + openDataDashboard / openDataMap, port :3211 hardcode, script attendu a metalVizDir/web/server.js - MenuBarContent : nouvelle row 'Dashboard data-only' en mode .dataOnly et .bodyMesh + boutons Dashboard/Carte monde quand le serveur tourne - stopAll inclut dataWebProc
96 lines
2.8 KiB
JavaScript
96 lines
2.8 KiB
JavaScript
// Carte mondiale fullscreen : seismes, foudre, avions, ISS, volcans.
|
|
// Markers ephemeres (fade out apres TTL_MS) + ISS persistant.
|
|
|
|
const map = L.map("map", { zoomControl: false, attributionControl: false })
|
|
.setView([20, 0], 2);
|
|
L.tileLayer(
|
|
"https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}.png",
|
|
{ maxZoom: 12 }
|
|
).addTo(map);
|
|
|
|
const connEl = document.getElementById("conn");
|
|
const countsEl = document.getElementById("counts");
|
|
let total = 0;
|
|
|
|
// ------------------- WebSocket --------------------------------
|
|
function connect() {
|
|
const url = (location.protocol === "https:" ? "wss://" : "ws://")
|
|
+ location.host;
|
|
const ws = new WebSocket(url);
|
|
ws.onopen = () => connEl.textContent = "connecte";
|
|
ws.onclose = () => {
|
|
connEl.textContent = "deconnecte";
|
|
setTimeout(connect, 2000);
|
|
};
|
|
ws.onmessage = (ev) => {
|
|
try { handleMessage(JSON.parse(ev.data)); }
|
|
catch (e) { console.warn(e); }
|
|
};
|
|
}
|
|
connect();
|
|
|
|
// ------------------- Marker helpers ---------------------------
|
|
const TTL_MS = 60_000; // markers visibles 60s
|
|
|
|
function ephemeralMarker(lat, lon, kind, label) {
|
|
const icon = L.divIcon({
|
|
className: "",
|
|
iconSize: [10, 10],
|
|
html: `<div class="${kind}" style="width:10px;height:10px"></div>`,
|
|
});
|
|
const m = L.marker([lat, lon], { icon }).addTo(map);
|
|
if (label) m.bindTooltip(label, { direction: "top" });
|
|
setTimeout(() => map.removeLayer(m), TTL_MS);
|
|
total += 1;
|
|
countsEl.textContent = `${total} events`;
|
|
return m;
|
|
}
|
|
|
|
// ISS : un seul marker persistant qui bouge
|
|
let issMarker = null;
|
|
function updateIss(lat, lon) {
|
|
const icon = L.divIcon({
|
|
className: "",
|
|
iconSize: [16, 16],
|
|
html: `<div class="iss" style="width:16px;height:16px"></div>`,
|
|
});
|
|
if (issMarker) issMarker.setLatLng([lat, lon]);
|
|
else issMarker = L.marker([lat, lon], { icon })
|
|
.addTo(map)
|
|
.bindTooltip("ISS", { permanent: true, direction: "top",
|
|
offset: [0, -8] });
|
|
}
|
|
|
|
// ------------------- Handlers ---------------------------------
|
|
const handlers = {
|
|
usgs: (m) => {
|
|
if (m.sub !== "event") return;
|
|
const [lat, lon, mag] = m.args;
|
|
ephemeralMarker(lat, lon, "quake", `M${mag.toFixed(1)} seisme`);
|
|
},
|
|
blitzortung: (m) => {
|
|
if (m.sub !== "strike") return;
|
|
const [lat, lon] = m.args;
|
|
ephemeralMarker(lat, lon, "strike", "foudre");
|
|
},
|
|
opensky: (m) => {
|
|
if (m.sub !== "state") return;
|
|
const [, , lat, lon] = m.args;
|
|
if (lat && lon) ephemeralMarker(lat, lon, "plane", "vol");
|
|
},
|
|
iss: (m) => {
|
|
if (m.sub === "pos") updateIss(m.args[0], m.args[1]);
|
|
},
|
|
volcano: (m) => {
|
|
if (m.sub !== "eruption") return;
|
|
const [lat, lon, vei, region] = m.args;
|
|
ephemeralMarker(lat, lon, "volcano",
|
|
`volcan ${region} (VEI ${vei})`);
|
|
},
|
|
};
|
|
|
|
function handleMessage(msg) {
|
|
const h = handlers[msg.feed];
|
|
if (h) h(msg);
|
|
}
|