/**
 * Mars3D平台插件,结合echarts可视化功能插件  mars3d-echarts
 *
 * 版本信息：v3.4.7
 * 编译日期：2022-09-15 16:25:35
 * 版权所有：Copyright by 火星科技  http://mars3d.cn
 * 使用单位：安徽XX有限公司 ，2021-08-18
 */
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, (window.echarts || require('echarts')), (window.mars3d || require('mars3d'))) :
  typeof define === 'function' && define.amd ? define(['exports', 'echarts', 'mars3d'], factory) :
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["mars3d-echarts"] = {}, global.echarts, global.mars3d));
})(this, (function (exports, echarts, mars3d) { 'use strict';

  function _interopNamespace(e) {
    if (e && e.__esModule) return e;
    var n = Object.create(null);
    if (e) {
      Object.keys(e).forEach(function (k) {
        if (k !== 'default') {
          var d = Object.getOwnPropertyDescriptor(e, k);
          Object.defineProperty(n, k, d.get ? d : {
            enumerable: true,
            get: function () { return e[k]; }
          });
        }
      });
    }
    n["default"] = e;
    return n;
  }

  var echarts__namespace = /*#__PURE__*/_interopNamespace(echarts);
  var mars3d__namespace = /*#__PURE__*/_interopNamespace(mars3d);

  const Cesium$1 = mars3d__namespace.Cesium;


  // 地图坐标系统类。参考了开源：https://github.com/sharpzao/EchartsCesium
  class CompositeCoordinateSystem {
    //= ========= 构造方法 ==========
    constructor(scene, api) {
      this._mars3d_scene = scene;
      this.dimensions = ["lng", "lat"];
      this._mapOffset = [0, 0];

      this._api = api;
    }

    //= ========= 方法 ==========
    setMapOffset(mapOffset) {
      this._mapOffset = mapOffset;
    }

    getBMap() {
      return this._mars3d_scene
    }

    dataToPoint(data) {
      const scene = this._mars3d_scene;
      const defVal = [NaN, NaN]; // echarts内部就是用NaN来做判断的

      let heightTerrain = scene.echartsFixedHeight;
      if (scene.echartsAutoHeight) {
        heightTerrain = scene.globe.getHeight(Cesium$1.Cartographic.fromDegrees(data[0], data[1]));
      }

      const position = Cesium$1.Cartesian3.fromDegrees(data[0], data[1], heightTerrain);
      if (!position) {
        return defVal
      }

      const px = Cesium$1.SceneTransforms.wgs84ToWindowCoordinates(scene, position);
      // var px = scene.cartesianToCanvasCoordinates(position);
      if (!px) {
        return defVal
      }

      // 判断是否在球的背面
      if (scene.echartsDepthTest && scene.mode === Cesium$1.SceneMode.SCENE3D) {
        const occluder = new Cesium$1.EllipsoidalOccluder(scene.globe.ellipsoid, scene.camera.positionWC);
        const visible = occluder.isPointVisible(position);
        // visible为true说明点在球的正面，否则点在球的背面。
        // 需要注意的是不能用这种方法判断点的可见性，如果球放的比较大，点跑到屏幕外面，它返回的依然为true
        if (!visible) {
          return defVal
        }
      }
      // 判断是否在球的背面

      return [px.x - this._mapOffset[0], px.y - this._mapOffset[1]]
    }

    getViewRect() {
      const api = this._api;
      return new echarts__namespace.graphic.BoundingRect(0, 0, api.getWidth(), api.getHeight())
    }

    getRoamTransform() {
      return echarts__namespace.matrix.create()
    }
  }

  // 用于确定创建列表数据时要使用的维度
  CompositeCoordinateSystem.dimensions = ["lng", "lat"];
  CompositeCoordinateSystem.create = function (ecModel, api) {
    let coordSys;
    const scene = ecModel.scheduler.ecInstance._mars3d_scene;

    ecModel.eachComponent("mars3dMap", function (mars3dMapModel) {
      const painter = api.getZr().painter;
      if (!painter) {
        return
      }

      if (!coordSys) {
        coordSys = new CompositeCoordinateSystem(scene, api);
      }
      mars3dMapModel.coordinateSystem = coordSys;

      coordSys.setMapOffset(mars3dMapModel.__mapOffset || [0, 0]);
    });

    ecModel.eachSeries(function (seriesModel) {
      if (seriesModel.get("coordinateSystem") === "mars3dMap") {
        if (!coordSys) {
          coordSys = new CompositeCoordinateSystem(scene, api);
        }
        seriesModel.coordinateSystem = coordSys;
      }
    });
  };

  /// //////扩展echarts///////////
  if (echarts__namespace?.init) {
    echarts__namespace.registerCoordinateSystem("mars3dMap", CompositeCoordinateSystem);
    echarts__namespace.registerAction(
      {
        type: "mars3dMapRoam",
        event: "mars3dMapRoam",
        update: "updateLayout"
      },
      function (payload, ecModel) {}
    );

    echarts__namespace.extendComponentModel({
      type: "mars3dMap",
      getBMap: function () {
        return this._mars3d_scene
      },
      defaultOption: { roam: false }
    });

    echarts__namespace.extendComponentView({
      type: "mars3dMap",
      init: function (ecModel, api) {
        this.api = api;
        this.scene = ecModel.scheduler.ecInstance._mars3d_scene;
        this.scene.postRender.addEventListener(this.moveHandler, this);
      },
      moveHandler: function (type, target) {
        this.api.dispatchAction({ type: "mars3dMapRoam" });
      },
      render: function (mars3dMapModel, ecModel, api) {},
      dispose: function (target) {
        this.scene.postRender.removeEventListener(this.moveHandler, this);
      }
    });
  } else {
    throw new Error("请引入 echarts 库 ")
  }

  const Cesium = mars3d__namespace.Cesium;
  const BaseLayer = mars3d__namespace.layer.BaseLayer;
  let _div_zIndex = 999;

  /**
   * Echarts图层，
   * 【需要引入 echarts 库 和 mars3d-echarts 插件库】
   *
   * @param {Object} [options] 参数对象，包括以下：
   * @param {Object} [options.Echarts本身] 支持Echarts本身所有Options参数，具体查阅 [Echarts配置项手册]{@link https://echarts.apache.org/zh/option.html}
   *
   * @param {Boolean} [options.depthTest=true] 是否进行计算深度判断，在地球背面或被遮挡时不显示（大数据时，需要关闭）
   * @param {Number} [options.fixedHeight=0] 点的固定的海拔高度
   * @param {Boolean} [options.clampToGround=false] 点是否贴地
   * @param {Boolean} [options.pointerEvents=false] 图层是否可以进行鼠标交互，为false时可以穿透操作及缩放地图
   *
   *
   * @param {String|Number} [options.id = createGuid()] 图层id标识
   * @param {String|Number} [options.pid = -1] 图层父级的id，一般图层管理中使用
   * @param {String} [options.name = ''] 图层名称
   * @param {Boolean} [options.show = true] 图层是否显示
   * @param {BaseClass|Boolean} [options.eventParent]  指定的事件冒泡对象，默认为map对象，false时不冒泡
   * @param {Object} [options.center] 图层自定义定位视角 {@link Map#setCameraView}
   * @param {Number} options.center.lng 经度值, 180 - 180
   * @param {Number} options.center.lat 纬度值, -90 - 90
   * @param {Number} [options.center.alt] 高度值
   * @param {Number} [options.center.heading] 方向角度值，绕垂直于地心的轴旋转角度, 0至360
   * @param {Number} [options.center.pitch] 俯仰角度值，绕纬度线旋转角度, -90至90
   * @param {Number} [options.center.roll] 翻滚角度值，绕经度线旋转角度, -90至90
   * @param {Boolean} [options.flyTo] 加载完成数据后是否自动飞行定位到数据所在的区域。
   * @export
   * @class EchartsLayer
   * @extends {BaseLayer}
   */
  class EchartsLayer extends BaseLayer {
    constructor(options = {}) {
      super(options);

      this._pointerEvents = this.options.pointerEvents;
    }

    /**
     * echarts对象，是echarts.init方法返回的 echartsInstance 实例
     * @type {HTMLCanvasElement}
     * @readonly
     * @see https://echarts.apache.org/zh/api.html#echartsInstance
     */
    get layer() {
      return this._echartsInstance
    }

    /**
     * 是否可以鼠标交互，为false时可以穿透操作及缩放地图，但无法进行鼠标交互及触发相关事件。true时无法缩放地球，但可以使用echarts相关的事件或toolitp等。
     * @type {Boolean}
     */
    get pointerEvents() {
      return this._pointerEvents
    }

    set pointerEvents(value) {
      this._pointerEvents = value;
      if (this._echartsContainer) {
        if (value) {
          this._echartsContainer.style.pointerEvents = "all";
        } else {
          /* 加上这个css后鼠标可以穿透，但是无法触发单击等鼠标事件 */
          this._echartsContainer.style.pointerEvents = "none";
        }
      }
    }

    _setOptionsHook(options, newOptions) {
      this.setEchartsOption(options);
    }

    _showHook(show) {
      if (show) {
        this._echartsContainer.style.visibility = "visible";
      } else {
        this._echartsContainer.style.visibility = "hidden";
      }
    }

    /**
     * 对象添加到地图前创建一些对象的钩子方法，
     * 只会调用一次
     * @return {void}  无
     * @private
     */
    _mountedHook() {
      this._map.scene.echartsDepthTest = this.options.depthTest ?? true; // 是否进行计算深度（大数据时，需要关闭）
      this._map.scene.echartsAutoHeight = this.options.clampToGround ?? false;
      this._map.scene.echartsFixedHeight = this.options.fixedHeight ?? 0;
    }

    /**
     * 对象添加到地图上的创建钩子方法，
     * 每次add时都会调用
     * @return {void}  无
     * @private
     */
    _addedHook() {
      this._echartsContainer = this._createChartOverlay();
      this._echartsInstance = echarts__namespace.init(this._echartsContainer);
      this._echartsInstance._mars3d_scene = this._map.scene;

      this.setEchartsOption(this.options);
    }

    /**
     * 对象从地图上移除的创建钩子方法，
     * 每次remove时都会调用
     * @return {void}  无
     * @private
     */
    _removedHook() {
      if (this._echartsInstance) {
        this._echartsInstance.clear();
        this._echartsInstance.dispose();
        delete this._echartsInstance;
      }
      if (this._echartsContainer) {
        this._map.container.removeChild(this._echartsContainer);
        delete this._echartsContainer;
      }
    }

    _createChartOverlay() {
      const scene = this._map.scene;
      scene.canvas.setAttribute("tabIndex", 0);

      const chartContainer = mars3d__namespace.DomUtil.create("div", "mars3d-echarts", this._map.container);
      chartContainer.style.position = "absolute";
      chartContainer.style.top = "0px";
      chartContainer.style.left = "0px";
      chartContainer.style.width = scene.canvas.clientWidth + "px";
      chartContainer.style.height = scene.canvas.clientHeight + "px";
      chartContainer.style.pointerEvents = this._pointerEvents ? "all" : "none"; // auto时可以交互，但是没法放大地球， none 没法交互
      chartContainer.style.zIndex = this.options.zIndex || _div_zIndex++;

      return chartContainer
    }

    /**
     * 改变图层canvas容器尺寸，在容器大小发生改变时需要手动调用。
     * @return {void}  无
     * @see https://echarts.apache.org/zh/api.html#echartsInstance.resize
     */
    resize() {
      if (!this._echartsInstance) {
        return
      }
      const scene = this._map.scene;

      this._echartsContainer.style.width = scene.canvas.clientWidth + "px";
      this._echartsContainer.style.height = scene.canvas.clientHeight + "px";

      this._echartsInstance.resize();
    }

    /**
     * 设置图表实例的配置项以及数据，
     * 万能接口，所有参数和数据的修改都可以通过 setOption 完成，
     * ECharts 会合并新的参数和数据，然后刷新图表。
     * 如果开启动画的话，ECharts 找到两组数据之间的差异然后通过合适的动画去表现数据的变化。
     * @param {Object} option 图表的配置项和数据，具体见 [Echarts配置项手册]{@link https://echarts.apache.org/zh/option.html}。
     * @param {Boolean} [notMerge=false] 是否不跟之前设置的 option 进行合并。默认为 false。即表示合并。合并的规则，详见 组件合并模式。如果为 true，表示所有组件都会被删除，然后根据新 option 创建所有新组件。
     * @param {Boolean} [lazyUpdate=false] 在设置完 option 后是否不立即更新图表，默认为 false，即同步立即更新。如果为 true，则会在下一个 animation frame 中，才更新图表。
     * @return {void}  无
     * @see https://echarts.apache.org/zh/api.html#echartsInstance.setOption
     */
    setEchartsOption(option, notMerge, lazyUpdate) {
      if (this._echartsInstance) {
        option.mars3dMap = option.mars3dMap || {}; // 需要注册
        this._echartsInstance.setOption(option, notMerge, lazyUpdate);
      }
    }

    /**
     * 获取图层内所有数据的 矩形边界值
     * @param {Boolean} [options] 控制参数
     * @param {Boolean} [options.isFormat=false] 是否格式化，格式化时示例： { xmin: 73.16895, xmax: 134.86816, ymin: 12.2023, ymax: 54.11485 }
     * @return {Cesium.Rectangle|Object} isFormat：true时，返回格式化对象，isFormat：false时返回Cesium.Rectangle对象
     */
    getRectangle(options) {
      let xmin, xmax, ymin, ymax;

      function refPoint(coors) {
        if (!Array.isArray(coors)) {
          return
        }

        const lng = coors[0] || 0;
        const lat = coors[1] || 0;

        if (lng !== 0 && lat !== 0) {
          if (xmin === undefined) {
            xmin = lng;
            xmax = lng;
            ymin = lat;
            ymax = lat;
          } else {
            xmin = Math.min(xmin, lng);
            xmax = Math.max(xmax, lng);
            ymin = Math.min(ymin, lat);
            ymax = Math.max(ymax, lat);
          }
        }
      }

      const series = this.options.series;
      if (series) {
        series.forEach((serie) => {
          if (serie.data) {
            serie.data.forEach((item) => {
              if (item.value) {
                refPoint(item.value);
              } else if (item.coords) {
                item.coords.forEach((coord) => {
                  refPoint(coord);
                });
              }
            });
          }
        });
      }

      if (xmin === 0 && ymin === 0 && xmax === 0 && ymax === 0) {
        return null
      }

      if (options?.isFormat) {
        return { xmin: xmin, xmax: xmax, ymin: ymin, ymax: ymax }
      } else {
        return Cesium.Rectangle.fromDegrees(xmin, ymin, xmax, ymax)
      }
    }

    /// /////////////////事件相关//////////////////////

    /**
     * 绑定事件处理函数,
     *
     * @param {String} eventName  事件名称，全小写，例如'click'，'mousemove', 'legendselected' ,可以参考[echarts官网说明]{@link https://echarts.apache.org/zh/api.html#echartsInstance.on}
     * @param {Function} callback 绑定的监听器回调方法
     * @param {Object} [context]  侦听器的上下文(this关键字将指向的对象)。
     * @return {EchartsLayer} 当前对象本身,可以链式调用
     */
    on(eventName, callback, context) {
      this._echartsInstance.on(eventName, callback, context || this);
      return this
    }

    /**
     * 带条件的绑定事件处理函数
     * @param {String} eventName  事件名称，全小写，例如'click'，'mousemove', 'legendselected'
     * @param {String|Object} query  可选的过滤条件，能够只在指定的组件或者元素上进行响应。可以参考[echarts官网说明]{@link https://echarts.apache.org/zh/api.html#echartsInstance.on}
     * @param {Function} callback 绑定的监听器回调方法
     * @param {Object} [context]  侦听器的上下文(this关键字将指向的对象)
     * @return {EchartsLayer} 当前对象本身,可以链式调用
     */
    onByQuery(eventName, query, callback, context) {
      this._echartsInstance.on(eventName, query, callback, context || this);
      return this
    }

    /**
     * 解除绑定指定类型事件监听器
     *
     * @param {String} eventName  事件名称，全小写，例如'click'，'mousemove', 'legendselected'
     * @param {Function} [callback] 绑定的监听器回调方法,未传值时解绑所有指定类型对应事件
     * @param {Object} [context]  侦听器的上下文(this关键字将指向的对象)。
     * @return {EchartsLayer} 当前对象本身,可以链式调用
     */
    off(eventName, callback, context) {
      this._echartsInstance.off(eventName, callback, context || this);
      return this
    }
  }

  // 注册下
  mars3d__namespace.LayerUtil.register("echarts", EchartsLayer);

  mars3d__namespace.layer.EchartsLayer = EchartsLayer;

  exports.EchartsLayer = EchartsLayer;
  Object.keys(echarts).forEach(function (k) {
    if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
      enumerable: true,
      get: function () { return echarts[k]; }
    });
  });

  Object.defineProperty(exports, '__esModule', { value: true });

}));
