import * as turf from "@turf/turf";
import GeoJsonProvider from "../geog/GeoJsonProvider";

/**
 * 地理工具类
 */
const GeogUtils = {

  /**
   * 经纬度转弧度
   * @param {Number} degrees -经纬度
   *
   * @returns {Number} 弧度
   */
  toRadians(degrees) {
    return degrees * Math.PI / 180;
  },

  /**
   * 弧度转经纬度
   * @param {Number} degrees -弧度
   *
   * @returns {Number} 经纬度
   */
  toDegrees(radians) {
    return radians * 180 / Math.PI;
  },

  /**
   * 缓冲区
   * @param {Object} data -GeoJSON要素
   * @param {Number} radius -半径
   *
   * @returns {Object} GeoJSON要素
   */
  buffer(data, radius = 0.02) {
    let coordinates = data.geometry.coordinates;
    let steps = 16;

    // 构造缓冲区
    let feature;
    switch (data.geometry.type) {
      case 'Point': {
        steps = 16;
        feature = turf.point(coordinates);
        break;
      }
      case 'LineString': {
        steps = 8;
        feature = turf.lineString(coordinates);
        break;
      }
      default:
        break;
    }
    if (!feature) return null;

    // 创建缓冲区
    return turf.buffer(feature, radius, {
      units: 'kilometers',
      steps: steps
    });
  },

  /**
   * 反转线要素图形
   * @param {Object} feature -要素
   */
  reverse(feature) {
    if (feature.geometry.type !== 'LineString') {
      return feature;
    }
    // 反转数组
    feature.geometry.coordinates.reverse();
  },

  /**
   * 计算方位角
   * @param {Number[]} lonLat -起点经纬度
   * @param {Number[]} lonLat2 -终点经纬度
   *
   * @returns {Number} 方位角
   * @summary<返回值单位是°>
   */
  calcBearing(lonLat, lonLat2) {
    // 经纬度转弧度
    let startLon = this.toRadians(lonLat[0]);
    let startLat = this.toRadians(lonLat[1]);
    let destLon = this.toRadians(lonLat2[0]);
    let destLat = this.toRadians(lonLat2[1]);

    let y = Math.sin(destLon - startLon) * Math.cos(destLat);
    let x = Math.cos(startLat) * Math.sin(destLat) - Math.sin(startLat) * Math.cos(destLat) * Math.cos(destLon - startLon);
    let brng = Math.atan2(y, x);
    let brngDgr = this.toDegrees(brng);

    return (brngDgr + 360) % 360;
  },

  /**
   * 计算方位角（Mapbox-gl）
   * @param {Number[]} lonLat -起点经纬度
   * @param {Number[]} lonLat2 -终点经纬度
   *
   * @returns {Number} 方位角
   * @summary<返回值单位是°>
   */
  bearing(lonLat, lonLat2) {
    return turf.bearing(turf.point(lonLat), turf.point(lonLat2)) - 90;
  },

  /**
   * 根据起终点计算Bezier曲线控制点
   * @param {Array} start -起点
   * @param {Array} end -终点
   * @param {Number} arc -弧度(0-1)
   *
   * @returns {Array}
   */
  calcControlPoint(lonLat, lonLat2, arc = 0.25) {
    const deltaX = lonLat2[0] - lonLat[0];
    const deltaY = lonLat2[1] - lonLat[1];

    const theta = Math.atan(deltaY / deltaX);
    const len = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY)) / 2 * arc;
    const newTheta = theta - Math.PI / 2;

    return [
      (lonLat[0] + lonLat2[0]) / 2 + len * Math.cos(newTheta),
      (lonLat[1] + lonLat2[1]) / 2 + len * Math.sin(newTheta),
    ];
  },

  /**
   * 计算边界框
   * @param {Object} data -GeoJSON要素集/要素
   *
   * @returns {Number[][]}
   */
  calcBBox(data) {
    let bbox = turf.bbox(data);

    return bbox ? [[bbox[0], bbox[1]], [bbox[2], bbox[3]]] : null;
  },

  /**
   * 计算两点间距离
   * @param {Number[]} lonLat -起点坐标
   * @param {Number[]} lonLat2 -终点坐标
   *
   * @returns {Number} 两点间距离(单位：m)
   */
  calcDistance(lonLat, lonLat2) {
    return turf.distance(turf.point(lonLat), turf.point(lonLat2), {
      units: 'kilometers'
    }) * 1000.0;
  },

  /**
   * 获取折线长度
   * @param {Object} feature -GeoJSON要素
   *
   * @returns {Number} 单位:m
   */
  calcLength(feature) {
    return turf.length(feature) * 1000;
  },

  /**
   * 计算中点/中心点
   * @param {Object} feature -GeoJSON要素
   *
   * @returns {Number[]}
   */
  calcCenter(feature) {
    switch (feature.geometry.type.toLowerCase()) {
      case 'point': {
        return feature.geometry.coordinates;
      }
      case 'multipoint': {
        // 计算中心点
        const centerFea = turf.center(feature);

        return centerFea.geometry.coordinates;
      }
      case 'linestring': {
        // 获取折线长度
        let len = turf.length(feature);
        // 获取沿折线的点
        const centerFea = turf.along(feature, len / 2, {
          units: 'kilometers'
        });

        return centerFea.geometry.coordinates;
      }
      case 'multilinestring': {
        const ls = feature.geometry.coordinates[0];
        const lsFea = GeoJsonProvider.getLineStringFeature(ls, feature.properties);

        // 获取折线长度
        let len = turf.length(lsFea);
        // 获取沿折线的点
        const centerFea = turf.along(lsFea, len / 2, {
          units: 'kilometers'
        });

        return centerFea.geometry.coordinates;
      }
      case 'polygon': {
        // 计算多边形中心
        const centerFea = turf.centerOfMass(feature);

        return centerFea.geometry.coordinates;
      }
      case 'multipolygon': {
        const poly = feature.geometry.coordinates[0];
        const polyFea = GeoJsonProvider.getPolygonFeature(poly, feature.properties);
        // 计算多边形中心
        const centerFea = turf.centerOfMass(polyFea);

        return centerFea.geometry.coordinates;
      }
      default:
        return null;
    }
  },

  /**
   * 计算多边形外边环最北边的点坐标
   * @param {Object} feature -GeoJSON要素
   *
   * @returns {Number[]}
   */
  calcNorthLonLatOfPolygon(feature) {
    let ls = feature.geometry.coordinates[0];

    let lon = -180, lat = -90;
    for (let i = 0; i < ls.length; i++) {
      if (lat < ls[i][1]) {
        lon = ls[i][0];
        lat = ls[i][1];
      }
    }

    return [lon, lat];
  },

  /**
   * 计算在多边形内的点集
   * @param {Object} pointFC -点GeoJSON要素集合
   * @param {Object} polygonFeature -多边形要素
   */
  calcPointsWithinPolygon(pointFC, polygonFeature) {
    return turf.pointsWithinPolygon(pointFC, polygonFeature);
  },

  /**
   * 计算线与线要素的交点
   * @param {Object} feature -线要素
   * @param {Object} feature2 -线要素
   *
   * @returns {Array} 点要素集合
   */
  calcLineIntersect(feature, feature2) {
    return turf.lineIntersect(feature, feature2);
  },

  /**
   * 判断点是否在多边形内
   * @param {Number[]} lonLat -经纬度坐标
   * @param {Object} feature -GeoJSON要素
   *
   * @returns {Boolean}
   */
  isPointInPolygon(lonLat, feature) {
    let pt = turf.point(lonLat);

    return turf.booleanPointInPolygon(pt, feature);
  },

  /**
   * 判断other是否包含在feature内
   *
   * @returns {Boolean}
   */
  isContains(feature, other) {
    return turf.booleanContains(feature, other);
  },

  /**
   * 判断other和feature是否相离
   *
   * @returns {Boolean}
   */
  isDisjoint(feature, other) {
    return turf.booleanDisjoint(feature, other);
  }
};

export default GeogUtils;
