Files
gods-eye-view/src/ui/flyToDataSource.test.mjs
T
kapturnerandClaude Opus 5.5 644a341b4c fix(import): address review on metadata, bounds, navigation, clock and GPX
- Imported metadata stays inert: descriptions, native labels, properties and
  KML extended data are dropped from every entity; names are kept as short
  plain text. GeoJSON loads with a describe that builds no HTML, and
  simplestyle (including marker-symbol) is dropped, so no icon asset is
  fetched for a file and GeoJSON draws in its row's color.
- Bound geometry before loading: features and vertices are counted in the
  parsed GeoJSON, the KML document and while reading GPX, and a file over
  20,000 features or 250,000 vertices is refused before Cesium builds it.
- Imports run one batch at a time, so concurrent picks or drops cannot pass
  the 12-file cap.
- Fly-to framing walks long tracks by index instead of spreading them into a
  call.
- Fly-to is an immediate navigation through the application shell; the fly
  after an import is a deferred navigation stamped when the batch starts and
  re-asserted just before flying.
- Dated imports never change the shared clock: imported sources expose no
  clock, entities are available for all time, and a KML gx:Track is drawn as
  its line.
- GPX is read into GeoJSON with one line per track segment, without times.
- KML/KMZ rows show a hatched swatch (they keep their own styles); keyboard
  focus is kept on the same control after hide/show and moves to the next
  row after remove.
- Unit regressions for each, and the rendered QA now imports hostile
  metadata, dated KML/GPX and a two-segment track, and fails on any request
  for a file's contents or icon assets, a clock change, or surviving
  metadata.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 08:50:35 -05:00

77 lines
2.6 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import * as Cesium from 'cesium';
import { dataSourcePoints, flyToDataSource } from './flyToDataSource.js';
function source() {
const ds = new Cesium.CustomDataSource('t');
ds.entities.add({
position: Cesium.Cartesian3.fromDegrees(-122.4862, 37.7694, -2667),
billboard: { heightReference: Cesium.HeightReference.CLAMP_TO_GROUND },
});
ds.entities.add({
polyline: {
positions: Cesium.Cartesian3.fromDegreesArray([
-122.49, 37.77, -122.48, 37.771,
]),
},
});
ds.entities.add({
polygon: {
hierarchy: Cesium.Cartesian3.fromDegreesArray([
-122.5, 37.76, -122.49, 37.76, -122.49, 37.765,
]),
},
});
return ds;
}
test('points come from positions, lines and polygons, as lon/lat only', () => {
const points = dataSourcePoints(source());
assert.equal(points.length, 6);
assert.ok(Math.abs(points[0][0] + 122.4862) < 1e-6);
assert.ok(Math.abs(points[0][1] - 37.7694) < 1e-6);
});
test('the camera frames the footprint at the surface, never a buried center', () => {
const calls = [];
const viewer = {
camera: {
heading: 0,
flyToBoundingSphere: (sphere, options) => calls.push({ sphere, options }),
},
flyTo: () => calls.push('viewer.flyTo'),
};
assert.equal(flyToDataSource(viewer, source()), true);
const center = Cesium.Cartographic.fromCartesian(calls[0].sphere.center);
assert.ok(Math.abs(center.height) < 50, `center height ${center.height}`);
assert.ok(calls[0].options.offset.range >= 800);
assert.equal(
typeof calls[0].options.complete,
'function',
'the ground guard runs on landing',
);
assert.equal(
flyToDataSource(viewer, new Cesium.CustomDataSource('empty')),
false,
);
assert.equal(calls.at(-1), 'viewer.flyTo');
});
test('a very long track is framed without spreading it into a call', () => {
// 400k vertices: `push(...line)` throws RangeError (too many arguments)
// well below this; the walk keeps a bounded sample and both ends.
const n = 400_000;
const line = new Array(n);
for (let i = 0; i < n; i++)
line[i] = Cesium.Cartesian3.fromDegrees(-120 + (i / n) * 2, 40);
const ds = new Cesium.CustomDataSource('long');
ds.entities.add({ polyline: { positions: line } });
ds.entities.add({ position: Cesium.Cartesian3.fromDegrees(-110, 41) });
const points = dataSourcePoints(ds);
assert.ok(points.length <= 5002, `${points.length} points kept`);
const lons = points.map(([lon]) => lon);
assert.ok(Math.min(...lons) < -119.99 && Math.max(...lons) > -110.01);
assert.ok(lons.some((lon) => Math.abs(lon - -118.000005) < 0.01));
});