diff --git a/src/js/collections/maps/Features.js b/src/js/collections/maps/Features.js
index 768c17197..c49eb33b3 100644
--- a/src/js/collections/maps/Features.js
+++ b/src/js/collections/maps/Features.js
@@ -1,125 +1,118 @@
-'use strict';
+"use strict";
-define(
- [
- 'jquery',
- 'underscore',
- 'backbone',
- 'models/maps/Feature'
- ],
- function (
- $,
- _,
- Backbone,
- Feature
- ) {
+define(["jquery", "underscore", "backbone", "models/maps/Feature"], function (
+ $,
+ _,
+ Backbone,
+ Feature
+) {
+ /**
+ * @class Features
+ * @classdesc A Features collection contains the relevant properties of a group of
+ * selected geo-spatial features from a map.
+ * @class Features
+ * @classcategory Collections/Maps
+ * @extends Backbone.Collection
+ * @since 2.18.0
+ * @constructor
+ */
+ var Features = Backbone.Collection.extend(
+ /** @lends Features.prototype */ {
+ /**
+ * The class/model that this collection contains.
+ * @type {Backbone.Model}
+ */
+ model: Feature,
- /**
- * @class Features
- * @classdesc A Features collection contains the relevant properties of a group of
- * selected geo-spatial features from a map.
- * @class Features
- * @classcategory Collections/Maps
- * @extends Backbone.Collection
- * @since 2.18.0
- * @constructor
- */
- var Features = Backbone.Collection.extend(
- /** @lends Features.prototype */ {
+ /**
+ * Get an array of all of the unique Map Assets that are associated with this
+ * collection. (When a feature model is part of a layer, it will have the layer
+ * model (Map Asset) set as a property)
+ * @returns {MapAsset[]} Returns an a array of all the unique Map Assets (imagery,
+ * tile sets, etc.) in this collection.
+ */
+ getMapAssets: function () {
+ return this.getUniqueAttrs("mapAsset");
+ },
- /**
- * The class/model that this collection contains.
- * @type {Backbone.Model}
- */
- model: Feature,
+ /**
+ * Get an array of all the unique feature objects associated with this collection.
+ * @param {string} [type] Optionally set a type of feature to return. If set, then
+ * only features that have this constructor name will be returned.
+ * @returns {Array} Returns an array of all of the unique feature objects in the
+ * collection. Feature objects are the objects used by the map widget to represent
+ * a feature in the map. For example, in Cesium this could be a
+ * Cesium3DTileFeature or an Entity.
+ */
+ getFeatureObjects: function (type) {
+ let featureObjects = this.getUniqueAttrs("featureObject");
+ if (type) {
+ featureObjects = featureObjects.filter(function (featureObject) {
+ return featureObject.constructor.name === type;
+ });
+ }
+ return featureObjects;
+ },
- /**
- * Get an array of all of the unique Map Assets that are associated with this
- * collection. (When a feature model is part of a layer, it will have the layer
- * model (Map Asset) set as a property)
- * @returns {MapAsset[]} Returns an a array of all the unique Map Assets (imagery,
- * tile sets, etc.) in this collection.
- */
- getMapAssets: function () {
- return this.getUniqueAttrs('mapAsset')
- },
+ /**
+ * Get an array of unique values for some attribute that may be set on the models
+ * in this collection
+ * @param {string} attrName The name of the attr to get unique values for
+ * @returns {Array} Returns an array of unique values of the given attribute
+ */
+ getUniqueAttrs: function (attrName) {
+ try {
+ let uniqueAttrs = [];
+ this.each(function (featureModel) {
+ const attr = featureModel.get(attrName);
+ if (attr && !uniqueAttrs.includes(attr)) {
+ uniqueAttrs.push(attr);
+ }
+ });
+ return uniqueAttrs;
+ } catch (error) {
+ console.log(
+ `Failed to get unique attributes for "${attrName}".`,
+ error
+ );
+ }
+ },
- /**
- * Get an array of all the unique feature objects associated with this collection.
- * @param {string} [type] Optionally set a type of feature to return. If set, then
- * only features that have this constructor name will be returned.
- * @returns {Array} Returns an array of all of the unique feature objects in the
- * collection. Feature objects are the objects used by the map widget to represent
- * a feature in the map. For example, in Cesium this could be a
- * Cesium3DTileFeature or an Entity.
- */
- getFeatureObjects: function (type) {
- let featureObjects = this.getUniqueAttrs('featureObject')
- if (type) {
- featureObjects = featureObjects.filter(function (featureObject) {
- return featureObject.constructor.name === type
- })
- }
- return featureObjects
- },
+ /**
+ * Checks if a given feature object is an attribute in one of the Feature models
+ * in this collection.
+ * @param {Feature|Cesium.Cesium3DTilesetFeature|Cesium.Entity} featureObject
+ * @returns {boolean} Returns true if the given feature object is in this
+ * collection, false otherwise.
+ * @since 2.25.0
+ */
+ containsFeature: function (featureObject) {
+ if (this.models.length === 0) return false;
+ if (!featureObject) return false;
+ featureObject =
+ featureObject instanceof Feature
+ ? featureObject.get("featureObject")
+ : featureObject;
+ return this.findWhere({ 'featureObject': featureObject }) ? true : false;
+ },
- /**
- * Get an array of unique values for some attribute that may be set on the models
- * in this collection
- * @param {string} attrName The name of the attr to get unique values for
- * @returns {Array} Returns an array of unique values of the given attribute
- */
- getUniqueAttrs: function (attrName) {
- try {
- let uniqueAttrs = []
- this.each(function (featureModel) {
- const attr = featureModel.get(attrName)
- if (attr && !uniqueAttrs.includes(attr)) {
- uniqueAttrs.push(attr)
- }
- })
- return uniqueAttrs
- }
- catch (error) {
- console.log(
- 'Failed to get unique values for an attribute in a Features collection' +
- '. Error details: ' + error
- );
- }
- },
+ /**
+ * Checks if a given array of feature objects are attributes in one of the
+ * Feature models in this collection.
+ * @param {Array} featureObjects An array of feature objects to check if they are
+ * in this collection.
+ * @returns {boolean} Returns true if all of the given feature objects are in this
+ * collection, false otherwise.
+ */
+ containsFeatures: function (featureObjects) {
+ if (!featureObjects || !featureObjects.length) return false;
+ return featureObjects.every((featureObject) =>
+ this.containsFeature(featureObject)
+ );
+ },
- /**
- * Checks if a given feature object is an attribute in one of the Feature models
- * in this collection.
- * @param {Feature|Cesium.Cesium3DTilesetFeature|Cesium.Entity} featureObject
- * @returns {boolean} Returns true if the given feature object is in this
- * collection, false otherwise.
- * @since 2.25.0
- */
- containsFeature: function (featureObject) {
- if (!featureObject) return false;
- featureObject = featureObject instanceof Feature ? featureObject.get('featureObject') : featureObject;
- return this.findWhere({ featureObject: featureObject }) ? true : false;
- },
+ }
+ );
- /**
- * Checks if a given array of feature objects are attributes in one of the
- * Feature models in this collection.
- * @param {Array} featureObjects An array of feature objects to check if they are
- * in this collection.
- * @returns {boolean} Returns true if all of the given feature objects are in this
- * collection, false otherwise.
- */
- containsFeatures: function (featureObjects) {
- if (!featureObjects || !featureObjects.length) return false;
- return featureObjects.every(
- (featureObject) => this.containsFeature(featureObject));
- },
-
- }
- );
-
- return Features;
-
- }
-);
\ No newline at end of file
+ return Features;
+});
diff --git a/src/js/collections/maps/MapAssets.js b/src/js/collections/maps/MapAssets.js
index 262ff3c2a..1dc3a6d7b 100644
--- a/src/js/collections/maps/MapAssets.js
+++ b/src/js/collections/maps/MapAssets.js
@@ -142,12 +142,8 @@ define([
this.each(function (mapAssetModel) {
mapAssetModel.set("mapModel", mapModel);
});
- } catch (error) {
- console.log(
- "Failed to set the map model on a MapAssets collection" +
- ". Error details: " +
- error
- );
+ } catch (e) {
+ console.log("Failed to set the map model on MapAssets collection", e);
}
},
@@ -236,6 +232,7 @@ define([
* @since 2.25.0
*/
getFeatureAttributes: function (features) {
+ if (!Array.isArray(features)) features = [features];
return features.map((feature) => {
const asset = this.findAssetWithFeature(feature);
return asset?.getFeatureAttributes(feature);
diff --git a/src/js/models/maps/Feature.js b/src/js/models/maps/Feature.js
index f68c61e4a..66b2b0fc6 100644
--- a/src/js/models/maps/Feature.js
+++ b/src/js/models/maps/Feature.js
@@ -1,190 +1,156 @@
-'use strict';
-
-define(
- [
- 'jquery',
- 'underscore',
- 'backbone'
- ],
- function (
- $,
- _,
- Backbone
- ) {
- /**
- * @classdesc A Feature Model organizes information about a single feature of a vector
- * layer in a map.
- * @classcategory Models/Maps
- * @class Feature
- * @name Feature
- * @extends Backbone.Model
- * @since 2.18.0
- * @constructor
- */
- var Feature = Backbone.Model.extend(
- /** @lends Feature.prototype */ {
-
- /**
- * The name of this type of model
- * @type {string}
- */
- type: 'Feature',
-
- /**
- * Default attributes for Feature models
- * @name Feature#defaults
- * @type {Object}
- * @property {Object} properties Property names (keys) and property values
- * (values) for properties set on this feature. For example, the properties that
- * would be in an attributes table for a shapefile.
- * @property {MapAsset} mapAsset If the feature is part of a Map Asset, then the
- * model for that asset. For example, if this is a feature if a 3D tileset, then
- * the Cesium3DTileset map asset model.
- * @property {string|number} featureID An ID that's used to identify this feature
- * in the map. It should be unique among all map features. (In Cesium, this is the
- * Pick ID key.)
- * @property {*} featureObject The object that a Map widget uses to represent this
- * feature in the map. For example, in Cesium this could be a
- * Cesium.Cesium3DTileFeature or a Cesium.Entity.
- * @property {string} label An optional friendly label or name for this feature.
- */
- defaults: function () {
- return {
- properties: {},
- mapAsset: null,
- featureID: null,
- featureObject: null,
- label: null
+"use strict";
+
+define(["jquery", "underscore", "backbone"], function ($, _, Backbone) {
+ /**
+ * @classdesc A Feature Model organizes information about a single feature of
+ * a vector layer in a map.
+ * @classcategory Models/Maps
+ * @class Feature
+ * @name Feature
+ * @extends Backbone.Model
+ * @since 2.18.0
+ * @constructor
+ */
+ var Feature = Backbone.Model.extend(
+ /** @lends Feature.prototype */ {
+ /**
+ * The name of this type of model
+ * @type {string}
+ */
+ type: "Feature",
+
+ /**
+ * Default attributes for Feature models
+ * @name Feature#defaults
+ * @type {Object}
+ * @property {Object} properties Property names (keys) and property values
+ * (values) for properties set on this feature. For example, the
+ * properties that would be in an attributes table for a shapefile.
+ * @property {MapAsset} mapAsset If the feature is part of a Map Asset,
+ * then the model for that asset. For example, if this is a feature if a
+ * 3D tileset, then the Cesium3DTileset map asset model.
+ * @property {string|number} featureID An ID that's used to identify this
+ * feature in the map. It should be unique among all map features. (In
+ * Cesium, this is the Pick ID key.)
+ * @property {*} featureObject The object that a Map widget uses to
+ * represent this feature in the map. For example, in Cesium this could be
+ * a Cesium.Cesium3DTileFeature or a Cesium.Entity.
+ * @property {string} label An optional friendly label or name for this
+ * feature.
+ */
+ defaults: function () {
+ return {
+ properties: {},
+ mapAsset: null,
+ featureID: null,
+ featureObject: null,
+ label: null,
+ };
+ },
+
+ /**
+ * Checks if the attributes for this model are only the default
+ * attributes.
+ * @returns {boolean} Returns true if all of the attributes are equal to
+ * the default attributes, false otherwise.
+ */
+ isDefault: function () {
+ try {
+ var defaults = this.defaults();
+ var current = this.attributes;
+ return _.isEqual(defaults, current);
+ } catch (error) {
+ console.log(
+ "Failed to check if a Feature model is the default.",
+ error
+ );
+ }
+ },
+
+ /**
+ * Clears all the model attributes and sets them to the default values.
+ * This will trigger a change event. No event is triggered if all of the
+ * value are already set to default.
+ */
+ setToDefault: function () {
+ try {
+ // Don't make changes if model is already the default
+ if (!this.isDefault()) {
+ this.clear({ silent: true });
+ this.set(this.defaults());
}
- },
-
- // /**
- // * Executed when a new Feature model is created.
- // * @param {Object} [attributes] The initial values of the attributes, which will
- // * be set on the model.
- // * @param {Object} [options] Options for the initialize function.
- // */
- // initialize: function (attributes, options) {
- // try {
-
- // }
- // catch (error) {
- // console.log(
- // 'There was an error initializing a Feature model' +
- // '. Error details: ' + error
- // );
- // }
- // },
-
- /**
- * Checks if the attributes for this model are only the default attributes.
- * @returns {boolean} Returns true if all of the attributes are equal to the
- * default attributes, false otherwise.
- */
- isDefault: function () {
- try {
- var defaults = this.defaults()
- var current = this.attributes
- return _.isEqual(defaults, current)
+ } catch (error) {
+ console.log(
+ "There was an error reset a Feature model to default" +
+ ". Error details: " +
+ error
+ );
+ }
+ },
+
+ /**
+ * Given an map-widget-specific-object representing a feature, and a
+ * MapAssets collection, this function will attempt to find the
+ * corresponding MapAsset model in the collection, and extract the
+ * feature attributes from that model.
+ * @param {*} feature - An object representing a feature in the map. For
+ * example, in Cesium this could be a Cesium.Cesium3DTileFeature or a
+ * Cesium.Entity.
+ * @param {MapAssets} assets - A MapAssets collection to use to extract
+ * feature attributes from a feature object.
+ * @returns {object} - The JSON object of all the Feature attributes
+ * @since x.x.x
+ */
+ attrsFromFeatureObject: function (feature, assets) {
+ if (feature instanceof Feature) {
+ return feature.clone().attributes;
+ }
+ let attrs = null;
+ // if this is already an object with feature attributes, return it
+ if (typeof feature == "object") {
+ if (
+ feature.hasOwnProperty("mapAsset") &&
+ feature.hasOwnProperty("properties")
+ ) {
+ attrs = feature;
+ } else if (assets) {
+ attrs = assets.getFeatureAttributes([feature])[0];
}
- catch (error) {
- console.log(
- 'There was an error checking if a Feature model has only default attributes in a Feature' +
- '. Error details: ' + error
+ }
+ return attrs;
+ },
+
+ /**
+ * Parses the given input into a JSON object to be set on the model. If
+ * passed a MapAssets collection as an option, and the input includes an
+ * assets property, then the parse function will attempt to find the
+ * feature object's corresponding MapAsset model in the collection, and
+ * extract the feature attributes from that model.
+ *
+ * @param {object} input - The raw response object
+ * @param {object} [options] - Options for the parse function
+ * @property {MapAssets} [options.assets] - A MapAssets collection to use
+ * to extract feature attributes from a feature object.
+ * @return {object} - The JSON object of all the Feature attributes
+ */
+ parse: function (input, options) {
+ try {
+ if (!input) return null;
+ if (input.featureObject && options.assets) {
+ const attrs = this.attrsFromFeatureObject(
+ input.featureObject,
+ options.assets
);
+ input = Object.assign({}, input, attrs);
}
- },
-
- /**
- * Clears all the model attributes and sets them to the default values. This will
- * trigger a change event. No event is triggered if all of the value are already
- * set to default.
- */
- setToDefault: function () {
- try {
- // Don't make changes if model is already the default
- if (!this.isDefault()) {
- this.clear({ silent: true })
- this.set(this.defaults())
- }
- }
- catch (error) {
- console.log(
- 'There was an error reset a Feature model to default' +
- '. Error details: ' + error
- );
- }
- },
-
- // /**
- // * Parses the given input into a JSON object to be set on the model.
- // *
- // * @param {TODO} input - The raw response object
- // * @return {TODO} - The JSON object of all the Feature attributes
- // */
- // parse: function (input) {
-
- // try {
-
- // var modelJSON = {};
-
- // return modelJSON
-
- // }
- // catch (error) {
- // console.log(
- // 'There was an error parsing a Feature model' +
- // '. Error details: ' + error
- // );
- // }
-
- // },
-
- // /**
- // * Overrides the default Backbone.Model.validate.function() to check if this if
- // * the values set on this model are valid.
- // *
- // * @param {Object} [attrs] - A literal object of model attributes to validate.
- // * @param {Object} [options] - A literal object of options for this validation
- // * process
- // *
- // * @return {Object} - Returns a literal object with the invalid attributes and
- // * their corresponding error message, if there are any. If there are no errors,
- // * returns nothing.
- // */
- // validate: function (attrs, options) {
- // try {
-
- // }
- // catch (error) {
- // console.log(
- // 'There was an error validating a Feature model' +
- // '. Error details: ' + error
- // );
- // }
- // },
-
- // /**
- // * Creates a string using the values set on this model's attributes.
- // * @return {string} The Feature string
- // */
- // serialize: function () {
- // try {
- // var serializedFeature = '';
-
- // return serializedFeature;
- // }
- // catch (error) {
- // console.log(
- // 'There was an error serializing a Feature model' +
- // '. Error details: ' + error
- // );
- // }
- // },
-
- });
- return Feature;
+ return input;
+ } catch (error) {
+ console.log("Failed to parse a Feature model", error);
+ }
+ },
+ }
+ );
- }
-);
+ return Feature;
+});
diff --git a/src/js/models/maps/GeoBoundingBox.js b/src/js/models/maps/GeoBoundingBox.js
index 93dc4959c..87e4b7cb2 100644
--- a/src/js/models/maps/GeoBoundingBox.js
+++ b/src/js/models/maps/GeoBoundingBox.js
@@ -84,10 +84,7 @@ define(["backbone"], function (Backbone) {
*/
getArea: function () {
if (!this.isValid()) {
- console.warn(
- `Bounds are invalid: ${JSON.stringify(bounds)}. ` +
- `Returning the globe's area for the given bounding box.`
- );
+ console.warn("Invalid bounding box, returning globe area");
return 360 * 180;
}
const { north, south, east, west } = this.attributes;
diff --git a/src/js/models/maps/Map.js b/src/js/models/maps/Map.js
index 341c73962..f15cfe4a5 100644
--- a/src/js/models/maps/Map.js
+++ b/src/js/models/maps/Map.js
@@ -210,11 +210,7 @@ define([
}
this.setUpInteractions();
} catch (error) {
- console.log(
- "There was an error initializing a Map model" +
- ". Error details: " +
- error
- );
+ console.log('Failed to initialize a Map model.', error);
}
},
diff --git a/src/js/models/maps/MapInteraction.js b/src/js/models/maps/MapInteraction.js
index b7dc2efd4..efa0e6ce6 100644
--- a/src/js/models/maps/MapInteraction.js
+++ b/src/js/models/maps/MapInteraction.js
@@ -123,9 +123,9 @@ define([
* zoom) and sets the 'firstInteraction' attribute to true when it occurs.
*/
listenForFirstInteraction: function () {
+ const model = this;
if (model.get("firstInteraction")) return;
const listener = new Backbone.Model();
- const model = this;
listener.listenTo(
this,
"change:previousAction",
@@ -153,8 +153,11 @@ define([
// Clone the models in hovered features and set them as clicked features
const hoveredFeatures = this.get("hoveredFeatures").models;
this.setClickedFeatures(hoveredFeatures);
- if (this.get("mapModel")?.get("clickFeatureAction") === "showDetails") {
+ const clickAction = this.get("mapModel")?.get("clickFeatureAction");
+ if (clickAction === "showDetails") {
this.selectFeatures(hoveredFeatures);
+ } else if (clickAction === "zoom") {
+ this.set("zoomTarget", hoveredFeatures[0]);
}
},
@@ -235,6 +238,7 @@ define([
* view.
*/
selectFeatures: function (features) {
+ const model = this;
this.setFeatures(features, "selectedFeatures", true);
},
@@ -257,13 +261,13 @@ define([
// Create a features collection if one doesn't already exist
if (!model.get(type)) model.set(type, new Features());
- // Remove any null or undefined features
+ // Remove any null, undefined, or empty features
if (Array.isArray(features)) features = features.filter((f) => f);
- // Remove any default features (which are empty models)
if (features instanceof Features) {
features = features.filter((f) => !f.isDefault());
}
- // If no feature is passed to this function (and replace is true),
+
+ // Empty collection if features array is empty (and replace is true)
if (!features || features.length === 0) {
if (replace) model.get(type).set([], { remove: true });
return;
@@ -280,51 +284,17 @@ define([
return;
}
- // Convert the feature objects, which may be types specific to the map
- // widget (Cesium), to a generic Feature model
- features = model.convertFeatures(features);
+ const assets = this.get("mapModel")?.get("layers");
- // Update the Feature model with the new selected feature information.
- const newAttrs = features.map(function (feature) {
- return Object.assign(
- {},
- new Feature().defaults(),
- feature.attributes
- );
- });
- model.get(type).set(newAttrs, { remove: replace });
+ const newAttrs = features.map((f) => ({ featureObject: f }));
+
+ model
+ .get(type)
+ .set(newAttrs, { remove: replace, parse: true, assets: assets });
} catch (e) {
console.log("Failed to select a Feature in a Map model.", e);
}
},
-
- /**
- * Convert an array of feature objects to an array of Feature models.
- * @param {Cesium.Entity|Cesium.Cesium3DTileFeature|Feature[]} features -
- * An array of feature objects selected directly from the map view, or
- * @returns {Feature[]} An array of Feature models.
- * @since 2.25.0
- */
- convertFeatures: function (features) {
- if (!features) return [];
- if (!features.map) features = [features];
- const mapModel = this.get("mapModel");
- const attrs = features.map(function (feature) {
- if (!feature) return null;
- if (feature instanceof Feature) return feature.attributes;
- // if this is already an object with feature attributes, return it
- if (
- feature.hasOwnProperty("mapAsset") &&
- feature.hasOwnProperty("properties")
- ) {
- return feature;
- }
- // Otherwise, assume it's a Cesium object and get the feature
- // attributes
- return mapModel.get("layers").getFeatureAttributes(features)?.[0];
- });
- return attrs.map((attr) => new Feature(attr));
- },
}
);
diff --git a/src/js/models/maps/assets/Cesium3DTileset.js b/src/js/models/maps/assets/Cesium3DTileset.js
index 0968dda8e..df560048a 100644
--- a/src/js/models/maps/assets/Cesium3DTileset.js
+++ b/src/js/models/maps/assets/Cesium3DTileset.js
@@ -2,22 +2,14 @@
define(
[
- 'jquery',
- 'underscore',
- 'backbone',
'cesium',
'models/maps/assets/MapAsset',
- 'models/maps/AssetColor',
'models/maps/AssetColorPalette',
'collections/maps/VectorFilters'
],
function (
- $,
- _,
- Backbone,
Cesium,
MapAsset,
- AssetColor,
AssetColorPalette,
VectorFilters
) {
@@ -74,7 +66,8 @@ define(
* specific to each type of asset.
*/
defaults: function () {
- return _.extend(
+ return Object.assign(
+ {},
this.constructor.__super__.defaults(),
{
type: 'Cesium3DTileset',
@@ -215,6 +208,9 @@ define(
setListeners: function () {
try {
+ // call the super method
+ this.constructor.__super__.setListeners.call(this);
+
// When opacity, color, or visibility changes (will also update the filters)
this.stopListening(this, 'change:opacity change:color change:visible')
this.listenTo(
@@ -245,6 +241,8 @@ define(
// changes on a Cesium map.
const cesiumModel = model.get('cesiumModel')
+ if(!cesiumModel) return
+
// If the layer isn't visible at all, don't bother setting up colors or
// filters. Just set every feature to hidden.
if (!model.isVisible()) {
diff --git a/src/js/models/maps/assets/CesiumVectorData.js b/src/js/models/maps/assets/CesiumVectorData.js
index fee95fc5b..060c0b6d4 100644
--- a/src/js/models/maps/assets/CesiumVectorData.js
+++ b/src/js/models/maps/assets/CesiumVectorData.js
@@ -106,7 +106,7 @@ define(
initialize: function (assetConfig) {
try {
- if(!assetConfig) assetConfig = {};
+ if (!assetConfig) assetConfig = {};
MapAsset.prototype.initialize.call(this, assetConfig);
@@ -240,19 +240,18 @@ define(
*/
setListeners: function () {
try {
+ this.constructor.__super__.setListeners.call(this);
const appearEvents =
'change:visible change:opacity change:color change:outlineColor' +
' change:temporarilyHidden'
this.stopListening(this, appearEvents)
this.listenTo(this, appearEvents, this.updateAppearance)
- this.stopListening(this.get('filters'), 'update')
- this.listenTo(this.get('filters'), 'update', this.updateFeatureVisibility)
+ const filters = this.get('filters');
+ this.stopListening(filters, 'update')
+ this.listenTo(filters, 'update', this.updateFeatureVisibility)
}
catch (error) {
- console.log(
- 'There was an error setting listeners in a CesiumVectorData model' +
- '. Error details: ' + error
- );
+ console.log('Failed to set CesiumVectorData listeners.', error);
}
},
@@ -289,7 +288,7 @@ define(
* @returns {Cesium.Entity} - The Entity object if found, otherwise null.
* @since 2.25.0
*/
- getEntityFromMapObject(mapObject) {
+ getEntityFromMapObject: function(mapObject) {
const entityType = this.get("featureType")
if (mapObject instanceof entityType) return mapObject
if (mapObject.id instanceof entityType) return mapObject.id
@@ -328,7 +327,7 @@ define(
* @returns {Object} An object containing key-value mapping of property names to
* properties.
*/
- getPropertiesFromFeature(feature) {
+ getPropertiesFromFeature: function(feature) {
feature = this.getEntityFromMapObject(feature)
if (!feature) return null
const featureProps = feature.properties
@@ -382,7 +381,10 @@ define(
try {
const model = this;
- const cesiumModel = this.get('cesiumModel')
+ const cesiumModel = this.get('cesiumModel');
+
+ if (!cesiumModel) return
+
const entities = cesiumModel.entities.values
// Suspending events while updating a large number of entities helps
@@ -564,10 +566,11 @@ define(
* @since 2.25.0
*/
getStyles: function (entity) {
- if(!entity) return null
+ if (!entity) return null
+ entity = this.getEntityFromMapObject(entity)
if (this.featureIsSelected(entity)) {
return this.getSelectedStyles(entity)
- }
+ }
const color = this.colorForEntity(entity);
if (!color) { return null }
const outlineColor = this.colorToCesiumColor(
diff --git a/src/js/models/maps/assets/MapAsset.js b/src/js/models/maps/assets/MapAsset.js
index 0441f4b46..b47b0e42e 100644
--- a/src/js/models/maps/assets/MapAsset.js
+++ b/src/js/models/maps/assets/MapAsset.js
@@ -1,296 +1,285 @@
-'use strict';
-
-define(
- [
- 'jquery',
- 'underscore',
- 'backbone',
- 'models/portals/PortalImage',
- 'models/maps/AssetColorPalette',
- MetacatUI.root + '/components/dayjs.min.js'
- ],
- function (
- $,
- _,
- Backbone,
- PortalImage,
- AssetColorPalette,
- dayjs
- ) {
- /**
- * @classdesc A MapAsset Model comprises information required to fetch source data for
- * some asset or resource that is displayed in a map, such as imagery (raster) tiles,
- * vector data, a 3D tileset, or a terrain model. This model also contains metadata
- * about the source data, like an attribution and a description. It represents the
- * most generic type of asset, and can be extended to support specific assets that are
- * compatible with a given map widget.
- * @classcategory Models/Maps/Assets
- * @class MapAsset
- * @name MapAsset
- * @extends Backbone.Model
- * @since 2.18.0
- * @constructor
- */
- var MapAsset = Backbone.Model.extend(
- /** @lends MapAsset.prototype */ {
-
- /**
- * The name of this type of model
- * @type {string}
- */
- type: 'MapAsset',
-
- /**
- * Default attributes for MapAsset models
- * @name MapAsset#defaults
- * @type {Object}
- * @property {('Cesium3DTileset'|'BingMapsImageryProvider'|'IonImageryProvider'|'WebMapTileServiceImageryProvider'|'TileMapServiceImageryProvider'|'CesiumTerrainProvider')} type
- * The format of the data. Must be one of the supported types.
- * @property {string} label A user friendly name for this asset, to be displayed
- * in a map.
- * @property {string} [icon = '']
- * A PID for an SVG saved as a dataObject, or an SVG string. The SVG will be used
- * as an icon that will be displayed next to the label in the layers list. It
- * should be an SVG file that has no fills, borders, or styles set on it (since
- * the icon will be shaded dynamically by the maps CSS using a fill attribute). It
- * must use a viewbox property rather than a height and width.
- * @property {string} [description = ''] A brief description about the asset, e.g.
- * which area it covers, the resolution, etc.
- * @property {string} [attribution = ''] A credit or attribution to display along
- * with this map resource.
- * @property {string} [moreInfoLink = ''] A link to show in a map where a user can
- * find more information about this resource.
- * @property {string} [downloadLink = ''] A link to show in a map where a user can
- * go to download the source data.
- * @property {string} [id = ''] If this asset's data is archived in a DataONE
- * repository, the ID of the data package.
- * @property {Boolean} [selected = false] Set to true when this asset has been
- * selected by the user in the layer list.
- * @property {Number} [opacity = 1] A number between 0 and 1 indicating the
- * opacity of the layer on the map, with 0 representing fully transparent and 1
- * representing fully opaque. This applies to raster (imagery) and vector assets,
- * not to terrain assets.
- * @property {Boolean} [visible = true] Set to true if the layer is visible on the
- * map, false if it is hidden. This applies to raster (imagery) and vector assets,
- * not to terrain assets.
- * @property {AssetColorPalette} [colorPalette] The color or colors mapped to
- * attributes of this asset. This applies to raster/imagery and vector assets. For
- * imagery, the colorPalette will be used to create a legend. For vector assets
- * (e.g. 3Dtilesets), it will also be used to style the features.
- * @property {MapConfig#FeatureTemplate} [featureTemplate] Configuration for
- * content and layout of the Feature Info panel - the panel that shows information
- * about a selected feature from a vector asset ({@link FeatureInfoView}).
- * @property {Cesium.Entity|Cesium.3DTilesetFeature} [featureType] For vector
- * and 3d tileset assets, the object type that cesium uses to represent features
- * from the asset. Null for imagery and terrain assets.
- * @property {MapConfig#CustomProperties} [customProperties] Configuration that
- * allows for the definition of custom feature properties, potentially based on
- * other properties. For example, a custom property could be a formatted version
- * of an existing date property.
- * @property {MapConfig#Notification} [notification] A custom badge and message to
- * display about the layer in the Layer list. For example, this could highlight
- * the layer if it is new, give a warning if they layer is under development, etc.
- * @property {'ready'|'error'|null} [status = null] Set to 'ready' when the
- * resource is loaded and ready to be rendered in a map view. Set to 'error' when
- * the asset is not supported, or there was a problem requesting the resource.
- * @property {string} [statusDetails = null] Any further details about the status,
- * especially when there was an error.
- */
- defaults: function () {
- return {
- type: '',
- label: '',
- icon: '',
- description: '',
- attribution: '',
- moreInfoLink: '',
- downloadLink: '',
- id: '',
- selected: false,
- opacity: 1,
- visible: true,
- colorPalette: null,
- customProperties: {},
- featureTemplate: {},
- featureType: null,
- notification: {},
- status: null,
- statusDetails: null
- }
- },
-
- /**
- * The source of a specific asset (i.e. layer or terrain data) to show on the map,
- * as well as metadata and display properties of the asset. Some properties listed
- * here do not apply to all asset types, but this is specified in the property
- * description.
- * @typedef {Object} MapAssetConfig
- * @name MapConfig#MapAssetConfig
- * @property {('Cesium3DTileset'|'BingMapsImageryProvider'|'IonImageryProvider'|'WebMapTileServiceImageryProvider'|'WebMapServiceImageryProvider'|'TileMapServiceImageryProvider'|'NaturalEarthII'|'CesiumTerrainProvider'|'GeoJsonDataSource'|'USGSImageryTopo'|'OpenStreetMapImageryProvider')} type -
- * A string indicating the format of the data. Some of these types correspond
- * directly to Cesium classes. The NaturalEarthII type is a special imagery layer
- * that automatically sets the cesiumOptions to load the Natural Earth II imagery
- * that is shipped with Cesium/MetacatUI. If this type is set, then no other
- * cesiumOptions are required. The same is true for USGSImageryTopo, which pulls
- * imagery directly from USGS.
- * @property {(Cesium3DTileset#cesiumOptions|CesiumImagery#cesiumOptions|CesiumTerrain#cesiumOptions|CesiumVectorData#cesiumOptions)} [cesiumOptions] -
- * For MapAssets that are configured for Cesium, like
- * Cesium3DTilesets, an object with options to pass to the Cesium constructor
- * function that creates the Cesium model. Options are specific to each type of
- * asset. For details, see documentation for each of the types.
- * @property {string} label - A user friendly name for this asset, to be displayed
- * in a map.
- * @property {string} [icon] - A PID for an SVG saved as a dataObject, or an SVG
- * string. The SVG will be used as an icon that will be displayed next to the
- * label in the layers list. It should be an SVG file that has no fills, borders,
- * or styles set on it (since the icon will be shaded dynamically by the maps CSS
- * using a fill attribute). It must use a viewbox property rather than a height
- * and width.
- * @property {Number} [opacity=1] - A number between 0 and 1 indicating the
- * opacity of the layer on the map, with 0 representing fully transparent and 1
- * representing fully opaque. This applies to raster (imagery) and vector assets,
- * not to terrain assets.
- * @property {Boolean} [visible=true] - Set to true if the layer is visible on the
- * map, false if it is hidden. This applies to raster (imagery) and vector assets,
- * not to terrain assets.
- * @property {string} [description] - A brief description about the asset, e.g.
- * which area it covers, the resolution, etc.
- * @property {string} [attribution] A credit or attribution to display along with
- * this asset.
- * @property {string} [moreInfoLink] A complete URL used to create a link to show
- * in a map where a user can find more information about this resource.
- * @property {string} [downloadLink] A complete URL used to show a link in a map
- * where a user can go to download the source data.
- * @property {string} [id] If this asset's data is archived in a DataONE
- * repository, the ID of the data package.
- * @property {MapConfig#ColorPaletteConfig} [colorPalette] The color or colors
- * mapped to attributes of this asset. This applies to raster/imagery and vector
- * assets. For imagery, the colorPalette will be used to create a legend. For
- * vector assets (e.g. 3Dtilesets), it will also be used to style the features.
- * @property {MapConfig#FeatureTemplate} [featureTemplate] Configuration for the
- * content and layout of the Feature Info panel ({@link FeatureInfoView}) - the
- * panel that shows information about a selected feature from a vector asset. If
- * no feature template is set, then the default table layout is used.
- * @property {MapConfig#CustomProperties} [customProperties] Definitions of custom
- * properties of features, potentially based on existing properties. For example,
- * a custom property could be a formatted version of another date property. These
- * custom properties can be used in the filters, colorPalette, or featureTemplate.
- * So far, custom strings and formatted dates are supported. Eventually, the
- * custom properties may be expanded to support formatted numbers and booleans.
- * @property {MapConfig#VectorFilterConfig} [filters] - A set of conditions used
- * to show or hide specific features of this tileset.
- * @property {MapConfig#Notification} [notification] A custom badge and message to
- * display about the layer in the Layer list. For example, this could highlight
- * the layer if it is new, give a warning if they layer is under development, etc.
- */
-
- /**
- * A feature template configures the format and content of information displayed
- * in the Feature Info panel ({@link FeatureInfoView}). The Feature Info panel is
- * displayed in a map when a user clicks on a vector feature in a map.
- * @typedef {Object} FeatureTemplate
- * @name MapConfig#FeatureTemplate
- * @since 2.19.0
- * @property {'story'|'table'} [template='table'] The name/ID of the template to
- * use. This must match the name of one of the templates available in
- * {@link FeatureInfoView#contentTemplates}.
- * @property {string} [label] Sets which of the feature properties to use as the
- * title for the FeatureInfoView. The string must exactly match the key for a
- * property that exists in the feature.
- * @property {MapConfig#StoryTemplateOptions} [options] A list of key-value pairs
- * that map the template variable to a property/attribute of the the feature. Keys
- * are the template variable names and values are the names of properties in the
- * feature. Template variable names are specific to each template. Currently only
- * the 'story' template allows variables. These are specified in the
- * {@link FeatureInfoView#contentTemplates}.
- * @example
- * // Use the "story" template, which shows a secondary title, image, description,
- * // and link.
- * {
- * "template": "story",
- * "label": "title",
- * "options": {
- * "subtitle": "formattedDate",
- * "description": "summary",
- * "thumbnail": "imageSrc",
- * "url": "newsLink",
- * "urlText": "newsTitle",
- * }
- * }
- * @example
- * // Use the default template (a table), but use the "forestName" attribute for
- * // the FeatureInfo panel label
- * {
- * "label": "forestName"
- * }
- */
-
- /**
- * An object that maps template variable to feature properties for the "story"
- * template.
- * @typedef {Object}
- * @name MapConfig#StoryTemplateOptions
- * @since 2.19.0
- * @property {string} subtitle The name of a feature property to use for a
- * secondary title in the template
- * @property {string} description The name of a feature property that contains a
- * brief summary or description of the feature; displayed as a paragraph.
- * @property {string} thumbnail The name of a feature property that contains a URL
- * for an image. Displayed as a thumbnail next to the description.
- * @property {string} url The name of a feature property with a URL to use to
- * create a link (e.g. to learn more information about the given feature)
- * @property {string} urlText The name of a feature property that has text to
- * display for the url. Defaults to 'Read More' if none is set.
- */
-
- /**
- * An object where the keys indicate the name/ID of the new custom property to
- * create, and the values are an object that defines the new property.
- * @typedef {Object.} CustomProperties
- * @name MapConfig#CustomProperties
- * @since 2.19.0
- * @example
- * {
- * "year": {
- * "type": "date",
- * "property": "dateTime",
- * "format": "YYYY",
- * },
- * "urlText": {
- * "type": "string",
- * "value": "Click here to learn more about this feature"
- * }
- * }
- */
-
- /**
- * An object that defines a formatted date to use as a property in a feature. Used
- * in the {@link MapConfig#CustomProperties} object.
- * @typedef {Object} CustomDateProperty
- * @name MapConfig#CustomDateProperty
- * @since 2.19.0
- * @property {'date'} type Must be set to 'date' to indicate that this is a custom
- * date property
- * @property {string} property The name/ID of the existing date property to format
- * @property {string} format A string that indicates the new format to use.
- * Follows the syntax used by Day.JS, see
- * {@link https://day.js.org/docs/en/display/format}
- */
-
- /**
- * An object that defines a custom string to use as a property in a feature. Used
- * in the {@link MapConfig#CustomProperties} object.
- * @typedef {Object} CustomStringProperty
- * @name MapConfig#CustomStringProperty
- * @since 2.19.0
- * @property {'string'} type Must be set to 'string' to indicate that this is a
- * custom string property
- * @property {string} value The new string to use. So far only static strings are
- * available. In the future, templates that include other properties may be
- * supported.
- */
-
- /**
+"use strict";
+
+define([
+ "underscore",
+ "backbone",
+ "models/portals/PortalImage",
+ "models/maps/AssetColorPalette",
+ MetacatUI.root + "/components/dayjs.min.js",
+], function (_, Backbone, PortalImage, AssetColorPalette, dayjs) {
+ /**
+ * @classdesc A MapAsset Model comprises information required to fetch source data for
+ * some asset or resource that is displayed in a map, such as imagery (raster) tiles,
+ * vector data, a 3D tileset, or a terrain model. This model also contains metadata
+ * about the source data, like an attribution and a description. It represents the
+ * most generic type of asset, and can be extended to support specific assets that are
+ * compatible with a given map widget.
+ * @classcategory Models/Maps/Assets
+ * @class MapAsset
+ * @name MapAsset
+ * @extends Backbone.Model
+ * @since 2.18.0
+ * @constructor
+ */
+ var MapAsset = Backbone.Model.extend(
+ /** @lends MapAsset.prototype */ {
+ /**
+ * The name of this type of model
+ * @type {string}
+ */
+ type: "MapAsset",
+
+ /**
+ * Default attributes for MapAsset models
+ * @name MapAsset#defaults
+ * @type {Object}
+ * @property {('Cesium3DTileset'|'BingMapsImageryProvider'|'IonImageryProvider'|'WebMapTileServiceImageryProvider'|'TileMapServiceImageryProvider'|'CesiumTerrainProvider')} type
+ * The format of the data. Must be one of the supported types.
+ * @property {string} label A user friendly name for this asset, to be displayed
+ * in a map.
+ * @property {string} [icon = '']
+ * A PID for an SVG saved as a dataObject, or an SVG string. The SVG will be used
+ * as an icon that will be displayed next to the label in the layers list. It
+ * should be an SVG file that has no fills, borders, or styles set on it (since
+ * the icon will be shaded dynamically by the maps CSS using a fill attribute). It
+ * must use a viewbox property rather than a height and width.
+ * @property {string} [description = ''] A brief description about the asset, e.g.
+ * which area it covers, the resolution, etc.
+ * @property {string} [attribution = ''] A credit or attribution to display along
+ * with this map resource.
+ * @property {string} [moreInfoLink = ''] A link to show in a map where a user can
+ * find more information about this resource.
+ * @property {string} [downloadLink = ''] A link to show in a map where a user can
+ * go to download the source data.
+ * @property {string} [id = ''] If this asset's data is archived in a DataONE
+ * repository, the ID of the data package.
+ * @property {Boolean} [selected = false] Set to true when this asset has been
+ * selected by the user in the layer list.
+ * @property {Number} [opacity = 1] A number between 0 and 1 indicating the
+ * opacity of the layer on the map, with 0 representing fully transparent and 1
+ * representing fully opaque. This applies to raster (imagery) and vector assets,
+ * not to terrain assets.
+ * @property {Boolean} [visible = true] Set to true if the layer is visible on the
+ * map, false if it is hidden. This applies to raster (imagery) and vector assets,
+ * not to terrain assets.
+ * @property {AssetColorPalette} [colorPalette] The color or colors mapped to
+ * attributes of this asset. This applies to raster/imagery and vector assets. For
+ * imagery, the colorPalette will be used to create a legend. For vector assets
+ * (e.g. 3Dtilesets), it will also be used to style the features.
+ * @property {MapConfig#FeatureTemplate} [featureTemplate] Configuration for
+ * content and layout of the Feature Info panel - the panel that shows information
+ * about a selected feature from a vector asset ({@link FeatureInfoView}).
+ * @property {Cesium.Entity|Cesium.3DTilesetFeature} [featureType] For vector
+ * and 3d tileset assets, the object type that cesium uses to represent features
+ * from the asset. Null for imagery and terrain assets.
+ * @property {MapConfig#CustomProperties} [customProperties] Configuration that
+ * allows for the definition of custom feature properties, potentially based on
+ * other properties. For example, a custom property could be a formatted version
+ * of an existing date property.
+ * @property {MapConfig#Notification} [notification] A custom badge and message to
+ * display about the layer in the Layer list. For example, this could highlight
+ * the layer if it is new, give a warning if they layer is under development, etc.
+ * @property {'ready'|'error'|null} [status = null] Set to 'ready' when the
+ * resource is loaded and ready to be rendered in a map view. Set to 'error' when
+ * the asset is not supported, or there was a problem requesting the resource.
+ * @property {string} [statusDetails = null] Any further details about the status,
+ * especially when there was an error.
+ */
+ defaults: function () {
+ return {
+ type: "",
+ label: "",
+ icon: '',
+ description: "",
+ attribution: "",
+ moreInfoLink: "",
+ downloadLink: "",
+ id: "",
+ selected: false,
+ opacity: 1,
+ visible: true,
+ colorPalette: null,
+ customProperties: {},
+ featureTemplate: {},
+ featureType: null,
+ notification: {},
+ status: null,
+ statusDetails: null,
+ };
+ },
+
+ /**
+ * The source of a specific asset (i.e. layer or terrain data) to show on the map,
+ * as well as metadata and display properties of the asset. Some properties listed
+ * here do not apply to all asset types, but this is specified in the property
+ * description.
+ * @typedef {Object} MapAssetConfig
+ * @name MapConfig#MapAssetConfig
+ * @property {('Cesium3DTileset'|'BingMapsImageryProvider'|'IonImageryProvider'|'WebMapTileServiceImageryProvider'|'WebMapServiceImageryProvider'|'TileMapServiceImageryProvider'|'NaturalEarthII'|'CesiumTerrainProvider'|'GeoJsonDataSource'|'USGSImageryTopo'|'OpenStreetMapImageryProvider')} type -
+ * A string indicating the format of the data. Some of these types correspond
+ * directly to Cesium classes. The NaturalEarthII type is a special imagery layer
+ * that automatically sets the cesiumOptions to load the Natural Earth II imagery
+ * that is shipped with Cesium/MetacatUI. If this type is set, then no other
+ * cesiumOptions are required. The same is true for USGSImageryTopo, which pulls
+ * imagery directly from USGS.
+ * @property {(Cesium3DTileset#cesiumOptions|CesiumImagery#cesiumOptions|CesiumTerrain#cesiumOptions|CesiumVectorData#cesiumOptions)} [cesiumOptions] -
+ * For MapAssets that are configured for Cesium, like
+ * Cesium3DTilesets, an object with options to pass to the Cesium constructor
+ * function that creates the Cesium model. Options are specific to each type of
+ * asset. For details, see documentation for each of the types.
+ * @property {string} label - A user friendly name for this asset, to be displayed
+ * in a map.
+ * @property {string} [icon] - A PID for an SVG saved as a dataObject, or an SVG
+ * string. The SVG will be used as an icon that will be displayed next to the
+ * label in the layers list. It should be an SVG file that has no fills, borders,
+ * or styles set on it (since the icon will be shaded dynamically by the maps CSS
+ * using a fill attribute). It must use a viewbox property rather than a height
+ * and width.
+ * @property {Number} [opacity=1] - A number between 0 and 1 indicating the
+ * opacity of the layer on the map, with 0 representing fully transparent and 1
+ * representing fully opaque. This applies to raster (imagery) and vector assets,
+ * not to terrain assets.
+ * @property {Boolean} [visible=true] - Set to true if the layer is visible on the
+ * map, false if it is hidden. This applies to raster (imagery) and vector assets,
+ * not to terrain assets.
+ * @property {string} [description] - A brief description about the asset, e.g.
+ * which area it covers, the resolution, etc.
+ * @property {string} [attribution] A credit or attribution to display along with
+ * this asset.
+ * @property {string} [moreInfoLink] A complete URL used to create a link to show
+ * in a map where a user can find more information about this resource.
+ * @property {string} [downloadLink] A complete URL used to show a link in a map
+ * where a user can go to download the source data.
+ * @property {string} [id] If this asset's data is archived in a DataONE
+ * repository, the ID of the data package.
+ * @property {MapConfig#ColorPaletteConfig} [colorPalette] The color or colors
+ * mapped to attributes of this asset. This applies to raster/imagery and vector
+ * assets. For imagery, the colorPalette will be used to create a legend. For
+ * vector assets (e.g. 3Dtilesets), it will also be used to style the features.
+ * @property {MapConfig#FeatureTemplate} [featureTemplate] Configuration for the
+ * content and layout of the Feature Info panel ({@link FeatureInfoView}) - the
+ * panel that shows information about a selected feature from a vector asset. If
+ * no feature template is set, then the default table layout is used.
+ * @property {MapConfig#CustomProperties} [customProperties] Definitions of custom
+ * properties of features, potentially based on existing properties. For example,
+ * a custom property could be a formatted version of another date property. These
+ * custom properties can be used in the filters, colorPalette, or featureTemplate.
+ * So far, custom strings and formatted dates are supported. Eventually, the
+ * custom properties may be expanded to support formatted numbers and booleans.
+ * @property {MapConfig#VectorFilterConfig} [filters] - A set of conditions used
+ * to show or hide specific features of this tileset.
+ * @property {MapConfig#Notification} [notification] A custom badge and message to
+ * display about the layer in the Layer list. For example, this could highlight
+ * the layer if it is new, give a warning if they layer is under development, etc.
+ */
+
+ /**
+ * A feature template configures the format and content of information displayed
+ * in the Feature Info panel ({@link FeatureInfoView}). The Feature Info panel is
+ * displayed in a map when a user clicks on a vector feature in a map.
+ * @typedef {Object} FeatureTemplate
+ * @name MapConfig#FeatureTemplate
+ * @since 2.19.0
+ * @property {'story'|'table'} [template='table'] The name/ID of the template to
+ * use. This must match the name of one of the templates available in
+ * {@link FeatureInfoView#contentTemplates}.
+ * @property {string} [label] Sets which of the feature properties to use as the
+ * title for the FeatureInfoView. The string must exactly match the key for a
+ * property that exists in the feature.
+ * @property {MapConfig#StoryTemplateOptions} [options] A list of key-value pairs
+ * that map the template variable to a property/attribute of the the feature. Keys
+ * are the template variable names and values are the names of properties in the
+ * feature. Template variable names are specific to each template. Currently only
+ * the 'story' template allows variables. These are specified in the
+ * {@link FeatureInfoView#contentTemplates}.
+ * @example
+ * // Use the "story" template, which shows a secondary title, image, description,
+ * // and link.
+ * {
+ * "template": "story",
+ * "label": "title",
+ * "options": {
+ * "subtitle": "formattedDate",
+ * "description": "summary",
+ * "thumbnail": "imageSrc",
+ * "url": "newsLink",
+ * "urlText": "newsTitle",
+ * }
+ * }
+ * @example
+ * // Use the default template (a table), but use the "forestName" attribute for
+ * // the FeatureInfo panel label
+ * {
+ * "label": "forestName"
+ * }
+ */
+
+ /**
+ * An object that maps template variable to feature properties for the "story"
+ * template.
+ * @typedef {Object}
+ * @name MapConfig#StoryTemplateOptions
+ * @since 2.19.0
+ * @property {string} subtitle The name of a feature property to use for a
+ * secondary title in the template
+ * @property {string} description The name of a feature property that contains a
+ * brief summary or description of the feature; displayed as a paragraph.
+ * @property {string} thumbnail The name of a feature property that contains a URL
+ * for an image. Displayed as a thumbnail next to the description.
+ * @property {string} url The name of a feature property with a URL to use to
+ * create a link (e.g. to learn more information about the given feature)
+ * @property {string} urlText The name of a feature property that has text to
+ * display for the url. Defaults to 'Read More' if none is set.
+ */
+
+ /**
+ * An object where the keys indicate the name/ID of the new custom property to
+ * create, and the values are an object that defines the new property.
+ * @typedef {Object.} CustomProperties
+ * @name MapConfig#CustomProperties
+ * @since 2.19.0
+ * @example
+ * {
+ * "year": {
+ * "type": "date",
+ * "property": "dateTime",
+ * "format": "YYYY",
+ * },
+ * "urlText": {
+ * "type": "string",
+ * "value": "Click here to learn more about this feature"
+ * }
+ * }
+ */
+
+ /**
+ * An object that defines a formatted date to use as a property in a feature. Used
+ * in the {@link MapConfig#CustomProperties} object.
+ * @typedef {Object} CustomDateProperty
+ * @name MapConfig#CustomDateProperty
+ * @since 2.19.0
+ * @property {'date'} type Must be set to 'date' to indicate that this is a custom
+ * date property
+ * @property {string} property The name/ID of the existing date property to format
+ * @property {string} format A string that indicates the new format to use.
+ * Follows the syntax used by Day.JS, see
+ * {@link https://day.js.org/docs/en/display/format}
+ */
+
+ /**
+ * An object that defines a custom string to use as a property in a feature. Used
+ * in the {@link MapConfig#CustomProperties} object.
+ * @typedef {Object} CustomStringProperty
+ * @name MapConfig#CustomStringProperty
+ * @since 2.19.0
+ * @property {'string'} type Must be set to 'string' to indicate that this is a
+ * custom string property
+ * @property {string} value The new string to use. So far only static strings are
+ * available. In the future, templates that include other properties may be
+ * supported.
+ */
+
+ /**
* A notification displays a badge in the {@link LayerListView} and a message in
* the {@link LayerDetailsView}. This is useful for indicating some special status
* of the layer: "new", "under development", etc.
@@ -309,551 +298,596 @@ define(
* @param {MapConfig#MapAssetConfig} [assetConfig] The initial values of the
* attributes, which will be set on the model.
*/
- initialize: function (assetConfig) {
- try {
-
- const model = this;
-
- if (!assetConfig || typeof assetConfig !== 'object') {
- assetConfig = {}
- } else {
- assetConfig = JSON.parse(JSON.stringify(assetConfig))
- }
-
- // Set the color palette
- if (assetConfig.colorPalette) {
- this.set('colorPalette', new AssetColorPalette(assetConfig.colorPalette))
- }
-
- // Fetch the icon, if there is one
- if (assetConfig.icon) {
- if (model.isSVG(assetConfig.icon)) {
- model.updateIcon(assetConfig.icon)
- } else {
- // If the string is not an SVG then assume it is a PID and try to fetch
- // the SVG file. fetchIcon will update the icon when complete.
- model.fetchIcon(assetConfig.icon)
- }
- }
+ initialize: function (assetConfig) {
+ try {
+ const model = this;
- this.setListeners();
- }
- catch (e) {
- console.log('Error initializing a MapAsset model', e);
+ if (!assetConfig || typeof assetConfig !== "object") {
+ assetConfig = {};
+ } else {
+ assetConfig = JSON.parse(JSON.stringify(assetConfig));
}
- },
-
- /**
- * Set all of the listeners for this model
- * @since x.x.x
- */
- setListeners: function () {
- try {
-
- // The map asset cannot be visible on the map if there was an error loading
- // the asset
- this.stopListening(this, 'change:status')
- this.listenTo(this, 'change:status', function (model, status) {
- if (status === 'error') {
- this.set('visible', false)
- }
- })
- this.stopListening(this, 'change:visible')
- this.listenTo(this, 'change:visible', function (model, visible) {
- if (this.get('status') === 'error') {
- this.set('visible', false)
- }
- })
-
- // Update the style of the asset to highlight the selected features when
- // features from this asset are selected in the map.
- if (typeof this.updateAppearance === 'function') {
- const setSelectFeaturesListeners = function () {
- const mapModel = this.get('mapModel');
- if (!mapModel) { return }
- const interactions = mapModel.get('interactions');
- const selectedFeatures = mapModel.getSelectedFeatures();
-
- this.stopListening(selectedFeatures, 'update');
- this.listenTo(selectedFeatures, 'update', this.updateAppearance)
-
- this.stopListening(interactions, 'change:selectedFeatures')
- this.listenTo(interactions, 'change:selectedFeatures', function () {
- this.updateAppearance()
- setSelectFeaturesListeners()
- })
+ // Set the color palette
+ if (assetConfig.colorPalette) {
+ this.set(
+ "colorPalette",
+ new AssetColorPalette(assetConfig.colorPalette)
+ );
+ }
- }
- setSelectFeaturesListeners.call(this)
- this.stopListening(this, 'change:mapModel', setSelectFeaturesListeners)
- this.listenTo(this, 'change:mapModel', setSelectFeaturesListeners)
+ // Fetch the icon, if there is one
+ if (assetConfig.icon) {
+ if (model.isSVG(assetConfig.icon)) {
+ model.updateIcon(assetConfig.icon);
+ } else {
+ // If the string is not an SVG then assume it is a PID and try to fetch
+ // the SVG file. fetchIcon will update the icon when complete.
+ model.fetchIcon(assetConfig.icon);
}
-
- // Listen for changes to the cesiumOptions object
- this.stopListening(this, 'change:cesiumOptions');
- this.listenTo(this, 'change:cesiumOptions', function () {
- this.createCesiumModel(true)
- })
- } catch (e) {
- console.log("Error setting MapAsset Listeners.", e);
}
- },
-
- /**
- * Get the asset config's cesiumOptions, if it has any. This will return
- * a copy of the cesiumOptions object, so that changes made to the
- * returned object will not affect the original cesiumOptions object.
- * @returns {Object} A copy of the cesiumOptions object, or null if there
- * are no cesiumOptions.
- * @since 2.26.0
- */
- getCesiumOptions: function () {
- const cesiumOptions = this.get('cesiumOptions')
- if (!cesiumOptions) { return null }
- return JSON.parse(JSON.stringify(cesiumOptions))
- },
-
- /**
- * Given a feature object from a Feature model, checks if it is part of the
- * selectedFeatures collection. See featureObject property from
- * {@link Feature#defaults}. For vector and 3d tile models only.
- * @param {*} feature - An object that a Map widget uses to represent this feature
- * in the map, e.g. a Cesium.Entity or a Cesium.Cesium3DTileFeature
- * @returns {boolean} Returns true if the given feature is part of the
- * selectedFeatures collection in this asset
- */
- featureIsSelected: function (feature) {
- const map = this.get('mapModel')
- if (!map) { return false }
- return map.getSelectedFeatures();
- },
-
- /**
- * Checks if a feature from the map (a Cesium object) is the type of
- * feature that this map asset model contains. For example, if a
- * Cesium3DTilesetFeature is passed to this function, this function
- * will return true if it is a Cesium3DTileset model, and false if it
- * is a CesiumVectorData model.
- * @param {Cesium.Cesium3DTilesetFeature|Cesium.Entity} feature
- * @returns {boolean} true if the feature is an instance of the feature
- * type set on the asset model, false otherwise.
- * @since 2.25.0
- */
- usesFeatureType: function(feature) {
- const ft = this.get("featureType");
- if (!feature || !ft) return false
- if (!feature instanceof ft) return false
- return true
- },
-
- /**
- * Given a feature object from a Feature model, checks if it is part of the
- * selectedFeatures collection. See featureObject property from
- * {@link Feature#defaults}. For vector and 3d tile models only.
- * @param {*} feature - An object that a Map widget uses to represent this feature
- * in the map, e.g. a Cesium.Entity or a Cesium.Cesium3DTileFeature
- * @returns {boolean} Returns true if the given feature is part of the
- * selectedFeatures collection in this asset
- * @since 2.25.0
- */
- containsFeature: function (feature) {
- if (!this.usesFeatureType(feature)) return false
- if (!this.getCesiumModelFromFeature) return false
- const cesiumModel = this.getCesiumModelFromFeature(feature)
- if (!cesiumModel) return false
- if (this.get('cesiumModel') == cesiumModel) return true
- return false
- },
-
- /**
- * Given a feature object from a Feature model, returns the attributes
- * needed to create a Feature model. For vector and 3d tile models only.
- * @param {*} feature - An object that a Map widget uses to represent this feature
- * in the map, e.g. a Cesium.Entity or a Cesium.Cesium3DTileFeature
- * @returns {Object} An object with properties, mapAsset, featureID, featureObject,
- * and label properties. Returns null if the feature is not the correct type
- * for this asset model.
- */
- getFeatureAttributes: function (feature) {
- if (!this.usesFeatureType(feature)) return null;
- if (!this.getCesiumModelFromFeature) return null;
- return {
- properties: this.getPropertiesFromFeature(feature),
- mapAsset: this,
- featureID: this.getIDFromFeature(feature),
- featureObject: feature,
- label: this.getLabelFromFeature(feature),
+ this.setListeners();
+ } catch (e) {
+ console.log("Error initializing a MapAsset model", e);
+ }
+ },
+
+ /**
+ * When the asset can't be loaded, hide it from the map and show an error.
+ * @since x.x.x
+ */
+ handleError: function () {
+ this.set("visible", false);
+ this.stopListening(this, "change:visible");
+ },
+
+ /**
+ * Set all of the listeners for this model
+ * @since x.x.x
+ */
+ setListeners: function () {
+ try {
+ const status = this.get("status");
+ if (status === "error") {
+ this.handleError();
+ return;
}
- },
-
- /**
- * Given a set of properties from a Feature from this Map Asset model, add any
- * custom properties to the properties object and return it.
- * @since 2.19.0
- * @param {Object} properties A set of key-value pairs representing the existing
- * properties of a feature from this asset.
- * @returns {Object} The properties object with any custom properties added.
- */
- addCustomProperties: function (properties) {
- try {
-
- const model = this;
- const customProperties = model.get('customProperties');
- const formattedProperties = {};
-
- if (!customProperties || !Object.keys(customProperties).length) {
- return properties
+ // The map asset cannot be visible on the map if there was an error
+ // loading the asset
+ this.stopListening(this, "change:status");
+ this.listenTo(this, "change:status", function (model, status) {
+ if (status === "error") {
+ this.handleError();
+ } else {
+ this.setListeners();
}
+ });
- if (!properties || typeof properties !== 'object') {
- properties = {}
- }
+ // Listen for changes to the cesiumOptions object
+ this.stopListening(this, "change:cesiumOptions");
+ this.listenTo(this, "change:cesiumOptions", function () {
+ this.createCesiumModel(true);
+ });
- if (customProperties) {
- _.each(customProperties, function (config, key) {
- let formattedValue = '';
- if (config.type === 'date') {
- formattedValue = model.formatDateProperty(config, properties)
- // TODO: support formatted numbers and booleans...
- // } else if (config.type === 'number') {
- // formattedValue = model.formatNumberProperty(config, properties)
- // } else if (config.type === 'boolean') {
- // formattedValue = model.formatBooleanProperty(config, properties)
- } else {
- formattedValue = model.formatStringProperty(config, properties)
- }
- formattedProperties[key] = formattedValue;
- });
- }
- // merge the properties with the formatted properties
- return Object.assign(properties, formattedProperties);
- } catch (error) {
- console.log(
- 'There was an error adding custom properties. Returning properties ' +
- 'unchanged. Error details: ' +
- error
- );
- return properties
- }
- },
+ this.listenToSelectedFeatures();
+ } catch (e) {
+ console.log("Error setting MapAsset Listeners.", e);
+ }
+ },
+
+ /**
+ * Update the appearance of features from this asset when they are
+ * selected or deselected in the map widget.
+ * @since x.x.x
+ */
+ listenToSelectedFeatures: function () {
+ if (typeof this.updateAppearance !== "function") {
+ return;
+ }
+
+ const mapModel = this.get("mapModel");
+ if (!mapModel) {
+ this.listenToOnce(
+ this,
+ "change:mapModel",
+ this.listenToSelectedFeatures
+ );
+ return;
+ }
+
+ const interactions = mapModel.get("interactions");
+
+ if (!interactions) {
+ this.listenToOnce(
+ mapModel,
+ "change:interactions",
+ this.listenToSelectedFeatures
+ );
+ return;
+ }
+
+ const selectedFeatures = mapModel.getSelectedFeatures();
+
+ if(selectedFeatures){
+ this.stopListening(selectedFeatures, "update");
+ this.listenTo(selectedFeatures, "update", this.updateAppearance);
+ }
+
+ // Reset the listeners if the selectedFeatures collection or the
+ // interactions model is replaced
+ this.listenToOnce(interactions, "change:selectedFeatures", function () {
+ this.updateAppearance();
+ this.listenToSelectedFeatures();
+ });
+
+ this.listenToOnce(mapModel, "change:interactions", function () {
+ this.updateAppearance();
+ this.listenToSelectedFeatures();
+ });
+ },
+
+ /**
+ * Get the asset config's cesiumOptions, if it has any. This will return
+ * a copy of the cesiumOptions object, so that changes made to the
+ * returned object will not affect the original cesiumOptions object.
+ * @returns {Object} A copy of the cesiumOptions object, or null if there
+ * are no cesiumOptions.
+ * @since 2.26.0
+ */
+ getCesiumOptions: function () {
+ const cesiumOptions = this.get("cesiumOptions");
+ if (!cesiumOptions) {
+ return null;
+ }
+ return JSON.parse(JSON.stringify(cesiumOptions));
+ },
+
+ /**
+ * Given a feature object from a Feature model, checks if it is part of the
+ * selectedFeatures collection. See featureObject property from
+ * {@link Feature#defaults}. For vector and 3d tile models only.
+ * @param {*} feature - An object that a Map widget uses to represent this feature
+ * in the map, e.g. a Cesium.Entity or a Cesium.Cesium3DTileFeature
+ * @returns {boolean} Returns true if the given feature is part of the
+ * selectedFeatures collection in this asset
+ */
+ featureIsSelected: function (feature) {
+ const map = this.get("mapModel");
+ if (!map) {
+ return false;
+ }
+ const selectedFeatures = map.getSelectedFeatures();
+ if (!selectedFeatures) {
+ return false;
+ }
+ const isSelected = selectedFeatures.containsFeature(feature);
+ return isSelected;
+ },
+
+ /**
+ * Checks if a feature from the map (a Cesium object) is the type of
+ * feature that this map asset model contains. For example, if a
+ * Cesium3DTilesetFeature is passed to this function, this function
+ * will return true if it is a Cesium3DTileset model, and false if it
+ * is a CesiumVectorData model.
+ * @param {Cesium.Cesium3DTilesetFeature|Cesium.Entity} feature
+ * @returns {boolean} true if the feature is an instance of the feature
+ * type set on the asset model, false otherwise.
+ * @since 2.25.0
+ */
+ usesFeatureType: function (feature) {
+ const ft = this.get("featureType");
+ if (!feature || !ft) return false;
+ if (!feature instanceof ft) return false;
+ return true;
+ },
+
+ /**
+ * Given a feature object from a Feature model, checks if it is part of the
+ * selectedFeatures collection. See featureObject property from
+ * {@link Feature#defaults}. For vector and 3d tile models only.
+ * @param {*} feature - An object that a Map widget uses to represent this feature
+ * in the map, e.g. a Cesium.Entity or a Cesium.Cesium3DTileFeature
+ * @returns {boolean} Returns true if the given feature is part of the
+ * selectedFeatures collection in this asset
+ * @since 2.25.0
+ */
+ containsFeature: function (feature) {
+ if (!this.usesFeatureType(feature)) return false;
+ if (!this.getCesiumModelFromFeature) return false;
+ const cesiumModel = this.getCesiumModelFromFeature(feature);
+ if (!cesiumModel) return false;
+ if (this.get("cesiumModel") == cesiumModel) return true;
+ return false;
+ },
+
+ /**
+ * Given a feature object from a Feature model, returns the attributes
+ * needed to create a Feature model. For vector and 3d tile models only.
+ * @param {*} feature - An object that a Map widget uses to represent this feature
+ * in the map, e.g. a Cesium.Entity or a Cesium.Cesium3DTileFeature
+ * @returns {Object} An object with properties, mapAsset, featureID, featureObject,
+ * and label properties. Returns null if the feature is not the correct type
+ * for this asset model.
+ */
+ getFeatureAttributes: function (feature) {
+ if (!this.usesFeatureType(feature)) return null;
+ if (!this.getCesiumModelFromFeature) return null;
+ return {
+ properties: this.getPropertiesFromFeature(feature),
+ mapAsset: this,
+ featureID: this.getIDFromFeature(feature),
+ featureObject: feature,
+ label: this.getLabelFromFeature(feature),
+ };
+ },
+
+ /**
+ * Given a set of properties from a Feature from this Map Asset model, add any
+ * custom properties to the properties object and return it.
+ * @since 2.19.0
+ * @param {Object} properties A set of key-value pairs representing the existing
+ * properties of a feature from this asset.
+ * @returns {Object} The properties object with any custom properties added.
+ */
+ addCustomProperties: function (properties) {
+ try {
+ const model = this;
+ const customProperties = model.get("customProperties");
+ const formattedProperties = {};
- /**
- * Given a definition for a new date property, and the properties that already
- * exist on a specific feature, returns a new string with the formatted date.
- * @since 2.19.0
- * @param {MapConfig#CustomDateProperty} config - An object that defines the new
- * date property to create
- * @param {Object} properties key-value pairs representing existing properties in
- * a Feature
- * @returns {string} The value for the new date property, formatted as defined by
- * config, for the given feature
- */
- formatDateProperty: function (config, properties) {
- try {
- if (!properties) {
- properties = {}
- }
- let formattedDate = ''
- if (!config || !config.format) {
- return formattedDate;
- }
- const value = properties[config.property];
- if (value) {
- formattedDate = dayjs(value).format(config.format);
- }
- return formattedDate;
- }
- catch (error) {
- console.log(
- 'There was an error formatting a date for a Feature model' +
- '. Error details: ' + error
- );
- return '';
+ if (!customProperties || !Object.keys(customProperties).length) {
+ return properties;
}
- },
- /**
- * For a given set of Feature properties and a definition for a new sting
- * property, returns the value of the custom property. Note that since only static
- * strings are supported so far, this function essentially just returns the value
- * of config.value. This function exists to allow support of dynamic strings in
- * the future (e.g. combining strings from existing properties)
- * @since 2.19.0
- * @param {MapConfig#CustomStringProperty} config The object the defines the new
- * custom property
- * @param {Object} properties key-value pairs representing existing properties in
- * a Feature
- * @returns {string} The new string for the given Feature property
- */
- formatStringProperty: function (config, properties) {
- try {
- if (!properties) {
- properties = {}
- }
- let formattedString = ''
- if (!config || !config.value) {
- return formattedString;
- }
- formattedString = config.value;
- return formattedString;
+ if (!properties || typeof properties !== "object") {
+ properties = {};
}
- catch (error) {
- console.log(
- 'There was an error formatting a string for a Feature model' +
- '. Error details: ' + error
- );
- return '';
- }
- },
-
- // formatNumberProperty: function (config, properties) {
- // try {
- // if (!properties) {
- // properties = {}
- // }
- // let formattedNumber = ''
- // // TODO...
- // }
- // catch (error) {
- // console.log(
- // 'There was an error formatting a number for a Feature model' +
- // '. Error details: ' + error
- // );
- // return '';
- // }
- // },
-
- // formatBooleanProperty: function (config, properties) {
- // try {
- // if (!properties) {
- // properties = {}
- // }
- // let formattedBoolean = ''
- // // TODO...
- // }
- // catch (error) {
- // console.log(
- // 'There was an error formatting a boolean for a Feature model' +
- // '. Error details: ' + error
- // );
- // return '';
- // }
- // },
- /**
- * Sanitizes an SVG string and updates the model's 'icon' attribute the sanitized
- * string. Also sets the 'iconStatus' attribute to 'success'.
- * @param {string} icon An SVG string to use for the MapAsset icon
- */
- updateIcon: function (icon) {
- const model = this
- try {
- model.sanitizeIcon(icon, function (sanitizedIcon) {
- model.set('icon', sanitizedIcon)
- model.set('iconStatus', 'success')
- })
+ if (customProperties) {
+ _.each(customProperties, function (config, key) {
+ let formattedValue = "";
+ if (config.type === "date") {
+ formattedValue = model.formatDateProperty(config, properties);
+ // TODO: support formatted numbers and booleans...
+ // } else if (config.type === 'number') {
+ // formattedValue = model.formatNumberProperty(config, properties)
+ // } else if (config.type === 'boolean') {
+ // formattedValue = model.formatBooleanProperty(config, properties)
+ } else {
+ formattedValue = model.formatStringProperty(config, properties);
+ }
+ formattedProperties[key] = formattedValue;
+ });
}
- catch (error) {
- console.log(
- 'There was an error updating an icon in a MapAsset model' +
- '. Error details: ' + error
- );
+ // merge the properties with the formatted properties
+ return Object.assign(properties, formattedProperties);
+ } catch (error) {
+ console.log(
+ "There was an error adding custom properties. Returning properties " +
+ "unchanged. Error details: " +
+ error
+ );
+ return properties;
+ }
+ },
+
+ /**
+ * Given a definition for a new date property, and the properties that already
+ * exist on a specific feature, returns a new string with the formatted date.
+ * @since 2.19.0
+ * @param {MapConfig#CustomDateProperty} config - An object that defines the new
+ * date property to create
+ * @param {Object} properties key-value pairs representing existing properties in
+ * a Feature
+ * @returns {string} The value for the new date property, formatted as defined by
+ * config, for the given feature
+ */
+ formatDateProperty: function (config, properties) {
+ try {
+ if (!properties) {
+ properties = {};
}
- },
-
- /**
- * Simple test to see if a string is an SVG
- * @param {string} str The string to check
- * @returns {Boolean} Returns true if the string starts with `