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