问题:使用leaflet库中自带的contains方法来判断点是否在绘制范围内时,不够准确,会导致判断错误
一、使用leaflet库中自带的contains方法
this.map.on('pm:create', (e) => {
console.log(e, '绘制完成时调用')
// 获取被选中的设备
const bounds = e.layer.getBounds()
const fillMarket = this.deviceListData.filter((item) => bounds.contains(item.latlng))
console.log('图形包含选中设备:', fillMarket)
if (fillMarket.length) this.handleSelected(fillMarket)
// 循环markersLayer图层上所有marker
this.markersLayer.eachLayer((marker) => {
// 判断marker在绘制范围内
if (bounds.contains(marker.getLatLng())) {
const { alt } = marker.options
marker.setIcon(
this.$leaflet.icon({
iconUrl: this.transIcons({ devType: Number(alt.split('-')[1]), status: 99 }),
iconSize: [40, 40], // 图标大小,单位(px)
popupAnchor: [-20, 0], // popup相对于锚点中心的坐标
tooltipAnchor: [0, -20] // tooltip相对于锚点中心的坐标
})
)
}
})
this.cancelDraw()
})
二、自定义判断方法
leaflet-pm-create.js,其中有判断点是否在矩形内、判断点是否多边形内、判断点是否在圆的内部
export default {
methods: {
/**
* 判断点是否在矩形内
* @param {Point} point 点对象
* @param {Bounds} bounds 矩形边界对象
* @returns {Boolean} 点在矩形内返回true,否则返回false
*/
isPointInRect(point, bounds) {
const sw = bounds.getSouthWest() // 西南脚点
const ne = bounds.getNorthEast() // 东北脚点
return (
point.lng >= sw.lng && point.lng <= ne.lng && point.lat >= sw.lat && point.lat <= ne.lat
)
},
/**
* 判断点是否多边形内
* @param {Point} point 点对象
* @param {Polyline} polygon 多边形对象
* @returns {Boolean} 点在多边形内返回true,否则返回false
*/
isPointInPolygon(point, polygon) {
// 首先判断点是否在多边形的外包矩形内,如果在,则进一步判断,否则返回false
const polygonBounds = polygon.getBounds()
if (!this.isPointInRect(point, polygonBounds)) {
return false
}
const pts = polygon.getLatLngs()[0] // 获取多边形点
// 下述代码来源:http://paulbourke.net/geometry/insidepoly/,进行了部分修改
// 基本思想是利用射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则
// 在多边形内。还会考虑一些特殊情况,如点在多边形顶点上,点在多边形边上等特殊情况。
const N = pts.length
const boundOrVertex = true // 如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
let intersectCount = 0 // cross points count of x
const precision = 2e-10 // 浮点类型计算时候与0比较时候的容差
let p1 = pts[0] // left vertex
let p2 = null // neighbour bound vertices
const p = point // 测试点
for (let i = 1; i <= N; i += 1) {
// check all rays
if (p.equals(p1)) {
return boundOrVertex // p is an vertex
}
p2 = pts[i % N] // right vertex
if (p.lat < Math.min(p1.lat, p2.lat) || p.lat > Math.max(p1.lat, p2.lat)) {
// ray is outside of our interests
p1 = p2
}
if (p.lat > Math.min(p1.lat, p2.lat) && p.lat < Math.max(p1.lat, p2.lat)) {
// ray is crossing over by the algorithm (common part of)
if (p.lng <= Math.max(p1.lng, p2.lng)) {
// x is before of ray
if (p1.lat === p2.lat && p.lng >= Math.min(p1.lng, p2.lng)) {
// overlies on a horizontal ray
return boundOrVertex
}
if (p1.lng === p2.lng) {
// ray is vertical
if (p1.lng === p.lng) {
// overlies on a vertical ray
return boundOrVertex
}
intersectCount += 1
} else {
// cross point on the left side
const xinters = ((p.lat - p1.lat) * (p2.lng - p1.lng)) / (p2.lat - p1.lat) + p1.lng // cross point of lng
if (Math.abs(p.lng - xinters) < precision) {
// overlies on a ray
return boundOrVertex
}
if (p.lng < xinters) {
// before ray
intersectCount += 1
}
}
}
} else if (p.lat === p2.lat && p.lng <= p2.lng) {
// special case when ray is crossing through the vertex
// {
// p crossing over p2
const p3 = pts[(i + 1) % N] // next vertex
if (p.lat >= Math.min(p1.lat, p3.lat) && p.lat <= Math.max(p1.lat, p3.lat)) {
// p.lat lies between p1.lat & p3.lat
intersectCount += 1
} else {
intersectCount += 2
}
// }
}
p1 = p2 // next ray left point
}
if (intersectCount % 2 === 0) {
console.log('false', intersectCount % 2)
// 偶数在多边形外
return false
}
// 奇数在多边形内
console.log('true=====', intersectCount % 2)
return true
},
/**
判断一个点是否在圆的内部
@param point 测试点坐标
@param circle 圆心坐标
@param r 圆半径
返回true为真,false为假
*/
pointInsideCircle(point, circle, r) {
if (r === 0) return false
const dx = circle[0] - point[0]
const dy = circle[1] - point[1]
return dx * dx + dy * dy <= r * r
},
IsPtInPoly(ALon, ALat, APoints) {
let iSum = 0
let iCount = 0
let dLon1 = null
let dLon2 = null
let dLat1 = null
let dLat2 = null
let dLon = null
if (APoints.length < 3) return false
iCount = APoints.length
for (let i = 0; i < iCount; i += 1) {
if (i === iCount - 1) {
dLon1 = APoints[i].lng
dLat1 = APoints[i].lat
dLon2 = APoints[0].lng
dLat2 = APoints[0].lat
} else {
dLon1 = APoints[i].lng
dLat1 = APoints[i].lat
dLon2 = APoints[i + 1].lng
dLat2 = APoints[i + 1].lat
}
// 以下语句判断A点是否在边的两端点的水平平行线之间,在则可能有交点,开始判断交点是否在左射线上
if ((ALat >= dLat1 && ALat < dLat2) || (ALat >= dLat2 && ALat < dLat1)) {
if (Math.abs(dLat1 - dLat2) > 0) {
// 得到 A点向左射线与边的交点的x坐标:
dLon = dLon1 - ((dLon1 - dLon2) * (dLat1 - ALat)) / (dLat1 - dLat2)
if (dLon < ALon) iSum += 1
}
}
}
if (iSum % 2 !== 0) return true
return false
}
}
}
应用:
this.map.on('pm:create', (e) => {
console.log(e, '绘制完成时调用')
// 获取被选中的设备
const polygon = e.layer
const bounds = polygon.getBounds()
let fillMarket = this.deviceListData.filter((item) => bounds.contains(item.latlng))
console.log('图形包含选中设备:', fillMarket)
const isPolygon = polygon instanceof this.$leaflet.Polygon
if (fillMarket.length) {
console.log('isPolygon===', isPolygon)
if (isPolygon) {
fillMarket = []
this.deviceListData.forEach((item) => {
if (this.isPointInPolygon(this.$leaflet.latLng(item.latlng), polygon)) {
fillMarket.push(item)
}
})
console.log('fillMarket====', fillMarket)
}
if (fillMarket?.length > 0) this.handleSelected(fillMarket)
}
// 循环markersLayer图层上所有marker
this.markersLayer.eachLayer((marker) => {
// 判断marker在绘制范围内
if (bounds.contains(marker.getLatLng())) {
if (isPolygon && !this.isPointInPolygon(marker.getLatLng(), polygon)) return
const { alt } = marker.options
marker.setIcon(
this.$leaflet.icon({
iconUrl: this.transIcons({ devType: Number(alt.split('-')[1]), status: 99 }),
iconSize: [40, 40], // 图标大小,单位(px)
popupAnchor: [-20, 0], // popup相对于锚点中心的坐标
tooltipAnchor: [0, -20] // tooltip相对于锚点中心的坐标
})
)
}
})
this.cancelDraw()
})