杂记[前端] 如何判断一个点是否在一个多边形范围内?

268 阅读2分钟

在开发航空项目时我们有个功能需求是这样设计的,获取雷达的格点数据,并渲染到mapbox中,但是我们需要根据所在位置的默认区间去判断每个区间格点数据大于某一阈值的点位数量比,这里涉及到每个点与多边形的位置判断

理论知识推荐博客:o_0123的文章

实现思路如下

  1. 判断点是否在多边形范围内。
  2. 判断点的值是否大于阈值。

上面两个步骤谁先谁后区别不大,问题是如何判断一个点是否在多边形矩阵中

经过调研,我们找到了市面上使用的比较多的npm库@turf/turf,该库的周下载量为289721算是使用量比较大的了

该库提供了inside方法,参数分别接收点和一个二维面数组,并返回boolean表示是否在平面内

深入该方法,我们发现该方法的实现是使用了一个很小的专门处理这种问题的实现库point-in-polygon-hao

该仓库的源代码74行,属于是比较小的了,经过分析,该库使用的是一种叫做射线投射法的算法实现点在多边形内判断的,该方法是通过对点在一个横轴贯穿的多边形交点进行奇偶判断,如果是奇数则表示点在面内,否则点在面外,代码实现如下

export default function pointInPolygon(p, polygon) {
    let i = 0
    let ii = 0
    let k = 0
    let f = 0
    let u1 = 0
    let v1 = 0
    let u2 = 0
    let v2 = 0
    let currentP = null
    let nextP = null

    const x = p[0]
    const y = p[1]

    const numContours = polygon.length
    for (i; i < numContours; i++) {
        ii = 0
        const contourLen = polygon[i].length - 1
        const contour = polygon[i]

        currentP = contour[0]
        // 闭合多边形检查
        if (currentP[0] !== contour[contourLen][0] &&
            currentP[1] !== contour[contourLen][1]) {
            throw new Error('First and last coordinates in a ring must be the same')
        }

        u1 = currentP[0] - x
        v1 = currentP[1] - y

        for (ii; ii < contourLen; ii++) {
            nextP = contour[ii + 1]

            v2 = nextP[1] - y

            if ((v1 < 0 && v2 < 0) || (v1 > 0 && v2 > 0)) {
                currentP = nextP
                v1 = v2
                u1 = currentP[0] - x
                continue
            }

            u2 = nextP[0] - p[0]

            if (v2 > 0 && v1 <= 0) {
                f = (u1 * v2) - (u2 * v1)
                if (f > 0) k = k + 1
                else if (f === 0) return 0
            } else if (v1 > 0 && v2 <= 0) {
                f = (u1 * v2) - (u2 * v1)
                if (f < 0) k = k + 1
                else if (f === 0) return 0
            } else if (v2 === 0 && v1 < 0) {
                f = (u1 * v2) - (u2 * v1)
                if (f === 0) return 0
            } else if (v1 === 0 && v2 < 0) {
                f = u1 * v2 - u2 * v1
                if (f === 0) return 0
            } else if (v1 === 0 && v2 === 0) {
                if (u2 <= 0 && u1 >= 0) {
                    return 0
                } else if (u1 <= 0 && u2 >= 0) {
                    return 0
                }
            }
            currentP = nextP
            v1 = v2
            u1 = u2
        }
    }

    if (k % 2 === 0) return false
    return true
}

这段代码的核心就在 最后的if else分支语句中,如果发现交点,则自增器k就自增1

if (v2 > 0 && v1 <= 0) {
  // 这两个就是判断亮点是否存在交集
  f = (u1 * v2) - (u2 * v1)
  if (f > 0) k = k + 1
  else if (f === 0) return 0

} else if (v1 > 0 && v2 <= 0) {

  f = (u1 * v2) - (u2 * v1)
  if (f < 0) k = k + 1
  else if (f === 0) return 0

} else if (v2 === 0 && v1 < 0) {

  f = (u1 * v2) - (u2 * v1)
  if (f === 0) return 0

} else if (v1 === 0 && v2 < 0) {
  f = (u1 * v2) - (u2 * v1)
  if (f === 0) return 0

} else if (v1 === 0 && v2 === 0) {

  if (u2 <= 0 && u1 >= 0) {
      return 0
  } else if (u1 <= 0 && u2 >= 0) {
      return 0
  }
}