From ddb9543debfa43b4fa248c17be7f2b365b256d33 Mon Sep 17 00:00:00 2001 From: Robyn Thiessen-Bock Date: Thu, 24 Aug 2023 13:06:45 -0400 Subject: [PATCH] Update Cesium on add/remove layers collection - Add listeners for when layers are added or removed from the MapAsset collection and update the layers rendered on the Cesium map and the layers displayed in the layer list. Issue #2180, #1923, #1775 --- src/js/models/connectors/Map-Search.js | 2 - src/js/models/maps/Map.js | 21 +++++- src/js/views/maps/CesiumWidgetView.js | 100 ++++++++++++++++++------- src/js/views/maps/LayerListView.js | 30 ++++++++ 4 files changed, 120 insertions(+), 33 deletions(-) diff --git a/src/js/models/connectors/Map-Search.js b/src/js/models/connectors/Map-Search.js index 827593203..62958b408 100644 --- a/src/js/models/connectors/Map-Search.js +++ b/src/js/models/connectors/Map-Search.js @@ -206,8 +206,6 @@ define([ * See {@link MapSearchFiltersConnector#onMoveEnd} */ onMoveEnd: function () { - const searchResults = this.get("searchResults"); - const map = this.get("map"); this.showGeoHashLayer(); this.updateFacet(); }, diff --git a/src/js/models/maps/Map.js b/src/js/models/maps/Map.js index 6b7390d08..9ae10fd0b 100644 --- a/src/js/models/maps/Map.js +++ b/src/js/models/maps/Map.js @@ -351,16 +351,31 @@ define([ * Add a layer or other asset to the map. This is the best way to add a * layer to the map because it will ensure that this map model is set on * the layer model. - * @param {Object | MapAsset} layer - A map asset model or object with + * @todo Enable adding a terrain asset. + * @param {Object | MapAsset} asset - A map asset model or object with * attributes to set on a new map asset model. * @returns {MapAsset} The new layer model. * @since 2.25.0 */ - addAsset: function (layer) { + addAsset: function (asset) { const layers = this.get("layers") || this.resetLayers(); - return layers.addAsset(layer, this); + return layers.addAsset(asset, this); }, + /** + * Remove a layer from the map. + * @param {MapAsset} asset - The layer model to remove from the map. + * @since x.x.x + */ + removeAsset: function (asset) { + if(!asset) return; + const layers = this.get("layers"); + if(!layers) return; + // Remove by ID because the model is passed directly. Not sure if this + // is a bug in the MapAssets collection or Backbone? + if (layers) layers.remove(asset.cid); + } + } ); diff --git a/src/js/views/maps/CesiumWidgetView.js b/src/js/views/maps/CesiumWidgetView.js index b57aa1e0b..ae5cbdbf9 100644 --- a/src/js/views/maps/CesiumWidgetView.js +++ b/src/js/views/maps/CesiumWidgetView.js @@ -81,23 +81,30 @@ define( * @property {string} renderFunction The name of the function in the view that * will add the asset to the map and render it, when passed the cesiumModel * attribute from the MapAsset model + * @property {string} removeFunction The name of the function in the view that + * will remove the asset from the map, when passed the cesiumModel attribute from + * the MapAsset model */ mapAssetRenderFunctions: [ { types: ['Cesium3DTileset'], - renderFunction: 'add3DTileset' + renderFunction: 'add3DTileset', + removeFunction: 'remove3DTileset' }, { types: ['GeoJsonDataSource', 'CzmlDataSource'], - renderFunction: 'addVectorData' + renderFunction: 'addVectorData', + removeFunction: 'removeVectorData' }, { types: ['BingMapsImageryProvider', 'IonImageryProvider', 'TileMapServiceImageryProvider', 'WebMapTileServiceImageryProvider', 'WebMapServiceImageryProvider', 'OpenStreetMapImageryProvider'], - renderFunction: 'addImagery' + renderFunction: 'addImagery', + removeFunction: 'removeImagery' }, { types: ['CesiumTerrainProvider'], - renderFunction: 'updateTerrain' + renderFunction: 'updateTerrain', + removeFunction: null } ], @@ -194,7 +201,6 @@ define( // raised. view.camera.percentChanged = 0.1 - // Disable HDR lighting for better performance and to avoid changing imagery colors. view.scene.highDynamicRange = false; view.scene.globe.enableLighting = false; @@ -251,6 +257,14 @@ define( const view = this; + // Listen for addition or removal of layers + // TODO: Add similar listeners for terrain + const layers = view.model.get('layers') + view.stopListening(layers, 'add'); + view.listenTo(layers, 'add', view.addAsset); + view.stopListening(layers, 'remove'); + view.listenTo(layers, 'remove', view.removeAsset); + // Zoom functions executed after each scene render view.scene.postRender.addEventListener(function () { view.postRender(); @@ -1156,10 +1170,10 @@ define( * @since x.x.x */ addNewAsset: function (mapAsset) { - // TODO: Set a listener on the layers collection for add events, and - // call this function when a new layer is added + if(!mapAsset) return const newAsset = this.model.addAsset(mapAsset); - this.addAsset(newAsset); + // The add event on the layers collection will trigger the addAsset + // function below, which will render the asset in the map return newAsset }, @@ -1227,27 +1241,25 @@ define( }, /** - * Remove an asset (layer) from the map model and remove it from the map - * @param {MapAsset} mapAsset - The MapAsset model to remove from the map + * When an asset is removed from the map model, remove it from the map. + * @param {MapAsset} mapAsset - The MapAsset model removed from the map * @since x.x.x */ - removeAsset: function (mapAsset) { - // TODO: Set a listener on the layers collection for remove events, and - // call this function when a new layer is removed - try { - if (!mapAsset) { - return - } - // TODO: Implement this! - // this.model.removeAsset(mapAsset) - // Remove the layer from the map - // ... - } - catch (error) { - console.log( - 'There was an error removing an asset from a CesiumWidgetView' + - '. Error details: ' + error - ); + removeAsset: function (mapAsset, b, c) { + if (!mapAsset) return + // Get the cesium model from the asset + const cesiumModel = mapAsset.get('cesiumModel') + if (!cesiumModel) return + // Find the remove function for this type of asset + const removeFunctionName = this.mapAssetRenderFunctions.find(function (option) { + return option.types.includes(mapAsset.get('type')) + })?.removeFunction + const removeFunction = this[removeFunctionName] + // If there is a function for this type of asset, call it + if (removeFunction && typeof removeFunction === 'function') { + removeFunction.call(this, cesiumModel) + } else { + console.log('No remove function found for this type of asset', mapAsset); } }, @@ -1272,6 +1284,16 @@ define( this.scene.primitives.add(cesiumModel) }, + /** + * Remove a 3D tileset from the map. + * @param {Cesium.Cesium3DTileset} cesiumModel The Cesium 3D tileset model to + * remove from the map + * @since x.x.x + */ + remove3DTileset: function (cesiumModel) { + this.scene.primitives.remove(cesiumModel) + }, + /** * Renders vector data (excluding 3D tilesets) in the Map. * @param {Cesium.GeoJsonDataSource} cesiumModel - The Cesium data source @@ -1281,6 +1303,16 @@ define( this.dataSourceCollection.add(cesiumModel) }, + /** + * Remove vector data (excluding 3D tilesets) from the Map. + * @param {Cesium.GeoJsonDataSource} cesiumModel - The Cesium data source + * model to remove from the map + * @since x.x.x + */ + removeVectorData: function (cesiumModel) { + this.dataSourceCollection.remove(cesiumModel) + }, + /** * Renders imagery in the Map. * @param {Cesium.ImageryLayer} cesiumModel The Cesium imagery model to render @@ -1290,12 +1322,24 @@ define( this.sortImagery() }, + /** + * Remove imagery from the Map. + * @param {Cesium.ImageryLayer} cesiumModel The Cesium imagery model to remove + * from the map + * @since x.x.x + */ + removeImagery: function (cesiumModel) { + console.log('Removing imagery from map', cesiumModel); + console.log('Imagery layers', this.scene.imageryLayers); + this.scene.imageryLayers.remove(cesiumModel) + }, + /** * Arranges the imagery that is rendered the Map according to the order * that the imagery is arranged in the layers collection. * @since 2.21.0 */ - sortImagery() { + sortImagery: function() { try { const imageryInMap = this.scene.imageryLayers const imageryModels = this.model.get('layers').getAll('CesiumImagery') diff --git a/src/js/views/maps/LayerListView.js b/src/js/views/maps/LayerListView.js index e55ce24ee..f53e300c4 100644 --- a/src/js/views/maps/LayerListView.js +++ b/src/js/views/maps/LayerListView.js @@ -79,12 +79,42 @@ define( this[key] = value; } } + this.setListeners(); } catch (e) { console.log('A LayerListView failed to initialize. Error message: ' + e); } }, + /** + * Remove any event listeners on the collection + * @since x.x.x + */ + removeListeners: function () { + try { + if (this.collection) { + this.stopListening(this.collection); + } + } catch (e) { + console.log('Failed to remove listeners:', e); + } + }, + + /** + * Add or remove items from the list when the collection changes + * @since x.x.x + */ + setListeners: function () { + try { + if (this.collection) { + this.listenTo(this.collection, 'add', this.render); + this.listenTo(this.collection, 'remove', this.render); + } + } catch (e) { + console.log('Failed to set listeners:', e); + } + }, + /** * Renders this view * @return {LayerListView} Returns the rendered view element