mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
feat(globe): bring the Navara engine up to the shell contract
- camera.livePosition: one engine-owned Cartesian3, refreshed on read (the model replaces its state on every move); camera.height() - render.request(reason) takes the diagnostic reason and ignores it - maps.current() returns the sources as passed: the base imagery on the globe (none while tiles replace it) and the tiles shown - maps.releasePhotorealTiles forgets a source and takes its tiles away if they show - base imagery and photoreal share one switch order; tiles off brings back the last base imagery with its credit; imagery chosen while tiles show waits hidden, credit included - Navara QA runs the contract's maps checks with a Google 3D tiles source on the dev server's browser key: 58/58
This commit is contained in:
1 parent
a4ac0b9b0d
commit
840e34a881
5 files changed
+127
-7
No files matched your search
@@ -266,7 +266,15 @@ export function createCameraModel({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// camera.livePosition: one owned object, refreshed on read (place() and
|
||||||
|
// sync() replace `state`, so it is copied rather than aliased).
|
||||||
|
const livePosition = cartesian3.create();
|
||||||
|
|
||||||
const camera = {
|
const camera = {
|
||||||
|
get livePosition() {
|
||||||
|
return cartesian3.clone(state.position, livePosition);
|
||||||
|
},
|
||||||
|
height: () => cartographic.fromCartesian(state.position, WGS84).height,
|
||||||
pose() {
|
pose() {
|
||||||
const current = view();
|
const current = view();
|
||||||
const angles = headingPitchRoll(
|
const angles = headingPitchRoll(
|
||||||
|
|||||||
@@ -439,3 +439,21 @@ test('rounding noise in the drawn camera is no movement, so moveEnd still fires'
|
|||||||
assert.equal(clock.time >= 960, true);
|
assert.equal(clock.time >= 960, true);
|
||||||
assert.equal(ends, 1, 'moveEnd did not fire while Navara redrew the pose');
|
assert.equal(ends, 1, 'moveEnd did not fire while Navara redrew the pose');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('livePosition is one engine-owned object holding the current position; height() reads it', () => {
|
||||||
|
const { camera, port, frame } = setup();
|
||||||
|
const live = camera.livePosition;
|
||||||
|
const destination = cartesian3.fromDegrees(20, 10, 12345);
|
||||||
|
camera.setView({ destination });
|
||||||
|
assert.equal(camera.livePosition, live, 'not the same object');
|
||||||
|
near(cartesian3.distance(live, destination), 0, 1e-9, 'follows setView');
|
||||||
|
near(camera.height(), 12345, 1e-6, 'height');
|
||||||
|
frame();
|
||||||
|
const moved = cartesian3.fromDegrees(21, 11, 9e5);
|
||||||
|
port.drawn = { position: moved, ...orientationVectors(moved, 0.3, -1, 0) };
|
||||||
|
frame();
|
||||||
|
near(cartesian3.distance(camera.livePosition, moved), 0, 1e-9, 'follows Navara');
|
||||||
|
near(camera.height(), 9e5, 1e-6, 'height after a user move');
|
||||||
|
const pose = camera.pose();
|
||||||
|
assert.notEqual(pose.position, live, 'pose shares the live object');
|
||||||
|
});
|
||||||
@@ -115,6 +115,8 @@ test('render requests are forceUpdate and continuous mode is Navara animation',
|
|||||||
const { engine, view } = setup();
|
const { engine, view } = setup();
|
||||||
engine.render.request();
|
engine.render.request();
|
||||||
assert.deepEqual(view.calls.at(-1), ['forceUpdate']);
|
assert.deepEqual(view.calls.at(-1), ['forceUpdate']);
|
||||||
|
engine.render.request('world-overlay');
|
||||||
|
assert.deepEqual(view.calls.at(-1), ['forceUpdate'], 'the reason is ignored');
|
||||||
engine.render.setContinuous(true);
|
engine.render.setContinuous(true);
|
||||||
assert.equal(view.animation, true);
|
assert.equal(view.animation, true);
|
||||||
assert.equal(engine.render.isContinuous(), true);
|
assert.equal(engine.render.isContinuous(), true);
|
||||||
|
|||||||
@@ -67,7 +67,9 @@ export function createNavaraMaps(
|
|||||||
{ fetch: fetchJson = globalThis.fetch } = {},
|
{ fetch: fetchJson = globalThis.fetch } = {},
|
||||||
) {
|
) {
|
||||||
const slots = { imagery: null, terrain: null, photoreal: null };
|
const slots = { imagery: null, terrain: null, photoreal: null };
|
||||||
const generations = { imagery: 0, terrain: 0, photoreal: 0 };
|
// Base imagery and photoreal tiles share one switch order (display): the
|
||||||
|
// call made last decides what shows.
|
||||||
|
const generations = { display: 0, terrain: 0 };
|
||||||
let destroyed = false;
|
let destroyed = false;
|
||||||
|
|
||||||
function retire(slot) {
|
function retire(slot) {
|
||||||
@@ -95,7 +97,14 @@ export function createNavaraMaps(
|
|||||||
);
|
);
|
||||||
retire(previous);
|
retire(previous);
|
||||||
if (shown.length) view.attribution?.add(shown);
|
if (shown.length) view.attribution?.add(shown);
|
||||||
slots[name] = { key, source, layer, layerDesc, credits: shown };
|
slots[name] = {
|
||||||
|
key,
|
||||||
|
source,
|
||||||
|
layer,
|
||||||
|
layerDesc,
|
||||||
|
credits: shown,
|
||||||
|
hidden: false,
|
||||||
|
};
|
||||||
view.forceUpdate();
|
view.forceUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,15 +155,26 @@ export function createNavaraMaps(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// What current() reports: the base imagery on the globe (none while tiles
|
||||||
|
// replace it) and the tiles shown, as the objects passed in.
|
||||||
|
let baseSource = null;
|
||||||
|
let currentBase = null;
|
||||||
|
let tilesSource = null;
|
||||||
|
const knownTiles = new Set();
|
||||||
|
|
||||||
const maps = {
|
const maps = {
|
||||||
async setBaseImagery(source) {
|
async setBaseImagery(source) {
|
||||||
const generation = ++generations.imagery;
|
const generation = ++generations.display;
|
||||||
const { url, credit } = imageryFor(source);
|
const { url, credit } = imageryFor(source);
|
||||||
if (destroyed || generation !== generations.imagery) return;
|
if (destroyed || generation !== generations.display) return;
|
||||||
if (slots.imagery?.key === url) return setImageryShown(true);
|
baseSource = source;
|
||||||
|
currentBase = source;
|
||||||
|
if (slots.imagery?.key !== url)
|
||||||
show('imagery', url, { type: 'raster-tile', url }, { type: 'raster' }, [
|
show('imagery', url, { type: 'raster-tile', url }, { type: 'raster' }, [
|
||||||
credit,
|
credit,
|
||||||
]);
|
]);
|
||||||
|
// Under tiles the base imagery waits hidden, credit included.
|
||||||
|
setImageryShown(slots.photoreal == null);
|
||||||
},
|
},
|
||||||
async setTerrain(source) {
|
async setTerrain(source) {
|
||||||
const generation = ++generations.terrain;
|
const generation = ++generations.terrain;
|
||||||
@@ -169,9 +189,15 @@ export function createNavaraMaps(
|
|||||||
show('terrain', description.url, description, { type: 'terrain' });
|
show('terrain', description.url, description, { type: 'terrain' });
|
||||||
},
|
},
|
||||||
async setPhotorealTiles(source) {
|
async setPhotorealTiles(source) {
|
||||||
const generation = ++generations.photoreal;
|
const generation = ++generations.display;
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
clear('photoreal');
|
clear('photoreal');
|
||||||
|
tilesSource = null;
|
||||||
|
// The globe comes back with the last base imagery and its credit.
|
||||||
|
if (slots.imagery) {
|
||||||
|
setImageryShown(true);
|
||||||
|
currentBase = baseSource;
|
||||||
|
}
|
||||||
view.forceUpdate();
|
view.forceUpdate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -180,9 +206,12 @@ export function createNavaraMaps(
|
|||||||
throw new Error(
|
throw new Error(
|
||||||
'Google 3D tiles on Navara need a browser key (googleApiKey)',
|
'Google 3D tiles on Navara need a browser key (googleApiKey)',
|
||||||
);
|
);
|
||||||
if (destroyed || generation !== generations.photoreal) return;
|
if (destroyed || generation !== generations.display) return;
|
||||||
const url = `${GOOGLE_TILES_ROOT}?key=${encodeURIComponent(key)}`;
|
const url = `${GOOGLE_TILES_ROOT}?key=${encodeURIComponent(key)}`;
|
||||||
setImageryShown(false);
|
setImageryShown(false);
|
||||||
|
currentBase = null;
|
||||||
|
tilesSource = source;
|
||||||
|
knownTiles.add(source);
|
||||||
if (slots.photoreal?.key === url) return;
|
if (slots.photoreal?.key === url) return;
|
||||||
show(
|
show(
|
||||||
'photoreal',
|
'photoreal',
|
||||||
@@ -201,7 +230,15 @@ export function createNavaraMaps(
|
|||||||
// them; Navara lists them in its attribution card, collapsed by default.
|
// them; Navara lists them in its attribution card, collapsed by default.
|
||||||
view.attribution?.show();
|
view.attribution?.show();
|
||||||
},
|
},
|
||||||
|
releasePhotorealTiles(source) {
|
||||||
|
if (!knownTiles.delete(source)) return;
|
||||||
|
if (tilesSource !== source) return;
|
||||||
|
tilesSource = null;
|
||||||
|
clear('photoreal');
|
||||||
|
view.forceUpdate();
|
||||||
|
},
|
||||||
photorealActive: () => slots.photoreal != null,
|
photorealActive: () => slots.photoreal != null,
|
||||||
|
current: () => ({ baseImagery: currentBase, photoreal: tilesSource }),
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -223,3 +223,58 @@ test('destroy removes every layer, source and credit it added', async () => {
|
|||||||
assert.deepEqual(view.sources, []);
|
assert.deepEqual(view.sources, []);
|
||||||
assert.deepEqual(view.shown, []);
|
assert.deepEqual(view.shown, []);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('current() returns the passed sources: base while the globe shows, tiles while they show', async () => {
|
||||||
|
const view = fakeView();
|
||||||
|
const { maps } = createNavaraMaps(view);
|
||||||
|
const osm = { kind: 'osm' };
|
||||||
|
const esri = { kind: 'esri-imagery' };
|
||||||
|
const tiles = { kind: 'photoreal', googleApiKey: 'k' };
|
||||||
|
assert.deepEqual(maps.current(), { baseImagery: null, photoreal: null });
|
||||||
|
await maps.setBaseImagery(osm);
|
||||||
|
assert.deepEqual(maps.current(), { baseImagery: osm, photoreal: null });
|
||||||
|
await maps.setPhotorealTiles(tiles);
|
||||||
|
assert.equal(maps.current().baseImagery, null, 'tiles replace the base');
|
||||||
|
assert.equal(maps.current().photoreal, tiles);
|
||||||
|
// Imagery chosen while the tiles show goes on the hidden globe.
|
||||||
|
await maps.setBaseImagery(esri);
|
||||||
|
const [raster] = layersOf(view, 'raster');
|
||||||
|
assert.match(raster.desc.source.desc.url, /World_Imagery/);
|
||||||
|
assert.equal(raster.desc.raster?.show, false, 'the new layer shows over tiles');
|
||||||
|
assert.ok(
|
||||||
|
!view.shown.some((item) => /esri/i.test(JSON.stringify(item))),
|
||||||
|
'its credit shows over tiles',
|
||||||
|
);
|
||||||
|
assert.equal(maps.current().baseImagery, esri);
|
||||||
|
assert.equal(maps.current().photoreal, tiles);
|
||||||
|
await maps.setPhotorealTiles(null);
|
||||||
|
assert.deepEqual(maps.current(), { baseImagery: esri, photoreal: null });
|
||||||
|
assert.notEqual(raster.desc.raster?.show, false, 'base not back');
|
||||||
|
assert.ok(view.shown.some((item) => /esri/i.test(JSON.stringify(item))), 'credit not back');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the same tiles under a new source object count as that source', async () => {
|
||||||
|
const view = fakeView();
|
||||||
|
const { maps } = createNavaraMaps(view);
|
||||||
|
const first = { kind: 'photoreal', googleApiKey: 'k' };
|
||||||
|
const second = { kind: 'photoreal', googleApiKey: 'k' };
|
||||||
|
await maps.setPhotorealTiles(first);
|
||||||
|
await maps.setPhotorealTiles(second);
|
||||||
|
assert.equal(maps.current().photoreal, second);
|
||||||
|
assert.equal(layersOf(view, '3d-tiles').length, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('releasePhotorealTiles forgets the source and takes its tiles away if shown', async () => {
|
||||||
|
const view = fakeView();
|
||||||
|
const { maps } = createNavaraMaps(view);
|
||||||
|
const tiles = { kind: 'photoreal', googleApiKey: 'k' };
|
||||||
|
await maps.setPhotorealTiles(tiles);
|
||||||
|
maps.releasePhotorealTiles({ kind: 'photoreal', googleApiKey: 'k' });
|
||||||
|
assert.equal(maps.photorealActive(), true, 'another object released it');
|
||||||
|
maps.releasePhotorealTiles(tiles);
|
||||||
|
maps.releasePhotorealTiles(tiles);
|
||||||
|
assert.equal(maps.photorealActive(), false);
|
||||||
|
assert.equal(maps.current().photoreal, null);
|
||||||
|
assert.equal(layersOf(view, '3d-tiles').length, 0);
|
||||||
|
});
|
||||||
|
|
||||||
Reference in new issue
Block a user