类名 common/sketchEditor/base/SketchPolylineToPolygonDrawTool.js
import { Feature, ViewEventType } from '../../base'
import { Polygon, LineString, Circle, Extent } from '../../base/geometry'
import { defaultValue } from '../../util'
import SketchBaseDrawTool from './SketchBaseDrawTool'
import Color from '../../base/Color'
import SketchPointDrawTool from './SketchPointDrawTool'
// import SketchSnappingTool from './SketchSnappingTool'
import { SimpleLineSymbol } from '../../base/symbol'
import GeometryEngine from '../../base/geometry/GeometryEngine'
/**
 * 面绘图工具类
 * @class SketchPolylineDrawTool
 * @moduleEX SketchEditorModule
 * @extends SketchBaseDrawTool
 * @param {Object} options 构造参数
 * @param {MapView|SceneView}  [options.view]  地图视图对象
 * @param {GraphicsLayer}  [options.layer]  草图图层管对象
 * @param {SketchStyle}  [options.sketchStyle]  草图符号
 */
class SketchPolylineDrawTool extends SketchBaseDrawTool {
  constructor(options) {
    super(options)
    this._drawTools = [this]
    this._otherDrawTools = []
    this.snapAndReferGeometries = defaultValue(
      options.snapAndReferGeometries,
      []
    )
  }

  /**
   * @description 鼠标绘制图形:鼠标单击绘制区的一个顶点;鼠标移动,区图形随鼠标位置变动;鼠标双击,完成区图形绘制。
   */
  drawFeature() {
    // 屏蔽地图默认双击事件
    this.view._mapActionControl('double-click-zoom', false)
    // 控制鼠标移动事件频率
    const timer = null
    // 鼠标点击两次以上,即有两个以上的点构成区时,触发鼠标移动时逻辑
    let isStartDrawing = false
    let afterClick = false
    // 鼠标移动过程中,控制鼠标移动逻辑触发次数
    let moving = false
    // 当前正在绘制的图形对象
    let feature = null
    let pixelCoords = []
    // 绘制过程中所有的端点
    let polylineSpots = []
    // 绘制过程中所有所有线段的斜率
    const polylineSpotSlopes = []
    // timer判断是单击事件、双击事件
    const clickTimer = null
    // 区分单击、双击事件,若为双击事件,则阻止单击事件逻辑触发
    // let preventClick = false
    let lastAction = ''
    // 获取两点斜率
    const getSlope = function (point1, point2) {
      return (point1[1] - point2[1]) / (point1[0] - point2[0])
    }
    // 捕获到的坐标点
    let snapPoint = null
    // 处理鼠标点击事件。点击后渲染端点图形,渲染区图形
    const handlerClick = (options) => {
      // clickTimer = setTimeout(() => {
      //   preventClick = false
      // }, 300)
      // // 判断是否为单击事件
      // if (preventClick) return
      if (!options || !options.x || !options.y) return
      clearTimeout(timer)
      this._editMode = 3
      const pixelCoord = { x: options.x, y: options.y }
      let geoCoord = null
      if (!snapPoint) {
        // 如果没有捕获结果,则此时坐标点为屏幕地理坐标
        geoCoord = this.view.toMap({ x: pixelCoord.x, y: pixelCoord.y })
        // if (this._hight) {
        //   geoCoord = new Point({
        //     coordinates: [
        //       geoCoord.coordinates[0],
        //       geoCoord.coordinates[1],
        //       this._hight
        //     ],
        //     spatialReference: this._spatialReference
        //   })
        // }
      } else {
        // 如果有捕获结果,则此时坐标点选取捕获的坐标点
        geoCoord = snapPoint
      }
      if (polylineSpots.length === 0) {
        pixelCoords.push(pixelCoord)
        polylineSpots.push(geoCoord.coordinates)
      } else if (polylineSpots.length === 1) {
        pixelCoords.push(pixelCoord)
        polylineSpots.push(geoCoord.coordinates)
        feature = this._getFeature()
        feature.geometry._pixelCoords = pixelCoords
        feature.geometry.coordinates = polylineSpots
        // 保存当前编辑中的geometry
        this._polylineGeometry = feature
      } else {
        pixelCoords[pixelCoords.length - 1] = pixelCoord
        polylineSpots[polylineSpots.length - 1] = geoCoord.coordinates
        feature.geometry._pixelCoords = pixelCoords
        feature.geometry.coordinates = polylineSpots
        // feature.geometry.coordinates[feature.geometry.coordinates.length - 1] =
        //   geoCoord.coordinates
      }
      // 临时解决,move事件有可能发生在两个click事件之间,导致多绘制一个点
      if (
        lastAction === 'move' &&
        polylineSpots[polylineSpots.length - 1][0] ===
          polylineSpots[polylineSpots.length - 2][0] &&
        polylineSpots[polylineSpots.length - 1][1] ===
          polylineSpots[polylineSpots.length - 2][1]
      ) {
        polylineSpots.splice(polylineSpots.length - 1)
      }

      const pointDrawTool = new SketchPointDrawTool({
        view: this.view,
        layer: this.layer,
        _parent: this,
        sketchStyle: this.sketchStyle
      })
      pointDrawTool.addFeature(geoCoord)
      const vertexGraphic = pointDrawTool.sketchStage.entityGraphic
      this.sketchStage.entityGraphic = feature
      this.sketchStage.vertexGraphics.push(vertexGraphic)

      // 在polyline中更新所有图形
      if (feature) {
        isStartDrawing = true
        afterClick = isStartDrawing
      }
      // preventClick = true
      // 计算线段斜率
      if (polylineSpots.length > 1) {
        polylineSpotSlopes.push(
          getSlope(
            polylineSpots[polylineSpots.length - 1],
            polylineSpots[polylineSpots.length - 2]
          )
        )
      }
      // this.startTime = new Date().getTime()
      lastAction = 'click'
      // 发送绘制一个顶点完成事件
      this.fire(
        'drawn-vertex',
        { geometry: pointDrawTool.sketchStage.entityGraphic },
        self
      )
    }
    // 处理平移时,鼠标移动时逻辑。鼠标移动时根据鼠标坐标实时更新区图形。
    let isFirstAdd = true
    const handlerMove = (options) => {
      if (!options || !options.x || !options.y) return
      if (!isStartDrawing || moving) return
      clearTimeout(timer)
      moving = true
      // 清除捕获效果(上一次)
      if (this.sketchStage.snapGraphics.length > 0) {
        this.sketchStage.snapGraphics.forEach((feature) => {
          this._removeFeatureFromMap(feature)
        })
        this.sketchStage.snapGraphics = []
      }

      const pixelCoord = { x: options.x, y: options.y }
      const geoCoord = this.view.toMap({ x: pixelCoord.x, y: pixelCoord.y })
      // if (this._hight) {
      //   geoCoord = new Point({
      //     coordinates: [
      //       geoCoord.coordinates[0],
      //       geoCoord.coordinates[1],
      //       this._hight
      //     ],
      //     spatialReference: this._spatialReference
      //   })
      // }

      this.endTime = new Date().getTime()
      if (afterClick) {
        pixelCoords[pixelCoords.length] = pixelCoord
        polylineSpots[polylineSpots.length] = geoCoord.coordinates
        afterClick = false
      } else {
        pixelCoords[pixelCoords.length - 1] = pixelCoord
        polylineSpots[polylineSpots.length - 1] = geoCoord.coordinates
      }
      this.sketchStage.entityGraphic = feature

      // 鼠标移动时,去捕获重合点、点在线上、平行线、垂直线
      snapPoint = this.getSnapGraphics(
        geoCoord,
        polylineSpots,
        polylineSpotSlopes
      )
      if (snapPoint) {
        polylineSpots[polylineSpots.length - 1] = snapPoint.coordinates // geoCoord.coordinates
      }

      // 更新草图图层
      feature.geometry._pixelCoords = pixelCoords
      feature.geometry.coordinates = JSON.parse(JSON.stringify(polylineSpots))
      if (isFirstAdd) {
        this._addFeaturesToMap(feature)
        isFirstAdd = false
      }
      moving = false
      lastAction = 'move'
    }
    // 处理鼠标双击时,处理逻辑
    const handlerDoubleClick = () => {
      clearTimeout(clickTimer)
      clearTimeout(timer)
      // preventClick = true

      // if (!isStartDrawing) {
      //   feature = null
      //   polylineSpots = []
      //   pixelCoords = []
      //   return
      // }

      polylineSpots = []
      pixelCoords = []
      // 停止绘制
      this.stop()
      // 清除捕获效果
      if (this.sketchStage.snapGraphics.length > 0) {
        this.sketchStage.snapGraphics.forEach((feature) => {
          this._removeFeatureFromMap(feature)
        })
        this.sketchStage.snapGraphics = []
      }
      this._editMode = 0
      // 绘制完成后,移除顶点
      this.sketchStage.vertexGraphics.forEach((feature) => {
        this._removeFeatureFromMap(feature)
      })
      this.sketchStage.vertexGraphics = []
      // 恢复地图默认双击事件
      this.view._mapActionControl('double-click-zoom', true)
      lastAction = 'double-click'

      // 计算交点,将polyline转为polygon 的feature,并更新图层
      const polygon = this._createPolygonFeatureByPolyline(
        this.sketchStage.entityGraphic.geometry
      )
      this._removeFeatureFromMap(this.sketchStage.entityGraphic)
      // 发送绘制完成事件
      this.fire('drawn', { geometry: polygon }, self)
    }
    // 移除绘制事件
    this._drawEventHandlers.push(handlerClick, handlerMove, handlerDoubleClick)
    this.view.on(ViewEventType.immediateClick, handlerClick)
    this.view.on(ViewEventType.pointerMove, handlerMove)
    this.view.on(ViewEventType.doubleClick, handlerDoubleClick)
  }

  _createPolygonFeatureByPolyline(polyline) {
    // const polyline = feature.geometry
    const lineToCloseLine = (polyline) => {
      const coordinates = JSON.parse(JSON.stringify(polyline.coordinates))
      coordinates.push([polyline.coordinates[0]])
      const closePolyline = new LineString({
        coordinates,
        spatialReference: polyline._spatialReference
      })
      return closePolyline
    }
    const lineToPolygon = (polyline) => {
      const coordinates = JSON.parse(JSON.stringify(polyline.coordinates))
      coordinates.push(coordinates[0])
      const closePolyline = new Polygon({
        coordinates: [coordinates],
        spatialReference: polyline._spatialReference
      })
      return closePolyline
    }
    const closePolygon = lineToPolygon(polyline)
    const getValidGraphics = () => {
      const validGraphics = []
      this.snapAndReferGeometries.forEach((graphic) => {
        if (
          graphic instanceof Polygon ||
          graphic instanceof Circle ||
          graphic instanceof Extent
        ) {
          const isIntersect = GeometryEngine.intersects(polyline, graphic)
          if (isIntersect) {
            validGraphics.push(graphic)
          }
        }
      })
      return validGraphics
    }
    const validGraphics = getValidGraphics()
    const comparedPolygon = GeometryEngine.union(validGraphics)
    let resultPolygon
    if (comparedPolygon && !(comparedPolygon instanceof Array)) {
      resultPolygon = GeometryEngine.difference(closePolygon, comparedPolygon)
    } else {
      resultPolygon = closePolygon
    }
    return resultPolygon // polygonFeature
  }

  /**
   * @description 获取feature实例
   * @private
   * @param {Point} point 生成面的点地理坐标集
   * @param {Symbol} symbol 点的符号样式
   */
  _getFeature(points) {
    if (!points) {
      points = [
        [0, 0],
        [1, 1]
      ]
    }
    const geometry = new LineString({
      coordinates: points,
      spatialReference: this._spatialReference
    })
    const symbol = this.sketchStyle.lineStyle
    const polylineGeometry = new Feature({
      geometry,
      symbol
    })
    return polylineGeometry
  }

  /**
   * 图形捕获,捕获重合点、点在线上、平行线、垂直线
   * @private
   * @param {Point} geoCoord 当前坐标点
   * @param {Array} polygonSpots 被捕获的顶点数据组
   * @param {Array} polygonSpotSlopes 被捕获的直线斜率数据组
   */
  getSnapGraphics(targetPoint, polygonSpots) {
    let realPoint = null
    const tolerance = this.getDistanceByPixel
      ? this.getDistanceByPixel(this.sketchSnappingTool.pixelTolerance)
      : 200
    const moveLine = new LineString({
      coordinates: polygonSpots.slice(polygonSpots.length - 2),
      spatialReference: this._spatialReference
    })
    let geometries
    if (this.sketchSnappingTool.snapSketchGeometry && polygonSpots.length > 3) {
      const entityPolyline = new LineString({
        coordinates: polygonSpots.slice(0, polygonSpots.length - 2),
        spatialReference: this._spatialReference
      })
      geometries = this.snapAndReferGeometries.concat([entityPolyline])
    } else {
      geometries = this.snapAndReferGeometries
    }
    // 获取捕获结果
    const snapResult = this.sketchSnappingTool.snapGeometries(
      targetPoint,
      moveLine,
      geometries,
      tolerance
    )
    if (snapResult.coincidentPoint) {
      // 端点重合
      const pointFeature = new Feature({
        geometry: snapResult.coincidentPoint.realPoint,
        symbol: this.sketchStyle._coincidentPointStyle
      })
      this.sketchStage.snapGraphics.push(pointFeature)
      realPoint = snapResult.coincidentPoint.realPoint
    } else if (snapResult.inLineAndParallel) {
      snapResult.inLineAndParallel.lines.forEach((line) => {
        const lineFeature = new Feature({
          geometry: line,
          symbol: new SimpleLineSymbol({
            color: new Color(255, 255, 0, 1),
            width: 3
          })
        })
        this.sketchStage.snapGraphics.push(lineFeature)
      })
      const pointFeature = new Feature({
        geometry: snapResult.inLineAndParallel.realPoint,
        symbol: this.sketchStyle._selectVertexStyle
      })
      this.sketchStage.snapGraphics.push(pointFeature)
      if (snapResult.inLineAndParallel.symbolGeometry) {
        const symbolFeature = new Feature({
          geometry: snapResult.inLineAndParallel.symbolGeometry,
          symbol: new SimpleLineSymbol({
            color: new Color(255, 255, 0, 1),
            width: 3
          })
        })
        this.sketchStage.snapGraphics.push(symbolFeature)
      }
      realPoint = snapResult.inLineAndParallel.realPoint
    } else if (snapResult.inLineAndPerpendicular) {
      snapResult.inLineAndPerpendicular.lines.forEach((line) => {
        const lineFeature = new Feature({
          geometry: line,
          symbol: new SimpleLineSymbol({
            color: new Color(255, 255, 0, 1),
            width: 3
          })
        })
        this.sketchStage.snapGraphics.push(lineFeature)
      })
      const pointFeature = new Feature({
        geometry: snapResult.inLineAndPerpendicular.realPoint,
        symbol: this.sketchStyle._selectVertexStyle
      })
      this.sketchStage.snapGraphics.push(pointFeature)
      if (snapResult.inLineAndPerpendicular.symbolGeometry) {
        const symbolFeature = new Feature({
          geometry: snapResult.inLineAndPerpendicular.symbolGeometry,
          symbol: new SimpleLineSymbol({
            color: new Color(255, 255, 0, 1),
            width: 3
          })
        })
        this.sketchStage.snapGraphics.push(symbolFeature)
      }
      realPoint = snapResult.inLineAndPerpendicular.realPoint
    } else if (snapResult.pointInLine) {
      // 点在线上捕获上图
      const lineFeature = new Feature({
        geometry: snapResult.pointInLine.line,
        symbol: new SimpleLineSymbol({
          color: new Color(255, 255, 0, 1),
          width: 3
        })
      })
      this.sketchStage.snapGraphics.push(lineFeature)
      const pointFeature = new Feature({
        geometry: snapResult.pointInLine.realPoint,
        symbol: this.sketchStyle._selectVertexStyle
      })
      this.sketchStage.snapGraphics.push(pointFeature)
      realPoint = snapResult.pointInLine.realPoint
    } else if (snapResult.parallelLine) {
      // 平行捕获结果上图
      const lineFeature = new Feature({
        geometry: snapResult.parallelLine.line,
        symbol: new SimpleLineSymbol({
          color: new Color(255, 255, 0, 1),
          width: 3
        })
      })
      this.sketchStage.snapGraphics.push(lineFeature)
      const pointFeature = new Feature({
        geometry: snapResult.parallelLine.realPoint,
        symbol: this.sketchStyle._selectVertexStyle
      })
      this.sketchStage.snapGraphics.push(pointFeature)
      // 渲染高亮平行
      if (snapResult.parallelLine.symbolGeometry) {
        const symbolFeature = new Feature({
          geometry: snapResult.parallelLine.symbolGeometry,
          symbol: new SimpleLineSymbol({
            color: new Color(255, 255, 0, 1),
            width: 3
          })
        })
        this.sketchStage.snapGraphics.push(symbolFeature)
      }
      realPoint = snapResult.parallelLine.realPoint
    } else if (snapResult.perpendicularLine) {
      // 渲染高亮垂直,坐标点
      const lineFeature = new Feature({
        geometry: snapResult.perpendicularLine.line,
        symbol: new SimpleLineSymbol({
          color: new Color(255, 255, 0, 1),
          width: 3
        })
      })
      this.sketchStage.snapGraphics.push(lineFeature)
      const pointFeature = new Feature({
        geometry: snapResult.perpendicularLine.realPoint,
        symbol: this.sketchStyle._selectVertexStyle
      })
      this.sketchStage.snapGraphics.push(pointFeature)
      // 渲染高亮垂足
      if (snapResult.perpendicularLine.symbolGeometry) {
        const symbolFeature = new Feature({
          geometry: snapResult.perpendicularLine.symbolGeometry,
          symbol: new SimpleLineSymbol({
            color: new Color(255, 255, 0, 1),
            width: 3
          })
        })
        this.sketchStage.snapGraphics.push(symbolFeature)
      }
      realPoint = snapResult.perpendicularLine.realPoint
    }
    this.sketchStage.snapGraphics.forEach((feature) => {
      this._addFeaturesToMap(feature)
    })
    return realPoint
  }
}
export default SketchPolylineDrawTool
构造函数
成员变量
方法
事件