tighten the remaining voice outline review items

- One place-name key for server and tiles, so "St. Paul" and "Saint Paul"
  agree in Nominatim name checks.
- Tile street names: a lone letter is a direction only beside another name
  word ("E Street" is not "East Street"), and a trailing quadrant ("E St NW")
  may go unsaid in the ask.
- A building cut at the tile edge that is nearer the point than any whole
  one leaves the point instead of a farther neighbour standing in; an edge
  without a tile buffer counts as a cut, so a cut area never answers grounds.
- A batch building install re-judges only results that installed; a late
  timeout during caption conversion keeps the pending point drawn; a failed
  plain recaption keeps the old mark addressable.
- An interrupted Nominatim dispatch is reclaimed by the next lock owner ten
  minutes after the start its marker names; node:sqlite loads on first use
  without its experimental warning.
- PHOTON_URL accepts only https, which the page's connect-src allows.
- Shared OpenFreeMap tiles decode each layer when a consumer first selects
  it; outline selection reuses the shared ring area and simplification, and
  ground probes the shared ring centroid; the grounds word list lives once.
This commit is contained in:
Sameh Khamis committed 2026-11-02 08:50:05 -08:00
1 parent a38a065c5c
commit a61d0f4c2a
24 files changed
+422 -128

No files matched your search

+53
View File
@@ -1249,3 +1249,56 @@ test('recaption inherits outline credit from borrowed geometry rather than its n
assert.equal(engine.list()[0].ring, ring);
engine.clear();
});
test('a building install in the batch does not re-judge marks that never installed', async (t) => {
installAnimationFrameStubs(t);
const h = controlledBuildingRenderer();
const engine = createAnnotationEngine({
viewer: {},
renderer: h.renderer,
resolveTarget: async ({ target }) =>
target === 'Tower' ? { ...BUILDING_FP, lon: -97.7 } : { ...FIXED_POINT },
});
// The recolour replaces the first point within the same batch; without the
// building that point's result stays ok, so the building must not change it.
const batch = engine.annotate([
{ type: 'point', target: 'Capitol', label: 'Capitol' },
{ type: 'area', target: 'Tower', label: 'Tower' },
{ type: 'point', target: 'Capitol', label: 'Capitol', color: 'amber' },
]);
await flushMicrotasks();
h.pending[0].finish();
const completed = await batch;
assert.deepEqual(completed.results.map((result) => result.ok), [true, true, true]);
assert.equal(completed.drawn, 3);
});
test('plain recaption keeps the old mark addressable when its removal throws', async (t) => {
installAnimationFrameStubs(t);
const { renderer } = fakeRenderer();
const engine = createAnnotationEngine({
viewer: {}, renderer, resolveTarget: async () => ({ ...FIXED_POINT }),
});
await engine.annotate([{ type: 'point', target: 'Capitol', label: 'Capitol' }]);
const old = engine.list()[0];
const removals = [];
let fail = true;
renderer.remove = (anno) => {
removals.push(anno.id);
if (anno.id === old.id && fail) throw new Error('old route removal failed');
};
const swap = await engine.annotate([
{ type: 'point', target: 'Capitol', label: 'Capitol', color: 'amber' },
]);
assert.equal(swap.drawn, 0);
assert.deepEqual(engine.list().map((anno) => anno.id), [old.id], 'failed cleanup remains reachable');
assert.equal(engine.list()[0].cleanupPending, true);
fail = false;
const attempts = removals.filter((id) => id === old.id).length;
engine.clear();
assert.ok(
removals.filter((id) => id === old.id).length > attempts,
'the old route is retried on the next clear',
);
assert.equal(engine.count(), 0);
});