@cesium/widgets
Version:
A widgets library for use with CesiumJS. CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
332 lines (300 loc) • 10.3 kB
JavaScript
import {
Frozen,
defined,
DeveloperError,
EllipsoidTerrainProvider,
ImageryLayer,
Terrain,
} from "@cesium/engine";
import knockout from "../ThirdParty/knockout.js";
import createCommand from "../createCommand.js";
/**
* The view model for {@link BaseLayerPicker}.
* @alias BaseLayerPickerViewModel
* @constructor
*
* @param {object} options Object with the following properties:
* @param {Globe} options.globe The Globe to use.
* @param {ProviderViewModel[]} [options.imageryProviderViewModels=[]] The array of ProviderViewModel instances to use for imagery.
* @param {ProviderViewModel} [options.selectedImageryProviderViewModel] The view model for the current base imagery layer, if not supplied the first available imagery layer is used.
* @param {ProviderViewModel[]} [options.terrainProviderViewModels=[]] The array of ProviderViewModel instances to use for terrain.
* @param {ProviderViewModel} [options.selectedTerrainProviderViewModel] The view model for the current base terrain layer, if not supplied the first available terrain layer is used.
*
* @exception {DeveloperError} imageryProviderViewModels must be an array.
* @exception {DeveloperError} terrainProviderViewModels must be an array.
*/
function BaseLayerPickerViewModel(options) {
options = options ?? Frozen.EMPTY_OBJECT;
const globe = options.globe;
const imageryProviderViewModels =
options.imageryProviderViewModels ?? Frozen.EMPTY_ARRAY;
const terrainProviderViewModels =
options.terrainProviderViewModels ?? Frozen.EMPTY_ARRAY;
//>>includeStart('debug', pragmas.debug);
if (!defined(globe)) {
throw new DeveloperError("globe is required");
}
//>>includeEnd('debug');
this._globe = globe;
/**
* Gets or sets an array of ProviderViewModel instances available for imagery selection.
* This property is observable.
* @type {ProviderViewModel[]}
*/
this.imageryProviderViewModels = imageryProviderViewModels.slice(0);
/**
* Gets or sets an array of ProviderViewModel instances available for terrain selection.
* This property is observable.
* @type {ProviderViewModel[]}
*/
this.terrainProviderViewModels = terrainProviderViewModels.slice(0);
/**
* Gets or sets whether the imagery selection drop-down is currently visible.
* @type {boolean}
* @default false
*/
this.dropDownVisible = false;
knockout.track(this, [
"imageryProviderViewModels",
"terrainProviderViewModels",
"dropDownVisible",
]);
const imageryObservable = knockout.getObservable(
this,
"imageryProviderViewModels",
);
const imageryProviders = knockout.pureComputed(function () {
const providers = imageryObservable();
const categories = {};
let i;
for (i = 0; i < providers.length; i++) {
const provider = providers[i];
const category = provider.category;
if (defined(categories[category])) {
categories[category].push(provider);
} else {
categories[category] = [provider];
}
}
const allCategoryNames = Object.keys(categories);
const result = [];
for (i = 0; i < allCategoryNames.length; i++) {
const name = allCategoryNames[i];
result.push({
name: name,
providers: categories[name],
});
}
return result;
});
this._imageryProviders = imageryProviders;
const terrainObservable = knockout.getObservable(
this,
"terrainProviderViewModels",
);
const terrainProviders = knockout.pureComputed(function () {
const providers = terrainObservable();
const categories = {};
let i;
for (i = 0; i < providers.length; i++) {
const provider = providers[i];
const category = provider.category;
if (defined(categories[category])) {
categories[category].push(provider);
} else {
categories[category] = [provider];
}
}
const allCategoryNames = Object.keys(categories);
const result = [];
for (i = 0; i < allCategoryNames.length; i++) {
const name = allCategoryNames[i];
result.push({
name: name,
providers: categories[name],
});
}
return result;
});
this._terrainProviders = terrainProviders;
/**
* Gets the button tooltip. This property is observable.
* @type {string}
*/
this.buttonTooltip = undefined;
knockout.defineProperty(this, "buttonTooltip", function () {
const selectedImagery = this.selectedImagery;
const selectedTerrain = this.selectedTerrain;
const imageryTip = defined(selectedImagery)
? selectedImagery.name
: undefined;
const terrainTip = defined(selectedTerrain)
? selectedTerrain.name
: undefined;
if (defined(imageryTip) && defined(terrainTip)) {
return `${imageryTip}\n${terrainTip}`;
} else if (defined(imageryTip)) {
return imageryTip;
}
return terrainTip;
});
/**
* Gets the button background image. This property is observable.
* @type {string}
*/
this.buttonImageUrl = undefined;
knockout.defineProperty(this, "buttonImageUrl", function () {
const selectedImagery = this.selectedImagery;
if (defined(selectedImagery)) {
return selectedImagery.iconUrl;
}
});
/**
* Gets or sets the currently selected imagery. This property is observable.
* @type {ProviderViewModel}
* @default undefined
*/
this.selectedImagery = undefined;
const selectedImageryViewModel = knockout.observable();
this._currentImageryLayers = [];
knockout.defineProperty(this, "selectedImagery", {
get: function () {
return selectedImageryViewModel();
},
set: function (value) {
if (selectedImageryViewModel() === value) {
this.dropDownVisible = false;
return;
}
let i;
const currentImageryLayers = this._currentImageryLayers;
const currentImageryLayersLength = currentImageryLayers.length;
const imageryLayers = this._globe.imageryLayers;
let hadExistingBaseLayer = false;
for (i = 0; i < currentImageryLayersLength; i++) {
const layersLength = imageryLayers.length;
for (let x = 0; x < layersLength; x++) {
const layer = imageryLayers.get(x);
if (layer === currentImageryLayers[i]) {
imageryLayers.remove(layer);
hadExistingBaseLayer = true;
break;
}
}
}
if (defined(value)) {
const newProviders = value.creationCommand();
if (Array.isArray(newProviders)) {
const newProvidersLength = newProviders.length;
this._currentImageryLayers = [];
for (i = newProvidersLength - 1; i >= 0; i--) {
const layer = ImageryLayer.fromProviderAsync(newProviders[i]);
imageryLayers.add(layer, 0);
this._currentImageryLayers.push(layer);
}
} else {
this._currentImageryLayers = [];
const layer = ImageryLayer.fromProviderAsync(newProviders);
layer.name = value.name;
if (hadExistingBaseLayer) {
imageryLayers.add(layer, 0);
} else {
const baseLayer = imageryLayers.get(0);
if (defined(baseLayer)) {
imageryLayers.remove(baseLayer);
}
imageryLayers.add(layer, 0);
}
this._currentImageryLayers.push(layer);
}
}
selectedImageryViewModel(value);
this.dropDownVisible = false;
},
});
/**
* Gets or sets the currently selected terrain. This property is observable.
* @type {ProviderViewModel}
* @default undefined
*/
this.selectedTerrain = undefined;
const selectedTerrainViewModel = knockout.observable();
knockout.defineProperty(this, "selectedTerrain", {
get: function () {
return selectedTerrainViewModel();
},
set: function (value) {
if (selectedTerrainViewModel() === value) {
this.dropDownVisible = false;
return;
}
let newProvider;
if (defined(value)) {
newProvider = value.creationCommand();
}
// If this is not a promise, we must set this synchronously to avoid overriding depthTestAgainstTerrain
// See https://github.com/CesiumGS/cesium/issues/6991
if (defined(newProvider) && !defined(newProvider.then)) {
this._globe.depthTestAgainstTerrain = !(
newProvider instanceof EllipsoidTerrainProvider
);
this._globe.terrainProvider = newProvider;
} else if (defined(newProvider)) {
let cancelUpdate = false;
const removeCancelListener =
this._globe.terrainProviderChanged.addEventListener(() => {
cancelUpdate = true;
removeCancelListener();
});
const terrain = new Terrain(newProvider);
const removeEventListener = terrain.readyEvent.addEventListener(
(terrainProvider) => {
if (cancelUpdate) {
// Early return in case something has changed outside of the picker.
return;
}
this._globe.depthTestAgainstTerrain = !(
terrainProvider instanceof EllipsoidTerrainProvider
);
this._globe.terrainProvider = terrainProvider;
removeEventListener();
},
);
}
selectedTerrainViewModel(value);
this.dropDownVisible = false;
},
});
const that = this;
this._toggleDropDown = createCommand(function () {
that.dropDownVisible = !that.dropDownVisible;
});
this.selectedImagery =
options.selectedImageryProviderViewModel ?? imageryProviderViewModels[0];
this.selectedTerrain = options.selectedTerrainProviderViewModel;
}
Object.defineProperties(BaseLayerPickerViewModel.prototype, {
/**
* Gets the command to toggle the visibility of the drop down.
* @memberof BaseLayerPickerViewModel.prototype
*
* @type {Command}
*/
toggleDropDown: {
get: function () {
return this._toggleDropDown;
},
},
/**
* Gets the globe.
* @memberof BaseLayerPickerViewModel.prototype
*
* @type {Globe}
*/
globe: {
get: function () {
return this._globe;
},
},
});
export default BaseLayerPickerViewModel;