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.
This commit is contained in:
doxa-maps-88655c70 2026-09-25 15:47:34 +00:00
parent 2624cf8ef5
commit 258fb5006c

View file

@ -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: {