perf: faster map boot — zoom-gate heavy layers, static basemap, defer HUD, lazy HLS
World view no longer fetches cameras/NWS/WFIGS. Default Blue Marble so init does not wait on GIBS times. News/summary/events load with their views. hls.min.js loads only on the first HLS camera popup.
This commit is contained in:
parent
dd7a09aadd
commit
07638288a9
1 changed files with 71 additions and 20 deletions
|
|
@ -1026,10 +1026,22 @@
|
||||||
<script src="/static/vendor/leaflet/leaflet.js"></script>
|
<script src="/static/vendor/leaflet/leaflet.js"></script>
|
||||||
<script src="/static/vendor/leaflet/leaflet.markercluster.js"></script>
|
<script src="/static/vendor/leaflet/leaflet.markercluster.js"></script>
|
||||||
<script src="/static/vendor/leaflet/leaflet.heat.js"></script>
|
<script src="/static/vendor/leaflet/leaflet.heat.js"></script>
|
||||||
<script src="/static/vendor/hls/hls.min.js"></script>
|
|
||||||
<script>
|
<script>
|
||||||
const API = '';
|
const API = '';
|
||||||
|
|
||||||
|
function loadHls() {
|
||||||
|
if (window.Hls) return Promise.resolve();
|
||||||
|
if (window._hlsLoading) return window._hlsLoading;
|
||||||
|
window._hlsLoading = new Promise((res, rej) => {
|
||||||
|
const s = document.createElement('script');
|
||||||
|
s.src = '/static/vendor/hls/hls.min.js';
|
||||||
|
s.onload = res;
|
||||||
|
s.onerror = rej;
|
||||||
|
document.head.appendChild(s);
|
||||||
|
});
|
||||||
|
return window._hlsLoading;
|
||||||
|
}
|
||||||
|
|
||||||
/* ═══════════════ BOOT SPLASH ═══════════════ */
|
/* ═══════════════ BOOT SPLASH ═══════════════ */
|
||||||
(function () {
|
(function () {
|
||||||
const boot = document.getElementById('boot');
|
const boot = document.getElementById('boot');
|
||||||
|
|
@ -1136,7 +1148,8 @@ function showView(name) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (name === 'news') loadNews(false);
|
if (name === 'news') loadNews(false);
|
||||||
if (name === 'settings') renderSysInfo();
|
if (name === 'events') { loadSummary(); loadEvents(); }
|
||||||
|
if (name === 'settings') { loadSummary(); renderSysInfo(); }
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1651,7 +1664,10 @@ async function initMap() {
|
||||||
const sel = document.getElementById('map-layer');
|
const sel = document.getElementById('map-layer');
|
||||||
sel.innerHTML = mapLayers.map(l =>
|
sel.innerHTML = mapLayers.map(l =>
|
||||||
`<option value="${l.id}">${l.title}</option>`).join('');
|
`<option value="${l.id}">${l.title}</option>`).join('');
|
||||||
const preferred = mapLayers.find(l => l.has_time) || mapLayers[0];
|
const preferred =
|
||||||
|
mapLayers.find(l => l.id === 'BlueMarble_ShadedRelief_Bathymetry')
|
||||||
|
|| mapLayers.find(l => !l.has_time)
|
||||||
|
|| mapLayers[0];
|
||||||
if (preferred) sel.value = preferred.id;
|
if (preferred) sel.value = preferred.id;
|
||||||
map = L.map('map', {
|
map = L.map('map', {
|
||||||
center: [25, 10], zoom: 2,
|
center: [25, 10], zoom: 2,
|
||||||
|
|
@ -1676,20 +1692,24 @@ async function initMap() {
|
||||||
img.alt = 'camera preview';
|
img.alt = 'camera preview';
|
||||||
v.replaceWith(img);
|
v.replaceWith(img);
|
||||||
};
|
};
|
||||||
if (window.Hls && Hls.isSupported()) {
|
const startHls = () => {
|
||||||
activeHls = new Hls({ enableWorker: true, lowLatencyMode: true });
|
if (window.Hls && Hls.isSupported()) {
|
||||||
activeHls.loadSource(v.dataset.hls);
|
activeHls = new Hls({ enableWorker: true, lowLatencyMode: true });
|
||||||
activeHls.attachMedia(v);
|
activeHls.loadSource(v.dataset.hls);
|
||||||
v.play && v.play().catch(() => {});
|
activeHls.attachMedia(v);
|
||||||
activeHls.on(Hls.Events.ERROR, (_, data) => {
|
v.play && v.play().catch(() => {});
|
||||||
if (data && data.fatal) fallback();
|
activeHls.on(Hls.Events.ERROR, (_, data) => {
|
||||||
});
|
if (data && data.fatal) fallback();
|
||||||
} else if (v.canPlayType && v.canPlayType('application/vnd.apple.mpegurl')) {
|
});
|
||||||
v.src = v.dataset.hls;
|
} else if (v.canPlayType && v.canPlayType('application/vnd.apple.mpegurl')) {
|
||||||
v.addEventListener('error', fallback, { once: true });
|
v.src = v.dataset.hls;
|
||||||
} else {
|
v.addEventListener('error', fallback, { once: true });
|
||||||
fallback();
|
} else {
|
||||||
}
|
fallback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (window.Hls) startHls();
|
||||||
|
else loadHls().then(startHls).catch(fallback);
|
||||||
});
|
});
|
||||||
map.on('popupclose', () => {
|
map.on('popupclose', () => {
|
||||||
if (activeHls) { try { activeHls.destroy(); } catch (_) {} activeHls = null; }
|
if (activeHls) { try { activeHls.destroy(); } catch (_) {} activeHls = null; }
|
||||||
|
|
@ -1719,7 +1739,8 @@ async function initMap() {
|
||||||
refreshLiveOverlays();
|
refreshLiveOverlays();
|
||||||
}, 300);
|
}, 300);
|
||||||
});
|
});
|
||||||
await mapLayerChanged();
|
// Static Blue Marble has no time-domain fetch — don't block overlays on GIBS.
|
||||||
|
mapLayerChanged();
|
||||||
// Overlays default ON (checkboxes in the layer panel + saved settings)
|
// Overlays default ON (checkboxes in the layer panel + saved settings)
|
||||||
applyMapSettings(readMapPrefs(), false);
|
applyMapSettings(readMapPrefs(), false);
|
||||||
firesOn = document.getElementById('lp-fires-on').checked;
|
firesOn = document.getElementById('lp-fires-on').checked;
|
||||||
|
|
@ -2036,6 +2057,13 @@ function camThumb(c) {
|
||||||
}
|
}
|
||||||
async function loadCams() {
|
async function loadCams() {
|
||||||
if (!map) return;
|
if (!map) return;
|
||||||
|
if (tooZoomedOut()) {
|
||||||
|
camsGroup = dropLayer(camsGroup);
|
||||||
|
markZoom('lp-cams-count');
|
||||||
|
hudCamsCount = null;
|
||||||
|
syncHud();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const req = ++camReq;
|
const req = ++camReq;
|
||||||
try {
|
try {
|
||||||
const r = await fetch(`${API}/api/cameras?bbox=${currentBBox()}&limit=5000`);
|
const r = await fetch(`${API}/api/cameras?bbox=${currentBBox()}&limit=5000`);
|
||||||
|
|
@ -2175,6 +2203,14 @@ function dropLayer(ref) {
|
||||||
if (ref && map && map.hasLayer(ref)) map.removeLayer(ref);
|
if (ref && map && map.hasLayer(ref)) map.removeLayer(ref);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
/* Polygon/camera layers hitch at world scale. Same idea as aircraft's zoom<=3
|
||||||
|
skip: do not fetch or render these until the user is zoomed in. */
|
||||||
|
const HEAVY_MIN_ZOOM = 4;
|
||||||
|
function tooZoomedOut() { return !map || map.getZoom() <= HEAVY_MIN_ZOOM; }
|
||||||
|
function markZoom(id) {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
if (el) el.textContent = 'zoom';
|
||||||
|
}
|
||||||
function intersectsConus() {
|
function intersectsConus() {
|
||||||
if (!map) return false;
|
if (!map) return false;
|
||||||
const b = map.getBounds();
|
const b = map.getBounds();
|
||||||
|
|
@ -2338,6 +2374,11 @@ async function toggleWxAlerts() {
|
||||||
}
|
}
|
||||||
async function loadWxAlerts() {
|
async function loadWxAlerts() {
|
||||||
if (!map) return;
|
if (!map) return;
|
||||||
|
if (tooZoomedOut()) {
|
||||||
|
wxAlertsGroup = dropLayer(wxAlertsGroup);
|
||||||
|
markZoom('lp-alerts-count');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const req = ++overlayReq.alerts;
|
const req = ++overlayReq.alerts;
|
||||||
try {
|
try {
|
||||||
const r = await fetch(`${API}/api/weather-alerts?bbox=${currentBBox()}`);
|
const r = await fetch(`${API}/api/weather-alerts?bbox=${currentBBox()}`);
|
||||||
|
|
@ -2369,6 +2410,11 @@ async function togglePerimeters() {
|
||||||
}
|
}
|
||||||
async function loadPerimeters() {
|
async function loadPerimeters() {
|
||||||
if (!map) return;
|
if (!map) return;
|
||||||
|
if (tooZoomedOut()) {
|
||||||
|
perimGroup = dropLayer(perimGroup);
|
||||||
|
markZoom('lp-perim-count');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const req = ++overlayReq.perim;
|
const req = ++overlayReq.perim;
|
||||||
try {
|
try {
|
||||||
const r = await fetch(`${API}/api/fire-perimeters?bbox=${currentBBox()}`);
|
const r = await fetch(`${API}/api/fire-perimeters?bbox=${currentBBox()}`);
|
||||||
|
|
@ -2402,6 +2448,11 @@ async function toggleIncidents() {
|
||||||
}
|
}
|
||||||
async function loadIncidents() {
|
async function loadIncidents() {
|
||||||
if (!map) return;
|
if (!map) return;
|
||||||
|
if (tooZoomedOut()) {
|
||||||
|
incidentsGroup = dropLayer(incidentsGroup);
|
||||||
|
markZoom('lp-incidents-count');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const req = ++overlayReq.incidents;
|
const req = ++overlayReq.incidents;
|
||||||
try {
|
try {
|
||||||
const r = await fetch(`${API}/api/fire-incidents?bbox=${currentBBox()}`);
|
const r = await fetch(`${API}/api/fire-incidents?bbox=${currentBBox()}`);
|
||||||
|
|
@ -2511,9 +2562,9 @@ async function loadStorms() {
|
||||||
initNav();
|
initNav();
|
||||||
initSettings();
|
initSettings();
|
||||||
initMarketTicker();
|
initMarketTicker();
|
||||||
loadSummary(); loadEvents(); loadNews(true); checkHealth();
|
checkHealth();
|
||||||
setInterval(() => { loadSummary(); loadEvents(); checkHealth(); }, 30000);
|
|
||||||
initMap();
|
initMap();
|
||||||
|
setInterval(checkHealth, 30000);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue