terriajs
Version:
Geospatial data visualization platform.
267 lines (234 loc) • 9.15 kB
JavaScript
'use strict';
/*global require*/
var Cartesian2 = require('terriajs-cesium/Source/Core/Cartesian2');
var defined = require('terriajs-cesium/Source/Core/defined');
var defineProperties = require('terriajs-cesium/Source/Core/defineProperties');
var DeveloperError = require('terriajs-cesium/Source/Core/DeveloperError');
var EasingFunction = require('terriajs-cesium/Source/Core/EasingFunction');
var knockout = require('terriajs-cesium/Source/ThirdParty/knockout');
var SceneTransforms = require('terriajs-cesium/Source/Scene/SceneTransforms');
const selectionIndicatorUrl = require('../../wwwroot/images/NM-LocationTarget.svg');
var screenSpacePos = new Cartesian2();
var offScreen = '-1000px';
var CesiumSelectionIndicator = function(cesium) {
//>>includeStart('debug', pragmas.debug);
if (!defined(cesium)) {
throw new DeveloperError('cesium is required.');
}
//>>includeEnd('debug')
this._cesium = cesium;
this._screenPositionX = offScreen;
this._screenPositionY = offScreen;
this._tweens = cesium.scene.tweens;
this._container = cesium.viewer.container;
/**
* Gets or sets the world position of the object for which to display the selection indicator.
* @type {Cartesian3}
*/
this.position = undefined;
/**
* Gets or sets the visibility of the selection indicator.
* @type {Boolean}
*/
this.showSelection = true;
this.transform = '';
this.opacity = 1.0;
knockout.track(this, ['position', '_screenPositionX', '_screenPositionY', '_scale', 'rotate', 'showSelection', 'transform', 'opacity']);
/**
* Gets the visibility of the position indicator. This can be false even if an
* object is selected, when the selected object has no position.
* @type {Boolean}
*/
this.isVisible = undefined;
knockout.defineProperty(this, 'isVisible', {
get : function() {
return this.showSelection && defined(this.position);
}
});
/**
* Gets or sets the function for converting the world position of the object to the screen space position.
*
* @member
* @type {SelectionIndicatorViewModel~ComputeScreenSpacePosition}
* @default SceneTransforms.wgs84ToWindowCoordinates
*
* @example
* selectionIndicatorViewModel.computeScreenSpacePosition = function(position, result) {
* return Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, position, result);
* };
*/
this.computeScreenSpacePosition = function(position, result) {
return SceneTransforms.wgs84ToWindowCoordinates(cesium.scene, position, result);
};
var el = document.createElement('div');
el.className = 'selection-indicator';
this._container.appendChild(el);
this._selectionIndicatorElement = el;
var img = document.createElement('img');
img.setAttribute('src', selectionIndicatorUrl);
img.setAttribute('alt', '');
img.setAttribute('width', 50);
img.setAttribute('height', 50);
el.appendChild(img);
var that = this;
function update() {
el.style.top = that._screenPositionY;
el.style.left = that._screenPositionX;
el.style.transform = that.transform;
el.style.opacity = that.opacity;
}
update();
this._subscriptions = [];
this._subscriptions.push(knockout.getObservable(this, '_screenPositionX').subscribe(update));
this._subscriptions.push(knockout.getObservable(this, '_screenPositionY').subscribe(update));
this._subscriptions.push(knockout.getObservable(this, 'transform').subscribe(update));
this._subscriptions.push(knockout.getObservable(this, 'opacity').subscribe(update));
};
CesiumSelectionIndicator.prototype.destroy = function() {
this._selectionIndicatorElement.parentNode.removeChild(this._selectionIndicatorElement);
this._subscriptions.forEach(function(subscription) {
subscription.dispose();
});
};
/**
* Updates the view of the selection indicator to match the position and content properties of the view model.
* This function should be called as part of the render loop.
*/
CesiumSelectionIndicator.prototype.update = function() {
if (this.showSelection && defined(this.position)) {
var screenPosition = this.computeScreenSpacePosition(this.position, screenSpacePos);
if (!defined(screenPosition)) {
this._screenPositionX = offScreen;
this._screenPositionY = offScreen;
} else {
var container = this._container;
var containerWidth = container.clientWidth;
var containerHeight = container.clientHeight;
var indicatorSize = this._selectionIndicatorElement.clientWidth;
var halfSize = indicatorSize * 0.5;
screenPosition.x = Math.min(Math.max(screenPosition.x, -indicatorSize), containerWidth + indicatorSize) - halfSize;
screenPosition.y = Math.min(Math.max(screenPosition.y, -indicatorSize), containerHeight + indicatorSize) - halfSize;
this._screenPositionX = Math.floor(screenPosition.x + 0.25) + 'px';
this._screenPositionY = Math.floor(screenPosition.y + 0.25) + 'px';
}
}
};
/**
* Animate the indicator to draw attention to the selection.
*/
CesiumSelectionIndicator.prototype.animateAppear = function() {
if (defined(this._selectionIndicatorTween)) {
if (this._selectionIndicatorIsAppearing) {
// Already appearing; don't restart the animation.
return;
}
this._selectionIndicatorTween.cancelTween();
this._selectionIndicatorTween = undefined;
}
this._selectionIndicatorIsAppearing = true;
var that = this;
this._selectionIndicatorTween = this._tweens.add({
startObject: {
scale: 2.0,
opacity: 0.0,
rotate: -180
},
stopObject: {
scale: 1.0,
opacity: 1.0,
rotate: 0
},
duration: 0.8,
easingFunction: EasingFunction.EXPONENTIAL_OUT,
update: function(value) {
that.opacity = value.opacity;
that.transform = 'scale(' + value.scale + ') rotate(' + value.rotate + 'deg)';
},
complete: function() {
that._selectionIndicatorTween = undefined;
},
cancel: function() {
that._selectionIndicatorTween = undefined;
}
});
};
/**
* Animate the indicator to release the selection.
*/
CesiumSelectionIndicator.prototype.animateDepart = function() {
if (defined(this._selectionIndicatorTween)) {
if (!this._selectionIndicatorIsAppearing) {
// Already disappearing, don't restart the animation.
return;
}
this._selectionIndicatorTween.cancelTween();
this._selectionIndicatorTween = undefined;
}
this._selectionIndicatorIsAppearing = false;
var that = this;
this._selectionIndicatorTween = this._tweens.add({
startObject: {
scale: 1.0,
opacity: 1.0
},
stopObject: {
scale: 1.5,
opacity: 0.0
},
duration: 0.8,
easingFunction: EasingFunction.EXPONENTIAL_OUT,
update: function(value) {
that.opacity = value.opacity;
that.transform = 'scale(' + value.scale + ') rotate(0deg)';
},
complete: function() {
that._selectionIndicatorTween = undefined;
},
cancel: function() {
that._selectionIndicatorTween = undefined;
}
});
};
defineProperties(CesiumSelectionIndicator.prototype, {
/**
* Gets the HTML element containing the selection indicator.
* @memberof CesiumSelectionIndicator.prototype
*
* @type {Element}
*/
container : {
get : function() {
return this._container;
}
},
/**
* Gets the HTML element that holds the selection indicator.
* @memberof CesiumSelectionIndicator.prototype
*
* @type {Element}
*/
selectionIndicatorElement : {
get : function() {
return this._selectionIndicatorElement;
}
},
/**
* Gets the scene being used.
* @memberof CesiumSelectionIndicator.prototype
*
* @type {Scene}
*/
scene : {
get : function() {
return this._scene;
}
}
});
/**
* A function that converts the world position of an object to a screen space position.
* @callback CesiumSelectionIndicator~ComputeScreenSpacePosition
* @param {Cartesian3} position The position in WGS84 (world) coordinates.
* @param {Cartesian2} result An object to return the input position transformed to window coordinates.
* @returns {Cartesian2} The modified result parameter.
*/
module.exports = CesiumSelectionIndicator;