UPG Neighbor: Campaigns shows its languages on the map before one is picked
One bubble per bridge language, on its largest unreached group and sized by the unreached population a campaign in it reaches; it follows the search and the believers filter, names itself on hover and picks the language on a click. The map was empty until a language was chosen.
This commit is contained in:
parent
cbe9158d60
commit
667eb5b91c
1 changed files with 23 additions and 2 deletions
|
|
@ -81,7 +81,14 @@ function exportAll() {
|
|||
}
|
||||
|
||||
// ─── Map: believers (green) and the unreached they would reach (red) ─────────
|
||||
const SRC_R = 'cp-r', SRC_U = 'cp-u'
|
||||
// Before a language is picked, the map shows the list itself: one bubble per bridge language, placed on
|
||||
// its largest unreached group and sized by the unreached population it reaches. A click picks it.
|
||||
const SRC_R = 'cp-r', SRC_U = 'cp-u', SRC_B = 'cp-b'
|
||||
const anchor = (b) => b.unreached.reduce((m, g) => (!m || g.pop > m.pop ? g : m), null)
|
||||
function bridgeFeatures() {
|
||||
if (active.value) return fc([])
|
||||
return fc(list.value.map((b) => { const g = anchor(b); return g && { type: 'Feature', geometry: { type: 'Point', coordinates: [g.lng, g.lat] }, properties: { lang: b.lang, pop: b.unreachedPop } } }).filter(Boolean))
|
||||
}
|
||||
const fc = (features) => ({ type: 'FeatureCollection', features })
|
||||
// Hover label: after a language is picked, its believers (green) and the unreached they reach (red)
|
||||
// were anonymous until clicked, and a green dot could not be clicked at all. Same label as Mobilize.
|
||||
|
|
@ -96,8 +103,12 @@ function repaint() {
|
|||
// be dropped for good: the start view showed no dots at all (rega, upgn6). Try again once idle.
|
||||
if (!m.isStyleLoaded?.()) { if (!repaintPending) { repaintPending = true; m.once('idle', () => { repaintPending = false; repaint() }) } return }
|
||||
const b = active.value
|
||||
setSrc(m, SRC_B, bridgeFeatures())
|
||||
setSrc(m, SRC_U, fc(b ? b.unreached.map(pt) : []))
|
||||
setSrc(m, SRC_R, fc(b ? b.reached.map(pt) : []))
|
||||
if (!m.getLayer('cp-b')) m.addLayer({ id: 'cp-b', type: 'circle', source: SRC_B, paint: {
|
||||
'circle-color': UNREACHED, 'circle-opacity': 0.45, 'circle-stroke-width': 1.5, 'circle-stroke-color': E_COLORS[1],
|
||||
'circle-radius': ['interpolate', ['linear'], ['ln', ['max', ['get', 'pop'], 1]], 8, 3, 12, 6, 16, 12, 19, 18] } })
|
||||
if (!m.getLayer('cp-u')) m.addLayer({ id: 'cp-u', type: 'circle', source: SRC_U, paint: {
|
||||
'circle-color': UNREACHED, 'circle-opacity': 0.7, 'circle-stroke-width': 0.6, 'circle-stroke-color': '#fff',
|
||||
'circle-radius': ['interpolate', ['linear'], ['ln', ['max', ['get', 'pop'], 1]], 6, 2, 12, 4, 16, 8, 19, 12] } })
|
||||
|
|
@ -121,6 +132,7 @@ function frame() {
|
|||
} catch (_) { /* a camera call on a map mid-teardown is not worth an error */ }
|
||||
}
|
||||
watch(() => [nbState.lang, data.value], () => { repaint(); frame() })
|
||||
watch(list, () => repaint()) // the bubbles follow the search and the believers filter
|
||||
|
||||
onMounted(async () => {
|
||||
loadNearData(profileConfig).then((d) => { data.value = d; repaint(); frame() })
|
||||
|
|
@ -143,10 +155,19 @@ onMounted(async () => {
|
|||
const r = active.value?.reached.find((g) => g.pgid === id)
|
||||
if (r) openGroup(r)
|
||||
})
|
||||
for (const id of ['cp-u', 'cp-r']) {
|
||||
m.on('click', 'cp-b', (ev) => {
|
||||
const b = data.value?.bridges.find((x) => x.lang === ev.features?.[0]?.properties?.lang)
|
||||
if (b) pick(b)
|
||||
})
|
||||
for (const id of ['cp-u', 'cp-r', 'cp-b']) {
|
||||
m.on('mouseenter', id, () => { m.getCanvas().style.cursor = 'pointer' })
|
||||
m.on('mouseleave', id, () => { m.getCanvas().style.cursor = '' })
|
||||
}
|
||||
m.on('mousemove', 'cp-b', (ev) => {
|
||||
const b = data.value?.bridges.find((x) => x.lang === ev.features?.[0]?.properties?.lang)
|
||||
hover.value = b ? { x: ev.point.x, y: ev.point.y, text: `${b.langName} · ${nt('neighbor.camp.unreachedPeople', { n: fmtNum(b.unreachedPop) })}` } : null
|
||||
})
|
||||
m.on('mouseleave', 'cp-b', () => { hover.value = null })
|
||||
const byId = (id) => active.value && [...active.value.reached, ...active.value.unreached].find((g) => g.pgid === id)
|
||||
for (const id of ['cp-u', 'cp-r']) {
|
||||
m.on('mousemove', id, (ev) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue