fix(geo): wrap longitude differences across the antimeridian

The flights ground-floor pipeline subtracted longitudes without wrapping.
approxDistanceKm, and its copy in the mesh-floor sampler, measured two
points 0.2° apart either side of 180° (21 km at Fiji) as about 38,000 km,
so low and grounded aircraft just across the line from the viewer were
never floored or mesh-sampled. corridorFloorCells walked a segment across
180° the long way round, filling its cell budget with ground behind the
contact and never requesting the cells it was about to cross, and
stickyFloorCell dropped its hysteresis for a contact on the seam. Each now
takes the short way round, and corridor points are normalised back into
[-180, 180].

SEARCH NEARBY SITES centred its Google Places search on the mean of west
and east, which for a view across 180° (west > east) is longitude 0, and
sized the radius from the view's height alone. It now measures the view's
width through 180° and searches its real centre.
This commit is contained in:
Aditya committed 2026-10-16 16:48:37 +05:30
1 parent d4492d8f8c
commit 2c61d9e2b7
9 files changed
+146 -17

No files matched your search

+5 -4
View File
@@ -133,15 +133,16 @@ export function createIngestion({
let placesError = null;
if (layerState.googleSearchRequested) {
layerState.googleSearchRequested = false;
// A view across 180° has west > east; its width runs through 180°.
const lonSpan = box.east - box.west + (box.east < box.west ? 360 : 0);
const latitude = (box.south + box.north) / 2;
const longitude = (box.west + box.east) / 2;
let longitude = box.west + lonSpan / 2;
if (longitude > 180) longitude -= 360;
const radiusM = Math.min(
50000,
Math.max(
1000,
Math.round(
Math.max(box.north - box.south, box.east - box.west) * 55_000,
),
Math.round(Math.max(box.north - box.south, lonSpan) * 55_000),
),
);
try {
@@ -0,0 +1,51 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { createIngestion } from './ingestion.js';
// Taveuni, Fiji: Cesium reports a view across 180° with west > east. Each view
// is 0.4° wide and 0.2° tall; the last one does not cross 180°.
for (const [west, east, centre] of [
[179.7, -179.9, 179.9],
[179.9, -179.7, -179.9],
[179.5, 179.9, 179.7],
])
test(`a nearby search is centred on the view at ${centre}`, async () => {
const box = { south: -17, west, north: -16.8, east };
const searches = [];
const state = {
enabled: true,
viewer: {},
records: [],
googleSearchRequested: true,
};
const ingestion = createIngestion({
state,
source: {
getMappedSites: async () => ({ records: [], wide: true }),
searchNearby: async (query) => {
searches.push(query);
return { places: [] };
},
},
services: {
render: { governorRequestRender() {} },
ground: { resolveGroundFloorCellsBounded: async () => {} },
},
parts: {
viewport: {
loadArea: () => ({ box, coverage: {} }),
clearUnavailableRetry() {},
scheduleUnavailableRetry() {},
},
model: { installationWithinViewport: () => true },
rendering: { renderRecords() {}, warmInstallationFloors() {} },
},
});
await ingestion.loadInstallations();
assert.equal(searches.length, 1);
const { latitude, longitude, radiusM } = searches[0];
assert.ok(Math.abs(latitude + 16.9) < 1e-9);
assert.ok(Math.abs(longitude - centre) < 1e-9, `longitude ${longitude}`);
// The width, not the height, sets the radius.
assert.equal(radiusM, 22000);
});