622 lines
52 KiB
JavaScript
622 lines
52 KiB
JavaScript
/**
|
||
* gpkg-reader.ts — Read geometry from a local GeoPackage file.
|
||
*
|
||
* Parses GeoPackage Binary (GPKG header + WKB) into GeoJSON.
|
||
* Uses better-sqlite3 for SQLite access.
|
||
*
|
||
* GeoPackage Binary format:
|
||
* [2 bytes magic "GP"] [1 byte version] [1 byte flags]
|
||
* [4 bytes SRS ID] [envelope (0–64 bytes)] [WKB geometry]
|
||
*
|
||
* Flags byte: bit 0 = byte order, bits 1-3 = envelope type, bit 5 = empty
|
||
* Envelope sizes: 0=none, 1=xy(32B), 2=xyz(48B), 3=xym(48B), 4=xyzm(64B)
|
||
*/
|
||
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
||
import { resolve, join, dirname } from 'path';
|
||
import { fileURLToPath } from 'url';
|
||
import { enrichFeatureWithGHS } from './enrich-ghs.js';
|
||
const _filename = fileURLToPath(import.meta.url);
|
||
const _dirname = dirname(_filename);
|
||
// ────────── GeoPackage paths ──────────
|
||
const DEFAULT_GPKG_PATHS = [
|
||
// 1. Explicit environment-defined path
|
||
process.env.GADM_GPKG_PATH,
|
||
// 2. Production CWD data mount
|
||
resolve(process.cwd(), 'data/gadm_410.gpkg'),
|
||
resolve(process.cwd(), 'cache/gadm/gadm_410.gpkg'),
|
||
];
|
||
let _gpkgPath = null;
|
||
/** Set or override the GeoPackage file path */
|
||
export function setGpkgPath(p) {
|
||
_gpkgPath = p;
|
||
}
|
||
function findGpkg() {
|
||
if (_gpkgPath && existsSync(_gpkgPath))
|
||
return _gpkgPath;
|
||
for (const p of DEFAULT_GPKG_PATHS) {
|
||
if (p && existsSync(p)) {
|
||
_gpkgPath = p;
|
||
return p;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
// ────────── WKB Parser ──────────
|
||
class WKBReader {
|
||
buf;
|
||
pos;
|
||
le; // little-endian
|
||
constructor(buf, offset) {
|
||
this.buf = buf;
|
||
this.pos = offset;
|
||
// First byte of WKB = endianness indicator
|
||
this.le = this.buf[this.pos] === 1;
|
||
this.pos++;
|
||
}
|
||
readUInt32() {
|
||
const v = this.le
|
||
? this.buf.readUInt32LE(this.pos)
|
||
: this.buf.readUInt32BE(this.pos);
|
||
this.pos += 4;
|
||
return v;
|
||
}
|
||
readDouble() {
|
||
const v = this.le
|
||
? this.buf.readDoubleLE(this.pos)
|
||
: this.buf.readDoubleBE(this.pos);
|
||
this.pos += 8;
|
||
return v;
|
||
}
|
||
readCoord() {
|
||
const x = this.readDouble(); // longitude
|
||
const y = this.readDouble(); // latitude
|
||
return [x, y];
|
||
}
|
||
readRing() {
|
||
const count = this.readUInt32();
|
||
const ring = [];
|
||
for (let i = 0; i < count; i++) {
|
||
ring.push(this.readCoord());
|
||
}
|
||
return ring;
|
||
}
|
||
readPolygon() {
|
||
const ringCount = this.readUInt32();
|
||
const rings = [];
|
||
for (let i = 0; i < ringCount; i++) {
|
||
rings.push(this.readRing());
|
||
}
|
||
return rings;
|
||
}
|
||
readGeometry() {
|
||
const wkbType = this.readUInt32();
|
||
switch (wkbType) {
|
||
case 1: // Point
|
||
return { type: 'Point', coordinates: this.readCoord() };
|
||
case 2: { // LineString
|
||
const count = this.readUInt32();
|
||
const coords = [];
|
||
for (let i = 0; i < count; i++)
|
||
coords.push(this.readCoord());
|
||
return { type: 'LineString', coordinates: coords };
|
||
}
|
||
case 3: // Polygon
|
||
return { type: 'Polygon', coordinates: this.readPolygon() };
|
||
case 4: { // MultiPoint
|
||
const count = this.readUInt32();
|
||
const points = [];
|
||
for (let i = 0; i < count; i++) {
|
||
this.le = this.buf[this.pos] === 1;
|
||
this.pos++;
|
||
this.readUInt32(); // skip type
|
||
points.push(this.readCoord());
|
||
}
|
||
return { type: 'MultiPoint', coordinates: points };
|
||
}
|
||
case 5: { // MultiLineString
|
||
const count = this.readUInt32();
|
||
const lines = [];
|
||
for (let i = 0; i < count; i++) {
|
||
this.le = this.buf[this.pos] === 1;
|
||
this.pos++;
|
||
this.readUInt32(); // skip type
|
||
const ptCount = this.readUInt32();
|
||
const coords = [];
|
||
for (let j = 0; j < ptCount; j++)
|
||
coords.push(this.readCoord());
|
||
lines.push(coords);
|
||
}
|
||
return { type: 'MultiLineString', coordinates: lines };
|
||
}
|
||
case 6: { // MultiPolygon
|
||
const count = this.readUInt32();
|
||
const polys = [];
|
||
for (let i = 0; i < count; i++) {
|
||
this.le = this.buf[this.pos] === 1;
|
||
this.pos++;
|
||
this.readUInt32(); // skip type (3 = Polygon)
|
||
polys.push(this.readPolygon());
|
||
}
|
||
return { type: 'MultiPolygon', coordinates: polys };
|
||
}
|
||
default:
|
||
throw new Error(`Unsupported WKB type: ${wkbType}`);
|
||
}
|
||
}
|
||
}
|
||
/** Parse GeoPackage Binary blob → GeoJSON geometry */
|
||
export function parseGpkgGeometry(blob) {
|
||
// Validate magic
|
||
if (blob[0] !== 0x47 || blob[1] !== 0x50) {
|
||
throw new Error('Not a GeoPackage geometry (missing GP magic)');
|
||
}
|
||
const flags = blob[3];
|
||
const isEmpty = (flags >> 5) & 1;
|
||
if (isEmpty) {
|
||
return { type: 'MultiPolygon', coordinates: [] };
|
||
}
|
||
const envelopeType = (flags >> 1) & 7;
|
||
const envSizes = [0, 32, 48, 48, 64];
|
||
const envSize = envSizes[envelopeType] || 0;
|
||
const wkbStart = 8 + envSize;
|
||
const reader = new WKBReader(blob, wkbStart);
|
||
return reader.readGeometry();
|
||
}
|
||
import { feature } from '@turf/helpers';
|
||
import polygonClipping from 'polygon-clipping';
|
||
import simplifyJs from 'simplify-js';
|
||
// ────────── Query ──────────
|
||
/**
|
||
* Merge polygon coordinates by dissolving them.
|
||
*/
|
||
async function mergeGeometries(geometries) {
|
||
const polys = [];
|
||
for (const g of geometries) {
|
||
if (g.type === 'MultiPolygon') {
|
||
for (const coords of g.coordinates) {
|
||
polys.push(feature({ type: 'Polygon', coordinates: coords }));
|
||
}
|
||
}
|
||
else if (g.type === 'Polygon') {
|
||
polys.push(feature(g));
|
||
}
|
||
}
|
||
// Chunked Dissolve
|
||
let currentPolys = polys;
|
||
const CHUNK_SIZE = 100;
|
||
while (currentPolys.length > 1) {
|
||
const nextPolys = [];
|
||
for (let i = 0; i < currentPolys.length; i += CHUNK_SIZE) {
|
||
const chunk = currentPolys.slice(i, i + CHUNK_SIZE);
|
||
if (chunk.length === 1) {
|
||
nextPolys.push(chunk[0]);
|
||
continue;
|
||
}
|
||
try {
|
||
const startTime = Date.now();
|
||
// Use polygon-clipping instead of Turf.js as turf.dissolve is prone to infinite loops
|
||
const polyCoords = chunk.map(f => {
|
||
const g = f.geometry;
|
||
return g.type === 'Polygon' ? [g.coordinates] : g.coordinates;
|
||
});
|
||
const unionedCoords = polygonClipping.union(polyCoords[0], ...polyCoords.slice(1));
|
||
if (unionedCoords.length > 0) {
|
||
nextPolys.push(feature({ type: 'MultiPolygon', coordinates: unionedCoords }));
|
||
}
|
||
}
|
||
catch (e) {
|
||
console.warn('[mergeGeometries] polygon-clipping failed on chunk, falling back to basic merge:', e);
|
||
nextPolys.push(...chunk);
|
||
}
|
||
// microtask yield barrier
|
||
await new Promise(r => setTimeout(r, 0));
|
||
}
|
||
// If length did not decrease, we can't dissolve further
|
||
if (nextPolys.length >= currentPolys.length) {
|
||
currentPolys = nextPolys;
|
||
break;
|
||
}
|
||
currentPolys = nextPolys;
|
||
}
|
||
if (currentPolys.length === 1) {
|
||
return currentPolys[0].geometry;
|
||
}
|
||
// fallback if dissolve fails or returns multiple disjoint polygons
|
||
const allPolygons = [];
|
||
for (const p of currentPolys) {
|
||
const g = p.geometry;
|
||
if (g.type === 'MultiPolygon') {
|
||
allPolygons.push(...g.coordinates);
|
||
}
|
||
else if (g.type === 'Polygon') {
|
||
allPolygons.push(g.coordinates);
|
||
}
|
||
}
|
||
return { type: 'MultiPolygon', coordinates: allPolygons };
|
||
}
|
||
// ────────── Geometry Optimization ──────────
|
||
function optimizeCoordinates(coords, tolerance) {
|
||
if (coords.length > 0 && typeof coords[0] === 'number') {
|
||
const x = Number(coords[0].toFixed(5));
|
||
const y = Number(coords[1].toFixed(5));
|
||
return [x, y];
|
||
}
|
||
// If it's a ring of points (array of arrays of numbers)
|
||
if (coords.length > 0 && Array.isArray(coords[0]) && typeof coords[0][0] === 'number') {
|
||
// Map to {x, y} for simplify-js
|
||
let points = coords.map((c) => ({ x: c[0], y: c[1] }));
|
||
if (tolerance > 0 && points.length > 2) {
|
||
points = simplifyJs(points, tolerance, false);
|
||
}
|
||
// Map back to [x, y] and round
|
||
return points.map(p => [Number(p.x.toFixed(5)), Number(p.y.toFixed(5))]);
|
||
}
|
||
// Deeper arrays (Polygon / MultiPolygon)
|
||
return coords.map(c => optimizeCoordinates(c, tolerance));
|
||
}
|
||
/**
|
||
* Get boundary features for a specific GID.
|
||
*
|
||
* The flat GADM table has one row per leaf-level area (all levels filled).
|
||
* This function:
|
||
* 1. Queries all rows where `GID_{gidLevel} = gadmId`
|
||
* 2. Groups them by `GID_{contentLevel}`
|
||
* 3. Merges leaf geometries into one MultiPolygon per group
|
||
*
|
||
* Examples:
|
||
* - getBoundaryFromGpkg('DEU', 0) → 1 feature (Germany outline, merged from all leaves)
|
||
* - getBoundaryFromGpkg('DEU.14_1', 2) → 13 features (one per L2 district in Sachsen)
|
||
* - getBoundaryFromGpkg('DEU.14_1') → 1 feature (Sachsen outline)
|
||
*
|
||
* Returns null if GeoPackage is not available.
|
||
*/
|
||
import { createRequire } from 'module';
|
||
let sharedDb = null;
|
||
let sharedTableName = '';
|
||
function ensureDb() {
|
||
if (sharedDb)
|
||
return true;
|
||
const gpkgPath = findGpkg();
|
||
if (!gpkgPath)
|
||
return false;
|
||
let Database;
|
||
try {
|
||
if (typeof require !== 'undefined') {
|
||
Database = require('better-sqlite3');
|
||
}
|
||
else {
|
||
const req = createRequire(import.meta.url);
|
||
Database = req('better-sqlite3');
|
||
}
|
||
}
|
||
catch (e) {
|
||
console.warn('Failed to load better-sqlite3 (GeoPackage bounds will be skipped):', e.message);
|
||
return false;
|
||
}
|
||
sharedDb = new Database(gpkgPath, { readonly: true });
|
||
try {
|
||
const tables = sharedDb.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'gpkg_%' AND name NOT LIKE 'rtree_%' AND name NOT LIKE 'sqlite_%'").all();
|
||
sharedTableName = tables.find((t) => t.name === 'gadm_410' || t.name === 'gadm41_raw')?.name;
|
||
if (!sharedTableName) {
|
||
sharedDb.close();
|
||
sharedDb = null;
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
catch (e) {
|
||
sharedDb.close();
|
||
sharedDb = null;
|
||
return false;
|
||
}
|
||
}
|
||
export async function getBoundaryFromGpkg(gadmId, contentLevel, externalCache, resolution = 3) {
|
||
const levelStr = contentLevel != null ? contentLevel.toString() : 'auto';
|
||
const safeId = gadmId.replace(/[^a-zA-Z0-9\.\-_]/g, '_').substring(0, 50);
|
||
const cacheKey = `boundary_${safeId}_${levelStr}`;
|
||
// Resolve cache locations for cross-environment compatibility (Dev Workspace vs Prod Server)
|
||
const possibleCacheDirs = [];
|
||
if (process.env.GADM_CACHE)
|
||
possibleCacheDirs.push(process.env.GADM_CACHE);
|
||
possibleCacheDirs.push(resolve(process.cwd(), 'cache/gadm'));
|
||
possibleCacheDirs.push(resolve(_dirname, '../cache/gadm'));
|
||
const uniqueCacheDirs = [...new Set(possibleCacheDirs)];
|
||
if (externalCache) {
|
||
try {
|
||
const cached = await externalCache.get(cacheKey);
|
||
if (cached)
|
||
return cached;
|
||
}
|
||
catch (e) { /* ignore */ }
|
||
}
|
||
else {
|
||
for (const dir of uniqueCacheDirs) {
|
||
const cacheFile = join(dir, `${cacheKey}.json`);
|
||
if (existsSync(cacheFile)) {
|
||
try {
|
||
return JSON.parse(readFileSync(cacheFile, 'utf-8'));
|
||
}
|
||
catch (e) {
|
||
console.warn(`[gpkg-reader] Failed to read cache from ${cacheFile}:`, e);
|
||
}
|
||
}
|
||
}
|
||
// Fallback: check for C++ pre-built cache (boundary_{COUNTRY}_{LEVEL}.json)
|
||
// The C++ pipeline outputs one file per country per level, containing ALL features.
|
||
// For sub-region queries (e.g. 'DEU.2_1' at level 3), we read the full country file
|
||
// and filter by GID prefix — since GADM GIDs are hierarchical (DEU.2.91.1_1 ⊂ DEU.2_1).
|
||
const dotCount = (gadmId.match(/\./g) || []).length;
|
||
const gidLevel = dotCount;
|
||
const resolvedLevel = contentLevel != null ? contentLevel : gidLevel;
|
||
const countryCode = gadmId.split('.')[0];
|
||
// C++ outputs sub-region precise files too if batched with `--split-levels`.
|
||
// We look for exact gadmId match first, then fall back to full country.
|
||
const fallbackNames = [
|
||
`boundary_${gadmId}_${resolvedLevel}.json`,
|
||
`boundary_${countryCode}_${resolvedLevel}.json`
|
||
];
|
||
// Remove duplicates if gadmId == countryCode
|
||
const uniqueFallbackNames = [...new Set(fallbackNames)];
|
||
// Build a prefix for sub-region filtering (used mostly for the full-country fallback)
|
||
const isSubRegion = gadmId.includes('.');
|
||
const gidPrefix = isSubRegion
|
||
? gadmId.replace(/_\d+$/, '') + '.' // strip version suffix, add dot
|
||
: null;
|
||
for (const cppFileName of uniqueFallbackNames) {
|
||
for (const dir of uniqueCacheDirs) {
|
||
const cppFile = join(dir, cppFileName);
|
||
if (existsSync(cppFile)) {
|
||
try {
|
||
const raw = JSON.parse(readFileSync(cppFile, 'utf-8'));
|
||
if (raw.features && Array.isArray(raw.features)) {
|
||
console.log(`[gpkg-reader] Loading from CPP Cache File ${cppFile} : ${gidPrefix || 'country-wide'}`);
|
||
let rawFeatures = raw.features;
|
||
// Filter by GID prefix if we had to read the fallback country-wide file
|
||
// (If we loaded the exact sub-region file, it's already perfectly chunked)
|
||
if (gidPrefix && cppFileName.includes(countryCode) && !cppFileName.includes(gadmId)) {
|
||
rawFeatures = rawFeatures.filter((f) => f.code && f.code.startsWith(gidPrefix));
|
||
}
|
||
if (rawFeatures.length === 0)
|
||
continue; // no matches, try next dir
|
||
const features = rawFeatures.map((f) => {
|
||
const { geometry, code, name, ...enrichment } = f;
|
||
return {
|
||
type: 'Feature',
|
||
properties: { name, code, ...enrichment },
|
||
geometry,
|
||
};
|
||
});
|
||
const result = { type: 'FeatureCollection', features };
|
||
const outCacheFile = join(uniqueCacheDirs[1] || uniqueCacheDirs[0], `${cacheKey}.json`);
|
||
try {
|
||
writeFileSync(outCacheFile, JSON.stringify(result));
|
||
}
|
||
catch (e) { /* ignore */ }
|
||
return result;
|
||
}
|
||
}
|
||
catch (e) {
|
||
console.warn(`[gpkg-reader] Failed to read C++ cache from ${cppFile}:`, e);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (!ensureDb())
|
||
return null;
|
||
const db = sharedDb;
|
||
const tableName = sharedTableName;
|
||
// Determine the GID's own level from dot count: "ESP" = L0, "ESP.6_1" = L1
|
||
const dotCount = (gadmId.match(/\./g) || []).length;
|
||
const gidLevel = dotCount;
|
||
const level = contentLevel != null ? contentLevel : gidLevel;
|
||
// Columns to select
|
||
const propCols = [];
|
||
for (let l = 0; l <= Math.min(level, 5); l++) {
|
||
propCols.push(`NAME_${l}`, `GID_${l}`);
|
||
}
|
||
const allCols = ['geom', ...propCols].filter((c, i, a) => a.indexOf(c) === i);
|
||
const colList = allCols.map(c => `"${c}"`).join(', ');
|
||
try {
|
||
// Query all leaf rows for this GID
|
||
const gidCol = `GID_${gidLevel}`;
|
||
const rows = db.prepare(`SELECT ${colList} FROM "${tableName}" WHERE "${gidCol}" = ?`).all(gadmId);
|
||
if (rows.length === 0)
|
||
return null;
|
||
// Group by content-level GID, merge geometries
|
||
const groupCol = `GID_${level}`;
|
||
const groups = new Map();
|
||
for (const row of rows) {
|
||
if (!row.geom)
|
||
continue;
|
||
const groupKey = row[groupCol] || gadmId;
|
||
if (!groups.has(groupKey)) {
|
||
const props = {};
|
||
for (const col of propCols) {
|
||
if (row[col] != null)
|
||
props[col] = String(row[col]);
|
||
}
|
||
groups.set(groupKey, { props, geoms: [] });
|
||
}
|
||
try {
|
||
const geometry = parseGpkgGeometry(Buffer.from(row.geom));
|
||
groups.get(groupKey).geoms.push(geometry);
|
||
}
|
||
catch (e) {
|
||
console.warn(`[gpkg-reader] Failed to parse geometry for ${groupKey}:`, e);
|
||
}
|
||
}
|
||
// Build features
|
||
const features = [];
|
||
for (const [, { props, geoms }] of groups) {
|
||
if (geoms.length === 0)
|
||
continue;
|
||
const geometry = geoms.length === 1 ? geoms[0] : await mergeGeometries(geoms);
|
||
if (level === gidLevel) {
|
||
props.isOuter = true;
|
||
}
|
||
features.push({ type: 'Feature', properties: props, geometry });
|
||
}
|
||
// Removed includeOuter unshift logic entirely
|
||
let result = { type: 'FeatureCollection', features };
|
||
// Determine tolerance for simplification based on resolution
|
||
let tolerance = 0.0;
|
||
switch (resolution) {
|
||
case 1:
|
||
tolerance = 0.0;
|
||
break;
|
||
case 2:
|
||
tolerance = 0.001;
|
||
break;
|
||
case 3:
|
||
tolerance = 0.002;
|
||
break;
|
||
case 4:
|
||
tolerance = 0.005;
|
||
break;
|
||
case 5:
|
||
tolerance = 0.01;
|
||
break;
|
||
case 6:
|
||
tolerance = 0.05;
|
||
break;
|
||
default:
|
||
tolerance = 0.0;
|
||
break;
|
||
}
|
||
// Simplify and truncate geometries to reduce payload size safely
|
||
const simplifiedFeatures = [];
|
||
for (const feat of result.features) {
|
||
try {
|
||
// recursively simplify and truncate to 5 decimal places matching C++ processing
|
||
feat.geometry.coordinates = optimizeCoordinates(feat.geometry.coordinates, tolerance);
|
||
}
|
||
catch (e) {
|
||
console.warn(`[gpkg-reader] geometry optimization failed for ${gadmId}:`, e);
|
||
}
|
||
simplifiedFeatures.push(feat);
|
||
}
|
||
result = { type: 'FeatureCollection', features: simplifiedFeatures };
|
||
// Enrich features with population and built density data
|
||
for (const feat of result.features) {
|
||
try {
|
||
const enriched = await enrichFeatureWithGHS(feat, { pop: true, built: true });
|
||
Object.assign(feat.properties, enriched);
|
||
}
|
||
catch (err) {
|
||
console.warn(`[gpkg-reader] GHS enrichment failed for ${gadmId}:`, err);
|
||
}
|
||
}
|
||
if (externalCache) {
|
||
try {
|
||
await externalCache.set(cacheKey, result);
|
||
}
|
||
catch (e) { /* ignore */ }
|
||
}
|
||
else {
|
||
const targetDir = uniqueCacheDirs[0];
|
||
const targetFile = join(targetDir, `${cacheKey}.json`);
|
||
try {
|
||
if (!existsSync(targetDir)) {
|
||
mkdirSync(targetDir, { recursive: true });
|
||
}
|
||
console.log('GADM Writing External cache:', targetFile);
|
||
writeFileSync(targetFile, JSON.stringify(result));
|
||
}
|
||
catch (e) {
|
||
console.warn(`[gpkg-reader] Failed to write cache for ${gadmId}:`, e);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
catch (err) {
|
||
console.error("Error reading from GeoPackage:", err);
|
||
return null;
|
||
}
|
||
}
|
||
// ────────── Point in Polygon ──────────
|
||
function pointInRing(pt, ring) {
|
||
let isInside = false;
|
||
const [x, y] = pt;
|
||
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
||
const xi = ring[i][0], yi = ring[i][1];
|
||
const xj = ring[j][0], yj = ring[j][1];
|
||
const intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
|
||
if (intersect)
|
||
isInside = !isInside;
|
||
}
|
||
return isInside;
|
||
}
|
||
function pointInPolygon(pt, polygon) {
|
||
if (polygon.length === 0)
|
||
return false;
|
||
if (!pointInRing(pt, polygon[0]))
|
||
return false;
|
||
for (let i = 1; i < polygon.length; i++) {
|
||
if (pointInRing(pt, polygon[i]))
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
function pointInMultiPolygon(pt, multiPolygon) {
|
||
for (const poly of multiPolygon) {
|
||
if (pointInPolygon(pt, poly))
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
export function containsPoint(geom, pt) {
|
||
if (geom.type === 'Polygon') {
|
||
return pointInPolygon(pt, geom.coordinates);
|
||
}
|
||
else if (geom.type === 'MultiPolygon') {
|
||
return pointInMultiPolygon(pt, geom.coordinates);
|
||
}
|
||
return false;
|
||
}
|
||
/**
|
||
* Perform a spatial query to find the exact GADM hierarchy containing a point.
|
||
*/
|
||
export function getHierarchyByPoint(lat, lon) {
|
||
if (!ensureDb())
|
||
return null;
|
||
const db = sharedDb;
|
||
const tableName = sharedTableName;
|
||
try {
|
||
const rows = db.prepare(`SELECT a.* FROM "${tableName}" a
|
||
JOIN "rtree_${tableName}_geom" b ON a.fid = b.id
|
||
WHERE b.minx <= ? AND b.maxx >= ? AND b.miny <= ? AND b.maxy >= ?`).all(lon, lon, lat, lat);
|
||
for (const row of rows) {
|
||
if (!row.geom)
|
||
continue;
|
||
try {
|
||
const geometry = parseGpkgGeometry(Buffer.from(row.geom));
|
||
if (containsPoint(geometry, [lon, lat])) {
|
||
// Exact match found among candidate bounding boxes!
|
||
const results = [];
|
||
for (let i = 0; i <= 5; i++) {
|
||
const gid = row[`GID_${i}`];
|
||
const name = row[`NAME_${i}`];
|
||
if (gid || name) {
|
||
results.push({
|
||
level: i,
|
||
gadmName: name || null,
|
||
gid: gid || null
|
||
});
|
||
}
|
||
}
|
||
return results;
|
||
}
|
||
}
|
||
catch (e) {
|
||
// Ignore parsing errors for individual geometries
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
catch (err) {
|
||
console.error("Error performing spatial point query against GeoPackage:", err);
|
||
return null;
|
||
}
|
||
}
|
||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3BrZy1yZWFkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZ3BrZy1yZWFkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7OztHQVlHO0FBQ0gsT0FBTyxFQUFFLFVBQVUsRUFBRSxZQUFZLEVBQUUsYUFBYSxFQUFFLFNBQVMsRUFBRSxNQUFNLElBQUksQ0FBQztBQUN4RSxPQUFPLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDOUMsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLEtBQUssQ0FBQztBQUNwQyxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUV2RCxNQUFNLFNBQVMsR0FBRyxhQUFhLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUNqRCxNQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFvQnBDLHlDQUF5QztBQUV6QyxNQUFNLGtCQUFrQixHQUFHO0lBQ3ZCLHVDQUF1QztJQUN2QyxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWM7SUFDMUIsK0JBQStCO0lBQy9CLE9BQU8sQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEVBQUUsb0JBQW9CLENBQUM7SUFDNUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBRSwwQkFBMEIsQ0FBQztDQUNyRCxDQUFDO0FBRUYsSUFBSSxTQUFTLEdBQWtCLElBQUksQ0FBQztBQUVwQywrQ0FBK0M7QUFDL0MsTUFBTSxVQUFVLFdBQVcsQ0FBQyxDQUFTO0lBQ2pDLFNBQVMsR0FBRyxDQUFDLENBQUM7QUFDbEIsQ0FBQztBQUVELFNBQVMsUUFBUTtJQUNiLElBQUksU0FBUyxJQUFJLFVBQVUsQ0FBQyxTQUFTLENBQUM7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUN6RCxLQUFLLE1BQU0sQ0FBQyxJQUFJLGtCQUFrQixFQUFFLENB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
|