UNPKG

terriajs

Version:

Geospatial data visualization platform.

683 lines (608 loc) 24.6 kB
import i18next from "i18next"; import { runInAction } from "mobx"; import Cartesian3 from "terriajs-cesium/Source/Core/Cartesian3"; import Cartographic from "terriajs-cesium/Source/Core/Cartographic"; import Ellipsoid from "terriajs-cesium/Source/Core/Ellipsoid"; import CesiumMath from "terriajs-cesium/Source/Core/Math"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; import Entity from "terriajs-cesium/Source/DataSources/Entity"; import supportsWebGL from "../../lib/Core/supportsWebGL"; import PickedFeatures from "../../lib/Map/PickedFeatures/PickedFeatures"; import TerriaFeature from "../../lib/Models/Feature/Feature"; import Terria from "../../lib/Models/Terria"; import UserDrawing from "../../lib/Models/UserDrawing"; const describeIfSupported = supportsWebGL() ? describe : xdescribe; describeIfSupported("UserDrawing that requires WebGL", function () { let terria: Terria; let container: HTMLElement; beforeEach(() => { terria = new Terria(); container = document.createElement("div"); document.body.appendChild(container); terria.mainViewer.attach(container); }); afterEach(() => { terria.mainViewer.destroy(); document.body.removeChild(container); }); it("changes cursor to crosshair when entering drawing mode", async function () { await terria.mainViewer.viewerLoadPromise; const userDrawing = new UserDrawing({ terria }); const cesium = terria.cesium; expect(cesium).toBeDefined(); if (cesium) { expect(cesium.cesiumWidget.canvas.style.cursor).toEqual(""); userDrawing.enterDrawMode(); expect(cesium.cesiumWidget.canvas.style.cursor).toEqual("crosshair"); (userDrawing as any).cleanUp(); expect(cesium.cesiumWidget.canvas.style.cursor).toEqual("auto"); } }); }); describe("UserDrawing", function () { let terria: Terria; beforeEach(function () { terria = new Terria(); }); it("will use default options if options are not specified", function () { const options = { terria: terria }; const userDrawing = new UserDrawing(options); expect(userDrawing.getDialogMessage()).toEqual( `<div><strong>${i18next.t( "models.userDrawing.messageHeader" )}</strong></br><i>${i18next.t( "models.userDrawing.clickToAddFirstPoint" )}</i></div>` ); }); it("getDialogMessage contains callback message if callback is specified", function () { const options = { terria: terria, onMakeDialogMessage: function () { return "HELLO"; } }; const userDrawing = new UserDrawing(options); expect(userDrawing.getDialogMessage()).toEqual( `<div><strong>${i18next.t( "models.userDrawing.messageHeader" )}</strong></br>HELLO</br><i>${i18next.t( "models.userDrawing.clickToAddFirstPoint" )}</i></div>` ); }); it("listens for user picks on map after entering drawing mode", function () { const userDrawing = new UserDrawing({ terria }); expect(userDrawing.terria.mapInteractionModeStack.length).toEqual(0); userDrawing.enterDrawMode(); expect(userDrawing.terria.mapInteractionModeStack.length).toEqual(1); }); it("disables feature info requests when in drawing mode", function () { const options = { terria: terria }; const userDrawing = new UserDrawing(options); expect(userDrawing.terria.allowFeatureInfoRequests).toEqual(true); userDrawing.enterDrawMode(); expect(userDrawing.terria.allowFeatureInfoRequests).toEqual(false); }); it("re-enables feature info requests on cleanup", function () { const options = { terria: terria }; const userDrawing = new UserDrawing(options); userDrawing.enterDrawMode(); expect(userDrawing.terria.allowFeatureInfoRequests).toEqual(false); userDrawing.cleanUp(); expect(userDrawing.terria.allowFeatureInfoRequests).toEqual(true); }); it("ensures onPointClicked callback is called when point is picked by user", function () { const onPointClicked = jasmine.createSpy(); const userDrawing = new UserDrawing({ terria, onPointClicked }); userDrawing.enterDrawMode(); const pickedFeatures = new PickedFeatures(); // Auckland, in case you're wondering pickedFeatures.pickPosition = new Cartesian3( -5088454.576893678, 465233.10329933715, -3804299.6786334896 ); runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); const pointEntities = onPointClicked.calls.mostRecent().args[0]; expect(pointEntities.entities.values.length).toEqual(1); }); it("ensures graphics are added when point is picked by user", function () { const userDrawing = new UserDrawing({ terria }); expect(userDrawing.pointEntities.entities.values.length).toEqual(0); expect(userDrawing.otherEntities.entities.values.length).toEqual(0); userDrawing.enterDrawMode(); const pickedFeatures = new PickedFeatures(); // Auckland, in case you're wondering pickedFeatures.pickPosition = new Cartesian3( -5088454.576893678, 465233.10329933715, -3804299.6786334896 ); runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); expect(userDrawing.pointEntities.entities.values.length).toEqual(1); expect(userDrawing.otherEntities.entities.values.length).toEqual(1); }); it("ensures graphics are updated when points change", function () { const options = { terria: terria }; const userDrawing = new UserDrawing(options); expect(userDrawing.pointEntities.entities.values.length).toEqual(0); expect(userDrawing.otherEntities.entities.values.length).toEqual(0); userDrawing.enterDrawMode(); const pickedFeatures = new PickedFeatures(); // Auckland, in case you're wondering const x = -5088454.576893678; const y = 465233.10329933715; const z = -3804299.6786334896; pickedFeatures.pickPosition = new Cartesian3(x, y, z); runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); // Check point const currentPoint = userDrawing.pointEntities.entities.values[0]; expect(currentPoint.position).toBeDefined(); if (currentPoint.position !== undefined) { const currentPointPos = currentPoint.position.getValue( terria.timelineClock.currentTime ); expect(currentPointPos?.x).toEqual(x); expect(currentPointPos?.y).toEqual(y); expect(currentPointPos?.z).toEqual(z); } // Check line as well let lineEntity = userDrawing.otherEntities.entities.values[0]; expect(lineEntity.polyline).toBeDefined(); if (lineEntity.polyline !== undefined) { expect(lineEntity.polyline.positions).toBeDefined(); if (lineEntity.polyline.positions !== undefined) { const currentPointPos = lineEntity.polyline.positions.getValue( terria.timelineClock.currentTime )[0]; expect(currentPointPos.x).toEqual(x); expect(currentPointPos.y).toEqual(y); expect(currentPointPos.z).toEqual(z); } } // Okay, now change points. LA. const newPickedFeatures = new PickedFeatures(); const newX = -2503231.890682526; const newY = -4660863.528418564; const newZ = 3551306.84427321; newPickedFeatures.pickPosition = new Cartesian3(newX, newY, newZ); runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = newPickedFeatures; }); // Check point const newPoint = userDrawing.pointEntities.entities.values[1]; expect(newPoint.position).toBeDefined(); if (newPoint.position !== undefined) { const newPointPos = newPoint.position.getValue( terria.timelineClock.currentTime ); expect(newPointPos?.x).toEqual(newX); expect(newPointPos?.y).toEqual(newY); expect(newPointPos?.z).toEqual(newZ); } // Check line as well lineEntity = userDrawing.otherEntities.entities.values[0]; expect(lineEntity.polyline).toBeDefined(); if (lineEntity.polyline !== undefined) { expect(lineEntity.polyline.positions).toBeDefined(); if (lineEntity.polyline.positions !== undefined) { const newPointPos = lineEntity.polyline.positions.getValue( terria.timelineClock.currentTime )[1]; expect(newPointPos.x).toEqual(newX); expect(newPointPos.y).toEqual(newY); expect(newPointPos.z).toEqual(newZ); } } }); it("returns correct button text for any given number of points on map", function () { const options = { terria: terria }; const userDrawing = new UserDrawing(options); expect(userDrawing.getButtonText()).toEqual( i18next.t("models.userDrawing.btnCancel") ); userDrawing.pointEntities.entities.values.push(new Entity()); expect(userDrawing.getButtonText()).toEqual( i18next.t("models.userDrawing.btnCancel") ); userDrawing.pointEntities.entities.values.push(new Entity()); expect(userDrawing.getButtonText()).toEqual( i18next.t("models.userDrawing.btnDone") ); }); it("cleans up when cleanup is called", function () { const options = { terria: terria }; const userDrawing = new UserDrawing(options); expect(userDrawing.pointEntities.entities.values.length).toEqual(0); expect(userDrawing.otherEntities.entities.values.length).toEqual(0); userDrawing.enterDrawMode(); const pickedFeatures = new PickedFeatures(); // Auckland, in case you're wondering pickedFeatures.pickPosition = new Cartesian3( -5088454.576893678, 465233.10329933715, -3804299.6786334896 ); runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); expect(userDrawing.pointEntities.entities.values.length).toEqual(1); expect(userDrawing.otherEntities.entities.values.length).toEqual(1); (userDrawing as any).cleanUp(); expect(userDrawing.pointEntities.entities.values.length).toEqual(0); expect(userDrawing.otherEntities.entities.values.length).toEqual(0); expect((userDrawing as any).inDrawMode).toBeFalsy(); expect((userDrawing as any).closeLoop).toBeFalsy(); }); it("ensures onCleanUp callback is called when clean up occurs", function () { const onCleanUp = jasmine.createSpy(); const userDrawing = new UserDrawing({ terria, onCleanUp }); userDrawing.enterDrawMode(); expect(onCleanUp).not.toHaveBeenCalled(); (userDrawing as any).cleanUp(); expect(onCleanUp).toHaveBeenCalled(); }); it("function clickedExistingPoint detects and handles if existing point is clicked", function () { const userDrawing = new UserDrawing({ terria }); userDrawing.enterDrawMode(); const pickedFeatures = new PickedFeatures(); // First point // Points around Parliament house const pt1Position = new Cartographic( CesiumMath.toRadians(149.121), CesiumMath.toRadians(-35.309), CesiumMath.toRadians(0) ); const pt1CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt1Position); pickedFeatures.pickPosition = pt1CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); // Second point const pt2Position = new Cartographic( CesiumMath.toRadians(149.124), CesiumMath.toRadians(-35.311), CesiumMath.toRadians(0) ); const pt2CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt2Position); pickedFeatures.pickPosition = pt2CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); // Third point const pt3Position = new Cartographic( CesiumMath.toRadians(149.127), CesiumMath.toRadians(-35.308), CesiumMath.toRadians(0) ); const pt3CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt3Position); pickedFeatures.pickPosition = pt3CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); expect((userDrawing as any).closeLoop).toBeFalsy(); // Now pick the first point pickedFeatures.pickPosition = pt1CartesianPosition; // If in the UI the user clicks on a point, it returns that entity, so we're pulling it out of userDrawing and // pretending the user actually clicked on it. const pt1Entity = userDrawing.pointEntities.entities.values[0]; pickedFeatures.features = [pt1Entity as TerriaFeature]; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); expect((userDrawing as any).closeLoop).toBeTruthy(); expect(userDrawing.pointEntities.entities.values.length).toEqual(3); }); it("loop does not close if polygon is not allowed", function () { const options = { terria: terria, allowPolygon: false }; const userDrawing = new UserDrawing(options); userDrawing.enterDrawMode(); const pickedFeatures = new PickedFeatures(); // First point // Points around Parliament house const pt1Position = new Cartographic( CesiumMath.toRadians(149.121), CesiumMath.toRadians(-35.309), CesiumMath.toRadians(0) ); const pt1CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt1Position); pickedFeatures.pickPosition = pt1CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); // Second point const pt2Position = new Cartographic( CesiumMath.toRadians(149.124), CesiumMath.toRadians(-35.311), CesiumMath.toRadians(0) ); const pt2CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt2Position); pickedFeatures.pickPosition = pt2CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); // Third point const pt3Position = new Cartographic( CesiumMath.toRadians(149.127), CesiumMath.toRadians(-35.308), CesiumMath.toRadians(0) ); const pt3CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt3Position); pickedFeatures.pickPosition = pt3CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); expect((userDrawing as any).closeLoop).toBeFalsy(); // Now pick the first point pickedFeatures.pickPosition = pt1CartesianPosition; // If in the UI the user clicks on a point, it returns that entity, so we're pulling it out of userDrawing and // pretending the user actually clicked on it. const pt1Entity = userDrawing.pointEntities.entities.values[0]; pickedFeatures.features = [pt1Entity as TerriaFeature]; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); expect((userDrawing as any).closeLoop).toBeFalsy(); expect(userDrawing.pointEntities.entities.values.length).toEqual(2); }); it("polygon is only drawn once", function () { const userDrawing = new UserDrawing({ terria }); userDrawing.enterDrawMode(); const pickedFeatures = new PickedFeatures(); // First point // Points around Parliament house const pt1Position = new Cartographic( CesiumMath.toRadians(149.121), CesiumMath.toRadians(-35.309), CesiumMath.toRadians(0) ); const pt1CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt1Position); pickedFeatures.pickPosition = pt1CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); // Second point const pt2Position = new Cartographic( CesiumMath.toRadians(149.124), CesiumMath.toRadians(-35.311), CesiumMath.toRadians(0) ); const pt2CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt2Position); pickedFeatures.pickPosition = pt2CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); // Third point const pt3Position = new Cartographic( CesiumMath.toRadians(149.127), CesiumMath.toRadians(-35.308), CesiumMath.toRadians(0) ); const pt3CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt3Position); pickedFeatures.pickPosition = pt3CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); expect((userDrawing as any).closeLoop).toBeFalsy(); expect(userDrawing.otherEntities.entities.values.length).toEqual(1); // Now pick the first point pickedFeatures.pickPosition = pt1CartesianPosition; // If in the UI the user clicks on a point, it returns that entity, so we're pulling it out of userDrawing and // pretending the user actually clicked on it. const pt1Entity = userDrawing.pointEntities.entities.values[0]; pickedFeatures.features = [pt1Entity as TerriaFeature]; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); expect((userDrawing as any).closeLoop).toBeTruthy(); expect(userDrawing.otherEntities.entities.values.length).toEqual(2); // Another point. Polygon is still closed. const newPtPosition = new Cartographic( CesiumMath.toRadians(149.0), CesiumMath.toRadians(-35.0), CesiumMath.toRadians(0) ); const newPtCartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(newPtPosition); pickedFeatures.pickPosition = newPtCartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); expect((userDrawing as any).closeLoop).toBeTruthy(); expect(userDrawing.otherEntities.entities.values.length).toEqual(2); }); it("point is removed if it is clicked on and it is not the first point", function () { const options = { terria: terria }; const userDrawing = new UserDrawing(options); userDrawing.enterDrawMode(); const pickedFeatures = new PickedFeatures(); // First point // Points around Parliament house const pt1Position = new Cartographic( CesiumMath.toRadians(149.121), CesiumMath.toRadians(-35.309), CesiumMath.toRadians(0) ); const pt1CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt1Position); pickedFeatures.pickPosition = pt1CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); // Second point const pt2Position = new Cartographic( CesiumMath.toRadians(149.124), CesiumMath.toRadians(-35.311), CesiumMath.toRadians(0) ); const pt2CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt2Position); pickedFeatures.pickPosition = pt2CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); // Third point const pt3Position = new Cartographic( CesiumMath.toRadians(149.127), CesiumMath.toRadians(-35.308), CesiumMath.toRadians(0) ); const pt3CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt3Position); pickedFeatures.pickPosition = pt3CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); expect((userDrawing as any).closeLoop).toBeFalsy(); // Now pick the second point pickedFeatures.pickPosition = pt2CartesianPosition; // If in the UI the user clicks on a point, it returns that entity, so we're pulling it out of userDrawing and // pretending the user actually clicked on it. const pt2Entity = userDrawing.pointEntities.entities.values[1]; pickedFeatures.features = [pt2Entity as TerriaFeature]; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); expect(userDrawing.pointEntities.entities.values.length).toEqual(2); expect(userDrawing.mapItems.length).toBe(2); }); it("draws rectangle", function () { const userDrawing = new UserDrawing({ terria, allowPolygon: false, drawRectangle: true }); userDrawing.enterDrawMode(); const pickedFeatures = new PickedFeatures(); // First point // Points around Parliament house const pt1Position = new Cartographic( CesiumMath.toRadians(149.121), CesiumMath.toRadians(-35.309), CesiumMath.toRadians(0) ); const pt1CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt1Position); pickedFeatures.pickPosition = pt1CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); expect(userDrawing.pointEntities.entities.values.length).toEqual(1); expect(userDrawing.otherEntities.entities.values.length).toEqual(1); let rectangle: Rectangle = userDrawing.otherEntities.entities .getById("rectangle") ?.rectangle?.coordinates?.getValue(terria.timelineClock.currentTime); expect(rectangle).toBeUndefined(); // Second point const pt2Position = new Cartographic( CesiumMath.toRadians(149.124), CesiumMath.toRadians(-35.311), CesiumMath.toRadians(0) ); const pt2CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt2Position); pickedFeatures.pickPosition = pt2CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); expect(userDrawing.pointEntities.entities.values.length).toEqual(2); expect(userDrawing.otherEntities.entities.values.length).toEqual(1); rectangle = userDrawing.otherEntities.entities .getById("rectangle") ?.rectangle?.coordinates?.getValue(terria.timelineClock.currentTime); expect(rectangle.east).toBeCloseTo(CesiumMath.toRadians(149.124)); expect(rectangle.west).toBeCloseTo(CesiumMath.toRadians(149.121)); expect(rectangle.north).toBeCloseTo(CesiumMath.toRadians(-35.309)); expect(rectangle.south).toBeCloseTo(CesiumMath.toRadians(-35.311)); expect(userDrawing.mapItems.length).toBe(1); }); it("calls onDrawingComplete with the drawn points or rectangle", function () { let completedPoints: Cartesian3[] | undefined; let completedRectangle: Rectangle | undefined; const userDrawing = new UserDrawing({ terria, allowPolygon: false, drawRectangle: true, onDrawingComplete: ({ points, rectangle }) => { completedPoints = points; completedRectangle = rectangle; } }); userDrawing.enterDrawMode(); const pickedFeatures = new PickedFeatures(); // First point // Points around Parliament house const pt1Position = new Cartographic( CesiumMath.toRadians(149.121), CesiumMath.toRadians(-35.309), CesiumMath.toRadians(0) ); const pt1CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt1Position); pickedFeatures.pickPosition = pt1CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); // Second point const pt2Position = new Cartographic( CesiumMath.toRadians(149.124), CesiumMath.toRadians(-35.311), CesiumMath.toRadians(0) ); const pt2CartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(pt2Position); pickedFeatures.pickPosition = pt2CartesianPosition; runInAction(() => { userDrawing.terria.mapInteractionModeStack[0].pickedFeatures = pickedFeatures; }); // Check onDrawingComplete was called when we end the drawing. userDrawing.terria.mapInteractionModeStack[0].onCancel?.(); expect(completedPoints).toBeDefined(); if (completedPoints) { expect(completedPoints.length).toEqual(2); } expect(completedRectangle).toBeDefined(); }); });