mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
fix(geo): wrap longitude differences across the antimeridian in point distances (#1304)
Wrap longitude differences in annotation proximity checks and nearby-place distances so points across the antimeridian remain close. Original contribution by quanticsoul4772. Co-authored-by: quanticsoul4772 <[email protected]>
This commit is contained in:
1 parent
9d3548c27c
commit
07318cc9a4
6 files changed
+48
-2
No files matched your search
@@ -0,0 +1,2 @@
|
|||||||
|
- Point distances wrap longitude differences across the antimeridian, in voice
|
||||||
|
proximity checks and `places_nearby` (quanticsoul4772, #1304).
|
||||||
@@ -5515,6 +5515,11 @@ easier to meet (detection is now on more often), but does not create it.
|
|||||||
short-way-round longitude span and wraps the padded edges, so a dateline-
|
short-way-round longitude span and wraps the padded edges, so a dateline-
|
||||||
crossing box stays its true width. Raw subtraction inflated a 0.41° metro box
|
crossing box stays its true width. Raw subtraction inflated a 0.41° metro box
|
||||||
to 86.7° and a 60° territory to 132°.
|
to 86.7° and a 60° territory to 132°.
|
||||||
|
- **Point distances take the short way round 180°.** The annotation
|
||||||
|
resolver's `approximateDistanceM` (voice "point out" proximity checks) and
|
||||||
|
`places_nearby` distances wrap the longitude difference. Raw subtraction put
|
||||||
|
Savusavu 38,148 km from a camera over the Lau Islands, so the proximity check
|
||||||
|
refused it, and sorted a place 2 km across 180° last.
|
||||||
- CCTV calibration persists at `godsEyeView.cctv.calibration.v2` (v2 rebuild;
|
- CCTV calibration persists at `godsEyeView.cctv.calibration.v2` (v2 rebuild;
|
||||||
the store was wiped clean, no import from the old `v1` key).
|
the store was wiped clean, no import from the old `v1` key).
|
||||||
- CCTV v3 floor QA pins are: zero samples for heading-only edits; zero transient
|
- CCTV v3 floor QA pins are: zero samples for heading-only edits; zero transient
|
||||||
|
|||||||
@@ -13,8 +13,11 @@ function approximateDistanceM(latA, lonA, latB, lonB) {
|
|||||||
return Number.MAX_SAFE_INTEGER;
|
return Number.MAX_SAFE_INTEGER;
|
||||||
const latitudeScale = 111320;
|
const latitudeScale = 111320;
|
||||||
const longitudeScale = latitudeScale * Math.cos((latA * Math.PI) / 180);
|
const longitudeScale = latitudeScale * Math.cos((latA * Math.PI) / 180);
|
||||||
|
// The short way round, so places either side of 180° stay close.
|
||||||
|
let dLon = lonB - lonA;
|
||||||
|
dLon -= 360 * Math.round(dLon / 360);
|
||||||
return Math.round(
|
return Math.round(
|
||||||
Math.hypot((latB - latA) * latitudeScale, (lonB - lonA) * longitudeScale),
|
Math.hypot((latB - latA) * latitudeScale, dLon * longitudeScale),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -250,10 +250,13 @@ export function approximateAreaM2(coords) {
|
|||||||
return Math.abs(area) / 2;
|
return Math.abs(area) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Local equirectangular distance in metres, the short way round 180°. */
|
||||||
export function approximateDistanceM(latA, lonA, latB, lonB) {
|
export function approximateDistanceM(latA, lonA, latB, lonB) {
|
||||||
const latScale = 111_320;
|
const latScale = 111_320;
|
||||||
const lonScale = latScale * Math.cos(((latA + latB) / 2) * (Math.PI / 180));
|
const lonScale = latScale * Math.cos(((latA + latB) / 2) * (Math.PI / 180));
|
||||||
return Math.hypot((latB - latA) * latScale, (lonB - lonA) * lonScale);
|
let dLon = lonB - lonA;
|
||||||
|
dLon -= 360 * Math.round(dLon / 360);
|
||||||
|
return Math.hypot((latB - latA) * latScale, dLon * lonScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pointInPolygon(lon, lat, coords) {
|
export function pointInPolygon(lon, lat, coords) {
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import { approximateDistanceM } from './featureGeometry.js';
|
||||||
|
|
||||||
|
test('approximate distance takes the short way round 180°', () => {
|
||||||
|
// Lau Islands (-179.0) to Savusavu (179.33): about 178 km apart.
|
||||||
|
const across = approximateDistanceM(-17.5, -179.0, -16.78, 179.33);
|
||||||
|
assert.ok(across > 150_000 && across < 200_000, String(across));
|
||||||
|
assert.equal(
|
||||||
|
Math.round(approximateDistanceM(0, 179.9, 0, -179.9)),
|
||||||
|
Math.round(approximateDistanceM(0, -0.1, 0, 0.1)),
|
||||||
|
);
|
||||||
|
// Away from 180° nothing changes.
|
||||||
|
assert.equal(Math.round(approximateDistanceM(0, 0, 0, 1)), 111_320);
|
||||||
|
});
|
||||||
@@ -81,6 +81,24 @@ test('nearby labels rank landmarks, deduplicate names/addresses and bound the pr
|
|||||||
assert.deepEqual(projectNearbyPlaces({}, 0, 0), []);
|
assert.deepEqual(projectNearbyPlaces({}, 0, 0), []);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('nearby place distances take the short way round 180°', () => {
|
||||||
|
const at = (name, longitude) => ({
|
||||||
|
id: name,
|
||||||
|
displayName: { text: name },
|
||||||
|
location: { latitude: -16.8, longitude },
|
||||||
|
types: ['point_of_interest'],
|
||||||
|
});
|
||||||
|
// From Taveuni at 179.98: 2 km east across 180°, 4 km west on this side.
|
||||||
|
const result = projectNearbyPlaces(
|
||||||
|
{ places: [at('West', 179.942), at('East', -179.999)] },
|
||||||
|
-16.8,
|
||||||
|
179.98,
|
||||||
|
);
|
||||||
|
const byName = Object.fromEntries(result.map((p) => [p.name, p.distanceM]));
|
||||||
|
assert.ok(byName.East > 1500 && byName.East < 2500, String(byName.East));
|
||||||
|
assert.ok(byName.West > 3500 && byName.West < 4500, String(byName.West));
|
||||||
|
});
|
||||||
|
|
||||||
test('text search preserves bounds, rejects malformed bounds and tolerates absent locations', () => {
|
test('text search preserves bounds, rejects malformed bounds and tolerates absent locations', () => {
|
||||||
const viewport = {
|
const viewport = {
|
||||||
low: { latitude: 1, longitude: 2 },
|
low: { latitude: 1, longitude: 2 },
|
||||||
|
|||||||
Reference in new issue
Block a user