123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- import defaultValue from "../../Core/defaultValue.js";
- import defined from "../../Core/defined.js";
- import DeveloperError from "../../Core/DeveloperError.js";
- import Event from "../../Core/Event.js";
- import wrapFunction from "../../Core/wrapFunction.js";
- import CzmlDataSource from "../../DataSources/CzmlDataSource.js";
- import GeoJsonDataSource from "../../DataSources/GeoJsonDataSource.js";
- import KmlDataSource from "../../DataSources/KmlDataSource.js";
- import GpxDataSource from "../../DataSources/GpxDataSource.js";
- import getElement from "../getElement.js";
- /**
- * A mixin which adds default drag and drop support for CZML files to the Viewer widget.
- * Rather than being called directly, this function is normally passed as
- * a parameter to {@link Viewer#extend}, as shown in the example below.
- * @function viewerDragDropMixin
- * @param {Viewer} viewer The viewer instance.
- * @param {Object} [options] Object with the following properties:
- * @param {Element|String} [options.dropTarget=viewer.container] The DOM element which will serve as the drop target.
- * @param {Boolean} [options.clearOnDrop=true] When true, dropping files will clear all existing data sources first, when false, new data sources will be loaded after the existing ones.
- * @param {Boolean} [options.flyToOnDrop=true] When true, dropping files will fly to the data source once it is loaded.
- * @param {Boolean} [options.clampToGround=true] When true, datasources are clamped to the ground.
- * @param {Proxy} [options.proxy] The proxy to be used for KML network links.
- *
- * @exception {DeveloperError} Element with id <options.dropTarget> does not exist in the document.
- * @exception {DeveloperError} dropTarget is already defined by another mixin.
- * @exception {DeveloperError} dropEnabled is already defined by another mixin.
- * @exception {DeveloperError} dropError is already defined by another mixin.
- * @exception {DeveloperError} clearOnDrop is already defined by another mixin.
- *
- * @example
- * // Add basic drag and drop support and pop up an alert window on error.
- * const viewer = new Cesium.Viewer('cesiumContainer');
- * viewer.extend(Cesium.viewerDragDropMixin);
- * viewer.dropError.addEventListener(function(viewerArg, source, error) {
- * window.alert('Error processing ' + source + ':' + error);
- * });
- */
- function viewerDragDropMixin(viewer, options) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(viewer)) {
- throw new DeveloperError("viewer is required.");
- }
- if (viewer.hasOwnProperty("dropTarget")) {
- throw new DeveloperError("dropTarget is already defined by another mixin.");
- }
- if (viewer.hasOwnProperty("dropEnabled")) {
- throw new DeveloperError(
- "dropEnabled is already defined by another mixin."
- );
- }
- if (viewer.hasOwnProperty("dropError")) {
- throw new DeveloperError("dropError is already defined by another mixin.");
- }
- if (viewer.hasOwnProperty("clearOnDrop")) {
- throw new DeveloperError(
- "clearOnDrop is already defined by another mixin."
- );
- }
- if (viewer.hasOwnProperty("flyToOnDrop")) {
- throw new DeveloperError(
- "flyToOnDrop is already defined by another mixin."
- );
- }
- //>>includeEnd('debug');
- options = defaultValue(options, defaultValue.EMPTY_OBJECT);
- //Local variables to be closed over by defineProperties.
- let dropEnabled = true;
- let flyToOnDrop = defaultValue(options.flyToOnDrop, true);
- const dropError = new Event();
- let clearOnDrop = defaultValue(options.clearOnDrop, true);
- let dropTarget = defaultValue(options.dropTarget, viewer.container);
- let clampToGround = defaultValue(options.clampToGround, true);
- let proxy = options.proxy;
- dropTarget = getElement(dropTarget);
- Object.defineProperties(viewer, {
- /**
- * Gets or sets the element to serve as the drop target.
- * @memberof viewerDragDropMixin.prototype
- * @type {Element}
- */
- dropTarget: {
- //TODO See https://github.com/CesiumGS/cesium/issues/832
- get: function () {
- return dropTarget;
- },
- set: function (value) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(value)) {
- throw new DeveloperError("value is required.");
- }
- //>>includeEnd('debug');
- unsubscribe(dropTarget, handleDrop);
- dropTarget = value;
- subscribe(dropTarget, handleDrop);
- },
- },
- /**
- * Gets or sets a value indicating if drag and drop support is enabled.
- * @memberof viewerDragDropMixin.prototype
- * @type {Element}
- */
- dropEnabled: {
- get: function () {
- return dropEnabled;
- },
- set: function (value) {
- if (value !== dropEnabled) {
- if (value) {
- subscribe(dropTarget, handleDrop);
- } else {
- unsubscribe(dropTarget, handleDrop);
- }
- dropEnabled = value;
- }
- },
- },
- /**
- * Gets the event that will be raised when an error is encountered during drop processing.
- * @memberof viewerDragDropMixin.prototype
- * @type {Event}
- */
- dropError: {
- get: function () {
- return dropError;
- },
- },
- /**
- * Gets or sets a value indicating if existing data sources should be cleared before adding the newly dropped sources.
- * @memberof viewerDragDropMixin.prototype
- * @type {Boolean}
- */
- clearOnDrop: {
- get: function () {
- return clearOnDrop;
- },
- set: function (value) {
- clearOnDrop = value;
- },
- },
- /**
- * Gets or sets a value indicating if the camera should fly to the data source after it is loaded.
- * @memberof viewerDragDropMixin.prototype
- * @type {Boolean}
- */
- flyToOnDrop: {
- get: function () {
- return flyToOnDrop;
- },
- set: function (value) {
- flyToOnDrop = value;
- },
- },
- /**
- * Gets or sets the proxy to be used for KML.
- * @memberof viewerDragDropMixin.prototype
- * @type {Proxy}
- */
- proxy: {
- get: function () {
- return proxy;
- },
- set: function (value) {
- proxy = value;
- },
- },
- /**
- * Gets or sets a value indicating if the datasources should be clamped to the ground
- * @memberof viewerDragDropMixin.prototype
- * @type {Boolean}
- */
- clampToGround: {
- get: function () {
- return clampToGround;
- },
- set: function (value) {
- clampToGround = value;
- },
- },
- });
- function handleDrop(event) {
- stop(event);
- if (clearOnDrop) {
- viewer.entities.removeAll();
- viewer.dataSources.removeAll();
- }
- const files = event.dataTransfer.files;
- const length = files.length;
- for (let i = 0; i < length; i++) {
- const file = files[i];
- const reader = new FileReader();
- reader.onload = createOnLoadCallback(viewer, file, proxy, clampToGround);
- reader.onerror = createDropErrorCallback(viewer, file);
- reader.readAsText(file);
- }
- }
- //Enable drop by default;
- subscribe(dropTarget, handleDrop);
- //Wrap the destroy function to make sure all events are unsubscribed from
- viewer.destroy = wrapFunction(viewer, viewer.destroy, function () {
- viewer.dropEnabled = false;
- });
- //Specs need access to handleDrop
- viewer._handleDrop = handleDrop;
- }
- function stop(event) {
- event.stopPropagation();
- event.preventDefault();
- }
- function unsubscribe(dropTarget, handleDrop) {
- const currentTarget = dropTarget;
- if (defined(currentTarget)) {
- currentTarget.removeEventListener("drop", handleDrop, false);
- currentTarget.removeEventListener("dragenter", stop, false);
- currentTarget.removeEventListener("dragover", stop, false);
- currentTarget.removeEventListener("dragexit", stop, false);
- }
- }
- function subscribe(dropTarget, handleDrop) {
- dropTarget.addEventListener("drop", handleDrop, false);
- dropTarget.addEventListener("dragenter", stop, false);
- dropTarget.addEventListener("dragover", stop, false);
- dropTarget.addEventListener("dragexit", stop, false);
- }
- function createOnLoadCallback(viewer, file, proxy, clampToGround) {
- const scene = viewer.scene;
- return function (evt) {
- const fileName = file.name;
- try {
- let loadPromise;
- if (/\.czml$/i.test(fileName)) {
- loadPromise = CzmlDataSource.load(JSON.parse(evt.target.result), {
- sourceUri: fileName,
- });
- } else if (
- /\.geojson$/i.test(fileName) ||
- /\.json$/i.test(fileName) ||
- /\.topojson$/i.test(fileName)
- ) {
- loadPromise = GeoJsonDataSource.load(JSON.parse(evt.target.result), {
- sourceUri: fileName,
- clampToGround: clampToGround,
- });
- } else if (/\.(kml|kmz)$/i.test(fileName)) {
- loadPromise = KmlDataSource.load(file, {
- sourceUri: fileName,
- proxy: proxy,
- camera: scene.camera,
- canvas: scene.canvas,
- clampToGround: clampToGround,
- screenOverlayContainer: viewer.container,
- });
- } else if (/\.gpx$/i.test(fileName)) {
- loadPromise = GpxDataSource.load(file, {
- sourceUri: fileName,
- proxy: proxy,
- });
- } else {
- viewer.dropError.raiseEvent(
- viewer,
- fileName,
- `Unrecognized file: ${fileName}`
- );
- return;
- }
- if (defined(loadPromise)) {
- viewer.dataSources
- .add(loadPromise)
- .then(function (dataSource) {
- if (viewer.flyToOnDrop) {
- viewer.flyTo(dataSource);
- }
- })
- .catch(function (error) {
- viewer.dropError.raiseEvent(viewer, fileName, error);
- });
- }
- } catch (error) {
- viewer.dropError.raiseEvent(viewer, fileName, error);
- }
- };
- }
- function createDropErrorCallback(viewer, file) {
- return function (evt) {
- viewer.dropError.raiseEvent(viewer, file.name, evt.target.error);
- };
- }
- export default viewerDragDropMixin;
|