Skip to content

Commit

Permalink
Merge pull request #2392 from NCEAS/feature-2275/4-zoom-presets-in-vi…
Browse files Browse the repository at this point in the history
…ewfinder

Add zoom presets UI to viewfinder behind showZoomPresets flag
  • Loading branch information
ianguerin authored May 2, 2024
2 parents 0599edc + ce6360e commit ec8ed9a
Show file tree
Hide file tree
Showing 16 changed files with 547 additions and 105 deletions.
70 changes: 70 additions & 0 deletions src/js/collections/maps/viewfinder/ZoomPresets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
'use strict';

define(
[
'underscore',
'backbone',
'models/maps/viewfinder/ZoomPresetModel',
],
function (_, Backbone, ZoomPresetModel) {
/**
* @class ZoomPresets
* @classdesc A ZoomPresets collection is a group of ZoomPresetModel models
* that provide a location and list of layers to make visible when the user
* selects.
* @class ZoomPresets
* @classcategory Collections/Maps
* @extends Backbone.Collection
* @since x.x.x
* @constructor
*/
const ZoomPresets = Backbone.Collection.extend(
/** @lends ZoomPresets.prototype */ {
/** @inheritdoc */
model: ZoomPresetModel,

/**
* @param {Object[]} zoomPresets The raw list of objects that represent
* the zoom presets, to be converted into ZoomPresetModels.
* @param {MapAsset[]} allLayers All of the layers available for display
* in the map.
*/
parse({ zoomPresetObjects, allLayers }) {
if (isNonEmptyArray(zoomPresetObjects)) {
const zoomPresets = zoomPresetObjects.map(zoomPresetObj => {
const enabledLayerIds = [];
const enabledLayerLabels = [];
for (const layer of allLayers) {
if (zoomPresetObj.layerIds?.find(id => id === layer.get('layerId'))) {
enabledLayerIds.push(layer.get('layerId'));
enabledLayerLabels.push(layer.get('label'));
}
}

return new ZoomPresetModel({
description: zoomPresetObj.description,
enabledLayerLabels,
enabledLayerIds,
position: {
latitude: zoomPresetObj.latitude,
longitude: zoomPresetObj.longitude,
height: zoomPresetObj.height
},
title: zoomPresetObj.title,
}, { parse: true });
});

return zoomPresets;
}

return [];
},
}
);

function isNonEmptyArray(a) {
return a && a.length && Array.isArray(a);
}

return ZoomPresets;
});
49 changes: 46 additions & 3 deletions src/js/models/maps/Map.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,17 @@ define([
"collections/maps/MapAssets",
"models/maps/MapInteraction",
"collections/maps/AssetCategories",
], function ($, _, Backbone, MapAssets, Interactions, AssetCategories) {
"models/maps/GeoPoint",
"collections/maps/viewfinder/ZoomPresets",
], function ($,
_,
Backbone,
MapAssets,
Interactions,
AssetCategories,
GeoPoint,
ZoomPresets,
) {
/**
* @class MapModel
* @classdesc The Map Model contains all of the settings and options for a
Expand Down Expand Up @@ -54,7 +64,7 @@ define([
* @property {Boolean} [showHomeButton=true] - Whether or not to show the
* home button in the toolbar.
* @property {Boolean} [showViewfinder=false] - Whether or not to show the
* viefinder UI and viewfinder button in the toolbar. The ViewfinderView
* viewfinder UI and viewfinder button in the toolbar. The ViewfinderView
* requires a Google Maps API key present in the AppModel. In order to
* work properly the Geocoding API and Places API must be enabled.
* @property {Boolean} [toolbarOpen=false] - Whether or not the toolbar is
Expand All @@ -80,6 +90,10 @@ define([
* feedback section. showFeedback must be true for this to be shown.
* @property {String} [globeBaseColor=null] - The base color of the globe when no
* layer is shown.
* @property {ZoomPresets} [zoomPresets=null] - A Backbone.Collection of a
* predefined list of locations with an enabled list of layer IDs to be
* shown the zoom presets UI. Requires `showViewfinder` to be true as this
* UI appears within the ViewfinderView.
*
* @example
* {
Expand Down Expand Up @@ -179,7 +193,7 @@ define([
* @property {Boolean} [showHomeButton=true] - Whether or not to show the
* home button in the toolbar. True by default.
* @property {Boolean} [showViewfinder=false] - Whether or not to show the
* viefinder UI and viewfinder button in the toolbar. Defaults to false.
* viewfinder UI and viewfinder button in the toolbar. Defaults to false.
* @property {Boolean} [toolbarOpen=false] - Whether or not the toolbar is
* open when the map is initialized. Set to false by default, so that the
* toolbar is hidden by default.
Expand All @@ -199,6 +213,10 @@ define([
* feedback section.
* @property {String} [globeBaseColor=null] - The base color of the globe when no
* layer is shown.
* @property {ZoomPresets} [zoomPresets=null] - A Backbone.Collection of a
* predefined list of locations with an enabled list of layer IDs to be
* shown the zoom presets UI. Requires `showViewfinder` to be true as this
* UI appears within the ViewfinderView.
*/
defaults: function () {
return {
Expand Down Expand Up @@ -229,6 +247,7 @@ define([
showFeedback: false,
feedbackText: null,
globeBaseColor: null,
zoomPresets: null,
};
},

Expand Down Expand Up @@ -258,6 +277,11 @@ define([
if (isNonEmptyArray(config.terrains)) {
this.set("terrains", new MapAssets(config.terrains));
}

this.set('zoomPresetsCollection', new ZoomPresets({
zoomPresetObjects: config.zoomPresets,
allLayers: this.getAllLayers(),
}, { parse: true }));
}
this.setUpInteractions();
} catch (error) {
Expand Down Expand Up @@ -344,6 +368,25 @@ define([
return [];
},

/**
* Get all of the layers regardless of presences of layerCategories in a
* flat list of MapAsset models.
* @returns {MapAsset[]} All of the layers, or empty array if no layers
* are configured.
* @since x.x.x
*/
getAllLayers() {
if (this.has("layerCategories")) {
return this.get("layerCategories")
.getMapAssets()
.map(assets => assets.models)
.flat();
} else if (this.has("layers")) {
return this.get("layers").models;
}
return [];
},

/**
* 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
Expand Down
25 changes: 24 additions & 1 deletion src/js/models/maps/viewfinder/ViewfinderModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ define(
'backbone',
'cesium',
'models/geocoder/GeocoderSearch',
'models/maps/GeoPoint'],
'models/maps/GeoPoint'
],
(_, Backbone, Cesium, GeocoderSearch, GeoPoint) => {
const EMAIL = MetacatUI.appModel.get('emailContact');
const NO_RESULTS_MESSAGE = 'No search results found, try using another place name.';
Expand Down Expand Up @@ -40,6 +41,7 @@ define(
focusIndex: -1,
predictions: [],
query: '',
zoomPresets: [],
}
},

Expand All @@ -50,6 +52,9 @@ define(
initialize({ mapModel }) {
this.geocoderSearch = new GeocoderSearch();
this.mapModel = mapModel;
this.allLayers = this.mapModel.getAllLayers();

this.set('zoomPresets', mapModel.get('zoomPresetsCollection')?.models || []);
},

/**
Expand Down Expand Up @@ -146,6 +151,24 @@ define(
});
},

/**
* Select a ZoomPresetModel from the list of presets and navigate there.
* This function hides all layers that are not to be visible according to
* the ZoomPresetModel configuration.
* @param {ZoomPresetModel} preset A user selected preset for which to
* enable layers and navigate.
*/
selectZoomPreset(preset) {
const enabledLayerIds = preset.get('enabledLayerIds');
for (const layer of this.allLayers) {
const isVisible = enabledLayerIds.includes(layer.get('layerId'));
// Show or hide the layer according to the preset.
layer.set('visible', isVisible);
}

this.mapModel.zoomTo(preset.get('geoPoint'));
},

/**
* Select a prediction from the list of predictions and navigate there.
* @param {Prediction} prediction is the user-selected Prediction that
Expand Down
89 changes: 54 additions & 35 deletions src/js/models/maps/viewfinder/ZoomPresetModel.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,60 @@
'use strict';

define(['underscore', 'backbone',], (_, Backbone) => {
/**
* @class ZoomPresetModel
* @classdesc ZoomPresetModel represents a point of interest on a map that can
* be configured within a MapView.
* @classcategory Models/Maps
* @extends Backbone.Model
* @since x.x.x
*/
const ZoomPresetModel = Backbone.Model.extend(
define(
['underscore', 'backbone', 'models/maps/GeoPoint'],
(_, Backbone, GeoPoint) => {
/**
* @class ZoomPresetModel
* @classdesc ZoomPresetModel represents a point of interest on a map that can
* be configured within a MapView.
* @classcategory Models/Maps
* @extends Backbone.Model
* @since x.x.x
*/
const ZoomPresetModel = Backbone.Model.extend(
/** @lends ZoomPresetModel.prototype */ {

/**
* @typedef {Object} ZoomPresetModelOptions
* @property {string} title The displayed title for the preset.
* @property {GeoPoint} geoPoint The location representing this preset,
* including height information.
* @property {string} description A brief description of the layers and
* location.
* @property {string[]} enabledLayers A list of layer identifiers which are
* to be enabled for this preset.
*/
/**
* @typedef {Object} ZoomPresetModelOptions
* @property {string} title The displayed title for the preset.
* @property {GeoPoint} geoPoint The location representing this preset,
* including height information.
* @property {string} description A brief description of the layers and
* location.
* @property {string[]} enabledLayerIds A list of layer IDs which are to
* be enabled for this preset.
* @property {string[]} enabledLayerLabels A list of layer labels which
* are enabled for this preset.
*/

/**
* @name ZoomPresetModel#defaults
* @type {ZoomPresetModelOptions}
*/
defaults() {
return {
title: '',
geoPoint: null,
description: '',
enabledLayers: [],
}
},
});
/**
* @name ZoomPresetModel#defaults
* @type {ZoomPresetModelOptions}
*/
defaults() {
return {
description: '',
enabledLayerIds: [],
enabledLayerLabels: [],
geoPoint: null,
title: '',
}
},

return ZoomPresetModel;
});
/**
* @param {Object} position The latitude, longitude, and height of this
* ZoomPresetModel's GeoPoint.
*/
parse({ position, ...rest }) {
const geoPoint = new GeoPoint({
latitude: position.latitude,
longitude: position.longitude,
height: position.height
});

return { geoPoint, ...rest };
},
});

return ZoomPresetModel;
});
6 changes: 3 additions & 3 deletions src/js/templates/maps/viewfinder/viewfinder-zoom-preset.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
<%= preset.description %>
</div>
<div class="<%= classNames.layers %>">
<% _.each(preset.enabledLayers, enabledLayer=> { %>
<div class="<%= classNames.layer %>" title="<%= enabledLayer %>">
<% _.each(preset.enabledLayerLabels, label => { %>
<div class="<%= classNames.layer %>" title="<%= label %>">
<div class="<%= classNames.layerContent %>">
<i class="icon icon-eye-open"></i>
<%= enabledLayer %>
<%= label %>
</div>
</div>
<% }); %>
Expand Down
3 changes: 2 additions & 1 deletion src/js/templates/maps/viewfinder/viewfinder.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<div class="toolbar__content-header">Viewfinder</div>

<div class="<%= classNames.searchView %>"></div>
<div class="<%= classNames.searchView %>"></div>
<div class="<%= classNames.zoomPresetsView %>"></div>
2 changes: 1 addition & 1 deletion src/js/views/maps/viewfinder/SearchView.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ define(
},

/**
* Helper function to focus input on the searh query input and ensure
* Helper function to focus input on the search query input and ensure
* that the cursor is at the end of the text (as opposed to the beginning
* which appears to be the default jQuery behavior).
*/
Expand Down
Loading

0 comments on commit ec8ed9a

Please sign in to comment.