mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
Merge branch 'main' into feat/voice-geometry
Picks up the terrain partial-heights work (#1456). No conflicts; it changes no voice prompt or tool description.
This commit is contained in:
commit
0b6cdc8e1f
8 files changed
+152
-14
No files matched your search
@@ -151,12 +151,25 @@ export function createApplicationRequestServices({
|
||||
const query = points
|
||||
.map(({ lat, lon }) => `${lon.toFixed(5)},${lat.toFixed(5)}`)
|
||||
.join(';');
|
||||
return requireOk(
|
||||
await request(`${urls.terrain}?points=${encodeURIComponent(query)}`, {
|
||||
signal,
|
||||
}),
|
||||
'Terrain heights',
|
||||
)?.results;
|
||||
const response = await request(
|
||||
`${urls.terrain}?points=${encodeURIComponent(query)}`,
|
||||
{ signal },
|
||||
);
|
||||
// A failed request still carries the real heights the server had, so
|
||||
// the caller can keep them; null marks a point without one.
|
||||
const partial = response.data?.partial;
|
||||
if (
|
||||
!response.ok &&
|
||||
Array.isArray(partial) &&
|
||||
partial.length === points.length
|
||||
) {
|
||||
const error = new Error(
|
||||
`Terrain heights unavailable (${response.status})`,
|
||||
);
|
||||
error.partial = partial;
|
||||
throw error;
|
||||
}
|
||||
return requireOk(response, 'Terrain heights')?.results;
|
||||
},
|
||||
},
|
||||
regional: {
|
||||
|
||||
@@ -110,6 +110,23 @@ test('a failed probe is retried after its backoff, not on every query', async ()
|
||||
]);
|
||||
});
|
||||
|
||||
test('a failed terrain request carries the real heights the server had', async () => {
|
||||
const partial = [{ ellipsoid: 412.5 }, null];
|
||||
const services = createApplicationRequestServices({
|
||||
fetchImpl: async () => Response.json({ error: 'x', partial }, { status: 502 }),
|
||||
});
|
||||
const points = [{ lat: 30, lon: -97 }, { lat: 31, lon: -97 }];
|
||||
await assert.rejects(services.terrain.getHeights(points), (error) => {
|
||||
assert.match(error.message, /Terrain heights unavailable \(502\)/);
|
||||
assert.deepEqual(error.partial, partial);
|
||||
return true;
|
||||
});
|
||||
await assert.rejects(services.terrain.getHeights(points.slice(0, 1)), (error) => {
|
||||
assert.equal(error.partial, undefined, 'a list of the wrong length is not attached');
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
test('the summary capability probe is asked once and unknown when unanswered', async () => {
|
||||
for (const [answer, expected] of [
|
||||
[() => Response.json({ configured: false }), false],
|
||||
|
||||
@@ -226,7 +226,8 @@ export function createTerrainHeights({
|
||||
* - Remaining (deduplicated) coordinates are sent to `/api/terrain/heights`
|
||||
* in sequential chunks of <=64 points.
|
||||
* - If a chunk request fails (network error, non-ok HTTP, malformed body),
|
||||
* every point in THAT chunk falls back to geoid math
|
||||
* every point in THAT chunk without a real height in the failure's
|
||||
* `partial` falls back to geoid math
|
||||
* (`sourceOrthometricM + geoidHeight` or `geoidHeight` alone) rather than
|
||||
* failing the whole batch — a transient proxy outage in the middle of a
|
||||
* large batch shouldn't blank out entities whose chunk happened to land
|
||||
@@ -314,15 +315,34 @@ export function createTerrainHeights({
|
||||
cacheStore(item.key, entry);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
} catch (error) {
|
||||
requestSignal?.throwIfAborted();
|
||||
// Proxy down (or cold cache had nothing to serve-stale) — fall back to
|
||||
// geoid math for every point in this chunk. `ensureGeoidReady()` is
|
||||
// awaited lazily, only on the fallback path, so the common (proxy
|
||||
// healthy) case never pays for the geoid grid's dynamic import.
|
||||
// geoid math for the points in this chunk. A failure that still
|
||||
// carries real heights (the proxy's `partial`, e.g. cached points
|
||||
// during an upstream 429 pause) keeps them; only the rest use the
|
||||
// geoid. `ensureGeoidReady()` is awaited lazily, only on the fallback
|
||||
// path, so the common (proxy healthy) case never pays for the geoid
|
||||
// grid's dynamic import.
|
||||
const partial =
|
||||
Array.isArray(error?.partial) && error.partial.length === chunk.length
|
||||
? error.partial
|
||||
: [];
|
||||
const fallback = [];
|
||||
chunk.forEach((item, index) => {
|
||||
const ellipsoid = partial[index]?.ellipsoid;
|
||||
if (!Number.isFinite(ellipsoid)) {
|
||||
fallback.push(item);
|
||||
return;
|
||||
}
|
||||
const entry = { ellipsoid, source: 'reearth' };
|
||||
resolvedThisCall.set(item.key, entry);
|
||||
cacheStore(item.key, entry);
|
||||
});
|
||||
if (fallback.length === 0) continue;
|
||||
await ensureGeoidReady();
|
||||
requestSignal?.throwIfAborted();
|
||||
for (const item of chunk) {
|
||||
for (const item of fallback) {
|
||||
const ellipsoid = geoidFallback(
|
||||
item.lat,
|
||||
item.lon,
|
||||
|
||||
Reference in new issue
Block a user