mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
fix: preserve landmark identity when resolving annotation outlines
This commit is contained in:
1 parent
f883dfbc1e
commit
38b591863c
4 files changed
+98
-7
No files matched your search
@@ -395,7 +395,11 @@ export async function resolveAnnotationTarget({
|
||||
// (which classifies building-vs-area and stitches compound multipolygons).
|
||||
// Point-like targets use the strict 'point' selection: exact-ish name + monument
|
||||
// scale, else no polygon at all — the honest point beats a locality-word match.
|
||||
fp = await fetchFootprint(lat, lon, matchName, scope, signal, pointLike ? 'point' : 'loose');
|
||||
// Without a feature-name component, match the original ask and require
|
||||
// identity or containment before replacing its geocoded anchor.
|
||||
const footprintMode = pointLike ? 'point'
|
||||
: (fromGeocode && !usablePrimary ? 'anchored' : 'loose');
|
||||
fp = await fetchFootprint(lat, lon, matchName, scope, signal, footprintMode);
|
||||
// A "grounds/compound/campus" phrase ("Texas Capitol grounds") names an ENCLOSING area. The
|
||||
// primary footprint above returns the BUILDING (the dome) or null — neither is the grounds. The
|
||||
// real enclosing polygon (e.g. "Capitol Square", leisure=park) IS in OSM but only surfaces via a
|
||||
@@ -715,9 +719,9 @@ async function placesTextSearch(query, centerLat, centerLon, radiusM, signal) {
|
||||
/**
|
||||
* The canonical name of the geocoded feature: the address component whose own
|
||||
* types match the result's feature type (e.g. the `neighborhood` component for a
|
||||
* neighborhood result), falling back to the first component / leading label
|
||||
* token. This is the user's INTENT, stripped of the trailing admin context that
|
||||
* makes the raw utterance match the wrong-scope OSM feature.
|
||||
* neighborhood result). Address-only responses may identify a landmark's city
|
||||
* without naming the landmark itself. Return null in that case so the caller
|
||||
* keeps the requested name instead of matching a nearby building in that city.
|
||||
*/
|
||||
function extractPrimaryName(result) {
|
||||
const resultTypes = new Set((result.types || []).map((t) => String(t).toLowerCase()));
|
||||
@@ -726,8 +730,7 @@ function extractPrimaryName(result) {
|
||||
const ct = (c.types || []).map((t) => String(t).toLowerCase());
|
||||
if (ct.some((t) => t !== 'political' && resultTypes.has(t))) return c.long_name;
|
||||
}
|
||||
if (comps[0]?.long_name) return comps[0].long_name;
|
||||
return String(result.formatted_address || '').split(',')[0].trim() || null;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1222,6 +1225,7 @@ function perpDistance(p, a, b) {
|
||||
* 'loose' — default word-overlap scoring (generic POIs/compounds).
|
||||
* 'strict' — named, non-building, district-sized areas only (neighborhood fallback).
|
||||
* 'point' — point-like targets: (almost) exactly-named, monument-scale polygons only.
|
||||
* 'anchored' — address-only geocodes: require name identity or anchor containment.
|
||||
*
|
||||
* @returns {Promise<null | { ring: Array<[number,number]>, kind: 'building'|'area', heightM: number|null }>}
|
||||
*/
|
||||
@@ -1487,7 +1491,7 @@ const POINTLIKE_AREA_CAP_M2 = 60_000;
|
||||
|
||||
/**
|
||||
* Pick the best OSM polygon for a query from raw Overpass elements. `mode` selects the
|
||||
* acceptance contract ('loose' | 'strict' | 'point' — see fetchFootprint). Exported for
|
||||
* acceptance contract ('loose' | 'strict' | 'point' | 'anchored' — see fetchFootprint). Exported for
|
||||
* the unit tests, which pin the mode contracts with fixtures captured from live data.
|
||||
*/
|
||||
export function selectFootprint(elements, targetLat, targetLon, query, mode = 'loose') {
|
||||
@@ -1539,6 +1543,10 @@ export function selectFootprint(elements, targetLat, targetLon, query, mode = 'l
|
||||
}
|
||||
|
||||
const contains = pointInPolygon(targetLon, targetLat, coords);
|
||||
if (mode === 'anchored' && !contains) {
|
||||
const intentCoverage = queryWords.size ? nameOverlap / queryWords.size : 0;
|
||||
if (!named || intentCoverage < 0.5 || completeness < 0.6) continue;
|
||||
}
|
||||
const centroid = ringCentroid(coords.map((p) => [p.lon, p.lat]));
|
||||
const distanceM = centroid
|
||||
? approximateDistanceM(targetLat, targetLon, centroid.lat, centroid.lon)
|
||||
|
||||
Reference in new issue
Block a user