Fix the follow-up review findings in import, layers, startup and engines

- KML/KMZ import: a per-file canvas budget and cache for ground overlays,
  limits on rings and vertices of raised polygons, overlays across 360° and
  near the poles, degenerate holes, polar and single-latitude fills,
  absolute overlays at their altitude, overlay order by drawOrder as on
  Cesium, and pins on tiles keep their measured height.
- Points on the surface: OSM Places and Street Level cones share the
  surface sampler and sit on the terrain before they are measured, within
  a sampling budget per pass; Cockpit's contact-dot bank keeps the dot
  atlas, and a failed fleet model loads again after a pause.
- Startup: the loading screen lifts after the share-link view is restored,
  the retry screen names the failed engine and takes focus, and the
  fallback toast stays for 6 s.
- Engines: an engine switch lands a running camera flight at its
  destination (camera.completeFlight on both engines), the sighting card
  leaves focus on the engine picker after a switch, and a Cesium model
  error stays with the instance that failed.
- The MCP panel builds again: its wind-mesh alias resolves to the stub.
- QA: the wildlife switch waits for the camera to rest, GBFS --w2 says it
  needs the dev server, and test fixtures are shared instead of copied.
This commit is contained in:
German Khokhlov committed 2026-11-10 01:57:11 +04:00
1 parent 0ef29781e5
commit 60fb8fee07
82 files changed
+2363 -1574

No files matched your search

+36
View File
@@ -442,6 +442,42 @@ test('a model that fails to load marks its instance failed, once', async (t) =>
assert.equal(set.failed(), false, 'a destroyed set reports nothing');
});
// A layer removes an instance by moving its last one into the freed index
// (src/layers/shared/render/moverBank.js).
test('an instance moved into a failed index loads its own model', async (t) => {
t.mock.method(console, 'warn', () => {});
let loads = 0;
const { movers } = setup({
loadModel: async (options) => {
if (loads++ === 0) throw new Error('503');
return fakeModel(options);
},
});
const set = movers.createSet({
id: 'fleet',
kind: 'model',
capacity: 2,
model: { url: '/a.glb' },
});
set.set(0, { position: at(0, 0), pickId: 'a' });
set.set(1, { position: at(1, 0), pickId: 'b' });
set.setCount(2);
set.commit();
await settle();
assert.equal(set.failed(0), true);
assert.equal(set.failed(1), false);
// 'a' is removed: 'b' moves into index 0.
set.set(0, { position: at(1, 0), pickId: 'b' });
set.setCount(1);
assert.equal(set.failed(0), false, "'b' does not take the failure of 'a'");
assert.equal(set.failed(), false);
set.commit();
await settle();
assert.equal(loads, 3, 'index 0 loads again for its new instance');
assert.equal(set.failed(0), false);
set.destroy();
});
test('identify names the set, instance and pickId of a picked billboard or point', async () => {
const { movers, root, identify, viewer } = setup();
// The fake camera is over (0°, ~1°), 7000 km from the centre.