import * as turf from '@turf/turf'
import { MapFeatureType } from './BasicClass'
import { MapCoordTrans } from '../export/MapCoordTrans'
/**
 *@class GeospatialAnalysis
 *@classdesc 空间分析
 */
export class GeospatialAnalysis {
  /**
   * 获取要素集合范围（多点，多线，多面 或者组合）
   * @param {object[]} geoObj 要素集合
   * @param {MapFeatureType} geoObj.type 要素类型
   * @param {number[]} geoObj.features 要素集合
   * @returns 边界范围[minX, minY, maxX, maxY]
   */
  getBoundingBox(geoObj) {
    var collection = this._getFeatureCollection(geoObj)
    var features = turf.featureCollection(collection)
    return turf.bbox(features)
  }

  /**
   * 获取要素集合中心点（多点，多线，多面 或者组合）
   * @param {object[]} geoObj 要素集合
   * @param {MapFeatureType} geoObj.type 要素类型
   * @param {number[]} geoObj.features 要素集合
   * @returns 中心点坐标[centerX, centerY]
   */
  getBoundingCenter(geoObj) {
    var collection = this._getFeatureCollection(geoObj)
    var features = turf.featureCollection(collection)
    var center = turf.center(features)
    return center.geometry.coordinates
  }

  //geoObj =[{type:'point',features:[]},{type:'line',features:[]},{type:'polygon',features:[]}]
  _getFeatureCollection(geoObj) {
    var collection = []
    for (var i = 0; i < geoObj.length; i++) {
      if (geoObj[i].type == MapFeatureType.Point) {
        collection.push(turf.multiPoint(geoObj[i].features))
      } else if (geoObj[i].type == MapFeatureType.Line) {
        collection.push(turf.multiLineString(geoObj[i].features))
      } else if (geoObj[i].type == MapFeatureType.Polygon) {
        var mpoly = []
        for (var j = 0; j < geoObj[i].features.length; j++) {
          mpoly.push([geoObj[i].features[j]])
        }
        collection.push(turf.multiPolygon(mpoly))
      }
    }
    return collection
  }

  /**
   * 单个多边形面积
   * @param {Number[][]} boundary 多边形点
   * @returns 面积，单位m2
   */
  getArea(boundary) {
    if (!this.booleanEqual(boundary[0], boundary[boundary.length - 1], MapFeatureType.Point)) {
      boundary.push(boundary[0])
    }
    var polygon = turf.polygon([boundary])
    return turf.area(polygon)
  }

  /**
   * 获取两个点之间从正北算起的角度(0度),顺时针为正，逆时针为负
   * @param {number[]} start 起始点
   * @param {Number[]} end 终点
   * @returns 角度 [-180,180]
   */
  getAngle(start, end) {
    var point1 = turf.point(start)
    var point2 = turf.point(end)
    return turf.bearing(point1, point2)
  }

  /**
   * 获取两点距离
   * @param {number[]} from 起点
   * @param {number[]} to 终点
   * @returns 距离，单位km
   */
  getPointDistance(from, to) {
    var from = turf.point(from)
    var to = turf.point(to)
    return turf.distance(from, to)
  }

  /**
   * 返回点到直线的最小距离
   * @param {number[]} pt 点
   * @param {number[][]} path 线
   * @returns 距离，单位km
   */
  getNearestPathDistance(pt, path) {
    var pt = turf.point(pt)
    var line = turf.lineString(path)
    return turf.pointToLineDistance(pt, line)
  }

  /**
   * 获取线上离目标点最近的点
   * @param {number[]} target 目标点
   * @param {number[][]} path 线
   * @returns 线上距离目标点最近的点
   */
  getNearestPathPoint(target, path) {
    var line = turf.lineString(path)
    var pt = turf.point(target)
    var snapped = turf.nearestPointOnLine(line, pt)
    return snapped.geometry.coordinates
  }

  /**
   * 获取点集里距离目标点最近的点
   * @param {number[]} target 目标点
   * @param {number[][]} points
   * @returns
   */
  getNearestPoint(target, points) {
    var targetPoint = turf.point(target)
    var mpoints = []
    points.forEach((p) => {
      mpoints.push(turf.point(p))
    })

    var pts = turf.featureCollection(mpoints)
    var nearest = turf.nearestPoint(targetPoint, pts)
    return nearest.geometry.coordinates
  }
  /**
   * 获取线长度
   * @param {number[][]} path 线
   * @returns 长度，单位km
   */
  getPathLength(path) {
    var line = turf.lineString(path)
    return turf.length(line)
  }

  /**
   * 获取大圆路线
   * @param {number[]} from 起点
   * @param {number[]} to 终点
   * @returns 路线点
   */
  getGreatCircle(from, to) {
    var start = turf.point(from)
    var end = turf.point(to)
    var gcircle = turf.greatCircle(start, end)
    return gcircle.geometry.coordinates
  }

  /**
   * 平滑路径（贝塞尔曲线）
   * @param {number[][]} path 路径
   * @returns 平滑后路径
   */
  smoothPath(path) {
    var line = turf.lineString(path)
    var spath = turf.bezierSpline(line)
    return spath.geometry.coordinates
  }

  /**
   * 获取缓冲区（支持多个同类型要素）
   * @param {object[]} geoObj
   * @param {number} radius 缓冲区半径，单位km
   * @returns 缓冲区
   */
  getBufferArea(geoObj, radius) {
    var collection = this._getFeatureCollection(geoObj)
    var features = turf.featureCollection(collection)
    var buf = turf.buffer(features, radius)
    var area = this._loadoffMulti(buf.features[0].geometry)

    return area
  }
  _loadoffMulti(geometry) {
    var items = []
    if (geometry.type.startsWith('Multi')) {
      for (var i = 0; i < geometry.coordinates.length; i++) {
        items.push(geometry.coordinates[i][0])
      }
    } else {
      items = geometry.coordinates
    }
    return items
  }
  /**
   * 获取两个多边形交集
   * @param {number[][]} poly1 多边形1
   * @param {number[][]} poly2 多边形2
   * @returns 交集存在，返回多边形数组；交集不存在，返回null
   */
  getIntersect(poly1, poly2) {
    var p1 = turf.polygon([poly1])
    var p2 = turf.polygon([poly2])
    var intersection = turf.intersect(p1, p2)
    if (intersection) {
      return intersection.geometry.coordinates
    } else {
      return intersection
    }
  }

  /**
   * 返回具有指定距离偏移量的线
   * @param {number[][]} path 路径
   * @param {number} offset 偏移距离，单位开km,可有负值
   * @returns 偏移后路径点
   */
  getLinebyOffset(path, offset) {
    var line = turf.lineString(path)
    var offsetLine = turf.lineOffset(line, offset)
    return offsetLine.geometry.coordinates
  }

  /**
   * 返回两个多边形的组合多边形，如果输入的多边形没有交集，这个函数将返回这两个多边形
   * @param {number[][]} poly1 多边形1
   * @param {number[][]} poly2 多边形2
   * @returns 组合多边形或两个多边形
   */
  getUnionArea(poly1, poly2) {
    var p1 = turf.polygon([poly1])
    var p2 = turf.polygon([poly2])
    var union = turf.union(p1, p2)
    var unionarea = this._loadoffMulti(union.geometry)
    return unionarea
  }

  /**
   * 获取两点间避开障碍物的最短路径
   * @param {number[]} from 起点
   * @param {number[]} to 终点
   * @param {number[]} obstacles 障碍（多边形数组）
   * @returns 最短路径
   */
  getShortestPath(from, to, obstacles) {
    var mpoly = []
    var collection = []
    for (var j = 0; j < obstacles.length; j++) {
      mpoly.push([obstacles[j]])
    }
    collection.push(turf.multiPolygon(mpoly))
    var feature = turf.featureCollection(collection)

    var options = {
      obstacles: feature
    }
    var path = turf.shortestPath(from, to, options)
    return path.geometry.coordinates
  }

  /**
   * idw插值
   * @param {number[]} points 插值点集 [x,y,value]
   * @param {number} gridsize 网格宽度,单位km
   * @returns 插值结果 [{features:[],value}]
   */
  interpolateIDW(points, gridsize) {
    var collection = []
    for (var i = 0; i < points.length; i++) {
      collection.push(turf.point([points[i][0], points[i][1]], { value: points[i][2] }))
    }
    var feature = turf.featureCollection(collection)

    var options = { gridType: 'triangle', property: 'value' }
    var grid = turf.interpolate(feature, gridsize, options)
    var out = []
    turf.featureEach(grid, function (g) {
      out.push({ features: g.geometry.coordinates[0], value: g.properties.value })
    })
    return out
  }

  /**
   * 获得多边形内点
   * @param {number[][]} points 要筛选的点集
   * @param {number[][]} polygon 多边形
   * @returns 在多边形内的点
   */
  getPointsWithinPolygon(points, polygon) {
    var pts = turf.multiPoint(points)
    var searchWithin = turf.polygon([polygon])
    var ptsW = turf.pointsWithinPolygon(pts, searchWithin)
    var ptsWithin = []
    if (ptsW.length > 0) {
      for (var i = 0; i < ptsW.features[0].geometry.coordinates.length; i++) {
        ptsWithin.push(ptsW.features[0].geometry.coordinates[i])
      }
    }
    return ptsWithin
  }
  /**
   * 获取两个多边形差集
   * @param {number[][]} inputPoly 计算差集多边形
   * @param {number[][]} overlayPoly 参照多边形
   * @returns 差集多边形 inputPoly-overlayPoly
   */
  getDifference(inputPoly, overlayPoly) {
    var polygon1 = turf.polygon([inputPoly])
    var polygon2 = turf.polygon([overlayPoly])
    var difference = turf.difference(polygon1, polygon2)
    return difference.geometry.coordinates
  }

  /**
   * 点聚合
   * @param {object[]} points 点集
   * @param {number} distance 聚合距离
   * @returns object[],聚合结果
   */
  clustersDbscan(points, distance) {
    var collection = []
    for (var i = 0; i < points.length; i++) {
      collection.push(turf.point(points[i].position, { id: points[i].id }))
    }
    var feature = turf.featureCollection(collection)

    var clustered = turf.clustersDbscan(feature, distance)
    var levels = []
    turf.clusterEach(clustered, 'cluster', function (cluster, clusterValue, currentIndex) {
      var centerCollection = turf.featureCollection(cluster.features)
      var center = turf.center(centerCollection)
      levels.push({ cluster: clusterValue, coord: center.geometry.coordinates, count: cluster.features.length })
    })
    var fIds = []
    turf.geomEach(clustered, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
      if (featureProperties.cluster == undefined) {
        fIds.push(featureProperties.id)
      }
    })
    if (fIds.length > 0) {
      levels.push({ cluster: -1, ids: fIds })
    }
    return levels
  }

  /**
   * 点是否在多边形内
   * @param {number[]} point 点
   * @param {number[][]} polygon 多边形
   * @returns bool
   */
  booleanPointInPolygon(point, polygon) {
    var pt = turf.point(point)
    var poly = turf.polygon([polygon])
    return turf.booleanPointInPolygon(pt, poly)
  }

  /**
   * 点是否在线上
   * @param {number[]} point 点
   * @param {number[][]} line 线
   * @returns bool
   */
  booleanPointOnLine(point, line) {
    var pt = turf.point(point)
    var linestr = turf.lineString(line)
    return turf.booleanPointOnLine(pt, linestr)
  }

  /**
   * 相同类型的两要素是否相同
   * @param {Number[]} feature1 要素1
   * @param {Number[]} feature2 要素2
   * @param {MapFeatureType} type 要素类型（点、线、面）
   * @returns bool
   */
  booleanEqual(feature1, feature2, type) {
    var f1, f2
    switch (type) {
      case MapFeatureType.Point:
        f1 = turf.point(feature1)
        f2 = turf.point(feature2)
        break
      case MapFeatureType.Line:
        f1 = turf.lineString(feature1)
        f2 = turf.lineString(feature2)
        break
      case MapFeatureType.Polygon:
        f1 = turf.polygon([feature1])
        f2 = turf.polygon([feature2])
        break
    }
    return turf.booleanEqual(f1, f2)
  }

  /**
   * 面积单位转换
   * @param {number} number 面积
   * @param {string} oriUnit 当前面积单位，（kilometers, metres, centimetres, millimeters, acres, miles, yards, feet, inches, hectares）
   * @param {string} desUnit 转换面积单位，（kilometers, metres, centimetres, millimeters, acres, miles, yards, feet, inches, hectares）
   * @returns 转换后数据
   */
  convertArea(number, oriUnit, desUnit) {
    return turf.convertArea(number, oriUnit, desUnit)
  }

  /**
   * 长度单位转换
   * @param {number} number
   * @param {string} oriUnit 当前长度单位，（miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet）
   * @param {string} desUnit 转换面积单位，（miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet）
   * @returns 转换后数据
   */
  convertLength(number, oriUnit, desUnit) {
    return turf.convertLength(number, oriUnit, desUnit)
  }

  /**
   * 角度转弧度
   * @param {number} degree 角度，（0-360）
   * @returns 弧度
   */
  degreesToRadians(degree) {
    return turf.degreesToRadians(degree)
  }

  /**
   * 弧度转角度
   * @param {number} radians 弧度
   * @returns 角度，（0-360）
   */
  radiansToDegrees(radians) {
    return turf.radiansToDegrees(radians)
  }
  /**
   * 路径平滑插值
   * @param {number[][]} paths 路径点
   * @param {number} speed 行驶路径速度,m/s
   * @param {number} seed  定时器间隔，单位ms
   * @param {number} iconAngle 图片角度
   * @return {number[]} 插值后路径
   */
  getPathStepPoints(paths, speed, seed, iconAngle) {
    var pathPoints = []
    var pathLastIndex = paths.length - 1
    for (var i = 0; i < pathLastIndex; i++) {
      var longitude1 = paths[i][0]
      var latitude1 = paths[i][1]
      var longitude2 = paths[i + 1][0]
      var latitude2 = paths[i + 1][1]

      //两点之间的图标倾斜角度
      var angle = this._angleByLongLat(longitude1, latitude1, longitude2, latitude2, iconAngle)
      // console.log(angle)
      //计算两点之间的方向角(单位:度)
      var azimuth = this.getAngle([longitude1, latitude1], [longitude2, latitude2])

      //将起点添加到数组中
      pathPoints.push({ longitude: longitude1, latitude: latitude1, angle: angle })

      //计算两点间的距离(单位:米)
      var distance = this.getPointDistance([longitude1, latitude1], [longitude2, latitude2])
      //定时器平均每次能运行的距离(单位:米/次)
      var avg_distance = (speed * seed) / 1000000
      //如果两点间得距离小于定时器每次运行的距离，则不用在两个经纬度点之间选取分割点
      if (distance <= avg_distance) {
        continue
      }
      //计算两点间，定时器需要执行的次数
      var times = distance / avg_distance
      for (var j = 1; j < times; j++) {
        var curr_distance = avg_distance * j
        var pt = this._getNextPoint(longitude1, latitude1, curr_distance, azimuth)
        pt.angle = angle
        pathPoints.push(pt)
      }
    }
    var ptLast = {
      longitude: paths[pathLastIndex][0],
      latitude: paths[pathLastIndex][1],
      angle: pathPoints[pathPoints.length - 1].angle
    }
    pathPoints.push(ptLast)
    return pathPoints
  }

  _angleByLongLat(longitude1, latitude1, longitude2, latitude2, iconAngle) {
    var coordTrans = new MapCoordTrans()
    var ptTemp1 = coordTrans.lonLatToMercator([longitude1, latitude1])
    var ptTemp2 = coordTrans.lonLatToMercator([longitude2, latitude2])
    var x = ptTemp2[0] - ptTemp1[0]
    var y = ptTemp2[1] - ptTemp1[1]
    var angle = Math.atan2(y, x)
    var angleRadians = (iconAngle * Math.PI) / 180
    angle = ((angle - angleRadians) / Math.PI) * 180

    // var point1 = turf.point([longitude1, latitude1]);
    // var point2 = turf.point([longitude2, latitude2]);
    // var angleRadians = iconAngle * Math.PI / 180

    // var bearing = turf.bearing(point1, point2);
    // var angle1 = 90 - bearing
    // var angle = angle1 * Math.PI / 180
    // angle = (angle - angleRadians) / Math.PI * 180;

    return angle
  }
  _getNextPoint(longitude1, latitude1, distance, azimuth) {
    // var EARTH_ARC = 111.199
    // // distance表示两点间得距离(单位:km)
    // azimuth = azimuth * Math.PI / 180.0
    // // 将距离转换成经度的计算公式
    // var lon = longitude1 + (distance * Math.sin(azimuth)) / (EARTH_ARC * Math.cos(latitude1 * Math.PI / 180.0));
    // // 将距离转换成纬度的计算公式
    // var lat = latitude1 + (distance * Math.cos(azimuth)) / EARTH_ARC;
    // return { "longitude": lon, "latitude": lat };

    var point = turf.point([longitude1, latitude1])
    var destination = turf.destination(point, distance, azimuth)

    return { longitude: destination.geometry.coordinates[0], latitude: destination.geometry.coordinates[1] }
  }
}
