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