diff --git a/plugin-designs/all-people-groups/all-people-groups.vue b/plugin-designs/all-people-groups/all-people-groups.vue index 29b49fc21..3cfa097f5 100644 --- a/plugin-designs/all-people-groups/all-people-groups.vue +++ b/plugin-designs/all-people-groups/all-people-groups.vue @@ -193,8 +193,8 @@ const mapData = useMapData({ // row's index) and one category number. Every name, country, language and religion used to ride on // all 12,379 features, 3.7 MB of GeoJSON built and re-tiled TWICE (first paint, then the DOXA recolour); // slim, it is 1.25 MB, built once. The popup reads the row by id. The DOXA recolour no longer rebuilds -// the 12,379: DOXA pins are a small source of their own drawn on top, and their base duplicates are -// hidden by feature-state. +// the 12,379 with all their fields: DOXA pins are a small source of their own drawn on top, and the base +// is re-sent once, slim, WITHOUT them (an invisible twin per DOXA group still cost a draw: rega measured). let rows = [] // the decoded rows; a feature's id is its index here let doxaIds = new Set() // row indexes in the DOXA set (empty until the prayer API answers) let doxaBySlugRop = new Map() // rop3 → { slug, name } for the pray link @@ -240,7 +240,7 @@ const slimPoint = (i, c) => ({ type: 'Feature', id: i, geometry: { type: 'Point' // Every pin by GSEC, from the file alone: built ONCE. The DOXA overlay (applyDoxa) comes later and never blocks. function buildBase() { const feats = [] - for (let i = 0; i < rows.length; i++) if (placed(rows[i])) feats.push(slimPoint(i, GSEC_CAT.indexOf(catOf(i)))) + for (let i = 0; i < rows.length; i++) if (placed(rows[i]) && !doxaIds.has(i)) feats.push(slimPoint(i, GSEC_CAT.indexOf(catOf(i)))) recount() return { type: 'FeatureCollection', features: feats } } @@ -249,17 +249,16 @@ function recount() { for (let i = 0; i < rows.length; i++) if (placed(rows[i])) c[catOf(i)]++ counts.value = c } -// The DOXA set arrived: its pins go on the top layer (a ~2,100-point source) and their base duplicates -// are hidden by feature-state. The 12,379-feature source is never rebuilt. +// The DOXA set arrived: its pins go on the top layer (a ~2,100-point source) and the base is re-sent, +// slim, without them, so no DOXA group is drawn twice. function applyDoxa(doxaSet) { const m = map.value if (!m || !m.getSource('apg')) return const ids = new Set() for (let i = 0; i < rows.length; i++) if (placed(rows[i]) && doxaSet.has(String(rows[i].rop3 || ''))) ids.add(i) doxaIds = ids - for (const i of ids) m.setFeatureState({ source: 'apg', id: i }, { doxa: true }) + m.getSource('apg').setData(buildBase()) m.getSource('apg-doxa').setData({ type: 'FeatureCollection', features: [...ids].map((i) => slimPoint(i, 0)) }) - recount() } // The live DOXA set (rop3 = peoplegroups.org ROP3Code). Resolves to an empty set on failure. @@ -284,8 +283,6 @@ async function loadDoxaSet() { // Pin size and ring, shared by the base and the DOXA layer. const PIN_RADIUS = ['interpolate', ['linear'], ['zoom'], 0, 2.2, 4, 3.4, 8, 6, 12, 9] const PIN_STROKE_WIDTH = ['interpolate', ['linear'], ['zoom'], 0, 0.3, 6, 0.6, 12, 1] -// A base pin whose group is DOXA is hidden: its black twin is drawn on the layer above. -const HIDDEN_IF_DOXA = (v) => ['case', ['boolean', ['feature-state', 'doxa'], false], 0, v] function addLayer(fc) { const m = map.value @@ -312,9 +309,9 @@ function addLayer(fc) { // green legible on near-white (a white ring was right only on the old dark map). 'circle-stroke-color': '#1f2937', 'circle-stroke-width': PIN_STROKE_WIDTH, - 'circle-stroke-opacity': HIDDEN_IF_DOXA(0.55), + 'circle-stroke-opacity': 0.55, 'circle-radius': PIN_RADIUS, - 'circle-opacity': HIDDEN_IF_DOXA(0.92), + 'circle-opacity': 0.92, }, }) // DOXA: the assignment, black, above everything else. @@ -330,10 +327,10 @@ function addLayer(fc) { m.on('mouseenter', id, () => { m.getCanvas().style.cursor = 'pointer' }) m.on('mouseleave', id, () => { m.getCanvas().style.cursor = '' }) } - // ONE click handler: a DOXA pin (top layer) wins over its hidden base twin at the same point. + // ONE click handler, top layer first: a DOXA pin wins over a base pin under it. m.on('click', (e) => { const hits = m.queryRenderedFeatures(e.point, { layers: ['apg-doxa-pins', 'apg-pins'].filter((id) => m.getLayer(id)) }) - const f = hits.find((h) => h.layer.id === 'apg-doxa-pins' || !doxaIds.has(Number(h.id))) + const f = hits[0] if (!f || rows[Number(f.id)] == null) return const i = Number(f.id), row = rows[i] const category = catOf(i) @@ -373,8 +370,8 @@ function selectCat(id) { return } activeCat.value = id - // DOXA lives on its own layer; the other three are the base layer's category numbers (a DOXA group's - // base twin stays hidden by feature-state, so a GSEC filter never shows it twice). + // DOXA lives on its own layer; the other three are the base layer's category numbers (the base holds + // no DOXA group, so a GSEC filter never shows one twice). if (id === 'doxa') m.setFilter('apg-pins', ['==', ['get', 'c'], -1]) else m.setFilter('apg-pins', ['==', ['get', 'c'], GSEC_CAT.indexOf(id)]) m.setLayoutProperty('apg-doxa-pins', 'visibility', id === 'doxa' ? 'visible' : 'none')