类名 common/sketchEditor/base/SketchBaseDrawTool.js
import { Evented, ViewEventType } from '../../base'
import { defaultValue, getGUID } from '../../util'
import { SpatialReference } from '../../base/geometry'
import SketchStage from './SketchStage'
/**
 * 草图基础绘图工具类
 * @class SketchBaseDrawTool
 * @moduleEX SketchEditorModule
 * @param {Object} options 构造参数
 * @param {MapView|SceneView}  [options.view]  地图视图对象
 * @param {GraphicsLayer}  [options.layer]  草图图层管对象
 * @param {SketchStyle}  [options.sketchStyle]  草图符号
 */

class SketchBaseDrawTool extends Evented {
  constructor(options) {
    super()
    this._id = defaultValue(options.id, getGUID())
    /**
     * 地图视图
     * @member {MapView|SceneView} SketchBaseDrawTool.prototype.view
     */
    this.view = defaultValue(options.view, null)
    /**
     * 草图图层
     * @member {GraphicsLayer} SketchBaseDrawTool.prototype.layer
     */
    this.layer = defaultValue(options.layer, undefined)
    /**
     * 草图绘图形集合
     * @member {SketchStage} SketchBaseDrawTool.prototype.sketchStage
     */
    this.sketchStage = new SketchStage()
    /**
     * 草图符号
     * @member {SketchStyle} SketchBaseDrawTool.prototype.sketchStyle
     */
    this.sketchStyle = defaultValue(options.sketchStyle, undefined)
    // 草图编辑状态
    // 0:没有画图或编辑状态
    // 1:图形编辑
    // 2:节点编辑
    // 3:画图中
    this._editMode = 0
    // 当前空间参考系
    this._spatialReference = options.view.crs
      ? new SpatialReference(options.view.crs.code)
      : new SpatialReference('EPSG:4326')
    // 草图绘制事件处理集
    this._drawEventHandlers = []
    // 草图拾取事件处理集
    this._hitTestEventHandlers = []
    // 草图类型
    this._sketchDataType = null
    // 所属父级绘图工具
    this._parent = defaultValue(options._parent, undefined)
    /**
     * 草图撤销回退管理器
     * @member {String} SketchBaseDrawTool.prototype.undoRedoManager
     */
    this.undoRedoManager = defaultValue(options.undoRedoManager, undefined)

    /**
     * 草图选中编辑配置项
     * @member {Object} SketchBaseDrawTool.prototype.editOption
     * @param {Object} [editOption.showSelectBox = {}] 捕捉时是否展示选中编辑框
     * @param {Object} [editOption.showSelectVertex = {}] 捕捉时是否展示顶点可编辑图形
     * @param {Object} [editOption._hitTestMode = {}] 捕捉拾取方式,0:click鼠标拾取。1:pointerDown鼠标按下拾取
     */
    const editOption = {
      showSelectBox: true,
      showSelectVertex: false,
      canEditFeature: true,
      canEditVertex: true,
      _hitTestMode: 0
    }
    this.editOption = defaultValue(options.editOption, editOption)

    // todo sketchFeatures需要删掉
    this.view.sketchFeatures = this.view.sketchFeatures
      ? this.view.sketchFeatures
      : []
    this._drawTools = []
    this._hitTestDrawToolStack = []
    this._clampToGround = true
    // this._hight = 1000

    this.snapAndReferGeometries = []

    // 所属sketchEditor对象
    this._sketchEditor = defaultValue(options._sketchEditor, undefined)
    if (this._sketchEditor) {
      this.sketchSnappingTool = this._sketchEditor._sketchSnappingTool
      this.snapAndReferGeometries = this._sketchEditor.snapAndReferGeometries
    }
  }

  /**
   * 开始绘制草图
   */
  start() {
    this.stop()
    this.drawFeature()
  }

  /**
   * 停止绘制草图
   */
  stop() {
    if (this._editMode === 3 || this._editMode === 0) {
      this._stopDraw()
    } else if (this._editMode === 1 || this._editMode === 2) {
      this._stopHitTest()
    } else {
      this._stopDraw()
      this._stopHitTest()
    }
  }

  /**
   * 停止所有鼠标事件
   * @private
   */
  _stopAll() {
    this._stopDraw()
    this._stopHitTest()
  }

  /**
   * 停止所有鼠标绘制相关事件
   * @private
   */
  _stopDraw() {
    if (this._drawEventHandlers) {
      this._drawEventHandlers.forEach((handler) => {
        this.view.off(ViewEventType.immediateClick, handler)
        this.view.off(ViewEventType.pointerUp, handler)
        this.view.off(ViewEventType.pointerMove, handler)
        this.view.off(ViewEventType.pointerDown, handler)
        this.view.off(ViewEventType.doubleClick, handler)
        this.view.off(ViewEventType.drag, handler)
      })
      this._drawEventHandlers = []
    }
  }

  /**
   * 停止所有拾取相关事件
   * @private
   */
  _stopHitTest() {
    if (this._hitTestEventHandlers && this._hitTestILayer) {
      this._hitTestEventHandlers.forEach((handler) => {
        this._hitTestILayer.off(ViewEventType.click, handler)
        this.view.off(ViewEventType.pointerUp, handler)
        this.view.off(ViewEventType.pointerMove, handler)
        this.view.off(ViewEventType.pointerDown, handler)
        // 确定hitTest事件是click,还是immediateClick
        this.view.off(ViewEventType.click, handler)
      })
      this._hitTestEventHandlers = null
    }
  }

  /**
   * 设置草图样式
   * @param {SketchStyle} sketchStyle
   */
  setSketchStyle(sketchStyle) {
    this.sketchStyle = sketchStyle
  }

  /**
   * @description 删除某编辑状态的辅助图形
   * @private
   * @param {Number} editMode 编辑状态码
   */
  _clearEditGraphics(editMode) {
    const clearSelectFeature = () => {
      const features = this.sketchStage.selectBoxGraphics.concat(
        this.sketchStage.selectBoxVertexGraphics
      )
      features.forEach((feature) => {
        this._removeFeatureFromMap(feature)
      })
      this._otherDrawTools = []
      this.sketchStage.selectBoxGraphics = []
      this.sketchStage.selectBoxVertexGraphics = []
    }
    const clearSelectFeatureVertex = () => {
      let target = null
      if (this._parent) {
        target = this._parent
      } else {
        target = this
      }
      const features = target.sketchStage.vertexGraphics.concat(
        target.sketchStage.midVertexGraphics
      )
      features.forEach((feature) => {
        target._removeFeatureFromMap(feature)
      })

      target._otherDrawTools = []
      target.sketchStage.vertexGraphics = []
      target.sketchStage.midVertexGraphics = []
    }
    if (editMode === 1) {
      clearSelectFeature()
    } else if (editMode === 2) {
      clearSelectFeatureVertex()
    } else {
      clearSelectFeature()
      clearSelectFeatureVertex()
    }
  }

  /**
   * 判断feature是否是绘制图形中
   * @private
   * @param {SketchStyle} sketchStyle
   */
  _isDrawToolFeature(feature) {
    const id = feature.id
    let isDrawTool = false
    if (this.sketchStage.entityGraphic.id === id) {
      isDrawTool = true
      this._editMode = 1
    }
    this.sketchStage.vertexGraphics.forEach((item) => {
      if (item.id === id) {
        isDrawTool = true
        this._editMode = 2
      }
    })
    this.sketchStage.midVertexGraphics.forEach((item) => {
      if (item.id === id) {
        isDrawTool = true
        this._editMode = 2
      }
    })
    if (!isDrawTool) {
      this._editMode = 0
    }
    return isDrawTool
  }

  _getDrawToolByFeature(feature) {
    const id = feature.id
    let drawTool = null
    if (this.sketchStage.entityGraphic.id === id) {
      drawTool = this.sketchStage.entityGraphic
    }
    return drawTool.length > 0 ? drawTool[0] : null
  }

  /**
   * 将图形渲染到地图中
   * @private
   * */
  _addFeaturesToMap(feature) {
    if (this.layer) {
      // 判断this._clampToGround,三维中贴地设置
      this.layer.add(feature)
    }
  }

  _removeFeatureFromMap(feature) {
    if (this.layer) {
      this.layer.remove(feature)
    }
  }

  /**
   * 草图事件分发
   * @private
   * @param {String} type 事件类型
   * @param {object} data 传递数据
   */
  _dispatchEvent(type, data, propagate) {
    // 父级草图更新
    if (this._parent) {
      this._parent.fire(type, data, propagate)
    }
    return this
  }

  /**
   * 更新feature
   * @param {Point} point 待更新的位置
   * @param {Feature} feature 被更新的feature
   */
  updateFeature(point, feature) {
    const id = feature.id
    if (this.sketchStage.entityGraphic.id === id) {
      this.sketchStage.entityGraphic.coordinates = point.coordinates
      this.sketchStage.entityGraphic.geometry = point
      feature = this.sketchStage.entityGraphic
    }
  }

  updateSketchStage(sketchStage) {
    this.sketchStage = sketchStage
    const features = this.sketchStage.getAllFeatures()
    this.view.sketchFeatures = features
    this._addFeaturesToMap()
  }

  /**
   * @description 清除草图工具
   */
  removeDrawTool() {
    this.sketchStage.getAllFeatures().forEach((feature) => {
      this._removeFeatureFromMap(feature)
    })
    this._stopAll()
  }

  /**
   * @description 清除实体图形
   * @private
   */
  removeEntityGraphic() {
    this._removeFeatureFromMap(this.sketchStage.entityGraphic)
    this.sketchStage.entityGraphic = null
  }

  /**
   * @description 清除顶点图形
   * @private
   */
  removeVertexGraphics() {
    this.sketchStage.vertexGraphics.forEach((feature) => {
      this._removeFeatureFromMap(feature)
    })
    this.sketchStage.vertexGraphics = []
  }

  /**
   * 当前草图图形中插入新的顶点
   * @param {Point} point 新增/插入顶点
   * @param {Number} index 新增/新增点的序号
   */
  addVertex(point, index) {}

  /**
   * 更新当前草图图形的某个顶点
   * @param {Point} point 新的顶点
   * @param {Number} index 需更新的顶点的序号
   */
  updateVertex(point, index) {}

  /**
   * 移除草图图形的某个顶点
   * @param {Number} index 需更新的顶点的序号
   */
  removeVertex(point, index) {}

  /**
   * @description 删除选中的顶点
   * @private
   */
  deleteSelectedVertex() {}

  // 临时用于更新图形
  removeAddGraphic(newFeature) {
    // test
    // 更新sketchFeatures
    this.view.sketchFeatures = this.view.sketchFeatures
      ? this.view.sketchFeatures
      : []
    let haveFeature = false
    this.view.sketchFeatures.forEach((feature) => {
      if (feature.id === newFeature.id) {
        feature = newFeature
        haveFeature = true
      }
    })
    if (!haveFeature) {
      this.view.sketchFeatures.push(newFeature)
    }
    this._addFeaturesToMap(true)
  }

  updateAllFeature(point) {
    this.sketchStage.entityGraphic.coordinates = point.coordinates
    this.sketchStage.entityGraphic.geometry = point
    this.view.sketchFeatures.forEach((item) => {
      if (item.id === this.sketchStage.entityGraphic.id) {
        item = this.sketchStage.entityGraphic
      }
    })
    this._addFeaturesToMap()
  }

  getDistanceByPixel(pixel) {
    if (this.view.getResolution) {
      return this.view.getResolution() * pixel
    } else {
      return 1000
    }
  }

  getPanCoordinates(coordinates, deltaX, deltaY) {
    let newCoordinates = []
    if (coordinates.length > 2) {
      newCoordinates = [
        coordinates[0] + deltaX,
        coordinates[1] + deltaY,
        coordinates[2]
      ]
    } else {
      newCoordinates = [coordinates[0] + deltaX, coordinates[1] + deltaY]
    }
    return newCoordinates
  }

  getScaleCoordinates(
    coordinates,
    direction,
    extent,
    startPoint,
    movePoint,
    oriCoordinates
  ) {
    const getXY = function (xy, extent, direction, oriCoordinate) {
      let scaleX1 = 1
      let scaleY1 = 1
      let newX = Number(xy[0].toString())
      let newY = Number(xy[1].toString())
      const sCoordinate = startPoint.coordinates
      const mCoordinate = movePoint.coordinates
      oriCoordinate = oriCoordinate || []
      if (direction === 'ne') {
        scaleX1 =
          (mCoordinate[0] - extent.xmin) / (sCoordinate[0] - extent.xmin)
        newX = ([oriCoordinate[0]] - extent.xmin) * scaleX1 + extent.xmin

        scaleY1 =
          (mCoordinate[1] - extent.ymin) / (sCoordinate[1] - extent.ymin)
        newY = (oriCoordinate[1] - extent.ymin) * scaleY1 + extent.ymin
      }
      if (direction === 'se') {
        scaleX1 =
          (mCoordinate[0] - extent.xmin) / (sCoordinate[0] - extent.xmin)
        newX = ([oriCoordinate[0]] - extent.xmin) * scaleX1 + extent.xmin

        scaleY1 =
          (mCoordinate[1] - extent.ymax) / (sCoordinate[1] - extent.ymax)
        newY = (oriCoordinate[1] - extent.ymax) * scaleY1 + extent.ymax
      }
      if (direction === 'sw') {
        scaleX1 =
          (mCoordinate[0] - extent.xmax) / (sCoordinate[0] - extent.xmax)
        newX = ([oriCoordinate[0]] - extent.xmax) * scaleX1 + extent.xmax

        scaleY1 =
          (mCoordinate[1] - extent.ymax) / (sCoordinate[1] - extent.ymax)
        newY = (oriCoordinate[1] - extent.ymax) * scaleY1 + extent.ymax
      }
      if (direction === 'nw') {
        scaleX1 =
          (mCoordinate[0] - extent.xmax) / (sCoordinate[0] - extent.xmax)
        newX = ([oriCoordinate[0]] - extent.xmax) * scaleX1 + extent.xmax

        scaleY1 =
          (mCoordinate[1] - extent.ymin) / (sCoordinate[1] - extent.ymin)
        newY = (oriCoordinate[1] - extent.ymin) * scaleY1 + extent.ymin
      }
      return [newX, newY]
    }
    const newCoordinates = []
    oriCoordinates = oriCoordinates || []
    coordinates.forEach((coordinate, i) => {
      const result = getXY(coordinate, extent, direction, oriCoordinates[i])
      if (coordinate[2] !== undefined) {
        result[2] = coordinate[2]
      }
      newCoordinates.push(result)
    })
    return newCoordinates
  }

  getMidScaleCoordinates(
    coordinates,
    direction,
    extent,
    startPoint,
    movePoint,
    oriCoordinates
  ) {
    const getXY = function (xy, extent, direction, oriCoordinate) {
      let scaleX1 = 1
      let scaleY1 = 1
      let newX = xy[0]
      let newY = xy[1]
      const sCoordinate = startPoint.coordinates
      const mCoordinate = movePoint.coordinates
      oriCoordinate = oriCoordinate || []
      if (direction === 'e') {
        // 判断是否超过边缘坐标
        scaleX1 =
          (mCoordinate[0] - extent.xmin) / (sCoordinate[0] - extent.xmin)
        newX = ([oriCoordinate[0]] - extent.xmin) * scaleX1 + extent.xmin
      }
      if (direction === 's') {
        scaleY1 =
          (mCoordinate[1] - extent.ymax) / (sCoordinate[1] - extent.ymax)
        newY = (oriCoordinate[1] - extent.ymax) * scaleY1 + extent.ymax
      }
      if (direction === 'w') {
        scaleX1 =
          (mCoordinate[0] - extent.xmax) / (sCoordinate[0] - extent.xmax)
        newX = ([oriCoordinate[0]] - extent.xmax) * scaleX1 + extent.xmax
      }
      if (direction === 'n') {
        scaleY1 =
          (mCoordinate[1] - extent.ymin) / (sCoordinate[1] - extent.ymin)
        newY = (oriCoordinate[1] - extent.ymin) * scaleY1 + extent.ymin
      }
      return [newX, newY]
    }
    const newCoordinates = []
    oriCoordinates = oriCoordinates || []
    coordinates.forEach((coordinate, i) => {
      const result = getXY(coordinate, extent, direction, oriCoordinates[i])
      if (coordinate[2]) {
        result[2] = coordinate[2]
      }
      newCoordinates.push(result)
    })
    return newCoordinates
  }

  getCenterScaleCoordinates(
    coordinates,
    direction,
    extent,
    startPoint,
    movePoint,
    oriCoordinates
  ) {
    const getXY = function (xy, extent, direction, oriCoordinate) {
      let scaleX1 = 1
      let scaleY1 = 1
      let newX = xy[0]
      let newY = xy[1]
      const sCoordinate = startPoint.coordinates
      const mCoordinate = movePoint.coordinates
      oriCoordinate = oriCoordinate || []
      if (direction === 'e') {
        // 判断是否超过边缘坐标
        scaleX1 =
          (mCoordinate[0] - extent.xmin) / (sCoordinate[0] - extent.xmin)
        newX = ([oriCoordinate[0]] - extent.xmin) * scaleX1 + extent.xmin
      }
      if (direction === 's') {
        scaleY1 =
          (mCoordinate[1] - extent.ymax) / (sCoordinate[1] - extent.ymax)
        newY = (oriCoordinate[1] - extent.ymax) * scaleY1 + extent.ymax
      }
      if (direction === 'w') {
        scaleX1 =
          (mCoordinate[0] - extent.xmax) / (sCoordinate[0] - extent.xmax)
        newX = ([oriCoordinate[0]] - extent.xmax) * scaleX1 + extent.xmax
      }
      if (direction === 'n') {
        scaleY1 =
          (mCoordinate[1] - extent.ymin) / (sCoordinate[1] - extent.ymin)
        newY = (oriCoordinate[1] - extent.ymin) * scaleY1 + extent.ymin
      }
      return [newX, newY]
    }
    const newCoordinates = []
    oriCoordinates = oriCoordinates || []
    coordinates.forEach((coordinate, i) => {
      const result = getXY(coordinate, extent, direction, oriCoordinates[i])
      if (coordinate[2]) {
        result[2] = coordinate[2]
      }
      newCoordinates.push(result)
    })
    return newCoordinates
  }

  addByFeature(feature) {
    this.sketchStage.entityGraphic = feature
    this.view.sketchFeatures.push(feature)
    // // 更新图层图形
    // this._addFeaturesToMap()
  }

  _setSketchStage(stage) {
    this.sketchStage = stage
  }

  // 临时方法
  _removeFromSketchFeatures(removedFeatures) {
    this.view.sketchFeatures.forEach((feature, i) => {
      if (removedFeatures.filter((item) => item.id === feature.id).length > 0) {
        delete this.view.sketchFeatures[i]
      }
    })
    this.view.sketchFeatures = this.view.sketchFeatures.filter(
      (item) => item !== undefined
    )
  }

  _getDrawToolByFeatureId(id) {
    const drawTool = this._drawTools.find(
      (drawTool) =>
        drawTool.sketchStage.entityGraphic &&
        drawTool.sketchStage.entityGraphic.id === id
    )
    return drawTool
  }

  // 清除编辑辅助图形
  _removeEditGraphics() {
    const removedFeatures = this.sketchStage.selectBoxGraphics
      .concat(this.sketchStage.selectBoxVertexGraphics)
      .concat(this.sketchStage.vertexGraphics)
      .concat(this.sketchStage.midVertexGraphics)
    removedFeatures.forEach((feature) => {
      this._removeFeatureFromMap(feature)
    })
    this.sketchStage.selectBoxGraphics = []
    this.sketchStage.selectBoxVertexGraphics = []
    this.sketchStage.vertexGraphics = []
    this.sketchStage.midVertexGraphics = []
  }
}
export default SketchBaseDrawTool
构造函数
成员变量
方法
事件