address review findings on voice outlines

- remove the unused Nominatim reserve(); spacing tests now measure the
  actual transport start across processes
- start each queued building's 2 s ground-sampling budget when it
  reaches the head of the queue, and request a frame on promotion
- accept Nominatim and grounds outlines that cross the antimeridian
- let an unnamed school, university, hospital, cemetery, stadium or zoo
  area answer a grounds ask whose head noun names that kind, using only
  the part that holds the point; compare grounds names symmetrically
- charge shared OpenFreeMap consumers their own slice and keep the
  charged budget at the previous 64 MiB
- share one place-name fold, point-in-polygon, line stitching and
  nextTask helper instead of duplicates
- exclude Who's On First sources licensed only as "CC BY (equivalent)"
  or "CC0 (equivalent)"; rebuild the pack from the pinned archives
- whole-word neighbourhood alias guard; correct the outline size-limit
  comment
- restore DATA_SOURCES.md table formatting, keeping only real edits
- test behaviour instead of private engine fields
This commit is contained in:
manjunath committed 2026-10-30 20:47:20 +05:30
1 parent 5a1cfa6800
commit 2e82425d62
37 files changed
+921 -481

No files matched your search

+4 -13
View File
@@ -1,6 +1,7 @@
import crypto from 'node:crypto';
import fs from 'node:fs/promises';
import path from 'node:path';
import { foldPlaceName } from '../../../src/data/placeNames.js';
const DAY_MS = 86_400_000;
@@ -15,24 +16,14 @@ export const NOMINATIM_CACHE_TTL_MS = Object.freeze({
});
/**
* Comparable form of a place name: Unicode-normalized (NFKC), Latin accents
* removed, case folded, punctuation and symbols collapsed to single spaces,
* and a leading article dropped. Non-Latin scripts are kept as they are:
* "東京" stays "東京", while "São Paulo", "sao paulo" and "SÃO PAULO!" meet.
* Comparable form of a place name for matching results to the ask: the
* shared fold (see `foldPlaceName`), bounded in length.
*
* @param {string} value
* @returns {string}
*/
export function normalizePlaceName(value) {
return String(value ?? '')
.normalize('NFKD')
.replace(/(\p{Script=Latin})\p{M}+/gu, '$1')
.normalize('NFKC')
.toLocaleLowerCase('en-US')
.replace(/[\p{P}\p{S}\p{Z}\s]+/gu, ' ')
.trim()
.replace(/^(?:the) /, '')
.slice(0, 200);
return foldPlaceName(value).slice(0, 200);
}
/**
@@ -489,63 +489,6 @@ export function createGateStateStore({
nextStartAt: state.nextStartAt,
})).count;
},
reserve(day, { now, dailyCap, minSpacingMs, latestStartAt = Infinity }) {
if (!file) {
const error = new Error(
'no durable Nominatim usage state is configured',
);
report(error);
return { status: 'unavailable', error };
}
try {
let result;
withLock(() => {
if (pendingDispatches({ strict: true }).length)
throw new Error('unfinished durable Nominatim dispatch');
const disk = readDisk({ strict: true });
if (disk) memory = merge(memory, disk);
const pending = pendingPauses({ strict: true });
if (pending.length)
memory = merge(memory, {
...memory,
pausedUntil: Math.max(...pending.map((item) => item.pausedUntil)),
});
const state = memory;
if ((state.pausedUntil || 0) > now) {
result = {
status: 'paused',
retryAfterMs: state.pausedUntil - now,
};
return;
}
const count = state.day === day ? state.count : 0;
if (count >= dailyCap) {
result = { status: 'cap' };
return;
}
const startAt = Math.max(now, state.nextStartAt || 0);
if (startAt > latestStartAt) {
result = { status: 'abandoned' };
return;
}
const next = {
day,
count: count + 1,
pausedUntil: state.pausedUntil || 0,
nextStartAt: startAt + minSpacingMs,
};
writeDisk(next);
memory = next;
clearPersistedPauses(next.pausedUntil);
lastError = null;
result = { status: 'reserved', startAt, count: next.count };
});
return result;
} catch (error) {
report(error);
return { status: 'unavailable', error };
}
},
dispatch(
day,
{ clock, dailyCap, minSpacingMs, latestStartAt = Infinity },
+5 -1
View File
@@ -30,7 +30,11 @@ export const OUTLINE_POLYGON_THRESHOLD = Object.freeze({
building: 0.00005,
});
/** Total vertices returned for one outline, and parts/holes kept. */
/**
* Size limits for one outline. Vertices beyond the budget are simplified away;
* more parts, or more holes in one part, than these limits refuse the outline
* whole, because a truncated multipart place would misrepresent it.
*/
const OUTLINE_MAX_VERTICES = 6000;
const OUTLINE_MAX_PARTS = 24;
const OUTLINE_MAX_HOLES = 16;