mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
keep bent streets, enclosed landmarks and same-name outlines honest
A street ribbon that holds neither its ring mean nor the anchor is centred on the street's nearest point when the street runs within 100 m. Nominatim outline selection ranks an exact name, then one holding every asked word, then an overlap, and refuses an overlap whose name lacks the ask's last word unless that word is in the result's address or type, so "Central Park Zoo" is no longer Central Park. The server caches every accepted outline and answers each ask with the one holding its resolved anchor (sent as alat/alon), read across the antimeridian. A duplicate mark that fails to clean up no longer tears down the new outline it duplicates.
This commit is contained in:
1 parent
d9a460658d
commit
a38a065c5c
16 files changed
+516
-133
No files matched your search
@@ -150,6 +150,46 @@ export function polygonsContain(polygons, lat, lon) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether polygons contain a point. A part whose outer ring crosses the
|
||||
* antimeridian ([179.99, …], [-179.99, …]) is read contiguously in its own
|
||||
* longitude frame, with the point moved into that frame; every other part,
|
||||
* including one genuinely wider than 180°, is tested as stored. Crossing is
|
||||
* read from an edge jumping more than 180°, which assumes densely noded rings
|
||||
* (OSM, Nominatim, tiles); a ring that is both wider than 180° and crossing, or
|
||||
* a non-crossing ring with a single edge over 180°, is not supported.
|
||||
*/
|
||||
export function containsAnchor(polygons, lat, lon) {
|
||||
if (!Number.isFinite(lon)) return false;
|
||||
for (const part of polygons || []) {
|
||||
const outer = part?.[0];
|
||||
if (!Array.isArray(outer) || !outer.length) continue;
|
||||
const reference = outer[0][0];
|
||||
const shift = (x) => x - 360 * Math.round((x - reference) / 360);
|
||||
const span = (ring, f) => {
|
||||
let west = Infinity;
|
||||
let east = -Infinity;
|
||||
for (const [x] of ring) {
|
||||
west = Math.min(west, f(x));
|
||||
east = Math.max(east, f(x));
|
||||
}
|
||||
return east - west;
|
||||
};
|
||||
// An edge jumping more than 180° wraps the line; a polar ring whose
|
||||
// edges run along ±180 collapses when reframed and is read as stored.
|
||||
const jumps = outer.some(
|
||||
([x], i) => i > 0 && Math.abs(x - outer[i - 1][0]) > 180,
|
||||
);
|
||||
const framedSpan = jumps ? span(outer, shift) : 0;
|
||||
const crosses = framedSpan > 0 && framedSpan < 180;
|
||||
const framed = crosses
|
||||
? part.map((ring) => ring.map(([x, y]) => [shift(x), y]))
|
||||
: part;
|
||||
if (polygonsContain([framed], lat, crosses ? shift(lon) : lon)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit's geometry, decoded on first use. Longitudes are unwrapped around
|
||||
* the largest part for the box so units cut at the antimeridian (Alaska,
|
||||
|
||||
Reference in new issue
Block a user