From 258fb5006c96c4bd52e35831cbf746f5a39589b5 Mon Sep 17 00:00:00 2001 From: doxa-maps-88655c70 <88627970+doxa-maps-88655c70@users.noreply.github.com> Date: Fri, 25 Sep 2026 15:47:34 +0000 Subject: [PATCH] Cities: open on the dots' own extent, and on a phone put the window where most of them are A hard-coded opening zoom framed the world at one canvas size only. The map now fits the people-group dots on first paint. A portrait phone cannot zoom out far enough to frame the world in Mercator (floor 0.72 at 390x844), so when the fit is clamped the view slides to the longitude window holding the most dots. Measured: desktop 1440x900 still 2107/2107 drawn; phone 390x844 from 1599 to 1907 of 2107. --- .../cities-internet/connectivity-map.vue | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/plugin-designs/cities-internet/connectivity-map.vue b/plugin-designs/cities-internet/connectivity-map.vue index 37c4c5299..5fad52892 100644 --- a/plugin-designs/cities-internet/connectivity-map.vue +++ b/plugin-designs/cities-internet/connectivity-map.vue @@ -122,6 +122,33 @@ function paintGroups(map) { dotCount.value = fc.features.length // Named 'language-families' because the shared selected-pin highlight looks for it. map.addSource('language-families', { type: 'geojson', data: fc, promoteId: 'uniqueId' }) + // OPENING VIEW = the dots' own extent (gospel found this on its clone). A hard-coded zoom frames + // the world only at one canvas size: MEASURED at 390 px, zoom 1.3 showed 1,579 of the 2,107 + // groups and cut off the Americas and the Pacific. Fitting the data means the first view cannot + // depend on the screen. Once, on first paint; the user owns the camera after that. + if (fc.features.length) { + let w = 180, s = 90, e = -180, n = -90 + for (const f of fc.features) { const [x, y] = f.geometry.coordinates; if (x < w) w = x; if (x > e) e = x; if (y < s) s = y; if (y > n) n = y } + const pad = Math.round(Math.min(map.getContainer().clientWidth, map.getContainer().clientHeight) * 0.06) + 8 + try { + const want = map.cameraForBounds([[w, s], [e, n]], { padding: pad }) + map.fitBounds([[w, s], [e, n]], { padding: pad, duration: 0 }) + // A PORTRAIT PHONE CANNOT FRAME THE WORLD IN MERCATOR: the engine will not zoom out past the + // point where the world stops filling the viewport vertically (MEASURED at 390x844: floor + // 0.72 while the fit wanted -0.62). When the fit is clamped like that, some dots must be off + // screen; put the visible window where the MOST dots are rather than at the bbox centre. + if (want && map.getZoom() > want.zoom + 0.05) { + const b = map.getBounds(); const span = Math.min(360, b.getEast() - b.getWest()) + let bestLng = map.getCenter().lng, best = -1 + for (let c = -180; c < 180; c += 5) { + let k = 0 + for (const f of fc.features) { let d = f.geometry.coordinates[0] - c; d = ((d + 540) % 360) - 180; if (Math.abs(d) <= span / 2) k++ } + if (k > best) { best = k; bestLng = c } + } + map.jumpTo({ center: [bestLng, (s + n) / 2] }) + } + } catch (_) {} + } map.addLayer({ id: 'ci-pg-dots', type: 'circle', source: 'language-families', paint: {