42 lines
1.9 KiB
JavaScript
42 lines
1.9 KiB
JavaScript
/**
|
|
* colocation.js — which people groups share ONE coordinate on a map (APPROXPIN, 2026-09-26).
|
|
*
|
|
* A people-group pin is a representative point, not an address, and some pins ARE THE SAME POINT:
|
|
* measured on the Doxa roster (gospel-plugin): 2,107 groups on 2,048 distinct coordinates, 108 groups on
|
|
* a point shared with another (41 pairs, 6 triples, 2 quadruples). Saying "approximate" hides that; naming
|
|
* the co-located groups is the only version a reader can check. The index is built from THE ROSTER THE MAP
|
|
* DRAWS (every map's roster differs), never hardcoded.
|
|
*/
|
|
const KEY = (lat, lng) => `${Number(lat).toFixed(6)},${Number(lng).toFixed(6)}`
|
|
|
|
/** Read [lat, lng] from a roster row or a GeoJSON feature; null when it has no usable position. */
|
|
export function pointOf(r) {
|
|
if (!r) return null
|
|
const c = r.geometry && r.geometry.coordinates
|
|
const p = r.properties || r
|
|
const lat = c ? c[1] : (p.latitude ?? p.lat), lng = c ? c[0] : (p.longitude ?? p.lng ?? p.lon)
|
|
return Number.isFinite(Number(lat)) && Number.isFinite(Number(lng)) && lat !== '' && lng !== '' ? [Number(lat), Number(lng)] : null
|
|
}
|
|
const nameOf = (r) => { const p = (r && (r.properties || r)) || {}; return p.name || p.peopleName || p.PeopNameInCountry || '' }
|
|
|
|
/** Build once per roster: Map<coordinate key, names[]>. */
|
|
export function buildColocationIndex(rows) {
|
|
const idx = new Map()
|
|
for (const r of rows || []) {
|
|
const pt = pointOf(r); const n = nameOf(r)
|
|
if (!pt || !n) continue
|
|
const k = KEY(pt[0], pt[1])
|
|
if (!idx.has(k)) idx.set(k, [])
|
|
idx.get(k).push(n)
|
|
}
|
|
return idx
|
|
}
|
|
|
|
/** The OTHER groups on this row's point ([] when the point is not shared). */
|
|
export function colocatedWith(idx, row) {
|
|
const pt = pointOf(row); if (!idx || !pt) return []
|
|
const me = nameOf(row)
|
|
const all = idx.get(KEY(pt[0], pt[1])) || []
|
|
const others = all.filter((n) => n !== me)
|
|
return all.length > 1 ? [...new Set(others)] : []
|
|
}
|