UNPKG

terriajs

Version:

Geospatial data visualization platform.

362 lines (322 loc) 12.6 kB
import "../../../SpecMain"; import { reaction, runInAction } from "mobx"; import i18next from "i18next"; import Cartesian2 from "terriajs-cesium/Source/Core/Cartesian2"; import IonResource from "terriajs-cesium/Source/Core/IonResource"; import Cesium3DTileFeature from "terriajs-cesium/Source/Scene/Cesium3DTileFeature"; import Cesium3DTileset from "terriajs-cesium/Source/Scene/Cesium3DTileset"; import Cesium3DTileStyle from "terriajs-cesium/Source/Scene/Cesium3DTileStyle"; import Cesium3DTileColorBlendMode from "terriajs-cesium/Source/Scene/Cesium3DTileColorBlendMode"; import ShadowMode from "terriajs-cesium/Source/Scene/ShadowMode"; import Cesium3DTilesCatalogItem from "../../../../lib/Models/Catalog/CatalogItems/Cesium3DTilesCatalogItem"; import createStratumInstance from "../../../../lib/Models/Definition/createStratumInstance"; import Terria from "../../../../lib/Models/Terria"; import Matrix4 from "terriajs-cesium/Source/Core/Matrix4"; import HeadingPitchRollTraits from "../../../../lib/Traits/TraitsClasses/HeadingPitchRollTraits"; import LatLonHeightTraits from "../../../../lib/Traits/TraitsClasses/LatLonHeightTraits"; import CommonStrata from "../../../../lib/Models/Definition/CommonStrata"; import Quaternion from "terriajs-cesium/Source/Core/Quaternion"; import Matrix3 from "terriajs-cesium/Source/Core/Matrix3"; import HeadingPitchRoll from "terriajs-cesium/Source/Core/HeadingPitchRoll"; import Cartesian3 from "terriajs-cesium/Source/Core/Cartesian3"; import { OptionsTraits, FilterTraits } from "../../../../lib/Traits/TraitsClasses/Cesium3dTilesTraits"; describe("Cesium3DTilesCatalogItemSpec", function () { let item: Cesium3DTilesCatalogItem; const testUrl = "/test/Cesium3DTiles/tileset.json"; beforeEach(function () { item = new Cesium3DTilesCatalogItem("test", new Terria()); runInAction(() => { item.setTrait("definition", "url", testUrl); }); }); it("should have a type and a typeName", function () { expect(Cesium3DTilesCatalogItem.type).toBe("3d-tiles"); expect(item.type).toBe("3d-tiles"); expect(item.typeName).toBe(i18next.t("models.cesiumTerrain.name3D")); }); it("supports zooming", function () { expect(item.disableZoomTo).toBeFalsy(); }); it("supports show info", function () { expect(item.disableAboutData).toBeFalsy(); }); it("is mappable", function () { expect(item.isMappable).toBeTruthy(); }); describe("showExpressionFromFilters", function () { it("correctly converts filters to show expression", function () { runInAction(() => item.setTrait("definition", "filters", [ createStratumLevelFilter(-2, 11, -1, 10) ]) ); const show: any = item.showExpressionFromFilters; expect(show).toBe( "Number(${feature['stratumlev']}) >= -1 && Number(${feature['stratumlev']}) <= 10" ); }); describe("when minimumShown and maximumShown are outside the value range", function () { it("should be undefined", function () { runInAction(() => item.setTrait("definition", "filters", [ createStratumLevelFilter(-2, 11, -2, 11) ]) ); const show: any = item.showExpressionFromFilters; expect(show).toBeUndefined(); }); }); }); describe("cesiumTileStyle", function () { let style: any; beforeEach(function () { runInAction(() => item.setTrait("definition", "style", { color: "vec4(${Height})", show: "${Height} > 30", meta: { description: '"Building id ${id} has height ${Height}."' } }) ); style = item.cesiumTileStyle; }); it("is a Cesium3DTileStyle", function () { expect(style instanceof Cesium3DTileStyle).toBeTruthy(); }); it("creates the style correctly", function () { expect(style.show._expression).toBe("${Height} > 30"); expect(style.color._expression).toBe("vec4(${Height})"); }); it("reflects changes to the catalog item's opacity in its style", async function () { item.setTrait("definition", "style", { color: "#ff0000" }); item.setTrait("user", "opacity", 0.5); style = item.cesiumTileStyle; await item.loadMapItems(); expect(style.color._expression).toBe("color('#ff0000', ${opacity})"); }); describe("when filters are specified", function () { it("adds the filters to the style", async function () { runInAction(() => item.setTrait("definition", "filters", [ createStratumLevelFilter(-2, 11, 5, 8) ]) ); style = item.cesiumTileStyle; await item.loadMapItems(); expect(style.show._expression).toBe(item.showExpressionFromFilters); }); }); }); describe("when loading", function () { describe("if ionAssetId is provided", function () { it("loads the IonResource", async function () { runInAction(() => { item.setTrait("definition", "ionAssetId", 4242); item.setTrait("definition", "ionAccessToken", "fakeToken"); item.setTrait("definition", "ionServer", "fakeServer"); }); // @ts-expect-error: spy on static method spyOn(IonResource, "fromAssetId").and.callFake(async () => {}); try { await item.loadMapItems(); } catch {} expect(IonResource.fromAssetId).toHaveBeenCalledWith(4242, { accessToken: "fakeToken", server: "fakeServer" }); }); }); xit("sets the extra options", async function () { runInAction(() => { item.setTrait( "definition", "options", createStratumInstance(OptionsTraits, { maximumScreenSpaceError: 3 }) ); }); try { await item.loadMapItems(); } catch {} const tileset = item.mapItems[0] as Cesium3DTileset; expect(tileset.maximumScreenSpaceError).toBe(3); }); }); describe("after loading", function () { let dispose: () => void; beforeEach(async function () { try { await item.loadMapItems(); } catch {} // observe mapItems dispose = reaction( () => item.mapItems, () => {} ); }); afterEach(function () { dispose(); }); describe("mapItems", function () { it("has exactly 1 mapItem", function () { expect(item.mapItems.length).toBe(1); }); describe("the mapItem", function () { it("should be a Cesium3DTileset", function () { expect(item.mapItems[0] instanceof Cesium3DTileset).toBeTruthy(); }); describe("the tileset", function () { it("sets `show`", function () { runInAction(() => item.setTrait("definition", "show", false)); expect(item.mapItems[0].show).toBe(false); }); it("sets the shadow mode", function () { runInAction(() => item.setTrait("definition", "shadows", "CAST")); const tileset = item.mapItems[0] as Cesium3DTileset; expect(tileset.shadows).toBe(ShadowMode.CAST_ONLY); }); it("sets the color blend mode", function () { runInAction(() => { item.setTrait("definition", "colorBlendMode", "REPLACE"); const tileset = item.mapItems[0] as Cesium3DTileset; expect(tileset.colorBlendMode).toBe( Cesium3DTileColorBlendMode.REPLACE ); }); }); it("sets the color blend amount", function () { runInAction(() => { item.setTrait("user", "colorBlendAmount", 0.42); const tileset = item.mapItems[0] as Cesium3DTileset; expect(tileset.colorBlendAmount).toBe(0.42); }); }); it("sets the shadow mode after blend", function () { runInAction(() => item.setTrait("definition", "shadows", "CAST")); const tileset = item.mapItems[0] as Cesium3DTileset; expect(tileset.shadows).toBe(ShadowMode.CAST_ONLY); }); it("sets the style", function () { runInAction(() => item.setTrait("definition", "style", { show: "${ZipCode} === '19341'" }) ); const tileset = item.mapItems[0] as Cesium3DTileset; expect(tileset.style).toBe((item as any).cesiumTileStyle); }); // TODO: fix later // describe("when the item is reloaded after destroying the tileset", function() { // it("generates a new tileset", async function() { // const tileset = item.mapItems[0]; // await item.loadMapItems(); // expect(item.mapItems[0] === tileset).toBeTruthy(); // runInAction(() => { // tileset.destroy(); // }); // await item.loadMapItems(); // expect(item.mapItems[0] === tileset).toBeFalsy(); // }); // }); it("sets the rootTransform to IDENTITY", function () { const tileset = item.mapItems[0] as Cesium3DTileset; expect( Matrix4.equals(tileset.root.transform, Matrix4.IDENTITY) ).toBeTruthy(); }); it("computes a new model matrix from the given transformations", function () { item.setTrait( CommonStrata.user, "rotation", createStratumInstance(HeadingPitchRollTraits, { heading: 42, pitch: 42, roll: 42 }) ); item.setTrait( CommonStrata.user, "origin", createStratumInstance(LatLonHeightTraits, { latitude: 10, longitude: 10 }) ); item.setTrait(CommonStrata.user, "scale", 5); const tileset = item.mapItems[0] as Cesium3DTileset; const modelMatrix = tileset.modelMatrix; const rotation = HeadingPitchRoll.fromQuaternion( Quaternion.fromRotationMatrix( Matrix4.getMatrix3(modelMatrix, new Matrix3()) ) ); expect(rotation.heading.toFixed(2)).toBe("-2.39"); expect(rotation.pitch.toFixed(2)).toBe("-1.57"); expect(rotation.roll.toFixed(2)).toBe("3.12"); const scale = Matrix4.getScale(modelMatrix, new Cartesian3()); expect(scale.x.toFixed(2)).toEqual("5.00"); expect(scale.y.toFixed(2)).toEqual("5.00"); expect(scale.z.toFixed(2)).toEqual("5.00"); const position = Matrix4.getTranslation( modelMatrix, new Cartesian3() ); expect(position.x.toFixed(2)).toEqual("6186437.07"); expect(position.y.toFixed(2)).toEqual("1090835.77"); expect(position.z.toFixed(2)).toEqual("-3804844.21"); }); }); }); }); }); it("correctly builds `Feature` from picked Cesium3DTileFeature", async function () { const picked = new Cesium3DTileFeature(); spyOn(picked, "getPropertyIds").and.returnValue([]); const feature = await item.buildFeatureFromPickResult( Cartesian2.ZERO, picked ); expect(feature).toBeDefined(); if (feature) { expect(feature._cesium3DTileFeature).toBe(picked); } }); it("can change the visibility of a feature", function () { const feature = new Cesium3DTileFeature(); spyOn(feature, "getProperty").and.callFake((prop: string) => { const props: any = { doorNumber: 10, color: "red" }; return props[prop]; }); item.setTrait(CommonStrata.user, "featureIdProperties", [ "doorNumber", "color" ]); item.setFeatureVisibility(feature, false); // @ts-expect-error JsonValue expect(item.style.show.conditions).toEqual([ ['${color} === "red" && ${doorNumber} === 10', false], ["true", true] // fallback rule ]); }); }); function createStratumLevelFilter( minimumValue: number, maximumValue: number, minimumValueShown: number, maximumValueShown: number ) { const filter = createStratumInstance(FilterTraits, { name: "Stratum Level", property: "stratumlev", minimumValue, maximumValue, minimumShown: minimumValueShown, maximumShown: maximumValueShown }); return filter; }