import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Terria from "../../../../lib/Models/Terria";
import Cesium3DTilesCatalogItem from "../../../../lib/Models/Catalog/CatalogItems/Cesium3DTilesCatalogItem";
import ViewingControls from "../../../../lib/ReactViews/Workbench/Controls/ViewingControls";
import ViewState from "../../../../lib/ReactViewModels/ViewState";
import CesiumMath from "terriajs-cesium/Source/Core/Math";
import {
IdealZoomTraits,
CameraTraits,
LookAtTraits
} from "../../../../lib/Traits/TraitsClasses/MappableTraits";
import createStratumInstance from "../../../../lib/Models/Definition/createStratumInstance";
describe("Ideal Zoom", function () {
let terria: Terria;
let theItem: Cesium3DTilesCatalogItem;
let viewState: ViewState;
beforeEach(function () {
terria = new Terria({
baseUrl: "./"
});
theItem = new Cesium3DTilesCatalogItem("my3dtiles", terria);
theItem.setTrait("definition", "url", "/test/Cesium3DTiles/tileset.json");
const options = {
terria: terria,
catalogSearchProvider: undefined
};
viewState = new ViewState(options);
});
it("should use default camera view if no parameters are given.", async function () {
await theItem.loadMapItems();
render();
await userEvent.click(
screen.getByRole("button", { name: "workbench.zoomTo" })
);
const theCameraView = terria.currentViewer.getCurrentCameraView();
expect(theCameraView.direction).toBe(undefined);
expect(theCameraView.position).toBe(undefined);
expect(theCameraView.up).toBe(undefined);
// The rectangle values are from default camera view.
const rectangle = {
east: 3.141592653589793,
north: 1.5707963267948966,
south: -1.5707963267948966,
west: -3.141592653589793
};
expect(theCameraView.rectangle?.east).toBeCloseTo(rectangle.east, 6);
expect(theCameraView.rectangle?.north).toBeCloseTo(rectangle.north, 6);
expect(theCameraView.rectangle?.south).toBeCloseTo(rectangle.south, 6);
expect(theCameraView.rectangle?.west).toBeCloseTo(rectangle.west, 6);
});
it("should customise camera view if the given lookAt parameters are valid.", async function () {
const lookAtValues = {
targetLongitude: 150.60832,
targetLatitude: -34.19483,
targetHeight: 200,
heading: 180,
pitch: 15,
range: 200
};
const lookAt = createStratumInstance(LookAtTraits, lookAtValues);
const idealZoom = createStratumInstance(IdealZoomTraits);
idealZoom.lookAt = lookAt;
theItem.setTrait("definition", "idealZoom", idealZoom);
await theItem.loadMapItems();
render();
await userEvent.click(
screen.getByRole("button", { name: "workbench.zoomTo" })
);
const theCameraView = terria.currentViewer.getCurrentCameraView();
const directionX = 0.6595071845691584;
const directionY = -0.37148703631220265;
const directionZ = -0.6534888333809832;
expect(theCameraView.direction?.x).toBeCloseTo(directionX, 6);
expect(theCameraView.direction?.y).toBeCloseTo(directionY, 6);
expect(theCameraView.direction?.z).toBeCloseTo(directionZ, 6);
const positionX = -4601657.155837906;
const positionY = 2592020.25230978;
const positionZ = -3564324.3086873693;
expect(theCameraView.position?.x).toBeCloseTo(positionX, 6);
expect(theCameraView.position?.y).toBeCloseTo(positionY, 6);
expect(theCameraView.position?.z).toBeCloseTo(positionZ, 6);
const upX = -0.5693750748843667;
const upY = 0.3207174448857751;
const upZ = -0.7569361562551771;
expect(theCameraView.up?.x).toBeCloseTo(upX, 6);
expect(theCameraView.up?.y).toBeCloseTo(upY, 6);
expect(theCameraView.up?.z).toBeCloseTo(upZ, 6);
// The rectangle values are calculated from the given idealZoom parameters,
// i.e., the customised camera's direction, position and up parameters.
// A 2D viewer will zoom to this rectangle only.
const rectangle = {
east: 2.6286473947864932,
north: -0.5967760407704654,
south: -0.596848700549,
west: 2.628574735007959
};
expect(theCameraView.rectangle?.east).toBeCloseTo(rectangle.east, 6);
expect(theCameraView.rectangle?.north).toBeCloseTo(rectangle.north, 6);
expect(theCameraView.rectangle?.south).toBeCloseTo(rectangle.south, 6);
expect(theCameraView.rectangle?.west).toBeCloseTo(rectangle.west, 6);
});
it("should override the using home camera rule.", async function () {
// Using home camera rule might kick in given the wrapped rectangle.
const wrappedRectangle = {
east: 511,
north: -33,
west: 149,
south: -35
};
theItem.setTrait("definition", "rectangle", wrappedRectangle);
// But using home camera rule will be orverridden by idealZoom.
const lookAtValues = {
targetLongitude: 150.60832,
targetLatitude: -34.19483,
targetHeight: 200,
heading: 180,
pitch: 15,
range: 200
};
const lookAt = createStratumInstance(LookAtTraits, lookAtValues);
const idealZoom = createStratumInstance(IdealZoomTraits);
idealZoom.lookAt = lookAt;
theItem.setTrait("definition", "idealZoom", idealZoom);
await theItem.loadMapItems();
render();
await userEvent.click(
screen.getByRole("button", { name: "workbench.zoomTo" })
);
const theCameraView = terria.currentViewer.getCurrentCameraView();
const directionX = 0.6595071845691584;
const directionY = -0.37148703631220265;
const directionZ = -0.6534888333809832;
expect(theCameraView.direction?.x).toBeCloseTo(directionX, 6);
expect(theCameraView.direction?.y).toBeCloseTo(directionY, 6);
expect(theCameraView.direction?.z).toBeCloseTo(directionZ, 6);
const positionX = -4601657.155837906;
const positionY = 2592020.25230978;
const positionZ = -3564324.3086873693;
expect(theCameraView.position?.x).toBeCloseTo(positionX, 6);
expect(theCameraView.position?.y).toBeCloseTo(positionY, 6);
expect(theCameraView.position?.z).toBeCloseTo(positionZ, 6);
const upX = -0.5693750748843667;
const upY = 0.3207174448857751;
const upZ = -0.7569361562551771;
expect(theCameraView.up?.x).toBeCloseTo(upX, 6);
expect(theCameraView.up?.y).toBeCloseTo(upY, 6);
expect(theCameraView.up?.z).toBeCloseTo(upZ, 6);
// The rectangle values are calculated from the given idealZoom parameters,
// i.e., the customised camera's direction, position and up parameters.
// A 2D viewer will zoom to this rectangle only.
const rectangle = {
east: 2.6286473947864932,
north: -0.5967760407704654,
south: -0.596848700549,
west: 2.628574735007959
};
expect(theCameraView.rectangle?.east).toBeCloseTo(rectangle.east, 6);
expect(theCameraView.rectangle?.north).toBeCloseTo(rectangle.north, 6);
expect(theCameraView.rectangle?.south).toBeCloseTo(rectangle.south, 6);
expect(theCameraView.rectangle?.west).toBeCloseTo(rectangle.west, 6);
});
it("should use default camera view if the given lookAt parameters are invalid.", async function () {
const lookAtValues = {
targetLongitude: undefined,
targetLatitude: -34.19483,
targetHeight: 200,
heading: 180,
pitch: 15,
range: 200
};
const lookAt = createStratumInstance(LookAtTraits, lookAtValues);
const idealZoom = createStratumInstance(IdealZoomTraits);
idealZoom.lookAt = lookAt;
theItem.setTrait("definition", "idealZoom", idealZoom);
await theItem.loadMapItems();
render();
await userEvent.click(
screen.getByRole("button", { name: "workbench.zoomTo" })
);
const theCameraView = terria.currentViewer.getCurrentCameraView();
expect(theCameraView.direction).toBe(undefined);
expect(theCameraView.position).toBe(undefined);
expect(theCameraView.up).toBe(undefined);
});
it("should customise camera view if the given camera parameters are complete.", async function () {
const cameraValues = {
west: 143.85665964592238,
south: -37.5588985189224,
east: 143.85932639124115,
north: -37.55761610087383,
position: {
x: -4088564.111098504,
y: 2985823.720726511,
z: -3867066.0705771768
},
direction: {
x: 0.25654239033528903,
y: 0.8049473516085732,
z: 0.5350194044138963
},
up: {
x: -0.6168995401836412,
y: 0.5624968267277541,
z: -0.5504836757274634
}
};
const camera = createStratumInstance(CameraTraits, cameraValues);
const idealZoom = createStratumInstance(IdealZoomTraits);
idealZoom.camera = camera;
theItem.setTrait("definition", "idealZoom", idealZoom);
await theItem.loadMapItems();
render();
await userEvent.click(
screen.getByRole("button", { name: "workbench.zoomTo" })
);
const theCameraView = terria.currentViewer.getCurrentCameraView();
expect(theCameraView.direction?.x).toBeCloseTo(cameraValues.direction.x, 6);
expect(theCameraView.direction?.y).toBeCloseTo(cameraValues.direction.y, 6);
expect(theCameraView.direction?.z).toBeCloseTo(cameraValues.direction.z, 6);
expect(theCameraView.position?.x).toBeCloseTo(cameraValues.position.x, 6);
expect(theCameraView.position?.y).toBeCloseTo(cameraValues.position.y, 6);
expect(theCameraView.position?.z).toBeCloseTo(cameraValues.position.z, 6);
expect(theCameraView.up?.x).toBeCloseTo(cameraValues.up.x, 6);
expect(theCameraView.up?.y).toBeCloseTo(cameraValues.up.y, 6);
expect(theCameraView.up?.z).toBeCloseTo(cameraValues.up.z, 6);
expect(theCameraView.rectangle?.east).toBeCloseTo(
CesiumMath.toRadians(cameraValues.east),
6
);
expect(theCameraView.rectangle?.north).toBeCloseTo(
CesiumMath.toRadians(cameraValues.north),
6
);
expect(theCameraView.rectangle?.south).toBeCloseTo(
CesiumMath.toRadians(cameraValues.south),
6
);
expect(theCameraView.rectangle?.west).toBeCloseTo(
CesiumMath.toRadians(cameraValues.west),
6
);
});
it("should use given rectangle if any other values are missing in camera.", async function () {
const cameraValues = {
west: 143.85665964592238,
south: -37.5588985189224,
east: 143.85932639124115,
north: -37.55761610087383,
position: {
x: -4088564.111098504,
y: 2985823.720726511,
z: -3867066.0705771768
},
direction: {
x: 0.25654239033528903,
y: 0.8049473516085732,
z: 0.5350194044138963
},
up: {
x: -0.6168995401836412,
y: 0.5624968267277541,
z: undefined
}
};
const camera = createStratumInstance(CameraTraits, cameraValues);
const idealZoom = createStratumInstance(IdealZoomTraits);
idealZoom.camera = camera;
theItem.setTrait("definition", "idealZoom", idealZoom);
await theItem.loadMapItems();
render();
await userEvent.click(
screen.getByRole("button", { name: "workbench.zoomTo" })
);
const theCameraView = terria.currentViewer.getCurrentCameraView();
expect(theCameraView.direction).toBe(undefined);
expect(theCameraView.position).toBe(undefined);
expect(theCameraView.up).toBe(undefined);
expect(theCameraView.rectangle?.east).toBeCloseTo(
CesiumMath.toRadians(cameraValues.east),
6
);
expect(theCameraView.rectangle?.north).toBeCloseTo(
CesiumMath.toRadians(cameraValues.north),
6
);
expect(theCameraView.rectangle?.south).toBeCloseTo(
CesiumMath.toRadians(cameraValues.south),
6
);
expect(theCameraView.rectangle?.west).toBeCloseTo(
CesiumMath.toRadians(cameraValues.west),
6
);
});
it("should use default camera view if missing any required rectagle parameters in camera.", async function () {
const cameraValues = {
west: undefined,
south: -37.5588985189224,
east: 143.85932639124115,
north: -37.55761610087383,
position: {
x: -4088564.111098504,
y: 2985823.720726511,
z: -3867066.0705771768
},
direction: {
x: 0.25654239033528903,
y: 0.8049473516085732,
z: 0.5350194044138963
},
up: {
x: -0.6168995401836412,
y: 0.5624968267277541,
z: -0.5504836757274634
}
};
const camera = createStratumInstance(CameraTraits, cameraValues);
const idealZoom = createStratumInstance(IdealZoomTraits);
idealZoom.camera = camera;
theItem.setTrait("definition", "idealZoom", idealZoom);
await theItem.loadMapItems();
render();
await userEvent.click(
screen.getByRole("button", { name: "workbench.zoomTo" })
);
const theCameraView = terria.currentViewer.getCurrentCameraView();
expect(theCameraView.direction).toBe(undefined);
expect(theCameraView.position).toBe(undefined);
expect(theCameraView.up).toBe(undefined);
// The rectangle values are from default camera view.
const rectangle = {
east: 3.141592653589793,
north: 1.5707963267948966,
south: -1.5707963267948966,
west: -3.141592653589793
};
expect(theCameraView.rectangle?.east).toBeCloseTo(rectangle.east, 6);
expect(theCameraView.rectangle?.north).toBeCloseTo(rectangle.north, 6);
expect(theCameraView.rectangle?.south).toBeCloseTo(rectangle.south, 6);
expect(theCameraView.rectangle?.west).toBeCloseTo(rectangle.west, 6);
});
it("should custimise camera view based on the given lookAt parameters when camera parameters also exist.", async function () {
const lookAtValues = {
targetLongitude: 150.60832,
targetLatitude: -34.19483,
targetHeight: 200,
heading: 180,
pitch: 15,
range: 200
};
const lookAt = createStratumInstance(LookAtTraits, lookAtValues);
const cameraValues = {
west: 143.85665964592238,
south: -37.5588985189224,
east: 143.85932639124115,
north: -37.55761610087383,
position: {
x: -4088564.111098504,
y: 2985823.720726511,
z: -3867066.0705771768
},
direction: {
x: 0.25654239033528903,
y: 0.8049473516085732,
z: 0.5350194044138963
},
up: {
x: -0.6168995401836412,
y: 0.5624968267277541,
z: -0.5504836757274634
}
};
const camera = createStratumInstance(CameraTraits, cameraValues);
const idealZoom = createStratumInstance(IdealZoomTraits);
idealZoom.lookAt = lookAt;
idealZoom.camera = camera;
theItem.setTrait("definition", "idealZoom", idealZoom);
await theItem.loadMapItems();
render();
await userEvent.click(
screen.getByRole("button", { name: "workbench.zoomTo" })
);
const theCameraView = terria.currentViewer.getCurrentCameraView();
const directionX = 0.6595071845691584;
const directionY = -0.37148703631220265;
const directionZ = -0.6534888333809832;
expect(theCameraView.direction?.x).toBeCloseTo(directionX, 6);
expect(theCameraView.direction?.y).toBeCloseTo(directionY, 6);
expect(theCameraView.direction?.z).toBeCloseTo(directionZ, 6);
const positionX = -4601657.155837906;
const positionY = 2592020.25230978;
const positionZ = -3564324.3086873693;
expect(theCameraView.position?.x).toBeCloseTo(positionX, 6);
expect(theCameraView.position?.y).toBeCloseTo(positionY, 6);
expect(theCameraView.position?.z).toBeCloseTo(positionZ, 6);
const upX = -0.5693750748843667;
const upY = 0.3207174448857751;
const upZ = -0.7569361562551771;
expect(theCameraView.up?.x).toBeCloseTo(upX, 6);
expect(theCameraView.up?.y).toBeCloseTo(upY, 6);
expect(theCameraView.up?.z).toBeCloseTo(upZ, 6);
// The rectangle values are calculated from the given idealZoom parameters,
// i.e., the customised camera's direction, position and up parameters.
// A 2D viewer will zoom to this rectangle only.
const rectangle = {
east: 2.6286473947864932,
north: -0.5967760407704654,
south: -0.596848700549,
west: 2.628574735007959
};
expect(theCameraView.rectangle?.east).toBeCloseTo(rectangle.east, 6);
expect(theCameraView.rectangle?.north).toBeCloseTo(rectangle.north, 6);
expect(theCameraView.rectangle?.south).toBeCloseTo(rectangle.south, 6);
expect(theCameraView.rectangle?.west).toBeCloseTo(rectangle.west, 6);
});
});