gadm-ts/dist/wrapper.js
2026-03-29 03:12:14 +02:00

268 lines
21 KiB
JavaScript

/**
* Wrapper API — replaces gadm_wrapper.py CLI.
*
* Provides the same 3 operations: searchRegions, getBoundary, getRegionNames.
* Includes file-based caching identical to the Python wrapper.
*/
import { getNames } from './names.js';
import { getItems } from './items.js';
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { enrichFeatureWithGHS } from './enrich-ghs.js';
// ---------- cache ----------
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const LOCAL_CACHE_DIR = join(_dirname, '../cache/gadm');
const CACHE_DIR = process.env.GADM_CACHE || LOCAL_CACHE_DIR;
function getCacheKey(prefix, value) {
const safeValue = value.replace(/[^a-zA-Z0-9\.\-_]/g, '_').substring(0, 50);
return `${prefix}_${safeValue}`;
}
function cachePath(key) {
return join(CACHE_DIR, `${key}.json`);
}
async function readCache(key, extCache) {
if (extCache) {
try {
const cached = await extCache.get(key);
if (cached)
return cached;
}
catch { }
return null; // fallback or ignore
}
const path = cachePath(key);
if (!existsSync(path))
return null;
try {
return JSON.parse(readFileSync(path, 'utf-8'));
}
catch {
return null;
}
}
async function writeCache(key, data, extCache) {
if (extCache) {
try {
await extCache.set(key, data);
}
catch { }
return;
}
const path = cachePath(key);
try {
mkdirSync(dirname(path), { recursive: true });
console.log('Writing cache:', path);
writeFileSync(path, JSON.stringify(data));
}
catch (e) {
console.error('Cache write failed:', e);
}
}
/**
* Search for admin regions by name.
* Returns metadata rows or GeoJSON FeatureCollection.
*/
export async function searchRegions(opts) {
const { query, contentLevel, geojson = false, country, cache } = opts;
const prefix = `search_${contentLevel ?? 'all'}_${geojson ? 'geo' : 'meta'}_${country ?? 'all'}`;
const key = getCacheKey(prefix, query);
const cached = await readCache(key, cache);
if (cached)
return cached;
try {
if (geojson) {
// GeoJSON mode — use Items
const itemOpts = { name: [query], cache };
if (contentLevel != null)
itemOpts.contentLevel = contentLevel;
try {
const gdf = await getItems(itemOpts);
const output = {
type: 'FeatureCollection',
features: gdf.features,
};
await writeCache(key, output, cache);
return output;
}
catch (e) {
// Try first part if comma-separated
if (query.includes(',')) {
const first = query.split(',')[0].trim();
itemOpts.name = [first];
try {
const gdf = await getItems(itemOpts);
const output = {
type: 'FeatureCollection',
features: gdf.features,
};
await writeCache(key, output, cache);
return output;
}
catch {
return { data: [] };
}
}
throw e;
}
}
else {
// Metadata mode — use Names
const namesOpts = { name: query };
if (contentLevel != null)
namesOpts.contentLevel = contentLevel;
if (country)
namesOpts.admin = country;
try {
const result = await getNames(namesOpts);
const output = { data: result.rows };
await writeCache(key, output, cache);
return output;
}
catch {
// Try first part if comma-separated
if (query.includes(',')) {
const first = query.split(',')[0].trim();
namesOpts.name = first;
try {
const result = await getNames(namesOpts);
const output = { data: result.rows };
await writeCache(key, output, cache);
return output;
}
catch {
return { data: [] };
}
}
return { data: [] };
}
}
}
catch (e) {
return { error: e.stack || e.message };
}
}
import { getBoundaryFromGpkg } from './gpkg-reader.js';
/**
* Get boundary GeoJSON for a specific GADM ID.
* Optionally enriches the boundary with GHS Population and Built-up Area data!
*/
export async function getBoundary(gadmId, contentLevel, cache, enrichOptions, resolution = 3) {
console.log('getBoundary', gadmId, contentLevel, enrichOptions, resolution, cache);
const enrichKeySuffix = enrichOptions ? '_enriched' : '';
const keySuffix = `${contentLevel ?? 'auto'}_${gadmId}${enrichKeySuffix}`;
const key = getCacheKey(`boundary`, keySuffix);
// 1. Check if we already have the EXACT requested state cached
const cached = await readCache(key, cache);
if (cached) {
console.log('getBoundary cache hit', key);
return cached;
}
// 2. Fetch the base geometry
let baseCollection = null;
// First try the far superior SQLite GeoPackage
const gpkgRes = await getBoundaryFromGpkg(gadmId, contentLevel, cache, resolution);
if (gpkgRes) {
console.log('getBoundary gpkgRes', gpkgRes);
baseCollection = gpkgRes;
}
else {
// Fallback exactly as before to Parquet mode
const baseKey = getCacheKey(`boundary_${contentLevel ?? 'auto'}`, gadmId);
const baseCached = await readCache(baseKey, cache);
if (baseCached) {
console.log('getBoundary baseCached', baseCached);
baseCollection = baseCached;
}
else {
console.log('getBoundary baseCached miss', baseCached);
try {
const gdf = await getItems({ admin: [gadmId], contentLevel, cache });
if (gdf.features.length === 0) {
return { error: 'Region not found' };
}
baseCollection = gdf;
await writeCache(baseKey, baseCollection, cache);
}
catch (e) {
return { error: e.message };
}
}
}
let collectionToReturn = baseCollection;
// 3. Apply GeoTIFF enrichment if requested — skip if already enriched (e.g. from C++ cache)
const alreadyEnriched = baseCollection?.features?.some((f) => f.properties?.ghsPopulation !== undefined);
if (enrichOptions && !alreadyEnriched && baseCollection && baseCollection.features) {
console.log('getBoundary enrichOptions', enrichOptions);
// Deep clone so we don't mutate an in-memory cached object accidentally
collectionToReturn = JSON.parse(JSON.stringify(baseCollection));
for (const feature of collectionToReturn.features) {
try {
const enrichResult = await enrichFeatureWithGHS(feature, enrichOptions);
Object.assign(feature.properties, enrichResult);
// Set the default population property to the highly accurate GHS number
if (enrichResult.ghsPopulation !== undefined) {
feature.properties.population = enrichResult.ghsPopulation;
}
}
catch (e) {
console.error('GHS Enrichment failed for feature', feature.properties?.name || '', e);
}
}
await writeCache(key, collectionToReturn, cache);
}
return collectionToReturn;
}
/**
* Get names for all sub-regions of an admin area, optionally recursing through levels.
*/
export async function getRegionNames(opts) {
const { admin, contentLevel, depth = 1, cache } = opts;
const key = getCacheKey(`names_${admin}_${contentLevel}_${depth}`, 'names');
const cached = await readCache(key, cache);
if (cached)
return cached;
try {
const allResults = [];
// Determine starting level from admin code
let startLevel;
if (contentLevel != null) {
startLevel = contentLevel;
}
else {
// GID format: XXX.L1.L2_1 — count dots + 1
startLevel = admin.split('.').length; // "ESP" = 1 dot → level 1 start
if (!admin.includes('.'))
startLevel = 1; // country code → start at level 1
}
// Handle infinite depth
const depthStr = String(depth).toLowerCase();
const limit = (depthStr === 'infinite' || depthStr === 'inf' || depthStr === '-1')
? 5
: Number(depth);
let currentLevel = startLevel;
for (let i = 0; i < limit; i++) {
try {
const result = await getNames({
admin,
contentLevel: currentLevel,
});
if (result.rows.length === 0)
break;
allResults.push(...result.rows);
}
catch {
break; // Level doesn't exist
}
currentLevel++;
}
const output = { data: allResults };
await writeCache(key, output, cache);
return output;
}
catch (e) {
return { error: e.stack || e.message };
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3JhcHBlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy93cmFwcGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7OztHQUtHO0FBQ0gsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUN0QyxPQUFPLEVBQUUsUUFBUSxFQUErQyxNQUFNLFlBQVksQ0FBQztBQUNuRixPQUFPLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxhQUFhLEVBQUUsU0FBUyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3hFLE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3JDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxLQUFLLENBQUM7QUFHcEMsT0FBTyxFQUFFLG9CQUFvQixFQUF5QixNQUFNLGlCQUFpQixDQUFDO0FBRTlFLDhCQUE4QjtBQUU5QixNQUFNLFNBQVMsR0FBRyxhQUFhLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUNqRCxNQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFFcEMsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLFFBQVEsRUFBRSxlQUFlLENBQUMsQ0FBQztBQUN4RCxNQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVUsSUFBSSxlQUFlLENBQUM7QUFFNUQsU0FBUyxXQUFXLENBQUMsTUFBYyxFQUFFLEtBQWE7SUFDOUMsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxvQkFBb0IsRUFBRSxHQUFHLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQzVFLE9BQU8sR0FBRyxNQUFNLElBQUksU0FBUyxFQUFFLENBQUM7QUFDcEMsQ0FBQztBQUVELFNBQVMsU0FBUyxDQUFDLEdBQVc7SUFDMUIsT0FBTyxJQUFJLENBQUMsU0FBUyxFQUFFLEdB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