fix(location): preserve geocoded places and region provenance

This commit is contained in:
manjunath committed 2026-10-19 11:01:18 +05:30
1 parent 93ea088f45
commit a331616a9d
11 files changed
+999 -33

No files matched your search

+152
View File
@@ -120,6 +120,123 @@ test('Georgia resolves from camera geography without a geocoder', async () => {
assert.deepEqual(geocodes, []);
});
test('the Alps under the view resolve before an unrelated remote geocode', async () => {
const { resolve, lookups, geocodes } = harness({
lat: -27.96,
lng: 152.2,
label: 'The Alps, Queensland',
name: 'The Alps',
types: ['locality', 'political'],
});
const resolved = await resolve('the Alps', viewerAt(46.5, 10, 248_000));
assert.equal(resolved?.source, 'bundled');
assert.equal(resolved.naturalRegion, 'Alps');
assert.equal(resolved.synthesized, false);
assert.equal(resolved.footprintKind, 'area');
assert.ok(resolved.ring.length >= 8);
assert.deepEqual(resolved.ring[0], resolved.ring.at(-1));
assert.equal(resolved.resolveOutline, undefined);
assert.deepEqual(geocodes, []);
assert.deepEqual(lookups, []);
});
test('Gold Coast in Australia cannot acquire its West African namesake outline', async () => {
for (const types of [['locality', 'political'], ['natural_feature']]) {
const { resolve, geocodes } = harness({
lat: -28.0167,
lng: 153.4,
label: 'Gold Coast, Australia',
name: 'Gold Coast',
types,
});
const resolved = await resolve('Gold Coast', viewerAt(-28.0167, 153.4));
assert.equal(resolved?.source, 'geocode');
assert.equal(resolved.lat, -28.0167);
assert.equal(resolved.lon, 153.4);
assert.equal(resolved.naturalRegion, undefined);
const outline = await resolved.resolveOutline();
assert.equal(outline?.naturalRegion, undefined);
assert.deepEqual(geocodes, ['Gold Coast']);
}
});
test('unique natural names without a containing view still need a geocoded anchor', async () => {
for (const viewer of [AUSTIN, {}]) {
const miss = harness();
assert.equal(await miss.resolve('the Alps', viewer), null);
assert.ok(miss.geocodes.includes('the Alps'));
}
const anchored = harness({
lat: 46.5,
lng: 10,
label: 'Alps',
name: 'Alps',
types: ['natural_feature'],
});
const resolved = await anchored.resolve('the Alps', {});
assert.equal(resolved.source, 'geocode');
const outline = await resolved.resolveOutline();
assert.equal(outline.naturalRegion, 'Alps');
assert.ok(outline.ring.length >= 8);
});
test('overlapping natural candidates continue through geocoding without choosing a ring', async () => {
const { resolve, geocodes } = harness({
lat: 45.6336,
lng: -74.86493,
label: 'Canadian Shield',
name: 'Canadian Shield',
types: ['natural_feature'],
});
const resolved = await resolve(
'Canadian Shield',
viewerAt(45.6336, -74.86493),
);
assert.equal(resolved?.source, 'geocode');
assert.equal((await resolved.resolveOutline())?.naturalRegion, undefined);
assert.deepEqual(geocodes, ['Canadian Shield']);
});
test('natural view recovery preserves point, explicit-coordinate, entity and around intent', async () => {
for (const extra of [
{ footprint: false },
{ latitude: 46.5, longitude: 10 },
{ entityKind: 'building' },
{ entityKind: 'compound' },
{ entityKind: 'district' },
{ entityKind: 'street' },
{ entityKind: 'point_feature' },
{ intent: 'around_the_thing' },
]) {
const { resolve, geocodes } = harness({
lat: 46.5,
lng: 10,
label: 'Alps',
name: 'Alps',
types:
extra.entityKind === 'district' ? ['locality'] : ['natural_feature'],
});
const resolved = await resolve('the Alps', viewerAt(46.5, 10), extra);
assert.notEqual(resolved?.source, 'bundled');
assert.equal(resolved?.naturalRegion, undefined);
if (extra.entityKind === 'district')
assert.ok(geocodes.includes('the Alps'));
if (extra.intent === 'around_the_thing' || extra.entityKind === 'district')
assert.equal((await resolved.resolveOutline())?.naturalRegion, undefined);
}
});
test('cancellation while a natural view lookup is pending cannot publish its outline', async () => {
const controller = new AbortController();
const { resolve, geocodes } = harness();
const pending = resolve('the Alps', viewerAt(46.5, 10), {
signal: controller.signal,
});
controller.abort();
await assert.rejects(pending, { name: 'AbortError' });
assert.deepEqual(geocodes, []);
});
test('country asks and geocoded country scopes use the bundled outline', async () => {
const direct = harness();
for (const name of ['Switzerland', 'Iran', 'France', 'Japan']) {
@@ -234,3 +351,38 @@ test('the engine keeps every part on the mark, drawn at once or upgraded', async
);
engine.destroy();
});
test('cancellation after the admin miss rejects the resumed natural view lookup', async () => {
const controller = new AbortController();
const signal = controller.signal;
const throwIfAborted = signal.throwIfAborted.bind(signal);
const checkpoints = [];
let abortCheckpoint = null;
// This exact path has entry, post-admin-miss and post-natural checkpoints.
// Queue at the known post-admin checkpoint: after this callback returns,
// the real natural lookup runs synchronously to its await before the queued
// abort executes. No timeout or guessed number of promise turns is used.
Object.defineProperty(signal, 'throwIfAborted', {
value() {
checkpoints.push(signal.aborted);
throwIfAborted();
if (checkpoints.length === 2) {
queueMicrotask(() => {
abortCheckpoint = checkpoints.length;
controller.abort();
});
}
},
});
const { resolve, lookups, geocodes } = harness();
const pending = resolve('the Alps', viewerAt(46.5, 10), { signal });
await assert.rejects(pending, { name: 'AbortError' });
assert.equal(
abortCheckpoint,
2,
'abort follows admin miss, before natural resume',
);
assert.deepEqual(checkpoints, [false, false, true]);
assert.deepEqual(lookups, []);
assert.deepEqual(geocodes, []);
});
+106
View File
@@ -44,10 +44,115 @@ test('region ring: Natural Earth names resolve without a geocoder', async () =>
for (const name of ['Gulf of Mexico', 'the Alps']) {
const region = await resolveRegionRingForQuery(name);
assert.ok(region?.ring?.length >= 3, `${name} resolves to a ring`);
assert.equal(region.source, 'natural-earth');
assert.equal(region.error, undefined);
}
});
test('region ring: duplicate natural names need a containing geocoded anchor', async () => {
const { resolveRegionRingForQuery } = createAnnotationResolver({
featureSource: featureSource(),
});
const miss = geocoderFor(null);
assert.equal(
await resolveRegionRingForQuery('Cordillera Oriental', undefined, miss),
null,
);
assert.deepEqual(miss.calls, ['Cordillera Oriental']);
const inside = geocoderFor({
lat: 5,
lng: -73,
label: 'Cordillera Oriental',
name: 'Cordillera Oriental',
types: ['natural_feature'],
});
const region = await resolveRegionRingForQuery(
'Cordillera Oriental',
undefined,
inside,
);
assert.equal(region?.source, 'natural-earth');
assert.equal(region.name, 'Cordillera Oriental');
assert.ok(region.ring.length >= 8);
assert.deepEqual(inside.calls, ['Cordillera Oriental']);
});
test('region ring: outside and overlapping geocoded natural anchors stay unresolved', async () => {
const { resolveRegionRingForQuery } = createAnnotationResolver({
featureSource: featureSource(),
});
for (const [name, lat, lng] of [
['Cordillera Oriental', 30.27, -97.74],
['Canadian Shield', 45.6336, -74.86493],
]) {
const geocoder = geocoderFor({
lat,
lng,
label: name,
name,
types: ['natural_feature'],
});
assert.equal(
await resolveRegionRingForQuery(name, undefined, geocoder),
null,
);
assert.deepEqual(geocoder.calls, [name]);
}
});
test('region ring: geocoded administrative scope wins over duplicate natural geography', async () => {
const lookups = [];
const { resolveRegionRingForQuery } = createAnnotationResolver({
featureSource: featureSource({
getAdministrativeAreas: async (point) => {
lookups.push(point);
return [];
},
}),
});
const geo = geocoderFor({
lat: 5,
lng: -73,
label: 'Cordillera Oriental',
name: 'Cordillera Oriental',
types: ['locality'],
});
const resolved = await resolveRegionRingForQuery(
'Cordillera Oriental',
undefined,
geo,
);
assert.equal(
resolved,
null,
'an unavailable city boundary is not a natural-region outline',
);
assert.deepEqual(lookups, [{ lat: 5, lon: -73 }]);
assert.deepEqual(geo.calls, ['Cordillera Oriental']);
});
test('region ring: cancellation during bundled lookup and geocode never returns a region', async () => {
const { resolveRegionRingForQuery } = createAnnotationResolver({
featureSource: featureSource(),
});
const controller = new AbortController();
const pending = resolveRegionRingForQuery('the Alps', controller.signal);
controller.abort();
await assert.rejects(pending, { name: 'AbortError' });
const later = new AbortController();
await assert.rejects(
resolveRegionRingForQuery('Cordillera Oriental', later.signal, {
async geocode() {
later.abort();
return { place: null };
},
}),
{ name: 'AbortError' },
);
});
test('region ring: a slow admin-boundary lookup returns region-timeout within the budget', async () => {
let adminSignal;
let release;
@@ -119,6 +224,7 @@ test('region ring: bundled states and counties resolve without a geocoder', asyn
]) {
const region = await resolveRegionRingForQuery(query, undefined, geocoder);
assert.equal(region?.name, name, query);
assert.equal(region.source, 'bundled-admin');
assert.ok(region.ring.length >= 8, `${query} has a real ring`);
}
assert.deepEqual(geocoder.calls, [], 'no geocoding');
+87 -8
View File
@@ -202,6 +202,22 @@ export function createAnnotationResolver({
);
signal?.throwIfAborted();
if (admin) return bundledAdminTarget(viewer, admin, trace.query);
// A unique pack name can still name an unrelated modern place. Recover
// natural outlines only from a containing view, including unique names.
if (
center &&
entityKind !== 'district' &&
intent !== 'around_the_thing'
) {
const natural = await lookupNaturalRegionOutline(
trace.query,
center.lat,
center.lon,
).catch(() => null);
signal?.throwIfAborted();
if (natural) return bundledNaturalTarget(viewer, natural, trace.query);
}
}
if (!Number.isFinite(lat) || !Number.isFinite(lon)) {
@@ -444,6 +460,7 @@ export function createAnnotationResolver({
* AFTER the anchor mark is drawn and upgrade the mark in place.
*/
const resolveOutline = async () => {
signal?.throwIfAborted();
let scope = baseScope;
const isAdmin =
scope === 'country' ||
@@ -469,6 +486,7 @@ export function createAnnotationResolver({
const ne = await lookupNaturalRegionOutline(target, lat, lon).catch(
() => null,
);
signal?.throwIfAborted();
if (ne) {
registerDynamicCredit(viewer, NATURAL_EARTH_CREDIT);
const neCentroid = ringCentroid(ne.ring);
@@ -838,6 +856,37 @@ export function createAnnotationResolver({
};
}
/** A complete annotation target for a natural ring containing the view. */
function bundledNaturalTarget(viewer, natural, query) {
const ring = closeRing([...natural.ring]);
const centroid = ringCentroid(ring);
if (!centroid) return null;
registerDynamicCredit(viewer, NATURAL_EARTH_CREDIT);
const lons = ring.map(([lon]) => lon);
const lats = ring.map(([, lat]) => lat);
console.log(
`[Resolver] "${query}": bundled ${natural.kind} "${natural.name}" ` +
'(natural-earth, view-contained) → FINAL source=bundled',
);
return {
lon: centroid.lon,
lat: centroid.lat,
height: sampleGroundHeight(viewer, centroid.lon, centroid.lat),
ring,
footprintKind: 'area',
buildingHeight: null,
label: natural.name,
source: 'bundled',
synthesized: false,
outlineUnavailable: false,
naturalRegion: natural.name,
viewport: {
low: { latitude: Math.min(...lats), longitude: Math.min(...lons) },
high: { latitude: Math.max(...lats), longitude: Math.max(...lons) },
},
};
}
/**
* Synthesize an approximate circular AREA by buffering a label point. Returns a ring
* marked `synthesized:true` so the renderers draw it "approximate" (dashed/feathered),
@@ -2004,8 +2053,8 @@ export function createAnnotationResolver({
/**
* Region ring for ANALYST queries ("how many flights over Texas / the Alps") —
* a name-only entry point that reuses this module's boundary machinery
* without the annotation pipeline. Natural Earth pack first (offline,
* instant; largest-area match is correct for a global name-only ask), then
* without the annotation pipeline. Unique Natural Earth names resolve first
* (offline); duplicate names require a geocoded containment anchor, then
* geocode + admin boundary for states/countries/counties (Tier A disk-cached
* Overpass, so repeat asks are instant). Returns null when the name doesn't
* resolve to a region-like boundary — the analyst engine reports that
@@ -2020,7 +2069,7 @@ export function createAnnotationResolver({
* @param {AbortSignal} [signal]
* @param {object} [placeSearch]
* @param {{budgetMs?: number}} [options] `Infinity` waits for the lookup.
* @returns {Promise<{name:string, ring:Array<[number,number]>}|{name:string, ring:null, error:'region-timeout'}|null>}
* @returns {Promise<{name:string, ring:Array<[number,number]>, source:string}|{name:string, ring:null, error:'region-timeout'}|null>}
*/
async function resolveRegionRingForQuery(
name,
@@ -2037,16 +2086,24 @@ export function createAnnotationResolver({
const q = String(name || '').trim();
if (!q) return null;
const ne = await findNaturalRegion(q).catch(() => null);
if (ne?.polygons?.length) {
signal?.throwIfAborted();
if (ne?.candidates === 1 && ne.polygons?.length) {
// Largest ring carries the query scope; multi-ring regions (Andes) keep
// their main cordillera — good enough for containment counting.
const ring = [...ne.polygons].sort((a, b) => b.length - a.length)[0];
if (ring?.length >= 3) return { name: ne.name, ring };
if (ring?.length >= 3)
return { name: ne.name, ring, source: 'natural-earth' };
}
// Bundled states/provinces/counties by name (offline); the main part only,
// like the Natural Earth rung above.
const admin = await findAdminArea(q).catch(() => null);
if (admin) return { name: admin.name, ring: [...admin.ring] };
signal?.throwIfAborted();
if (admin)
return {
name: admin.name,
ring: [...admin.ring],
source: 'bundled-admin',
};
const lookup = resolveAdminRegionRing(q, signal, placeSearch);
if (!Number.isFinite(budgetMs)) return lookup;
let timer;
@@ -2067,8 +2124,26 @@ export function createAnnotationResolver({
const geo = await geocodePlace(q, null, signal, placeSearch).catch(
() => null,
);
signal?.throwIfAborted();
if (!geo) return null;
const scope = scopeFromTypes(geo.types);
if (
scope === 'auto' ||
(scope === 'compound' && geo.types?.includes('natural_feature'))
) {
const natural = await lookupNaturalRegionOutline(
q,
geo.lat,
geo.lon,
).catch(() => null);
signal?.throwIfAborted();
if (natural)
return {
name: natural.name,
ring: natural.ring,
source: 'natural-earth',
};
}
if (!['country', 'state', 'county', 'city'].includes(scope)) return null;
if (scope === 'state' || scope === 'county' || scope === 'country') {
const admin = await findAdminAreaAt(
@@ -2077,12 +2152,16 @@ export function createAnnotationResolver({
geo.lon,
scope,
).catch(() => null);
if (admin) return { name: q, ring: [...admin.ring] };
signal?.throwIfAborted();
if (admin)
return { name: q, ring: [...admin.ring], source: 'bundled-admin' };
}
const fp = await fetchAdminArea(geo.lat, geo.lon, q, scope, signal).catch(
() => null,
);
if (fp?.ring?.length >= 3) return { name: q, ring: fp.ring };
signal?.throwIfAborted();
if (fp?.ring?.length >= 3)
return { name: q, ring: fp.ring, source: 'remote-boundary' };
return null;
}