类名 common/base/geometry/GeometryEngine.js
import * as T from '@turf/turf'
import Zondy from '../Zondy'
import { defaultValue, isNull, Log } from '../../util'
import { LengthUnit } from '../enum'
import Geometry from './Geometry'
import Polygon from './Polygon'
import LineString from './LineString'
import MultiPolygon from './MultiPolygon'
import Projection from '../Projection'
import SpatialReference from './SpatialReference'
import GeometryType from '../enum/GeometryType'
import MultiPoint from './MultiPoint'
import Point from './Point'
import Circle from './Circle'
import Extent from './Extent'
import MultiLineString from './MultiLineString'

/**
 * 几何分析工具
 * @class GeometryEngine
 * @moduleEX GeometryModule
 *
 * @summary <h5>支持如下方法:</h5>
 * <a href='#contains'>[1、是否包含(contains)]</a><br/>
 * <a href='#crosses'>[2、是否穿过]</a><br/>
 * <a href='#cut'>[3、几何切割]</a><br/>
 * <a href='#difference'>[4、几何求差]</a><br/>
 * <a href='#disjoint'>[5、是否相离]</a><br/>
 * <a href='#equals'>[6、是否相等]</a><br/>
 * <a href='#intersects'>[7、是否相交]</a><br/>
 * <a href='#intersect'>[8、返回两个几何对象的相交部分]</a><br/>
 * <a href='#nearestCoordinate'>[9、距离最近的点]</a><br/>
 * <a href='#overlaps'>[10、是否重叠]</a><br/>
 * <a href='#planarArea'>[11、计算平面面积]</a><br/>
 * <a href='#planarLength'>[12、计算平面长度]</a><br/>
 * <a href='#geodesicLength'>[13、计算地理长度]</a><br/>
 * <a href='#rotate'>[14、旋转]</a><br/>
 * <a href='#distance'>[15、计算距离]</a><br/>
 * <a href='#touches'>[16、是否相邻]</a><br/>
 * <a href='#union'>[17、求并]</a><br/>
 * <a href='#within'>[18、是否被包含(within)]</a><br/>
 * <a href='#buffer'>[19、缓冲区分析]</a><br/>
 * <a href='#isSimple'>[20、拓扑检查]</a><br/>
 * <a href='#getPositionsFromArc'>[21、通过三点弧段构造一组离散的点坐标]</a><br/>
 * <a href='#getArcFromGeometry'>[22、通过几何中的三点弧段数据,构造离散后的几何对象]</a><br/>
 * <a href='#getCenter'>[23、通过三个点求圆心]</a><br/>
 * <a href='#getAngleFromPoints'>[24、计算三点之间的夹角]</a><br/>
 * <a href='#simplify'>[25、拓扑矫正]</a><br/>
 */
class GeometryEngine {}

/**
 * <a id='difference'></a>
 * 几何求差,即返回inputGeometry几何对象减去subtractGeometry几何对象之后的部分;<br/>
 * 支持区、多区、矩形和圆形之间进行相互求差;<br/>
 * 支持自定义坐标系几何,忽略三维高度;<br/>
 * 求差后可能返回一个区几何或一个多区几何或者一个空对象;<br/>
 * 由于示例太多,仅列举部分示例,示例如下:<br/>
 * <a href='#difference1'>[1、区和区求差]</a><br/>
 * <a href='#difference2'>[2、区和多区求差]</a><br/>
 * <a href='#difference3'>[3、多区和圆形求差]</a><br/>
 * <a href='#difference4'>[4、矩形和圆求差]</a><br/>
 * @param {Polygon | MultiPolygon | Extent | Circle} inputGeometry 被求差几何对象
 * @param {Polygon | MultiPolygon | Extent | Circle} subtractGeometry 求差几何对象
 * @return { Polygon | MultiPolygon } 求差后的几何对象
 * @example <caption><h5 id='difference1'>区和区求差</h5></caption>
 * // ES5引入方式
 * const { Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造第一个区
 * const polygon1 = new Polygon({
 *   coordinates: [
 *     [
 *       [108.36341, 29.032578],
 *       [116.13094, 29.032578],
 *       [116.13094, 33.273224],
 *       [108.36341, 33.273224],
 *       [108.36341, 29.032578]
 *     ]
 *   ]
 * })
 * // 构造第二个区
 * onst polygon2 = new Polygon({
 *   coordinates: [
 *     [
 *       [109.36341, 30.032578],
 *       [115.13094, 30.032578],
 *       [115.13094, 32.273224],
 *       [109.36341, 32.273224],
 *       [109.36341, 30.032578]
 *     ],
 *     [
 *       [110.36341, 31.032578],
 *       [114.13094, 31.032578],
 *       [114.13094, 31.5],
 *       [110.36341, 31.5],
 *       [110.36341, 31.032578]
 *     ]
 *   ]
 * })
 * // 开始求差
 * const geometry = GeometryEngine.difference(polygon1, polygon2)
 * console.log("区和区求差:", geometry)
 *
 * @example <caption><h5 id='difference2'>区和多区求差</h5></caption>
 * // ES5引入方式
 * const { Polygon, MultiPolygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, MultiPolygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造第一个区
 * const polygon1 = new Polygon({
 *   coordinates: [
 *     [
 *       [108.36341, 29.032578],
 *       [116.13094, 29.032578],
 *       [116.13094, 33.273224],
 *       [108.36341, 33.273224],
 *       [108.36341, 29.032578]
 *     ]
 *   ]
 * })
 * // 构造第二个区
 * const polygon2 = new MultiPolygon({
 *   coordinates: [
 *     [
 *       [
 *         [109.36341, 30.032578],
 *         [110.13094, 30.032578],
 *         [110.13094, 32.273224],
 *         [109.36341, 32.273224],
 *         [109.36341, 30.032578]
 *       ]
 *     ],
 *     [
 *       [
 *         [111.36341, 30.032578],
 *         [112.13094, 30.032578],
 *         [112.13094, 32.273224],
 *         [111.36341, 32.273224],
 *         [111.36341, 30.032578]
 *       ]
 *     ]
 *   ]
 * })
 * // 开始求差
 * const geometry = GeometryEngine.difference(polygon1, polygon2)
 * console.log("区和多区求差:", geometry)
 *
 * @example <caption><h5 id='difference3'>多区和圆形求差</h5></caption>
 * // ES5引入方式
 * const { MultiPolygon, Circle, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { MultiPolygon, Circle,GeometryEngine } from "@mapgis/webclient-common"
 * // 构造多区几何
 * const polygon = new MultiPolygon({
 *   coordinates: [
 *     [
 *       [
 *         [108.36341, 29.032578],
 *         [110.13094, 29.032578],
 *         [110.13094, 33.273224],
 *         [108.36341, 33.273224],
 *         [108.36341, 29.032578]
 *       ]
 *     ],
 *     [
 *       [
 *         [111.36341, 29.032578],
 *         [116.13094, 29.032578],
 *         [116.13094, 33.273224],
 *         [111.36341, 33.273224],
 *         [111.36341, 29.032578]
 *       ]
 *     ]
 *   ]
 * })
 * // 构造圆几何
 * const circle = new Circle({
 *   center: [112.247175, 31.152901],
 *   radius: 2
 * })
 * // 开始求差
 * const geometry = GeometryEngine.difference(polygon, circle)
 * console.log("多区和圆求差:", geometry)
 *
 * @example <caption><h5 id='difference4'>矩形和圆求差</h5></caption>
 * // ES5引入方式
 * const { Extent, Circle, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Extent, Circle,GeometryEngine } from "@mapgis/webclient-common"
 * // 构造矩形对象
 * const extent = new Extent({
 *   xmin: 108.36341,
 *   ymin: 29.032578,
 *   xmax: 116.13094,
 *   ymax: 33.273224
 * })
 * // 构造圆形对象
 * const circle = new Circle({
 *   center: [112.247175, 31.152901],
 *   radius: 2
 * })
 * // 开始求差
 * const geometry = GeometryEngine.difference(extent, circle)
 * console.log("矩形和圆求差:", geometry)
 * */
GeometryEngine.difference = function (inputGeometry, subtractGeometry) {
  // 入参检测
  if (isNull(inputGeometry)) {
    Log.error('inputGeometry不能为空!')
  }
  if (isNull(subtractGeometry)) {
    Log.error('subtractGeometry不能为空!')
  }
  // 类型检查
  if (
    !(
      inputGeometry instanceof Polygon ||
      inputGeometry instanceof MultiPolygon ||
      inputGeometry instanceof Extent ||
      inputGeometry instanceof Circle
    )
  ) {
    Log.error('inputGeometry类型错误!')
  }
  if (
    !(
      subtractGeometry instanceof Polygon ||
      subtractGeometry instanceof MultiPolygon ||
      subtractGeometry instanceof Extent ||
      subtractGeometry instanceof Circle
    )
  ) {
    Log.error('subtractGeometry类型错误!')
  }

  let _geometry = undefined

  // 根据类型处理数据
  switch (inputGeometry.type) {
    case GeometryType.polygon:
      _geometry = GeometryEngine._polygonDifferenceGeometry(
        T.polygon(inputGeometry.coordinates),
        subtractGeometry
      )
      break
    case GeometryType.extent:
      const _polygon = new Polygon({
        coordinates: [
          [
            [inputGeometry.xmin, inputGeometry.ymin],
            [inputGeometry.xmax, inputGeometry.ymin],
            [inputGeometry.xmax, inputGeometry.ymax],
            [inputGeometry.xmin, inputGeometry.ymax],
            [inputGeometry.xmin, inputGeometry.ymin]
          ]
        ]
      })
      _geometry = GeometryEngine._polygonDifferenceGeometry(
        T.polygon(_polygon.coordinates),
        subtractGeometry
      )
      break
    case GeometryType.circle:
      _geometry = GeometryEngine._polygonDifferenceGeometry(
        T.polygon(inputGeometry.toPolygon().coordinates),
        subtractGeometry
      )
      break
    case GeometryType.multiPolygon:
      _geometry = GeometryEngine._polygonDifferenceGeometry(
        T.multiPolygon(inputGeometry.coordinates),
        subtractGeometry
      )
      break
    default:
      break
  }

  // 根据几何类型返回几何
  if (!isNull(_geometry)) {
    if (_geometry.geometry.type === 'Polygon') {
      _geometry = new Polygon({
        coordinates: _geometry.geometry.coordinates,
        spatialReference: SpatialReference.fromJSON(
          inputGeometry.spatialReference
        )
      })
    } else {
      _geometry = new MultiPolygon({
        coordinates: _geometry.geometry.coordinates,
        spatialReference: SpatialReference.fromJSON(
          inputGeometry.spatialReference
        )
      })
    }
  }

  return _geometry
}

GeometryEngine._polygonDifferenceGeometry = function (
  inputGeometry,
  subtractGeometry
) {
  const _polygon1 = inputGeometry
  let _polygon2 = undefined
  switch (subtractGeometry.type) {
    case GeometryType.polygon:
      _polygon2 = T.polygon(subtractGeometry.coordinates)
      break
    case GeometryType.multiPolygon:
      _polygon2 = T.multiPolygon(subtractGeometry.coordinates)
      break
    case GeometryType.circle:
      _polygon2 = T.polygon(subtractGeometry.toPolygon().coordinates)
      break
    case GeometryType.extent:
      const _polygon = new Polygon({
        coordinates: [
          [
            [subtractGeometry.xmin, subtractGeometry.ymin],
            [subtractGeometry.xmax, subtractGeometry.ymin],
            [subtractGeometry.xmax, subtractGeometry.ymax],
            [subtractGeometry.xmin, subtractGeometry.ymax],
            [subtractGeometry.xmin, subtractGeometry.ymin]
          ]
        ]
      })
      _polygon2 = T.polygon(_polygon.coordinates)
      break
    default:
      break
  }

  return T.difference(_polygon1, _polygon2)
}

/**
 * <a id='buffer'></a>
 * 缓冲区分析;<br/>
 * 支持点几何、多点几何、线几何、多线几何、区几何、多区几何、矩形几何、圆形几何或任意几何组成的几何数组;<br/>
 * 忽略三维高度,当geometry为几何数组时,其子元素的坐标系必须相同;<br/>
 * 如果distance是数字,则可以为正值或负值,是正值则向外扩张,是负值,则表示向内收缩;<br/>
 * 如果distance是数字,且geometry为几何数组,则该数字会应用到所有geometry数组的子元素;<br/>
 * 如果distance是数组且geometry是数组,当distance的长度小于geometry数组的长度时,会以distance的最后一个子元素扩充distance数组,使其长度和geometry数组的长度相同,之后将distance数组和geometry数组根据下标一一对应,执行缓冲区分析;<br/>
 * 如果distance是数组,但geometry不是数组,则使distance数组的第一个子元素做为缓冲距离来执行缓冲区分析;<br/>
 * 注意一个或多个几何执行缓冲区分析后,由于缓冲后几何扩大,可能导致一个或多个几何出现重合的情况;;<br/>
 * 示例如下:<br/>
 * <a href='#buffer1'>[1、区缓冲分析]</a><br/>
 * <a href='#buffer2'>[2、多区缓冲分析]</a><br/>
 * <a href='#buffer3'>[3、几何数组缓冲区分析]</a><br/>
 * <a href='#buffer4'>[4、几何数组缓冲区分析 - 结果几何合并]</a><br/>
 * <a href='#buffer5'>[5、区缓冲分析 - 自定义坐标系]</a><br/>
 * <a href='#buffer6'>[6、点缓冲分析]</a><br/>
 * <a href='#buffer7'>[7、线缓冲分析]</a><br/>
 * <a href='#buffer8'>[8、矩形缓冲分析]</a><br/>
 * <a href='#buffer9'>[9、圆形缓冲分析]</a><br/>
 * @param {Geometry | Array<Geometry>} [geometry = null] 要进行缓冲区分析的几何对象
 * @param {Number | Array<Number>} [distance = 1000] 缓冲距离
 * @param {LengthUnit} [unit = LengthUnit.meter] 单位,默认米
 * @param {Boolean} [unionResults = false] 是否将返回的多个几何对象合并为单几何对象,默认为false,即不合并几何
 * @return {Geometry | Array<Geometry>} 缓冲区分析后的几何对象
 * @example <caption><h5 id='buffer1'>区缓冲分析</h5></caption>
 * // ES5引入方式
 * const { Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造多边形
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [108.36341, 29.032578],
 *       [116.13094, 29.032578],
 *       [116.13094, 33.273224],
 *       [108.36341, 33.273224],
 *       [108.36341, 29.032578],
 *     ]
 *   ]
 * })
 * // 执行缓冲区分析
 * const bufferedPolygon = GeometryEngine.buffer(polygon, 100000)
 *
 * @example <caption><h5 id='rotate2'>多区缓冲分析</h5></caption>
 * // ES5引入方式
 * const { MultiPolygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { MultiPolygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造多边形
 * const polygon = new MultiPolygon({
 *   coordinates: [
 *     [
 *       [
 *         [108.36341, 29.032578],
 *         [112.13094, 29.032578],
 *         [112.13094, 33.273224],
 *         [108.36341, 33.273224],
 *         [108.36341, 29.032578],
 *       ]
 *     ],
 *     [
 *       [
 *         [113.36341, 29.032578],
 *         [116.13094, 29.032578],
 *         [116.13094, 33.273224],
 *         [113.36341, 33.273224],
 *         [113.36341, 29.032578],
 *       ]
 *     ]
 *   ]
 * })
 * // 执行缓冲区分析
 * const bufferedPolygon = GeometryEngine.buffer(polygon, 30000)
 *
 * @example <caption><h5 id='buffer3'>几何对象数组的缓冲区分析</h5></caption>
 * // ES5引入方式
 * const { Polygon, MultiPolygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, MultiPolygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造多边形
 * const polygon1 = new MultiPolygon({
 *   coordinates: [
 *     [
 *       [
 *         [110.36341, 29.032578],
 *         [112.13094, 29.032578],
 *         [112.13094, 33.273224],
 *         [110.36341, 33.273224],
 *         [110.36341, 29.032578],
 *       ]
 *     ],
 *     [
 *       [
 *         [113.36341, 29.032578],
 *         [116.13094, 29.032578],
 *         [116.13094, 33.273224],
 *         [113.36341, 33.273224],
 *         [113.36341, 29.032578],
 *       ]
 *     ]
 *   ]
 * })
 * // 构造多边形2
 * const polygon2 = new Polygon({
 *   coordinates: [
 *     [
 *       [108.36341, 29.032578],
 *       [109.13094, 29.032578],
 *       [109.13094, 33.273224],
 *       [108.36341, 33.273224],
 *       [108.36341, 29.032578],
 *     ]
 *   ]
 * })
 * // 执行缓冲区分析,distance可谓数字或数组
 * const bufferedPolygon = GeometryEngine.buffer([polygon1, polygon2], [30000, 30000])
 *
 * @example <caption><h5 id='buffer4'>几何数组缓冲区分析 - 分析结果几何求并</h5></caption>
 * // ES5引入方式
 * const { Polygon, MultiPolygon, LengthUnit, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, MultiPolygon, LengthUnit, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造多边形
 * const polygon1 = new MultiPolygon({
 *   coordinates: [
 *     [
 *       [
 *         [110.36341, 29.032578],
 *         [112.13094, 29.032578],
 *         [112.13094, 33.273224],
 *         [110.36341, 33.273224],
 *         [110.36341, 29.032578],
 *       ]
 *     ],
 *     [
 *       [
 *         [113.36341, 29.032578],
 *         [116.13094, 29.032578],
 *         [116.13094, 33.273224],
 *         [113.36341, 33.273224],
 *         [113.36341, 29.032578],
 *       ]
 *     ]
 *   ]
 * })
 * // 构造多边形2
 * const polygon2 = new Polygon({
 *   coordinates: [
 *     [
 *       [108.36341, 29.032578],
 *       [109.13094, 29.032578],
 *       [109.13094, 33.273224],
 *       [108.36341, 33.273224],
 *       [108.36341, 29.032578],
 *     ]
 *   ]
 * })
 * // 执行缓冲区分析,设置unionResults参数为true
 * const bufferedPolygon = GeometryEngine.buffer([polygon1, polygon2], [30000], LengthUnit.meter, true)
 *
 * @example <caption><h5 id='buffer5'>区缓冲分析 - 自定义坐标系</h5></caption>
 * // ES5引入方式
 * const { Polygon, SpatialReference, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, SpatialReference, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造多边形
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [-45257.10778559791, 3212885.1836444484],
 *       [705989.8953363781, 3212885.1836444484],
 *       [705989.8953363781, 3691623.86404564],
 *       [-45257.10778559791, 3691623.86404564],
 *       [-45257.10778559791, 3212885.1836444484],
 *     ]
 *   ],
 *   // 注意要指定坐标系
 *   spatialReference: new SpatialReference({
 *     wkid: 4547
 *   })
 * })
 * // 执行缓冲区分析
 * const bufferedPolygon = GeometryEngine.buffer(polygon, 100000)
 *
 * @example <caption><h5 id='buffer6'>点缓冲分析</h5></caption>
 * // ES5引入方式
 * const { Point, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Point, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造点几何
 * const point = new Point({
 *   coordinates: [108.36341, 29.032578]
 * })
 * // 执行缓冲区分析
 * const bufferedPolygon = GeometryEngine.buffer(point, 100000)
 *
 * @example <caption><h5 id='buffer7'>线缓冲分析</h5></caption>
 * // ES5引入方式
 * const { LineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造线
 * const lineString = new LineString({
 *   coordinates: [
 *     [108.36341, 29.032578],
 *     [109.36341, 29.032578]
 *   ]
 * })
 * // 执行缓冲区分析
 * const bufferedPolygon = GeometryEngine.buffer(lineString, 10000)
 *
 * @example <caption><h5 id='buffer8'>矩形缓冲分析</h5></caption>
 * // ES5引入方式
 * const { LineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造矩形
 * const extent = new Extent({
 *   xmin: 112,
 *   xmax: 114,
 *   ymin: 30,
 *   ymax: 32
 * })
 * // 执行缓冲区分析
 * const bufferedPolygon = GeometryEngine.buffer(extent, 10000)
 *
 * @example <caption><h5 id='buffer9'>圆形缓冲分析</h5></caption>
 * // ES5引入方式
 * const { Circle, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Circle, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造圆形
 * const circle = new Circle({
 *   center: [112, 30],
 *   radius: 3
 * })
 * // 执行缓冲区分析
 * const bufferedPolygon = GeometryEngine.buffer(circle, 10000)
 * */
GeometryEngine.buffer = function (geometry, distance, unit, unionResults) {
  // 设置入参默认值
  unit = defaultValue(unit, LengthUnit.meter)
  unionResults = defaultValue(unionResults, false)
  distance = defaultValue(distance, 1000)

  // 检擦buffer函数的参数
  GeometryEngine._checkBufferOptions(geometry, distance, unit, unionResults)

  // 构造缓冲距离
  let _distance = undefined
  if (geometry instanceof Array) {
    if (distance instanceof Array) {
      if (distance.length < geometry.length) {
        _distance = []
        const _addDistance =
          distance.length > 0 ? distance[distance.length - 1] : 1000
        for (let i = distance.length; i < geometry.length; i++) {
          _distance.push(_addDistance)
        }
      }
    } else {
      _distance = []
      for (let i = 0; i < geometry.length; i++) {
        _distance.push(distance)
      }
    }
  } else if (distance instanceof Array) {
    _distance = distance.length > 0 ? distance[0] : 1000
  } else {
    _distance = distance
  }

  // 如果不是4326,则进行坐标系投影转为4326
  let _geometry = geometry
  // 不是数组,是区或多区几何
  if (!(geometry instanceof Array)) {
    if (geometry.spatialReference.wkid !== 4326) {
      _geometry = Projection.projectGeometry(
        geometry,
        new SpatialReference({
          wkid: 4326
        })
      )
    }
  } else {
    _geometry = []
    for (let i = 0; i < geometry.length; i++) {
      _geometry.push(
        Projection.projectGeometry(
          geometry[i],
          new SpatialReference({
            wkid: 4326
          })
        )
      )
    }
  }

  let _buffers = undefined
  // 是几何对象
  if (!(geometry instanceof Array)) {
    // 执行缓冲区分析
    switch (geometry.type) {
      case GeometryType.point:
      case GeometryType.multiPoint:
      case GeometryType.lineString:
      case GeometryType.multiLineString:
      case GeometryType.polygon:
      case GeometryType.multiPolygon:
        _buffers = GeometryEngine._polygonBuffer(_geometry, _distance, unit)
        break
      case GeometryType.extent:
        const _polygon = new Polygon({
          coordinates: [
            [
              [_geometry.xmin, _geometry.ymin],
              [_geometry.xmax, _geometry.ymin],
              [_geometry.xmax, _geometry.ymax],
              [_geometry.xmin, _geometry.ymax],
              [_geometry.xmin, _geometry.ymin]
            ]
          ]
        })
        _buffers = GeometryEngine._polygonBuffer(_polygon, _distance, unit)
        break
      case GeometryType.circle:
        _buffers = Geometry.fromJSON(geometry)
        _buffers.radius += _distance
        break
      default:
        Log.error('geometry是不支持的几何类型!')
        break
    }
  }
  // 数组类型
  else if (geometry instanceof Array) {
    _buffers = []
    // 如果distance是数组类型,则用最后一个子元素填充满distance数组
    if (distance instanceof Array && distance.length < geometry.length) {
      const _distance = distance[distance.length - 1]
      for (let i = distance.length - 1; i < geometry.length; i++) {
        distance.push(_distance)
      }
    }
    // 根据类型执行缓冲区分析
    for (let i = 0; i < _geometry.length; i++) {
      // 获取distance
      let _distance = 0
      if (distance instanceof Array) {
        _distance = distance[i]
      } else {
        _distance = distance
      }
      _buffers.push(
        GeometryEngine._polygonBuffer(_geometry[i], _distance, unit)
      )
    }

    // 是否合并区几何
    if (unionResults) {
      _buffers = GeometryEngine.union(_buffers)
    }
  }

  // 将坐标系转回原始坐标系
  if (geometry instanceof Array) {
    if (!geometry[0].spatialReference.isGeographic) {
      if (_buffers instanceof Array) {
        for (let i = 0; i < _buffers.length; i++) {
          if (_buffers[i].type !== GeometryType.circle) {
            _buffers[i] = Projection.projectGeometry(
              _buffers[i],
              SpatialReference.fromJSON(geometry[0].spatialReference)
            )
          }
        }
      } else if (_buffers.type !== GeometryType.circle) {
        _buffers = Projection.projectGeometry(
          _buffers,
          SpatialReference.fromJSON(geometry[0].spatialReference)
        )
      }
    }
  } else if (
    !geometry.spatialReference.isGeographic &&
    geometry.type !== GeometryType.circle
  ) {
    _buffers = Projection.projectGeometry(
      _buffers,
      SpatialReference.fromJSON(geometry.spatialReference)
    )
  }

  return _buffers
}

/**
 * 检擦buffer函数的参数
 * @private
 * @param {Polygon | Array<Polygon> | MultiPolygon | Array<MultiPolygon>} geometry 要进行缓冲区分析的区几何对象或多区几何对象
 * @param {Number | Array<Number>} distance 缓冲距离或缓冲距离数组
 * @param {LengthUnit} unit 单位,默认米
 * @param {Boolean} unionResults 是否将返回的多个区几何对象合并为单几何对象
 * */
GeometryEngine._checkBufferOptions = function (
  geometry,
  distance,
  unit,
  unionResults
) {
  // 非空判断
  if (isNull(geometry)) {
    Log.error('geometry不能为空!')
  }
  if (isNull(distance)) {
    Log.error('distance不能为空!')
  }
  if (isNull(geometry)) {
    Log.error('unit不能为空!')
  }
  if (isNull(geometry)) {
    Log.error('unionResults不能为空!')
  }
  // 判断geometry类型
  // 判断是否是Polygon数组、MultiPolygon数组或者这两个的混合数组
  if (geometry instanceof Array) {
    for (let i = 0; i < geometry.length; i++) {
      // 不是上述类型,则报错
      if (
        !(geometry[i] instanceof Polygon || geometry[i] instanceof MultiPolygon)
      ) {
        Log.error('geometry的子元素类型不为Polygon或者MultiPolygon!')
        break
      }
    }
    if (geometry.length === 0) {
      Log.error('geometry的长度为0!')
    }
  }
  // 判断是不是Polygon或者MultiPolygon类型
  else if (!(geometry instanceof Geometry)) {
    Log.error('geometry不是基础几何类型!')
  }
  // 判断distance类型
  // 是数组类型
  if (distance instanceof Array) {
    // 查看geometry是否是数组
    if (!(geometry instanceof Array)) {
      Log.error('distance的类型为数组,但是geometry的类型不是数组!')
    }
    // 检查子元素必须是数字类型
    for (let i = 0; i < distance.length; i++) {
      // 不是数字类型
      if (!(typeof distance[i] === 'number')) {
        Log.error('distance的子元素不是数字类型!')
        break
      }
    }
  }
  // 不是数字类型
  else if (!(typeof distance === 'number')) {
    Log.error('distance的不是数字类型!')
  }
  // 判断unit类型
  // 是字符串类型
  if (typeof unit === 'string') {
    // 是空值
    if (!unit) {
      Log.error('unit的值不能为空!')
    }
    // 判断单位是否在LengthUnit中
    else {
      let isLengthUnit = false
      Object.keys(LengthUnit).forEach(function (key) {
        if (!isLengthUnit && LengthUnit[key] === unit) {
          isLengthUnit = true
        }
      })
      if (!isLengthUnit) {
        Log.error('unit是不支持的单位类型!')
      }
    }
  } else {
    Log.error('unit类型不是字符串!')
  }
  // 判断unionResults类型
  if (!(typeof unionResults === 'boolean')) {
    Log.error('unionResults类型不是布尔值类型!')
  }
}

/**
 * 区几何的缓冲分析
 * @private
 * @param {Polygon} geometry 要进行缓冲区分析的区几何对象
 * @param {Number} distance 缓冲距离
 * @param {LengthUnit} unit 单位,默认米
 * @return {Polygon} 缓冲分析后的新几何对象
 * */
GeometryEngine._polygonBuffer = function (geometry, distance, unit) {
  // 构建turf多边形对象
  let _geometry = undefined
  switch (geometry.type) {
    case GeometryType.point:
      _geometry = T.point(geometry.coordinates)
      break
    case GeometryType.multiPoint:
      _geometry = T.multiPoint(geometry.coordinates)
      break
    case GeometryType.lineString:
      _geometry = T.lineString(geometry.coordinates)
      break
    case GeometryType.multiLineString:
      _geometry = T.multiLineString(geometry.coordinates)
      break
    case GeometryType.polygon:
      _geometry = T.polygon(geometry.coordinates)
      break
    case GeometryType.multiPolygon:
      _geometry = T.multiPolygon(geometry.coordinates)
      break
    default:
      break
  }
  // 执行缓冲区分析
  const _buffered = T.buffer(_geometry, distance, {
    units: unit
  })

  return Geometry.fromJSON({
    type: _buffered.geometry.type,
    coordinates: _buffered.geometry.coordinates
  })
}

/**
 * 多区几何的缓冲分析
 * @private
 * @param {MultiPolygon} geometry 要进行缓冲区分析的多区几何对象
 * @param {Number} distance 缓冲距离
 * @param {LengthUnit} unit 单位,默认米
 * @return {Polygon | MultiPolygon} 缓冲分析后的新几何对象,可能为区或多区对象,缓冲后有可能合并为一个区
 * */
GeometryEngine._multiPolygonBuffer = function (geometry, distance, unit) {
  // 构建turf的多多边形对象
  const multiPoly = T.multiPolygon(geometry.coordinates)
  // 执行缓冲区分析
  const _buffered = T.buffer(multiPoly, distance, {
    units: unit
  })
  // 缓冲分析后是多边形
  if (_buffered.geometry.type === 'Polygon') {
    return new Polygon({
      coordinates: _buffered.geometry.coordinates
    })
  }
  // 缓冲分析后是多多边形
  else {
    return new MultiPolygon({
      coordinates: _buffered.geometry.coordinates
    })
  }
}

/**
 * 单个几何对象的缓冲区分析
 * @private
 * @param {Polygon | MultiPolygon} geometry 要进行缓冲区分析的区几何对象或多区几何对象
 * @param {Number} distance 缓冲距离
 * @param {LengthUnit} unit 单位,默认米
 * */
GeometryEngine._buffer = function (geometry, distance, unit) {
  // 判断是否含有coordinates
  if (!geometry.hasOwnProperty('coordinates')) {
    Log.error('geometry缺少coordinates属性!')
  }
  // 是Polygon类型
  if (geometry instanceof Polygon) {
    // 初始化一个turf的几何对象
    const _polygon = T.polygon(geometry.coordinates)
    // 执行缓冲区分析
    return T.buffer(_polygon, distance, { units: unit })
  }
  // 是多区类型
  else {
  }
}

/**
 * <a id='union'></a>
 * 多个几何对象求并,并返回求并后的新几何对象;<br/>
 * 输入的几何类型必须一致,点和多点、区和多区视为同一个类型,且几何对象的坐标系要一致;<br/>
 * 忽略三维高度;<br>
 * 支持的几何有:点(多点)几何对象、区(多区)几何对象、范围几何对象、圆几何对象<br/>
 * 点和点、点和多点、多点和多点合并为一个多点几何对象,如果有多个点坐标(XYZ)相同,则会去重;<br/>
 * 线和线、线和多线、多线和多线合并为一个线几何对象或多线几何对象;<br/>
 * 区和区、区和多区、多区和多区合并为一个区级和对象或多区几何对象;<br/>
 * 矩形和矩形合并为一个多边形几何对象或一个更大的矩形几何对象;<br/>
 * 圆和圆合并为一个多边形几何对象或一个圆几何对象或两个不相交的圆;
 * @param {Array<Geometry>} geometries 求并几何或几何数组
 * @return { Geometry } 求并后的新几何对象
 * @example <caption><h5>点和点求并</h5></caption>
 * // ES5引入方式
 * const { Point, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, Point } from "@mapgis/webclient-common"
 * // 点和点求并
 * const point1 = new Point({
 *   coordinates: [1, 1]
 * })
 * const point2 = new Point({
 *   coordinates: [2, 1]
 * })
 * const point3 = new Point({
 *   coordinates: [3, 1, 100]
 * })
 * const union = GeometryEngine.union([point1, point2, point3])
 * console.log("点和点求并:", union)
 *
 * @example <caption><h5>点和多点求并</h5></caption>
 * // ES5引入方式
 * const { Point, MultiPoint, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, Point, MultiPoint } from "@mapgis/webclient-common"
 * // 点和多点求并
 * const point1 = new Point({
 *   coordinates: [1, 1]
 * })
 * const point2 = new MultiPoint({
 *   coordinates: [
 *     [2, 1],
 *     [3, 1]
 *   ]
 * })
 * const union = GeometryEngine.union([point1, point2])
 * console.log("点和多点求并:", union)
 *
 * @example <caption><h5>多点和多点求并</h5></caption>
 * // ES5引入方式
 * const { Point, MultiPoint, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, Point, MultiPoint } from "@mapgis/webclient-common"
 * // 多点和多点求并
 * const point1 = new MultiPoint({
 *   coordinates: [
 *     [2, 1],
 *     [3, 1],
 *     [3, 3, 4]
 *   ]
 * })
 * const point2 = new MultiPoint({
 *   coordinates: [
 *     [2, 1],
 *     [3, 1]
 *   ]
 * })
 * const union = GeometryEngine.union([point1, point2])
 * console.log("多点和多点求并:", union)
 *
 * @example <caption><h5>区和区求并</h5></caption>
 * // ES5引入方式
 * const { Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, Polygon } from "@mapgis/webclient-common"
 * // 区和区求并
 * const polygon1 = new Polygon({
 *   coordinates: [
 *     [
 *       [-82.574787, 35.594087],
 *       [-82.574787, 35.615581],
 *       [-82.545261, 35.615581],
 *       [-82.545261, 35.594087],
 *       [-82.574787, 35.594087]
 *     ]
 *   ]
 * })
 * const polygon2 = new Polygon({
 *   coordinates: [
 *     [
 *       [-82.560024, 35.585153],
 *       [-82.560024, 35.602602],
 *       [-82.52964, 35.602602],
 *       [-82.52964, 35.585153],
 *       [-82.560024, 35.585153]
 *     ]
 *   ]
 * })
 * const union = GeometryEngine.union([polygon1, polygon2])
 * console.log("区和区求并:", union)
 *
 * @example <caption><h5>区和带洞区求并</h5></caption>
 * // ES5引入方式
 * const { Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, Polygon } from "@mapgis/webclient-common"
 * // 区和带洞区求并
 * const polygon1 = new Polygon({
 *   coordinates: [
 *     [
 *       [100, 30],
 *       [110, 30],
 *       [110, 40],
 *       [100, 40],
 *       [100, 30]
 *     ]
 *   ]
 * })
 * const polygon2 = new Polygon({
 *   coordinates: [
 *     [
 *       [105, 30],
 *       [120, 30],
 *       [120, 40],
 *       [105, 40],
 *       [105, 30]
 *     ],
 *     [
 *       [108, 35],
 *       [116, 35],
 *       [116, 36],
 *       [108, 36],
 *       [108, 35]
 *     ]
 *   ]
 * })
 * const union = GeometryEngine.union([polygon1, polygon2])
 * console.log("区和带洞区求并:", union)
 *
 * @example <caption><h5>带洞区和带洞区的求并</h5></caption>
 * // ES5引入方式
 * const { Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, Polygon } from "@mapgis/webclient-common"
 * // 带洞区和带洞区的求并
 * const polygon1 = new Polygon({
 *    coordinates: [
 *      [
 *        [100, 30],
 *        [110, 30],
 *        [110, 40],
 *        [100, 40],
 *        [100, 30]
 *      ],
 *      [
 *        [103, 35],
 *        [106, 35],
 *        [106, 36],
 *        [103, 36],
 *        [103, 35]
 *      ]
 *    ]
 *  })
 *  const polygon2 = new Polygon({
 *    coordinates: [
 *      [
 *        [105, 30],
 *        [120, 30],
 *        [120, 40],
 *        [105, 40],
 *        [105, 30]
 *      ],
 *      [
 *        [108, 35],
 *        [116, 35],
 *        [116, 36],
 *        [108, 36],
 *        [108, 35]
 *      ]
 *    ]
 *  })
 *  const union = GeometryEngine.union([polygon1, polygon2])
 *  console.log("带洞区和带洞区的求并:", union)
 * */
GeometryEngine.union = function (geometries) {
  geometries = defaultValue(geometries, [])

  // 判断是否为数组类型
  if (!(geometries instanceof Array)) {
    Log.error('geometries必须为数组类型!')
  }

  if (geometries.length === 0) return []

  // 判断是否类型相同
  let _isSameType = true
  // 判断坐标系是否相同
  let _isSameSP = true

  // 求并的几何类型
  const unionType = geometries[0].type

  // 数量为一的话一定是相同的类型
  if (geometries.length > 1) {
    // 判断是否类型相同
    switch (geometries[0].type) {
      case GeometryType.polygon:
      case GeometryType.multiPolygon:
        for (let i = 1; i < geometries.length; i++) {
          if (
            geometries[i].type !== GeometryType.polygon &&
            geometries[i].type !== GeometryType.multiPolygon
          ) {
            _isSameType = false
            break
          }
        }
        break
      case GeometryType.point:
      case GeometryType.multiPoint:
        for (let i = 1; i < geometries.length; i++) {
          if (
            geometries[i].type !== GeometryType.point &&
            geometries[i].type !== GeometryType.multiPoint
          ) {
            _isSameType = false
            break
          }
        }
        break
      case GeometryType.circle:
        for (let i = 1; i < geometries.length; i++) {
          if (geometries[i].type !== GeometryType.circle) {
            _isSameType = false
            break
          }
        }
        break
      case GeometryType.extent:
        for (let i = 1; i < geometries.length; i++) {
          if (geometries[i].type !== GeometryType.extent) {
            _isSameType = false
            break
          }
        }
        break
      case GeometryType.lineString:
      case GeometryType.multiLineString:
        for (let i = 1; i < geometries.length; i++) {
          if (
            geometries[i].type !== GeometryType.lineString &&
            geometries[i].type !== GeometryType.multiLineString
          ) {
            _isSameType = false
            break
          }
        }
        break
      // 不支持的几何类型
      default:
        Log.error(`geometries的子元素中有求并方法不支持的数据类型!`)
        break
    }

    // 几何数组的类型不同
    if (!_isSameType) {
      Log.error('几何数组的类型不同!')
    }

    // 判断坐标系是否相同
    const _firstSP = geometries[0].spatialReference
    for (let i = 1; i < geometries.length; i++) {
      if (!_firstSP.equals(geometries[i].spatialReference)) {
        _isSameSP = false
      }
    }
    // 坐标系不同
    if (!_isSameSP) {
      Log.error('几何数组的坐标系不同!')
    }

    // 根据第一个几何的类型开始求并
    let unionGeometry = undefined
    let _polygons = []
    switch (unionType) {
      case GeometryType.point:
      case GeometryType.multiPoint:
        // 获取所有点或多点的点坐标
        let _pointCoords = []
        for (let i = 0; i < geometries.length; i++) {
          if (geometries[i] instanceof Point) {
            _pointCoords.push(geometries[i].coordinates)
          } else if (geometries[i] instanceof MultiPoint) {
            _pointCoords = _pointCoords.concat(geometries[i].coordinates)
          }
        }
        if (_pointCoords.length === 1) {
          unionGeometry = new Point({
            coordinates: _pointCoords[0],
            spatialReference: SpatialReference.fromJSON(
              geometries[0].spatialReference
            )
          })
        } else {
          let _multiPoint = T.multiPoint(_pointCoords)
          _multiPoint = T.cleanCoords(_multiPoint)
          unionGeometry = new MultiPoint({
            coordinates: _multiPoint.geometry.coordinates,
            spatialReference: SpatialReference.fromJSON(
              geometries[0].spatialReference
            )
          })
        }
        break
      case GeometryType.polygon:
      case GeometryType.multiPolygon:
        let _geometry = undefined
        // 先计算前两个区或多区的求并结果
        const _polygon1 =
          geometries[0].type === GeometryType.polygon
            ? T.polygon(geometries[0].coordinates)
            : T.multiPolygon(geometries[0].coordinates)
        const _polygon2 =
          geometries[1].type === GeometryType.polygon
            ? T.polygon(geometries[1].coordinates)
            : T.multiPolygon(geometries[1].coordinates)
        _geometry = T.union(_polygon1, _polygon2)

        // 如果后面还有,则继续合并
        for (let i = 2; i < geometries.length; i++) {
          const _polygon =
            geometries[i].type === GeometryType.polygon
              ? T.polygon(geometries[i].coordinates)
              : T.multiPolygon(geometries[i].coordinates)
          _geometry = T.union(_geometry.geometry, _polygon)
        }

        // 返回区几何或多区几何
        if (_geometry.geometry.type === GeometryType.polygon) {
          unionGeometry = new Polygon({
            coordinates: _geometry.geometry.coordinates,
            spatialReference: SpatialReference.fromJSON(
              geometries[0].spatialReference
            )
          })
        } else {
          unionGeometry = new MultiPolygon({
            coordinates: _geometry.geometry.coordinates,
            spatialReference: SpatialReference.fromJSON(
              geometries[0].spatialReference
            )
          })
        }
        break
      case GeometryType.lineString:
      case GeometryType.multiLineString:
        let _lineCoords = []
        for (let i = 0; i < geometries.length; i++) {
          if (geometries[i] instanceof LineString) {
            _lineCoords.push(geometries[i].coordinates)
          } else {
            _lineCoords = _lineCoords.concat(geometries[i].coordinates)
          }
        }
        if (_lineCoords.length === 1) {
          unionGeometry = new LineString({
            coordinates: _lineCoords[0],
            spatialReference: SpatialReference.fromJSON(
              geometries[0].spatialReference
            )
          })
        } else {
          unionGeometry = new MultiLineString({
            coordinates: _lineCoords,
            spatialReference: SpatialReference.fromJSON(
              geometries[0].spatialReference
            )
          })
        }
        break
      case GeometryType.extent:
        _polygons = []
        for (let i = 0; i < geometries.length; i++) {
          const _polygon = new Polygon({
            coordinates: [
              [
                [geometries[i].xmin, geometries[i].ymin],
                [geometries[i].xmax, geometries[i].ymin],
                [geometries[i].xmax, geometries[i].ymax],
                [geometries[i].xmin, geometries[i].ymax],
                [geometries[i].xmin, geometries[i].ymin]
              ]
            ],
            spatialReference: SpatialReference.fromJSON(
              geometries[0].spatialReference
            )
          })
          _polygons.push(_polygon)
        }
        unionGeometry = GeometryEngine.union(_polygons)
        break
      case GeometryType.circle:
        _polygons = []
        for (let i = 0; i < geometries.length; i++) {
          _polygons.push(geometries[i].toPolygon())
        }
        unionGeometry = GeometryEngine.union(_polygons)
        break
      default:
        break
    }

    return unionGeometry
  } else if (geometries.length === 1) {
    return geometries[0]
  }
}

/**
 * <a id='intersects'></a>
 * 判断两个几何对象是否相交;<br/>
 * 支持任意几何类型求交;<br/>
 * 支持自定义坐标系的几何对象,两个求交几何的坐标系必须相同,忽略三位高度;<br/>
 * 由于示例太多,这里仅列举部分示例,示例如下:<br/>
 * <a href='#intersects1'>[1、线和区求交]</a><br/>
 * <a href='#intersects2'>[2、点和区求交]</a><br/>
 * <a href='#intersects3'>[3、区和多区求交]</a><br/>
 * <a href='#intersects4'>[4、矩形和圆形求交]</a><br/>
 * <a href='#intersects5'>[5、几何求交 - 自定义参考系]</a><br/>
 * @param {Geometry} [geometry1 = null] 求交的几何对象
 * @param {Geometry} [geometry2 = null] 求交的几何对象
 * @return {Boolean} 是否相交
 *
 * @example <caption><h5 id='intersects1'>线和区求交</h5></caption>
 * // ES5引入方式
 * const { LineString, Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, Polygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造线几何对象
 * const line = new LineString({
 *   coordinates: [
 *     [113, 28],
 *     [113, 34]
 *   ]
 * })
 * // 构造区几何对象
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [108, 30],
 *       [115, 30],
 *       [115, 33],
 *       [108, 33],
 *       [108, 30]
 *     ],
 *     [
 *       [110, 31],
 *       [113, 31],
 *       [113, 32],
 *       [110, 32],
 *       [110, 31]
 *     ]
 *   ]
 * })
 * // 开始求交
 * const isIntersect = GeometryEngine.intersects(line, polygon)
 * console.log("是否相交:", isIntersect)
 *
 * @example <caption><h5 id='intersects2'>点和区求交</h5></caption>
 * // ES5引入方式
 * const { Point, Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Point, Polygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造点几何对象
 * const point = new Point({
 *   coordinates: [112, 32]
 * })
 * // 构造区几何对象
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [108, 30],
 *       [115, 30],
 *       [115, 33],
 *       [108, 33],
 *       [108, 30]
 *     ]
 *   ]
 * })
 * // 开始求交
 * const isIntersect = GeometryEngine.intersects(point, polygon)
 * console.log("是否相交:", isIntersect)
 *
 * @example <caption><h5 id='intersects3'>区和多区求交</h5></caption>
 * // ES5引入方式
 * const { Polygon, MultiPolygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, MultiPolygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造区几何对象
 * const polygon1 = new Polygon({
 *   coordinates: [
 *     [
 *       [108, 32],
 *       [112, 32],
 *       [112, 34],
 *       [108, 34],
 *       [108, 32]
 *     ]
 *   ]
 * })
 * // 构造多区几何对象
 * const polygon2 = new MultiPolygon({
 *   coordinates: [
 *     [
 *       [
 *         [108, 33],
 *         [112, 33],
 *         [112, 35],
 *         [108, 35],
 *         [108, 33]
 *       ]
 *     ],
 *     [
 *       [
 *         [114, 32],
 *         [115, 32],
 *         [115, 34],
 *         [114, 34],
 *         [114, 32]
 *       ]
 *     ]
 *   ]
 * })
 * // 开始求交
 * const isIntersect = GeometryEngine.intersects(polygon1, polygon2)
 * console.log("是否相交:", isIntersect)
 *
 * @example <caption><h5 id='intersects4'>矩形和圆形求交</h5></caption>
 * // ES5引入方式
 * const { Extent, Circle, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Extent, Circle, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造矩形几何对象
 * const extent = new Extent({
 *   xmin: 108,
 *   ymin: 33,
 *   xmax: 115,
 *   ymax: 34
 * })
 * // 构造圆形几何对象
 * const circle = new Circle({
 *   center: [113, 32],
 *   radius: 4
 * })
 * // 开始求交
 * const isIntersect = GeometryEngine.intersects(extent, circle)
 * console.log("是否相交:", isIntersect)
 *
 * @example <caption><h5 id='intersects5'>几何求交 - 自定义参考系</h5></caption>
 * // ES5引入方式
 * const { Polygon, Circle, SpatialReference, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, Circle, SpatialReference, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造区几何对象
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [0, 3226025],
 *       [698545, 3226025],
 *       [698545, 3685076],
 *       [0, 3685076],
 *       [0, 3226025]
 *     ],
 *     [
 *       [100000, 3326025],
 *       [598545, 3326025],
 *       [598545, 3485076],
 *       [100000, 3485076],
 *       [100000, 3326025]
 *     ]
 *   ],
 *   // 此处要指定坐标系
 *   spatialReference: new SpatialReference({
 *     wkid: 4547
 *   })
 * })
 * // 构造圆几何对象
 * const circle = new Circle({
 *   center: [245257.10778559791, 3212885.1836444484],
 *   radius: 400000,
 *   // 此处要指定坐标系
 *   spatialReference: new SpatialReference({
 *     wkid: 4547
 *   })
 * })
 * // 开始求交
 * const isIntersect = GeometryEngine.intersects(circle, polygon)
 * console.log("是否相交:", isIntersect)
 * */
GeometryEngine.intersects = function (geometry1, geometry2) {
  // 值必须存在,且继承自Geometry对象
  GeometryEngine._checkTwoGeometry(geometry1, geometry2)

  // 根据几何类型转为turf几何对象
  const _geometry1 = GeometryEngine._geometryToTurf(geometry1)
  const _geometry2 = GeometryEngine._geometryToTurf(geometry2)

  // 返回是否相交
  return !T.booleanDisjoint(_geometry1, _geometry2)
}

/**
 * <a id='geodesicLength'></a>
 * 计算地理几何长度;<br/>
 * @param {LineString} [geometry = null] 几何对象
 * @param {LengthUnit} [unit = LengthUnit.meter] 单位,默认米
 * @return {Number} 几何对象的长度
 */
GeometryEngine.geodesicLength = function (geometry, unit) {
  // 输入格式判断
  if (isNull(geometry)) {
    Log.error('geometry对象不能为空!')
  }

  // 判断类型
  if (!(geometry instanceof LineString)) {
    Log.error('geometry类型不正确!')
  }

  unit = defaultValue(unit, LengthUnit.meter)
  // 判断类型
  if (!(typeof unit === 'string')) {
    Log.error('unit类型不正确!')
  }
  let _length = 0
  switch (geometry.type) {
    case GeometryType.lineString:
      const lineT = T.lineString(geometry.coordinates)
      _length = 1000 * T.length(lineT, { units: 'kilometers' })
      break
    default:
      break
  }
  // 处理单位
  // 当非经纬度坐标系时生效
  if (!geometry.spatialReference.isGeographic) {
    switch (unit) {
      case LengthUnit.kilometer:
        _length /= 1000
        break
      case LengthUnit.mile:
        _length *= 0.000621371192237
        break
      default:
        break
    }
  }
  return _length
}
/**
 * 基础几何对象转为turf几何对象
 * @private
 * @param {Geometry} geometry 基础几何对象
 * @return {Object} turf几何对象
 * */
GeometryEngine._geometryToTurf = function (geometry) {
  let coordinates = undefined
  if (
    geometry.type !== GeometryType.extent &&
    geometry.type !== GeometryType.circle
  ) {
    coordinates = JSON.parse(JSON.stringify(geometry.coordinates))
  }
  // 判断geometry的类型
  switch (geometry.type) {
    case GeometryType.point:
      return T.point(coordinates)
    case GeometryType.multiPoint:
      return T.multiPoint(coordinates)
    case GeometryType.lineString:
      return T.lineString(coordinates)
    case GeometryType.multiLineString:
      return T.multiLineString(coordinates)
    case GeometryType.polygon:
      return T.polygon(coordinates)
    case GeometryType.multiPolygon:
      return T.multiPolygon(coordinates)
    case GeometryType.extent:
      return T.polygon([
        [
          [geometry.xmin, geometry.ymin],
          [geometry.xmax, geometry.ymin],
          [geometry.xmax, geometry.ymax],
          [geometry.xmin, geometry.ymax],
          [geometry.xmin, geometry.ymin]
        ]
      ])
    case GeometryType.circle:
      return T.polygon(geometry.toPolygon().coordinates)
    default:
      Log.error('geometry为不支持的几何类型!')
      break
  }
}

/**
 * 判断线是否包含点
 * @private
 * @param {LineString} lineString 线几何对象
 * @param {Point} point 点几何对象
 * @return {Boolean} 是否包含
 * */
GeometryEngine._lineContainPoint = function (lineString, point) {
  // 构造turf对象
  const _point = T.point(point.coordinates)
  const _lineString = T.lineString(lineString.coordinates)
  let _isContain = T.booleanContains(_lineString, _point)
  // turf值判断是否在线上,还要判断是否包含点坐标
  if (!_isContain) {
    for (let i = 0; i < lineString.coordinates.length; i++) {
      const point2 = new Point({
        coordinates: lineString.coordinates[i],
        spatialReference: SpatialReference.fromJSON(lineString.spatialReference)
      })
      if (GeometryEngine.equals(point, point2)) {
        _isContain = true
        break
      }
    }
  }

  return _isContain
}

/**
 * 判断多线是否包含点
 * @private
 * @param {MultiLineString} containerGeometry 多线几何对象
 * @param {Point} insideGeometry 点几何对象
 * @return {Boolean} 是否包含
 * */
GeometryEngine._multiLineContainPoint = function (
  containerGeometry,
  insideGeometry
) {
  let _isContains = false
  for (let i = 0; i < containerGeometry.coordinates.length; i++) {
    const _line = new LineString({
      coordinates: containerGeometry.coordinates[i],
      spatialReference: SpatialReference.fromJSON(
        containerGeometry.spatialReference
      )
    })
    if (GeometryEngine._lineContainPoint(_line, insideGeometry)) {
      _isContains = true
      break
    }
  }
  return _isContains
}

/**
 * 判断区是否包含点,包含外圈的边界和内圈的边界
 * @private
 * @param {Polygon} polygon 区几何对象
 * @param {Point} point 点几何对象
 * @return {Boolean} 是否包含
 * */
GeometryEngine._polygonContainPoint = function (polygon, point) {
  let _isContains = false
  // 构造turf对象
  const _point = T.point(point.coordinates)
  const _polygon = T.polygon(polygon.coordinates)
  _isContains = T.booleanContains(_polygon, _point)
  return _isContains
}

/**
 * 判断区是否和线相交,要考虑内圈
 * @private
 * */
GeometryEngine._polygonContainLine = function (polygon, line) {
  // 构造turf对象
  const _outerPolygon = T.polygon(polygon.coordinates)
  const _line = T.lineString(line.coordinates)
  return T.booleanContains(_outerPolygon, _line)
}

/**
 * 判断多点是否包含点
 * @private
 * @param {MultiPoint} multiPoint 区几何对象
 * @param {Point} point 点几何对象
 * @return {Boolean} 是否包含
 * */
GeometryEngine._multiPointContainsPoint = function (multiPoint, point) {
  // 构造turf对象
  const _point = T.point(point.coordinates)
  const multiPt = T.multiPoint(multiPoint.coordinates)
  return T.booleanContains(multiPt, _point)
}

/**
 * 判断线是否包含多点
 * @private
 * @param {LineString} line 线几何对象
 * @param {MultiPoint} multiPoint 多点几何对象
 * @return {Boolean} 是否包含
 * */
GeometryEngine._lineContainMultiPoint = function (line, multiPoint) {
  let _isContains = true
  // 判断第一个几何是否完全包含第二个几何的点坐标
  for (let i = 0; i < multiPoint.coordinates.length; i++) {
    const _point = new Point({
      coordinates: multiPoint.coordinates[i]
    })
    _isContains = GeometryEngine._lineContainPoint(line, _point)
    if (!_isContains) {
      break
    }
  }
  return _isContains
}

GeometryEngine._polygonContainMultiPoint = function (polygon, multiPoint) {
  let _isContains = true
  // 判断第一个几何是否完全包含第二个几何的点坐标
  for (let i = 0; i < multiPoint.coordinates.length; i++) {
    const _point = new Point({
      coordinates: multiPoint.coordinates[i]
    })
    _isContains = GeometryEngine._polygonContainPoint(polygon, _point)
    if (!_isContains) {
      break
    }
  }
  return _isContains
}

/**
 * 检查两个geometry入参是否非空且继承自Geometry对象
 * @private
 * @param {Geometry} geometry1 第一个几何对象
 * @param {Geometry} geometry2 第二个几何对象
 * */
GeometryEngine._checkTwoGeometry = function (geometry1, geometry2) {
  // 值必须存在,且继承自Geometry对象
  if (isNull(geometry1)) {
    Log.error('geometry1对象为空!')
  }
  if (isNull(geometry2)) {
    Log.error('geometry2对象为空!')
  }
  if (!(geometry1 instanceof Geometry)) {
    Log.error('geometry1对象未继承于Geometry对象!')
  }
  if (!(geometry2 instanceof Geometry)) {
    Log.error('geometry2对象未继承于Geometry对象!')
  }
  if (!geometry1.spatialReference.equals(geometry2.spatialReference)) {
    Log.error('geometry1和geometry2的坐标系不相同!')
  }
}
/**
 * <a id='intersect'></a>
 * 计算并返回两个几何对象的相交部分;<br/>
 * 支持任意几何或几何对象数组和另一个几何进行相交,忽略三维高度,坐标系必须相同;<br/>
 * 点和任何几何相交返回点或空对象<br/>
 * 多点和任何几何相交返回点或多点或空对象<br/>
 * 线和任意几何对象相交返回相交点或重叠线或相交点和重叠线或空对象<br/>
 * 区和区、区和多区相交返回区或多区或空对象<br/>
 * 矩形、圆、区、多区之间相交返回区或多区或空对象<br/>
 * @param {Geometry | Array<Geometry>} geometry1 第一个几何对象或几何对象数组
 * @param {Geometry} geometry2 第二个几何对象
 * @return {Geometry | Array<Geometry>} 相交部分的几何对象或几何对象数组
 * */
GeometryEngine.intersect = function (geometry1, geometry2) {
  // 非空判断
  if (isNull(geometry1)) {
    Log.error('geometry1不能为空!')
  }
  if (isNull(geometry2)) {
    Log.error('geometry2不能为空!')
  }
  // 类型判断
  if (!(geometry1 instanceof Geometry || geometry1 instanceof Array)) {
    Log.error('geometry1必须为几何对象或者数组对象!')
  }
  if (!(geometry2 instanceof Geometry)) {
    Log.error('geometry2必须为几何对象或者数组对象!')
  }

  let _geometry = undefined

  // 根据类型进行数据处理
  // 是数组
  if (geometry1 instanceof Array) {
    _geometry = []
    for (let i = 0; i < geometry1.length; i++) {
      _geometry.push(GeometryEngine.intersect(geometry1[i], geometry2))
    }
    return _geometry
  }
  // 不是数组
  else {
    switch (geometry1.type) {
      // 点和其他几何相交
      case GeometryType.point:
        _geometry = GeometryEngine._pointIntersectGeometry(geometry1, geometry2)
        break
      // 多点和其他几何相交
      case GeometryType.multiPoint:
        _geometry = GeometryEngine._multiPointIntersectGeometry(
          geometry1,
          geometry2
        )
        break
      // 线和其他几何相交
      case GeometryType.lineString:
        switch (geometry2.type) {
          case GeometryType.point:
            if (GeometryEngine.contains(geometry1, geometry2)) {
              _geometry = new Point({
                coordinates: geometry2.coordinates
              })
            }
            break
          case GeometryType.multiPoint:
            _geometry = GeometryEngine._lineIntersectMultiPoint(
              geometry1,
              geometry2
            )
            break
          case GeometryType.polygon:
            _geometry = GeometryEngine._lineIntersectPolygon(
              geometry1,
              geometry2
            )
            break
          case GeometryType.multiPolygon:
            _geometry = GeometryEngine._lineIntersectMultiPolygon(
              geometry1,
              geometry2
            )
            break
          case GeometryType.lineString:
          case GeometryType.multiLineString:
            _geometry = GeometryEngine._lineIntersectLine(geometry1, geometry2)
            break
          case GeometryType.extent:
            _geometry = GeometryEngine._lineIntersectExtent(
              geometry1,
              geometry2
            )
            break
          case GeometryType.circle:
            _geometry = GeometryEngine._lineIntersectPolygon(
              geometry1,
              geometry2.toPolygon()
            )
            break
          default:
            break
        }
        break
      // 区和其他几何相交
      case GeometryType.polygon:
        switch (geometry2.type) {
          case GeometryType.point:
            _geometry = GeometryEngine._pointIntersectGeometry(
              geometry2,
              geometry1
            )
            break
          case GeometryType.multiPoint:
            _geometry = GeometryEngine._multiPointIntersectGeometry(
              geometry2,
              geometry1
            )
            break
          case GeometryType.lineString:
            _geometry = GeometryEngine._lineIntersectPolygon(
              geometry2,
              geometry1
            )
            break
          case GeometryType.multiLineString:
            _geometry = GeometryEngine._multiLineIntersectPolygon(
              geometry2,
              geometry1
            )
            break
          case GeometryType.polygon:
            _geometry = GeometryEngine._polygonIntersectPolygon(
              geometry1,
              geometry2
            )
            break
          case GeometryType.multiPolygon:
            _geometry = GeometryEngine._polygonIntersectMultiPolygon(
              geometry1,
              geometry2
            )
            break
          case GeometryType.circle:
            _geometry = GeometryEngine._polygonIntersectPolygon(
              geometry1,
              geometry2.toPolygon()
            )
            break
          case GeometryType.extent:
            _geometry = GeometryEngine._polygonIntersectExtent(
              geometry1,
              geometry2
            )
            break
          default:
            break
        }
        break
      // 多区和其他几何相交
      case GeometryType.multiPolygon:
        switch (geometry2.type) {
          case GeometryType.point:
            _geometry = GeometryEngine._pointIntersectGeometry(
              geometry2,
              geometry1
            )
            break
          case GeometryType.multiPoint:
            _geometry = GeometryEngine._multiPointIntersectGeometry(
              geometry2,
              geometry1
            )
            break
          case GeometryType.lineString:
            _geometry = GeometryEngine._lineIntersectMultiPolygon(
              geometry2,
              geometry1
            )
            break
          case GeometryType.multiLineString:
            _geometry = GeometryEngine._multiLineIntersectMultiPolygon(
              geometry2,
              geometry1
            )
            break
          case GeometryType.polygon:
            _geometry = GeometryEngine._polygonIntersectMultiPolygon(
              geometry2,
              geometry1
            )
            break
          case GeometryType.multiPolygon:
            _geometry = GeometryEngine._multiPolygonIntersectMultiPolygon(
              geometry1,
              geometry2
            )
            break
          case GeometryType.extent:
            _geometry = GeometryEngine._multiPolygonIntersectExtent(
              geometry1,
              geometry2
            )
            break
          case GeometryType.circle:
            _geometry = GeometryEngine._polygonIntersectMultiPolygon(
              geometry2.toPolygon(),
              geometry1
            )
            break
          default:
            break
        }
        break
      // 矩形和其他几何相交
      case GeometryType.extent:
        switch (geometry2.type) {
          case GeometryType.point:
            _geometry = GeometryEngine._pointIntersectGeometry(
              geometry2,
              geometry1
            )
            break
          case GeometryType.multiPoint:
            _geometry = GeometryEngine._multiPointIntersectGeometry(
              geometry2,
              geometry1
            )
            break
          case GeometryType.lineString:
            _geometry = GeometryEngine._lineIntersectExtent(
              geometry2,
              geometry1
            )
            break
          case GeometryType.multiLineString:
            _geometry = GeometryEngine._multiLineIntersectExtent(
              geometry2,
              geometry1
            )
            break
          case GeometryType.polygon:
            _geometry = GeometryEngine._polygonIntersectExtent(
              geometry2,
              geometry1
            )
            break
          case GeometryType.multiPolygon:
            _geometry = GeometryEngine._multiPolygonIntersectExtent(
              geometry2,
              geometry1
            )
            break
          case GeometryType.extent:
            const _polygon = new Polygon({
              coordinates: [
                [
                  [geometry2.xmin, geometry2.ymin],
                  [geometry2.xmax, geometry2.ymin],
                  [geometry2.xmax, geometry2.ymax],
                  [geometry2.xmin, geometry2.ymax],
                  [geometry2.xmin, geometry2.ymin]
                ]
              ]
            })
            _geometry = GeometryEngine._polygonIntersectExtent(
              _polygon,
              geometry1
            )
            break
          case GeometryType.circle:
            const _extent = new Polygon({
              coordinates: [
                [
                  [geometry1.xmin, geometry1.ymin],
                  [geometry1.xmax, geometry1.ymin],
                  [geometry1.xmax, geometry1.ymax],
                  [geometry1.xmin, geometry1.ymax],
                  [geometry1.xmin, geometry1.ymin]
                ]
              ]
            })
            _geometry = GeometryEngine._polygonIntersectPolygon(
              _extent,
              geometry2.toPolygon()
            )
            break
          default:
            break
        }
        break
      // 圆和其他几何相交
      case GeometryType.circle:
        const _circle = geometry1.toPolygon()
        switch (geometry2.type) {
          case GeometryType.point:
            _geometry = GeometryEngine._pointIntersectGeometry(
              geometry2,
              _circle
            )
            break
          case GeometryType.multiPoint:
            _geometry = GeometryEngine._multiPointIntersectGeometry(
              geometry2,
              _circle
            )
            break
          case GeometryType.lineString:
            _geometry = GeometryEngine._lineIntersectPolygon(geometry2, _circle)
            break
          case GeometryType.multiLineString:
            _geometry = GeometryEngine._multiLineIntersectPolygon(
              geometry2,
              _circle
            )
            break
          case GeometryType.polygon:
            _geometry = GeometryEngine._polygonIntersectPolygon(
              geometry2,
              _circle
            )
            break
          case GeometryType.multiPolygon:
            _geometry = GeometryEngine._polygonIntersectMultiPolygon(
              _circle,
              geometry2
            )
            break
          case GeometryType.circle:
            _geometry = GeometryEngine._polygonIntersectPolygon(
              geometry2.toPolygon(),
              _circle
            )
            break
          case GeometryType.extent:
            _geometry = GeometryEngine._polygonIntersectExtent(
              _circle,
              geometry2
            )
            break
          default:
            break
        }
        break
      // 多线和其他几何相交
      case GeometryType.multiLineString:
        switch (geometry2.type) {
          case GeometryType.point:
            _geometry = GeometryEngine._pointIntersectGeometry(
              geometry2,
              geometry1
            )
            break
          case GeometryType.multiPoint:
            _geometry = GeometryEngine._multiPointIntersectGeometry(
              geometry2,
              geometry1
            )
            break
          case GeometryType.lineString:
            _geometry = GeometryEngine._lineIntersectLine(geometry2, geometry1)
            break
          case GeometryType.multiLineString:
            const _points = []
            const _lines = []
            for (let i = 0; i < geometry1.coordinates.length; i++) {
              const _line = new LineString({
                coordinates: geometry1.coordinates[i]
              })
              const _intersectLine = GeometryEngine._lineIntersectLine(
                _line,
                geometry2
              )
              if (_intersectLine && _intersectLine.point) {
                _points.push(_intersectLine.point)
              }
              if (_intersectLine && _intersectLine.lineString) {
                _lines.push(_intersectLine.lineString)
              }
            }
            _geometry = {
              point: undefined,
              lineString: undefined
            }
            _geometry.point = GeometryEngine.union(_points)
            _geometry.lineString = GeometryEngine.union(_lines)
            break
          case GeometryType.polygon:
            _geometry = GeometryEngine._multiLineIntersectPolygon(
              geometry1,
              geometry2
            )
            break
          case GeometryType.multiPolygon:
            _geometry = GeometryEngine._multiLineIntersectMultiPolygon(
              geometry1,
              geometry2
            )
            break
          default:
            break
        }
        break
      default:
        break
    }
    return _geometry
  }
}

/**
 * 线和线相交,返回相交点或重叠线或相交点和重叠线
 * @private
 * @param {LineString} geometry1 线几何对象
 * @param {LineString} geometry2 线几何对象
 * @return {Object} 交点或重叠线或相交点和重叠线
 * */
GeometryEngine._lineIntersectLine = function (geometry1, geometry2) {
  // 根据类型转化为turf对象
  let line1 = undefined
  let line2 = undefined
  if (geometry1.type === GeometryType.lineString) {
    line1 = T.lineString(geometry1.coordinates)
  } else {
    line1 = T.multiLineString(geometry1.coordinates)
  }
  if (geometry2.type === GeometryType.lineString) {
    line2 = T.lineString(geometry2.coordinates)
  } else {
    line2 = T.multiLineString(geometry2.coordinates)
  }

  // 计算基础几何
  let _baseLine1 = undefined
  let _baseLine2 = undefined
  if (geometry1.type === GeometryType.lineString) {
    _baseLine1 = [geometry1]
  } else {
    _baseLine1 = []
    for (let i = 0; i < geometry1.coordinates.length; i++) {
      _baseLine1.push(
        new LineString({
          coordinates: geometry1.coordinates[i]
        })
      )
    }
  }
  if (geometry2.type === GeometryType.lineString) {
    _baseLine2 = [geometry2]
  } else {
    _baseLine2 = []
    for (let i = 0; i < geometry2.coordinates.length; i++) {
      _baseLine2.push(
        new LineString({
          coordinates: geometry2.coordinates[i]
        })
      )
    }
  }

  // 计算重叠线
  const _overLines = []
  for (let i = 0; i < _baseLine1.length; i++) {
    for (let j = 0; j < _baseLine2.length; j++) {
      const _l1 = T.lineString(_baseLine1[i].coordinates)
      const _l2 = T.lineString(_baseLine2[j].coordinates)
      const _overlap = T.lineOverlap(_l1, _l2)
      if (_overlap.features.length > 0) {
        for (let k = 0; k < _overlap.features.length; k++) {
          _overLines.push(
            new LineString({
              coordinates: _overlap.features[k].geometry.coordinates
            })
          )
        }
      }
    }
  }
  // 计算相交点
  const _intersectPoints = T.lineIntersect(line1, line2)
  // 构造返回的对象
  const _geometry = {
    point: undefined,
    lineString: undefined
  }
  // 如果只有一条线,则返回单线
  if (_overLines.length === 1) {
    _geometry.lineString = new LineString({
      coordinates: _overLines[0].coordinates
    })
  }
  // 否则返回多线
  else if (_overLines.length > 1) {
    _geometry.lineString = GeometryEngine.union(_overLines)
  }
  // 删除在重叠线上的点
  for (let i = 0; i < _intersectPoints.features.length; i++) {
    const _intersectPoint = new Point({
      coordinates: _intersectPoints.features[i].geometry.coordinates
    })
    if (
      _geometry.lineString &&
      GeometryEngine.contains(_geometry.lineString, _intersectPoint)
    ) {
      _intersectPoints.features.splice(i, 1)
    }
  }
  // 如果只有一个点,则返回单点
  if (_intersectPoints.features.length === 1) {
    _geometry.point = new Point({
      coordinates: _intersectPoints.features[0].geometry.coordinates
    })
  }
  // 否则返回多点
  else if (_intersectPoints.features.length > 1) {
    const _pointCoords = []
    for (let i = 0; i < _intersectPoints.features.length; i++) {
      _pointCoords.push(_intersectPoints.features[i].geometry.coordinates)
    }
    _geometry.point = new MultiPoint({
      coordinates: _pointCoords
    })
  }
  return _geometry
}

/**
 * 计算线和多点相交
 * @private
 * @param {LineString} geometry1 线几何对象
 * @param {MultiPoint} geometry2 多点几何对象
 * @return {Point | MultiPoint} 相交的点或多点对象
 * */
GeometryEngine._lineIntersectMultiPoint = function (geometry1, geometry2) {
  const _geometryArray = []
  let _geometry = undefined

  for (let i = 0; i < geometry2.coordinates.length; i++) {
    const _point = new Point({
      coordinates: geometry2.coordinates[i]
    })
    if (GeometryEngine.contains(geometry1, _point)) {
      _geometryArray.push(_point.coordinates)
    }
  }
  if (_geometryArray.length === 1) {
    _geometry = new Point({
      coordinates: _geometryArray[0]
    })
  } else if (_geometryArray.length > 1) {
    _geometry = new MultiPoint({
      coordinates: _geometryArray
    })
  }

  return _geometry
}

/**
 * 线和多边形相交
 * @private
 * @param {LineString} geometry1 线几何对象
 * @param {Polygon} geometry2 区几何对象
 * @return {LineString | MultiLineString | Point | MultiPoint} 相交线或点
 * */
GeometryEngine._lineIntersectPolygon = function (geometry1, geometry2) {
  // 构造turf对象
  const line = T.lineString(geometry1.coordinates)
  const polygon = T.polygon(geometry2.coordinates)

  // 计算交点
  const _geometry = T.lineIntersect(line, polygon)

  // 将交点按照点的x坐标从小到大排序
  _geometry.features = _geometry.features.sort(function (a, b) {
    return a.geometry.coordinates[0] - b.geometry.coordinates[0]
  })

  // 通过交点将线打散,在分别判断打散后的线段是否被区包含
  const _intersectLines = []
  // 开始打散线段
  for (let i = 0; i < geometry1.coordinates.length - 1; i++) {
    // 每次拿两个点造一个新的线段
    const _line = new LineString({
      coordinates: [geometry1.coordinates[i], geometry1.coordinates[i + 1]]
    })
    // 将线的点坐标以x坐标从小到大排列
    _line.coordinates = _line.coordinates.sort(function (a, b) {
      return a - b
    })
    let _pointCoords = []
    // 查找在该线段上的点
    for (let j = 0; j < _geometry.features.length; j++) {
      const _point = new Point({
        coordinates: _geometry.features[j].geometry.coordinates
      })
      if (GeometryEngine.contains(_line, _point)) {
        _pointCoords.push(_point)
      }
    }
    // 将线段的起始点加入
    const _firstPoint = new Point({
      coordinates: geometry1.coordinates[i]
    })
    if (
      !_pointCoords[0] ||
      !GeometryEngine.equals(_firstPoint, _pointCoords[0])
    ) {
      _pointCoords = [_firstPoint].concat(_pointCoords)
    }
    // 将线段的终点加入
    const _lastPoint = new Point({
      coordinates: geometry1.coordinates[i + 1]
    })
    if (
      _pointCoords.length === 0 ||
      !GeometryEngine.equals(_lastPoint, _pointCoords[_pointCoords.length - 1])
    ) {
      _pointCoords.push(_lastPoint)
    }
    // 判断区是否包含线,如果包含则存下来
    for (let j = 0; j < _pointCoords.length - 1; j++) {
      const _line = new LineString({
        coordinates: [
          _pointCoords[j].coordinates,
          _pointCoords[j + 1].coordinates
        ]
      })
      if (GeometryEngine.contains(geometry2, _line)) {
        _intersectLines.push(_line)
      }
    }
  }
  // 判断返回线还是多线
  if (_intersectLines.length === 1) {
    return _intersectLines[0]
  } else if (_intersectLines.length > 1) {
    const _linCoords = []
    for (let i = 0; i < _intersectLines.length; i++) {
      _linCoords.push(_intersectLines[i].coordinates)
    }
    return new MultiLineString({
      coordinates: _linCoords
    })
  } else {
    const _pointCoords = []
    for (let i = 0; i < _geometry.features.length; i++) {
      if (_geometry.features[i].geometry.type === GeometryType.point) {
        _pointCoords.push(_geometry.features[i].geometry.coordinates)
      }
    }
    if (_pointCoords.length === 1) {
      return new Point({
        coordinates: _pointCoords[0]
      })
    } else if (_pointCoords.length > 1) {
      return new MultiPoint({
        coordinates: _pointCoords
      })
    }
  }
}

/**
 * 多线和多边形相交
 * @private
 * @param {MultiLineString} geometry1 多线几何对象
 * @param {Polygon} geometry2 区几何对象
 * @return {LineString} 相交线
 * */
GeometryEngine._multiLineIntersectPolygon = function (geometry1, geometry2) {
  const _geometryArr = []
  for (let i = 0; i < geometry1.coordinates.length; i++) {
    const _line = new LineString({
      coordinates: geometry1.coordinates[i]
    })
    const _intersect = GeometryEngine._lineIntersectPolygon(_line, geometry2)
    if (_intersect) {
      _geometryArr.push(_intersect)
    }
  }

  if (_geometryArr.length === 1) {
    return new LineString({
      coordinates: _geometryArr[0].coordinates
    })
  } else if (_geometryArr.length > 1) {
    let _lineCoords = []
    for (let i = 0; i < _geometryArr.length; i++) {
      if (_geometryArr[i].type === GeometryType.lineString) {
        _lineCoords.push(_geometryArr[i].coordinates)
      } else {
        _lineCoords = _lineCoords.concat(_geometryArr[i].coordinates)
      }
    }
    return new MultiLineString({
      coordinates: _lineCoords
    })
  }
}

/**
 * 多线和多区相交
 * @private
 * @param {MultiLineString} geometry1 多线几何对象
 * @param {MultiPolygon} geometry2 多区几何对象
 * @return {LineString} 相交线
 * */
GeometryEngine._multiLineIntersectMultiPolygon = function (
  geometry1,
  geometry2
) {
  const _geometryArr = []
  for (let i = 0; i < geometry2.coordinates.length; i++) {
    const _polygon = new Polygon({
      coordinates: geometry2.coordinates[i]
    })
    const _intersect = GeometryEngine._multiLineIntersectPolygon(
      geometry1,
      _polygon
    )
    if (_intersect) {
      _geometryArr.push(_intersect)
    }
  }
  return GeometryEngine.union(_geometryArr)
}

/**
 * 线和矩形相交
 * @private
 * @param {LineString} geometry1 线几何对象
 * @param {Extent} geometry2 矩形几何对象
 * @return {LineString} 相交线
 * */
GeometryEngine._lineIntersectExtent = function (geometry1, geometry2) {
  const _polygon = new Polygon({
    coordinates: [
      [
        [geometry2.xmin, geometry2.ymin],
        [geometry2.xmax, geometry2.ymin],
        [geometry2.xmax, geometry2.ymax],
        [geometry2.xmin, geometry2.ymax],
        [geometry2.xmin, geometry2.ymin]
      ]
    ]
  })
  return GeometryEngine._lineIntersectPolygon(geometry1, _polygon)
}

/**
 * 多线和矩形相交
 * @private
 * @param {MultiLineString} geometry1 多线几何对象
 * @param {Extent} geometry2 矩形几何对象
 * @return {LineString} 相交线
 * */
GeometryEngine._multiLineIntersectExtent = function (geometry1, geometry2) {
  const _geometryArray = []
  for (let i = 0; i < geometry1.coordinates.length; i++) {
    const _line = new LineString({
      coordinates: geometry1.coordinates[i]
    })
    const _intersect = GeometryEngine._lineIntersectExtent(_line, geometry2)
    if (_intersect) {
      _geometryArray.push(_intersect)
    }
  }

  if (_geometryArray.length === 1) {
    return _geometryArray[0]
  } else if (_geometryArray.length > 1) {
    const _lineCoords = []
    for (let i = 0; i < _geometryArray.length; i++) {
      _lineCoords.push(_geometryArray[i].coordinates)
    }
    return new MultiLineString({
      coordinates: _lineCoords
    })
  }
}

/**
 * 线和多区相交
 * @private
 * @param {LineString} geometry1 线几何对象
 * @param {MultiPolygon} geometry2 多区几何对象
 * @return {Point | MultiPoint} 相交点或多点
 * */
GeometryEngine._lineIntersectMultiPolygon = function (geometry1, geometry2) {
  let _geometry = undefined
  const _geometryArray = []
  for (let i = 0; i < geometry2.coordinates.length; i++) {
    const _polygon2 = new Polygon({
      coordinates: geometry2.coordinates[i]
    })
    _geometryArray.push(
      GeometryEngine._lineIntersectPolygon(geometry1, _polygon2)
    )
  }
  // 构造并返回一个单线或多线对象
  let _coords = []
  for (let i = 0; i < _geometryArray.length; i++) {
    if (_geometryArray[i].type === GeometryType.lineString) {
      _coords.push(_geometryArray[i].coordinates)
    } else {
      _coords = _coords.concat(_geometryArray[i].coordinates)
    }
  }
  if (_coords.length === 1) {
    _geometry = new LineString({
      coordinates: _coords
    })
  } else if (_coords.length > 1) {
    _geometry = new MultiLineString({
      coordinates: _coords
    })
  }

  return _geometry
}

/**
 * 点和几何相交
 * @private
 * @param {Point} geometry1 点几何对象
 * @param {Geometry} geometry2 任意几何对象
 * @return {Point} 相交点
 * */
GeometryEngine._pointIntersectGeometry = function (geometry1, geometry2) {
  if (GeometryEngine.contains(geometry2, geometry1)) {
    return new Point({
      coordinates: geometry1.coordinates
    })
  }
}

/**
 * 多点和几何相交
 * @private
 * @param {MultiPoint} geometry1 点几何对象
 * @param {Geometry} geometry2 任意几何对象
 * @return {Point} 相交点或多点
 * */
GeometryEngine._multiPointIntersectGeometry = function (geometry1, geometry2) {
  const _geometryArray = []
  for (let i = 0; i < geometry1.coordinates.length; i++) {
    const _point = new Point({
      coordinates: geometry1.coordinates[i]
    })
    if (GeometryEngine.contains(geometry2, _point)) {
      _geometryArray.push(_point.coordinates)
    }
  }

  let _geometry = undefined
  if (_geometryArray.length === 1) {
    _geometry = new Point({
      coordinates: _geometryArray[0]
    })
  } else if (_geometryArray.length > 1) {
    _geometry = new MultiPoint({
      coordinates: _geometryArray
    })
  }

  return _geometry
}

/**
 * 区和区相交
 * @private
 * @param {Polygon} geometry1 区几何对象
 * @param {Polygon} geometry2 区几何对象
 * @return {Polygon | MultiPolygon} 相交的区或多区
 * */
GeometryEngine._polygonIntersectPolygon = function (geometry1, geometry2) {
  // 转为turf对象
  const _polygon1 = T.polygon(geometry1.coordinates)
  const _polygon2 = T.polygon(geometry2.coordinates)
  const intersection = T.intersect(_polygon1, _polygon2)
  if (intersection) {
    if (intersection.geometry.type === GeometryType.polygon) {
      return new Polygon({
        coordinates: intersection.geometry.coordinates
      })
    } else {
      return new MultiPolygon({
        coordinates: intersection.geometry.coordinates
      })
    }
  }
}

/**
 * 区和多区相交
 * @private
 * @param {Polygon} geometry1 区几何对象
 * @param {MultiPolygon} geometry2 矩形几何对象
 * @return {Polygon | MultiPolygon} 相交的区或多区
 * */
GeometryEngine._polygonIntersectMultiPolygon = function (geometry1, geometry2) {
  const _geometryArr = []
  for (let i = 0; i < geometry2.coordinates.length; i++) {
    const _polygon = new Polygon({
      coordinates: geometry2.coordinates[i]
    })
    const _intersect = GeometryEngine._polygonIntersectPolygon(
      geometry1,
      _polygon
    )
    if (_intersect) {
      _geometryArr.push(_intersect)
    }
  }
  return GeometryEngine.union(_geometryArr)
}

/**
 * 多区和多区相交
 * @private
 * @param {MultiPolygon} geometry1 多区几何对象
 * @param {MultiPolygon} geometry2 矩形几何对象
 * @return {Polygon | MultiPolygon} 相交的区或多区
 * */
GeometryEngine._multiPolygonIntersectMultiPolygon = function (
  geometry1,
  geometry2
) {
  const _geometryArr = []
  for (let i = 0; i < geometry1.coordinates.length; i++) {
    const _polygon = new Polygon({
      coordinates: geometry1.coordinates[i]
    })
    const _intersect = GeometryEngine._polygonIntersectMultiPolygon(
      _polygon,
      geometry2
    )
    if (_intersect) {
      _geometryArr.push(_intersect)
    }
  }
  return GeometryEngine.union(_geometryArr)
}

/**
 * 区和矩形相交
 * @private
 * @param {Polygon} geometry1 区几何对象
 * @param {Extent} geometry2 矩形几何对象
 * @return {Polygon | MultiPolygon} 相交的区或多区
 * */
GeometryEngine._polygonIntersectExtent = function (geometry1, geometry2) {
  const _polygon = new Polygon({
    coordinates: [
      [
        [geometry2.xmin, geometry2.ymin],
        [geometry2.xmax, geometry2.ymin],
        [geometry2.xmax, geometry2.ymax],
        [geometry2.xmin, geometry2.ymax],
        [geometry2.xmin, geometry2.ymin]
      ]
    ]
  })
  return GeometryEngine._polygonIntersectPolygon(geometry1, _polygon)
}

/**
 * 多区和矩形相交
 * @private
 * @param {MultiPolygon} geometry1 多区几何对象
 * @param {Extent} geometry2 矩形几何对象
 * @return {Polygon | MultiPolygon} 相交的区或多区
 * */
GeometryEngine._multiPolygonIntersectExtent = function (geometry1, geometry2) {
  const _geometryArr = []
  for (let i = 0; i < geometry1.coordinates.length; i++) {
    const _polygon = new Polygon({
      coordinates: geometry1.coordinates[i]
    })
    const _intersect = GeometryEngine._polygonIntersectExtent(
      _polygon,
      geometry2
    )
    if (_intersect) {
      _geometryArr.push(_intersect)
    }
  }
  return GeometryEngine.union(_geometryArr)
}

/**
 * <a id='overlaps'></a>
 * 判断两个几何对象是否重叠,即两个几何相交但不互相包含
 * @param {Geometry} geometry1 第一个几何对象
 * @param {Geometry} geometry2 第二个几何对象
 * @return {Boolean} 几何对象是否重叠
 *
 * @example <caption><h5>几何是否重叠</h5></caption>
 * // ES5引入方式
 * const { LineString, Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, Polygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造线几何对象
 * const line = new LineString({
 *   coordinates: [
 *     [113, 28],
 *     [113, 34]
 *   ]
 * })
 * // 构造区几何对象
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [108, 30],
 *       [115, 30],
 *       [115, 33],
 *       [108, 33],
 *       [108, 30]
 *     ],
 *     [
 *       [110, 31],
 *       [113, 31],
 *       [113, 32],
 *       [110, 32],
 *       [110, 31]
 *     ]
 *   ]
 * })
 * // 开始计算是否重叠
 * const isIntersect = GeometryEngine.overlaps(line, polygon)
 * console.log("是否重叠:", isIntersect)
 * */
GeometryEngine.overlaps = function (geometry1, geometry2) {
  // 判断是否相交
  if (!GeometryEngine.intersects(geometry1, geometry2)) {
    return false
  }

  // 判断geometry1是否包含geometry2
  if (GeometryEngine.contains(geometry1, geometry2)) {
    return false
  }

  // 判断geometry2是否包含geometry1
  if (GeometryEngine.contains(geometry2, geometry1)) {
    return false
  }

  // 上述条件都不满足
  return true
}

/**
 * <a id='rotate'></a>
 * 顺时针旋转几何,支持所有基础几何对象;<br/>
 * 可以指定旋转中心点,当不指定时使用几何质心;<br/>
 * 忽略三维高度;<br/>
 * 支持自定义坐标系几何,几何对象与旋转中心点的坐标系必须一致;<br/>
 * 示例如下:<br/>
 * <a href='#rotate1'>[1、旋转多点]</a><br/>
 * <a href='#rotate2'>[2、旋转区]</a><br/>
 * <a href='#rotate3'>[3、旋转多区]</a><br/>
 * <a href='#rotate4'>[4、旋转线]</a><br/>
 * <a href='#rotate5'>[5、旋转多线]</a><br/>
 * <a href='#rotate6'>[6、旋转矩形]</a><br/>
 * <a href='#rotate7'>[7、旋转圆形]</a><br/>
 * <a href='#rotate8'>[8、旋转区 - 自定义坐标系]</a><br/>
 * <a href='#rotate9'>[9、绕点旋转 - 区]</a><br/>
 * <a href='#rotate10'>[10、绕点旋转 - 点]</a><br/>
 * @param {Geometry} geometry 被旋转的几何对象
 * @param {Number} angle 旋转角度,单位度,顺时针旋转
 * @param {Point} rotationOrigin 旋转中心点,默认为几何质心
 * @return {Geometry} 旋转后的新几何对象
 *
 * @example <caption><h5 id='rotate1'>旋转多点</h5></caption>
 * // ES5引入方式
 * const { MultiPoint, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { MultiPoint, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造多点几何
 * const multiPoint = new MultiPoint({
 *   coordinates: [
 *     [108.36341, 29.032578],
 *     [112.13094, 29.032578],
 *     [112.13094, 33.273224]
 *   ]
 * })
 * // 执行旋转操作
 * const rotateGeometry = GeometryEngine.rotate(multiPoint, 10)
 *
 * @example <caption id='rotate2'><h5>旋转区</h5></caption>
 * // ES5引入方式
 * const { Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造区几何
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [108.36341, 29.032578],
 *       [116.13094, 29.032578],
 *       [116.13094, 33.273224],
 *       [108.36341, 33.273224],
 *       [108.36341, 29.032578],
 *     ]
 *   ]
 * })
 * // 执行旋转操作
 * const rotateGeometry = GeometryEngine.rotate(polygon, 10)
 *
 * @example <caption><h5 id='rotate3'>旋转多区</h5></caption>
 * // ES5引入方式
 * const { MultiPolygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { MultiPolygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造多区几何
 * const polygon = new MultiPolygon({
 *   coordinates: [
 *     [
 *       [
 *         [108.36341, 29.032578],
 *         [112.13094, 29.032578],
 *         [112.13094, 33.273224],
 *         [108.36341, 33.273224],
 *         [108.36341, 29.032578],
 *       ]
 *     ],
 *     [
 *       [
 *         [113.36341, 29.032578],
 *         [116.13094, 29.032578],
 *         [116.13094, 33.273224],
 *         [113.36341, 33.273224],
 *         [113.36341, 29.032578],
 *       ]
 *     ]
 *   ]
 * })
 * // 执行旋转操作
 * const rotateGeometry = GeometryEngine.rotate(polygon, 10)
 *
 * @example <caption><h5 id='rotate4'>旋转线</h5></caption>
 * // ES5引入方式
 * const { LineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造线几何
 * const line = new LineString({
 *   coordinates: [
 *     [108.36341, 29.032578],
 *     [116.13094, 29.032578],
 *     [116.13094, 33.273224],
 *     [108.36341, 33.273224]
 *   ]
 * })
 * // 执行旋转操作
 * const rotateGeometry = GeometryEngine.rotate(line, 10)
 *
 * @example <caption><h5 id='rotate5'>旋转多线</h5></caption>
 * // ES5引入方式
 * const { MultiLineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { MultiLineString, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造多线几何
 * const line = new MultiLineString({
 *   coordinates: [
 *     [
 *       [108.36341, 29.032578],
 *       [116.13094, 29.032578]
 *     ],
 *     [
 *       [116.13094, 33.273224],
 *       [108.36341, 33.273224]
 *     ]
 *   ]
 * })
 * // 执行旋转操作
 * const rotateGeometry = GeometryEngine.rotate(line, 10)
 *
 * @example <caption><h5 id='rotate6'>旋转矩形</h5></caption>
 * // ES5引入方式
 * const { Extent, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Extent, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造矩形几何
 * const extent = new Extent({
 *   xmin: 108.36341,
 *   ymin: 29.032578,
 *   xmax: 112.13094,
 *   ymax: 33.273224
 * })
 * // 执行旋转操作
 * const rotateGeometry = GeometryEngine.rotate(extent, 10)
 *
 * @example <caption><h5 id='rotate7'>旋转圆形</h5></caption>
 * // ES5引入方式
 * const { Circle, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Circle, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造圆形几何
 * const circle = new Circle({
 *   center: [108.36341, 29.032578],
 *   radius: 3
 * })
 * // 执行旋转操作
 * const rotateGeometry = GeometryEngine.rotate(circle, 10)
 *
 * @example <caption><h5 id='rotate8'>旋转区 - 自定义坐标系</h5></caption>
 * // ES5引入方式
 * const { Polygon, SpatialReference, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, SpatialReference, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造区几何
 * const polygon = new Polygon({
 *  coordinates: [
 *    [
 *      [0, 3226025],
 *      [698545, 3226025],
 *      [698545, 3685076],
 *      [0, 3685076],
 *      [0, 3226025]
 *    ]
 *  ],
 *  // 一定要指定坐标系
 *  spatialReference: new SpatialReference({
 *    wkid: 4547
 *  })
 * })
 * // 执行旋转操作
 * const rotateGeometry = GeometryEngine.rotate(polygon, 10)
 *
 * @example <caption><h5 id='rotate9'>绕点旋转 - 区</h5></caption>
 * // ES5引入方式
 * const { Polygon, Point, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, Point, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造区几何
 * const polygon = new Polygon({
 *  coordinates: [
 *    [
 *      [108.36341, 29.032578],
 *      [116.13094, 29.032578],
 *      [116.13094, 33.273224],
 *      [108.36341, 33.273224],
 *      [108.36341, 29.032578]
 *    ]
 *  ]
 * })
 * // 设置旋转中心点
 * const rotatePoint = new Point({
 *   coordinates: [108.36341, 29.032578]
 * })
 * // 执行旋转操作
 * const rotateGeometry = GeometryEngine.rotate(polygon, 10, rotatePoint)
 *
 * @example <caption><h5 id='rotate10'>绕点旋转 - 点</h5></caption>
 * // ES5引入方式
 * const { Point, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Point, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造点几何
 * const point = new Point({
 *   coordinates: [
 *     [108.36341, 29.032578]
 *   ]
 * })
 * // 设置旋转中心点
 * const rotatePoint = new Point({
 *   coordinates: [112.13094, 33.273224]
 * })
 * // 执行旋转操作
 * const rotateGeometry = GeometryEngine.rotate(point, 10, rotatePoint)
 * */
GeometryEngine.rotate = function (geometry, angle, rotationOrigin) {
  angle = defaultValue(angle, 0)

  // 判断geometry是否为空
  if (isNull(geometry)) {
    Log.error('geometry不能为空!')
  }

  if (isNull(angle)) {
    Log.error('angle不能为空!')
  }

  // 判断输入类型
  if (!(geometry instanceof Geometry)) {
    Log.error('geometry不是基础几何对象!')
  }

  if (!(typeof angle === 'number')) {
    Log.error('angle不是数字!')
  }

  if (rotationOrigin && !(rotationOrigin instanceof Point)) {
    Log.error('rotationOrigin不是Point对象!')
  }

  // 判断rotationOrigin和geometry是同一个坐标系
  if (
    rotationOrigin &&
    !geometry.spatialReference.equals(rotationOrigin.spatialReference)
  ) {
    Log.error('rotationOrigin和geometry不是同一个坐标系!')
  }

  // 判断入参坐标系,如果不是WGS84坐标系,则转为WGS84
  let _inputGeometry = undefined
  let _rotationOrigin = undefined
  if (!geometry.spatialReference.isGeographic) {
    _inputGeometry = Projection.projectGeometry(
      geometry,
      new SpatialReference({
        wkid: 4326
      })
    )
    _rotationOrigin = Projection.projectGeometry(
      rotationOrigin,
      new SpatialReference({
        wkid: 4326
      })
    )
  } else {
    _inputGeometry = geometry
    _rotationOrigin = rotationOrigin
  }

  // 转为turf对象
  const _geometryTurf = GeometryEngine._geometryToTurf(_inputGeometry)

  // 开始旋转
  let _pivot = undefined
  if (_rotationOrigin) {
    _pivot = [_rotationOrigin.coordinates[0], _rotationOrigin.coordinates[1]]
  }
  const options = {
    pivot: _pivot,
    mutate: true
  }
  let _geometry = T.transformRotate(_geometryTurf, angle, options)

  // 返回几何
  _geometry = GeometryEngine._turfFeatureToGeometry(_geometry)
  // 判断结果坐标系,如果不是WGS84坐标系,则将结果转为自身的
  if (!geometry.spatialReference.isGeographic) {
    _geometry = Projection.projectGeometry(
      _geometry,
      SpatialReference.fromJSON(geometry.spatialReference)
    )
  }
  return _geometry
}

/**
 * 将turf的要素对象转为基础几何对象
 * @private
 * @param {Object} feature turf的要素对象
 * @return {Geometry} 基础几何对象
 * */
GeometryEngine._turfFeatureToGeometry = function (feature) {
  switch (feature.geometry.type) {
    case GeometryType.point:
      return new Point({
        coordinates: feature.geometry.coordinates
      })
    case GeometryType.multiPoint:
      return new MultiPoint({
        coordinates: feature.geometry.coordinates
      })
    case GeometryType.lineString:
      return new LineString({
        coordinates: feature.geometry.coordinates
      })
    case GeometryType.multiLineString:
      return new MultiLineString({
        coordinates: feature.geometry.coordinates
      })
    case GeometryType.polygon:
      return new Polygon({
        coordinates: feature.geometry.coordinates
      })
    case GeometryType.multiPolygon:
      return new MultiPolygon({
        coordinates: feature.geometry.coordinates
      })
    default:
      break
  }
}

/**
 * <a id='nearestCoordinate'></a>
 * 在几何对象的coordinates中寻找与指定点距离最近的点
 * @param { LineString | Polygon | MultiPoint | MultiLineString |  MultiPolygon | Circle | Extent} geometry 几何对象,支持多点、线、多线、面、多面、圆、矩形
 * @param {Point} inputPoint 指定点,需为point
 * @return {Project<Point>} 距离最近的点数组
 *
 *  * @example <caption><h5>多点到目标点最近点</h5></caption>
 * // ES5引入方式
 * const { Point, MultiPoint, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, MultiPoint, Point } from "@mapgis/webclient-common"
 * // inputPoint
 * const inputPoint = new Point({
 *   coordinates: [108, 28],
 * });
 * // geometry  MultiPoint
 * const multiPoint = new MultiPoint({
 *   coordinates: [
 *     [110, 20],
 *     [109, 19],
 *   ],
 * });
 * // 多点到目标点最近点
 * const pointToMultiPoint = GeometryEngine.nearestCoordinate(
 *   multiPoint,
 *   inputPoint
 * );
 * console.log("多点到目标点最近点:", pointToMultiPoint);
 *
 * @example <caption><h5>线到目标点最近点</h5></caption>
 * // ES5引入方式
 * const { Point, LineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, Point, LineString } from "@mapgis/webclient-common"
 * // inputPoint
 *  const inputPoint = new Point({
 *    coordinates: [112, 30],
 *  });
 *  // geometry  lineString
 *  const lineString = new LineString({
 *    coordinates: [
 *      [108, 32],
 *      [115, 32],
 *    ],
 *  });
 *  // 线到目标点最近点
 *  const pointToLineString = GeometryEngine.nearestCoordinate(
 *    lineString,
 *    inputPoint
 *  );
 *  console.log("线到目标点最近点:", pointToLineString);
 *
 * @example <caption><h5>区到目标点最近点</h5></caption>
 * // ES5引入方式
 * const { Point, Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, Point, Polygon } from "@mapgis/webclient-common"
 * // inputPoint
 *  const inputPoint = new Point({
 *    coordinates: [100, 30],
 *  });
 *  // geometry  Polygon
 *  const polygon = new Polygon({
 *    coordinates: [
 *      [
 *        [105.0, 35.0],
 *        [110.0, 35.0],
 *        [110.0, 36.0],
 *        [105.0, 36.0],
 *        [105.0, 35.0],
 *      ],
 *    ],
 *  });
 *  // 区到目标点最近点
 *  const pointToPolygon = GeometryEngine.nearestCoordinate(
 *    polygon,
 *    inputPoint
 *  );
 *  console.log("区到目标点最近点:", pointToPolygon);
 *
 * */

GeometryEngine.nearestCoordinate = function (geometry, inputPoint) {
  // 1.先判断入参非空
  if (isNull(geometry)) {
    Log.error('geometry不能为空!')
  }
  if (isNull(inputPoint)) {
    Log.error('inputPoint不能为空!')
  }
  let _geometryPro1 = geometry
  let _geometryPro2 = inputPoint
  // 2.入参类型检查
  if (!(_geometryPro2 instanceof Point)) {
    Log.error('inputPoint类型错误,需要输入Point!')
  }
  if (
    !(
      _geometryPro1 instanceof Point ||
      _geometryPro1 instanceof LineString ||
      _geometryPro1 instanceof Polygon ||
      _geometryPro1 instanceof MultiPoint ||
      _geometryPro1 instanceof MultiLineString ||
      _geometryPro1 instanceof MultiPolygon ||
      _geometryPro1 instanceof Extent ||
      _geometryPro1 instanceof Circle
    )
  ) {
    Log.error('geometry类型错误!')
  }

  // 3.检查两个入参的坐标系是否相同
  if (!_geometryPro1.spatialReference.equals(_geometryPro2.spatialReference)) {
    Log.error('_geometryPro1、inputPoint坐标系不同!')
  }
  // 4.给默认值
  _geometryPro1 = defaultValue(_geometryPro1, undefined)
  _geometryPro2 = defaultValue(_geometryPro2, undefined)

  // 5.如果不是4326,则进行坐标系投影转为4326,因为上一步已经判断过两个入参的坐标系是否相同,所以只需要判断其中一个坐标系是否为WGS84即可
  if (!_geometryPro1.spatialReference.isGeographic) {
    _geometryPro1 = Projection.projectGeometry(
      _geometryPro1,
      new SpatialReference({
        wkid: 4326
      })
    )
    _geometryPro2 = Projection.projectGeometry(
      _geometryPro2,
      new SpatialReference({
        wkid: 4326
      })
    )
  }

  // 6.通过判断geometry的type 求取几何体中到目标点的最近点
  if (!isNull(_geometryPro1) && !isNull(_geometryPro2)) {
    let nearestPoint
    let _geometry
    let _polygon
    let _coords = []

    const _inputPoint = T.point(_geometryPro2.coordinates)
    switch (_geometryPro1.type) {
      case GeometryType.multiPoint:
        for (let i = 0; i < _geometryPro1.coordinates.length; i++) {
          _coords.push(T.point(_geometryPro1.coordinates[i]))
        }
        const points = T.featureCollection(_coords)
        nearestPoint = T.nearestPoint(_inputPoint, points)
        break
      case GeometryType.lineString:
        _geometry = T.lineString(_geometryPro1.coordinates)
        nearestPoint = T.nearestPointOnLine(_geometry, _inputPoint, {
          units: 'kilometers'
        })
        break
      case GeometryType.multiLineString:
        _geometry = T.multiLineString(_geometryPro1.coordinates)
        nearestPoint = T.nearestPointOnLine(_geometry, _inputPoint, {
          units: 'kilometers'
        })
        break
      case GeometryType.polygon:
        for (let i = 0; i < _geometryPro1.coordinates[0].length; i++) {
          _coords.push(_geometryPro1.coordinates[0][i])
        }
        _geometry = T.lineString(_coords)
        nearestPoint = T.nearestPointOnLine(_geometry, _inputPoint, {
          units: 'kilometers'
        })
        break
      case GeometryType.multiPolygon:
        const _multiLineString = []
        // 1.先遍历,多区=》区
        for (let i = 0; i < _geometryPro1.coordinates.length; i++) {
          // i表示第i个区
          // 然后再获取单个区的外圈
          _coords = []
          for (let j = 0; j < _geometryPro1.coordinates[i][0].length; j++) {
            _coords.push(_geometryPro1.coordinates[i][0][j])
          }
          // 将每个区的外圈组合起来,组成一个多线对象
          _multiLineString.push(_coords)
          _geometry = T.multiLineString(_multiLineString)
        }
        nearestPoint = T.nearestPointOnLine(_geometry, _inputPoint, {
          units: 'kilometers'
        })
        break
      case GeometryType.circle:
        _polygon = T.polygon(_geometryPro1.toPolygon().coordinates)
        for (let i = 0; i < _polygon.geometry.coordinates[0].length; i++) {
          _coords.push(_polygon.geometry.coordinates[0][i])
        }
        _geometry = T.lineString(_coords)
        nearestPoint = T.nearestPointOnLine(_geometry, _inputPoint, {
          units: 'kilometers'
        })
        break
      case GeometryType.extent:
        _polygon = new Polygon({
          coordinates: [
            [
              [_geometryPro1.xmin, _geometryPro1.ymin],
              [_geometryPro1.xmax, _geometryPro1.ymin],
              [_geometryPro1.xmax, _geometryPro1.ymax],
              [_geometryPro1.xmin, _geometryPro1.ymax],
              [_geometryPro1.xmin, _geometryPro1.ymin]
            ]
          ]
        })

        for (let i = 0; i < _polygon.coordinates[0].length; i++) {
          _coords.push(_polygon.coordinates[0][i])
        }
        _geometry = T.lineString(_coords)
        nearestPoint = T.nearestPointOnLine(_geometry, _inputPoint, {
          units: 'kilometers'
        })
        break
      default:
        break
    }

    nearestPoint = Geometry.fromJSON(nearestPoint.geometry)
    if (!inputPoint.spatialReference.isGeographic) {
      nearestPoint = Projection.projectGeometry(
        nearestPoint,
        inputPoint.spatialReference
      )
    }

    return nearestPoint
  }
}

/**
 * <a id='planarArea'></a>
 * 计算几何对象的面积;<br/>
 * 仅计算平面面积,忽略地球弧度,当坐标系为WGS84相关坐标系时,也仅会计算平面面积,例如坐标为[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]的区几何的面积为1;<br/>
 * 支持自定义坐标系,忽略三维高度;<br/>
 * 支持区、多区、矩形和圆形几何对象;<br/>
 * 当为区或多区几何对象时,若该区带洞或多区的子区带洞,则会减去这些洞的面积;<br/>
 * 示例如下:<br/>
 * <a href='#planarArea1'>[1、计算区(不带洞)面积]</a><br/>
 * <a href='#planarArea2'>[2、计算区(带洞)面积]</a><br/>
 * <a href='#planarArea3'>[3、计算多区面积]</a><br/>
 * <a href='#planarArea4'>[4、计算圆面积]</a><br/>
 * <a href='#planarArea5'>[5、计算矩形面积]</a><br/>
 * @param {Polygon | MultiPolygon | Circle | Extent} geometry 计算面积的几何对象
 * @return {Number} 几何对象的面积
 * @example <caption><h5 id='planarArea1'>计算区(不带洞)面积</h5></caption>
 * // ES5引入方式
 * const { Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, Polygon, LengthUnit } from "@mapgis/webclient-common"
 * // 初始化区几何
 * const polygon = new Polygon({
 *   coordinates: [
 *       [
 *           [100, 30],
 *           [110, 30],
 *           [110, 40],
 *           [100, 40],
 *           [100, 30]
 *       ]
 *   ]
 * })
 * // 计算区几何面积
 * const area = GeometryEngine.planarArea(polygon)
 *
 * @example <caption><h5 id='planarArea2'>计算区(带洞)面积</h5></caption>
 * // ES5引入方式
 * const { Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, Polygon, LengthUnit } from "@mapgis/webclient-common"
 * // 初始化区几何 - 带洞
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [0, 30],
 *       [140, 30],
 *       [140, 40],
 *       [0, 40],
 *       [0, 30]
 *     ],
 *     [
 *       [10, 30],
 *       [20, 30],
 *       [20, 40],
 *       [10, 40],
 *       [10, 30]
 *     ],
 *     [
 *       [30, 30],
 *       [40, 30],
 *       [40, 40],
 *       [30, 40],
 *       [30, 30]
 *     ]
 *   ]
 * })
 * // 计算区几何面积
 * const area = GeometryEngine.planarArea(polygon)
 *
 * @example <caption><h5 id='planarArea3'>计算多区面积</h5></caption>
 * // ES5引入方式
 * const { MultiPolygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, MultiPolygon, LengthUnit } from "@mapgis/webclient-common"
 * //多区几何
 * const multiPolygon = new MultiPolygon({
 *   coordinates: [
 *       [
 *         [
 *           [10, 30],
 *           [20, 30],
 *           [20, 40],
 *           [10, 40],
 *           [10, 30]
 *         ]
 *       ],
 *       [
 *         [
 *           [30, 30],
 *           [40, 30],
 *           [40, 40],
 *           [30, 40],
 *           [30, 30]
 *         ]
 *       ]
 *   ]
 * })
 * // 计算区几何面积
 * const Area = GeometryEngine.planarArea(multiPolygon)
 *
 * @example <caption><h5 id='planarArea4'>计算圆面积</h5></caption>
 * // ES5引入方式
 * const { Circle, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Circle, LengthUnit, GeometryEngine } from "@mapgis/webclient-common"
 * //圆几何
 * const circle = new Circle({
 *   center: [0, 0],
 *   radius: 10
 * })
 * // 计算圆几何面积
 * const area = GeometryEngine.planarArea(circle)
 *
 * @example <caption><h5 id='planarArea5'>计算矩形面积</h5></caption>
 * // ES5引入方式
 * const { Extent, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Extent, LengthUnit, GeometryEngine } from "@mapgis/webclient-common"
 * //矩形几何
 * const extent = new Extent({
 *   xmin: 0,
 *   ymin: 0,
 *   xmax: 2,
 *   ymax: 2
 * })
 * // 计算矩形几何面积
 * const area = GeometryEngine.planarArea(extent)
 * */
GeometryEngine.planarArea = function (geometry) {
  if (
    !(
      geometry instanceof Polygon ||
      geometry instanceof MultiPolygon ||
      geometry instanceof Circle ||
      geometry instanceof Extent
    )
  ) {
    Log.error('polygon对象不是Polyon或MultiPolygon或Circle或Extent类型!')
  }

  let _area = 0

  // 区几何对象
  if (geometry instanceof Polygon) {
    _area = GeometryEngine._polygonArea(geometry)
  }
  // 多区几何对象
  else if (geometry instanceof MultiPolygon) {
    const polygonArray = geometry.toPolygonArray()
    for (let i = 0; i < polygonArray.length; i++) {
      _area += GeometryEngine._polygonArea(polygonArray[i])
    }
  }
  // 圆几何对象
  else if (geometry instanceof Circle) {
    _area = Math.abs(Math.PI * geometry.radius * geometry.radius)
  }
  // 矩形几何对象
  else if (geometry instanceof Extent) {
    _area = Math.abs(
      (geometry.xmax - geometry.xmin) * (geometry.ymax - geometry.ymin)
    )
  }

  return _area
}

/**
 * 计算环的面积,环要首尾相连,忽略三维高度,忽略弧度
 * @private
 * @param {Array} coordinates 点坐标数组
 * @return {Number} 环的面积
 * */
GeometryEngine._ringArea = function (coordinates) {
  let total = 0

  for (let i = 0; i < coordinates.length; i++) {
    const addX = coordinates[i][0]
    const addY = coordinates[i === coordinates.length - 1 ? 0 : i + 1][1]
    const subX = coordinates[i === coordinates.length - 1 ? 0 : i + 1][0]
    const subY = coordinates[i][1]

    total += addX * addY * 0.5
    total -= subX * subY * 0.5
  }

  return Math.abs(total)
}

/**
 * 计算区几何的面积,外圈减去所有内圈之后的面积,单位为米
 * @private
 * @param {Polygon} polygon 区几何对象
 * @return {Number} 区几何的面积
 * */
GeometryEngine._polygonArea = function (polygon) {
  // 先计算外圈面积
  const _outerArea = GeometryEngine._ringArea(polygon.coordinates[0])
  // 再计算内圈面积
  let _innerArea = 0
  for (let i = 1; i < polygon.coordinates.length; i++) {
    _innerArea += GeometryEngine._ringArea(polygon.coordinates[i])
  }
  // 最终面积
  return _outerArea - _innerArea
}

/**
 * <a id='planarLength'></a>
 * 计算几何长度或周长;<br/>
 * 仅计算平面距离,忽略地球弧度,当坐标系为WGS84相关坐标系时,也仅会计算平面距离,例如坐标为[[0, 0], [0, 1]]的线几何的长度为1;<br/>
 * 支持自定义坐标系,忽略三维高度;<br/>
 * 支持的几何类型为线、多线、区、多区、圆形以及矩形;<br/>
 * 当几何为线时,返回线的长度;<br/>
 * 当几何为多线时,返回多线中子线的长度之和;<br/>
 * 当几何为区、圆形以及矩形时,返回其周长;<br/>
 * 当几何为多区时,返回多区中子区的周长之和;<br/>
 * 示例如下:<br/>
 * <a href='#planarLength1'>[1、计算几何长度]</a><br/>
 * <a href='#planarLength2'>[2、计算几何周长]</a><br/>
 * @param {LineString | MultiLineString | Polygon | MultiPolygon | Circle | Extent} [geometry = null] 几何对象
 * @return {Number} 几何对象的长度或周长
 * @example <caption><h5 id='planarLength1'>计算几何长度</h5></caption>
 * // ES5引入方式
 * const { LineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, GeometryEngine } from "@mapgis/webclient-common"
 * // 初始化一个线对象
 * const line = new LineString({
 *   coordinates: [
 *     [0, 1],
 *     [0, 2]
 *   ]
 * })
 * // 计算几何长度
 * const length = GeometryEngine.planarLength(line)
 *
 * @example <caption><h5 id='planarLength2'>计算几何周长</h5></caption>
 * // ES5引入方式
 * const { Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 初始化一个区对象
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [0, 0],
 *       [0, 1],
 *       [1, 1],
 *       [0, 1],
 *       [0, 0]
 *     ]
 *   ]
 * })
 * // 计算几何周长
 * const length = GeometryEngine.planarLength(polygon)
 * */
GeometryEngine.planarLength = function (geometry) {
  // 输入格式判断
  if (isNull(geometry)) {
    Log.error('geometry对象不能为空!')
  }

  // 判断类型
  if (
    !(
      geometry instanceof LineString ||
      geometry instanceof MultiLineString ||
      geometry instanceof Polygon ||
      geometry instanceof MultiPolygon ||
      geometry instanceof Circle ||
      geometry instanceof Extent
    )
  ) {
    Log.error('geometry类型不正确!')
  }

  let _length = 0
  let _polygon = undefined
  switch (geometry.type) {
    case GeometryType.lineString:
      _length = GeometryEngine._planarLineStringLength(geometry)
      break
    case GeometryType.multiLineString:
      _length = GeometryEngine._planarMultiLineStringLength(geometry)
      break
    case GeometryType.polygon:
      // 仅计算区几何的外圈
      _polygon = new Polygon({
        coordinates: [geometry.coordinates[0]]
      })
      _length = GeometryEngine._planarMultiLineStringLength(_polygon)
      break
    case GeometryType.multiPolygon:
      for (let i = 0; i < geometry.coordinates.length; i++) {
        // 仅计算区几何的外圈
        const _polygon = new Polygon({
          coordinates: [geometry.coordinates[i][0]]
        })
        _length += GeometryEngine._planarMultiLineStringLength(_polygon)
      }
      break
    case GeometryType.circle:
      _length = Math.PI * 2 * geometry.radius
      break
    case GeometryType.extent:
      // 转为区几何
      _polygon = new Polygon({
        coordinates: [
          [
            [geometry.xmin, geometry.ymin],
            [geometry.xmax, geometry.ymin],
            [geometry.xmax, geometry.ymax],
            [geometry.xmin, geometry.ymax],
            [geometry.xmin, geometry.ymin]
          ]
        ]
      })
      _length += GeometryEngine._planarMultiLineStringLength(_polygon)
      break
    default:
      break
  }

  return _length
}

/**
 * 计算单条线的长度
 * @private
 * @param {LineString} geometry 线几何对象
 * @return {Number} 线的长度
 * */
GeometryEngine._planarLineStringLength = function (geometry) {
  let _length = 0
  for (let i = 0; i < geometry.coordinates.length - 1; i++) {
    const _x = Math.abs(
      geometry.coordinates[i + 1][0] - geometry.coordinates[i][0]
    )
    const _y = Math.abs(
      geometry.coordinates[i + 1][1] - geometry.coordinates[i][1]
    )
    _length += Math.sqrt(Math.pow(_x, 2) + Math.pow(_y, 2))
  }
  return _length
}

/**
 * 计算多条线的长度
 * @private
 * @param {MultiLineString | Polygon} geometry 多线几何或区几何对象
 * @return {Number} 线的长度
 * */
GeometryEngine._planarMultiLineStringLength = function (geometry) {
  let _length = 0

  for (let i = 0; i < geometry.coordinates.length; i++) {
    const _line = new LineString({
      coordinates: geometry.coordinates[i]
    })
    _length += GeometryEngine._planarLineStringLength(_line)
  }

  return _length
}

/**
 * <a id='contains'></a>
 * 判断containerGeometry对象是否完全包含insideGeometry对象;<br/>
 * 忽略三维高度;<br/>
 * 支持自定义坐标系的几何对象,几何的坐标系必须相同;<br/>
 * 包含标准如下:
 * 点包含点的标准为:两个点几何坐标是否相等;<br/>
 * 线包含线的标准为:一条线上的点都在另一条线上,或两条线的点坐标相同;<br/>
 * 区包含线的标准为:线在区几何内,但线不全部在区几何边线上(包括内圈和外圈的边线);<br/>
 * 区包含线的标准为:点在区几何内,不在边线上(包括内圈和外圈的边线);<br/>
 * 区包含区的标准为:一个区在另一个区内(如果有带洞区域,则不能在带洞区域内,可以挨着边线),边线可以重合;<br/>
 * 其他情况为上述标准的组合;<br/>
 * 由于包含的情况太多,这里仅列举部分示例,示例如下:<br/>
 * <a href='#intersects1'>[1、区包含点]</a><br/>
 * <a href='#intersects2'>[2、多点包含点]</a><br/>
 * <a href='#intersects3'>[3、区包含线]</a><br/>
 * <a href='#intersects4'>[4、圆包含矩形]</a><br/>
 * <a href='#intersects5'>[5、几何包含 - 自定义坐标系]</a><br/>
 * @param {Geometry} [containerGeometry = null] 包含几何对象
 * @param {Geometry} [insideGeometry = null] 被包含的几何对象
 * @return {Boolean} 是否包含
 *
 * @example <caption><h5 id='contains1'>区包含点</h5></caption>
 * // ES5引入方式
 * const { Polygon, Point, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, Polygon, Point } from "@mapgis/webclient-common"
 * // 初始点几何,可以是任意几何
 * const point = new Point({
 *   coordinates: [1, 1]
 * })
 * // 初始化区几何,可以是任意几何
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [1, 0],
 *       [10, 0],
 *       [10, 20],
 *       [1, 20],
 *       [1, 0]
 *     ]
 *   ]
 * })
 * // 判断区几何是否包含点几何对象
 * const isContains = GeometryEngine.contains(polygon, point)
 * console.log("区是否包含点:", isContains)
 *
 * @example <caption><h5 id='contains2'>多点包含点</h5></caption>
 * // ES5引入方式
 * const { MultiPoint, Point, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { MultiPoint, Point, GeometryEngine } from "@mapgis/webclient-common"
 * // 初始点几何
 * const point = new Point({
 *   coordinates: [1, 1]
 * })
 * // 初始化多点几何
 * const multiPoint = new MultiPoint({
 *   coordinates: [
 *     [1, 1],
 *     [1, 2]
 *   ]
 * })
 * // 判断多点几何是否包含点几何对象
 * const isContains = GeometryEngine.contains(multiPoint, point)
 * console.log("多点是否包含点:", isContains)
 *
 * @example <caption><h5 id='contains3'>区包含线</h5></caption>
 * // ES5引入方式
 * const { Polygon, LineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, LineString, GeometryEngine } from "@mapgis/webclient-common"
 * // 初始化区几何
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [1, 0],
 *       [10, 0],
 *       [10, 20],
 *       [1, 20],
 *       [1, 0]
 *     ]
 *   ]
 * })
 * // 初始化线几何
 * const lineString = new LineString({
 *   coordinates: [
 *     [1, 0],
 *     [1, 6]
 *   ]
 * })
 * // 判断多点几何是否包含点几何对象
 * const isContains = GeometryEngine.contains(polygon, lineString)
 * console.log("区是否包含线:", isContains)
 *
 * @example <caption><h5 id='contains4'>圆包含矩形</h5></caption>
 * // ES5引入方式
 * const { Circle, LineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Circle, LineString, GeometryEngine } from "@mapgis/webclient-common"
 * // 初始化圆几何
 * const circle = new Circle({
 *   center: [5, 5],
 *   radius: 2
 * })
 * // 初始化矩几何
 * const extent = new Extent({
 *   xmin: 2,
 *   ymin: 2,
 *   xmax: 4,
 *   ymax: 4
 * })
 * // 判断多点几何是否包含点几何对象
 * const isContains = GeometryEngine.contains(circle, extent)
 * console.log("圆是否包含矩形:", isContains)
 *
 * @example <caption><h5 id='contains5'>几何包含 - 自定义坐标系</h5></caption>
 * // ES5引入方式
 * const { Polygon, LineString, SpatialReference, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, LineString, SpatialReference, GeometryEngine } from "@mapgis/webclient-common"
 * // 初始化线几何对象
 * const line = new LineString({
 *   coordinates: [
 *     [60000, 3426025],
 *     [598545, 3426025]
 *   ],
 *   // 要设置坐标系
 *   spatialReference: new SpatialReference({
 *     wkid: 4547
 *   })
 * })
 * // 初始化区几何对象
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [0, 3226025],
 *       [698545, 3226025],
 *       [698545, 3685076],
 *       [0, 3685076],
 *       [0, 3226025]
 *     ],
 *     [
 *       [100000, 3326025],
 *       [598545, 3326025],
 *       [598545, 3485076],
 *       [100000, 3485076],
 *       [100000, 3326025]
 *     ]
 *   ],
 *   // 要设置坐标系
 *   spatialReference: new SpatialReference({
 *     wkid: 4547
 *   })
 * })
 * // 判断多点几何是否包含点几何对象
 * const isContains = GeometryEngine.contains(circle, extent)
 * console.log("圆是否包含矩形:", isContains)
 * */
GeometryEngine.contains = function (containerGeometry, insideGeometry) {
  // 值必须存在,且继承自Geometry对象
  GeometryEngine._checkTwoGeometry(containerGeometry, insideGeometry)

  let _isContains = false
  let _point = undefined

  // 根据containerGeometry类型进行处理
  switch (containerGeometry.type) {
    case GeometryType.point:
      // 根据insideGeometry类型进行处理
      switch (insideGeometry.type) {
        // 判断点是否包含点
        case GeometryType.point:
          return GeometryEngine.equals(containerGeometry, insideGeometry)
        default:
          break
      }
      break
    case GeometryType.multiPoint:
      // 根据insideGeometry类型进行处理
      switch (insideGeometry.type) {
        // 判断多点是否包含点
        case GeometryType.point:
          return GeometryEngine._multiPointContainsPoint(
            containerGeometry,
            insideGeometry
          )
        // 判断多点是否包含多点
        case GeometryType.multiPoint:
          _isContains = true
          // 判断第一个几何是否完全包含第二个几何的点坐标
          for (let i = 0; i < insideGeometry.coordinates.length; i++) {
            _point = new Point({
              coordinates: insideGeometry.coordinates[i]
            })
            _isContains = GeometryEngine._lineContainPoint(
              containerGeometry,
              _point
            )
            if (!_isContains) {
              break
            }
          }
          return _isContains
        default:
          break
      }
      break
    case GeometryType.lineString:
      // 根据insideGeometry类型进行处理
      switch (insideGeometry.type) {
        // 判断线是否包含点
        case GeometryType.point:
          return GeometryEngine._lineContainPoint(
            containerGeometry,
            insideGeometry
          )
        // 判断线是否包含多点
        case GeometryType.multiPoint:
          _isContains = true
          for (let i = 0; i < insideGeometry.coordinates.length; i++) {
            _point = new Point({
              coordinates: insideGeometry.coordinates[i],
              spatialReference: SpatialReference.fromJSON(
                insideGeometry.spatialReference
              )
            })
            if (!GeometryEngine._lineContainPoint(containerGeometry, _point)) {
              _isContains = false
              break
            }
          }
          return _isContains
        // 判断线是否包含线
        case GeometryType.lineString:
          return GeometryEngine._lineContainLine(
            containerGeometry,
            insideGeometry
          )
        // 判断线是否包含多线
        case GeometryType.multiLineString:
          _isContains = true
          for (let i = 0; i < insideGeometry.coordinates.length; i++) {
            const _line = new LineString({
              coordinates: insideGeometry.coordinates[i],
              spatialReference: SpatialReference.fromJSON(
                insideGeometry.spatialReference
              )
            })
            if (!GeometryEngine._lineContainLine(containerGeometry, _line)) {
              _isContains = false
              break
            }
          }
          return _isContains
        default:
          break
      }
      break
    case GeometryType.multiLineString:
      // 根据insideGeometry类型进行处理
      switch (insideGeometry.type) {
        // 判断多线是否包含点
        case GeometryType.point:
          return GeometryEngine._multiLineContainPoint(
            containerGeometry,
            insideGeometry
          )
        // 判断多线是否包含多点
        case GeometryType.multiPoint:
          _isContains = true
          for (let i = 0; i < insideGeometry.coordinates.length; i++) {
            const _point = new Point({
              coordinates: insideGeometry.coordinates[i],
              spatialReference: SpatialReference.fromJSON(
                insideGeometry.spatialReference
              )
            })
            if (
              !GeometryEngine._multiLineContainPoint(containerGeometry, _point)
            ) {
              _isContains = false
              break
            }
          }
          return _isContains
        // 判断多线是否包含线
        case GeometryType.lineString:
          return GeometryEngine._multiLineContainLine(
            containerGeometry,
            insideGeometry
          )
        // 判断多线是否包含多线
        case GeometryType.multiLineString:
          _isContains = true
          for (let i = 0; i < insideGeometry.coordinates.length; i++) {
            const _line = new LineString({
              coordinates: insideGeometry.coordinates[i],
              spatialReference: SpatialReference.fromJSON(
                insideGeometry.spatialReference
              )
            })
            if (
              !GeometryEngine._multiLineContainLine(containerGeometry, _line)
            ) {
              _isContains = false
              break
            }
          }
          return _isContains
        default:
          break
      }
      break
    case GeometryType.polygon:
      switch (insideGeometry.type) {
        // 判断区是否包含点
        case GeometryType.point:
          return GeometryEngine._polygonContainPoint(
            containerGeometry,
            insideGeometry
          )
        // 判断区是否包含多点
        case GeometryType.multiPoint:
          _isContains = true
          for (let i = 0; i < insideGeometry.coordinates.length; i++) {
            const _point = new Point({
              coordinates: insideGeometry.coordinates[i]
            })
            if (
              !GeometryEngine._polygonContainPoint(containerGeometry, _point)
            ) {
              _isContains = false
              break
            }
          }
          return _isContains
        // 判断区是否包含线
        case GeometryType.lineString:
          return GeometryEngine._polygonContainLine(
            containerGeometry,
            insideGeometry
          )
        // 判断区是否包含多线
        case GeometryType.multiLineString:
          _isContains = true
          for (let i = 0; i < insideGeometry.coordinates.length; i++) {
            const _line = new LineString({
              coordinates: insideGeometry.coordinates[i]
            })
            if (!GeometryEngine._polygonContainLine(containerGeometry, _line)) {
              _isContains = false
              break
            }
          }
          return _isContains
        // 判断区是否包含区
        case GeometryType.polygon:
          return GeometryEngine._polygonContainPolygon(
            containerGeometry,
            insideGeometry
          )
        // 判断区是否包含多区
        case GeometryType.multiPolygon:
          _isContains = true
          for (let i = 0; i < insideGeometry.coordinates.length; i++) {
            const _polygon = new Polygon({
              coordinates: insideGeometry.coordinates[i]
            })
            if (
              !GeometryEngine._polygonContainPolygon(
                containerGeometry,
                _polygon
              )
            ) {
              _isContains = false
              break
            }
          }
          return _isContains
        // 判断区是否包含圆
        case GeometryType.circle:
          return GeometryEngine._polygonContainPolygon(
            containerGeometry,
            insideGeometry.toPolygon()
          )
        // 判断区是否包含矩形
        case GeometryType.extent:
          const _polygon = new Polygon({
            coordinates: [
              [
                [insideGeometry.xmin, insideGeometry.ymin],
                [insideGeometry.xmax, insideGeometry.ymin],
                [insideGeometry.xmax, insideGeometry.ymax],
                [insideGeometry.xmin, insideGeometry.ymax],
                [insideGeometry.xmin, insideGeometry.ymin]
              ]
            ]
          })
          return GeometryEngine._polygonContainPolygon(
            containerGeometry,
            _polygon
          )
        default:
          break
      }
      break
    case GeometryType.multiPolygon:
      // 根据insideGeometry类型进行处理
      switch (insideGeometry.type) {
        // 判断多区是否包含点
        case GeometryType.point:
          return GeometryEngine._multiPolygonContainsPoint(
            containerGeometry,
            insideGeometry
          )
        // 判断多区是否包含多点
        case GeometryType.multiPoint:
          return GeometryEngine._multiContainsMulti(
            containerGeometry,
            insideGeometry,
            Point,
            GeometryEngine._multiPolygonContainsPoint
          )
        // 判断多区是否包含线
        case GeometryType.lineString:
          return GeometryEngine._multiPolygonContainsLine(
            containerGeometry,
            insideGeometry
          )
        // 判断多区是否包含多线
        case GeometryType.multiLineString:
          return GeometryEngine._multiContainsMulti(
            containerGeometry,
            insideGeometry,
            LineString,
            GeometryEngine._multiPolygonContainsLine
          )
        // 判断多区是否包含区
        case GeometryType.polygon:
          return GeometryEngine._multiPolygonContainsPolygon(
            containerGeometry,
            insideGeometry
          )
        // 判断多区是否包含多区
        case GeometryType.multiPolygon:
          return GeometryEngine._multiContainsMulti(
            containerGeometry,
            insideGeometry,
            Polygon,
            GeometryEngine._multiPolygonContainsPolygon
          )
        // 判断多区是否包含圆
        case GeometryType.circle:
          _isContains = false
          for (let i = 0; i < containerGeometry.coordinates.length; i++) {
            const _polygon = new Polygon({
              coordinates: containerGeometry.coordinates[i]
            })
            if (
              GeometryEngine._polygonContainPolygon(
                _polygon,
                insideGeometry.toPolygon()
              )
            ) {
              _isContains = true
              break
            }
          }
          return _isContains
        // 判断多区是否包含矩形
        case GeometryType.extent:
          _isContains = false
          for (let i = 0; i < containerGeometry.coordinates.length; i++) {
            const _polygon = new Polygon({
              coordinates: containerGeometry.coordinates[i]
            })
            const _polygon2 = new Polygon({
              coordinates: [
                [
                  [insideGeometry.xmin, insideGeometry.ymin],
                  [insideGeometry.xmax, insideGeometry.ymin],
                  [insideGeometry.xmax, insideGeometry.ymax],
                  [insideGeometry.xmin, insideGeometry.ymax],
                  [insideGeometry.xmin, insideGeometry.ymin]
                ]
              ]
            })
            if (GeometryEngine._polygonContainPolygon(_polygon, _polygon2)) {
              _isContains = true
              break
            }
          }
          return _isContains
        default:
          break
      }
      break
    case GeometryType.circle:
      // 根据insideGeometry类型进行处理
      switch (insideGeometry.type) {
        // 判断圆是否包含点
        case GeometryType.point:
          return GeometryEngine._polygonContainPoint(
            containerGeometry.toPolygon(),
            insideGeometry
          )
        // 判断圆是否包含多点
        case GeometryType.multiPoint:
          return GeometryEngine._singleContainsMulti(
            containerGeometry.toPolygon(),
            insideGeometry,
            Point,
            GeometryEngine._polygonContainPoint
          )
        // 判断圆是否包含线
        case GeometryType.lineString:
          return GeometryEngine._polygonContainMultiPoint(
            containerGeometry.toPolygon(),
            insideGeometry
          )
        // 判断圆是否包含多线
        case GeometryType.multiLineString:
          return GeometryEngine._singleContainsMulti(
            containerGeometry.toPolygon(),
            insideGeometry,
            LineString,
            GeometryEngine._polygonContainMultiPoint
          )
        // 判断圆是否包含区
        case GeometryType.polygon:
          return GeometryEngine._polygonContainPolygon(
            containerGeometry.toPolygon(),
            insideGeometry
          )
        // 判断圆是否包含多区
        case GeometryType.multiPolygon:
          return GeometryEngine._singleContainsMulti(
            containerGeometry.toPolygon(),
            insideGeometry,
            Polygon,
            GeometryEngine._polygonContainPolygon
          )
        // 判断圆是否包含圆
        case GeometryType.circle:
          return GeometryEngine._polygonContainPolygon(
            containerGeometry.toPolygon(),
            insideGeometry.toPolygon()
          )
        // 判断圆是否包含矩形
        case GeometryType.extent:
          const _polygon = new Polygon({
            coordinates: [
              [
                [insideGeometry.xmin, insideGeometry.ymin],
                [insideGeometry.xmax, insideGeometry.ymin],
                [insideGeometry.xmax, insideGeometry.ymax],
                [insideGeometry.xmin, insideGeometry.ymax],
                [insideGeometry.xmin, insideGeometry.ymin]
              ]
            ]
          })
          return GeometryEngine._polygonContainPolygon(
            containerGeometry.toPolygon(),
            _polygon
          )
        default:
          break
      }
      break
    case GeometryType.extent:
      const _polygon = new Polygon({
        coordinates: [
          [
            [containerGeometry.xmin, containerGeometry.ymin],
            [containerGeometry.xmax, containerGeometry.ymin],
            [containerGeometry.xmax, containerGeometry.ymax],
            [containerGeometry.xmin, containerGeometry.ymax],
            [containerGeometry.xmin, containerGeometry.ymin]
          ]
        ]
      })
      // 根据insideGeometry类型进行处理
      switch (insideGeometry.type) {
        // 判断矩形是否包含点
        case GeometryType.point:
          return GeometryEngine._polygonContainPoint(_polygon, insideGeometry)
        // 判断矩形是否包含多点
        case GeometryType.multiPoint:
          return GeometryEngine._singleContainsMulti(
            _polygon,
            insideGeometry,
            Point,
            GeometryEngine._polygonContainPoint
          )
        // 判断矩形是否包含线
        case GeometryType.lineString:
          return GeometryEngine._polygonContainMultiPoint(
            _polygon,
            insideGeometry
          )
        // 判断矩形是否包含多线
        case GeometryType.multiLineString:
          return GeometryEngine._singleContainsMulti(
            _polygon,
            insideGeometry,
            LineString,
            GeometryEngine._polygonContainMultiPoint
          )
        // 判断矩形是否包含区
        case GeometryType.polygon:
          return GeometryEngine._polygonContainPolygon(_polygon, insideGeometry)
        // 判断矩形是否包含多区
        case GeometryType.multiPolygon:
          return GeometryEngine._singleContainsMulti(
            _polygon,
            insideGeometry,
            Polygon,
            GeometryEngine._polygonContainPolygon
          )
        // 判断矩形是否包含圆
        case GeometryType.circle:
          return GeometryEngine._polygonContainPolygon(
            _polygon,
            insideGeometry.toPolygon()
          )
        // 判断矩形是否包含矩形
        case GeometryType.extent:
          const _polygon2 = new Polygon({
            coordinates: [
              [
                [insideGeometry.xmin, insideGeometry.ymin],
                [insideGeometry.xmax, insideGeometry.ymin],
                [insideGeometry.xmax, insideGeometry.ymax],
                [insideGeometry.xmin, insideGeometry.ymax],
                [insideGeometry.xmin, insideGeometry.ymin]
              ]
            ]
          })
          return GeometryEngine._polygonContainPolygon(_polygon, _polygon2)
        default:
          break
      }
      break
    default:
      return false
  }
}

/**
 * 判断几何是否包含多几何
 * @private
 * @param {Point | LineString | Polygon} containerGeometry 几何对象
 * @param {MultiPoint | MultiLineString |  MultiPolygon} insideGeometry 多几何对象
 * @param {Function} construct 构造函数
 * @param {Function} func 执行函数函数
 * @return {Boolean} 是否包含
 * */
GeometryEngine._singleContainsMulti = function (
  containerGeometry,
  insideGeometry,
  construct,
  func
) {
  let _isContains = true
  for (let i = 0; i < insideGeometry.coordinates.length; i++) {
    // eslint-disable-next-line new-cap
    const _geometry = new construct({
      coordinates: insideGeometry.coordinates[i]
    })
    if (!func(containerGeometry, _geometry)) {
      _isContains = false
      break
    }
  }
  return _isContains
}

/**
 * 判断多几何是否包含多几何
 * @private
 * @param {MultiPoint | MultiLineString |  MultiPolygon} containerGeometry 多区对象
 * @param {Point | LineString | Polygon} insideGeometry 点对象
 * @param {Function} construct 构造函数
 * @param {Function} func 执行函数函数
 * @return {Boolean} 是否包含
 * */
GeometryEngine._multiContainsMulti = function (
  containerGeometry,
  insideGeometry,
  construct,
  func
) {
  let _isContains = true
  for (let i = 0; i < insideGeometry.coordinates.length; i++) {
    // eslint-disable-next-line new-cap
    const _geometry = new construct({
      coordinates: insideGeometry.coordinates[i]
    })
    if (!func(containerGeometry, _geometry)) {
      _isContains = false
      break
    }
  }
  return _isContains
}

/**
 * 判断多区是否包含点
 * @private
 * @param {MultiPolygon} containerGeometry 多区对象
 * @param {Point} insideGeometry 点对象
 * @return {Boolean} 是否包含
 * */
GeometryEngine._multiPolygonContainsPoint = function (
  containerGeometry,
  insideGeometry
) {
  let _isContains = false
  for (let i = 0; i < containerGeometry.coordinates.length; i++) {
    const _polygon = new Polygon({
      coordinates: containerGeometry.coordinates[i]
    })
    if (GeometryEngine._polygonContainPoint(_polygon, insideGeometry)) {
      _isContains = true
      break
    }
  }
  return _isContains
}

/**
 * 判断多区是否包含线
 * @private
 * @param {MultiPolygon} containerGeometry 多区对象
 * @param {LineString} insideGeometry 线对象
 * @return {Boolean} 是否包含
 * */
GeometryEngine._multiPolygonContainsLine = function (
  containerGeometry,
  insideGeometry
) {
  let _isContains = false
  for (let i = 0; i < containerGeometry.coordinates.length; i++) {
    const _polygon = new Polygon({
      coordinates: containerGeometry.coordinates[i]
    })
    if (GeometryEngine._polygonContainMultiPoint(_polygon, insideGeometry)) {
      _isContains = true
      break
    }
  }
  return _isContains
}

/**
 * 判断多区是否包含区
 * @private
 * @param {MultiPolygon} containerGeometry 多区对象
 * @param {Polygon} insideGeometry 区对象
 * @return {Boolean} 是否包含
 * */
GeometryEngine._multiPolygonContainsPolygon = function (
  containerGeometry,
  insideGeometry
) {
  let _isContains = false
  for (let i = 0; i < containerGeometry.coordinates.length; i++) {
    const _polygon = new Polygon({
      coordinates: containerGeometry.coordinates[i]
    })
    if (GeometryEngine._polygonContainPolygon(_polygon, insideGeometry)) {
      _isContains = true
      break
    }
  }
  return _isContains
}

/**
 * 判断线是否包含线
 * @private
 * @param {LineString} containerGeometry 要包含的线几何对象
 * @param {LineString} insideGeometry 被包含的线几何对象
 * @return {Boolean} 是否包含
 * */
GeometryEngine._lineContainLine = function (containerGeometry, insideGeometry) {
  const _line1 = T.lineString(containerGeometry.coordinates)
  const _line2 = T.lineString(insideGeometry.coordinates)
  let isContains = T.booleanContains(_line1, _line2)
  if (!isContains) {
    isContains = GeometryEngine.equals(containerGeometry, insideGeometry)
  }
  if (!isContains) {
    const _line3 = new LineString({
      coordinates: [
        insideGeometry.coordinates[1],
        insideGeometry.coordinates[0]
      ]
    })
    isContains = GeometryEngine.equals(containerGeometry, _line3)
  }
  return isContains
}

/**
 * 判断多线是否包含线
 * @private
 * @param {MultiLineString} containerGeometry 要包含的多线几何对象
 * @param {LineString} insideGeometry 被包含的线几何对象
 * @return {Boolean} 是否包含
 * */
GeometryEngine._multiLineContainLine = function (
  containerGeometry,
  insideGeometry
) {
  let _isContains = false
  for (let i = 0; i < containerGeometry.coordinates.length; i++) {
    const _line = new LineString({
      coordinates: containerGeometry.coordinates[i],
      spatialReference: SpatialReference.fromJSON(
        containerGeometry.spatialReference
      )
    })
    if (GeometryEngine._lineContainLine(_line, insideGeometry)) {
      _isContains = true
      break
    }
  }
  return _isContains
}

/**
 * 判断区是否包区,支持带洞区
 * @private
 * @param {Polygon} containerGeometry 要包含的区几何对象
 * @param {Polygon} insideGeometry 被包含的区几何对象
 * @return {Boolean} 是否包含
 * */
GeometryEngine._polygonContainPolygon = function (
  containerGeometry,
  insideGeometry
) {
  const _polygon1 = T.polygon(containerGeometry.coordinates)
  const _polygon2 = T.polygon(insideGeometry.coordinates)
  return T.booleanContains(_polygon1, _polygon2)
}

/**
 * <a id='distance'></a>
 * 计算两点之间的距离,坐标系取第一个图层的坐标系
 * @param {Point} geometry1 第一个几何体,需为point
 * @param {Point | LineString | Polygon | MultiPoint | MultiLineString |  MultiPolygon | Circle | Extent} geometry2 第二个几何体,可以为点、多点、线、多线、面、多面、圆、矩形
 * @param {String} unit 单位,默认为"kilometers"
 * @return {Number} 距离
 *
 * @example <caption><h5>点和点求距离</h5></caption>
 * // ES5引入方式
 * const { Point, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, Point } from "@mapgis/webclient-common"
 * // 点和点求距离
 * const point1 = new Point({
 *   coordinates: [1, 1]
 * })
 * const point2 = new Point({
 *   coordinates: [2, 1]
 * })
 * // 可以不传递单位,不传递时默认单位为 "kilometers"
 * const pointDistance = GeometryEngine.distance(point1, point2);
 * console.log("点和点求距离:", pointDistance)
 * // 可以传单位,如传入单位 'miles'
 * const pointDistance2 = GeometryEngine.distance(point1, point2, "miles")
 * console.log("点和点求距离(设置单位为"miles"):", pointDistance)
 *
 * @example <caption><h5>点和线求距离</h5></caption>
 * // ES5引入方式
 * const { Point, LineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, Point, LineString } from "@mapgis/webclient-common"
 * // 点和线求距离
 * const point1 = new Point({
 *   coordinates: [100.0, 100.0]
 * })
 * const lineString = new LineString({
 *   coordinates: [
 *        [100.0, 200.0],
 *        [101.0, 201.0],
 *        [103.0, 203.0],
 *      ],
 * })
 * const pointToLineDistance = GeometryEngine.distance(point1, lineString);
 * console.log("点和线求距离:", pointToLineDistance)
 *
 * @example <caption><h5>点和面求距离</h5></caption>
 * // ES5引入方式
 * const { Point, Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, Point, Polygon } from "@mapgis/webclient-common"
 * // 点和面求距离
 * const point1 = new Point({
 *   coordinates: [100.0, 100.0]
 * })
 * const polygon = new Polygon({
 *    coordinates: [
 *      [
 *        [100.0, 0.0],
 *        [101.0, 0.0],
 *        [101.0, 1.0],
 *        [100.0, 1.0],
 *        [100.0, 0.0],
 *      ],
 *    ],
 *  });
 * const pointToPolygonDistance = GeometryEngine.distance(point1, polygon);
 * console.log("点和面求距离:", pointToPolygonDistance)
 *
 * */
GeometryEngine.distance = function (geometry1, geometry2, unit) {
  // 1.先判断入参非空,unit可以为空
  if (isNull(geometry1)) {
    Log.error('geometry1不能为空!')
  }
  if (isNull(geometry2)) {
    Log.error('geometry2不能为空!')
  }
  // 2.入参类型检查
  if (!(geometry1 instanceof Point)) {
    Log.error('geometry1类型错误,需要输入Point!')
  }
  if (
    !(
      geometry2 instanceof Point ||
      geometry2 instanceof LineString ||
      geometry2 instanceof Polygon ||
      geometry2 instanceof MultiPoint ||
      geometry2 instanceof MultiLineString ||
      geometry2 instanceof MultiPolygon ||
      geometry2 instanceof Extent ||
      geometry2 instanceof Circle
    )
  ) {
    Log.error('geometry2类型错误!')
  }

  // 3.检查两个入参的坐标系是否相同
  if (!geometry1.spatialReference.equals(geometry2.spatialReference)) {
    Log.error('geometry1、geometry2坐标系不同!')
  }
  // 4.给默认值
  unit = defaultValue(unit, LengthUnit.kilometer)
  geometry1 = defaultValue(geometry1, undefined)
  geometry2 = defaultValue(geometry2, undefined)

  // 5.如果不是4326,则进行坐标系投影转为4326,因为上一步已经判断过两个入参的坐标系是否相同,所以只需要判断其中一个坐标系是否为WGS84即可
  if (!geometry1.spatialReference.isGeographic) {
    const _geometryPro1 = Projection.projectGeometry(
      geometry1,
      new SpatialReference({
        wkid: 4326
      })
    )
    geometry1 = _geometryPro1
    const _geometryPro2 = Projection.projectGeometry(
      geometry2,
      new SpatialReference({
        wkid: 4326
      })
    )
    geometry2 = _geometryPro2
  }

  // 6.通过判断geometry2的type进行距离计算
  if (!isNull(geometry1) && !isNull(geometry2)) {
    let distance
    let _geometry2
    let _polygon2
    let _coords = []
    const distanceArr = []
    const _geometry1 = T.point(geometry1.coordinates)
    switch (geometry2.type) {
      case GeometryType.point:
        _geometry2 = T.point(geometry2.coordinates)
        distance = T.distance(_geometry1, _geometry2, { units: unit })
        break
      case GeometryType.multiPoint:
        for (let i = 0; i < geometry2.coordinates.length; i++) {
          _coords.push(T.point(geometry2.coordinates[i]))
        }
        const points = T.featureCollection(_coords)
        const nearest = T.nearestPoint(_geometry1, points)
        distance = T.distance(_geometry1, nearest, { units: unit })
        break
      case GeometryType.lineString:
        _geometry2 = T.lineString(geometry2.coordinates)
        distance = T.pointToLineDistance(_geometry1, _geometry2, {
          units: unit
        })
        break
      case GeometryType.multiLineString:
        for (let i = 0; i < geometry2.coordinates.length; i++) {
          // i表示第i条线段
          // 分别计算点到每条线段的距离,然后找最小值
          _geometry2 = T.lineString(geometry2.coordinates[i])
          distanceArr.push(
            T.pointToLineDistance(_geometry1, _geometry2, {
              units: unit
            })
          )
        }
        distanceArr.sort((a, b) => {
          return a - b
        })
        distance = distanceArr[0]
        break
      case GeometryType.polygon:
        for (let i = 0; i < geometry2.coordinates[0].length; i++) {
          _coords.push(geometry2.coordinates[0][i])
        }
        _geometry2 = T.lineString(_coords)
        distance = T.pointToLineDistance(_geometry1, _geometry2, {
          units: unit
        })
        break
      case GeometryType.multiPolygon:
        // 1.先遍历,多区=》区
        for (let i = 0; i < geometry2.coordinates.length; i++) {
          // i表示第i个区
          // 然后再获取单个区的外圈
          _coords = []
          for (let j = 0; j < geometry2.coordinates[i][0].length; j++) {
            _coords.push(geometry2.coordinates[i][0][j])
          }
          _geometry2 = T.lineString(_coords)
          distanceArr.push(
            T.pointToLineDistance(_geometry1, _geometry2, {
              units: unit
            })
          )
        }
        distanceArr.sort((a, b) => {
          return a - b
        })
        distance = distanceArr[0]
        break
      case GeometryType.circle:
        _polygon2 = T.polygon(geometry2.toPolygon().coordinates)
        for (let i = 0; i < _polygon2.geometry.coordinates[0].length; i++) {
          _coords.push(_polygon2.geometry.coordinates[0][i])
        }
        _geometry2 = T.lineString(_coords)
        distance = T.pointToLineDistance(_geometry1, _geometry2, {
          units: unit
        })
        break
      case GeometryType.extent:
        const _polygon = new Polygon({
          coordinates: [
            [
              [geometry2.xmin, geometry2.ymin],
              [geometry2.xmax, geometry2.ymin],
              [geometry2.xmax, geometry2.ymax],
              [geometry2.xmin, geometry2.ymax],
              [geometry2.xmin, geometry2.ymin]
            ]
          ]
        })
        for (let i = 0; i < _polygon.coordinates[0].length; i++) {
          _coords.push(_polygon.coordinates[0][i])
        }
        _geometry2 = T.lineString(_coords)
        distance = T.pointToLineDistance(_geometry1, _geometry2, {
          units: unit
        })
        break
      default:
        break
    }

    return distance
  }
}

/**
 * <a id='equals'></a>
 * 判断两个几何是否相等;<br/>
 * 使用如下判断标准:<br/>
 * 1、类型是否相等;<br/>
 * 2、坐标系是否相等;<br/>
 * 3、两个几何的坐标点是否相等;<br/>
 * 支持任意几何类型;<br/>
 * @param {Geometry} [geometry1 = null] 第一个几何对象
 * @param {Geometry} [geometry2 = null] 第二个几何对象
 * @return {Boolean} 是否相等
 * @example <caption><h5>判断几何是否相等</h5></caption>
 * const isEquals = GeometryEngine.equals(new Point({
 *   coordinates: [1, 10]
 * }), new Point({
 *   coordinates: [1, 10]
 * }))
 * console.log("点和点是否相同:", isEquals)
 * */
GeometryEngine.equals = function (geometry1, geometry2) {
  // 值必须存在,且继承自Geometry对象
  if (isNull(geometry1)) {
    Log.error('geometry1对象为空!')
  }
  if (isNull(geometry2)) {
    Log.error('geometry2对象为空!')
  }
  if (!(geometry1 instanceof Geometry)) {
    Log.error('geometry1对象未继承于Geometry对象!')
  }
  if (!(geometry2 instanceof Geometry)) {
    Log.error('geometry2对象未继承于Geometry对象!')
  }

  // 判断类型是否相同
  if (geometry1.type !== geometry2.type) {
    return false
  }

  // 判断坐标系是否相同
  if (!geometry1.spatialReference.equals(geometry2.spatialReference)) {
    return false
  }

  // 判断坐标点是否相同
  switch (geometry1.type) {
    case GeometryType.point:
      if (!geometry1.equals(geometry2)) {
        return false
      }
      break
    case GeometryType.polygon:
      return GeometryEngine._equalPolygon(geometry1, geometry2)
    case GeometryType.lineString:
      return GeometryEngine._equalLinePoint(geometry1, geometry2)
    case GeometryType.extent:
      // 判断xmin、ymin、xmax、ymax是否有任意一个不同
      if (
        geometry1.xmin !== geometry2.xmin ||
        geometry1.ymin !== geometry2.ymin ||
        geometry1.xmax !== geometry2.xmax ||
        geometry1.ymax !== geometry2.ymax
      ) {
        return false
      }
      break
    case GeometryType.circle:
      // 判断radius或radiusUnit是否不相等
      if (
        geometry1.radius !== geometry2.radius ||
        geometry1.radiusUnit !== geometry2.radiusUnit
      ) {
        return false
      }
      // 判断center是否相等
      // 先判断子元素数量
      if (geometry1.center.length !== geometry2.center.length) {
        return false
      }
      // 在判断子元素的值
      for (let i = 0; i < geometry1.center.length; i++) {
        if (geometry1.center[i] !== geometry2.center[i]) {
          return false
        }
      }
      break
    case GeometryType.multiPoint:
      return GeometryEngine._equalLinePoint(geometry1, geometry2)
    case GeometryType.multiLineString:
      // 先判断子元素数量
      if (geometry1.coordinates.length !== geometry2.coordinates.length) {
        return false
      }
      // 再判断各个线段是否相等
      for (let i = 0; i < geometry1.coordinates.length; i++) {
        const _line1 = new LineString({
          coordinates: geometry1.coordinates[i],
          spatialReference: geometry1.spatialReference
        })
        const _line2 = new LineString({
          coordinates: geometry2.coordinates[i],
          spatialReference: geometry2.spatialReference
        })
        if (!GeometryEngine.equals(_line1, _line2)) {
          return false
        }
      }
      break
    case GeometryType.multiPolygon:
      // 先判断子元素数量
      if (geometry1.coordinates.length !== geometry2.coordinates.length) {
        return false
      }
      // 再判断各个区是否相等
      for (let i = 0; i < geometry1.coordinates.length; i++) {
        const _polygon1 = new Polygon({
          coordinates: geometry1.coordinates[i],
          spatialReference: geometry1.spatialReference
        })
        const _polygon2 = new Polygon({
          coordinates: geometry2.coordinates[i],
          spatialReference: geometry2.spatialReference
        })
        if (!GeometryEngine.equals(_polygon1, _polygon2)) {
          return false
        }
      }
      break
    default:
      break
  }

  return true
}

/**
 * 判断线或多点的点是否相等
 * @private
 * @param {Point | LineString} geometry1 第一个几何对象
 * @param {Point | LineString} geometry2 第二个几何对象
 * @return {Boolean} 是否相等
 * */
GeometryEngine._equalLinePoint = function (geometry1, geometry2) {
  // 先判断线里面点的数量是否相等
  if (geometry1.coordinates.length !== geometry2.coordinates.length) {
    return false
  }
  // 判断点坐标是否相同
  for (let i = 0; i < geometry1.coordinates.length; i++) {
    const _point1 = new Point({
      coordinates: geometry1.coordinates[i]
    })
    const _point2 = new Point({
      coordinates: geometry2.coordinates[i]
    })
    if (!_point1.equals(_point2)) {
      return false
    }
  }

  return true
}

/**
 * 判断区的点是否相等
 * @private
 * @param {Polygon} geometry1 第一个几何对象
 * @param {Polygon} geometry2 第二个几何对象
 * @return {Boolean} 是否相等
 * */
GeometryEngine._equalPolygon = function (geometry1, geometry2) {
  // 先判断环的数量
  if (geometry1.coordinates.length !== geometry2.coordinates.length) {
    return false
  }
  // 判断环里面点的数量
  for (let i = 0; i < geometry1.coordinates.length; i++) {
    if (geometry1.coordinates[i].length !== geometry2.coordinates[i].length) {
      return false
    }
  }
  // 判断点坐标是否相同
  for (let i = 0; i < geometry1.coordinates.length; i++) {
    const _line1 = new LineString({
      coordinates: geometry1.coordinates[i],
      spatialReference: geometry1.spatialReference
    })
    const _line2 = new LineString({
      coordinates: geometry2.coordinates[i],
      spatialReference: geometry2.spatialReference
    })
    if (!GeometryEngine.equals(_line1, _line2)) {
      return false
    }
  }

  return true
}

/**
 * <a id='disjoint'></a>
 * 判断两个几何是否相离,即完全不相交;<br/>
 * 支持自定义坐标系几何对象,两个几何对象的坐标系要一致,忽略三维高度;<br/>
 * 示例如下:<br/>
 * <a href='#disjoint1'>[是否相离]</a><br/>
 * @param {Geometry} [geometry1 = null] 第一个几何对象
 * @param {Geometry} [geometry2 = null] 第二个几何对象
 * @return {Boolean} 是否相离
 *
 * @example <caption><h5 id='disjoint1'>是否相离</h5></caption>
 * // ES5引入方式
 * const { LineString, Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, Polygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造线几何对象
 * const line = new LineString({
 *   coordinates: [
 *     [113, 28],
 *     [113, 34]
 *   ]
 * })
 * // 构造区几何对象
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [108, 30],
 *       [115, 30],
 *       [115, 33],
 *       [108, 33],
 *       [108, 30]
 *     ],
 *     [
 *       [110, 31],
 *       [113, 31],
 *       [113, 32],
 *       [110, 32],
 *       [110, 31]
 *     ]
 *   ]
 * })
 * // 开始求交
 * const isIntersect = GeometryEngine.disjoint(line, polygon)
 * console.log("是否相离:", isIntersect)
 * */
GeometryEngine.disjoint = function (geometry1, geometry2) {
  return !GeometryEngine.intersects(geometry1, geometry2)
}

/**
 * <a id='within'></a>
 * 判断innerGeometry几何对象是否完全在outerGeometry中
 * @param {Geometry} innerGeometry 内部几何对象
 * @param {Geometry} outerGeometry 外部几何对象
 * @return {Boolean} 是否完全包含
 * @example <caption><h5>判断是否包含</h5></caption>
 * // ES5引入方式
 * const { Polygon, Point, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine, Polygon, Point } from "@mapgis/webclient-common"
 * // 初始点几何,可以是任意几何
 * const point = new Point({
 *   coordinates: [1, 1]
 * })
 * // 初始化区几何,可以是任意几何
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [1, 0],
 *       [10, 0],
 *       [10, 20],
 *       [1, 20],
 *       [1, 0]
 *     ]
 *   ]
 * })
 * // 判断点几何是否被区几何对象包含
 * const isContains = GeometryEngine.within(polygon, point)
 * console.log("点几何是否被区几何对象包含:", isContains)
 * */
GeometryEngine.within = function (innerGeometry, outerGeometry) {
  return GeometryEngine.contains(outerGeometry, innerGeometry)
}

/**
 * <a id='crosses'></a>
 * 判断一个几何对象是否穿过另一个几何对象;<br/>
 * 要穿过的几何类型为:线、多线,忽略三维高度;<br/>
 * 被穿过的几何类型为:线、多线、矩形、圆、区以及多区,忽略三维高度,穿线和被穿过几何的坐标系必须相同;<br/>
 * 要穿过的几何与被穿过的几何(geometry1和geometry2)可以调换输入,但其中一个geometry必须是LineString、MultiLineString中的一类;<br/>
 * 当被穿过的几何类型为MultiLineString、MultiPolygon时,只要MultiLineString和MultiPolygon中有一个要素被穿过,就算穿过,返回true;<br/>
 * 当要穿过的几何类型为MultiLineString时,只要MultiLineString中有一条线穿过geometry2(被穿过的几何体),就算穿过,返回true;<br/>
 * 如果两条线出现重叠,此种情况不算穿过,返回false;<br/
 * 如果两条线出现“入”型、“L”型,此种情况不算穿过,返回false;<br/>
 * 线与圆相切的情况不算穿过,返回false;<br/>
 * 线和多边形的一条边重叠,算穿过,返回true;<br/>
 * 当polygon有内圈时,只要穿过外圈就算穿过,返回true;<br/>
 * 示例如下:<br/>
 * <a href='#cross1'>[1、线穿过线]</a><br/>
 * <a href='#cross2'>[2、线穿过多线]</a><br/>
 * <a href='#cross3'>[3、线穿过区]</a><br/>
 * <a href='#cross4'>[4、多线穿过区]</a><br/>
 * <a href='#cross5'>[5、多线穿过多区]</a><br/>
 * @param {LineString | MultiLineString} geometry1 第一个几何体,支持线、多线;
 * @param {LineString | Polygon | MultiLineString |  MultiPolygon | Circle | Extent} geometry2 第二个几何体,支持线、多线、面、多面、圆、矩形,两个参数可以调换输入,但其中一个geometry必须是LineString、MultiLineString中的一类;
 * @return {Boolean} 是否穿过
 * @example <caption><h5 id='cross1'>线穿过线</h5></caption>
 * // ES5引入方式
 * const { LineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造要穿过的线对象
 * const line1 = new LineString({
 *   coordinates: [
 *     [108.36341, 29.032578],
 *     [116.13094, 29.032578]
 *   ]
 * })
 * // 构造被穿过的线对象
 * const line2 = new LineString({
 *   coordinates: [
 *     [110, 28],
 *     [110, 30],
 *     [111, 30],
 *     [111, 28]
 *   ]
 * })
 * // 开始判断穿过
 * const cross = GeometryEngine.crosses(line1, line2)
 * console.log("线是否穿过线:", cross)
 *
 * @example <caption><h5 id='cross2'>线穿过多线</h5></caption>
 * // ES5引入方式
 * const { LineString, MultiLineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, MultiLineString, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造要穿过的线对象
 * const line1 = new LineString({
 *   coordinates: [
 *     [108.36341, 29.032578],
 *     [116.13094, 29.032578]
 *   ]
 * })
 * // 构造被穿过的多线对象
 *  const multiLineString = new MultiLineString({
 *   coordinates: [
 *     [
 *       [108.36341, 29.032578],
 *       [116.13094, 29.032578]
 *     ],
 *     [
 *       [108.36341, 29.332578],
 *       [116.13094, 29.332578]
 *     ]
 *   ]
 * })
 * // 开始判断穿过
 * const cross = GeometryEngine.crosses(line1, multiLineString)
 * console.log("线是否穿过多线:", cross)
 *
 * @example <caption><h5 id='cross3'>线穿过区</h5></caption>
 * // ES5引入方式
 * const { LineString, Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, Polygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造要穿过的线对象
 * const line = new LineString({
 *   coordinates: [
 *     [110, 28],
 *     [110, 34],
 *     [111, 34],
 *     [112, 28]
 *   ]
 * })
 * // 构造被穿过的区对象
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [108.36341, 29.032578],
 *       [116.13094, 29.032578],
 *       [116.13094, 33.273224],
 *       [108.36341, 33.273224],
 *       [108.36341, 29.032578],
 *     ],
 *     [
 *       [109, 29.5],
 *       [112, 29.5],
 *       [112, 32],
 *       [109, 32],
 *       [109, 29.5],
 *     ]
 *   ]
 * })
 * // 开始判断穿过
 * const cross = GeometryEngine.crosses(line,polygon)
 * console.log("线是否穿过区:", cross)
 *
 * @example <caption><h5 id='cross4'>多线穿过区</h5></caption>
 * // ES5引入方式
 * const { MultiLineString, Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { MultiLineString, Polygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造要穿过的多线对象
 *  const multiLineString = new MultiLineString({
 *   coordinates: [
 *     [
 *       [108.36341, 29.032578],
 *       [116.13094, 29.032578]
 *     ],
 *     [
 *       [108.36341, 29.332578],
 *       [116.13094, 29.332578]
 *     ]
 *   ]
 * })
 * // 构造被穿过的区对象
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [108.36341, 29.032578],
 *       [116.13094, 29.032578],
 *       [116.13094, 33.273224],
 *       [108.36341, 33.273224],
 *       [108.36341, 29.032578],
 *     ],
 *     [
 *       [109, 29.5],
 *       [112, 29.5],
 *       [112, 32],
 *       [109, 32],
 *       [109, 29.5],
 *     ]
 *   ]
 * })
 * // 开始判断穿过
 * const cross = GeometryEngine.crosses( multiLineString,polygon)
 * console.log("多线是否穿过区:", cross)
 *
 * @example <caption><h5 id='cross5'>多线穿过多区</h5></caption>
 * // ES5引入方式
 * const { MultiLineString, MultiPolygon, SpatialReference, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { MultiLineString, MultiPolygon, SpatialReference, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造要穿过的多线对象
 *  const multiLineString = new MultiLineString({
 *   coordinates: [
 *     [
 *       [-49522.6942635386, 3226025.20474049],
 *       [707596.826929215, 3214754.20105]
 *     ],
 *     [
 *       [-47918.3624481045, 3259361.82328017],
 *       [707596.826929215, 3259361.82328017]
 *     ]
 *   ],
 *   // 必须指定坐标系
 *   spatialReference: new SpatialReference({
 *     wkid: 4547
 *   })
 * })
 * // 构造被穿过的多区
 * const multiPolygon = new MultiPolygon({
 *   coordinates: [
 *     [
 *       [
 *         [-47918.3624481045, 3214754.20105],
 *         [707596.826929215, 3214754.20105],
 *         [707596.826929215, 3685076.66188228],
 *         [-47918.3624481045, 3685076.66188228],
 *         [-47918.3624481045, 3214754.20105]
 *       ],
 *       [
 *         [14849.3479447439, 3275130.08942507],
 *         [310996.760637403, 3275130.08942507],
 *         [310996.760637403, 3543600.9315051],
 *         [14849.3479447439, 3543600.9315051],
 *         [14849.3479447439, 3275130.08942507],
 *       ]
 *     ],
 *     [
 *       [
 *         [813421.283605543, 3214754.20105],
 *         [1071620.19329204, 3214754.20105],
 *         [1071620.19329204, 3685076.66188228],
 *         [813421.283605543, 3685076.66188228],
 *         [813421.283605543, 3214754.20105]
 *       ]
 *     ]
 *   ],
 *   // 必须指定坐标系
 *   spatialReference: new SpatialReference({
 *     wkid: 4547
 *   })
 * })
 * // 开始判断穿过
 * const cross = GeometryEngine.crosses(multiLineString, multiPolygon)
 * console.log("多线是否穿过多区:", cross)
 * */
GeometryEngine.crosses = function (geometry1, geometry2) {
  // 1.先判断入参非空
  if (isNull(geometry1)) {
    Log.error('geometry1不能为空!')
  }
  if (isNull(geometry2)) {
    Log.error('geometry2不能为空!')
  }
  // 3.检查两个入参的坐标系是否相同
  if (!geometry1.spatialReference.equals(geometry2.spatialReference)) {
    Log.error('geometry1、geometry2坐标系不同!')
  }
  if (geometry1 instanceof LineString || geometry1 instanceof MultiLineString) {
    return GeometryEngine._crosses(geometry1, geometry2)
  } else if (
    geometry2 instanceof LineString ||
    geometry2 instanceof MultiLineString
  ) {
    const _geometry = geometry1
    geometry1 = geometry2
    geometry2 = _geometry
    return GeometryEngine._crosses(geometry1, geometry2)
  } else {
    Log.error(
      'geometry1和geometry2中至少有一个参数类型必须为lineString和multiLineString中的一个!'
    )
  }
}
/**
 * 判断一个几何对象是否和另一个几何对象相交
 * @private
 * @param {LineString | MultiLineString} geometry1 内部几何对象,支持LineString或者MultiLineString类型
 * @param {LineString | Polygon | MultiLineString |  MultiPolygon | Circle | Extent} geometry2 外部几何对象,第二个几何体,可以为线、多线、面、多面、圆、矩形
 * @return {Boolean} 是否相交
 * */
GeometryEngine._crosses = function (geometry1, geometry2) {
  // 2.geometry2入参类型检查
  if (
    !(
      geometry2 instanceof LineString ||
      geometry2 instanceof Polygon ||
      geometry2 instanceof MultiLineString ||
      geometry2 instanceof MultiPolygon ||
      geometry2 instanceof Extent ||
      geometry2 instanceof Circle
    )
  ) {
    Log.error('geometry2类型错误!')
  }

  // 4.给默认值
  geometry1 = defaultValue(geometry1, undefined)
  geometry2 = defaultValue(geometry2, undefined)

  // 5.如果不是4326,则进行坐标系投影转为4326,因为上一步已经判断过两个入参的坐标系是否相同,所以只需要判断其中一个坐标系是否为WGS84即可
  if (!geometry1.spatialReference.isGeographic) {
    const _geometryPro1 = Projection.projectGeometry(
      geometry1,
      new SpatialReference({
        wkid: 4326
      })
    )
    geometry1 = _geometryPro1
    const _geometryPro2 = Projection.projectGeometry(
      geometry2,
      new SpatialReference({
        wkid: 4326
      })
    )
    geometry2 = _geometryPro2
  }

  // 6.通过判断geometry2的type进行cross计算
  if (!isNull(geometry1) && !isNull(geometry2)) {
    let cross
    let _geometry1
    let _geometry2
    switch (geometry1.type) {
      case GeometryType.lineString:
        switch (geometry2.type) {
          case GeometryType.lineString:
            _geometry1 = GeometryEngine._geometryToTurf(geometry1)
            _geometry2 = GeometryEngine._geometryToTurf(geometry2)
            cross = T.booleanCrosses(_geometry1, _geometry2)
            if (!cross) {
              // 如果不穿过,再进行一次判断保障
              if (GeometryEngine.intersects(geometry1, geometry2)) {
                const _coords = []
                // 如果相交,说明出现“十”、“L”型、“入”型、“中”型情况,需要进一步判断
                // 先排查是否为“中”型,也就是说geometry2是首尾相连的线
                // 先判断点是否在线的首尾两端
                const linePoint = new MultiPoint({
                  coordinates: [
                    geometry2.coordinates[0],
                    geometry2.coordinates[geometry2.coordinates.length - 1]
                  ]
                })
                // 判断线的首尾端是否重合
                if (
                  linePoint.coordinates[0].toString() ===
                  linePoint.coordinates[1].toString()
                ) {
                  // 如果是“中”型,且相交(上面判断过),这里一共会出现四种情况:1.线和闭合线有一个交点,但一种情况是线1端点在闭合线2上,这种不算穿过,另一种是线1穿过闭合线的一边,这种算穿过;
                  // 2.线和闭合线有两个交点,算穿过;
                  // 综上,先判断线1和闭合线有几个交点,有两个,cross为true;有一个,判断是不是线geometry1的一端在geometry2上,如果是,不算穿过,反之则算穿过;
                  const intersect = GeometryEngine.intersect(
                    geometry1,
                    geometry2
                  )
                  // 如果返回的是多点,则证明穿过
                  if (intersect.point.type === 'MultiPoint') {
                    cross = true
                  } else if (intersect.point.type === 'Point') {
                    // 判断线1端点是否在线2上
                    const line1Point1 = new Point({
                      coordinates: geometry1.coordinates[0]
                    })
                    const line1Point2 = new Point({
                      coordinates:
                        geometry1.coordinates[geometry1.coordinates.length - 1]
                    })
                    if (
                      GeometryEngine.contains(geometry2, line1Point1) ||
                      GeometryEngine.contains(geometry2, line1Point2)
                    ) {
                      cross = false
                    } else {
                      cross = true
                    }
                  }
                } else {
                  // 排除“中”型的情况后,进一步排查“十”、“L”型、“入”型
                  // 先判断是不是相连的情况,如果是,返回false,如果不是相连,说明是“十”型,则应返回true
                  for (let i = 0; i < geometry1.coordinates.length; i++) {
                    _coords.push(geometry1.coordinates[i])
                  }
                  for (let i = 0; i < geometry2.coordinates.length; i++) {
                    _coords.push(geometry2.coordinates[i])
                  }
                  const _points = T.multiPoint(_coords)
                  // 去除重复点
                  const _pointsClean = T.cleanCoords(_points)
                  // 只要剩下的点数量小于clean前的数量,则一定是相连线
                  if (
                    _pointsClean.geometry.coordinates.length <
                    _points.geometry.coordinates.length
                  ) {
                    // 出现相连的情况,L型,此时cross应为false
                    cross = false
                  } else {
                    // 出现“入”型情况,通过判断一条线上的所有点,有无某点出现在另一条线上,若出现,则证明是“入”型情况,应返回false
                    let con
                    for (let i = 0; i < geometry1.coordinates.length; i++) {
                      // 一旦检查到线上包含另一条线的某点,停止检查
                      if (con) {
                        cross = !con
                        break
                      }
                      const inputPoint = new Point({
                        coordinates: geometry1.coordinates[i]
                      })
                      con = GeometryEngine.contains(geometry2, inputPoint)
                    }
                    for (let i = 0; i < geometry2.coordinates.length; i++) {
                      if (con) {
                        cross = !con
                        break
                      }
                      const inputPoint = new Point({
                        coordinates: geometry2.coordinates[i]
                      })
                      con = GeometryEngine.contains(geometry1, inputPoint)
                    }
                    if (!con) {
                      // 说明两条线相交,但既不是“L”型,也不是“入”型,那就只能是“十”型,cross为true
                      cross = true
                    }
                    break
                  }
                }
              }
            }
            break
          case GeometryType.multiLineString:
            for (let i = 0; i < geometry2.coordinates.length; i++) {
              // i表示第i条线段
              // 分别检查线1与多线中每条线段是否cross,只要有一条线穿过,停止检查,cross为true
              if (cross) break
              _geometry2 = new LineString({
                coordinates: geometry2.coordinates[i]
              })
              cross = GeometryEngine._crosses(geometry1, _geometry2)
            }
            break
          case GeometryType.polygon:
          case GeometryType.extent:
          case GeometryType.circle:
            _geometry1 = GeometryEngine._geometryToTurf(geometry1)
            _geometry2 = GeometryEngine._geometryToTurf(geometry2)
            cross = T.booleanCrosses(_geometry1, _geometry2)
            if (cross) {
              // 再检查一遍是否是只有线的一个点和polygon相交,这种情况也会返回true
              const intersect = GeometryEngine.intersect(geometry1, geometry2)
              // 只要相交的部分是点,说明line与polygon只有一点相接,这种情况不算穿过
              if (intersect.type === 'Point') {
                cross = false
              }
            }
            break
          case GeometryType.multiPolygon:
            // 1.先遍历,多区=》区
            for (let i = 0; i < geometry2.coordinates.length; i++) {
              if (cross) break
              // i表示第i个区
              _geometry2 = new Polygon({
                coordinates: geometry2.coordinates[i]
              })
              cross = GeometryEngine._crosses(geometry1, _geometry2)
            }
            break
          default:
            break
        }
        break
      case GeometryType.multiLineString:
        for (let i = 0; i < geometry1.coordinates.length; i++) {
          // i表示第i条线段
          // 分别检查线1与多线中每条线段是否cross,只要有一条线穿过,停止检查,cross为true
          if (cross) break
          _geometry1 = new LineString({
            coordinates: geometry1.coordinates[i]
          })
          cross = GeometryEngine._crosses(_geometry1, geometry2)
        }
        break
      default:
        break
    }
    return cross
  }
}

/**
 * <a id='touches'></a>
 * 判断一个几何对象是否和另一个几何对象相邻
 * 被测试相邻关系的几何类型为:线、多线、矩形、圆、区以及多区,忽略三维高度;<br/>
 * 用来测试相邻关系的几何类型为:点、多点、线、多线、矩形、圆、区以及多区,忽略三维高度,穿线和被穿过几何的坐标系必须相同;<br/>
 * 线和点相邻的标准为:点在线的两端,且线不是头尾闭合的;<br/>
 * 对于multi类型的几何(如多点、多线、多区),无论其和任何其他支持的几何类型判断touch,只要multi中的一个与另一个geometry有包含关系、相交关系,touch即为false;<br/>
 * 对于区、线、圆、矩形和多点判断相邻关系时,只有有一个点在区内、线上、矩形内、圆内,touch即为false;<br/>
 * 示例如下:<br/>
 * <a href='#touch1'>[1、线和点相邻]</a><br/>
 * <a href='#touch2'>[2、多线和多点相邻]</a><br/>
 * <a href='#touch3'>[3、区和线相邻]</a><br/>
 * <a href='#touch4'>[4、多区和区相邻]</a><br/>
 * <a href='#touch5'>[5、圆和圆相邻]</a><br/>
 * @param {LineString | Polygon | MultiLineString |  MultiPolygon | Circle | Extent} geometry1 用来测试相邻关系的几何,支持线、多线、区、多区、圆、矩形
 * @param {Point | LineString | Polygon | MultiPoint | MultiLineString |  MultiPolygon | Circle | Extent} geometry2 被测试相邻关系的几何,支持点、多点、线、多线、区、多区、圆、矩形
 * @return {Boolean} 是否相邻
 * @example <caption><h5 id='touch1'>线和点相邻</h5></caption>
 * // ES5引入方式
 * const { Point, LineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Point, LineString, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造线对象
 * const line1 = new LineString({
 *   coordinates: [
 *     [107.36341, 31.032578],
 *     [110, 31.032578],
 *     [117.13094, 31.032578],
 *     [107.36341, 31.032578],
 *   ]
 * })
 * // 构造点对象
 * const point = new Point({
 *   coordinates: [107.36341, 31.032578],
 * });
 * // 开始判断相邻
 * const isTouch = GeometryEngine.touches(line1, point)
 * console.log("线和点是否相邻:", isTouch)
 *
 * @example <caption><h5 id='touch2'>多线和多点相邻</h5></caption>
 * // ES5引入方式
 * const { MultiPoint, MultiLineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { MultiPoint, MultiLineString, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造多线对象
 *  const multiLineString = new MultiLineString({
 *   coordinates: [
 *     [
 *       [112, 31],
 *       [112, 33],
 *     ],
 *     [
 *       [113, 31],
 *       [113, 33],
 *     ],
 *   ]
 * })
 * // 构造多点对象
 *    const multiPoint = new MultiPoint({
 *      coordinates: [
 *        [113, 32],
 *        [113, 31],
 *      ],
 *    });
 * // 开始判断相邻
 * const isTouch = GeometryEngine.touches(multiLineString,multiPoint)
 * console.log("多线和多点是否相邻:", isTouch)
 *
 * @example <caption><h5 id='touch3'>区和线相邻</h5></caption>
 * // ES5引入方式
 * const { LineString, Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, Polygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造区对象
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [108, 30],
 *       [115, 30],
 *       [115, 33],
 *       [108, 33],
 *       [108, 30],
 *     ],
 *     [
 *       [110, 31],
 *       [113, 31],
 *       [113, 32],
 *       [110, 32],
 *       [110, 31],
 *     ],
 *   ]
 * })
 * // 构造线对象
 * const line = new LineString({
 *   coordinates: [
 *     [105, 30],
 *     [108, 32],
 *   ]
 * })
 * // 开始判断相邻
 * const isTouch = GeometryEngine.touches(polygon, line)
 * console.log("区和线是否相邻:", isTouch)
 *
 * @example <caption><h5 id='touch4'>多区和区相邻</h5></caption>
 * // ES5引入方式
 * const { MultiPolygon, Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { MultiPolygon, Polygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造多区对象
 *  const multiPolygon = new MultiPolygon({
 *      coordinates: [
 *        [
 *          [
 *            [105.0, 35.0],
 *            [110.0, 35.0],
 *            [110.0, 36.0],
 *            [105.0, 36.0],
 *            [105.0, 35.0],
 *          ],
 *        ],
 *        [
 *          [
 *            [108, 30],
 *            [115, 30],
 *            [115, 33],
 *            [108, 33],
 *            [108, 30],
 *          ],
 *          [
 *            [110, 31],
 *            [113, 31],
 *            [113, 32],
 *            [110, 32],
 *            [110, 31],
 *          ],
 *        ],
 *      ],
 *    });
 * // 构造区对象
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [110, 33],
 *       [113, 33],
 *       [113, 37],
 *       [110, 37],
 *       [110, 33],
 *     ],
 *   ]
 * })
 * // 开始判断相邻
 * const isTouch = GeometryEngine.touches( multiPolygon, polygon)
 * console.log("多区是否和区相邻:", isTouch)
 *
 * @example <caption><h5 id='touch5'>圆和圆相邻</h5></caption>
 * // ES5引入方式
 * const { Circle, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Circle, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造圆对象
 *  const circle1 = new Circle({
 *      // 中心点
 *      center: [113, 34],
 *      // 半径
 *      radius: 1,
 *    });
 * // 构造圆对象
 *  const circle2 = new Circle({
 *    // 中心点
 *    center: [115, 34],
 *    // 半径
 *    radius: 2,
 *  });
 * // 开始判断相邻
 * const isTouch = GeometryEngine.touches(circle1, circle2)
 * console.log("圆和圆是否相邻:", isTouch)
 * */
GeometryEngine.touches = function (geometry1, geometry2) {
  // 1.先判断入参非空,unit可以为空
  if (isNull(geometry1)) {
    Log.error('geometry1不能为空!')
  }
  if (isNull(geometry2)) {
    Log.error('geometry2不能为空!')
  }
  // 2.入参类型检查
  if (
    !(
      geometry1 instanceof LineString ||
      geometry1 instanceof Polygon ||
      geometry1 instanceof MultiLineString ||
      geometry1 instanceof MultiPolygon ||
      geometry1 instanceof Extent ||
      geometry1 instanceof Circle
    )
  ) {
    Log.error(
      'geometry1类型错误,需要输入LineString、Polygon、MultiLineString、MultiPolygon、Circle、Extent!'
    )
  }
  if (
    !(
      geometry2 instanceof Point ||
      geometry2 instanceof LineString ||
      geometry2 instanceof Polygon ||
      geometry2 instanceof MultiPoint ||
      geometry2 instanceof MultiLineString ||
      geometry2 instanceof MultiPolygon ||
      geometry2 instanceof Extent ||
      geometry2 instanceof Circle
    )
  ) {
    Log.error('geometry2类型错误!')
  }

  // 3.检查两个入参的坐标系是否相同
  if (!geometry1.spatialReference.equals(geometry2.spatialReference)) {
    Log.error('geometry1、geometry2坐标系不同!')
  }
  // 4.给默认值
  geometry1 = defaultValue(geometry1, undefined)
  geometry2 = defaultValue(geometry2, undefined)
  // 5.如果不是4326,则进行坐标系投影转为4326,因为上一步已经判断过两个入参的坐标系是否相同,所以只需要判断其中一个坐标系是否为WGS84即可
  if (!geometry1.spatialReference.isGeographic) {
    const _geometryPro1 = Projection.projectGeometry(
      geometry1,
      new SpatialReference({
        wkid: 4326
      })
    )
    geometry1 = _geometryPro1
    const _geometryPro2 = Projection.projectGeometry(
      geometry2,
      new SpatialReference({
        wkid: 4326
      })
    )
    geometry2 = _geometryPro2
  }

  // 6.通过判断geometry1的type,然后进一步判断geometry2的type
  if (!isNull(geometry1) && !isNull(geometry2)) {
    let isTouch = false
    let _geometry1
    let _geometry2
    const _coords = []

    switch (geometry1.type) {
      case GeometryType.lineString:
        switch (geometry2.type) {
          case GeometryType.point:
            // 先判断点是否在线的首尾两端
            const linePoint = new MultiPoint({
              coordinates: [
                geometry1.coordinates[0],
                geometry1.coordinates[geometry1.coordinates.length - 1]
              ]
            })
            if (GeometryEngine.contains(linePoint, geometry2)) {
              // 判断线的首尾端是否重合
              if (
                !(
                  linePoint.coordinates[0].toString() ===
                  linePoint.coordinates[1].toString()
                )
              ) {
                isTouch = true
              }
            }
            break
          case GeometryType.multiPoint:
            for (let i = 0; i < geometry2.coordinates.length; i++) {
              const point = new Point({
                coordinates: geometry2.coordinates[i]
              })
              const _isTouch = GeometryEngine.touches(geometry1, point)
              if (_isTouch) {
                isTouch = _isTouch
              } else if (GeometryEngine.intersects(geometry1, point)) {
                isTouch = false
                break
              }
            }
            break
          case GeometryType.lineString:
            // 先判断线和线有无交点
            const lineIntersect = GeometryEngine.intersect(geometry1, geometry2)
            if (lineIntersect.point) {
              const line1Point1 = new Point({
                coordinates: geometry1.coordinates[0]
              })
              const line1Point2 = new Point({
                coordinates:
                  geometry1.coordinates[geometry1.coordinates.length - 1]
              })
              if (
                GeometryEngine.contains(geometry2, line1Point1) ||
                GeometryEngine.contains(geometry2, line1Point2)
              ) {
                // 判断线1和线2是否穿过
                if (GeometryEngine.crosses(geometry1, geometry2)) {
                  isTouch = false
                } else {
                  isTouch = true
                }
              } else {
                // 判断geometry2的首尾两端是否在geometry1上
                const line2Point1 = new Point({
                  coordinates: geometry2.coordinates[0]
                })
                const line2Point2 = new Point({
                  coordinates:
                    geometry2.coordinates[geometry2.coordinates.length - 1]
                })
                if (
                  GeometryEngine.contains(geometry1, line2Point1) ||
                  GeometryEngine.contains(geometry1, line2Point2)
                ) {
                  // 判断线1和线2是否穿过
                  if (GeometryEngine.crosses(geometry1, geometry2)) {
                    isTouch = false
                  } else {
                    isTouch = true
                  }
                } else {
                  isTouch = false
                }
              }
            } else {
              isTouch = false
            }
            break
          case GeometryType.multiLineString:
            for (let i = 0; i < geometry2.coordinates.length; i++) {
              // i表示第i条线段
              _geometry2 = new LineString({
                coordinates: geometry2.coordinates[i]
              })
              const _isTouch = GeometryEngine.touches(geometry1, _geometry2)
              if (_isTouch) {
                isTouch = _isTouch
              } else if (GeometryEngine.intersects(geometry1, _geometry2)) {
                isTouch = false
                break
              }
            }
            break
          case GeometryType.polygon:
            for (let i = 0; i < geometry2.coordinates[0].length; i++) {
              _coords.push(geometry2.coordinates[0][i])
            }
            _geometry2 = new LineString({
              coordinates: _coords
            })
            // 先判断线和 区的外圈线是否相交
            if (GeometryEngine.intersects(geometry1, _geometry2)) {
              // 线是否穿过区的外圈线
              if (!GeometryEngine.crosses(geometry1, _geometry2)) {
                isTouch = true
              }
            }
            break
          case GeometryType.multiPolygon:
            // 1.先遍历,多区=》区
            for (let i = 0; i < geometry2.coordinates.length; i++) {
              // i表示第i个区
              _geometry2 = new Polygon({
                coordinates: geometry2.coordinates[i]
              })
              // 分别判断线和多区中的每个区的touch关系
              const _isTouch = GeometryEngine.touches(geometry1, _geometry2)
              if (_isTouch) {
                isTouch = _isTouch
              } else if (GeometryEngine.intersects(geometry1, _geometry2)) {
                isTouch = false
                break
              }
            }
            break
          case GeometryType.circle:
            // 调用nearestCoordinate求得线(geometry1)离圆心最近的一点
            const circleCenter = new Point({
              coordinates: geometry2.center
            })
            const nearestPoint = GeometryEngine.nearestCoordinate(
              geometry1,
              circleCenter
            )
            // 求最近点离圆心的距离
            const line = new LineString({
              coordinates: [geometry2.center, nearestPoint.geometry.coordinates]
            })

            const distance = GeometryEngine.planarLength(line)
            // 如果线上最近点和圆心的距离 与 圆的半径一致,则证明线和圆相邻
            if (Math.round(distance) === geometry2.radius) {
              isTouch = true
            }
            break
          case GeometryType.extent:
            const _polygon = new Polygon({
              coordinates: [
                [
                  [geometry2.xmin, geometry2.ymin],
                  [geometry2.xmax, geometry2.ymin],
                  [geometry2.xmax, geometry2.ymax],
                  [geometry2.xmin, geometry2.ymax],
                  [geometry2.xmin, geometry2.ymin]
                ]
              ]
            })
            isTouch = GeometryEngine.touches(geometry1, _polygon)
            break
          default:
            break
        }
        break
      case GeometryType.multiLineString:
        // 遍历多线geometry1,判断每条线和geometry2的关系
        for (let i = 0; i < geometry1.coordinates.length; i++) {
          // i表示第i条线段
          _geometry1 = new LineString({
            coordinates: geometry1.coordinates[i]
          })
          const _isTouch = GeometryEngine.touches(_geometry1, geometry2)
          if (_isTouch) {
            isTouch = _isTouch
          } else if (GeometryEngine.intersects(_geometry1, geometry2)) {
            isTouch = false
            break
          }
        }
        break
      case GeometryType.polygon:
        switch (geometry2.type) {
          case GeometryType.point:
            // 先判断区是否包含点
            if (!GeometryEngine.contains(geometry1, geometry2)) {
              // 将区的内外圈转为多线
              for (let i = 0; i < geometry1.coordinates.length; i++) {
                _coords.push(geometry1.coordinates[i])
              }
              _geometry1 = new MultiLineString({
                coordinates: _coords
              })
              if (GeometryEngine.contains(_geometry1, geometry2)) {
                isTouch = true
              }
            }
            break
          case GeometryType.multiPoint:
            for (let i = 0; i < geometry2.coordinates.length; i++) {
              const point = new Point({
                coordinates: geometry2.coordinates[i]
              })
              const _isTouch = GeometryEngine.touches(geometry1, point)
              if (_isTouch) {
                isTouch = _isTouch
              } else if (GeometryEngine.contains(geometry1, point)) {
                isTouch = false
                break
              }
            }
            break
          case GeometryType.lineString:
          case GeometryType.multiLineString:
            // 也就是判断线和区、多线和区是否相邻
            isTouch = GeometryEngine.touches(geometry2, geometry1)
            break
          case GeometryType.polygon:
            // 先判断区和区是否相交
            if (GeometryEngine.intersects(geometry1, geometry2)) {
              // 如果相交,进一步求得相交的部分
              if (!GeometryEngine.intersect(geometry1, geometry2)) {
                isTouch = true
              }
            }
            break
          case GeometryType.multiPolygon:
            // 1.先遍历,多区=》区
            for (let i = 0; i < geometry2.coordinates.length; i++) {
              // i表示第i个区
              _geometry2 = new Polygon({
                coordinates: geometry2.coordinates[i]
              })
              // 分别判断区和多区中的第i个区的touch关系
              const _isTouch = GeometryEngine.touches(geometry1, _geometry2)
              if (_isTouch) {
                isTouch = _isTouch
              } else if (GeometryEngine.intersects(geometry1, _geometry2)) {
                isTouch = false
                break
              }
            }
            break
          case GeometryType.circle:
            // 1.区外圈转线
            for (let i = 0; i < geometry1.coordinates[0].length; i++) {
              _coords.push(geometry1.coordinates[0][i])
            }
            _geometry1 = new LineString({
              coordinates: _coords
            })
            // 2.求得线上离圆心最近的点
            const circleCenter = new Point({
              coordinates: geometry2.center
            })
            const nearestPoint = GeometryEngine.nearestCoordinate(
              _geometry1,
              circleCenter
            )
            // 3.求最近点离圆心的距离
            const line = new LineString({
              coordinates: [geometry2.center, nearestPoint.geometry.coordinates]
            })
            const distance = GeometryEngine.planarLength(line)
            // 4.判断线上最近点和圆心的距离 与 圆的半径是否一致
            if (Math.round(distance) === geometry2.radius) {
              // 5.求区和圆的交点,区和圆交点返回的是undefined
              if (!GeometryEngine.intersect(geometry1, geometry2)) {
                isTouch = true
              }
            }
            break
          case GeometryType.extent:
            const _polygon = new Polygon({
              coordinates: [
                [
                  [geometry2.xmin, geometry2.ymin],
                  [geometry2.xmax, geometry2.ymin],
                  [geometry2.xmax, geometry2.ymax],
                  [geometry2.xmin, geometry2.ymax],
                  [geometry2.xmin, geometry2.ymin]
                ]
              ]
            })
            isTouch = GeometryEngine.touches(geometry1, _polygon)
            break
          default:
            break
        }
        break
      case GeometryType.multiPolygon:
        switch (geometry2.type) {
          case GeometryType.point:
            // 1.先遍历,多区=》区
            for (let i = 0; i < geometry1.coordinates.length; i++) {
              // i表示第i个区
              _geometry1 = new Polygon({
                coordinates: geometry1.coordinates[i]
              })
              // 分别判断多区中的第i个区和geometry2的touch关系
              const _isTouch = GeometryEngine.touches(_geometry1, geometry2)
              if (_isTouch) {
                isTouch = _isTouch
              } else if (GeometryEngine.contains(_geometry1, geometry2)) {
                isTouch = false
                break
              }
            }
            break
          case GeometryType.multiPoint:
            for (let i = 0; i < geometry2.coordinates.length; i++) {
              const point = new Point({
                coordinates: geometry2.coordinates[i]
              })
              const _isTouch = GeometryEngine.touches(geometry1, point)
              if (_isTouch) {
                isTouch = _isTouch
              } else if (GeometryEngine.contains(geometry1, point)) {
                isTouch = false
                break
              }
            }
            break
          case GeometryType.lineString:
          case GeometryType.multiLineString:
          case GeometryType.polygon:
            isTouch = GeometryEngine.touches(geometry2, geometry1)
            break
          case GeometryType.multiPolygon:
            // 1.先遍历,多区=》区
            for (let i = 0; i < geometry2.coordinates.length; i++) {
              // i表示第i个区
              _geometry2 = new Polygon({
                coordinates: geometry2.coordinates[i]
              })
              // 分别判断多区1和多区2中的第i个区的touch关系
              const _isTouch = GeometryEngine.touches(geometry1, _geometry2)
              if (_isTouch) {
                isTouch = _isTouch
              } else if (GeometryEngine.intersects(geometry1, _geometry2)) {
                isTouch = false
                break
              }
            }
            break
          case GeometryType.extent:
            const _polygon = new Polygon({
              coordinates: [
                [
                  [geometry2.xmin, geometry2.ymin],
                  [geometry2.xmax, geometry2.ymin],
                  [geometry2.xmax, geometry2.ymax],
                  [geometry2.xmin, geometry2.ymax],
                  [geometry2.xmin, geometry2.ymin]
                ]
              ]
            })
            isTouch = GeometryEngine.touches(geometry1, _polygon)
            break
          case GeometryType.circle:
            // 1.先遍历,多区=》区
            for (let i = 0; i < geometry1.coordinates.length; i++) {
              // i表示第i个区
              _geometry1 = new Polygon({
                coordinates: geometry1.coordinates[i]
              })
              // 分别判断多区1中第i个区和圆的touch关系
              const _isTouch = GeometryEngine.touches(_geometry1, geometry2)
              if (_isTouch) {
                isTouch = _isTouch
              } else if (GeometryEngine.intersects(_geometry1, geometry2)) {
                isTouch = false
                break
              }
            }
            break
          default:
            break
        }
        break
      case GeometryType.extent:
        const _polygon = new Polygon({
          coordinates: [
            [
              [geometry1.xmin, geometry1.ymin],
              [geometry1.xmax, geometry1.ymin],
              [geometry1.xmax, geometry1.ymax],
              [geometry1.xmin, geometry1.ymax],
              [geometry1.xmin, geometry1.ymin]
            ]
          ]
        })
        isTouch = GeometryEngine.touches(_polygon, geometry2)
        break
      case GeometryType.circle:
        switch (geometry2.type) {
          case GeometryType.point:
            // 求点离圆心的距离
            const line = new LineString({
              coordinates: [geometry1.center, geometry2.coordinates]
            })
            const distance = GeometryEngine.planarLength(line)
            // 如果点和圆心的距离 与 圆的半径一致,则证明点和圆相邻
            if (Math.round(distance) === geometry1.radius) {
              isTouch = true
            }
            break
          case GeometryType.multiPoint:
            for (let i = 0; i < geometry2.coordinates.length; i++) {
              const point = new Point({
                coordinates: geometry2.coordinates[i]
              })
              const _isTouch = GeometryEngine.touches(geometry1, point)
              if (_isTouch) {
                isTouch = _isTouch
              } else {
                // 计算多点中第i个点到圆心距离
                const line = new LineString({
                  coordinates: [geometry1.center, point.coordinates]
                })
                const distance = GeometryEngine.planarLength(line)
                // 如果点和圆心的距离 小于 圆的半径,则证明点在圆内
                if (Math.round(distance) < geometry1.radius) {
                  isTouch = false
                  break
                }
              }
            }
            break
          case GeometryType.lineString:
          case GeometryType.multiLineString:
          case GeometryType.polygon:
          case GeometryType.multiPolygon:
          case GeometryType.extent:
            isTouch = GeometryEngine.touches(geometry2, geometry1)
            break
          case GeometryType.circle:
            // 计算两个圆心距离
            const lineCenter = new LineString({
              coordinates: [geometry1.center, geometry2.center]
            })
            const distanceCenter = GeometryEngine.planarLength(lineCenter)
            // 如果点和圆心的距离 与 圆的半径一致,则证明点和圆相邻
            if (
              Math.round(distanceCenter) ===
              geometry1.radius + geometry2.radius
            ) {
              isTouch = true
            }
            break
          default:
            break
        }
        break
      default:
        break
    }
    return isTouch
  }
}

/**
 * <a id='cut'></a>
 * 使用一条线来切割几何,将其分割为多个几何对象;<br/>
 * 被切割的几何类型为:线、多线、区以及多区,忽略三维高度,切线和被切割几何的坐标系必须相同;<br/>
 * 此接口不会像arcgis的geometryEngine.cut方法那样,返回切割线左右两边的几何对象数组,而是把所有的被切割好的几何组成一个数组并返回;<br/>
 * 如果切割线和被切割几何没有相交,则会返回一个空数组;<br/>
 * 线切割线会返回被切割后的线几何数组;<br/>
 * 线切割多线仅会返回被切割后的线几何数组,如果多线中的某一个线段不与切割线相交,则不会返回该线段几何;<br/>
 * 线切割区会返回被切割后的区几何数组,如果切线仅仅是部分和区相交,但是没有穿过几何,会返回一个仅包含该区几何对像的数组;<br/>
 * 线切割多区仅会返回被切割后的区几何数组,如果多区中的某一个区不与切割线相交,则不会返回该区几何;<br/>
 * 示例如下:<br/>
 * <a href='#cut1'>[1、线切割线]</a><br/>
 * <a href='#cut2'>[2、线切割多线]</a><br/>
 * <a href='#cut3'>[3、线切割区]</a><br/>
 * <a href='#cut4'>[4、线切割多区]</a><br/>
 * <a href='#cut5'>[5、几何切割 - 自定义坐标系]</a><br/>
 * @param {LineString | MultiLineString| Polygon | MultiPolygon} geometry 被切割的区对象
 * @param {LineString} cutter 切割线几何对象
 * @return {Array<Geometry>} 切割后的几何数组
 * @example <caption><h5 id='cut1'>线切割线</h5></caption>
 * // ES5引入方式
 * const { LineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造被切割的线对象
 * const line = new LineString({
 *   coordinates: [
 *     [108.36341, 29.032578],
 *     [116.13094, 29.032578]
 *   ]
 * })
 * // 构造切割线对象
 * const cutter = new LineString({
 *   coordinates: [
 *     [110, 28],
 *     [110, 30],
 *     [111, 30],
 *     [111, 28]
 *   ]
 * })
 * // 开始切割线
 * const cut = GeometryEngine.cut(line, cutter)
 * console.log("切割后的几何数组:", cut)
 *
 * @example <caption><h5 id='cut2'>线切割多线</h5></caption>
 * // ES5引入方式
 * const { LineString, MultiLineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, MultiLineString, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造被切割的多线对象
 * const line = new MultiLineString({
 *   coordinates: [
 *     [
 *       [108.36341, 29.032578],
 *       [116.13094, 29.032578]
 *     ],
 *     [
 *       [108.36341, 29.332578],
 *       [116.13094, 29.332578]
 *     ]
 *   ]
 * })
 * // 构造切割线对象
 * const cutter = new LineString({
 *   coordinates: [
 *     [110, 28],
 *     [110, 30],
 *     [111, 30],
 *     [112, 28]
 *   ]
 * })
 * // 开始切割线
 * const cut = GeometryEngine.cut(line, cutter)
 * console.log("切割后的几何数组:", cut)
 *
 * @example <caption><h5 id='cut3'>线切割区</h5></caption>
 * // ES5引入方式
 * const { LineString, Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, Polygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造被切割的区对象
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [108.36341, 29.032578],
 *       [116.13094, 29.032578],
 *       [116.13094, 33.273224],
 *       [108.36341, 33.273224],
 *       [108.36341, 29.032578],
 *     ],
 *     [
 *       [109, 29.5],
 *       [112, 29.5],
 *       [112, 32],
 *       [109, 32],
 *       [109, 29.5],
 *     ]
 *   ]
 * })
 * // 构造切割线对象
 * const cutter = new LineString({
 *   coordinates: [
 *     [110, 28],
 *     [110, 34],
 *     [111, 34],
 *     [112, 28]
 *   ]
 * })
 * // 开始切割区
 * const cut = GeometryEngine.cut(polygon, cutter)
 * console.log("切割后的几何数组:", cut)
 *
 * @example <caption><h5 id='cut4'>线切割多区</h5></caption>
 * // ES5引入方式
 * const { LineString, MultiPolygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, MultiPolygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造被切割的多区
 * const polygon = new MultiPolygon({
 *   coordinates: [
 *     [
 *       [
 *         [108.36341, 29.032578],
 *         [116.13094, 29.032578],
 *         [116.13094, 33.273224],
 *         [108.36341, 33.273224],
 *         [108.36341, 29.032578]
 *       ],
 *       [
 *         [109, 29.5],
 *         [112, 29.5],
 *         [112, 32],
 *         [109, 32],
 *         [109, 29.5],
 *       ]
 *     ],
 *     [
 *       [
 *         [117.36341, 29.032578],
 *         [120.13094, 29.032578],
 *         [120.13094, 33.273224],
 *         [117.36341, 33.273224],
 *         [117.36341, 29.032578]
 *       ]
 *     ]
 *   ]
 * })
 * // 构造切割线对象
 * const cutter = new LineString({
 *   coordinates: [
 *     [110, 28],
 *     [110, 34],
 *     [111, 34],
 *     [112, 28],
 *     [118, 28],
 *     [119, 34]
 *   ]
 * })
 * // 开始切割多区
 * const cut = GeometryEngine.cut(polygon, cutter)
 * console.log("切割后的几何数组:", cut)
 *
 * @example <caption><h5 id='cut5'>几何切割 - 自定义坐标系</h5></caption>
 * // ES5引入方式
 * const { LineString, Polygon, SpatialReference, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, Polygon, SpatialReference, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造被切割的区对象
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [0, 3226025],
 *       [698545, 3226025],
 *       [698545, 3685076],
 *       [0, 3685076],
 *       [0, 3226025]
 *     ],
 *     [
 *       [100000, 3326025],
 *       [598545, 3326025],
 *       [598545, 3485076],
 *       [100000, 3485076],
 *       [100000, 3326025]
 *     ]
 *   ],
 *   // 必须指定坐标系
 *   spatialReference: new SpatialReference({
 *     wkid: 4547
 *   })
 * })
 * // 构造切割线对象
 * const cutter = new LineString({
 *   coordinates: [
 *     [106372, 3104899],
 *     [130347, 3770883],
 *     [222797, 3767721],
 *     [303253, 3100054]
 *   ],
 *   // 必须指定坐标系
 *   spatialReference: new SpatialReference({
 *     wkid: 4547
 *   })
 * })
 * // 开始切割区
 * const cut = GeometryEngine.cut(polygon, cutter)
 * console.log("切割后的几何数组:", cut)
 * */
GeometryEngine.cut = function (geometry, cutter) {
  // 非空判断
  if (isNull(geometry)) {
    Log.error('geometry不能为空!')
  }
  if (isNull(cutter)) {
    Log.error('cutter不能为空!')
  }

  // 类型判断
  if (
    !(
      geometry instanceof LineString ||
      geometry instanceof MultiLineString ||
      geometry instanceof Polygon ||
      geometry instanceof MultiPolygon
    )
  ) {
    Log.error('geometry类型错误!')
  }
  if (!(cutter instanceof LineString)) {
    Log.error('cutter类型错误!')
  }

  if (!geometry.spatialReference.equals(cutter.spatialReference)) {
    Log.error('geometry对象和cutter对象的坐标系不同!')
  }

  // 判断是否相交
  if (!GeometryEngine.intersects(geometry, cutter)) {
    return []
  }

  // 根据类型进行切割
  let _geometry = []
  let _inputGeometry = undefined
  let _cutter = undefined
  switch (geometry.type) {
    case GeometryType.lineString:
      _geometry = GeometryEngine._lineCutLine(geometry, cutter)
      break
    case GeometryType.multiLineString:
      for (let i = 0; i < geometry.coordinates.length; i++) {
        const _line = new LineString({
          coordinates: geometry.coordinates[i],
          spatialReference: SpatialReference.fromJSON(geometry.spatialReference)
        })
        _geometry = _geometry.concat(GeometryEngine._lineCutLine(_line, cutter))
      }
      break
    case GeometryType.polygon:
      // 将非经纬度的转到类似经纬度的坐标下
      _inputGeometry = undefined
      if (!geometry.spatialReference.isGeographic) {
        const _param = GeometryEngine._polygonAndCutterToWGS84(geometry, cutter)
        _inputGeometry = _param.polygon
        _cutter = _param.cutter
      } else {
        _inputGeometry = geometry
        _cutter = cutter
      }
      _geometry = GeometryEngine._lineCutPolygon(_inputGeometry, _cutter)
      // 将类经纬度坐标转到原坐标下
      if (!geometry.spatialReference.isGeographic) {
        for (let i = 0; i < _geometry.length; i++) {
          _geometry[i] = GeometryEngine._geometryToOrigin(_geometry[i])
        }
      }
      break
    case GeometryType.multiPolygon:
      // 将非经纬度的转到类似经纬度的坐标下
      _inputGeometry = undefined
      if (!geometry.spatialReference.isGeographic) {
        const _param = GeometryEngine._polygonAndCutterToWGS84(geometry, cutter)
        _inputGeometry = _param.polygon
        _cutter = _param.cutter
      } else {
        _inputGeometry = geometry
        _cutter = cutter
      }
      for (let i = 0; i < _inputGeometry.coordinates.length; i++) {
        const _polygon = new Polygon({
          coordinates: _inputGeometry.coordinates[i],
          spatialReference: SpatialReference.fromJSON(geometry.spatialReference)
        })
        _geometry = _geometry.concat(
          GeometryEngine._lineCutPolygon(_polygon, _cutter)
        )
      }
      // 将类经纬度坐标转到原坐标下
      if (!geometry.spatialReference.isGeographic) {
        for (let i = 0; i < _geometry.length; i++) {
          _geometry[i] = GeometryEngine._geometryToOrigin(_geometry[i])
        }
      }
      break
    default:
      break
  }

  return _geometry
}

GeometryEngine._polygonAndCutterToWGS84 = function (polygon, cutter) {
  // 将非经纬度的转到类似经纬度的坐标下
  let _polygon = undefined
  if (!polygon.spatialReference.isGeographic) {
    _polygon = GeometryEngine._geometryToWGS84(polygon)
  } else {
    _polygon = polygon
  }
  let _cutter = undefined
  if (!cutter.spatialReference.isGeographic) {
    _cutter = GeometryEngine._geometryToWGS84(cutter)
  } else {
    _cutter = cutter
  }

  return {
    polygon: _polygon,
    cutter: _cutter
  }
}

/**
 * 线切割线
 * @private
 * @param {LineString} line 被切割的线对象
 * @param {LineString} cutter 切割线对象
 * @return {Array<LineString>} 切割后的线对象数组
 * */
GeometryEngine._lineCutLine = function (line, cutter) {
  const _geometry = []
  // 生成turf对象
  const _line1 = T.lineString(line.coordinates)
  const _line2 = T.lineString(cutter.coordinates)
  // 开始切割
  const _split = T.lineSplit(_line1, _line2)
  // 构造切割后的几何数组
  for (let i = 0; i < _split.features.length; i++) {
    _geometry.push(
      new LineString({
        coordinates: _split.features[i].geometry.coordinates,
        spatialReference: SpatialReference.fromJSON(line.spatialReference)
      })
    )
  }
  return _geometry
}

/**
 * 线切割单多边形
 * @private
 * @param {Polygon} polygon 被切割的区几何对象
 * @param {LineString} cutter 切割线对象
 * @return {Array<LineString>} 切割后的区对象数组
 * */
GeometryEngine._lineCutPolygon = function (polygon, cutter) {
  let _geometry = []
  // 将外圈转化为线
  const _outline = new LineString({
    coordinates: polygon.coordinates[0]
  })
  // 转为turf对象
  const _line1 = T.lineString(_outline.coordinates)
  let _line2 = T.lineString(cutter.coordinates)
  // 先判断切线是否和区相交,不相交直接返回区
  if (T.booleanDisjoint(_line2, _outline)) {
    return polygon.clone()
  }
  // 过滤不和区相交的线
  const _newCutters = []
  for (let i = 0; i < cutter.coordinates.length - 1; i++) {
    const _oneCutter = T.lineString([
      cutter.coordinates[i],
      cutter.coordinates[i + 1]
    ])
    if (!T.booleanDisjoint(_oneCutter, _outline)) {
      _newCutters.push(cutter.coordinates[i], cutter.coordinates[i + 1])
    }
  }
  _line2 = T.lineString(_newCutters)
  // 去重点坐标
  _line2 = T.cleanCoords(_line2)
  // 分割外边线
  const _outlineSplit = T.lineSplit(_line1, _line2)
  // 分割切线
  const _cutterSplit = T.lineSplit(_line2, _line1)
  // 剔除完全包含在几何中的切割线
  const _cutterLine = []
  // 生成外圈矩形
  const _outerPolygon = new Polygon({
    coordinates: [polygon.coordinates[0]]
  })
  for (let i = 0; i < _cutterSplit.features.length; i++) {
    const _line = new LineString({
      coordinates: _cutterSplit.features[i].geometry.coordinates
    })
    if (GeometryEngine.contains(_outerPolygon, _line)) {
      _cutterLine.push(_cutterSplit.features[i])
    }
  }
  // 通过剩余的线段构造新的多边形
  _outlineSplit.features = _outlineSplit.features.concat(_cutterLine)
  // 控制小数点位数为7位以内
  for (let i = 0; i < _outlineSplit.features.length; i++) {
    for (
      let j = 0;
      j < _outlineSplit.features[i].geometry.coordinates.length;
      j++
    ) {
      _outlineSplit.features[i].geometry.coordinates[j][0] =
        _outlineSplit.features[i].geometry.coordinates[j][0].toFixed(7) / 1
      _outlineSplit.features[i].geometry.coordinates[j][1] =
        _outlineSplit.features[i].geometry.coordinates[j][1].toFixed(7) / 1
    }
  }
  // 通过turf将线转为区
  const _polygons = T.polygonize(_outlineSplit)
  for (let i = 0; i < _polygons.features.length; i++) {
    const _polygon = new Polygon({
      coordinates: _polygons.features[i].geometry.coordinates,
      spatialReference: SpatialReference.fromJSON(polygon.spatialReference)
    })
    _geometry.push(_polygon)
  }
  // 如果原几何带洞,则去掉洞几何
  if (polygon.coordinates.length > 1) {
    for (let i = 0; i < _geometry.length; i++) {
      for (let j = 1; j < polygon.coordinates.length; j++) {
        const _innerPolygon = new Polygon({
          coordinates: [polygon.coordinates[j]]
        })
        const _innerPolygonT = T.polygon(_innerPolygon.coordinates)
        const _geometryT = T.polygon(_geometry[i].coordinates)
        // 只有相交才删除洞几何
        if (!T.booleanDisjoint(_innerPolygonT, _geometryT)) {
          _geometry[i] = GeometryEngine.difference(_geometry[i], _innerPolygon)
        }
      }
    }
  }
  // 将多区拆分为单区
  const _newGeometryArr = []
  for (let i = 0; i < _geometry.length; i++) {
    if (_geometry[i].type === GeometryType.multiPolygon) {
      for (let j = 0; j < _geometry[i].coordinates.length; j++) {
        _newGeometryArr.push(
          new Polygon({
            coordinates: _geometry[i].coordinates[j],
            spatialReference: SpatialReference.fromJSON(
              polygon.spatialReference
            )
          })
        )
      }
    } else {
      _newGeometryArr.push(_geometry[i])
    }
  }
  _geometry = _newGeometryArr
  return _geometry
}

/**
 * 将高斯坐标转到类经纬度的坐标中,方便turf计算
 * @private
 * @param {Array} coordinate 坐标点数组
 * @return {Array} 转化后的坐标点数组
 * */
GeometryEngine._coordinateToWGS84 = function (coordinate) {
  const x = (coordinate[0] / 20037508.34) * 180
  const y = (coordinate[1] / 20037508.34) * 180
  return [x.toFixed(7) / 1, y.toFixed(7) / 1]
}

/**
 * 将类经纬度坐标转到原始的坐标中
 * @private
 * @param {Array} coordinate 坐标点数组
 * @return {Array} 转化后的坐标点数组
 * */
GeometryEngine._coordinateToOrigin = function (coordinate) {
  const x = (coordinate[0] * 20037508.34) / 180
  const y = (coordinate[1] * 20037508.34) / 180
  return [x, y]
}

/**
 * 将几何对象的坐标转到类经纬度的坐标中,方便turf计算
 * @private
 * @param {Geometry} geometry 几何对象
 * @return {Geometry} 转化后的几何对象
 * */
GeometryEngine._geometryToWGS84 = function (geometry) {
  let _geometry = undefined
  switch (geometry.type) {
    case GeometryType.polygon:
      _geometry = Polygon.fromJSON(geometry.toJSON())
      for (let i = 0; i < _geometry.coordinates.length; i++) {
        for (let j = 0; j < _geometry.coordinates[i].length; j++) {
          _geometry.coordinates[i][j] = GeometryEngine._coordinateToWGS84(
            _geometry.coordinates[i][j]
          )
        }
      }
      break
    case GeometryType.multiPolygon:
      _geometry = MultiPolygon.fromJSON(geometry.toJSON())
      for (let i = 0; i < _geometry.coordinates.length; i++) {
        for (let j = 0; j < _geometry.coordinates[i].length; j++) {
          for (let k = 0; k < _geometry.coordinates[i][j].length; k++) {
            _geometry.coordinates[i][j][k] = GeometryEngine._coordinateToWGS84(
              _geometry.coordinates[i][j][k]
            )
          }
        }
      }
      break
    case GeometryType.lineString:
      _geometry = LineString.fromJSON(geometry.toJSON())
      for (let i = 0; i < _geometry.coordinates.length; i++) {
        _geometry.coordinates[i] = GeometryEngine._coordinateToWGS84(
          _geometry.coordinates[i]
        )
      }
      break
    default:
      break
  }
  return _geometry
}

/**
 * 将几何对象的坐标转到原始的坐标中
 * @private
 * @param {Geometry} geometry 几何对象
 * @return {Geometry} 转化后的几何对象
 * */
GeometryEngine._geometryToOrigin = function (geometry) {
  let _geometry = undefined
  switch (geometry.type) {
    case GeometryType.polygon:
      _geometry = Polygon.fromJSON(geometry.toJSON())
      for (let i = 0; i < _geometry.coordinates.length; i++) {
        for (let j = 0; j < _geometry.coordinates[i].length; j++) {
          _geometry.coordinates[i][j] = GeometryEngine._coordinateToOrigin(
            _geometry.coordinates[i][j]
          )
        }
      }
      break
    case GeometryType.lineString:
      _geometry = LineString.fromJSON(geometry.toJSON())
      for (let i = 0; i < _geometry.coordinates.length; i++) {
        _geometry.coordinates[i] = GeometryEngine._coordinateToOrigin(
          _geometry.coordinates[i]
        )
      }
      break
    default:
      break
  }
  return _geometry
}

/**
 * <a id='isSimple'></a>
 * 对几何对象进行拓扑检查;<br/>
 * 支持的几何类型为线、多线、区和多区,支持任意坐标系几何,忽略三维高度;<br/>
 * 线类型的标准为:1、自身的非相邻线段不会自相交(两个线段仅有一个端点重叠不算相交);2、线自身的线段不会重叠;<br/>
 * 多线类型的标准为:1、自身的子线段必须是拓扑正确的;2、子线段不会相交;<br/>
 * 区类型的标准为:1、外圈和内圈满足线类型的拓扑检查标准;2、外圈和内圈、内圈和内圈不会相交;3、内圈不会超过外圈;<br/>
 * 多区类型的标准为:1、自身的子区必须是拓扑正确的;2、子区不会相交;<br/>
 * 示例如下:<br/>
 * <a href='#isSimple1'>[1、对线几何进行拓扑检查]</a><br/>
 * <a href='#isSimple2'>[2、对多线几何进行拓扑检查]</a><br/>
 * <a href='#isSimple3'>[3、对区几何进行拓扑检查]</a><br/>
 * <a href='#isSimple4'>[4、对多区几何进行拓扑检查]</a><br/>
 * @param { LineString | MultiLineString | Polygon | MultiPolygon } geometry 几何对象
 * @return {Boolean} 拓扑检查是否通过
 *
 * @example <caption><h5 id='isSimple1'>对线几何进行拓扑检查</h5></caption>
 * // ES5引入方式
 * const { LineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造线几何对象
 * const line = new LineString({
 *   coordinates: [
 *     [108.36341, 29.032578],
 *     [116.13094, 29.032578],
 *     [112.13094, 33.273224],
 *     [112.13094, 28.273224]
 *   ]
 * })
 * // 对线几何进行拓扑检查
 * const isSimple = GeometryEngine.isSimple(line)
 * console.log("是否通过检查:", isSimple)
 *
 * @example <caption><h5 id='isSimple2'>对多线几何进行拓扑检查</h5></caption>
 * // ES5引入方式
 * const { MultiLineString, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { MultiLineString, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造多线几何对象
 * const line = new MultiLineString({
 *   coordinates: [
 *     [
 *       [108.36341, 29.032578],
 *       [116.13094, 29.032578]
 *     ],
 *     [
 *       [112.13094, 33.273224],
 *       [112.13094, 28.273224]
 *     ]
 *   ]
 * })
 * // 对多线几何进行拓扑检查
 * const isSimple = GeometryEngine.isSimple(line)
 * console.log("是否通过检查:", isSimple)
 *
 * @example <caption><h5 id='isSimple3'>对区几何进行拓扑检查</h5></caption>
 * // ES5引入方式
 * const { Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造区几何对象
 * const polygon = new Polygon({
 *   coordinates: [
 *     [
 *       [108.36341, 29.032578],
 *       [116.13094, 29.032578],
 *       [112.13094, 33.273224],
 *       [112.13094, 28.273224],
 *       [108.36341, 29.032578]
 *     ]
 *   ]
 * })
 * // 对区几何进行拓扑检查
 * const isSimple = GeometryEngine.isSimple(polygon)
 * console.log("是否通过检查:", isSimple)
 *
 * @example <caption><h5 id='isSimple4'>对多区几何进行拓扑检查</h5></caption>
 * // ES5引入方式
 * const { Polygon, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { Polygon, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造多区几何对象
 * const multiPolygon = new MultiPolygon({
 *   coordinates: [
 *     [
 *       [
 *         [108.36341, 29.032578],
 *         [112.13094, 29.032578],
 *         [112.13094, 33.273224],
 *         [108.36341, 33.273224],
 *         [108.36341, 29.032578]
 *       ]
 *     ],
 *     [
 *       [
 *         [112.13094, 29.032578],
 *         [114.13094, 29.032578],
 *         [114.13094, 33.273224],
 *         [112.13094, 33.273224],
 *         [112.13094, 29.032578]
 *       ]
 *     ]
 *   ]
 * })
 * // 对区几何进行拓扑检查
 * const isSimple = GeometryEngine.isSimple(multiPolygon)
 * console.log("是否通过检查:", isSimple)
 * */
GeometryEngine.isSimple = function (geometry) {
  // 判断geometry是否为空
  if (isNull(geometry)) {
    Log.error('geometry不能为空!')
  }
  // 判断geometry类型
  if (
    !(
      geometry instanceof LineString ||
      geometry instanceof MultiLineString ||
      geometry instanceof Polygon ||
      geometry instanceof MultiPolygon
    )
  ) {
    Log.error('geometry为不支持的类型!')
  }

  // 根据几何类型进行判断
  let _isSimple = true
  switch (geometry.type) {
    case GeometryType.lineString:
      _isSimple = GeometryEngine._lineIsSimple(geometry)
      break
    case GeometryType.multiLineString:
      _isSimple = GeometryEngine._multiLineIsSimple(geometry)
      break
    case GeometryType.polygon:
      _isSimple = GeometryEngine._polygonIsSimple(geometry)
      break
    case GeometryType.multiPolygon:
      _isSimple = GeometryEngine._multiPolygonIsSimple(geometry)
      break
    default:
      break
  }

  return _isSimple
}

/**
 * 线几何拓扑检查
 * @private
 * @param {LineString} geometry 线几何对象
 * @return {Boolean} 是否是拓扑正确的几何
 * */
GeometryEngine._lineIsSimple = function (geometry) {
  let _isSimple = true
  // 点的数量大于2
  if (geometry.coordinates.length > 2) {
    // 将线全部拆分为线段数组
    const _lines = []
    for (let i = 0; i < geometry.coordinates.length - 1; i++) {
      _lines.push(
        new LineString({
          coordinates: [geometry.coordinates[i], geometry.coordinates[i + 1]]
        })
      )
    }
    // 判断线段是否重叠
    for (let i = 0; i < _lines.length - 1; i++) {
      for (let j = i + 1; j < _lines.length; j++) {
        // 是重叠,未通过拓扑检查
        if (
          GeometryEngine.contains(_lines[i], _lines[j]) ||
          GeometryEngine.contains(_lines[j], _lines[i])
        ) {
          _isSimple = false
          break
        }
      }
    }
    // 判断线段是否相交,不判断相邻线段
    for (let i = 0; i < _lines.length - 2; i++) {
      for (let j = i + 2; j < _lines.length; j++) {
        // 是相交,未通过拓扑检查
        if (GeometryEngine.intersects(_lines[i], _lines[j])) {
          // 判断线段是否仅有一段相连
          let _points = T.multiPoint([
            _lines[i].coordinates[0],
            _lines[i].coordinates[1],
            _lines[j].coordinates[0],
            _lines[j].coordinates[1]
          ])
          // 去除重复点
          _points = T.cleanCoords(_points)
          // 只要剩下的点数量为3,则一定是相连线
          if (_points.geometry.coordinates.length === 3) {
            _isSimple = true
          } else {
            _isSimple = false
            break
          }
        }
      }
    }
    return _isSimple
  } else {
    return _isSimple
  }
}

/**
 * 从多边形获取线段
 * @private
 * @param {Polygon} polygon 多边形对象
 * @return {Array<LineString>} 线段数组
 * */
GeometryEngine._getLinesFromPolygon = function (polygon) {
  // 将线全部拆分为线段数组
  const _lines = []
  for (let i = 0; i < polygon.coordinates.length; i++) {
    for (let j = 0; j < polygon.coordinates[i].length - 1; j++) {
      _lines.push(
        new LineString({
          coordinates: [
            polygon.coordinates[i][j],
            polygon.coordinates[i][j + 1]
          ]
        })
      )
    }
  }
  return _lines
}

/**
 * 多线几何的拓扑检查
 * @private
 * @param {MultiLineString} geometry 多线几何对象
 * @return {Boolean} 是否是拓扑正确的几何
 * */
GeometryEngine._multiLineIsSimple = function (geometry) {
  let _isSimple = true

  if (geometry.coordinates.length === 1) {
    const _line = new LineString({
      coordinates: geometry.coordinates[0]
    })
    _isSimple = GeometryEngine._lineIsSimple(_line)
  } else {
    // 检查所有子线是否是拓扑正确的
    for (let i = 0; i < geometry.coordinates.length; i++) {
      const _line = new LineString({
        coordinates: geometry.coordinates[i]
      })
      if (GeometryEngine._lineIsSimple(_line)) {
        _isSimple = false
        break
      }
    }
    // 检查子线是否相交
    if (_isSimple) {
      for (let i = 0; i < geometry.coordinates.length - 1; i++) {
        const _line1 = new LineString({
          coordinates: geometry.coordinates[i]
        })
        const _line2 = new LineString({
          coordinates: geometry.coordinates[i + 1]
        })
        if (GeometryEngine.intersects(_line1, _line2)) {
          _isSimple = false
          break
        }
      }
    }
  }

  return _isSimple
}

/**
 * 区几何的拓扑检查
 * @private
 * @param {Polygon} geometry 区几何对象
 * @return {Boolean} 是否是拓扑正确的几何
 * */
GeometryEngine._polygonIsSimple = function (geometry) {
  const _line = new MultiLineString({
    coordinates: geometry.coordinates
  })
  return GeometryEngine._multiLineIsSimple(_line)
}

/**
 * 多区几何的拓扑检查
 * @private
 * @param {Polygon} geometry 多区几何对象
 * @return {Boolean} 是否是拓扑正确的几何
 * */
GeometryEngine._multiPolygonIsSimple = function (geometry) {
  const _lineCoords = []
  // 收集所有线段坐标
  for (let i = 0; i < geometry.coordinates.length; i++) {
    for (let j = 0; j < geometry.coordinates[i].length; j++) {
      _lineCoords.push(geometry.coordinates[i][j])
    }
  }
  // 构造多线对象
  const _line = new MultiLineString({
    coordinates: _lineCoords
  })
  return GeometryEngine._multiLineIsSimple(_line)
}

/**
 * <a id='getPositionsFromArc'></a>
 * 通过三点弧段构造一组离散的点坐标,此组离散点是线几何的点坐标
 * @param {Object} options 构造参数
 * @param {Point|Number[]} [options.point1] 弧段上的第一个点,弧段的起点
 * @param {Point|Number[]} [options.point2] 弧段上的第二个点,弧段穿过的点
 * @param {Point|Number[]} [options.point3] 弧段上的第三个点,弧段的终点
 * @param {Number} [options.numberOfPoints = 360] 弧段中点的数量,注意这个是圆弧的数量,意思是将圆分成360份,如果这个弧段的度数为120度,则有
 * 360 * (120/360) = 120段
 * @param {SpatialReference} [options.spatialReference = new Zondy.SpatialReference('EPSG:4326')] 几何点的空间参考系,默认4326,当不是4326时请指定坐标系,方便进行投影转换,参考示例:<a href='#SpatialReference'>[指定坐标系]</a>
 * @return {Array} 一组离散的点坐标
 *
 * @example <caption><h5>通过三点弧段构造一组离散的点坐标</h5></caption>
 * // ES5引入方式
 * const { SpatialReference, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { SpatialReference, GeometryEngine } from "@mapgis/webclient-common"
 * // 将弧段打散为离散点
 * const arc3Path = GeometryEngine.getPositionsFromArc({
 *   // 弧段上的第一个点,弧段的起点
 *   point1: [1, 2],
 *   // 弧段上的第二个点,弧段穿过的点
 *   point2: [2, 1],
 *   // 弧段上的第二个点,弧段的终点
 *   point3: [1, 0],
 *   // 弧段中点的数量
 *   numberOfPoints: 360,
 *   // 弧段的参考系,默认是4326,非4326坐标系请指定参考系
 *   spatialReference: new SpatialReference('EPSG:4326')
 * })
 * */
GeometryEngine.getPositionsFromArc = function (options) {
  options = defaultValue(options, {})

  // 构造弧段上的三个点
  let _coordinates = [options.point1, options.point2, options.point3]

  // 根据圆上三个点求圆心
  const _center = GeometryEngine.getCenter(
    _coordinates[0],
    _coordinates[1],
    _coordinates[2]
  )

  // 将弧段离散
  _coordinates = GeometryEngine._tranArcToPath({
    center: _center,
    pointBegan: _coordinates[0],
    pointEnd: _coordinates[2],
    spatialReference: options.spatialReference,
    numberOfPoints: options.numberOfPoints
  })

  return _coordinates
}

/**
 * 转换三点弧为区,注意点的顺序为逆时针
 * @private
 * @param {Object} options 参数
 * @param {Array<Number>} [options.center] 圆心坐标
 * @param {Array<Number>} [options.pointBegan] 起始点坐标
 * @param {Array<Number>} [options.pointEnd] 结束点坐标
 * @param {Number} [options.numberOfPoints = 360] 分割点数量
 * @param {SpatialReference} [options.spatialReference = 4326] 点坐标的参考系
 * @returns {Array} 返回转换后的坐标数组
 */
GeometryEngine._tranArcToPath = function (options) {
  // 获取必要参数
  let _center = defaultValue(options.center, undefined)
  _center = JSON.parse(JSON.stringify(_center))
  let _pointBegan = defaultValue(options.pointBegan, undefined)
  _pointBegan = JSON.parse(JSON.stringify(_pointBegan))
  let _pointEnd = defaultValue(options.pointEnd, undefined)
  _pointEnd = JSON.parse(JSON.stringify(_pointEnd))
  let _numberOfPoints = defaultValue(options.numberOfPoints, 360)
  let _spatialReference = defaultValue(
    options.spatialReference,
    new SpatialReference({ wkid: 4326 })
  )
  _spatialReference = SpatialReference.fromJSON(_spatialReference)

  // 如果不是经纬度坐标系,则投影到经纬度坐标系,方便求角度
  if (!_spatialReference.isGeographic) {
    const _sr = new SpatialReference({
      wkid: 4326
    })
    _center = Projection.project(
      new Point({
        coordinates: _center,
        spatialReference: _spatialReference
      }),
      _sr
    ).coordinates
    _pointBegan = Projection.project(
      new Point({
        coordinates: _pointBegan,
        spatialReference: _spatialReference
      }),
      _sr
    ).coordinates
    _pointEnd = Projection.project(
      new Point({
        coordinates: _pointEnd,
        spatialReference: _spatialReference
      }),
      _sr
    ).coordinates
  }

  // 计算圆心半径
  const line = new LineString({
    coordinates: [_center, _pointBegan]
  })
  const _radius = GeometryEngine.planarLength(line)

  // 设置起始角度
  let _beganAngle = GeometryEngine.getAngleFromPoints(_pointBegan, _center, [
    _center[0] + 10,
    _center[1]
  ])

  // 设置结束角度
  const _endAngle = GeometryEngine.getAngleFromPoints(_pointEnd, _center, [
    _center[0] + 10,
    _center[1]
  ])

  // 色设置弧段旋转的角度
  const _arcAngle = _endAngle - _beganAngle

  // 计算弧段中点的数量
  _numberOfPoints *= _arcAngle / 360
  _numberOfPoints = Math.abs(_numberOfPoints)

  // 计算步长
  const _step = (Math.PI * (_arcAngle / 180)) / _numberOfPoints

  // 将角度转为弧度
  _beganAngle = Math.PI * (_beganAngle / 180)

  // 开始计算弧段
  const _ringPoints = [_pointBegan]
  for (let i = 0; i < _numberOfPoints - 1; i++) {
    _beganAngle += _step
    const point = [
      _center[0] + _radius * Math.cos(_beganAngle),
      _center[1] + _radius * Math.sin(_beganAngle)
    ]
    _ringPoints.push(point)
  }
  _ringPoints.push(_pointEnd)

  // 如果有高度,则添加高度
  if (this.hasZ) {
    _ringPoints.map((coordinate) => {
      return [coordinate[0], coordinate[1], _center[2]]
    })
  }

  return _ringPoints
}

/**
 * <a id='getCenter'></a>
 * 通过三个点求圆心,数学公式,不用进行投影
 * @param {Array<Number>} point1 第一个点坐标
 * @param {Array<Number>} point2 第二个点坐标
 * @param {Array<Number>} point3 第三个点坐标
 * @param {Boolean} hasZ 是否含有z值
 * @return {Array<Number>} 圆心坐标
 *
 * @example <caption><h5>通过三个点求圆心</h5></caption>
 * // ES5引入方式
 * const { GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine } from "@mapgis/webclient-common"
 * // 计算圆心
 * const center = GeometryEngine.getCenter([1, 2], [2, 1], [1, 0])
 * */
GeometryEngine.getCenter = function (point1, point2, point3, hasZ) {
  const x1 = point1[0]
  const x2 = point2[0]
  const x3 = point3[0]
  const y1 = point1[1]
  const y2 = point2[1]
  const y3 = point3[1]
  const z1 = 0
  const z2 = 0
  const z3 = 0
  const a1 = y1 * z2 - y2 * z1 - y1 * z3 + y3 * z1 + y2 * z3 - y3 * z2
  const b1 = -(x1 * z2 - x2 * z1 - x1 * z3 + x3 * z1 + x2 * z3 - x3 * z2)
  const c1 = x1 * y2 - x2 * y1 - x1 * y3 + x3 * y1 + x2 * y3 - x3 * y2
  const d1 = -(
    x1 * y2 * z3 -
    x1 * y3 * z2 -
    x2 * y1 * z3 +
    x2 * y3 * z1 +
    x3 * y1 * z2 -
    x3 * y2 * z1
  )
  const a2 = 2 * (x2 - x1)
  const b2 = 2 * (y2 - y1)
  const c2 = 2 * (z2 - z1)
  const d2 = x1 * x1 + y1 * y1 + z1 * z1 - x2 * x2 - y2 * y2 - z2 * z2

  const a3 = 2 * (x3 - x1)
  const b3 = 2 * (y3 - y1)
  const c3 = 2 * (z3 - z1)
  const d3 = x1 * x1 + y1 * y1 + z1 * z1 - x3 * x3 - y3 * y3 - z3 * z3

  const cx =
    -(
      b1 * c2 * d3 -
      b1 * c3 * d2 -
      b2 * c1 * d3 +
      b2 * c3 * d1 +
      b3 * c1 * d2 -
      b3 * c2 * d1
    ) /
    (a1 * b2 * c3 -
      a1 * b3 * c2 -
      a2 * b1 * c3 +
      a2 * b3 * c1 +
      a3 * b1 * c2 -
      a3 * b2 * c1)
  const cy =
    (a1 * c2 * d3 -
      a1 * c3 * d2 -
      a2 * c1 * d3 +
      a2 * c3 * d1 +
      a3 * c1 * d2 -
      a3 * c2 * d1) /
    (a1 * b2 * c3 -
      a1 * b3 * c2 -
      a2 * b1 * c3 +
      a2 * b3 * c1 +
      a3 * b1 * c2 -
      a3 * b2 * c1)
  const cz =
    -(
      a1 * b2 * d3 -
      a1 * b3 * d2 -
      a2 * b1 * d3 +
      a2 * b3 * d1 +
      a3 * b1 * d2 -
      a3 * b2 * d1
    ) /
    (a1 * b2 * c3 -
      a1 * b3 * c2 -
      a2 * b1 * c3 +
      a2 * b3 * c1 +
      a3 * b1 * c2 -
      a3 * b2 * c1)

  if (hasZ) {
    return [cx, cy, point1[2]]
  } else {
    return [cx, cy]
  }
}

/**
 * 将一个含有三点弧段的path离散化
 * @private
 * @param {Object} options 构造参数
 * @param {Array} [options.coordinates = []] 线段点坐标数组
 * @param {Array<Object>} [options.arcInfo = []] 弧段信息
 * @param {SpatialReference} [options.spatialReference = 4326] 参考系对象
 * @param {Number} [options.numberOfPoints = 360] 圆弧中的分段点数量
 * */
GeometryEngine._getArcFromPath = function (options) {
  // 获取参数
  options = defaultValue(options, {})
  const _coordinates = defaultValue(options.coordinates, [])
  const _arcInfo = defaultValue(options.arcInfo, [])
  const _spatialReference = defaultValue(
    options.spatialReference,
    new SpatialReference({ wkid: 4326 })
  )
  const _numberOfPoints = defaultValue(options.numberOfPoints, 360)

  // 离散后的所有点坐标
  let _allPoints = []

  // 第一段弧段前面的点坐标
  const _beganPoints = _coordinates.slice(0, _arcInfo[0].centerPointIndex - 1)
  _allPoints = _allPoints.concat(_beganPoints)
  const _centerPointIndex = Number(_arcInfo[0].centerPointIndex)
  const _arcPoints = [
    _coordinates[_centerPointIndex - 1],
    _coordinates[_centerPointIndex],
    _coordinates[_centerPointIndex + 1]
  ]

  // 获取第一段弧段离散后的点坐标
  const _arcCoordinates = GeometryEngine.getPositionsFromArc({
    point1: _arcPoints[0],
    point2: _arcPoints[1],
    point3: _arcPoints[2],
    spatialReference: _spatialReference,
    numberOfPoints: _numberOfPoints
  })
  _allPoints = _allPoints.concat(_arcCoordinates)

  // 获取第二个弧段及后面弧段的点坐标
  for (let i = 1; i < _arcInfo.length; i++) {
    if (_arcInfo[i].hasOwnProperty('centerPointIndex')) {
      // 获取前一个弧段和当前弧段中间的点坐标
      const _coords = _coordinates.slice(
        _arcInfo[i - 1].centerPointIndex + 2,
        _arcInfo[i].centerPointIndex + 1
      )
      _allPoints = _allPoints.concat(_coords)

      // 获取当前弧段的三个点
      const _centerPointIndex = Number(_arcInfo[i].centerPointIndex)
      const _arcPoints = [
        _coordinates[_centerPointIndex - 1],
        _coordinates[_centerPointIndex],
        _coordinates[_centerPointIndex + 1]
      ]

      //  将三点弧段离散
      const _arcCoordinates = GeometryEngine.getPositionsFromArc({
        point1: _arcPoints[0],
        point2: _arcPoints[1],
        point3: _arcPoints[2],
        spatialReference: _spatialReference,
        numberOfPoints: _numberOfPoints
      })
      _allPoints = _allPoints.concat(_arcCoordinates)
    }
  }

  // 获取最后一段非弧段坐标点
  const _endPoints = _coordinates.slice(
    _arcInfo[_arcInfo.length - 1].centerPointIndex + 2,
    _coordinates.length
  )
  _allPoints = _allPoints.concat(_endPoints)

  // 返回一个path
  return _allPoints
}

/**
 * 通过点坐标和弧段信息对弧段信息进行分类
 * @private
 * @param {Array} arcInfo 弧段信息
 * @param {Array} coordinates 点坐标
 * @param {GeometryType} type 几何类型
 * @return {Object} 分类好的弧段信息
 * */
GeometryEngine._getArcInfos = function (arcInfo, coordinates, type) {
  const _arcInfos = {}
  switch (type) {
    case GeometryType.multiLineString:
    case GeometryType.polygon:
      for (let i = 0; i < coordinates.length; i++) {
        _arcInfos[i] = []
        for (let j = 0; j < arcInfo.length; j++) {
          const _index =
            type === GeometryType.multiLineString
              ? arcInfo[j].lineIndex
              : arcInfo[j].ringIndex
          if (_index === i) {
            _arcInfos[i].push(arcInfo[j])
          }
        }
        _arcInfos[i].sort(function (a, b) {
          return Number(a.centerPointIndex) - Number(b.centerPointIndex)
        })
      }
      return _arcInfos
    case GeometryType.multiPolygon:
      for (let i = 0; i < coordinates.length; i++) {
        _arcInfos[i] = []
        for (let j = 0; j < arcInfo.length; j++) {
          if (arcInfo[j].polygonIndex === i) {
            _arcInfos[i].push(arcInfo[j])
          }
        }
        _arcInfos[i] = GeometryEngine._getArcInfos(
          _arcInfos[i],
          coordinates[i],
          GeometryType.polygon
        )
      }
      return _arcInfos
    default:
      return _arcInfos
  }
}

/**
 * <a id='getAngleFromPoints'></a>
 * 计算三点之间的夹角
 * @param {Array<Number>} pointA 第一个点
 * @param {Array<Number>} pointB 第二个点
 * @param {Array<Number>} pointC 第三个点
 * @return {Number} 角度
 *
 * @example <caption><h5>计算三点之间的夹角</h5></caption>
 * // ES5引入方式
 * const { GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { GeometryEngine } from "@mapgis/webclient-common"
 * // 计算三点之间的夹角
 * const angle = GeometryEngine.getAngleFromPoints([1, 2], [1, 1], [2, 1])
 * */
GeometryEngine.getAngleFromPoints = function (pointA, pointB, pointC) {
  const ABX = pointA[0] - pointB[0]
  const ABY = pointA[1] - pointB[1]
  const CBX = pointC[0] - pointB[0]
  const CBY = pointC[1] - pointB[1]
  const AB_MUL_CB = ABX * CBX + ABY * CBY
  const DIST_AB = Math.sqrt(ABX * ABX + ABY * ABY)
  const DIST_CB = Math.sqrt(CBX * CBX + CBY * CBY)
  const cosValue = AB_MUL_CB / (DIST_AB * DIST_CB)
  const _angle = (Math.acos(cosValue) * 180) / Math.PI

  if (Math.abs(_angle) === 0 || Math.abs(_angle) === 180) {
    return Math.abs(_angle)
  }

  if (pointA[1] < pointC[1]) {
    return -1 * _angle
  }

  return _angle
}

/**
 * <a id='getArcFromGeometry'></a>
 * 通过几何中的三点弧段数据,构造离散后的几何对象,类型与原始对象一致
 * @param {Object} options 构造参数
 * @param {LineString | MultiLineString | Polygon | MultiPolygon} [options.geometry = null] 带三点弧段数据的几何对象
 * @param {Array<Object>} [options.arcInfo = []] 弧段信息参数
 * @param {Number} [options.numberOfPoints = 360] 弧段中点的数量,注意这个是圆弧的数量,意思是将圆分成360份,如果这个弧段的度数为120度,则有
 * 360 * (120/360) = 120段
 * @return {LineString | MultiLineString | Polygon | MultiPolygon} 离散后的新几何对象
 *
 * @example <caption><h5>通过几何中的三点弧段数据,构造离散后的几何对象</h5></caption>
 * // ES5引入方式
 * const { LineString, SpatialReference, GeometryEngine } = Zondy.Geometry
 * // ES6引入方式
 * import { LineString, SpatialReference, GeometryEngine } from "@mapgis/webclient-common"
 * // 构造一个线几何,其他几何对象同线几何对象
 * const line = new LineString({
 *   // 几何坐标点
 *   coordinates:  [
 *         [
 *           64.15012768690451,
 *           89.23584703643573
 *         ],
 *         [
 *           85.69715379735212,
 *           62.63186581843408
 *         ],
 *         [
 *           92.51304981188144,
 *           62.851733431806025
 *         ],
 *         [
 *           92.51304981188144,
 *           62.851733431806025
 *         ],
 *         [
 *           64.15012768690451,
 *           89.23584703643573
 *         ]
 *    ],
 *   // 几何的参考系,默认是4326,非4326坐标系请指定参考系
 *   spatialReference: new SpatialReference('EPSG:4326')
 * })
 * // 通过几何中的三点弧段数据,构造离散后的几何对象
 * const arcLine = GeometryEngine.getArcFromGeometry({
 *   // 几何对象
 *   geometry: line,
 *   // 弧段信息参数,必填项
 *   arcInfo: [
 *     {
 *       "centerPointIndex": 1,
 *       "ringIndex": 0
 *     }
 *   ],
 *   // 弧段中点的数量
 *   numberOfPoints: 360
 * })
 * */
GeometryEngine.getArcFromGeometry = function (options) {
  // 获取必要参数
  options = defaultValue(options, {})
  const _geometry = defaultValue(options.geometry, undefined)
  const _numberOfPoints = defaultValue(options.numberOfPoints, 360)
  const _arcInfo = defaultValue(options.arcInfo, [])

  // 非空和类型判断
  if (!_geometry) return _geometry
  if (!(_geometry instanceof Geometry)) {
    Log.error('geometry不是基础几何类型!')
    return _geometry
  }

  // 检测arcInfo参数
  if (!_arcInfo) return _geometry
  if (!(_arcInfo instanceof Array)) return _geometry
  if (_arcInfo.length < 1) return _geometry

  // 获取点坐标
  const _coordinates = _geometry.coordinates
  // 获取参考系信息
  const _spatialReference = _geometry.spatialReference

  // 根据几何类型进行处理
  let _allPoints
  let _arcInfos
  switch (_geometry.type) {
    case GeometryType.lineString:
      // 离散后的所有点坐标
      _allPoints = GeometryEngine._getArcFromPath({
        coordinates: _coordinates,
        arcInfo: _arcInfo,
        spatialReference: _spatialReference,
        numberOfPoints: _numberOfPoints
      })

      // 返回一个线几何
      return new LineString({
        coordinates: _allPoints,
        spatialReference: SpatialReference.fromJSON(_geometry.spatialReference)
      })
    case GeometryType.multiLineString:
      // 过滤出每一个线段的弧段信息
      _arcInfos = GeometryEngine._getArcInfos(
        _arcInfo,
        _coordinates,
        GeometryType.multiLineString
      )

      // 将每一段线中的弧段离散化
      _allPoints = []
      Object.keys(_arcInfos).forEach(function (key) {
        _allPoints.push(
          GeometryEngine._getArcFromPath({
            coordinates: _coordinates[key],
            arcInfo: _arcInfos[key],
            spatialReference: _spatialReference,
            numberOfPoints: _numberOfPoints
          })
        )
      })

      // 返回一个新的多线几何对象
      return new MultiLineString({
        coordinates: _allPoints,
        spatialReference: _spatialReference
      })
    case GeometryType.polygon:
      // 过滤出每一个线段的弧段信息
      _arcInfos = GeometryEngine._getArcInfos(
        _arcInfo,
        _coordinates,
        GeometryType.polygon
      )

      // 将每一段环中的弧段离散化
      _allPoints = []
      Object.keys(_arcInfos).forEach(function (key) {
        const _path = _coordinates[key].slice(0, _coordinates[key].length)
        const _rings = GeometryEngine._getArcFromPath({
          coordinates: _path,
          arcInfo: _arcInfos[key],
          spatialReference: _spatialReference,
          numberOfPoints: _numberOfPoints
        })
        _rings.push(_rings[0])
        _allPoints.push(_rings)
      })

      // 返回一个新的区几何对象
      return new MultiLineString({
        coordinates: _allPoints,
        spatialReference: _spatialReference
      })
    case GeometryType.multiPolygon:
      // 过滤出每一个线段的弧段信息
      _arcInfos = GeometryEngine._getArcInfos(
        _arcInfo,
        _coordinates,
        GeometryType.multiPolygon
      )

      // 将每一段环中的弧段离散化
      _allPoints = []
      for (let i = 0; i < _coordinates.length; i++) {
        const _polygonCoordinates = []
        Object.keys(_arcInfos[i]).forEach(function (key) {
          const _path = _coordinates[i][key].slice(
            0,
            _coordinates[i][key].length
          )
          const _rings = GeometryEngine._getArcFromPath({
            coordinates: _path,
            arcInfo: _arcInfos[i][key],
            spatialReference: _spatialReference,
            numberOfPoints: _numberOfPoints
          })
          _rings.push(_rings[0])
          _polygonCoordinates.push(_rings)
        })
        _allPoints.push(_polygonCoordinates)
      }

      // 返回一个新的区几何对象
      return new MultiPolygon({
        coordinates: _allPoints,
        spatialReference: _spatialReference
      })
    default:
      return _geometry
  }
}

/**
 * 通过线段几何数组,构造一个turf的多线几何对象
 * @private
 * @param {Array<LineString>} lines 多线几何对象
 * @return {Object} turf的多线对象
 * */
GeometryEngine._getTurfMultiLineStringFromLines = function (lines) {
  const _lines = []
  for (let i = 0; i < lines.length; i++) {
    _lines.push(lines[i].coordinates)
  }
  return T.multiLineString(_lines)
}

/**
 * 初始化相交信息
 * @private
 * @return {Object} 相交的点和线
 * */
GeometryEngine._initIntersects = function (lines) {
  const _intersects = {}
  for (let i = 0; i < lines.length; i++) {
    lines[i].id = i
    _intersects[i] = {
      intersectPoints: [],
      intersectLines: []
    }
  }
  return _intersects
}

/**
 * 判断点在线的两端点上
 * @private
 * @param {Point} point 点几何
 * @param {LineString} line 线几何
 * @return {Boolean} 点在线的两端点上
 * */
GeometryEngine._pointOnLineEndPoint = function (point, line) {
  return (
    (point.coordinates[0] === line.coordinates[0][0] &&
      point.coordinates[1] === line.coordinates[0][1]) ||
    (point.coordinates[0] === line.coordinates[1][0] &&
      point.coordinates[1] === line.coordinates[1][1])
  )
}

/**
 * 判断线和线相等
 * @private
 * @param {LineString} line1 线几何1
 * @param {LineString} line2 线几何2
 * @return {Boolean} 线和线相等
 * */
GeometryEngine._lineEqualLine = function (line1, line2) {
  return (
    (line1.coordinates[0][0] === line2.coordinates[0][0] &&
      line1.coordinates[0][1] === line2.coordinates[0][1] &&
      line1.coordinates[1][0] === line2.coordinates[1][0] &&
      line1.coordinates[1][1] === line2.coordinates[1][1]) ||
    (line1.coordinates[0][0] === line2.coordinates[1][0] &&
      line1.coordinates[0][1] === line2.coordinates[1][1] &&
      line1.coordinates[1][0] === line2.coordinates[0][0] &&
      line1.coordinates[1][1] === line2.coordinates[0][1])
  )
}

/**
 * 判断线和线相等,turf版本
 * @private
 * @param {LineString} line1 线几何1
 * @param {LineString} line2 线几何2
 * @return {Boolean} 线和线相等
 * */
GeometryEngine._lineEqualLineTurf = function (line1, line2) {
  return (
    line1.geometry.coordinates[0][0] === line2.geometry.coordinates[0][0] &&
    line1.geometry.coordinates[0][1] === line2.geometry.coordinates[0][1] &&
    line1.geometry.coordinates[1][0] === line2.geometry.coordinates[1][0] &&
    line1.geometry.coordinates[1][1] === line2.geometry.coordinates[1][1]
  )
}

/**
 * 仅对只有外圈的几何做拓扑矫正
 * @private
 * @param {Polygon} polygon 要进行矫正的几何对象
 * @return {Object} turf的要素集合
 * */
GeometryEngine._singRingSimplify = function (polygon) {
  // 将区、多区的外圈,内圈或者线、多线转为仅包含两个点的线段数组
  const _lines = GeometryEngine._getLinesFromPolygon(polygon)
  // 判断是否有线段相交
  // _lines上每个线段的交点和重合线段
  const _intersects = GeometryEngine._initIntersects(_lines)
  // 判断每一个线段和其他线段的交点,线段不和临近线段计算相交
  for (let i = 0; i < _lines.length - 1; i++) {
    for (let j = i + 1; j < _lines.length; j++) {
      // 计算线段和线段的交点
      const _intersect = GeometryEngine.intersect(_lines[i], _lines[j])
      // 存在交点,且交点不在线段的端点上
      if (_intersect.point) {
        if (
          !GeometryEngine._pointOnLineEndPoint(_intersect.point, _lines[i]) &&
          !GeometryEngine._pointOnLineEndPoint(_intersect.point, _lines[j])
        ) {
          // 保存交点
          _intersects[i].intersectPoints.push(_intersect.point)
          _intersects[j].intersectPoints.push(_intersect.point)
        }
      }

      // 保存重叠线
      if (_intersect.lineString) {
        _intersects[i].intersectLines.push(_intersect.lineString)
        _intersects[j].intersectLines.push(_intersect.lineString)
      }
    }
  }

  // turf的线几何数组
  const _turfLines = []
  // 如果线段上有交点,则拆分线段
  for (let i = 0; i < _lines.length; i++) {
    // 是否需要删除这一段线,判断依据为两个线段是否完全重合
    let _deleteLine = false
    for (let j = 0; j < _intersects[i].intersectLines.length; j++) {
      // 线段几何相等,则必然重叠,去掉此段线
      if (
        GeometryEngine._lineEqualLine(
          _lines[i],
          _intersects[i].intersectLines[j]
        )
      ) {
        _deleteLine = true
        break
      } else {
        // 线的一端不相等,将相交线段的端点存起来,给下一步分割线断使用
        if (
          _lines[i].coordinates[0][0] !==
            _intersects[i].intersectLines[j].coordinates[0][0] ||
          _lines[i].coordinates[0][1] !==
            _intersects[i].intersectLines[j].coordinates[0][1]
        ) {
          _intersects[i].intersectPoints.push(
            new Point({
              coordinates: _intersects[i].intersectLines[j].coordinates[0]
            }),
            new Point({
              coordinates: _intersects[i].intersectLines[j].coordinates[0]
            })
          )
        }
        // 线的一端不相等,将相交线段的端点存起来,给下一步分割线断使用
        if (
          _lines[i].coordinates[1][0] !==
            _intersects[i].intersectLines[j].coordinates[1][0] ||
          _lines[i].coordinates[1][1] !==
            _intersects[i].intersectLines[j].coordinates[1][1]
        ) {
          _intersects[i].intersectPoints.push(
            new Point({
              coordinates: _intersects[i].intersectLines[j].coordinates[1]
            }),
            new Point({
              coordinates: _intersects[i].intersectLines[j].coordinates[1]
            })
          )
        }
      }
    }

    // 确定不删除线
    if (!_deleteLine) {
      // 先对交点进行排序
      _intersects[i].intersectPoints.sort(function (a, b) {
        return a.coordinates[0] - b.coordinates[0]
      })
      _intersects[i].intersectPoints.sort(function (a, b) {
        return b.coordinates[1] - a.coordinates[1]
      })

      // 将线段的起点加入
      _intersects[i].intersectPoints.splice(
        0,
        0,
        new Point({
          coordinates: _lines[i].coordinates[0]
        })
      )

      // 将线段的末尾加入
      _intersects[i].intersectPoints.push(
        new Point({
          coordinates: _lines[i].coordinates[1]
        })
      )

      // 点坐标去重
      const _unionPoints = GeometryEngine.union(_intersects[i].intersectPoints)

      // 构造turf的线几何数组
      for (let j = 0; j < _unionPoints.coordinates.length - 1; j++) {
        _turfLines.push(
          T.lineString([
            _unionPoints.coordinates[j],
            _unionPoints.coordinates[j + 1]
          ])
        )
      }
    }
  }

  // 线坐标去重,后期换成union方法
  for (let i = 0; i < _turfLines.length; i++) {
    for (let j = i + 1; j < _turfLines.length; j++) {
      if (GeometryEngine._lineEqualLineTurf(_turfLines[i], _turfLines[j])) {
        _turfLines.splice(j, 1)
        j--
      }
    }
  }

  // 构造turf的线要素集合
  const _lineCollection = T.featureCollection(_turfLines)

  // 通过线,构造多边形
  return T.polygonize(_lineCollection)
}

GeometryEngine._getPolygonsFromRings = function (rings, polygons) {
  // 都给一个是外圈的标签
  for (let i = 0; i < rings.length; i++) {
    rings[i].isOuter = true
    rings[i].innerPolygons = []
  }

  if (rings.length === 1) {
    polygons.push(rings[0])
  }

  // 开始给外圈挑选内圈
  for (let i = 0; i < rings.length - 1; i++) {
    // 是外圈
    if (rings[i].isOuter) {
      // 添加到_polygons数组中
      polygons.push(rings[i])

      // 开始挑选内圈
      for (let j = i + 1; j < rings.length; j++) {
        // 第i个圈包含第j个圈
        if (GeometryEngine.contains(rings[i], rings[j])) {
          // 第j个圈的isOuter变为false
          rings[j].isOuter = false
          // 将第j个圈存入第i个几何的内圈数组中
          rings[i].innerPolygons.push(rings[j])
        }
      }
    }
  }

  if (rings.length > 1 && rings[rings.length - 1].isOuter) {
    polygons.push(rings[rings.length - 1])
  }

  // 额外要增加的多边形数组
  let _extPolygons = []

  // 判断polygons中的内圈是有包含关系
  for (let i = 0; i < polygons.length; i++) {
    const _rings = []
    for (let j = 0; j < polygons[i].innerPolygons.length - 1; j++) {
      for (let k = j + 1; k < polygons[i].innerPolygons.length; k++) {
        if (
          GeometryEngine.contains(
            polygons[i].innerPolygons[j],
            polygons[i].innerPolygons[k]
          ) &&
          !polygons[i].innerPolygons[k].isOuter
        ) {
          polygons[i].innerPolygons[k].isOuter = true
          polygons[i].innerPolygons[k].innerPolygons = []
          _rings.push(polygons[i].innerPolygons[k])
        }
      }
    }

    for (let j = 0; j < polygons[i].innerPolygons.length; j++) {
      if (polygons[i].innerPolygons[j].isOuter) {
        polygons[i].innerPolygons.splice(j, 1)
        j--
      }
    }

    if (_rings.length > 0) {
      const _polygons = GeometryEngine._getPolygonsFromRings(_rings, [])
      _extPolygons = _extPolygons.concat(_polygons)
    }
  }

  polygons = polygons.concat(_extPolygons)

  return polygons
}

GeometryEngine._singlePolygonSimplify = function (geometry) {
  // 内圈集合对象
  const _inners = {
    // 内圈比外圈的的几何数组
    largerInnerPolygon: [],
    // 内圈和外圈相交的几何数组
    intersectInnerPolygon: [],
    // 内圈在外圈中的几何数组
    smallInnerPolygon: []
  }

  // 构造外圈几何对象
  let _outerPolygon = new Polygon({
    coordinates: [geometry.coordinates[0]],
    spatialReference: SpatialReference.fromJSON(geometry.spatialReference)
  })

  // 将内圈坐标转为几何对象数组
  const _innerArr = []
  for (let i = 1; i < geometry.coordinates.length; i++) {
    const _innerPolygon = new Polygon({
      coordinates: [geometry.coordinates[i]],
      spatialReference: SpatialReference.fromJSON(geometry.spatialReference)
    })
    _innerArr.push(_innerPolygon)
  }

  // 内圈是否相交
  for (let i = 0; i < _innerArr.length - 1; i++) {
    for (let j = i + 1; j < _innerArr.length; j++) {
      const _innerIntersect = GeometryEngine.intersect(
        _innerArr[i],
        _innerArr[j]
      )
      if (
        _innerIntersect instanceof Polygon ||
        _innerIntersect instanceof MultiPolygon
      ) {
        _innerArr[i] = GeometryEngine.difference(_innerArr[i], _innerIntersect)
        _innerArr[j] = GeometryEngine.difference(_innerArr[j], _innerIntersect)
      }
    }
  }

  // 对内圈进行分类
  for (let i = 0; i < _innerArr.length; i++) {
    // 构造内圈多边形
    let _innerPolygon = _innerArr[i]

    // 判断外圈是否完全包含内圈
    if (GeometryEngine.contains(_outerPolygon, _innerPolygon)) {
      // 先求差
      const _differencePolygon = GeometryEngine.difference(
        _outerPolygon,
        _innerPolygon
      )

      // 判断求差后的几何是否和_outerPolygon在几何上相等,只有完全相等才是完全包含
      const _coords1 = JSON.parse(JSON.stringify(_outerPolygon.coordinates[0]))
      const _coords2 = JSON.parse(
        JSON.stringify(_differencePolygon.coordinates[0])
      )
      _coords1.sort(function (a, b) {
        return a[0] - b[0]
      })
      _coords1.sort(function (a, b) {
        return a[1] - b[1]
      })
      _coords2.sort(function (a, b) {
        return a[0] - b[0]
      })
      _coords2.sort(function (a, b) {
        return a[1] - b[1]
      })
      if (JSON.stringify(_coords1) === JSON.stringify(_coords2)) {
        _inners.smallInnerPolygon.push(_innerPolygon)
      }
      // 否则按相交处理
      else {
        _inners.intersectInnerPolygon.push(_innerPolygon)
      }
    }
    // 判断内圈包含外圈,这个还不完善
    else if (GeometryEngine.contains(_innerPolygon, _outerPolygon)) {
      // 替换一下内圈外圈
      const _outerPolygonJSON = _outerPolygon.toJSON()
      const _innerPolygonJSON = _innerPolygon.toJSON()
      _outerPolygon = Geometry.fromJSON(_innerPolygonJSON)
      _innerPolygon = Geometry.fromJSON(_outerPolygonJSON)
      _inners.smallInnerPolygon.push(_innerPolygon)
    }
    // 判断外圈和内圈相交
    else if (GeometryEngine.intersects(_outerPolygon, _innerPolygon)) {
      _inners.intersectInnerPolygon.push(_innerPolygon)
    }
    // 判断内圈和外圈相离
    else if (GeometryEngine.disjoint(_outerPolygon, _innerPolygon)) {
      _inners.largerInnerPolygon.push(_innerPolygon)
    }
  }

  // 外圈几何数组
  const _outerPolygons = []

  // 如果有内圈和外圈相交,则计算外圈和内圈的相交部分,外圈和内圈分别减去相交部分,之后内圈剩余的部分变成外圈
  for (let i = 0; i < _inners.intersectInnerPolygon.length; i++) {
    const _innerIntersect = GeometryEngine.intersect(
      _outerPolygon,
      _inners.intersectInnerPolygon[i]
    )
    if (
      _innerIntersect instanceof Polygon ||
      _innerIntersect instanceof MultiPolygon
    ) {
      _outerPolygon = GeometryEngine.difference(_outerPolygon, _innerIntersect)
      const _newOuterPolygon = GeometryEngine.difference(
        _inners.intersectInnerPolygon[i],
        _innerIntersect
      )
      _outerPolygons.push(_newOuterPolygon)
    }
  }

  // 对外圈几何进行拓扑矫正
  const _outerPolygonCollection =
    GeometryEngine._singRingSimplify(_outerPolygon)

  // 更新外圈几何数组
  for (let i = 0; i < _outerPolygonCollection.features.length; i++) {
    _outerPolygons.push(
      new Polygon({
        coordinates: _outerPolygonCollection.features[i].geometry.coordinates
      })
    )
  }

  // 存在内圈大于外圈的情况
  if (_inners.largerInnerPolygon.length > 0) {
    for (let i = 0; i < _inners.largerInnerPolygon.length; i++) {
      _outerPolygons.push(_inners.largerInnerPolygon[i])
    }
  }

  // 内圈几何数组
  let _innerPolygons = []

  // 对内圈几何进行拓扑矫正
  for (let i = 0; i < _inners.smallInnerPolygon.length; i++) {
    const _innerPolygonCollection = GeometryEngine._singRingSimplify(
      _inners.smallInnerPolygon[i]
    )
    for (let j = 0; j < _innerPolygonCollection.features.length; j++) {
      const _polygon = new Polygon({
        coordinates: _innerPolygonCollection.features[j].geometry.coordinates
      })
      // 判断是否被选取
      _polygon.isPicked = false
      _innerPolygons.push(_polygon)
    }
  }

  // 对内圈进行求并
  _innerPolygons = GeometryEngine.union(_innerPolygons)
  if (_innerPolygons && !(_innerPolygons instanceof Array)) {
    _innerPolygons = [_innerPolygons]
  }

  // 判断内圈在哪一个外圈中
  for (let i = 0; i < _outerPolygons.length; i++) {
    for (let j = 0; j < _innerPolygons.length; j++) {
      // 没有被归类,则进行包含判断
      if (
        !_innerPolygons[j].isPicked &&
        _outerPolygons[i] &&
        _innerPolygons[j] &&
        GeometryEngine.contains(_outerPolygons[i], _innerPolygons[j])
      ) {
        // 添加到该外圈中
        _outerPolygons[i].coordinates.push(_innerPolygons[j].coordinates[0])
        _innerPolygons[j].isPicked = true
      }
    }
  }

  return _outerPolygons
}

/**
 * 拓扑矫正
 * <a id='simplify'></a>
 * @param {Polygon | MultiPolygon | LineString | MultiLineString} geometry 要进行矫正的几何
 * @return {Geometry} 矫正后的几何对象
 * */
GeometryEngine.simplify = function (geometry) {
  // 进行非空判断
  if (isNull(geometry)) {
    Log.error('geometry不能为空!')
  }

  // 不是满足的类型,直接返回
  if (
    geometry.type === GeometryType.point ||
    geometry.type === GeometryType.multiPoint ||
    geometry.type === GeometryType.extent ||
    geometry.type === GeometryType.circle
  ) {
    return geometry
  }

  // 先进行拓扑正确性检查
  // if (GeometryEngine.isSimple(geometry)) {
  //   return geometry
  // }

  // 根据几何类型进行处理
  switch (geometry.type) {
    case GeometryType.polygon:
    case GeometryType.multiPolygon:
      // 获取所有的几何圈
      const _allRings = []
      let _coordinates
      if (geometry.type === GeometryType.polygon) {
        _coordinates = JSON.parse(JSON.stringify(geometry.coordinates))
        for (let i = 0; i < _coordinates.length; i++) {
          _allRings.push(
            new Polygon({
              coordinates: [_coordinates[i]],
              spatialReference: SpatialReference.fromJSON(
                geometry.spatialReference
              )
            })
          )
        }
      } else {
        const _coordinates = JSON.parse(JSON.stringify(geometry.coordinates))
        for (let i = 0; i < _coordinates.length; i++) {
          for (let j = 0; j < _coordinates[i].length; j++) {
            _allRings.push(
              new Polygon({
                coordinates: [_coordinates[i][j]],
                spatialReference: SpatialReference.fromJSON(
                  geometry.spatialReference
                )
              })
            )
          }
        }
      }

      // 计算每一个圈的面积
      for (let i = 0; i < _allRings.length; i++) {
        _allRings[i].area = GeometryEngine.planarArea(_allRings[i])
      }

      // 根据面积进行排序
      _allRings.sort(function (a, b) {
        return b.area - a.area
      })

      // 判断圈的内外圈关系
      const _polygons = GeometryEngine._getPolygonsFromRings(_allRings, [])

      // 对所有的几何外圈进行求差处理
      for (let i = 0; i < _polygons.length - 1; i++) {
        for (let j = i + 1; j < _polygons.length; j++) {
          if (!GeometryEngine.contains(_polygons[i], _polygons[j])) {
            const _intersect = GeometryEngine.intersect(
              _polygons[i],
              _polygons[j]
            )
            if (_intersect) {
              _polygons[i].coordinates = GeometryEngine.difference(
                _polygons[i],
                _intersect
              ).coordinates
              _polygons[j].coordinates = GeometryEngine.difference(
                _polygons[j],
                _intersect
              ).coordinates
            }
          }
        }
      }

      // 开始将内圈付给外圈多边形
      let _allSimpilfyPolygons = []
      for (let i = 0; i < _polygons.length; i++) {
        for (let j = 0; j < _polygons[i].innerPolygons.length; j++) {
          _polygons[i].coordinates.push(
            _polygons[i].innerPolygons[j].coordinates[0]
          )
        }
        const _simpilfyPolygons = GeometryEngine._singlePolygonSimplify(
          _polygons[i]
        )
        _allSimpilfyPolygons = _allSimpilfyPolygons.concat(_simpilfyPolygons)
      }

      // 判断返回区几何还是多区几何对象
      if (_allSimpilfyPolygons.length === 1) {
        return _allSimpilfyPolygons[0]
      } else if (_allSimpilfyPolygons.length > 1) {
        const _coordinates = []
        for (let i = 0; i < _allSimpilfyPolygons.length; i++) {
          if (_allSimpilfyPolygons[i]) {
            _coordinates.push(_allSimpilfyPolygons[i].coordinates)
          }
        }
        return new MultiPolygon({
          coordinates: _coordinates,
          spatialReference: SpatialReference.fromJSON(geometry.spatialReference)
        })
      }
      break
    case GeometryType.lineString:
      break
    default:
      break
  }
}

Zondy.Geometry.GeometryEngine = GeometryEngine
export default GeometryEngine
构造函数
成员变量
方法
事件