Source: Map/CesiumDragPoints.js

/*global require*/
"use strict";

var defined = require('terriajs-cesium/Source/Core/defined');
var ScreenSpaceEventHandler = require('terriajs-cesium/Source/Core/ScreenSpaceEventHandler');
var ScreenSpaceEventType = require('terriajs-cesium/Source/Core/ScreenSpaceEventType');
var CustomDataSource = require('terriajs-cesium/Source/DataSources/CustomDataSource');

/**
 * Callback for when a point is moved.
 * @callback PointMovedCallback
 * @param {CustomDataSource} customDataSource Contains all point entities that user has selected so far
 */

/**
 * For letting user drag existing points in Cesium ViewerModes only.
 *
 * @alias CesiumDragPoints
 * @constructor
 *
 * @param {Terria} terria The Terria instance.
 * @param {PointMovedCallback} pointMovedCallback A function that is called when a point is moved.
 */
var CesiumDragPoints = function(terria, pointMovedCallback) {
    this._terria = terria;
    this._setUp = false;
    this.type = "Cesium";

    /**
     * Callback that occurs when point is moved. Function takes a CustomDataSource which is a list of PointEntities.
     * @type {PointMovedCallback}
     * @default undefined
     */
    this._pointMovedCallback = pointMovedCallback;

    /**
     * List of entities that can be dragged, which is populated with user-created points only.
     * @type {CustomDataSource}
     */
    this._draggableObjects = new CustomDataSource();

    /**
     * Whether user is currently dragging point.
     * @type {Bool}
     */
    this._dragInProgress = false;

    /**
     * For determining whether a drag has just occurred, to avoid deleting a point at the end of the drag.
     * @type {Number}
     */
    this.dragCount = 0;
};

/**
 * Set up the drag point helper so that attempting to drag a point will move the point.
 */
CesiumDragPoints.prototype.setUp = function() {
    if (this._setUp) {
        return;
    }
    if (!defined(this._terria.cesium) || !defined(this._terria.cesium.scene) || !defined(this._terria.cesium.viewer)) {
        // Test context or something has gone *so* badly wrong
        return;
    }
    this._scene = this._terria.cesium.scene;
    this._viewer = this._terria.cesium.viewer;
    this._mouseHandler = new ScreenSpaceEventHandler(this._scene.canvas, false);

    var that = this;

    // Mousedown event. This is called for all mousedown events, not just mousedown on entity events like the Leaflet
    // equivalent.
    this._mouseHandler.setInputAction(function(click) {
        if (!defined(that._draggableObjects.entities) ||
                that._draggableObjects.entities.length === 0) {
            return;
        }
        var pickedObject = that._scene.pick(click.position);
        that._originalPosition = click.position;
        if (defined(pickedObject)) {
            var pickedEntity = pickedObject.id;
            var draggedEntity = that._draggableObjects.entities.values.filter(function(dragObjEntity) {
                return dragObjEntity.id === pickedEntity.id;
            })[0];
            if (draggedEntity) {
                that._dragInProgress = true;
                that._entityDragged = draggedEntity;
                that._setCameraMotion(false);
            }
        }
    }, ScreenSpaceEventType.LEFT_DOWN);

    // Mouse move event.
    this._mouseHandler.setInputAction(function(move) {
        if (!that._dragInProgress) {
            return;
        }
        that.dragCount = that.dragCount + 1;
        var cartesian = that._viewer.camera.pickEllipsoid(move.endPosition, that._scene.globe.ellipsoid);
        that._entityDragged.position = cartesian;
        for (var i=0; i<that._draggableObjects.entities.values.length; i++) {
            if (that._draggableObjects.entities.values[i].id === that._entityDragged.id) {
                that._draggableObjects.entities.values[i].position = cartesian;
            }
        }
    }, ScreenSpaceEventType.MOUSE_MOVE);

    // Mouse release event.
    this._mouseHandler.setInputAction(function(mouseUp) {
        if (that._dragInProgress && mouseUp.position !== that._originalPosition) {
            that._pointMovedCallback(that._draggableObjects);
        }
        that._dragInProgress = false;
        that._setCameraMotion(true);
    }, ScreenSpaceEventType.LEFT_UP);

    this._setUp = true;
};

/**
 * Update the list of draggable objects with a new list of entities that are able to be dragged. We are only interested
 * in entities that the user has drawn.
 *
 * @param {CustomDataSource} entities Entities that user has drawn on the map.
 */
CesiumDragPoints.prototype.updateDraggableObjects = function(entities) {
    this._draggableObjects = entities;
};

/**
 * A clean up function to call when destroying the object.
 */
CesiumDragPoints.prototype.destroy = function() {
    if (defined(this._mouseHandler)) {
        this._mouseHandler.destroy();
        this._setUp = false;
    }
};

/**
 * Enable or disable camera motion, so that the user can drag a point rather than dragging the map.
 * @param {Bool} state True to enable and false to disable camera motion.
 * @private
 */
CesiumDragPoints.prototype._setCameraMotion = function(state) {
    this._scene.screenSpaceCameraController.enableRotate = state;
    this._scene.screenSpaceCameraController.enableZoom = state;
    this._scene.screenSpaceCameraController.enableLook = state;
    this._scene.screenSpaceCameraController.enableTilt = state;
    this._scene.screenSpaceCameraController.enableTranslate = state;
};

module.exports = CesiumDragPoints;