mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 07:42:23 +00:00
fix(cockpit): the sensor strip follows the view through a zoom step; the pre-load level test checks a worked number; the boundary gate's dependency-asset exemption is tested
This commit is contained in:
1 parent
1453794573
commit
63aa45c520
4 files changed
+84
-14
No files matched your search
@@ -1658,14 +1658,16 @@ the satellite's live altitude and the window's shape is still at least as
|
|||||||
wide as that swath (`orbitalMaxZoom` in `src/cockpitOrbital.js`); when the
|
wide as that swath (`orbitalMaxZoom` in `src/cockpitOrbital.js`); when the
|
||||||
cap falls below the zoom shown, the view steps back to it. Unconfirmed
|
cap falls below the zoom shown, the view steps back to it. Unconfirmed
|
||||||
swaths add no steps, but are still drawn, read out and offered to SENSOR.
|
swaths add no steps, but are still drawn, read out and offered to SENSOR.
|
||||||
The sensor's swath is drawn to scale as a strip along the ground track with
|
The sensor's swath is drawn to scale as a strip along the ground track (its
|
||||||
|
width follows the view through a zoom step, not ahead of it) with
|
||||||
a SENSOR SWATH readout, and a SENSOR button zooms to exactly that width when
|
a SENSOR SWATH readout, and a SENSOR button zooms to exactly that width when
|
||||||
it lies between 1× and the cap. No strip is drawn for a geostationary view
|
it lies between 1× and the cap. No strip is drawn for a geostationary view
|
||||||
(no ground track; its sensors image areas, not strips) or a SAR (it images
|
(no ground track; its sensors image areas, not strips) or a SAR (it images
|
||||||
to one side of nadir, not under it); the readout stays. Tilted off nadir
|
to one side of nadir, not under it); the readout stays. Tilted off nadir
|
||||||
the strip stays (view and swath widen alike), the readout scales as the swath
|
the strip stays (view and swath widen alike), the readout scales as the swath
|
||||||
readout does (`SENSOR SWATH ≈ 191 KM`), and SENSOR, a nadir framing, is
|
readout does (`SENSOR SWATH ≈ 191 KM`), and SENSOR, a nadir framing, is
|
||||||
hidden; tilted past the Earth's limb neither the sensor nor the swath reads. A swath is confirmed only on the operator's, an agency's (ESA's
|
hidden; tilted past the Earth's limb neither the sensor nor the swath
|
||||||
|
reads. A swath is confirmed only on the operator's, an agency's (ESA's
|
||||||
eoPortal included) or an academic page for its platform. From
|
eoPortal included) or an academic page for its platform. From
|
||||||
16×, with flat imagery on the globe (not Google 3D tiles) and the view at
|
16×, with flat imagery on the globe (not Google 3D tiles) and the view at
|
||||||
nadir, every 2 s the view asks the base imagery layer for the tiles it will
|
nadir, every 2 s the view asks the base imagery layer for the tiles it will
|
||||||
|
|||||||
@@ -126,3 +126,49 @@ test('a lifecycle owner cannot acquire a layer panel even through an unused impo
|
|||||||
);
|
);
|
||||||
await assert.rejects(checkPackageBoundaries(root), /unowned module.*panel/);
|
await assert.rejects(checkPackageBoundaries(root), /unowned module.*panel/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('a declared dependency owns the asset files it ships, and only those', async (t) => {
|
||||||
|
const asset = (pkg) =>
|
||||||
|
`import url from './node_modules/${pkg}/flags/aa.svg?url&no-inline'; export const value = url;`;
|
||||||
|
const root = await fixture(t, asset('flags'));
|
||||||
|
for (const pkg of ['flags', 'flags-extra', 'stray']) {
|
||||||
|
await mkdir(path.join(root, 'node_modules', pkg, 'flags'), {
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
|
await writeFile(
|
||||||
|
path.join(root, 'node_modules', pkg, 'flags/aa.svg'),
|
||||||
|
'<svg xmlns="http://www.w3.org/2000/svg"/>',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await writeFile(
|
||||||
|
path.join(root, 'package.json'),
|
||||||
|
JSON.stringify({
|
||||||
|
name: 'boundary-fixture',
|
||||||
|
type: 'module',
|
||||||
|
exports: { './feature': './entry.js' },
|
||||||
|
dependencies: { flags: '1.0.0' },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
await writeFile(
|
||||||
|
path.join(root, 'scripts/package-boundaries.json'),
|
||||||
|
JSON.stringify({
|
||||||
|
feature: {
|
||||||
|
exports: ['./feature'],
|
||||||
|
modules: ['entry.js'],
|
||||||
|
external: ['flags'],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
// The entry and the flag it ships.
|
||||||
|
assert.deepEqual(await checkPackageBoundaries(root), [
|
||||||
|
{ name: 'feature', exports: 1, modules: 2 },
|
||||||
|
]);
|
||||||
|
// An undeclared package's file, and one whose name only starts the same.
|
||||||
|
for (const pkg of ['stray', 'flags-extra']) {
|
||||||
|
await writeFile(path.join(root, 'entry.js'), asset(pkg));
|
||||||
|
await assert.rejects(
|
||||||
|
checkPackageBoundaries(root),
|
||||||
|
new RegExp(`unowned module.*node_modules/${pkg}/`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -631,6 +631,11 @@ export function publishOrbitalView(info, transition = IDLE_TRANSITION) {
|
|||||||
: canvasAspect(this.viewer);
|
: canvasAspect(this.viewer);
|
||||||
const settledFov = fovForZoom(orbitalBaseFovRad(info.altitudeM), state.zoom);
|
const settledFov = fovForZoom(orbitalBaseFovRad(info.altitudeM), state.zoom);
|
||||||
const footprintKm = footprintWidthKm(info.altitudeM, settledFov, aspect);
|
const footprintKm = footprintWidthKm(info.altitudeM, settledFov, aspect);
|
||||||
|
// The strip is drawn over the picture, so it follows the view as a step
|
||||||
|
// zooms (republished every frame then), not the step it is heading for.
|
||||||
|
const shownKm = Number.isFinite(state.verticalFov)
|
||||||
|
? footprintWidthKm(info.altitudeM, state.verticalFov, aspect)
|
||||||
|
: footprintKm;
|
||||||
const maxZoom = orbitalMaxZoomFor.call(this, info);
|
const maxZoom = orbitalMaxZoomFor.call(this, info);
|
||||||
const swathKm = sensorSwathKm(info);
|
const swathKm = sensorSwathKm(info);
|
||||||
const trackUp = Number.isFinite(info.groundTrackDeg);
|
const trackUp = Number.isFinite(info.groundTrackDeg);
|
||||||
@@ -687,7 +692,7 @@ export function publishOrbitalView(info, transition = IDLE_TRANSITION) {
|
|||||||
// along, and a SAR images to one side of nadir, not under it.
|
// along, and a SAR images to one side of nadir, not under it.
|
||||||
stripFraction:
|
stripFraction:
|
||||||
trackUp && info.sensor?.type !== 'SAR'
|
trackUp && info.sensor?.type !== 'SAR'
|
||||||
? sensorStripFraction(swathKm, footprintKm)
|
? sensorStripFraction(swathKm, shownKm)
|
||||||
: null,
|
: null,
|
||||||
zoom: sensorSwathZoom(info.altitudeM, swathKm, aspect, maxZoom),
|
zoom: sensorSwathZoom(info.altitudeM, swathKm, aspect, maxZoom),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import {
|
|||||||
} from '../cockpitOrbital.js';
|
} from '../cockpitOrbital.js';
|
||||||
import { vesselCockpitInfo } from '../layers/vessels/cockpitSubject.js';
|
import { vesselCockpitInfo } from '../layers/vessels/cockpitSubject.js';
|
||||||
import { prefetchImageryAhead } from './cockpitOrbitalView.js';
|
import { prefetchImageryAhead } from './cockpitOrbitalView.js';
|
||||||
import { imageryLevelForView } from '../cockpitOrbital.js';
|
|
||||||
import { claimPointer, releasePointer } from '../data/inputOwnership.js';
|
import { claimPointer, releasePointer } from '../data/inputOwnership.js';
|
||||||
|
|
||||||
const ISS = Object.freeze({
|
const ISS = Object.freeze({
|
||||||
@@ -1161,6 +1160,27 @@ function imageryGlobe(viewer, { requestImage } = {}) {
|
|||||||
|
|
||||||
const WORLDVIEW_ON_TRACK = Object.freeze({ ...WORLDVIEW, groundTrackDeg: 52 });
|
const WORLDVIEW_ON_TRACK = Object.freeze({ ...WORLDVIEW, groundTrackDeg: 52 });
|
||||||
|
|
||||||
|
test('the sensor strip follows the view through a zoom step, not ahead of it', async (t) => {
|
||||||
|
const { cockpit, store, clock } = await orbitAt(t, {
|
||||||
|
satellite: WORLDVIEW,
|
||||||
|
reducedMotion: false,
|
||||||
|
});
|
||||||
|
const strip = () => store.orbital.sensor.stripFraction;
|
||||||
|
const atOne = strip();
|
||||||
|
assert.ok(atOne > 0 && atOne < 1);
|
||||||
|
press(cockpit, '+');
|
||||||
|
cockpit.update();
|
||||||
|
near(strip(), atOne, 'the step starts at 1x, and so does the strip');
|
||||||
|
clock.advance(ORBITAL_ZOOM_TRANSITION_MS / 2);
|
||||||
|
cockpit.update();
|
||||||
|
const midway = strip();
|
||||||
|
clock.advance(ORBITAL_ZOOM_TRANSITION_MS + 1000);
|
||||||
|
cockpit.update();
|
||||||
|
const atTwo = strip();
|
||||||
|
assert.ok(atTwo > atOne * 1.5, 'twice the zoom, about twice the share');
|
||||||
|
assert.ok(midway > atOne && midway < atTwo, 'part-way through the step');
|
||||||
|
});
|
||||||
|
|
||||||
test('from 16x the imagery ahead of the ground track is pre-loaded', async (t) => {
|
test('from 16x the imagery ahead of the ground track is pre-loaded', async (t) => {
|
||||||
const { cockpit, viewer, store, clock } = await orbitAt(t, {
|
const { cockpit, viewer, store, clock } = await orbitAt(t, {
|
||||||
satellite: WORLDVIEW_ON_TRACK,
|
satellite: WORLDVIEW_ON_TRACK,
|
||||||
@@ -1444,16 +1464,13 @@ test('the pre-load is throttled, at the level Cesium draws, and waits out a tilt
|
|||||||
);
|
);
|
||||||
assert.equal(request.type, Cesium.RequestType.IMAGERY);
|
assert.equal(request.type, Cesium.RequestType.IMAGERY);
|
||||||
}
|
}
|
||||||
// The level Cesium's own selection takes for this view.
|
// The level Cesium's own selection takes for this view, worked by hand:
|
||||||
const fov = fovForZoom(orbitalBaseFovRad(WORLDVIEW.altitudeM), 16);
|
// at 16x from 617 km the 900 px canvas is ~49.5 m a pixel. The ellipsoid
|
||||||
const metresPerPixel =
|
// terrain's level-zero error is 6378137 · 2π / 4 / 130 ≈ 77,067 m; it first
|
||||||
(WORLDVIEW.altitudeM * 2 * Math.tan(fov / 2)) /
|
// meets Cesium's screen-space error of 2 px (≤ 99 m) at level 10, 75.3 m.
|
||||||
viewer.scene.canvas.clientHeight;
|
// A Web Mercator level-zero texel at 30° is 6378137 · 2π · cos 30° / 256
|
||||||
const expected = imageryLevelForView(new Cesium.WebMercatorTilingScheme(), {
|
// ≈ 135,566 m, so the imagery level is round(log2(135,566 / 75.3)) = 11.
|
||||||
metresPerPixel,
|
assert.equal(Number(requests[0].split('/')[0]), 11);
|
||||||
latitudeDeg: WORLDVIEW.latitude,
|
|
||||||
});
|
|
||||||
assert.equal(Number(requests[0].split('/')[0]), expected);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the TLE age is measured from the time the satellite is shown at', async (t) => {
|
test('the TLE age is measured from the time the satellite is shown at', async (t) => {
|
||||||
|
|||||||
Reference in new issue
Block a user