mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 03:14:00 +00:00
fix: keep clamped traffic bounds monotonic across the antimeridian
The previous fix wrapped the span correctly but normalized the returned west/east back into [-180, 180), which re-inverts the box whenever it crosses the antimeridian (west > east again). Downstream consumers (getBoundsCenter, boundsOverlap) assume west <= east and silently misbehave on an inverted box: the fetch center lands on the opposite side of the planet, and the overlap gate never skips a re-fetch. Drop the normalization and leave west/east unnormalized instead, so the box stays monotonic. The only remaining edge case (coordinates outside the conventional range reaching the Overpass query builder) is a visible, separate problem rather than a silently wrong center. Addresses review feedback on #392.
This commit is contained in:
1 parent
c22ad6af32
commit
830c372c12
2 files changed
+18
-25
No files matched your search
@@ -129,17 +129,6 @@ function longitudeSpanDeg(west, east) {
|
|||||||
return raw >= 0 ? raw : raw + 360;
|
return raw >= 0 ? raw : raw + 360;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalize a longitude into [-180, 180). Matches the wrap idiom already
|
|
||||||
* used by `destinationPoint()` above.
|
|
||||||
*
|
|
||||||
* @param {number} lon - Longitude in degrees.
|
|
||||||
* @returns {number} Longitude normalized to [-180, 180).
|
|
||||||
*/
|
|
||||||
function normalizeLonDeg(lon) {
|
|
||||||
return ((lon + 540) % 360) - 180;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clamp a bounding box's spans to `maxSpanDeg` and recenter it on `center`.
|
* Clamp a bounding box's spans to `maxSpanDeg` and recenter it on `center`.
|
||||||
*
|
*
|
||||||
@@ -147,9 +136,12 @@ function normalizeLonDeg(lon) {
|
|||||||
* but centers the box on the derived look-at point instead of the view
|
* but centers the box on the derived look-at point instead of the view
|
||||||
* rectangle's midpoint. Idempotent when `center` is the box's own midpoint.
|
* rectangle's midpoint. Idempotent when `center` is the box's own midpoint.
|
||||||
*
|
*
|
||||||
* Longitude is handled as a cyclic axis: the input span wraps correctly
|
* Longitude is handled as a cyclic axis for the span calculation, so the
|
||||||
* across the antimeridian, and the output west/east are normalized back into
|
* input span wraps correctly across the antimeridian. The output west/east
|
||||||
* [-180, 180) instead of drifting outside the normal coordinate range.
|
* are deliberately left unnormalized (may fall outside [-180, 180)) so that
|
||||||
|
* `west <= east` always holds — callers such as `getBoundsCenter` and
|
||||||
|
* `boundsOverlap` rely on that invariant and would silently misbehave on a
|
||||||
|
* normalized-but-inverted box (see #392 review).
|
||||||
*
|
*
|
||||||
* @param {{south:number, west:number, north:number, east:number}} bounds
|
* @param {{south:number, west:number, north:number, east:number}} bounds
|
||||||
* Source bounds (span donor).
|
* Source bounds (span donor).
|
||||||
@@ -163,7 +155,7 @@ export function clampBoundsAroundCenter(bounds, center, maxSpanDeg = 0.05) {
|
|||||||
return {
|
return {
|
||||||
south: center.lat - latSpan / 2,
|
south: center.lat - latSpan / 2,
|
||||||
north: center.lat + latSpan / 2,
|
north: center.lat + latSpan / 2,
|
||||||
west: normalizeLonDeg(center.lon - lonSpan / 2),
|
west: center.lon - lonSpan / 2,
|
||||||
east: normalizeLonDeg(center.lon + lonSpan / 2),
|
east: center.lon + lonSpan / 2,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -124,14 +124,15 @@ test('span clamp handles bounds crossing the antimeridian', () => {
|
|||||||
|
|
||||||
const result = clampBoundsAroundCenter(bounds, center, 0.05);
|
const result = clampBoundsAroundCenter(bounds, center, 0.05);
|
||||||
|
|
||||||
assert.ok(result.west >= -180 && result.west < 180);
|
// west/east are deliberately left unnormalized (may exceed 180) so that
|
||||||
assert.ok(result.east >= -180 && result.east < 180);
|
// west <= east always holds for downstream consumers (getBoundsCenter,
|
||||||
|
// boundsOverlap) that assume a monotonic box. See #392 review.
|
||||||
|
assert.ok(result.west <= result.east, `west/east not monotonic: ${result.west}, ${result.east}`);
|
||||||
|
assert.ok(Math.abs(result.east - result.west - 0.04) < 1e-12);
|
||||||
|
|
||||||
// Wrapped longitude span should remain about 0.04 degrees.
|
// The plain-mean center a caller like getBoundsCenter computes must land
|
||||||
const span =
|
// back on the antimeridian near the requested center, not ~180 degrees
|
||||||
result.east >= result.west
|
// away on the opposite side of the planet.
|
||||||
? result.east - result.west
|
const center2 = (result.west + result.east) / 2;
|
||||||
: result.east - result.west + 360;
|
assert.ok(Math.abs(center2 - center.lon) < 1e-9, `center drifted: ${center2}`);
|
||||||
|
|
||||||
assert.ok(Math.abs(span - 0.04) < 1e-12);
|
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user