From 5681f9db2227fc3d00b573f1c478330306ba6a23 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Mon, 28 Sep 2026 21:00:15 +0000 Subject: [PATCH] Fit against the box the chart actually has, and make the cross read as one Raising the padding fixed seven layouts and left the eighth exactly as it was, which was the clue: padding cannot rescue a fit aimed at a box of the wrong shape. That layout is short and wide, and the fit ran against a stale measurement, so it resolved to the width and clipped the cloud top and bottom. The engine is now told to re-read its container before the fit, the fit runs on the next frame so the flex layout can finish, and it runs once more when the map settles for a container still resolving on that frame. None of it depends on a timer. The cross was drawing as a plus. The upper arm was two thirds of the lower, which is a real difference and far too small to see at this size. Latin proportions now: the lower arm is three times the upper and the bar is narrower than the figure is tall, so the shape is legible as what it is at the size it is actually drawn rather than only in the geometry. --- .../src/components/GiBoundedSetMap.vue | 33 +++++++++++-------- .../src/components/GiCenteredSetMap.vue | 33 +++++++++++-------- .../gospel-internet/src/setChartFeatures.js | 9 +++-- public/dx-maps/doxa-maps-spa/manifest.json | 4 +-- .../gospel-internet/board/board/index.html | 2 +- .../dx-maps/gospel-internet/board/index.html | 2 +- .../board/india-china/index.html | 2 +- .../board/language-tree/index.html | 2 +- .../board/platforms/index.html | 2 +- .../board/project-700/index.html | 2 +- .../gospel-internet/board/zero/index.html | 2 +- .../gospel-internet/gospel-internet.js | 26 +++++++-------- public/dx-maps/manifest.json | 4 +-- 13 files changed, 71 insertions(+), 52 deletions(-) diff --git a/plugin-designs/gospel-internet/src/components/GiBoundedSetMap.vue b/plugin-designs/gospel-internet/src/components/GiBoundedSetMap.vue index d2527cc5b..54ae341af 100644 --- a/plugin-designs/gospel-internet/src/components/GiBoundedSetMap.vue +++ b/plugin-designs/gospel-internet/src/components/GiBoundedSetMap.vue @@ -102,19 +102,26 @@ onMounted(async () => { console.warn('[gospel-internet] the centre cross could not be drawn:', e && e.message || e) } - // ★ FIT THE WHOLE TARGET ON LOAD. The default zoom clipped the outer rings top and bottom — - // the reader met a drawing already cut off, which reads as a rendering fault rather than as - // something to zoom out of. Fitted from the projected extent, so it follows the chart's own - // size rather than a guessed zoom. - try { - const [w, sLat] = project(-SPREAD, -SPREAD) - const [e, nLat] = project(SPREAD, SPREAD) - // Padding is generous and symmetric because the fit must hold the LABELS too, not just the - // points: at 1280 the outermost band label sat on the canvas edge. And a short, wide canvas - // (the bounded chart in a narrow column is 796x249) fits to WIDTH and clips top and bottom - // unless the bounds are given room in the dimension that is scarce. - map.fitBounds([[w, sLat], [e, nLat]], { padding: 40, duration: 0 }) - } catch (_) {} + // ★ FIT AFTER THE BOX HAS SETTLED, AND RESIZE FIRST. The previous version fitted + // immediately: on one narrow layout the canvas was still 796x249 from a stale measurement, + // so the fit was computed against the wrong aspect, resolved to the WIDTH, and clipped the + // cloud top and bottom. Raising the padding did nothing there, because padding cannot fix a + // fit that was aimed at a box of the wrong shape — which is why that one layout was + // unchanged while the others improved. + // map.resize() makes the engine re-read the container; fitting on the next frame lets the + // flex layout finish first. Both are cheap and neither depends on a timer. + const fitTarget = () => { + try { + map.resize() + const [w, sLat] = project(-SPREAD, -SPREAD) + const [e, nLat] = project(SPREAD, SPREAD) + map.fitBounds([[w, sLat], [e, nLat]], { padding: 40, duration: 0 }) + } catch (_) { /* a fit that cannot be computed leaves the default view, which still draws */ } + } + if (typeof requestAnimationFrame === 'function') requestAnimationFrame(fitTarget) + else fitTarget() + // and again once the engine settles, for a container that was still resolving on that frame + try { map.once('idle', fitTarget) } catch (_) {} } // ★ CALLED UNCONDITIONALLY, AND THE GUARD THAT USED TO BE HERE IS WHY THIS WAS BROKEN TWICE. // createBoardMap only resolves after `await map.once('load')`, so by here the event HAS fired. diff --git a/plugin-designs/gospel-internet/src/components/GiCenteredSetMap.vue b/plugin-designs/gospel-internet/src/components/GiCenteredSetMap.vue index de0f28477..aa2c669e8 100644 --- a/plugin-designs/gospel-internet/src/components/GiCenteredSetMap.vue +++ b/plugin-designs/gospel-internet/src/components/GiCenteredSetMap.vue @@ -117,19 +117,26 @@ onMounted(async () => { console.warn('[gospel-internet] the centre cross could not be drawn:', e && e.message || e) } - // ★ FIT THE WHOLE TARGET ON LOAD. The default zoom clipped the outer rings top and bottom — - // the reader met a drawing already cut off, which reads as a rendering fault rather than as - // something to zoom out of. Fitted from the projected extent, so it follows the chart's own - // size rather than a guessed zoom. - try { - const [w, sLat] = project(-EXTENT, -EXTENT) - const [e, nLat] = project(EXTENT, EXTENT) - // Padding is generous and symmetric because the fit must hold the LABELS too, not just the - // points: at 1280 the outermost band label sat on the canvas edge. And a short, wide canvas - // (the bounded chart in a narrow column is 796x249) fits to WIDTH and clips top and bottom - // unless the bounds are given room in the dimension that is scarce. - map.fitBounds([[w, sLat], [e, nLat]], { padding: 40, duration: 0 }) - } catch (_) {} + // ★ FIT AFTER THE BOX HAS SETTLED, AND RESIZE FIRST. The previous version fitted + // immediately: on one narrow layout the canvas was still 796x249 from a stale measurement, + // so the fit was computed against the wrong aspect, resolved to the WIDTH, and clipped the + // cloud top and bottom. Raising the padding did nothing there, because padding cannot fix a + // fit that was aimed at a box of the wrong shape — which is why that one layout was + // unchanged while the others improved. + // map.resize() makes the engine re-read the container; fitting on the next frame lets the + // flex layout finish first. Both are cheap and neither depends on a timer. + const fitTarget = () => { + try { + map.resize() + const [w, sLat] = project(-EXTENT, -EXTENT) + const [e, nLat] = project(EXTENT, EXTENT) + map.fitBounds([[w, sLat], [e, nLat]], { padding: 40, duration: 0 }) + } catch (_) { /* a fit that cannot be computed leaves the default view, which still draws */ } + } + if (typeof requestAnimationFrame === 'function') requestAnimationFrame(fitTarget) + else fitTarget() + // and again once the engine settles, for a container that was still resolving on that frame + try { map.once('idle', fitTarget) } catch (_) {} ready.value = true } // ★ CALLED UNCONDITIONALLY, AND THE GUARD THAT USED TO BE HERE IS WHY THIS WAS BROKEN TWICE. diff --git a/plugin-designs/gospel-internet/src/setChartFeatures.js b/plugin-designs/gospel-internet/src/setChartFeatures.js index 87e9b0c95..fa78c6f91 100644 --- a/plugin-designs/gospel-internet/src/setChartFeatures.js +++ b/plugin-designs/gospel-internet/src/setChartFeatures.js @@ -157,8 +157,13 @@ export function degreesToPixelRadius(degrees, { minZoom = 0, maxZoom = 22 } = {} * else here. */ export function crossFeature(project, { arm = 13, cx = 0, cy = 0 } = {}) { - const vert = [[cx, cy - arm], [cx, cy + arm]].map(([u, v]) => project(u, v)) - const horiz = [[cx - arm * 0.7, cy - arm * 0.35], [cx + arm * 0.7, cy - arm * 0.35]].map(([u, v]) => project(u, v)) + // Latin proportions, not a plus. Reported as reading "+ with equal arms": the upper arm was + // 0.65 of the lower, which is a real difference and too small to see at this size. The upper + // arm is now a third of the lower and the bar is narrower than the height, so the shape is + // legible as a cross at the size it is actually drawn. + const upper = arm * 0.55, lower = arm * 1.65, bar = arm * 0.62 + const vert = [[cx, cy - upper], [cx, cy + lower]].map(([u, v]) => project(u, v)) + const horiz = [[cx - bar, cy - upper * 0.05], [cx + bar, cy - upper * 0.05]].map(([u, v]) => project(u, v)) return { type: 'FeatureCollection', features: [ diff --git a/public/dx-maps/doxa-maps-spa/manifest.json b/public/dx-maps/doxa-maps-spa/manifest.json index c410f8d9b..0394f00bd 100644 --- a/public/dx-maps/doxa-maps-spa/manifest.json +++ b/public/dx-maps/doxa-maps-spa/manifest.json @@ -615,7 +615,7 @@ "bundle": "gospel-internet", "scriptUrl": "./gospel-internet/gospel-internet.js", "output": "dx-maps/gospel-internet/gospel-internet.js", - "integrity": "sha384-ms4BzXmzlKrMiucFY6RR1kcxpNI1KEllXaqE6K4JtxWe0I6VkpkFcf7GulVybnrr", + "integrity": "sha384-6ypksiuOg1UYnG8SJOD9nFoDxMeisCv7Z8ZxvQtGI8E0Yonjw1IkXPHOI2hoHVTn", "elements": [ "gospel-internet-board" ], @@ -635,7 +635,7 @@ "scriptUrl": "./gospel-internet/gospel-internet.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n" }, "instances": [ { diff --git a/public/dx-maps/gospel-internet/board/board/index.html b/public/dx-maps/gospel-internet/board/board/index.html index 27b8f8a7f..bfa643029 100644 --- a/public/dx-maps/gospel-internet/board/board/index.html +++ b/public/dx-maps/gospel-internet/board/board/index.html @@ -36,7 +36,7 @@ - + - + - + - + - + - + - + \n\n\n" + "script": "\n\n\n\n\n" }, "instances": [ {