All People Groups: no DOXA group is drawn twice; the base is re-sent slim without them
The feature-state approach left an invisible twin per DOXA group (2,616 circles drawn at alpha 0, and counted by queryRenderedFeatures). When the DOXA set arrives the base is re-sent once, slim (1.25 MB, not the old 3.7 MB), without the DOXA rows; the black pins stay on their own top layer. Popup fields and counts unchanged.
This commit is contained in:
parent
fd118fa2b5
commit
cf962e4065
1 changed files with 12 additions and 15 deletions
|
|
@ -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')
|
||||
|
|
|
|||
Loading…
Reference in a new issue