34 lines
1.2 KiB
TypeScript
34 lines
1.2 KiB
TypeScript
|
|
import { fold } from './text';
|
||
|
|
import type { Place } from './types';
|
||
|
|
|
||
|
|
export interface Labels {
|
||
|
|
byLabel: Map<string, Place>;
|
||
|
|
labelOf: Map<string, string>;
|
||
|
|
list: string[];
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Unique human-readable label per place, usable as a datalist value. */
|
||
|
|
export function buildLabels(places: Place[]): Labels {
|
||
|
|
const byLabel = new Map<string, Place>();
|
||
|
|
const labelOf = new Map<string, string>();
|
||
|
|
for (const p of places) {
|
||
|
|
if (!p.name) continue;
|
||
|
|
const addr = [p.tags['addr:street'], p.tags['addr:housenumber']].filter(Boolean).join(' ');
|
||
|
|
let label = addr ? `${p.name} — ${addr}` : p.name;
|
||
|
|
if (byLabel.has(label)) label = `${label} (${p.id})`;
|
||
|
|
byLabel.set(label, p);
|
||
|
|
labelOf.set(p.id, label);
|
||
|
|
}
|
||
|
|
return { byLabel, labelOf, list: [...byLabel.keys()] };
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Exact label, else unique folded match on name prefix. */
|
||
|
|
export function resolveLabel(labels: Labels, text: string): Place | null {
|
||
|
|
const exact = labels.byLabel.get(text.trim());
|
||
|
|
if (exact) return exact;
|
||
|
|
const q = fold(text.trim());
|
||
|
|
if (!q) return null;
|
||
|
|
const hits = [...labels.byLabel.entries()].filter(([l]) => fold(l).startsWith(q));
|
||
|
|
return hits.length === 1 ? hits[0]![1] : null;
|
||
|
|
}
|