/**
 * 空间参考系工具类
 */
 const SpatialRefUtils = {
  // 定义一些常量
  X_PI: 3.1415926535897932384626 * 3000.0 / 180.0,
  PI: 3.1415926535897932384626,
  A: 6378245.0,
  EE: 0.00669342162296594323,

  /** 外部调用方法 **/
  /**
   * 百度坐标系(BD-09)转大地2000坐标系(CGCS2000)
   * @param {Number[]} lonLat -百度坐标系经纬度
   *
   * @returns {Number[]}
   */
  bd09ToCgcs2000(lonLat) {
    // 百度转火星
    const gcj02LonLat = this.bd09ToGcj02(lonLat);

    // 火星转WGS84
    return this.gcj02toWgs84(gcj02LonLat);
  },

  /**
   * 大地2000坐标系(CGCS2000)转百度坐标系(BD-09)
   * @param {Number[]} lonLat -大地2000坐标系经纬度
   *
   * @returns {Number[]}
   */
  cgcs2000ToBd09(lonLat) {
    // WGS84转火星
    const gcj02LonLat = this.wgs84toGcj02(lonLat);

    // 火星转百度
    return this.gcj02tobd09(gcj02LonLat);
  },

  /** 公有方法 **/
  /**
   * 百度坐标系(BD-09)转火星坐标系(GCJ-02)
   * 即 百度 转 谷歌、高德
   * @param {Number[]} lonLat -经纬度
   *
   * @returns {Number[]}
   */
  bd09ToGcj02(lonLat) {
    const x_pi = this.X_PI;

    let x = lonLat[0] - 0.0065;
    let y = lonLat[1] - 0.006;
    let z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);

    let theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
    var gcj_lng = z * Math.cos(theta);
    var gcj_lat = z * Math.sin(theta);

    return [gcj_lng, gcj_lat];
  },

  /**
   * 火星坐标系(GCJ-02)转百度坐标系(BD-09)
   * 即谷歌、高德 转 百度
   * @param {Number[]} lonLat -经纬度
   *
   * @returns {Number[]}
   */
  gcj02tobd09(lonLat) {
    const x_pi = this.X_PI;

    let lng = lonLat[0];
    let lat = lonLat[1];
    var z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_pi);

    var theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_pi);
    var bd_lng = z * Math.cos(theta) + 0.0065;
    var bd_lat = z * Math.sin(theta) + 0.006;

    return [bd_lng, bd_lat]
  },

  /**
   * WGS84转火星坐标系(GCJ-02)
   * @param {Number[]} lonLat -经纬度
   *
   * @returns {Number[]}
   */
  wgs84toGcj02(lonLat) {
    let lng = lonLat[0];
    let lat = lonLat[1];
    if (this._isOutofChina(lng, lat)) {
      return lonLat;
    }

    const pi = this.PI;
    const ee = this.EE;
    const a = this.A;

    let dlat = this._transformLat(lng - 105.0, lat - 35.0);
    let dlng = this._transformLng(lng - 105.0, lat - 35.0);
    let radlat = lat / 180.0 * pi;
    let magic = Math.sin(radlat);
    magic = 1 - ee * magic * magic;

    let sqrtmagic = Math.sqrt(magic);
    dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi);
    dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);

    let gcjLng = lng + dlng;
    let gcjLat = lat + dlat;

    return [gcjLng, gcjLat];
  },

  /**
   * 火星坐标系(GCJ02)转WGS84
   * @param {Number[]} lonLat -经纬度
   *
   * @returns {Number[]}
   */
  gcj02toWgs84(lonLat) {
    let lng = lonLat[0];
    let lat = lonLat[1];
    if (this._isOutofChina(lng, lat)) {
      return lonLat;
    }

    const pi = this.PI;
    const ee = this.EE;
    const a = this.A;

    let dlat = this._transformLat(lng - 105.0, lat - 35.0);
    let dlng = this._transformLng(lng - 105.0, lat - 35.0);
    let radlat = lat / 180.0 * pi;
    let magic = Math.sin(radlat);
    magic = 1 - ee * magic * magic;

    let sqrtmagic = Math.sqrt(magic);
    dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi);
    dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);

    let mglat = lat + dlat;
    let mglng = lng + dlng;

    return [lng * 2 - mglng, lat * 2 - mglat]
  },

  /** 私有方法 **/
  /**
   * 是否超出中国范围
   * @param {Number} lng -经度
   * @param {Number} lat -纬度
   *
   * @returns {Boolean}
   */
  _isOutofChina(lng, lat) {
    return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false);
  },

  /**
   * 转换纬度
   * @param {Number} lng -经度
   * @param {Number} lat -纬度
   *
   * @returns {Number} 纬度
   */
  _transformLat(lng, lat) {
    const pi = this.PI;

    let ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
    ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(lat * pi) + 40.0 * Math.sin(lat / 3.0 * pi)) * 2.0 / 3.0;
    ret += (160.0 * Math.sin(lat / 12.0 * pi) + 320 * Math.sin(lat * pi / 30.0)) * 2.0 / 3.0;

    return ret;
  },

  /**
   * 转换经度
   * @param {Number} lng -经度
   * @param {Number} lat -纬度
   *
   * @returns {Number} 经度
   */
  _transformLng(lng, lat) {
    const pi = this.PI;

    let ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
    ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(lng * pi) + 40.0 * Math.sin(lng / 3.0 * pi)) * 2.0 / 3.0;
    ret += (150.0 * Math.sin(lng / 12.0 * pi) + 300.0 * Math.sin(lng / 30.0 * pi)) * 2.0 / 3.0;

    return ret;
  }
}

export default SpatialRefUtils;
