170 lines
15 KiB
JavaScript
170 lines
15 KiB
JavaScript
/**
|
|
* Names lookup — port of Python pygadm.Names class.
|
|
*
|
|
* Searches the GADM parquet database for admin areas by name or GID code.
|
|
* Returns matching rows with NAME_{level} and GID_{level} columns.
|
|
*/
|
|
import { loadDatabase } from './database.js';
|
|
// ---------- helpers ----------
|
|
/** Simple Levenshtein distance for fuzzy matching */
|
|
function levenshtein(a, b) {
|
|
const m = a.length, n = b.length;
|
|
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
|
|
for (let i = 0; i <= m; i++)
|
|
dp[i][0] = i;
|
|
for (let j = 0; j <= n; j++)
|
|
dp[0][j] = j;
|
|
for (let i = 1; i <= m; i++) {
|
|
for (let j = 1; j <= n; j++) {
|
|
dp[i][j] = a[i - 1] === b[j - 1]
|
|
? dp[i - 1][j - 1]
|
|
: 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
|
|
}
|
|
}
|
|
return dp[m][n];
|
|
}
|
|
/** Find the N closest matches to `query` from a list of candidates */
|
|
function closestMatches(query, candidates, n = 5) {
|
|
const q = query.toLowerCase();
|
|
const scored = candidates.map(c => ({ val: c, dist: levenshtein(q, c.toLowerCase()) }));
|
|
scored.sort((a, b) => a.dist - b.dist);
|
|
return scored.slice(0, n).map(s => s.val);
|
|
}
|
|
// ---------- main function ----------
|
|
/**
|
|
* Look up administrative area names/codes in the GADM database.
|
|
*
|
|
* Port of Python `pygadm.Names(name, admin, content_level, complete)`.
|
|
*/
|
|
export async function getNames(opts = {}) {
|
|
const { name, admin, contentLevel: rawContentLevel = -1, complete = false } = opts;
|
|
if (name && admin) {
|
|
throw new Error('"name" and "admin" cannot be set at the same time.');
|
|
}
|
|
const db = await loadDatabase();
|
|
let contentLevel = rawContentLevel;
|
|
if (name || admin) {
|
|
const isName = !!name;
|
|
const id = (name || admin);
|
|
const colPrefix = isName ? 'NAME_' : 'GID_';
|
|
// Search all 6 levels for the id
|
|
const matchedLevel = findLevel(db, colPrefix, id);
|
|
if (matchedLevel === -1) {
|
|
// Not found — suggest closest matches
|
|
const allIds = collectAllIds(db, colPrefix);
|
|
const close = closestMatches(id, allIds, 5);
|
|
const formatted = isName
|
|
? close.map(c => capitalize(c))
|
|
: close.map(c => c.toUpperCase());
|
|
throw new Error(`The requested "${id}" is not part of GADM. ` +
|
|
`The closest matches are: ${formatted.join(', ')}.`);
|
|
}
|
|
const level = matchedLevel;
|
|
// Filter to rows matching this id at the found level
|
|
const col = `${colPrefix}${level}`;
|
|
let subDf = db.filter(r => caseEqual(r[col], id));
|
|
// Find max available level in this subset
|
|
const maxLevel = findMaxLevel(subDf);
|
|
// Resolve content level
|
|
if (contentLevel === -1) {
|
|
contentLevel = level;
|
|
}
|
|
else if (contentLevel < level) {
|
|
// Requested level higher than area — fallback
|
|
contentLevel = level;
|
|
}
|
|
if (contentLevel > maxLevel) {
|
|
contentLevel = maxLevel;
|
|
}
|
|
// Deduplicate at content level
|
|
const nameCol = `NAME_${contentLevel}`;
|
|
const gidCol = `GID_${contentLevel}`;
|
|
const seen = new Set();
|
|
subDf = subDf.filter(r => {
|
|
if (!r[nameCol])
|
|
return false; // skip empty
|
|
const key = `${r[nameCol]}|${r[gidCol]}`;
|
|
if (seen.has(key))
|
|
return false;
|
|
seen.add(key);
|
|
return true;
|
|
});
|
|
// Select columns
|
|
const columns = complete
|
|
? ['NAME_0', 'GID_0', 'NAME_1', 'GID_1', 'NAME_2', 'GID_2', 'NAME_3', 'GID_3', 'NAME_4', 'GID_4', 'NAME_5', 'GID_5']
|
|
.filter((_, i) => Math.floor(i / 2) <= contentLevel)
|
|
: [nameCol, gidCol];
|
|
const rows = complete ? subDf : subDf.map(r => {
|
|
const out = {};
|
|
for (const c of columns)
|
|
out[c] = r[c] ?? '';
|
|
return out;
|
|
});
|
|
return { rows, level: contentLevel, columns };
|
|
}
|
|
// No name/admin — return world-level data
|
|
contentLevel = contentLevel === -1 ? 0 : contentLevel;
|
|
const nameCol = `NAME_${contentLevel}`;
|
|
const gidCol = `GID_${contentLevel}`;
|
|
const seen = new Set();
|
|
const rows = db.filter(r => {
|
|
if (!r[nameCol])
|
|
return false;
|
|
const key = `${r[nameCol]}|${r[gidCol]}`;
|
|
if (seen.has(key))
|
|
return false;
|
|
seen.add(key);
|
|
return true;
|
|
});
|
|
const columns = [nameCol, gidCol];
|
|
const filtered = rows.map(r => {
|
|
const out = {};
|
|
for (const c of columns)
|
|
out[c] = r[c] ?? '';
|
|
return out;
|
|
});
|
|
return { rows: filtered, level: contentLevel, columns };
|
|
}
|
|
// ---------- internal helpers ----------
|
|
function caseEqual(a, b) {
|
|
if (!a)
|
|
return false;
|
|
return a.toLowerCase() === b.toLowerCase();
|
|
}
|
|
function capitalize(s) {
|
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
}
|
|
/** Find which level (0-5) contains the given id */
|
|
function findLevel(db, colPrefix, id) {
|
|
const idLower = id.toLowerCase();
|
|
for (let level = 0; level < 6; level++) {
|
|
const col = `${colPrefix}${level}`;
|
|
if (db.some(r => r[col]?.toLowerCase() === idLower)) {
|
|
return level;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
/** Collect all unique ids across all levels for fuzzy matching */
|
|
function collectAllIds(db, colPrefix) {
|
|
const ids = new Set();
|
|
for (let level = 0; level < 6; level++) {
|
|
const col = `${colPrefix}${level}`;
|
|
for (const r of db) {
|
|
if (r[col])
|
|
ids.add(r[col].toLowerCase());
|
|
}
|
|
}
|
|
return [...ids];
|
|
}
|
|
/** Find the maximum level that has non-empty data */
|
|
function findMaxLevel(rows) {
|
|
for (let level = 5; level >= 0; level--) {
|
|
const col = `GID_${level}`;
|
|
if (rows.some(r => r[col] && r[col] !== '')) {
|
|
return level;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmFtZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvbmFtZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7O0dBS0c7QUFDSCxPQUFPLEVBQUUsWUFBWSxFQUFnQixNQUFNLGVBQWUsQ0FBQztBQXdCM0QsZ0NBQWdDO0FBRWhDLHFEQUFxRDtBQUNyRCxTQUFTLFdBQVcsQ0FBQyxDQUFTLEVBQUUsQ0FBUztJQUNyQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBQ2pDLE1BQU0sRUFBRSxHQUFlLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDakYsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLEVBQUU7UUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQzFDLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFO1FBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUMxQyxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDMUIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQzFCLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMs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
|