import { Cartesian2 } from "../../Source/Cesium.js"; import { Cartesian3 } from "../../Source/Cesium.js"; import { Ellipsoid } from "../../Source/Cesium.js"; import { Event } from "../../Source/Cesium.js"; import { JulianDate } from "../../Source/Cesium.js"; import { CustomDataSource } from "../../Source/Cesium.js"; import { DataSourceDisplay } from "../../Source/Cesium.js"; import { Entity } from "../../Source/Cesium.js"; import { EntityCluster } from "../../Source/Cesium.js"; import { SceneTransforms } from "../../Source/Cesium.js"; import createCanvas from "../createCanvas.js"; import createScene from "../createScene.js"; describe( "DataSources/EntityCluster", function () { var scene; var cluster; var depth; var farDepth; beforeAll(function () { scene = createScene({ canvas: createCanvas(10, 10), }); scene.globe = { ellipsoid: Ellipsoid.WGS84, _surface: { tileProvider: { ready: true, }, _tileLoadQueueHigh: [], _tileLoadQueueMedium: [], _tileLoadQueueLow: [], _debug: { tilesWaitingForChildren: 0, }, }, terrainProviderChanged: new Event(), imageryLayersUpdatedEvent: new Event(), beginFrame: function () {}, update: function () {}, render: function () {}, endFrame: function () {}, }; scene.globe.getHeight = function () { return 0.0; }; scene.globe.destroy = function () {}; scene.globe._surface.updateHeight = function () {}; scene.globe.terrainProviderChanged = new Event(); Object.defineProperties(scene.globe, { terrainProvider: { set: function (value) { this.terrainProviderChanged.raiseEvent(value); }, }, }); var camera = scene.camera; camera.setView({ destination: Cartesian3.fromDegrees(0.0, 0.0, 10000.0), }); scene.initializeFrame(); scene.render(); if (scene.logarithmicDepthBuffer) { depth = farDepth = 0.1; } else { depth = 0.5; farDepth = 0.9; } }); afterAll(function () { scene.destroyForSpecs(); }); afterEach(function () { cluster = cluster && cluster.destroy(); }); it("constructor sets default properties", function () { cluster = new EntityCluster(); expect(cluster.enabled).toEqual(false); expect(cluster.show).toEqual(true); expect(cluster.pixelRange).toEqual(80); expect(cluster.minimumClusterSize).toEqual(2); expect(cluster.clusterBillboards).toEqual(true); expect(cluster.clusterLabels).toEqual(true); expect(cluster.clusterPoints).toEqual(true); cluster.enabled = true; expect(cluster.enabled).toEqual(true); cluster.pixelRange = 30; expect(cluster.pixelRange).toEqual(30); cluster.minimumClusterSize = 5; expect(cluster.minimumClusterSize).toEqual(5); }); it("constructor sets expected properties", function () { var options = { enabled: true, show: false, pixelRange: 30, minimumClusterSize: 5, clusterBillboards: false, clusterLabels: false, clusterPoints: false, }; cluster = new EntityCluster(options); expect(cluster.enabled).toEqual(options.enabled); expect(cluster.show).toEqual(false); expect(cluster.pixelRange).toEqual(options.pixelRange); expect(cluster.minimumClusterSize).toEqual(options.minimumClusterSize); expect(cluster.clusterBillboards).toEqual(options.clusterBillboards); expect(cluster.clusterLabels).toEqual(options.clusterLabels); expect(cluster.clusterPoints).toEqual(options.clusterPoints); }); function createBillboardImage() { var canvas = document.createElement("canvas"); canvas.height = canvas.width = 1; var context2D = canvas.getContext("2d"); context2D.clearRect(0, 0, length, length); context2D.fillStyle = "#FF0000"; context2D.fillRect(0, 0, length, length); return canvas; } it("clusters billboards", function () { cluster = new EntityCluster(); cluster._initialize(scene); var entity = new Entity(); var billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(0.0, 0.0), depth ); entity = new Entity(); billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth ); var frameState = scene.frameState; cluster.update(frameState); expect(cluster._clusterLabelCollection).not.toBeDefined(); cluster.enabled = true; cluster.update(frameState); expect(cluster._clusterLabelCollection).toBeDefined(); expect(cluster._clusterLabelCollection.length).toEqual(1); cluster.clusterBillboards = false; cluster.update(frameState); expect(cluster._clusterLabelCollection).not.toBeDefined(); }); it("clusters billboards on first update", function () { cluster = new EntityCluster(); cluster._initialize(scene); var entity = new Entity(); var billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(0.0, 0.0), depth ); entity = new Entity(); billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth ); cluster.enabled = true; cluster.update(scene.frameState); expect(cluster._clusterLabelCollection).toBeDefined(); expect(cluster._clusterLabelCollection.length).toEqual(1); }); it("clusters labels", function () { cluster = new EntityCluster(); cluster._initialize(scene); var entity = new Entity(); var label = cluster.getLabel(entity); label.id = entity; label.text = "a"; label.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(0.0, 0.0), depth ); entity = new Entity(); label = cluster.getLabel(entity); label.id = entity; label.text = "b"; label.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth ); var frameState = scene.frameState; cluster.update(frameState); expect(cluster._clusterLabelCollection).not.toBeDefined(); cluster.enabled = true; cluster.update(frameState); expect(cluster._clusterLabelCollection).toBeDefined(); expect(cluster._clusterLabelCollection.length).toEqual(1); cluster.clusterLabels = false; cluster.update(frameState); expect(cluster._clusterLabelCollection).not.toBeDefined(); }); it("clusters labels on first update", function () { cluster = new EntityCluster(); cluster._initialize(scene); var entity = new Entity(); var label = cluster.getLabel(entity); label.id = entity; label.text = "a"; label.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(0.0, 0.0), depth ); entity = new Entity(); label = cluster.getLabel(entity); label.id = entity; label.text = "b"; label.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth ); cluster.enabled = true; cluster.update(scene.frameState); expect(cluster._clusterLabelCollection).toBeDefined(); expect(cluster._clusterLabelCollection.length).toEqual(1); }); it("clusters points", function () { cluster = new EntityCluster(); cluster._initialize(scene); var entity = new Entity(); var point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; point.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(0.0, 0.0), depth ); entity = new Entity(); point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; point.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth ); var frameState = scene.frameState; cluster.update(frameState); expect(cluster._clusterLabelCollection).not.toBeDefined(); cluster.enabled = true; cluster.update(frameState); expect(cluster._clusterLabelCollection).toBeDefined(); expect(cluster._clusterLabelCollection.length).toEqual(1); cluster.clusterPoints = false; cluster.update(frameState); expect(cluster._clusterLabelCollection).not.toBeDefined(); }); it("clusters points on first update", function () { cluster = new EntityCluster(); cluster._initialize(scene); var entity = new Entity(); var point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; point.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(0.0, 0.0), depth ); entity = new Entity(); point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; point.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth ); cluster.enabled = true; cluster.update(scene.frameState); expect(cluster._clusterLabelCollection).toBeDefined(); expect(cluster._clusterLabelCollection.length).toEqual(1); }); it("clusters points that have labels", function () { cluster = new EntityCluster(); cluster._initialize(scene); var entity = new Entity(); var point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; point.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(0.0, 0.0), depth ); entity = new Entity(); point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; point.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth ); var frameState = scene.frameState; cluster.update(frameState); expect(cluster._clusterLabelCollection).not.toBeDefined(); point.id.label = cluster.getLabel(entity); cluster.enabled = true; cluster.update(frameState); expect(cluster._clusterLabelCollection).toBeDefined(); expect(cluster._clusterLabelCollection.length).toEqual(1); cluster.clusterPoints = false; cluster.update(frameState); expect(cluster._clusterLabelCollection).not.toBeDefined(); }); it("records entity collection indices on getting billboard, label and point", function () { cluster = new EntityCluster(); cluster._initialize(scene); var entity = new Entity(); cluster.getBillboard(entity); cluster.getLabel(entity); cluster.getPoint(entity); expect( cluster._collectionIndicesByEntity[entity.id].billboardIndex ).toBeDefined(); expect( cluster._collectionIndicesByEntity[entity.id].labelIndex ).toBeDefined(); expect( cluster._collectionIndicesByEntity[entity.id].pointIndex ).toBeDefined(); }); it("removes entity collection indices when billboard, label and point have been removed", function () { cluster = new EntityCluster(); cluster._initialize(scene); var entity = new Entity(); cluster.getBillboard(entity); cluster.getLabel(entity); cluster.getPoint(entity); cluster.removeBillboard(entity); cluster.removeLabel(entity); cluster.removePoint(entity); expect(cluster._collectionIndicesByEntity[entity.id]).toBeUndefined(); }); it("can destroy cluster and re-add entities", function () { cluster = new EntityCluster(); cluster._initialize(scene); var entity1 = new Entity(); var billboard = cluster.getBillboard(entity1); billboard.id = entity1; var entity2 = new Entity(); var label = cluster.getLabel(entity2); label.id = entity2; var entity3 = new Entity(); var point = cluster.getPoint(entity3); point.id = entity3; cluster.destroy(); expect(cluster._billboardCollection).not.toBeDefined(); expect(cluster._labelCollection).not.toBeDefined(); expect(cluster._pointCollection).not.toBeDefined(); expect(cluster.getBillboard(entity1)).toBeDefined(); expect(cluster.getLabel(entity2)).toBeDefined(); expect(cluster.getPoint(entity3)).toBeDefined(); expect(cluster._billboardCollection).toBeDefined(); expect(cluster._labelCollection).toBeDefined(); expect(cluster._pointCollection).toBeDefined(); }); it("does not remove entity collection indices when at least one of billboard, label and point remain", function () { cluster = new EntityCluster(); cluster._initialize(scene); var entity = new Entity(); cluster.getBillboard(entity); cluster.getLabel(entity); cluster.getPoint(entity); cluster.removeBillboard(entity); cluster.removeLabel(entity); expect(cluster._collectionIndicesByEntity[entity.id]).toBeDefined(); }); it("pixel range", function () { cluster = new EntityCluster(); cluster._initialize(scene); var entity = new Entity(); var billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(0.0, 0.0), depth ); entity = new Entity(); billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth ); var frameState = scene.frameState; cluster.update(frameState); expect(cluster._clusterLabelCollection).not.toBeDefined(); cluster.enabled = true; cluster.update(frameState); expect(cluster._clusterLabelCollection).toBeDefined(); expect(cluster._clusterLabelCollection.length).toEqual(1); cluster.pixelRange = 1; cluster.update(frameState); expect(cluster._clusterLabelCollection).not.toBeDefined(); }); it("minimum cluster size", function () { cluster = new EntityCluster(); cluster._initialize(scene); var entity = new Entity(); var billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(0.0, 0.0), depth ); entity = new Entity(); billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(scene.canvas.clientWidth, 0), depth ); entity = new Entity(); billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(0, scene.canvas.clientHeight), depth ); entity = new Entity(); billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth ); var frameState = scene.frameState; cluster.update(frameState); expect(cluster._clusterLabelCollection).not.toBeDefined(); cluster.enabled = true; cluster.update(frameState); expect(cluster._clusterLabelCollection).toBeDefined(); expect(cluster._clusterLabelCollection.length).toEqual(1); cluster.minimumClusterSize = 5; cluster.update(frameState); expect(cluster._clusterLabelCollection).not.toBeDefined(); }); it("clusters around the same point", function () { cluster = new EntityCluster(); cluster._initialize(scene); var entity = new Entity(); var billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(0.0, 0.0), farDepth ); entity = new Entity(); billboard = cluster.getBillboard(entity); billboard.id = entity; billboard.image = createBillboardImage(); billboard.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), farDepth ); var frameState = scene.frameState; cluster.update(frameState); expect(cluster._clusterLabelCollection).not.toBeDefined(); cluster.enabled = true; cluster.update(frameState); expect(cluster._clusterLabelCollection).toBeDefined(); expect(cluster._clusterLabelCollection.length).toEqual(1); var position = Cartesian3.clone( cluster._clusterLabelCollection.get(0).position ); scene.camera.moveForward(1.0e-6); cluster.pixelRange = cluster.pixelRange - 1; cluster.update(frameState); expect(cluster._clusterLabelCollection).toBeDefined(); expect(cluster._clusterLabelCollection.length).toEqual(1); expect(cluster._clusterLabelCollection.get(0).position).toEqual(position); }); it("custom cluster styling", function () { cluster = new EntityCluster(); cluster._initialize(scene); cluster.clusterEvent.addEventListener(function ( clusteredEntities, cluster ) { cluster.billboard.show = true; cluster.billboard.image = createBillboardImage(); cluster.label.text = "cluster"; }); var entity = new Entity(); var point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; point.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(0.0, 0.0), farDepth ); entity = new Entity(); point = cluster.getPoint(entity); point.id = entity; point.pixelSize = 1; point.position = SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), farDepth ); var frameState = scene.frameState; cluster.update(frameState); expect(cluster._clusterBillboardCollection).not.toBeDefined(); expect(cluster._clusterLabelCollection).not.toBeDefined(); cluster.enabled = true; cluster.update(frameState); expect(cluster._clusterLabelCollection).toBeDefined(); expect(cluster._clusterLabelCollection.length).toEqual(1); expect(cluster._clusterLabelCollection.get(0).text).toEqual("cluster"); expect(cluster._clusterBillboardCollection).toBeDefined(); expect(cluster._clusterBillboardCollection.length).toEqual(1); }); it("renders billboards with invisible labels that are not clustered", function () { cluster = new EntityCluster(); cluster._initialize(scene); cluster.minimumClusterSize = 3; var dataSource = new CustomDataSource("test"); dataSource.clustering = cluster; dataSource._visualizers = DataSourceDisplay.defaultVisualizersCallback( scene, cluster, dataSource ); var entityCollection = dataSource.entities; entityCollection.add({ position: SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(0.0, 0.0), depth ), billboard: { image: createBillboardImage(), }, label: { show: true, }, }); entityCollection.add({ position: SceneTransforms.drawingBufferToWgs84Coordinates( scene, new Cartesian2(scene.canvas.clientWidth, scene.canvas.clientHeight), depth ), billboard: { image: createBillboardImage(), }, label: { show: true, }, }); var visualizers = dataSource._visualizers; var length = visualizers.length; for (var i = 0; i < length; i++) { visualizers[i].update(JulianDate.now()); } var frameState = scene.frameState; cluster.update(frameState); expect(cluster._clusterBillboardCollection).not.toBeDefined(); expect(cluster._clusterLabelCollection).not.toBeDefined(); cluster.enabled = true; cluster.update(frameState); expect(cluster._clusterLabelCollection).not.toBeDefined(); expect(cluster._clusterBillboardCollection).not.toBeDefined(); expect(cluster._labelCollection).not.toBeDefined(); expect(cluster._billboardCollection).toBeDefined(); expect(cluster._billboardCollection.length).toEqual(2); }); }, "WebGL" );