A clicked pin finds the groups that share its point
rega, row 46: on a point four groups share, the note never said so. A click hands the legend the RENDERED feature, whose geometry the engine rebuilt from tile coordinates (about 0.001 degree off at low zoom), and the co-location index matched on the exact coordinate. The pin is now resolved to its roster row by name, and to the nearest of those rows when a name repeats across countries; only a name the roster does not know falls back to the pin's own coordinate. check:approx-pin-note adds both cases; the tile-rounded one fails on the old code.
This commit is contained in:
parent
087b005350
commit
dffae0502c
2 changed files with 35 additions and 3 deletions
|
|
@ -29,6 +29,14 @@ check('three groups on one point find each other', colocatedWith(idx, rows[0]).s
|
|||
check('a GeoJSON feature is matched to rows on the same point', colocatedWith(idx, rows[2]).sort().join() === 'Godda,Koraga', colocatedWith(idx, rows[2]))
|
||||
check('a lone point shares with nobody', colocatedWith(idx, rows[3]).length === 0, colocatedWith(idx, rows[3]))
|
||||
check('a row without a position is ignored', colocatedWith(idx, rows[4]).length === 0, colocatedWith(idx, rows[4]))
|
||||
// A click hands over the RENDERED feature: the engine rebuilt its geometry from tile coordinates, so it is
|
||||
// near the roster point but not equal to it: ~0.001 degree at low zoom (rega, row 46, a shared point that never matched).
|
||||
const rendered = { type: 'Feature', properties: { name: 'Koraga' }, geometry: { type: 'Point', coordinates: [75.2486, 12.8431] } }
|
||||
check('a rendered (tile-rounded) feature still finds its co-located groups', colocatedWith(idx, rendered).sort().join() === 'Godda,Maila', colocatedWith(idx, rendered))
|
||||
// The same people in two countries: the nearest roster row wins, not the first.
|
||||
const twin = buildColocationIndex([...rows, { name: 'Koraga', latitude: 40, longitude: 40 }, { name: 'Other', latitude: 40, longitude: 40 }])
|
||||
const renderedFar = { type: 'Feature', properties: { name: 'Koraga' }, geometry: { type: 'Point', coordinates: [40.0007, 39.9994] } }
|
||||
check('a repeated name resolves to the nearest roster point', colocatedWith(twin, renderedFar).join() === 'Other', colocatedWith(twin, renderedFar))
|
||||
|
||||
const { createServer } = await import('vite')
|
||||
const { default: vue } = await import('@vitejs/plugin-vue')
|
||||
|
|
|
|||
|
|
@ -19,24 +19,48 @@ export function pointOf(r) {
|
|||
}
|
||||
const nameOf = (r) => { const p = (r && (r.properties || r)) || {}; return p.name || p.peopleName || p.PeopNameInCountry || '' }
|
||||
|
||||
/** Build once per roster: Map<coordinate key, names[]>. */
|
||||
/** Build once per roster: Map<coordinate key, names[]>, plus `byName` (name -> [[lat, lng, key]]). */
|
||||
export function buildColocationIndex(rows) {
|
||||
const idx = new Map()
|
||||
const byName = 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)
|
||||
if (!byName.has(n)) byName.set(n, [])
|
||||
byName.get(n).push([pt[0], pt[1], k])
|
||||
}
|
||||
idx.byName = byName
|
||||
return idx
|
||||
}
|
||||
|
||||
/**
|
||||
* The roster key for a selected pin. A click hands over the RENDERED feature, whose geometry the engine
|
||||
* rebuilt from tile coordinates, so it is close to the roster's point but not equal to it at six decimals
|
||||
* (rega, row 46: a four-group point never matched). So the pin is resolved to its ROSTER ROW by name, and
|
||||
* when a name repeats (the same people in several countries) to the nearest of those rows. Only a name the
|
||||
* roster does not know falls back to the pin's own coordinate.
|
||||
*/
|
||||
function keyFor(idx, row) {
|
||||
const pt = pointOf(row)
|
||||
const cands = idx.byName && idx.byName.get(nameOf(row))
|
||||
if (cands && cands.length) {
|
||||
if (cands.length === 1 || !pt) return cands[0][2]
|
||||
let best = cands[0], d = Infinity
|
||||
for (const c of cands) { const dd = (c[0] - pt[0]) ** 2 + (c[1] - pt[1]) ** 2; if (dd < d) { d = dd; best = c } }
|
||||
return best[2]
|
||||
}
|
||||
return pt ? KEY(pt[0], pt[1]) : null
|
||||
}
|
||||
|
||||
/** 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 []
|
||||
if (!idx) return []
|
||||
const k = keyFor(idx, row); if (!k) return []
|
||||
const me = nameOf(row)
|
||||
const all = idx.get(KEY(pt[0], pt[1])) || []
|
||||
const all = idx.get(k) || []
|
||||
const others = all.filter((n) => n !== me)
|
||||
return all.length > 1 ? [...new Set(others)] : []
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue