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