168 lines
15 KiB
JavaScript
168 lines
15 KiB
JavaScript
/**
|
|
* Items — GeoJSON boundary fetcher.
|
|
*
|
|
* Reads geometry from a local GeoPackage file when available,
|
|
* falls back to the GADM CDN. Results include corrected names
|
|
* from the parquet database.
|
|
*/
|
|
import { getNames } from './names.js';
|
|
import { getBoundaryFromGpkg } from './gpkg-reader.js';
|
|
import { readFileSync } from 'fs';
|
|
import { resolve, dirname } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
const __filename_ = fileURLToPath(import.meta.url);
|
|
const __dirname_ = dirname(__filename_);
|
|
// ---------- constants ----------
|
|
const GADM_URL = 'https://geodata.ucdavis.edu/gadm/gadm4.1/json/gadm41_{}_{}.json';
|
|
/** Continent → ISO3 codes mapping */
|
|
const CONTINENT_MAP = JSON.parse(readFileSync(resolve(__dirname_, '../data/gadm_continent.json'), 'utf-8'));
|
|
// ---------- main function ----------
|
|
/**
|
|
* Fetch GeoJSON boundaries for administrative areas.
|
|
* Tries local GeoPackage first, falls back to GADM CDN.
|
|
*/
|
|
export async function getItems(opts) {
|
|
let { name, admin, contentLevel = -1, cache } = opts;
|
|
// Normalize to arrays
|
|
let names = typeof name === 'string' ? [name] : (name || []);
|
|
let admins = typeof admin === 'string' ? [admin] : (admin || []);
|
|
if (names.length === 0 && admins.length === 0) {
|
|
throw new Error('At least "name" or "admin" need to be set.');
|
|
}
|
|
// Continent expansion
|
|
if (names.length === 1 && names[0].toLowerCase() in CONTINENT_MAP) {
|
|
admins = CONTINENT_MAP[names[0].toLowerCase()];
|
|
names = [];
|
|
}
|
|
// Fetch all items
|
|
const allFeatures = [];
|
|
for (const n of names) {
|
|
const features = await fetchSingleItem(n, '', contentLevel, cache);
|
|
allFeatures.push(...features);
|
|
}
|
|
for (const a of admins) {
|
|
const features = await fetchSingleItem('', a, contentLevel, cache);
|
|
allFeatures.push(...features);
|
|
}
|
|
return {
|
|
type: 'FeatureCollection',
|
|
features: allFeatures,
|
|
};
|
|
}
|
|
// ---------- internal ----------
|
|
async function fetchSingleItem(name, admin, contentLevel, cache) {
|
|
// Step 1: Look up the area to find its level + GID
|
|
const lookupOpts = name ? { name } : { admin };
|
|
const lookup = await getNames(lookupOpts);
|
|
if (lookup.rows.length > 1) {
|
|
const id = name || admin;
|
|
throw new Error(`The requested name ("${id}") is not unique (${lookup.rows.length} results). ` +
|
|
`Use the "admin" parameter instead. ` +
|
|
`To find the GADM code, use: getNames({ name: "${id}" })`);
|
|
}
|
|
const level = lookup.level;
|
|
const gidCol = `GID_${level}`;
|
|
const gadmId = lookup.rows[0][gidCol] || '';
|
|
// Step 2: Resolve content level
|
|
const contentLookup = await getNames({
|
|
...(name ? { name } : { admin }),
|
|
contentLevel,
|
|
});
|
|
const resolvedLevel = contentLookup.level;
|
|
// Step 3: Try local GeoPackage first
|
|
let localResult = await tryLocalGpkg(gadmId, name || admin, level, resolvedLevel, cache);
|
|
// Step 4: Fallback to GADM CDN
|
|
if (!localResult) {
|
|
localResult = await fetchFromCDN(name, admin, gadmId, level, resolvedLevel);
|
|
}
|
|
if (resolvedLevel === level) {
|
|
localResult.forEach(f => f.properties.isOuter = true);
|
|
}
|
|
return localResult;
|
|
}
|
|
/**
|
|
* Read boundaries from local GeoPackage.
|
|
* Returns features with all matching rows at the resolved content level.
|
|
*/
|
|
async function tryLocalGpkg(gadmId, id, level, resolvedLevel, cache) {
|
|
// Query the GeoPackage for all rows belonging to this region
|
|
const gidCol = `GID_${level}`;
|
|
const nameCols = [];
|
|
for (let l = 0; l <= Math.min(resolvedLevel, 5); l++) {
|
|
nameCols.push(`NAME_${l}`, `GID_${l}`);
|
|
}
|
|
const result = await getBoundaryFromGpkg(gadmId, resolvedLevel, cache);
|
|
if (!result || result.features.length === 0)
|
|
return null;
|
|
// Convert BoundaryFeature → GeoJSONFeature
|
|
const features = result.features.map(f => ({
|
|
type: 'Feature',
|
|
properties: f.properties,
|
|
geometry: f.geometry,
|
|
}));
|
|
return features;
|
|
}
|
|
/**
|
|
* Fallback: fetch from GADM CDN + correct names from parquet.
|
|
*/
|
|
async function fetchFromCDN(name, admin, gadmId, level, resolvedLevel) {
|
|
const iso3 = gadmId.substring(0, 3);
|
|
const url = GADM_URL.replace('{}', iso3).replace('{}', String(resolvedLevel));
|
|
let data;
|
|
try {
|
|
const res = await fetch(url);
|
|
if (!res.ok)
|
|
throw new Error(`HTTP ${res.status}`);
|
|
data = await res.json();
|
|
}
|
|
catch (e) {
|
|
throw new Error(`Cannot retrieve data from GADM server. ` +
|
|
`Try opening: ${url}. ` +
|
|
`If it fails, the error is from GADM servers.`);
|
|
}
|
|
// Convert to features
|
|
const rawFeatures = data.features || [];
|
|
let features = rawFeatures.map((f) => ({
|
|
type: 'Feature',
|
|
properties: { ...f.properties, ...(f.properties?.COUNTRY ? { NAME_0: f.properties.COUNTRY } : {}) },
|
|
geometry: f.geometry,
|
|
}));
|
|
// Name correction from parquet
|
|
const isos = [...new Set(features.map(f => f.properties.GID_0).filter(Boolean))];
|
|
if (isos.length > 0) {
|
|
const completeRows = [];
|
|
for (const iso of isos) {
|
|
const nr = await getNames({ admin: iso, contentLevel: resolvedLevel, complete: true });
|
|
completeRows.push(...nr.rows);
|
|
}
|
|
const gidKey = (row) => {
|
|
const parts = [];
|
|
for (let i = 0; i <= resolvedLevel; i++) {
|
|
parts.push(row[`GID_${i}`] || '');
|
|
}
|
|
return parts.join('|');
|
|
};
|
|
const nameLookup = new Map();
|
|
for (const row of completeRows) {
|
|
nameLookup.set(gidKey(row), row);
|
|
}
|
|
for (const feature of features) {
|
|
const key = gidKey(feature.properties);
|
|
const correct = nameLookup.get(key);
|
|
if (correct) {
|
|
for (let i = 0; i <= resolvedLevel; i++) {
|
|
if (correct[`NAME_${i}`]) {
|
|
feature.properties[`NAME_${i}`] = correct[`NAME_${i}`];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Filter to the requested area
|
|
const id = name || admin;
|
|
const colPrefix = name ? 'NAME_' : 'GID_';
|
|
const filterCol = `${colPrefix}${level}`;
|
|
features = features.filter(f => f.properties[filterCol]?.toLowerCase() === id.toLowerCase());
|
|
return features;
|
|
}
|
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaXRlbXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaXRlbXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBQ0gsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUN0QyxPQUFPLEVBQUUsbUJBQW1CLEVBQWtCLE1BQU0sa0JBQWtCLENBQUM7QUFDdkUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLElBQUksQ0FBQztBQUNsQyxPQUFPLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUN4QyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBRXBDLE1BQU0sV0FBVyxHQUFHLGFBQWEsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0FBQ25ELE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0FBQztBQThCeEMsa0NBQWtDO0FBRWxDLE1BQU0sUUFBUSxHQUFHLGlFQUFpRSxDQUFDO0FBRW5GLHFDQUFxQztBQUNyQyxNQUFNLGFBQWEsR0FBNkIsSUFBSSxDQUFDLEtBQUssQ0FDdEQsWUFBWSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsNkJBQTZCLENBQUMsRUFBRSxPQUFPLENBQUMsQ0FDNUUsQ0FBQztBQUVGLHNDQUFzQztBQUV0Qzs7O0dBR0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLFFBQVEsQ0FBQyxJQUFrQjtJQUM3QyxJQUFJLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxZQUFZLEdBQUcsQ0FBQyxDQUFDLEVBQUUsS0FBSyxFQUFFLEdBQUcsSUFBSSxDQUFDO0lBRXJELHNCQUFzQjtJQUN0QixJQUFJLEtBQUssR0FBRyxPQUFPLElBQUksS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lB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
|