feat(dae): first draft
This commit is contained in:
parent
acb70cfa1b
commit
31970b86fc
13 changed files with 1471 additions and 1 deletions
12
.claude/settings.local.json
Normal file
12
.claude/settings.local.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(cat /home/jo/lab/alerte-secours/apps/as-app/src/biz/*.js)",
|
||||
"Bash(npm install)",
|
||||
"Bash(node csv-to-sqlite.mjs --input ../.data/geodae.csv --output ../src/assets/db/geodae.db)",
|
||||
"Bash(sqlite3 /home/jo/lab/alerte-secours/apps/as-app/src/assets/db/geodae.db \"SELECT count\\(*\\) FROM defibs; SELECT * FROM defibs LIMIT 3; SELECT count\\(DISTINCT h3\\) FROM defibs;\")",
|
||||
"Bash(sqlite3 /home/jo/lab/alerte-secours/apps/as-app/src/assets/db/geodae.db \"SELECT id, nom, latitude, longitude FROM defibs WHERE h3 = ''881fb542d3fffff'' LIMIT 5;\")",
|
||||
"Bash(sqlite3 /home/jo/lab/alerte-secours/apps/as-app/src/assets/db/geodae.db \"SELECT h3, count\\(*\\) as cnt FROM defibs GROUP BY h3 ORDER BY cnt DESC LIMIT 5;\")"
|
||||
]
|
||||
}
|
||||
}
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
|
|
@ -100,3 +100,15 @@ android/app/google-services.json
|
|||
!android/app/google-services.example.json
|
||||
|
||||
screenshot-*.png
|
||||
|
||||
/.data
|
||||
|
||||
# Geodae preprocessing
|
||||
scripts/node_modules/
|
||||
scripts/.yarn/*
|
||||
!scripts/.yarn/patches
|
||||
!scripts/.yarn/plugins
|
||||
!scripts/.yarn/releases
|
||||
!scripts/.yarn/sdks
|
||||
!scripts/.yarn/versions
|
||||
src/assets/db/*.db
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const config = {
|
|||
/node_modules\/.*\/android\/build\/intermediates\/(library_jni|merged_jni_libs)\/.*/,
|
||||
]),
|
||||
sourceExts: [...sentryConfig.resolver.sourceExts, "cjs"],
|
||||
assetExts: [...defaultConfig.resolver.assetExts, "ttf"],
|
||||
assetExts: [...defaultConfig.resolver.assetExts, "ttf", "db"],
|
||||
},
|
||||
server: {
|
||||
enhanceMiddleware: (middleware) => {
|
||||
|
|
|
|||
7
scripts/.yarnrc.yml
Normal file
7
scripts/.yarnrc.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
compressionLevel: mixed
|
||||
|
||||
enableGlobalCache: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: ../.yarn/releases/yarn-4.5.3.cjs
|
||||
205
scripts/csv-to-sqlite.mjs
Normal file
205
scripts/csv-to-sqlite.mjs
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
#!/usr/bin/env node
|
||||
// CSV-to-SQLite pipeline for defibrillator data with H3 geo-indexing.
|
||||
// Usage: node csv-to-sqlite.mjs --input <path> --output <path> [--h3res 8] [--delimiter auto] [--batchSize 5000]
|
||||
|
||||
import { createReadStream, readFileSync, existsSync, unlinkSync } from "node:fs";
|
||||
import { createHash } from "node:crypto";
|
||||
import { parseArgs } from "node:util";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { createRequire } from "node:module";
|
||||
import { parse } from "csv-parse";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const Database = require("better-sqlite3");
|
||||
const h3 = require("h3-js");
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CLI args
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const { values: args } = parseArgs({
|
||||
options: {
|
||||
input: { type: "string", short: "i" },
|
||||
output: { type: "string", short: "o" },
|
||||
h3res: { type: "string", default: "8" },
|
||||
delimiter: { type: "string", default: "auto" },
|
||||
batchSize: { type: "string", default: "5000" },
|
||||
},
|
||||
});
|
||||
|
||||
const INPUT = args.input;
|
||||
const OUTPUT = args.output;
|
||||
const H3_RES = parseInt(args.h3res, 10);
|
||||
const BATCH_SIZE = parseInt(args.batchSize, 10);
|
||||
|
||||
if (!INPUT || !OUTPUT) {
|
||||
console.error("Usage: node csv-to-sqlite.mjs --input <csv> --output <db> [--h3res 8] [--delimiter auto] [--batchSize 5000]");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const SCHEMA_PATH = resolve(__dirname, "lib", "schema.sql");
|
||||
|
||||
function detectDelimiter(filePath) {
|
||||
// Read first line to detect delimiter
|
||||
const chunk = readFileSync(filePath, { encoding: "utf-8", end: 4096 });
|
||||
const firstLine = chunk.split(/\r?\n/)[0];
|
||||
const commaCount = (firstLine.match(/,/g) || []).length;
|
||||
const semicolonCount = (firstLine.match(/;/g) || []).length;
|
||||
const detected = semicolonCount > commaCount ? ";" : ",";
|
||||
console.log(`Delimiter auto-detected: "${detected}" (commas=${commaCount}, semicolons=${semicolonCount})`);
|
||||
return detected;
|
||||
}
|
||||
|
||||
function computeH3(lat, lon, res) {
|
||||
return h3.latLngToCell(lat, lon, res);
|
||||
}
|
||||
|
||||
function deterministicId(lat, lon, nom, adresse) {
|
||||
const payload = `${lat}|${lon}|${nom}|${adresse}`;
|
||||
return createHash("sha256").update(payload, "utf-8").digest("hex").slice(0, 16);
|
||||
}
|
||||
|
||||
function cleanFloat(val) {
|
||||
const n = parseFloat(val);
|
||||
return Number.isFinite(n) ? n : null;
|
||||
}
|
||||
|
||||
function cleanInt(val) {
|
||||
const n = parseInt(val, 10);
|
||||
return Number.isFinite(n) ? n : 0;
|
||||
}
|
||||
|
||||
function cleanStr(val) {
|
||||
return (val ?? "").trim();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function main() {
|
||||
const delimiter =
|
||||
args.delimiter === "auto" ? detectDelimiter(INPUT) : args.delimiter;
|
||||
|
||||
// Remove existing output to avoid UNIQUE constraint errors on re-run
|
||||
if (existsSync(OUTPUT)) {
|
||||
unlinkSync(OUTPUT);
|
||||
console.log(`Removed existing DB: ${OUTPUT}`);
|
||||
}
|
||||
|
||||
// Open database with fast-import PRAGMAs
|
||||
const db = new Database(OUTPUT);
|
||||
db.pragma("journal_mode = OFF");
|
||||
db.pragma("synchronous = OFF");
|
||||
db.pragma("temp_store = MEMORY");
|
||||
db.pragma("cache_size = -64000"); // 64 MB
|
||||
db.pragma("locking_mode = EXCLUSIVE");
|
||||
|
||||
// Create schema
|
||||
const schema = readFileSync(SCHEMA_PATH, "utf-8");
|
||||
db.exec(schema);
|
||||
|
||||
// Prepare insert statement
|
||||
const insert = db.prepare(
|
||||
`INSERT OR IGNORE INTO defibs (id, latitude, longitude, nom, adresse, horaires, acces, disponible_24h, h3)
|
||||
VALUES (@id, @latitude, @longitude, @nom, @adresse, @horaires, @acces, @disponible_24h, @h3)`
|
||||
);
|
||||
|
||||
const insertMany = db.transaction((rows) => {
|
||||
for (const row of rows) {
|
||||
insert.run(row);
|
||||
}
|
||||
});
|
||||
|
||||
// Streaming CSV parse
|
||||
const parser = createReadStream(INPUT, { encoding: "utf-8" }).pipe(
|
||||
parse({
|
||||
delimiter,
|
||||
columns: true,
|
||||
skip_empty_lines: true,
|
||||
trim: true,
|
||||
relax_column_count: true,
|
||||
bom: true,
|
||||
quote: '"',
|
||||
escape: '"',
|
||||
})
|
||||
);
|
||||
|
||||
let batch = [];
|
||||
let total = 0;
|
||||
let skipped = 0;
|
||||
|
||||
for await (const record of parser) {
|
||||
const lat = cleanFloat(record.latitude);
|
||||
const lon = cleanFloat(record.longitude);
|
||||
|
||||
// Skip rows with invalid coordinates
|
||||
if (lat === null || lon === null || lat < -90 || lat > 90 || lon < -180 || lon > 180) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const nom = cleanStr(record.nom);
|
||||
const adresse = cleanStr(record.adresse);
|
||||
const horaires = cleanStr(record.horaires);
|
||||
const acces = cleanStr(record.acces);
|
||||
const disponible_24h = cleanInt(record.disponible_24h);
|
||||
const id = deterministicId(lat, lon, nom, adresse);
|
||||
const h3Cell = computeH3(lat, lon, H3_RES);
|
||||
|
||||
batch.push({
|
||||
id,
|
||||
latitude: lat,
|
||||
longitude: lon,
|
||||
nom,
|
||||
adresse,
|
||||
horaires,
|
||||
acces,
|
||||
disponible_24h,
|
||||
h3: h3Cell,
|
||||
});
|
||||
|
||||
if (batch.length >= BATCH_SIZE) {
|
||||
insertMany(batch);
|
||||
total += batch.length;
|
||||
batch = [];
|
||||
process.stdout.write(`\rInserted ${total} rows...`);
|
||||
}
|
||||
}
|
||||
|
||||
// Flush remaining
|
||||
if (batch.length > 0) {
|
||||
insertMany(batch);
|
||||
total += batch.length;
|
||||
}
|
||||
|
||||
console.log(`\nDone: ${total} rows inserted, ${skipped} skipped.`);
|
||||
|
||||
// Restore safe PRAGMAs for the shipped DB
|
||||
db.pragma("journal_mode = DELETE");
|
||||
db.pragma("synchronous = NORMAL");
|
||||
|
||||
// VACUUM to compact
|
||||
db.exec("VACUUM");
|
||||
|
||||
// Final stats
|
||||
const count = db.prepare("SELECT count(*) AS cnt FROM defibs").get();
|
||||
const pageCount = db.pragma("page_count", { simple: true });
|
||||
const pageSize = db.pragma("page_size", { simple: true });
|
||||
const sizeBytes = pageCount * pageSize;
|
||||
console.log(`DB rows: ${count.cnt}`);
|
||||
console.log(`DB size: ${(sizeBytes / 1024 / 1024).toFixed(2)} MB`);
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
15
scripts/lib/schema.sql
Normal file
15
scripts/lib/schema.sql
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
CREATE TABLE IF NOT EXISTS defibs (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
latitude REAL NOT NULL,
|
||||
longitude REAL NOT NULL,
|
||||
nom TEXT NOT NULL DEFAULT '',
|
||||
adresse TEXT NOT NULL DEFAULT '',
|
||||
horaires TEXT NOT NULL DEFAULT '',
|
||||
acces TEXT NOT NULL DEFAULT '',
|
||||
disponible_24h INTEGER NOT NULL DEFAULT 0,
|
||||
h3 TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_defibs_h3 ON defibs (h3);
|
||||
CREATE INDEX IF NOT EXISTS idx_defibs_latlon ON defibs (latitude, longitude);
|
||||
CREATE INDEX IF NOT EXISTS idx_defibs_dispo ON defibs (disponible_24h);
|
||||
16
scripts/package.json
Normal file
16
scripts/package.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "geodae-pipeline",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "yarn@4.5.3",
|
||||
"scripts": {
|
||||
"build-db": "node csv-to-sqlite.mjs --input ../.data/geodae.csv --output ../src/assets/db/geodae.db",
|
||||
"build-db:semicolon": "node csv-to-sqlite.mjs --input ../.data/geodae.csv --output ../src/assets/db/geodae.db --delimiter ';'"
|
||||
},
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^11.7.0",
|
||||
"csv-parse": "^5.6.0",
|
||||
"h3-js": "^4.2.1"
|
||||
}
|
||||
}
|
||||
889
scripts/yarn.lock
Normal file
889
scripts/yarn.lock
Normal file
|
|
@ -0,0 +1,889 @@
|
|||
# This file is generated by running "yarn install" inside your project.
|
||||
# Manual changes might be lost - proceed with caution!
|
||||
|
||||
__metadata:
|
||||
version: 8
|
||||
cacheKey: 10
|
||||
|
||||
"@gar/promise-retry@npm:^1.0.0":
|
||||
version: 1.0.2
|
||||
resolution: "@gar/promise-retry@npm:1.0.2"
|
||||
dependencies:
|
||||
retry: "npm:^0.13.1"
|
||||
checksum: 10/b91326999ce94677cbe91973079eabc689761a93a045f6a2d34d4070e9305b27f6c54e4021688c7080cb14caf89eafa0c0f300af741b94c20d18608bdb66ca46
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@isaacs/fs-minipass@npm:^4.0.0":
|
||||
version: 4.0.1
|
||||
resolution: "@isaacs/fs-minipass@npm:4.0.1"
|
||||
dependencies:
|
||||
minipass: "npm:^7.0.4"
|
||||
checksum: 10/4412e9e6713c89c1e66d80bb0bb5a2a93192f10477623a27d08f228ba0316bb880affabc5bfe7f838f58a34d26c2c190da726e576cdfc18c49a72e89adabdcf5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@npmcli/agent@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "@npmcli/agent@npm:4.0.0"
|
||||
dependencies:
|
||||
agent-base: "npm:^7.1.0"
|
||||
http-proxy-agent: "npm:^7.0.0"
|
||||
https-proxy-agent: "npm:^7.0.1"
|
||||
lru-cache: "npm:^11.2.1"
|
||||
socks-proxy-agent: "npm:^8.0.3"
|
||||
checksum: 10/1a81573becc60515031accc696e6405e9b894e65c12b98ef4aeee03b5617c41948633159dbf6caf5dde5b47367eeb749bdc7b7dfb21960930a9060a935c6f636
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@npmcli/fs@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "@npmcli/fs@npm:5.0.0"
|
||||
dependencies:
|
||||
semver: "npm:^7.3.5"
|
||||
checksum: 10/4935c7719d17830d0f9fa46c50be17b2a3c945cec61760f6d0909bce47677c42e1810ca673305890f9e84f008ec4d8e841182f371e42100a8159d15f22249208
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"abbrev@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "abbrev@npm:4.0.0"
|
||||
checksum: 10/e2f0c6a6708ad738b3e8f50233f4800de31ad41a6cdc50e0cbe51b76fed69fd0213516d92c15ce1a9985fca71a14606a9be22bf00f8475a58987b9bfb671c582
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2":
|
||||
version: 7.1.4
|
||||
resolution: "agent-base@npm:7.1.4"
|
||||
checksum: 10/79bef167247789f955aaba113bae74bf64aa1e1acca4b1d6bb444bdf91d82c3e07e9451ef6a6e2e35e8f71a6f97ce33e3d855a5328eb9fad1bc3cc4cfd031ed8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"balanced-match@npm:^4.0.2":
|
||||
version: 4.0.4
|
||||
resolution: "balanced-match@npm:4.0.4"
|
||||
checksum: 10/fb07bb66a0959c2843fc055838047e2a95ccebb837c519614afb067ebfdf2fa967ca8d712c35ced07f2cd26fc6f07964230b094891315ad74f11eba3d53178a0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"base64-js@npm:^1.3.1":
|
||||
version: 1.5.1
|
||||
resolution: "base64-js@npm:1.5.1"
|
||||
checksum: 10/669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"better-sqlite3@npm:^11.7.0":
|
||||
version: 11.10.0
|
||||
resolution: "better-sqlite3@npm:11.10.0"
|
||||
dependencies:
|
||||
bindings: "npm:^1.5.0"
|
||||
node-gyp: "npm:latest"
|
||||
prebuild-install: "npm:^7.1.1"
|
||||
checksum: 10/5e4c7437c4fe6033335a79c82974d7ab29f33c51c36f48b73e87e087d21578468575de1c56a7badd4f76f17255e25abefddaeacf018e5eeb9e0cb8d6e3e4a5e1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bindings@npm:^1.5.0":
|
||||
version: 1.5.0
|
||||
resolution: "bindings@npm:1.5.0"
|
||||
dependencies:
|
||||
file-uri-to-path: "npm:1.0.0"
|
||||
checksum: 10/593d5ae975ffba15fbbb4788fe5abd1e125afbab849ab967ab43691d27d6483751805d98cb92f7ac24a2439a8a8678cd0131c535d5d63de84e383b0ce2786133
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bl@npm:^4.0.3":
|
||||
version: 4.1.0
|
||||
resolution: "bl@npm:4.1.0"
|
||||
dependencies:
|
||||
buffer: "npm:^5.5.0"
|
||||
inherits: "npm:^2.0.4"
|
||||
readable-stream: "npm:^3.4.0"
|
||||
checksum: 10/b7904e66ed0bdfc813c06ea6c3e35eafecb104369dbf5356d0f416af90c1546de3b74e5b63506f0629acf5e16a6f87c3798f16233dcff086e9129383aa02ab55
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"brace-expansion@npm:^5.0.2":
|
||||
version: 5.0.4
|
||||
resolution: "brace-expansion@npm:5.0.4"
|
||||
dependencies:
|
||||
balanced-match: "npm:^4.0.2"
|
||||
checksum: 10/cfd57e20d8ded9578149e47ae4d3fff2b2f78d06b54a32a73057bddff65c8e9b930613f0cbcfefedf12dd117151e19d4da16367d5127c54f3bff02d8a4479bb2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"buffer@npm:^5.5.0":
|
||||
version: 5.7.1
|
||||
resolution: "buffer@npm:5.7.1"
|
||||
dependencies:
|
||||
base64-js: "npm:^1.3.1"
|
||||
ieee754: "npm:^1.1.13"
|
||||
checksum: 10/997434d3c6e3b39e0be479a80288875f71cd1c07d75a3855e6f08ef848a3c966023f79534e22e415ff3a5112708ce06127277ab20e527146d55c84566405c7c6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cacache@npm:^20.0.1":
|
||||
version: 20.0.3
|
||||
resolution: "cacache@npm:20.0.3"
|
||||
dependencies:
|
||||
"@npmcli/fs": "npm:^5.0.0"
|
||||
fs-minipass: "npm:^3.0.0"
|
||||
glob: "npm:^13.0.0"
|
||||
lru-cache: "npm:^11.1.0"
|
||||
minipass: "npm:^7.0.3"
|
||||
minipass-collect: "npm:^2.0.1"
|
||||
minipass-flush: "npm:^1.0.5"
|
||||
minipass-pipeline: "npm:^1.2.4"
|
||||
p-map: "npm:^7.0.2"
|
||||
ssri: "npm:^13.0.0"
|
||||
unique-filename: "npm:^5.0.0"
|
||||
checksum: 10/388a0169970df9d051da30437f93f81b7e91efb570ad0ff2b8fde33279fbe726c1bc8e8e2b9c05053ffb4f563854c73db395e8712e3b62347a1bc4f7fb8899ff
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chownr@npm:^1.1.1":
|
||||
version: 1.1.4
|
||||
resolution: "chownr@npm:1.1.4"
|
||||
checksum: 10/115648f8eb38bac5e41c3857f3e663f9c39ed6480d1349977c4d96c95a47266fcacc5a5aabf3cb6c481e22d72f41992827db47301851766c4fd77ac21a4f081d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chownr@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "chownr@npm:3.0.0"
|
||||
checksum: 10/b63cb1f73d171d140a2ed8154ee6566c8ab775d3196b0e03a2a94b5f6a0ce7777ee5685ca56849403c8d17bd457a6540672f9a60696a6137c7a409097495b82c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"csv-parse@npm:^5.6.0":
|
||||
version: 5.6.0
|
||||
resolution: "csv-parse@npm:5.6.0"
|
||||
checksum: 10/4c82e11f50ae0ccbac2aed716ef2502d0468bf96552083561db789fc0258ee4bb0a30106fcfb2684f153cb4042f0413e0eac3645d5466874803b7ccdeba67ac8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"debug@npm:4, debug@npm:^4.3.4":
|
||||
version: 4.4.3
|
||||
resolution: "debug@npm:4.4.3"
|
||||
dependencies:
|
||||
ms: "npm:^2.1.3"
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
checksum: 10/9ada3434ea2993800bd9a1e320bd4aa7af69659fb51cca685d390949434bc0a8873c21ed7c9b852af6f2455a55c6d050aa3937d52b3c69f796dab666f762acad
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"decompress-response@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "decompress-response@npm:6.0.0"
|
||||
dependencies:
|
||||
mimic-response: "npm:^3.1.0"
|
||||
checksum: 10/d377cf47e02d805e283866c3f50d3d21578b779731e8c5072d6ce8c13cc31493db1c2f6784da9d1d5250822120cefa44f1deab112d5981015f2e17444b763812
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"deep-extend@npm:^0.6.0":
|
||||
version: 0.6.0
|
||||
resolution: "deep-extend@npm:0.6.0"
|
||||
checksum: 10/7be7e5a8d468d6b10e6a67c3de828f55001b6eb515d014f7aeb9066ce36bd5717161eb47d6a0f7bed8a9083935b465bc163ee2581c8b128d29bf61092fdf57a7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"detect-libc@npm:^2.0.0":
|
||||
version: 2.1.2
|
||||
resolution: "detect-libc@npm:2.1.2"
|
||||
checksum: 10/b736c8d97d5d46164c0d1bed53eb4e6a3b1d8530d460211e2d52f1c552875e706c58a5376854e4e54f8b828c9cada58c855288c968522eb93ac7696d65970766
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1":
|
||||
version: 1.4.5
|
||||
resolution: "end-of-stream@npm:1.4.5"
|
||||
dependencies:
|
||||
once: "npm:^1.4.0"
|
||||
checksum: 10/1e0cfa6e7f49887544e03314f9dfc56a8cb6dde910cbb445983ecc2ff426fc05946df9d75d8a21a3a64f2cecfe1bf88f773952029f46756b2ed64a24e95b1fb8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"env-paths@npm:^2.2.0":
|
||||
version: 2.2.1
|
||||
resolution: "env-paths@npm:2.2.1"
|
||||
checksum: 10/65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"expand-template@npm:^2.0.3":
|
||||
version: 2.0.3
|
||||
resolution: "expand-template@npm:2.0.3"
|
||||
checksum: 10/588c19847216421ed92befb521767b7018dc88f88b0576df98cb242f20961425e96a92cbece525ef28cc5becceae5d544ae0f5b9b5e2aa05acb13716ca5b3099
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"exponential-backoff@npm:^3.1.1":
|
||||
version: 3.1.3
|
||||
resolution: "exponential-backoff@npm:3.1.3"
|
||||
checksum: 10/ca25962b4bbab943b7c4ed0b5228e263833a5063c65e1cdeac4be9afad350aae5466e8e619b5051f4f8d37b2144a2d6e8fcc771b6cc82934f7dade2f964f652c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fdir@npm:^6.5.0":
|
||||
version: 6.5.0
|
||||
resolution: "fdir@npm:6.5.0"
|
||||
peerDependencies:
|
||||
picomatch: ^3 || ^4
|
||||
peerDependenciesMeta:
|
||||
picomatch:
|
||||
optional: true
|
||||
checksum: 10/14ca1c9f0a0e8f4f2e9bf4e8551065a164a09545dae548c12a18d238b72e51e5a7b39bd8e5494b56463a0877672d0a6c1ef62c6fa0677db1b0c847773be939b1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"file-uri-to-path@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "file-uri-to-path@npm:1.0.0"
|
||||
checksum: 10/b648580bdd893a008c92c7ecc96c3ee57a5e7b6c4c18a9a09b44fb5d36d79146f8e442578bc0e173dc027adf3987e254ba1dfd6e3ec998b7c282873010502144
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fs-constants@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "fs-constants@npm:1.0.0"
|
||||
checksum: 10/18f5b718371816155849475ac36c7d0b24d39a11d91348cfcb308b4494824413e03572c403c86d3a260e049465518c4f0d5bd00f0371cdfcad6d4f30a85b350d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fs-minipass@npm:^3.0.0":
|
||||
version: 3.0.3
|
||||
resolution: "fs-minipass@npm:3.0.3"
|
||||
dependencies:
|
||||
minipass: "npm:^7.0.3"
|
||||
checksum: 10/af143246cf6884fe26fa281621d45cfe111d34b30535a475bfa38dafe343dadb466c047a924ffc7d6b7b18265df4110224ce3803806dbb07173bf2087b648d7f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"geodae-pipeline@workspace:.":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "geodae-pipeline@workspace:."
|
||||
dependencies:
|
||||
better-sqlite3: "npm:^11.7.0"
|
||||
csv-parse: "npm:^5.6.0"
|
||||
h3-js: "npm:^4.2.1"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"github-from-package@npm:0.0.0":
|
||||
version: 0.0.0
|
||||
resolution: "github-from-package@npm:0.0.0"
|
||||
checksum: 10/2a091ba07fbce22205642543b4ea8aaf068397e1433c00ae0f9de36a3607baf5bcc14da97fbb798cfca6393b3c402031fca06d8b491a44206d6efef391c58537
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob@npm:^13.0.0":
|
||||
version: 13.0.6
|
||||
resolution: "glob@npm:13.0.6"
|
||||
dependencies:
|
||||
minimatch: "npm:^10.2.2"
|
||||
minipass: "npm:^7.1.3"
|
||||
path-scurry: "npm:^2.0.2"
|
||||
checksum: 10/201ad69e5f0aa74e1d8c00a481581f8b8c804b6a4fbfabeeb8541f5d756932800331daeba99b58fb9e4cd67e12ba5a7eba5b82fb476691588418060b84353214
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"graceful-fs@npm:^4.2.6":
|
||||
version: 4.2.11
|
||||
resolution: "graceful-fs@npm:4.2.11"
|
||||
checksum: 10/bf152d0ed1dc159239db1ba1f74fdbc40cb02f626770dcd5815c427ce0688c2635a06ed69af364396da4636d0408fcf7d4afdf7881724c3307e46aff30ca49e2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"h3-js@npm:^4.2.1":
|
||||
version: 4.4.0
|
||||
resolution: "h3-js@npm:4.4.0"
|
||||
checksum: 10/6db6888f143ed6a1e3ca10506f15c35679afd181e24b71bcdc90259206e3f02637bab38e2a35382d51f17151ea193dfab69c01ff3e31bf0e86abfb1957692576
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"http-cache-semantics@npm:^4.1.1":
|
||||
version: 4.2.0
|
||||
resolution: "http-cache-semantics@npm:4.2.0"
|
||||
checksum: 10/4efd2dfcfeea9d5e88c84af450b9980be8a43c2c8179508b1c57c7b4421c855f3e8efe92fa53e0b3f4a43c85824ada930eabbc306d1b3beab750b6dcc5187693
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"http-proxy-agent@npm:^7.0.0":
|
||||
version: 7.0.2
|
||||
resolution: "http-proxy-agent@npm:7.0.2"
|
||||
dependencies:
|
||||
agent-base: "npm:^7.1.0"
|
||||
debug: "npm:^4.3.4"
|
||||
checksum: 10/d062acfa0cb82beeb558f1043c6ba770ea892b5fb7b28654dbc70ea2aeea55226dd34c02a294f6c1ca179a5aa483c4ea641846821b182edbd9cc5d89b54c6848
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"https-proxy-agent@npm:^7.0.1":
|
||||
version: 7.0.6
|
||||
resolution: "https-proxy-agent@npm:7.0.6"
|
||||
dependencies:
|
||||
agent-base: "npm:^7.1.2"
|
||||
debug: "npm:4"
|
||||
checksum: 10/784b628cbd55b25542a9d85033bdfd03d4eda630fb8b3c9477959367f3be95dc476ed2ecbb9836c359c7c698027fc7b45723a302324433590f45d6c1706e8c13
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"iconv-lite@npm:^0.7.2":
|
||||
version: 0.7.2
|
||||
resolution: "iconv-lite@npm:0.7.2"
|
||||
dependencies:
|
||||
safer-buffer: "npm:>= 2.1.2 < 3.0.0"
|
||||
checksum: 10/24c937b532f868e938386b62410b303b7c767ce3d08dc2829cbe59464d5a26ef86ae5ad1af6b34eec43ddfea39e7d101638644b0178d67262fa87015d59f983a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ieee754@npm:^1.1.13":
|
||||
version: 1.2.1
|
||||
resolution: "ieee754@npm:1.2.1"
|
||||
checksum: 10/d9f2557a59036f16c282aaeb107832dc957a93d73397d89bbad4eb1130560560eb695060145e8e6b3b498b15ab95510226649a0b8f52ae06583575419fe10fc4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"imurmurhash@npm:^0.1.4":
|
||||
version: 0.1.4
|
||||
resolution: "imurmurhash@npm:0.1.4"
|
||||
checksum: 10/2d30b157a91fe1c1d7c6f653cbf263f039be6c5bfa959245a16d4ee191fc0f2af86c08545b6e6beeb041c56b574d2d5b9f95343d378ab49c0f37394d541e7fc8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"inherits@npm:^2.0.3, inherits@npm:^2.0.4":
|
||||
version: 2.0.4
|
||||
resolution: "inherits@npm:2.0.4"
|
||||
checksum: 10/cd45e923bee15186c07fa4c89db0aace24824c482fb887b528304694b2aa6ff8a898da8657046a5dcf3e46cd6db6c61629551f9215f208d7c3f157cf9b290521
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ini@npm:~1.3.0":
|
||||
version: 1.3.8
|
||||
resolution: "ini@npm:1.3.8"
|
||||
checksum: 10/314ae176e8d4deb3def56106da8002b462221c174ddb7ce0c49ee72c8cd1f9044f7b10cc555a7d8850982c3b9ca96fc212122749f5234bc2b6fb05fb942ed566
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ip-address@npm:^10.0.1":
|
||||
version: 10.1.0
|
||||
resolution: "ip-address@npm:10.1.0"
|
||||
checksum: 10/a6979629d1ad9c1fb424bc25182203fad739b40225aebc55ec6243bbff5035faf7b9ed6efab3a097de6e713acbbfde944baacfa73e11852bb43989c45a68d79e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"isexe@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "isexe@npm:4.0.0"
|
||||
checksum: 10/2ead327ef596042ef9c9ec5f236b316acfaedb87f4bb61b3c3d574fb2e9c8a04b67305e04733bde52c24d9622fdebd3270aadb632adfbf9cadef88fe30f479e5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.1":
|
||||
version: 11.2.6
|
||||
resolution: "lru-cache@npm:11.2.6"
|
||||
checksum: 10/91222bbd59f793a0a0ad57789388f06b34ac9bb1613433c1d1810457d09db5cd3ec8943227ce2e1f5d6a0a15d6f1a9f129cb2c49ae9b6b10e82d4965fddecbef
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"make-fetch-happen@npm:^15.0.0":
|
||||
version: 15.0.4
|
||||
resolution: "make-fetch-happen@npm:15.0.4"
|
||||
dependencies:
|
||||
"@gar/promise-retry": "npm:^1.0.0"
|
||||
"@npmcli/agent": "npm:^4.0.0"
|
||||
cacache: "npm:^20.0.1"
|
||||
http-cache-semantics: "npm:^4.1.1"
|
||||
minipass: "npm:^7.0.2"
|
||||
minipass-fetch: "npm:^5.0.0"
|
||||
minipass-flush: "npm:^1.0.5"
|
||||
minipass-pipeline: "npm:^1.2.4"
|
||||
negotiator: "npm:^1.0.0"
|
||||
proc-log: "npm:^6.0.0"
|
||||
ssri: "npm:^13.0.0"
|
||||
checksum: 10/4aa75baab500eff4259f2e1a3e76cf01ab3a3cd750037e4bd7b5e22bc5a60f12cc766b3c45e6288accb5ab609e88de5019a8014e0f96f6594b7b03cb504f4b81
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mimic-response@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "mimic-response@npm:3.1.0"
|
||||
checksum: 10/7e719047612411fe071332a7498cf0448bbe43c485c0d780046c76633a771b223ff49bd00267be122cedebb897037fdb527df72335d0d0f74724604ca70b37ad
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimatch@npm:^10.2.2":
|
||||
version: 10.2.4
|
||||
resolution: "minimatch@npm:10.2.4"
|
||||
dependencies:
|
||||
brace-expansion: "npm:^5.0.2"
|
||||
checksum: 10/aea4874e521c55bb60744685bbffe3d152e5460f84efac3ea936e6bbe2ceba7deb93345fec3f9bb17f7b6946776073a64d40ae32bf5f298ad690308121068a1f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimist@npm:^1.2.0, minimist@npm:^1.2.3":
|
||||
version: 1.2.8
|
||||
resolution: "minimist@npm:1.2.8"
|
||||
checksum: 10/908491b6cc15a6c440ba5b22780a0ba89b9810e1aea684e253e43c4e3b8d56ec1dcdd7ea96dde119c29df59c936cde16062159eae4225c691e19c70b432b6e6f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minipass-collect@npm:^2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "minipass-collect@npm:2.0.1"
|
||||
dependencies:
|
||||
minipass: "npm:^7.0.3"
|
||||
checksum: 10/b251bceea62090f67a6cced7a446a36f4cd61ee2d5cea9aee7fff79ba8030e416327a1c5aa2908dc22629d06214b46d88fdab8c51ac76bacbf5703851b5ad342
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minipass-fetch@npm:^5.0.0":
|
||||
version: 5.0.2
|
||||
resolution: "minipass-fetch@npm:5.0.2"
|
||||
dependencies:
|
||||
iconv-lite: "npm:^0.7.2"
|
||||
minipass: "npm:^7.0.3"
|
||||
minipass-sized: "npm:^2.0.0"
|
||||
minizlib: "npm:^3.0.1"
|
||||
dependenciesMeta:
|
||||
iconv-lite:
|
||||
optional: true
|
||||
checksum: 10/4f3f65ea5b20a3a287765ebf21cc73e62031f754944272df2a3039296cc75a8fc2dc50b8a3c4f39ce3ac6e5cc583e8dc664d12c6ab98e0883d263e49f344bc86
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minipass-flush@npm:^1.0.5":
|
||||
version: 1.0.5
|
||||
resolution: "minipass-flush@npm:1.0.5"
|
||||
dependencies:
|
||||
minipass: "npm:^3.0.0"
|
||||
checksum: 10/56269a0b22bad756a08a94b1ffc36b7c9c5de0735a4dd1ab2b06c066d795cfd1f0ac44a0fcae13eece5589b908ecddc867f04c745c7009be0b566421ea0944cf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minipass-pipeline@npm:^1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "minipass-pipeline@npm:1.2.4"
|
||||
dependencies:
|
||||
minipass: "npm:^3.0.0"
|
||||
checksum: 10/b14240dac0d29823c3d5911c286069e36d0b81173d7bdf07a7e4a91ecdef92cdff4baaf31ea3746f1c61e0957f652e641223970870e2353593f382112257971b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minipass-sized@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "minipass-sized@npm:2.0.0"
|
||||
dependencies:
|
||||
minipass: "npm:^7.1.2"
|
||||
checksum: 10/3b89adf64ca705662f77481e278eff5ec0a57aeffb5feba7cc8843722b1e7770efc880f2a17d1d4877b2d7bf227873cd46afb4da44c0fd18088b601ea50f96bb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minipass@npm:^3.0.0":
|
||||
version: 3.3.6
|
||||
resolution: "minipass@npm:3.3.6"
|
||||
dependencies:
|
||||
yallist: "npm:^4.0.0"
|
||||
checksum: 10/a5c6ef069f70d9a524d3428af39f2b117ff8cd84172e19b754e7264a33df460873e6eb3d6e55758531580970de50ae950c496256bb4ad3691a2974cddff189f0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2, minipass@npm:^7.1.3":
|
||||
version: 7.1.3
|
||||
resolution: "minipass@npm:7.1.3"
|
||||
checksum: 10/175e4d5e20980c3cd316ae82d2c031c42f6c746467d8b1905b51060a0ba4461441a0c25bb67c025fd9617f9a3873e152c7b543c6b5ac83a1846be8ade80dffd6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minizlib@npm:^3.0.1, minizlib@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "minizlib@npm:3.1.0"
|
||||
dependencies:
|
||||
minipass: "npm:^7.1.2"
|
||||
checksum: 10/f47365cc2cb7f078cbe7e046eb52655e2e7e97f8c0a9a674f4da60d94fb0624edfcec9b5db32e8ba5a99a5f036f595680ae6fe02a262beaa73026e505cc52f99
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mkdirp-classic@npm:^0.5.2, mkdirp-classic@npm:^0.5.3":
|
||||
version: 0.5.3
|
||||
resolution: "mkdirp-classic@npm:0.5.3"
|
||||
checksum: 10/3f4e088208270bbcc148d53b73e9a5bd9eef05ad2cbf3b3d0ff8795278d50dd1d11a8ef1875ff5aea3fa888931f95bfcb2ad5b7c1061cfefd6284d199e6776ac
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ms@npm:^2.1.3":
|
||||
version: 2.1.3
|
||||
resolution: "ms@npm:2.1.3"
|
||||
checksum: 10/aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"napi-build-utils@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "napi-build-utils@npm:2.0.0"
|
||||
checksum: 10/69adcdb828481737f1ec64440286013f6479d5b264e24d5439ba795f65293d0bb6d962035de07c65fae525ed7d2fcd0baab6891d8e3734ea792fec43918acf83
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"negotiator@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "negotiator@npm:1.0.0"
|
||||
checksum: 10/b5734e87295324fabf868e36fb97c84b7d7f3156ec5f4ee5bf6e488079c11054f818290fc33804cef7b1ee21f55eeb14caea83e7dafae6492a409b3e573153e5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-abi@npm:^3.3.0":
|
||||
version: 3.87.0
|
||||
resolution: "node-abi@npm:3.87.0"
|
||||
dependencies:
|
||||
semver: "npm:^7.3.5"
|
||||
checksum: 10/3c7beafed49d9486b4bd95166bcf182b26d4aafa63c8620d1b3bd70a740fc256e1789453cfd83653b6efa7d259f0b1a34eaa95a6c62e74974ad99129bc78842f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-gyp@npm:latest":
|
||||
version: 12.2.0
|
||||
resolution: "node-gyp@npm:12.2.0"
|
||||
dependencies:
|
||||
env-paths: "npm:^2.2.0"
|
||||
exponential-backoff: "npm:^3.1.1"
|
||||
graceful-fs: "npm:^4.2.6"
|
||||
make-fetch-happen: "npm:^15.0.0"
|
||||
nopt: "npm:^9.0.0"
|
||||
proc-log: "npm:^6.0.0"
|
||||
semver: "npm:^7.3.5"
|
||||
tar: "npm:^7.5.4"
|
||||
tinyglobby: "npm:^0.2.12"
|
||||
which: "npm:^6.0.0"
|
||||
bin:
|
||||
node-gyp: bin/node-gyp.js
|
||||
checksum: 10/4ebab5b77585a637315e969c2274b5520562473fe75de850639a580c2599652fb9f33959ec782ea45a2e149d8f04b548030f472eeeb3dbdf19a7f2ccbc30b908
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nopt@npm:^9.0.0":
|
||||
version: 9.0.0
|
||||
resolution: "nopt@npm:9.0.0"
|
||||
dependencies:
|
||||
abbrev: "npm:^4.0.0"
|
||||
bin:
|
||||
nopt: bin/nopt.js
|
||||
checksum: 10/56a1ccd2ad711fb5115918e2c96828703cddbe12ba2c3bd00591758f6fa30e6f47dd905c59dbfcf9b773f3a293b45996609fb6789ae29d6bfcc3cf3a6f7d9fda
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"once@npm:^1.3.1, once@npm:^1.4.0":
|
||||
version: 1.4.0
|
||||
resolution: "once@npm:1.4.0"
|
||||
dependencies:
|
||||
wrappy: "npm:1"
|
||||
checksum: 10/cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-map@npm:^7.0.2":
|
||||
version: 7.0.4
|
||||
resolution: "p-map@npm:7.0.4"
|
||||
checksum: 10/ef48c3b2e488f31c693c9fcc0df0ef76518cf6426a495cf9486ebbb0fd7f31aef7f90e96f72e0070c0ff6e3177c9318f644b512e2c29e3feee8d7153fcb6782e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"path-scurry@npm:^2.0.2":
|
||||
version: 2.0.2
|
||||
resolution: "path-scurry@npm:2.0.2"
|
||||
dependencies:
|
||||
lru-cache: "npm:^11.0.0"
|
||||
minipass: "npm:^7.1.2"
|
||||
checksum: 10/2b4257422bcb870a4c2d205b3acdbb213a72f5e2250f61c80f79c9d014d010f82bdf8584441612c8e1fa4eb098678f5704a66fa8377d72646bad4be38e57a2c3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"picomatch@npm:^4.0.3":
|
||||
version: 4.0.3
|
||||
resolution: "picomatch@npm:4.0.3"
|
||||
checksum: 10/57b99055f40b16798f2802916d9c17e9744e620a0db136554af01d19598b96e45e2f00014c91d1b8b13874b80caa8c295b3d589a3f72373ec4aaf54baa5962d5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prebuild-install@npm:^7.1.1":
|
||||
version: 7.1.3
|
||||
resolution: "prebuild-install@npm:7.1.3"
|
||||
dependencies:
|
||||
detect-libc: "npm:^2.0.0"
|
||||
expand-template: "npm:^2.0.3"
|
||||
github-from-package: "npm:0.0.0"
|
||||
minimist: "npm:^1.2.3"
|
||||
mkdirp-classic: "npm:^0.5.3"
|
||||
napi-build-utils: "npm:^2.0.0"
|
||||
node-abi: "npm:^3.3.0"
|
||||
pump: "npm:^3.0.0"
|
||||
rc: "npm:^1.2.7"
|
||||
simple-get: "npm:^4.0.0"
|
||||
tar-fs: "npm:^2.0.0"
|
||||
tunnel-agent: "npm:^0.6.0"
|
||||
bin:
|
||||
prebuild-install: bin.js
|
||||
checksum: 10/1b7e4c00d2750b532a4fc2a83ffb0c5fefa1b6f2ad071896ead15eeadc3255f5babd816949991af083cf7429e375ae8c7d1c51f73658559da36f948a020a3a11
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"proc-log@npm:^6.0.0":
|
||||
version: 6.1.0
|
||||
resolution: "proc-log@npm:6.1.0"
|
||||
checksum: 10/9033f30f168ed5a0991b773d0c50ff88384c4738e9a0a67d341de36bf7293771eed648ab6a0562f62276da12fde91f3bbfc75ffff6e71ad49aafd74fc646be66
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pump@npm:^3.0.0":
|
||||
version: 3.0.4
|
||||
resolution: "pump@npm:3.0.4"
|
||||
dependencies:
|
||||
end-of-stream: "npm:^1.1.0"
|
||||
once: "npm:^1.3.1"
|
||||
checksum: 10/d043c3e710c56ffd280711e98a94e863ab334f79ea43cee0fb70e1349b2355ffd2ff287c7522e4c960a247699d5b7825f00fa090b85d6179c973be13f78a6c49
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rc@npm:^1.2.7":
|
||||
version: 1.2.8
|
||||
resolution: "rc@npm:1.2.8"
|
||||
dependencies:
|
||||
deep-extend: "npm:^0.6.0"
|
||||
ini: "npm:~1.3.0"
|
||||
minimist: "npm:^1.2.0"
|
||||
strip-json-comments: "npm:~2.0.1"
|
||||
bin:
|
||||
rc: ./cli.js
|
||||
checksum: 10/5c4d72ae7eec44357171585938c85ce066da8ca79146b5635baf3d55d74584c92575fa4e2c9eac03efbed3b46a0b2e7c30634c012b4b4fa40d654353d3c163eb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0":
|
||||
version: 3.6.2
|
||||
resolution: "readable-stream@npm:3.6.2"
|
||||
dependencies:
|
||||
inherits: "npm:^2.0.3"
|
||||
string_decoder: "npm:^1.1.1"
|
||||
util-deprecate: "npm:^1.0.1"
|
||||
checksum: 10/d9e3e53193adcdb79d8f10f2a1f6989bd4389f5936c6f8b870e77570853561c362bee69feca2bbb7b32368ce96a85504aa4cedf7cf80f36e6a9de30d64244048
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"retry@npm:^0.13.1":
|
||||
version: 0.13.1
|
||||
resolution: "retry@npm:0.13.1"
|
||||
checksum: 10/6125ec2e06d6e47e9201539c887defba4e47f63471db304c59e4b82fc63c8e89ca06a77e9d34939a9a42a76f00774b2f46c0d4a4cbb3e287268bd018ed69426d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0":
|
||||
version: 5.2.1
|
||||
resolution: "safe-buffer@npm:5.2.1"
|
||||
checksum: 10/32872cd0ff68a3ddade7a7617b8f4c2ae8764d8b7d884c651b74457967a9e0e886267d3ecc781220629c44a865167b61c375d2da6c720c840ecd73f45d5d9451
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"safer-buffer@npm:>= 2.1.2 < 3.0.0":
|
||||
version: 2.1.2
|
||||
resolution: "safer-buffer@npm:2.1.2"
|
||||
checksum: 10/7eaf7a0cf37cc27b42fb3ef6a9b1df6e93a1c6d98c6c6702b02fe262d5fcbd89db63320793b99b21cb5348097d0a53de81bd5f4e8b86e20cc9412e3f1cfb4e83
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"semver@npm:^7.3.5":
|
||||
version: 7.7.4
|
||||
resolution: "semver@npm:7.7.4"
|
||||
bin:
|
||||
semver: bin/semver.js
|
||||
checksum: 10/26bdc6d58b29528f4142d29afb8526bc335f4fc04c4a10f2b98b217f277a031c66736bf82d3d3bb354a2f6a3ae50f18fd62b053c4ac3f294a3d10a61f5075b75
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"simple-concat@npm:^1.0.0":
|
||||
version: 1.0.1
|
||||
resolution: "simple-concat@npm:1.0.1"
|
||||
checksum: 10/4d211042cc3d73a718c21ac6c4e7d7a0363e184be6a5ad25c8a1502e49df6d0a0253979e3d50dbdd3f60ef6c6c58d756b5d66ac1e05cda9cacd2e9fc59e3876a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"simple-get@npm:^4.0.0":
|
||||
version: 4.0.1
|
||||
resolution: "simple-get@npm:4.0.1"
|
||||
dependencies:
|
||||
decompress-response: "npm:^6.0.0"
|
||||
once: "npm:^1.3.1"
|
||||
simple-concat: "npm:^1.0.0"
|
||||
checksum: 10/93f1b32319782f78f2f2234e9ce34891b7ab6b990d19d8afefaa44423f5235ce2676aae42d6743fecac6c8dfff4b808d4c24fe5265be813d04769917a9a44f36
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"smart-buffer@npm:^4.2.0":
|
||||
version: 4.2.0
|
||||
resolution: "smart-buffer@npm:4.2.0"
|
||||
checksum: 10/927484aa0b1640fd9473cee3e0a0bcad6fce93fd7bbc18bac9ad0c33686f5d2e2c422fba24b5899c184524af01e11dd2bd051c2bf2b07e47aff8ca72cbfc60d2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"socks-proxy-agent@npm:^8.0.3":
|
||||
version: 8.0.5
|
||||
resolution: "socks-proxy-agent@npm:8.0.5"
|
||||
dependencies:
|
||||
agent-base: "npm:^7.1.2"
|
||||
debug: "npm:^4.3.4"
|
||||
socks: "npm:^2.8.3"
|
||||
checksum: 10/ee99e1dacab0985b52cbe5a75640be6e604135e9489ebdc3048635d186012fbaecc20fbbe04b177dee434c319ba20f09b3e7dfefb7d932466c0d707744eac05c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"socks@npm:^2.8.3":
|
||||
version: 2.8.7
|
||||
resolution: "socks@npm:2.8.7"
|
||||
dependencies:
|
||||
ip-address: "npm:^10.0.1"
|
||||
smart-buffer: "npm:^4.2.0"
|
||||
checksum: 10/d19366c95908c19db154f329bbe94c2317d315dc933a7c2b5101e73f32a555c84fb199b62174e1490082a593a4933d8d5a9b297bde7d1419c14a11a965f51356
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ssri@npm:^13.0.0":
|
||||
version: 13.0.1
|
||||
resolution: "ssri@npm:13.0.1"
|
||||
dependencies:
|
||||
minipass: "npm:^7.0.3"
|
||||
checksum: 10/ae560d0378d074006a71b06af71bfbe84a3fe1ac6e16c1f07575f69e670d40170507fe52b21bcc23399429bc6a15f4bc3ea8d9bc88e9dfd7e87de564e6da6a72
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"string_decoder@npm:^1.1.1":
|
||||
version: 1.3.0
|
||||
resolution: "string_decoder@npm:1.3.0"
|
||||
dependencies:
|
||||
safe-buffer: "npm:~5.2.0"
|
||||
checksum: 10/54d23f4a6acae0e93f999a585e673be9e561b65cd4cca37714af1e893ab8cd8dfa52a9e4f58f48f87b4a44918d3a9254326cb80ed194bf2e4c226e2b21767e56
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"strip-json-comments@npm:~2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "strip-json-comments@npm:2.0.1"
|
||||
checksum: 10/1074ccb63270d32ca28edfb0a281c96b94dc679077828135141f27d52a5a398ef5e78bcf22809d23cadc2b81dfbe345eb5fd8699b385c8b1128907dec4a7d1e1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tar-fs@npm:^2.0.0":
|
||||
version: 2.1.4
|
||||
resolution: "tar-fs@npm:2.1.4"
|
||||
dependencies:
|
||||
chownr: "npm:^1.1.1"
|
||||
mkdirp-classic: "npm:^0.5.2"
|
||||
pump: "npm:^3.0.0"
|
||||
tar-stream: "npm:^2.1.4"
|
||||
checksum: 10/bdf7e3cb039522e39c6dae3084b1bca8d7bcc1de1906eae4a1caea6a2250d22d26dcc234118bf879b345d91ebf250a744b196e379334a4abcbb109a78db7d3be
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tar-stream@npm:^2.1.4":
|
||||
version: 2.2.0
|
||||
resolution: "tar-stream@npm:2.2.0"
|
||||
dependencies:
|
||||
bl: "npm:^4.0.3"
|
||||
end-of-stream: "npm:^1.4.1"
|
||||
fs-constants: "npm:^1.0.0"
|
||||
inherits: "npm:^2.0.3"
|
||||
readable-stream: "npm:^3.1.1"
|
||||
checksum: 10/1a52a51d240c118cbcd30f7368ea5e5baef1eac3e6b793fb1a41e6cd7319296c79c0264ccc5859f5294aa80f8f00b9239d519e627b9aade80038de6f966fec6a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tar@npm:^7.5.4":
|
||||
version: 7.5.10
|
||||
resolution: "tar@npm:7.5.10"
|
||||
dependencies:
|
||||
"@isaacs/fs-minipass": "npm:^4.0.0"
|
||||
chownr: "npm:^3.0.0"
|
||||
minipass: "npm:^7.1.2"
|
||||
minizlib: "npm:^3.1.0"
|
||||
yallist: "npm:^5.0.0"
|
||||
checksum: 10/98ba6421a250b233c36a54f7441647bdfee1ed0b916cd57850259a3602154d996f5b8422f67ef5c8ce77f582ed938054775c2873fc7c901e0c7530ed50febc40
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tinyglobby@npm:^0.2.12":
|
||||
version: 0.2.15
|
||||
resolution: "tinyglobby@npm:0.2.15"
|
||||
dependencies:
|
||||
fdir: "npm:^6.5.0"
|
||||
picomatch: "npm:^4.0.3"
|
||||
checksum: 10/d72bd826a8b0fa5fa3929e7fe5ba48fceb2ae495df3a231b6c5408cd7d8c00b58ab5a9c2a76ba56a62ee9b5e083626f1f33599734bed1ffc4b792406408f0ca2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tunnel-agent@npm:^0.6.0":
|
||||
version: 0.6.0
|
||||
resolution: "tunnel-agent@npm:0.6.0"
|
||||
dependencies:
|
||||
safe-buffer: "npm:^5.0.1"
|
||||
checksum: 10/7f0d9ed5c22404072b2ae8edc45c071772affd2ed14a74f03b4e71b4dd1a14c3714d85aed64abcaaee5fec2efc79002ba81155c708f4df65821b444abb0cfade
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unique-filename@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "unique-filename@npm:5.0.0"
|
||||
dependencies:
|
||||
unique-slug: "npm:^6.0.0"
|
||||
checksum: 10/a5f67085caef74bdd2a6869a200ed5d68d171f5cc38435a836b5fd12cce4e4eb55e6a190298035c325053a5687ed7a3c96f0a91e82215fd14729769d9ac57d9b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unique-slug@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "unique-slug@npm:6.0.0"
|
||||
dependencies:
|
||||
imurmurhash: "npm:^0.1.4"
|
||||
checksum: 10/b78ed9d5b01ff465f80975f17387750ed3639909ac487fa82c4ae4326759f6de87c2131c0c39eca4c68cf06c537a8d104fba1dfc8a30308f99bc505345e1eba3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"util-deprecate@npm:^1.0.1":
|
||||
version: 1.0.2
|
||||
resolution: "util-deprecate@npm:1.0.2"
|
||||
checksum: 10/474acf1146cb2701fe3b074892217553dfcf9a031280919ba1b8d651a068c9b15d863b7303cb15bd00a862b498e6cf4ad7b4a08fb134edd5a6f7641681cb54a2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"which@npm:^6.0.0":
|
||||
version: 6.0.1
|
||||
resolution: "which@npm:6.0.1"
|
||||
dependencies:
|
||||
isexe: "npm:^4.0.0"
|
||||
bin:
|
||||
node-which: bin/which.js
|
||||
checksum: 10/dbea77c7d3058bf6c78bf9659d2dce4d2b57d39a15b826b2af6ac2e5a219b99dc8a831b79fdbc453c0598adb4f3f84cf9c2491fd52beb9f5d2dececcad117f68
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"wrappy@npm:1":
|
||||
version: 1.0.2
|
||||
resolution: "wrappy@npm:1.0.2"
|
||||
checksum: 10/159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yallist@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "yallist@npm:4.0.0"
|
||||
checksum: 10/4cb02b42b8a93b5cf50caf5d8e9beb409400a8a4d85e83bb0685c1457e9ac0b7a00819e9f5991ac25ffabb56a78e2f017c1acc010b3a1babfe6de690ba531abd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yallist@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "yallist@npm:5.0.0"
|
||||
checksum: 10/1884d272d485845ad04759a255c71775db0fac56308764b4c77ea56a20d56679fad340213054c8c9c9c26fcfd4c4b2a90df993b7e0aaf3cdb73c618d1d1a802a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
65
src/data/getNearbyDefibs.js
Normal file
65
src/data/getNearbyDefibs.js
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// Final exported function to retrieve nearby defibrillators from the embedded DB.
|
||||
// Usage:
|
||||
// import getNearbyDefibs from "~/data/getNearbyDefibs";
|
||||
// const results = await getNearbyDefibs({ lat: 48.8566, lon: 2.3522, radiusMeters: 1000, limit: 20 });
|
||||
|
||||
import {
|
||||
getNearbyDefibs as queryNearby,
|
||||
getNearbyDefibsBbox,
|
||||
} from "~/db/defibsRepo";
|
||||
|
||||
/**
|
||||
* @typedef {Object} DefibResult
|
||||
* @property {string} id
|
||||
* @property {number} latitude
|
||||
* @property {number} longitude
|
||||
* @property {string} nom
|
||||
* @property {string} adresse
|
||||
* @property {string} horaires
|
||||
* @property {string} acces
|
||||
* @property {number} disponible_24h
|
||||
* @property {number} distanceMeters
|
||||
*/
|
||||
|
||||
/**
|
||||
* Retrieve nearby defibrillators, sorted by distance.
|
||||
* Uses H3 spatial index with automatic bbox fallback.
|
||||
*
|
||||
* @param {Object} params
|
||||
* @param {number} params.lat - User latitude (WGS84)
|
||||
* @param {number} params.lon - User longitude (WGS84)
|
||||
* @param {number} params.radiusMeters - Search radius in meters
|
||||
* @param {number} params.limit - Maximum number of results
|
||||
* @param {boolean} [params.disponible24hOnly] - Only return 24/7 accessible defibrillators
|
||||
* @param {boolean} [params.progressive] - Progressive H3 ring expansion (saves queries for small radii)
|
||||
* @returns {Promise<DefibResult[]>}
|
||||
*/
|
||||
export default async function getNearbyDefibs({
|
||||
lat,
|
||||
lon,
|
||||
radiusMeters,
|
||||
limit,
|
||||
disponible24hOnly = false,
|
||||
progressive = true,
|
||||
}) {
|
||||
try {
|
||||
return await queryNearby({
|
||||
lat,
|
||||
lon,
|
||||
radiusMeters,
|
||||
limit,
|
||||
disponible24hOnly,
|
||||
progressive,
|
||||
});
|
||||
} catch (err) {
|
||||
// Fallback to bbox if H3 fails (e.g. missing h3-js on a platform)
|
||||
console.warn("H3 query failed, falling back to bbox:", err.message);
|
||||
return getNearbyDefibsBbox({
|
||||
lat,
|
||||
lon,
|
||||
radiusMeters,
|
||||
limit,
|
||||
disponible24hOnly,
|
||||
});
|
||||
}
|
||||
}
|
||||
177
src/db/defibsRepo.js
Normal file
177
src/db/defibsRepo.js
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
// Defibrillator repository — nearby queries with H3 geo-indexing.
|
||||
import { latLngToCell, gridDisk } from "h3-js";
|
||||
|
||||
import getDb from "./openDb";
|
||||
import haversine from "~/utils/geo/haversine";
|
||||
|
||||
// H3 average edge lengths in meters per resolution (0..15).
|
||||
const H3_EDGE_M = [
|
||||
1107712, 418676, 158244, 59810, 22606, 8544, 3229, 1220, 461, 174, 65, 24,
|
||||
9, 3, 1, 0.5,
|
||||
];
|
||||
|
||||
const H3_RES = 8;
|
||||
|
||||
// SQLite max variable number is 999 by default; chunk IN() queries accordingly.
|
||||
const SQL_VAR_LIMIT = 900;
|
||||
|
||||
// Compute k (ring size) needed to cover a given radius at a given H3 resolution.
|
||||
function kForRadius(radiusMeters, res = H3_RES) {
|
||||
const edge = H3_EDGE_M[res];
|
||||
// sqrt(3) * edge ≈ diameter between parallel edges of a hexagon
|
||||
return Math.max(1, Math.ceil(radiusMeters / (edge * Math.sqrt(3))));
|
||||
}
|
||||
|
||||
// Build a bounding-box fallback SQL clause + params.
|
||||
function bboxClause(lat, lon, radiusMeters) {
|
||||
// 1 degree latitude ≈ 111_320 m
|
||||
const dLat = radiusMeters / 111_320;
|
||||
// 1 degree longitude shrinks with cos(lat)
|
||||
const dLon = radiusMeters / (111_320 * Math.cos((lat * Math.PI) / 180));
|
||||
return {
|
||||
clause:
|
||||
"latitude BETWEEN ? AND ? AND longitude BETWEEN ? AND ?",
|
||||
params: [lat - dLat, lat + dLat, lon - dLon, lon + dLon],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} Defib
|
||||
* @property {string} id
|
||||
* @property {number} latitude
|
||||
* @property {number} longitude
|
||||
* @property {string} nom
|
||||
* @property {string} adresse
|
||||
* @property {string} horaires
|
||||
* @property {string} acces
|
||||
* @property {number} disponible_24h
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fetch defibrillators near a given point.
|
||||
*
|
||||
* @param {Object} params
|
||||
* @param {number} params.lat - User latitude
|
||||
* @param {number} params.lon - User longitude
|
||||
* @param {number} params.radiusMeters - Search radius in meters
|
||||
* @param {number} params.limit - Max results returned
|
||||
* @param {boolean} [params.disponible24hOnly] - Filter 24/7 accessible only
|
||||
* @param {boolean} [params.progressive] - Enable progressive expansion (k=1,2,3…)
|
||||
* @returns {Promise<(Defib & { distanceMeters: number })[]>}
|
||||
*/
|
||||
export async function getNearbyDefibs({
|
||||
lat,
|
||||
lon,
|
||||
radiusMeters,
|
||||
limit,
|
||||
disponible24hOnly = false,
|
||||
progressive = false,
|
||||
}) {
|
||||
const db = await getDb();
|
||||
const maxK = kForRadius(radiusMeters);
|
||||
|
||||
if (progressive) {
|
||||
return progressiveSearch(db, lat, lon, radiusMeters, limit, disponible24hOnly, maxK);
|
||||
}
|
||||
|
||||
// One-shot: compute full disk and query
|
||||
const cells = gridDisk(latLngToCell(lat, lon, H3_RES), maxK);
|
||||
const candidates = await queryCells(db, cells, disponible24hOnly);
|
||||
return rankAndFilter(candidates, lat, lon, radiusMeters, limit);
|
||||
}
|
||||
|
||||
// Progressive expansion: start at k=1, expand until enough results or maxK.
|
||||
async function progressiveSearch(db, lat, lon, radiusMeters, limit, dispo24h, maxK) {
|
||||
let allCandidates = [];
|
||||
const seenIds = new Set();
|
||||
|
||||
for (let k = 1; k <= maxK; k++) {
|
||||
const cells = gridDisk(latLngToCell(lat, lon, H3_RES), k);
|
||||
const rows = await queryCells(db, cells, dispo24h);
|
||||
|
||||
for (const row of rows) {
|
||||
if (!seenIds.has(row.id)) {
|
||||
seenIds.add(row.id);
|
||||
allCandidates.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
// Early exit: if we already have more candidates than limit, rank and check
|
||||
if (allCandidates.length >= limit) {
|
||||
const ranked = rankAndFilter(allCandidates, lat, lon, radiusMeters, limit);
|
||||
if (ranked.length >= limit) return ranked;
|
||||
}
|
||||
}
|
||||
|
||||
return rankAndFilter(allCandidates, lat, lon, radiusMeters, limit);
|
||||
}
|
||||
|
||||
// Query the DB for rows matching a set of H3 cells, chunking if needed.
|
||||
async function queryCells(db, cells, dispo24h) {
|
||||
if (cells.length === 0) return [];
|
||||
|
||||
const results = [];
|
||||
|
||||
// Chunk cells to stay under SQLite variable limit
|
||||
for (let i = 0; i < cells.length; i += SQL_VAR_LIMIT) {
|
||||
const chunk = cells.slice(i, i + SQL_VAR_LIMIT);
|
||||
const placeholders = chunk.map(() => "?").join(",");
|
||||
|
||||
let sql = `SELECT id, latitude, longitude, nom, adresse, horaires, acces, disponible_24h
|
||||
FROM defibs WHERE h3 IN (${placeholders})`;
|
||||
const params = [...chunk];
|
||||
|
||||
if (dispo24h) {
|
||||
sql += " AND disponible_24h = 1";
|
||||
}
|
||||
|
||||
const rows = await db.getAllAsync(sql, params);
|
||||
results.push(...rows);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// Compute distance, filter by radius, sort, and limit.
|
||||
function rankAndFilter(candidates, lat, lon, radiusMeters, limit) {
|
||||
const withDist = [];
|
||||
for (const row of candidates) {
|
||||
const distanceMeters = haversine(lat, lon, row.latitude, row.longitude);
|
||||
if (distanceMeters <= radiusMeters) {
|
||||
withDist.push({ ...row, distanceMeters });
|
||||
}
|
||||
}
|
||||
withDist.sort((a, b) => a.distanceMeters - b.distanceMeters);
|
||||
return withDist.slice(0, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bbox fallback — use when H3 is unavailable.
|
||||
*
|
||||
* @param {Object} params
|
||||
* @param {number} params.lat
|
||||
* @param {number} params.lon
|
||||
* @param {number} params.radiusMeters
|
||||
* @param {number} params.limit
|
||||
* @param {boolean} [params.disponible24hOnly]
|
||||
* @returns {Promise<(Defib & { distanceMeters: number })[]>}
|
||||
*/
|
||||
export async function getNearbyDefibsBbox({
|
||||
lat,
|
||||
lon,
|
||||
radiusMeters,
|
||||
limit,
|
||||
disponible24hOnly = false,
|
||||
}) {
|
||||
const db = await getDb();
|
||||
const { clause, params } = bboxClause(lat, lon, radiusMeters);
|
||||
|
||||
let sql = `SELECT id, latitude, longitude, nom, adresse, horaires, acces, disponible_24h
|
||||
FROM defibs WHERE ${clause}`;
|
||||
if (disponible24hOnly) {
|
||||
sql += " AND disponible_24h = 1";
|
||||
}
|
||||
|
||||
const rows = await db.getAllAsync(sql, params);
|
||||
return rankAndFilter(rows, lat, lon, radiusMeters, limit);
|
||||
}
|
||||
41
src/db/openDb.js
Normal file
41
src/db/openDb.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// Open the pre-built geodae SQLite database (Expo variant).
|
||||
// Requires: expo-sqlite, expo-file-system, expo-asset
|
||||
import * as SQLite from "expo-sqlite";
|
||||
import * as FileSystem from "expo-file-system";
|
||||
import { Asset } from "expo-asset";
|
||||
|
||||
const DB_NAME = "geodae.db";
|
||||
|
||||
let _dbPromise = null;
|
||||
|
||||
export default function getDb() {
|
||||
if (!_dbPromise) {
|
||||
_dbPromise = initDb();
|
||||
}
|
||||
return _dbPromise;
|
||||
}
|
||||
|
||||
async function initDb() {
|
||||
const sqliteDir = `${FileSystem.documentDirectory}SQLite`;
|
||||
const dbPath = `${sqliteDir}/${DB_NAME}`;
|
||||
|
||||
// Ensure the SQLite directory exists
|
||||
const dirInfo = await FileSystem.getInfoAsync(sqliteDir);
|
||||
if (!dirInfo.exists) {
|
||||
await FileSystem.makeDirectoryAsync(sqliteDir, { intermediates: true });
|
||||
}
|
||||
|
||||
// Copy asset DB on first launch (or after app update clears documents)
|
||||
const fileInfo = await FileSystem.getInfoAsync(dbPath);
|
||||
if (!fileInfo.exists) {
|
||||
const asset = Asset.fromModule(require("../assets/db/geodae.db"));
|
||||
await asset.downloadAsync();
|
||||
await FileSystem.copyAsync({ from: asset.localUri, to: dbPath });
|
||||
}
|
||||
|
||||
const db = await SQLite.openDatabaseAsync(DB_NAME);
|
||||
// Read-only optimizations
|
||||
await db.execAsync("PRAGMA journal_mode = WAL");
|
||||
await db.execAsync("PRAGMA cache_size = -8000"); // 8 MB
|
||||
return db;
|
||||
}
|
||||
17
src/db/openDb.op-sqlite.js
Normal file
17
src/db/openDb.op-sqlite.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// Open the pre-built geodae SQLite database (Bare RN variant).
|
||||
// Requires: @op-engineering/op-sqlite
|
||||
// Install: npm install @op-engineering/op-sqlite
|
||||
// Place geodae.db in:
|
||||
// Android: android/app/src/main/assets/geodae.db
|
||||
// iOS: add geodae.db to Xcode project "Copy Bundle Resources"
|
||||
import { open } from "@op-engineering/op-sqlite";
|
||||
|
||||
let _db = null;
|
||||
|
||||
export default function getDb() {
|
||||
if (!_db) {
|
||||
_db = open({ name: "geodae.db", readOnly: true });
|
||||
_db.execute("PRAGMA cache_size = -8000"); // 8 MB
|
||||
}
|
||||
return _db;
|
||||
}
|
||||
14
src/utils/geo/haversine.js
Normal file
14
src/utils/geo/haversine.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// Haversine distance in meters between two WGS84 points.
|
||||
const DEG_TO_RAD = Math.PI / 180;
|
||||
const EARTH_RADIUS_M = 6_371_000;
|
||||
|
||||
export default function haversine(lat1, lon1, lat2, lon2) {
|
||||
const dLat = (lat2 - lat1) * DEG_TO_RAD;
|
||||
const dLon = (lon2 - lon1) * DEG_TO_RAD;
|
||||
const a =
|
||||
Math.sin(dLat / 2) ** 2 +
|
||||
Math.cos(lat1 * DEG_TO_RAD) *
|
||||
Math.cos(lat2 * DEG_TO_RAD) *
|
||||
Math.sin(dLon / 2) ** 2;
|
||||
return 2 * EARTH_RADIUS_M * Math.asin(Math.sqrt(a));
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue