类名 common/sketchEditor/base/SketchEditorNew.js
import { Zondy, Evented, SketchDataType, Projection } from '../../base'
import { defaultValue, Log } from '../../util'
import {
  Point,
  LineString,
  Polygon,
  SpatialReference,
  Circle,
  Extent
} from '../../base/geometry'
import SketchStyle from './SketchStyle'
import SketchPointDrawTool from './SketchPointDrawTool'
import SketchPolygonDrawTool from './SketchPolygonDrawTool'
import SketchSnappingTool from './SketchSnappingTool'
import SketchPolylineDrawTool from './SketchPolylineDrawTool'
import SketchCircleDrawTool from './SketchCircleDrawTool'
import SketchRectangleDrawTool from './SketchRectangleDrawTool'
import SketchTopologyTool from './SketchTopologyTool'
import UndoRedoManager from './UndoRedoManager'
/**
 * 草图编辑基类
 * @class SketchEditorNew
 * @moduleEX SketchEditorModule
 * @extends Evented
 * @param {Object} options 构造参数
 * @param {MapView|SecenView}  [options.mapView]  地图视图对象
 * @param {GraphicsLayer}  [options.layer]  草图图层管对象
 * @param {SketchStyle}  [options.sketchStyle]  草图符号
 * @param {Object}  [options.snappingOption]  草图捕获配置项
 *
 * @summary <h5>支持如下方法:</h5>
 * <a href='#start'>[1、开始绘制草图]</a><br/>
 * <a href='#stop'>[2、停止绘制]</a><br/>
 * <a href='#remove'>[3、移除当前草图]</a><br/>
 * <a href='#update'>[4、更新当前草图]</a><br/>
 * <a href='#addVertex'>[5、向当前线或面草图中插入新的顶点]</a><br/>
 * <a href='#updateVertex'>[6、更新草图图形的某个顶点]</a><br/>
 * <a href='#removeVertex'>[7、移除草图图形的某个顶点]</a><br/>
 * <a href='#getSketchDataType'>[8、获取草图图形类型]</a><br/>
 * <a href='#setSketchStyle'>[9、设置草图样式]</a><br/>
 * <a href='#getSketchStyle'>[10、获取草图样式]</a><br/>
 * <a href='#getGeometry'>[11、获取草图几何对象]</a><br/>
 * <a href='#union'>[12、合并多个区几何]</a><br/>
 * <a href='#split'>[13、分割草图对象或区几何对象]</a><br/>
 * <a href='#undo'>[14、撤销当前编辑操作]</a><br/>
 * <a href='#redo'>[15、恢复被撤销的草图]</a><br/>
 * @example
 * // ES5引入方式
 * const { SketchEditorNew } = Zondy
 * // ES6引入方式
 * import { SketchEditorNew } from "@mapgis/webclient-common"
 */
/**
 * @event SketchEditorNew#草图绘制完成事件
 * @property {Object} event 事件对象
 * @property {LayerEventType} [event.type = 'drawn'] 事件类型
 * @property {Geometry} [event.geometry] 绘制的几何对象
 * @example <caption><h7>鼠标绘制完成事件</h7></caption>
 * sketchEditor.on('drawn', (event) => {
 *   console.log("绘制的几何对象:", event.geometry)
 * })
 */
/**
 * @event SketchEditorNew#标绘制线或区的一个顶点完成事件
 * @property {Object} event 事件对象
 * @property {LayerEventType} [event.type = 'drawn-vertex'] 事件类型
 * @property {Geometry} [event.geometry] 绘制的几何对象
 * @example <caption><h7>鼠标绘制线或区的一个顶点完成事件</h7></caption>
 * sketchEditor.on('drawn-vertex', (event) => {
 *   console.log("绘制的几何对象:", event.geometry)
 * })
 */
/**
 * @event SketchEditorNew#草图被选中事件
 * @property {Object} event 事件对象
 * @property {LayerEventType} [event.type = 'selected'] 事件类型
 * @property {SketchEditorLeaflet|SketchEditorCesium} [event.selectedSketch] 被选中的草图对象
 * @example <caption><h7>草图被鼠标选中事件</h7></caption>
 * sketchEditor.on('selected', (event) => {
 *   console.log("被选中事件:", event.selectedSketch)
 * })
 */

class SketchEditorNew extends Evented {
  constructor(options) {
    super()
    options = defaultValue(options, {})
    if (!options.mapView) {
      Log.error('options.mapView is null!', options)
    }
    /**
     * 地图视图
     * @member {MapView|SecenView} SketchEditor.prototype._mapView
     */
    this._mapView = options.mapView
    this._spatialReference = options.mapView.crs
      ? new SpatialReference(options.mapView.crs.code)
      : new SpatialReference('EPSG:4326')
    /**
     * 草图图层
     * @member {GraphicsLayer} SketchEditor.prototype._layer
     */
    this._layer = defaultValue(options.layer, undefined)
    /**
     * 草图符号
     * @member {SketchStyle} SketchEditor.prototype._sketchStyle
     */
    this._sketchStyle = defaultValue(options.sketchStyle, new SketchStyle())
    /**
     * 草图是否可编辑
     * @member {SketchStyle} SketchEditor.prototype._editable
     */
    this._editable = defaultValue(options.editable, true)
    /**
     * 草图捕获配置项
     * @member {Object} SketchEditor.prototype.snappingOption
     */
    const snappingOption = {
      showSelectBox: false,
      showSelectVertex: false,
      canEditFeature: false,
      canEditVertex: false
    }
    this.snappingOption = Object.assign(snappingOption, options.snappingOption)
    /**
     * 草图量算配置项
     * @member {Number} SketchEditor.prototype.measureOption
     */
    this.measureOption = defaultValue(options.measureOption, undefined)
    /**
     * 当前选中草图绘制工具
     * @member {Number} SketchEditor.prototype._drawTool
     */
    this._drawTool = null
    /**
     * 草图回撤管理器
     * @member {Number} SketchEditor.prototype._redoUndoManager
     */
    this._redoUndoManager = undefined
    /**
     * 草图绘制类型
     * @member {Number} SketchEditor.prototype._sketchDataType
     */
    this._sketchDataType = null
    /**
     * 草图捕获工具
     * @member {Number} SketchEditor.prototype._sketchDataType
     */
    this._sketchSnappingTool = {}
    /**
     * 草图拓扑分析工具
     * @member {Number} SketchEditor.prototype._sketchTopologyTool
     */
    this._sketchTopologyTool = {}

    /**
     * 撤销回退管理器
     * @member {Strign} SketchEditor.prototype.undoRedoManager
     */
    this._undoRedoManager = new UndoRedoManager()
    this._editorState = {
      editMode: 0,
      undoRedoManager: this.undoRedoManager,
      _hitTestEvent: this._hitTestEvent
    }
    if (this._layer) {
      const layerExist = this._mapView._map.findLayerById(this._layer.id)
      if (!layerExist) {
        this._mapView._map.add(this._layer)
        // const self = this
        // this._layer.on('layerview-created', function (result) {
        //   self._layerView = result.layerView
        //   // self._hitTestEvent()
        // })
      }

      // 有图层存在,初始化捕捉和拓扑分析工具
      this._sketchSnappingTool = new SketchSnappingTool(this.snappingOption)
      this._sketchTopologyTool = new SketchTopologyTool()
    } else {
      // 没有图层存在,草图捕获置为空
      this._hitTestEvent = {}
    }
  }

  /**
   * 开始(鼠标)绘制草图<a id='start'></a>
   * 根据传入绘制草图类型,开始鼠标绘制。<br/>
   * 绘制点图形,鼠标单击即绘制。<br/>
   * 绘制线图形,鼠标单击绘制线的一个顶点;鼠标移动,延长线图形;鼠标双击,完成线图形绘制。<br/>
   * 绘制区图形,鼠标单击绘制区的一个顶点;鼠标移动,区图形随鼠标位置变动;鼠标双击,完成区图形绘制。<br/>
   * @param {SketchDataType} dataType 绘制的草图类型
   */
  start(data) {
    if (this._drawTool) {
      this._drawTool._stopHitTest()
    }
    let dataType = ''
    let isGeometry = false
    if (typeof data === 'number') {
      dataType = data
    } else if (data instanceof Point) {
      dataType = SketchDataType.POINT
      isGeometry = true
    } else if (data instanceof LineString) {
      dataType = SketchDataType.POLYLINE
      isGeometry = true
    } else if (data instanceof Polygon) {
      dataType = SketchDataType.POLYGON
      isGeometry = true
    } else if (data instanceof Circle) {
      dataType = SketchDataType.CIRCLE
      isGeometry = true
    } else if (data instanceof Extent) {
      dataType = SketchDataType.RECTANGLE
      isGeometry = true
    }
    switch (dataType) {
      case SketchDataType.POINT:
        // 新建草图点实例
        this._drawTool = new SketchPointDrawTool({
          mapView: this._mapView,
          sketchStyle: this._sketchStyle,
          layer: this._layer,
          _hitTestEvent: this._hitTestEvent,
          undoRedoManager: this._undoRedoManager,
          _sketchEditor: this
        })
        break
      case SketchDataType.POLYLINE:
        // 新建草图线实例
        this._drawTool = new SketchPolylineDrawTool({
          mapView: this._mapView,
          sketchStyle: this._sketchStyle,
          layer: this._layer,
          _hitTestEvent: this._hitTestEvent,
          undoRedoManager: this._undoRedoManager,
          _sketchEditor: this
        })
        break
      case SketchDataType.POLYGON:
        // 新建草图区实例
        this._drawTool = new SketchPolygonDrawTool({
          mapView: this._mapView,
          sketchStyle: this._sketchStyle,
          layer: this._layer,
          _hitTestEvent: this._hitTestEvent,
          undoRedoManager: this._undoRedoManager,
          _sketchEditor: this
        })
        break
      case SketchDataType.CIRCLE:
        // 新建草图区实例
        this._drawTool = new SketchCircleDrawTool({
          mapView: this._mapView,
          sketchStyle: this._sketchStyle,
          layer: this._layer,
          _hitTestEvent: this._hitTestEvent,
          undoRedoManager: this._undoRedoManager,
          _sketchEditor: this
        })
        break
      case SketchDataType.RECTANGLE:
        // 新建草图区实例
        this._drawTool = new SketchRectangleDrawTool({
          mapView: this._mapView,
          sketchStyle: this._sketchStyle,
          layer: this._layer,
          _hitTestEvent: this._hitTestEvent,
          undoRedoManager: this._undoRedoManager,
          _sketchEditor: this
        })
        break
      default:
        break
    }
    if (this._drawTool) {
      this._drawTool._sketchDataType = dataType
      if (isGeometry) {
        this._addGeometry(
          data,
          this._drawTool._hitTestEvent.bind(this._drawTool)
        )
        // 注册hitTest事件移动图形
        // this._drawTool._hitTestDrawToolStack.push(this._drawTool)
        // setTimeout(() => {
        //   this._drawTool._hitTestEvent()
        // }, 200)
        this._drawTool._hitTestEvent(false)
      } else {
        this._drawTool.start(this._sketchDataType)
      }
      // 响应草图被选中事件
      this._selectEvent()
      this._drawEvent()
    }
  }

  /**
   * 停止鼠标绘制草图<a id='stop'></a>
   */
  stop() {
    if (this._drawTool) {
      this._drawTool.stop()
      if (this._drawTool._editMode === 3) {
        this.remove()
      }
    }
  }

  /**
   * 移除当前草图<a id='remove'></a>
   */
  remove() {
    this._drawTool.removeDrawTool()
    this._drawTool = null
  }

  /**
   * 更新当前草图<a id='update'></a>
   * @private
   * @param {Object} data 更新的数据
   * @param {Number} index 新增/新增点的序号
   */
  update(data, featureId) {
    this._drawTool.updateFeature(data, featureId)
  }

  /**
   * 向当前线或区草图图形中插入新的顶点
   * @private
   * @param {Point} point 新增/插入顶点
   * @param {Number} index 新增/新增点的序号
   */
  _addGeometry(geometry) {
    this._drawTool.addFeatureByGeometry(geometry)
  }

  /**
   * 向当前线或区草图中插入新的顶点<a id='addVertex'></a>
   * @param {Point} point 新增/插入顶点
   * @param {Number} index 新增/新增点的序号
   * @returns {Geometry} 改变后的图形几何
   */
  addVertex(point, index) {
    const geometry = this._drawTool.addVertex(point, index)
    return geometry
  }

  /**
   * 更新当前选中或区草图图形的某个顶点<a id='updateVertex'></a>
   * @param {Point} point 新的顶点
   * @param {Number} index 需更新的顶点的序号
   * @returns {Geometry} 改变后的图形几何
   */
  updateVertex(point, index) {
    const geometry = this._drawTool.updateVertex(point, index)
    return geometry
  }

  /**
   * 移除草图图形的某个顶点<a id='removeVertex'></a>
   * @param {Number} index 需更新的顶点的序号
   * @returns {Geometry} 改变后的图形几何
   */
  removeVertex(index) {
    const geometry = this._drawTool.removeVertex(index)
    return geometry
  }

  /**
   * 获取草图图形类型<a id='getSketchDataType'></a>
   * @returns {SketchDataType}
   */
  getSketchDataType() {
    return this._sketchDataType
  }

  /**
   * 设置草图样式<a id='setSketchStyle'></a>
   * @param {SketchStyle} sketchStyle
   */
  setSketchStyle(sketchStyle) {
    this._sketchStyle = sketchStyle
  }

  /**
   * 获取草图样式<a id='getSketchStyle'></a>
   * @returns {SketchStyle}
   */
  getSketchStyle() {
    return this._sketchStyle
  }

  /**
   * 清空全部草图
   * @private
   */
  clearAllSketch() {
    this._mapView._map.remove(this._layer)
    this._drawTool = null
    // 临时测试用
    this._mapView._map.layers.forEach((layer) => {
      if (layer.graphics) {
        this._mapView._map.remove(layer)
      }
    })
  }

  /**
   * 合并多个区几何<a id='union'></a>
   * @param {Polygon} polygons 被合并的区几何对象
   * @returns {Polygon} 合并后的几何对象
   */
  union(polygons) {
    const unionPolygon = Projection.project(
      this._sketchTopologyTool.unionPolygons(polygons),
      this._spatialReference
    )
    // 创建合并后的草图图形
    const polygonDrawTool = new SketchPolygonDrawTool({
      mapView: this._mapView,
      layer: this._layer,
      snappingOption: {
        showSelectBox: true,
        _hitTestMode: 1
      },
      _hitTestEvent: this._hitTestEvent,
      _sketchEditor: this
    })
    this._drawTool = polygonDrawTool
    // 注册hitTest事件移动图形
    // this._drawTool._hitTestDrawToolStack.push(polygonDrawTool)
    // setTimeout(() => {
    //   this._drawTool._hitTestEvent()
    // }, 200)
    this._drawTool._hitTestEvent(false)
    polygonDrawTool.addFeatureByGeometry(unionPolygon)

    // 响应草图被选中事件
    this._selectEvent()
    this._drawEvent()
    const polygon = polygonDrawTool._sketchStage.entityGraphic
    return polygon
  }

  /**
   * 分割草图对象或区几何对象<a id='split'></a>
   * @param {Polygon|SketchEditorNew} target 被分割的几何/草图对象
   * @param {Polyline} splitPolyline 线几何对象
   * @returns {Array<SketchEditorNew>} 合并后的几何对象
   */
  split(target, splitPolyline) {
    let polygon = null
    const self = this
    if (target instanceof Polygon) {
      polygon = target
    } else if (
      target instanceof SketchEditorNew &&
      target.drawTool._sketchDataType === SketchDataType.POLYGON
    ) {
      polygon = target._sketchStage.entityGraphic
    } else {
      Log.error('传入对象不符合要求,请检查')
      return
    }
    const splitPolygon = function (polygon, splitPolyline) {
      const sketchEditors = []
      const splitPolygons = self._sketchTopologyTool.splitPolygonByPolyline(
        polygon,
        splitPolyline
      )
      if (splitPolygons.length > 0) {
        // 创建分割后的草图
        splitPolygons.forEach((polygon) => {
          // if (i != 0) return
          const sketchEditor = new SketchEditorNew({
            mapView: self._mapView,
            layer: self._layer
          })
          const polygonDrawTool = new SketchPolygonDrawTool({
            mapView: self._mapView,
            layer: self._layer,
            snappingOption: {
              showSelectBox: true,
              _hitTestMode: 1
            },
            _hitTestEvent: self._hitTestEvent,
            _sketchEditor: self
          })
          sketchEditor._drawTool = polygonDrawTool
          polygonDrawTool.addFeatureByGeometry(polygon)
          // 注册hitTest事件移动图形
          polygonDrawTool._hitTestDrawToolStack.push(polygonDrawTool)
          sketchEditors.push(sketchEditor)
        })
      }
      return sketchEditors
    }
    return splitPolygon(polygon, splitPolyline)
  }

  /**
   * 撤销当前编辑操作<a id='undo'></a>
   * @returns {Geometry} 撤销后的几何对象
   */
  undo() {
    const stage = this._undoRedoManager.undo()
    if (stage) {
      this._updateByStage(stage)
      return stage.entityGraphic.geometry
    } else {
      return null
    }
  }

  /**
   * 恢复被撤销的草图<a id='redo'></a>
   * @returns {Geometry} 恢复后的几何对象
   */
  redo() {
    const stage = this._undoRedoManager.redo()
    if (stage) {
      this._updateByStage(stage)
      return stage.entityGraphic.geometry
    } else {
      return null
    }
  }

  /**
   * 草图是否可执行撤销操作<a id='canUndo'></a>
   * @returns {Boolean}
   */
  canUndo() {
    return this._undoRedoManager.canUndo()
  }

  /**
   * 草图是否可执行恢复操作<a id='canRedo'></a>
   * @returns {Boolean}
   */
  canRedo() {
    return this._undoRedoManager.canRedo()
  }

  /**
   * 根据sketchStage状态更新草图
   * @private
   * @param {SketchStage} stage 被合并的区几何对象
   */
  _updateByStage(stage) {
    this._drawTool._sketchStage.getAllFeatures().forEach((feature) => {
      this._drawTool._removeFeatureFromMap(feature)
    })
    this._drawTool._setSketchStage(stage)
    // this._mapView.sketchFeatures = [] // 临时
    const drawTools = []
    stage.getAllFeatures().forEach((feature) => {
      const drawTool = this._updateDrawToolByFeature(feature)
      if (drawTool) {
        drawTools.push(drawTool)
        drawTool._addFeaturesToMap(feature)
        this._drawTool._hitTestDrawToolStack.push(drawTool)
      }
    })
  }

  /**
   * 根据sketchStage状态更新草图
   * @private
   * @param {SketchStage} stage 被合并的区几何对象
   */
  getDrawToolByFeatureId(id) {
    let drawTool = null
    for (let i = 0; i < this._drawTool._drawTools.length; i++) {
      if (this._drawTool._drawTools[i]._sketchStage.entityGraphic.id === id) {
        drawTool = this._drawTool._drawTools[i]
        break
      }
    }
    return drawTool
  }

  /**
   * 根据feature图形更新所属草图绘制工具drawTool
   * @private
   * @param {Feature} feature
   */
  _updateDrawToolByFeature(feature) {
    let drawTool = null
    const drawTools = this._drawTool._drawTools
    for (let i = 0; i < drawTools.length; i++) {
      if (drawTools[i]._sketchStage.entityGraphic.id === feature.id) {
        drawTools[i]._sketchStage.entityGraphic = feature
        drawTool = drawTools[i]
        break
      }
    }
    return drawTool
  }

  /**
   * 响应草图被选中事件
   * @private
   */
  _selectEvent() {
    this._drawTool.on('selected', (event) => {
      const selectedSketch = event.isSelected ? this : null
      this.fire(
        'selected',
        { selectedSketch, isSelected: event.isSelected },
        this
      )
    })
  }

  /**
   * 响应草图绘制完毕事件
   * @private
   */
  _drawEvent() {
    this._drawTool.on('drawn', (event) => {
      this.fire('drawn', { geometry: event.geometry }, this)
    })
    this._drawTool.on('drawn-vertex', (event) => {
      this.fire('drawn-vertex', { geometry: event.geometry }, this)
    })
  }

  /**
   * 获取草图几何对象<a id='getGeometry'></a>
   * @returns {Geometry}
   */
  getGeometry() {
    return this._sketchStage.entityGraphic.geometry
  }
}
Zondy.SketchEditorNew = SketchEditorNew
export default SketchEditorNew
构造函数
成员变量
方法
事件