From 0de6a7c2f869cd1d822cc5968b67852005c763e7 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Fri, 25 Sep 2026 23:24:26 +0000 Subject: [PATCH] PPLR data maps: keep the loading globe up until the pins are drawn, not merely added Six views set dataReady (which removes the LoadingGlobe) straight after addSource(). The engine then still tiles tens of thousands of points in its worker, so the globe vanished and the map sat empty with a full legend. Measured on the DX-life gallery: 0 features for 20 s, then 19,376. whenGeojsonDrawn(map) resolves once every GeoJSON source is loaded AND a frame has rendered (not 'idle', which an animating map may never reach), with a 45 s cap so it can never hang. Religion, pins, coverage, translations, language families and Rosetta cards await it before dataReady. Plugin-local: pplrData.js has no consumer outside pplr-data-maps. Headless proof: a fake-map unit test (already loaded / last source late / never loads -> false at the cap / style mid-swap / no map), 9/9 pass; BUNDLE=pplr-data-maps build + tree + check:sri PASS. --- .../pplr-data-maps/src/utils/pplrData.js | 23 +++++++++++++++++++ .../src/views/pplr-coverage.vue | 4 +++- .../src/views/pplr-language-families.vue | 4 +++- .../pplr-data-maps/src/views/pplr-pins.vue | 4 +++- .../src/views/pplr-religion.vue | 4 +++- .../src/views/pplr-rosetta-cards.vue | 4 +++- .../src/views/pplr-translations.vue | 4 +++- 7 files changed, 41 insertions(+), 6 deletions(-) diff --git a/plugin-designs/pplr-data-maps/src/utils/pplrData.js b/plugin-designs/pplr-data-maps/src/utils/pplrData.js index d8502ee6e..93ca5116c 100644 --- a/plugin-designs/pplr-data-maps/src/utils/pplrData.js +++ b/plugin-designs/pplr-data-maps/src/utils/pplrData.js @@ -63,6 +63,29 @@ export function ensureGeocoder(provider = 'mapbox') { // `new mapboxgl.Popup` throws on the first pin click. Now the library's engineFor (one copy). export { engineFor as glFor } from '@map/composables/useMapProvider.js' +// Resolves once every GeoJSON source on the map has LOADED and a frame has RENDERED after that, +// or after `timeoutMs` (a safety net, never a hang). The views flipped `dataReady` (which hides the +// LoadingGlobe) right after addSource(); the engine then still tiles tens of thousands of points in +// its worker, so the globe disappeared and the map sat EMPTY. Measured on the DX-life gallery: 0 +// dots for 20 s, then 19,376 features from 30 s. Not 'idle': an animating map may never go idle. +export function whenGeojsonDrawn(map, timeoutMs = 45000) { + return new Promise((resolve) => { + if (!map) return resolve(false) + let done = false + const finish = (drawn) => { if (done) return; done = true; clearTimeout(timer); map.off('sourcedata', check); resolve(drawn) } + const timer = setTimeout(() => finish(false), timeoutMs) + const check = () => { + try { + const srcs = (map.getStyle() || {}).sources || {} + const ids = Object.keys(srcs).filter((k) => srcs[k].type === 'geojson') + if (ids.every((id) => map.isSourceLoaded(id))) { map.once('render', () => finish(true)); map.triggerRepaint() } + } catch (_) { /* style mid-swap: wait for the next sourcedata */ } + } + map.on('sourcedata', check) + check() + }) +} + // Verification handle (the gospel-resources __grState contract): what the ACTIVE view's map // actually did, so a check can assert "loaded" rather than inferring it from missing requests. // window.__pplrMap() -> { engine, loaded, styleLayers, pplrLayers } diff --git a/plugin-designs/pplr-data-maps/src/views/pplr-coverage.vue b/plugin-designs/pplr-data-maps/src/views/pplr-coverage.vue index 7ace113fc..e6f6287ed 100644 --- a/plugin-designs/pplr-data-maps/src/views/pplr-coverage.vue +++ b/plugin-designs/pplr-data-maps/src/views/pplr-coverage.vue @@ -1,5 +1,5 @@