如何判断一个点在地图上?如何判断一个点在多边形内?

如何,判断,一个点,地图,多边形 · 浏览次数 : 50

小编点评

**射线法** 射线法是一种简单的算法,用于判断一个点是否在一个多边形内。该方法基于从被测点引出一条射线,并判断这条射线与多边形哪些边交集。 **非零环绕数规则** 非零环绕数规则是一种更加复杂的技术,用于判断一个点是否在一个多边形内。该方法基于从被测点引出一条射线,并判断这条射线与多边形哪些边交集,以及射线与多边形每条边的环绕次数。 **公式** 射线法和非零环绕数规则的基本公式如下: * **射线法:** \(\frac{y-y2}{y1-y2} = \frac{x-x2}{x1-x2}\) * **非零环绕数规则:** W(\(p_0,p_1,p_2\)) = W(\(p_0,p_3,p_4\)) = W(\(p_0,p_5,p_6\))

正文

highlight: a11y-dark

近期,有接手到一个echarts地图图表项目,因为采集的散点数据很多打不到准确的地图点上,故有了这个问题。

一般而言,标题的两个问题其是同一个问题,因为对与一个地图数据,也就是geoJson来说,其实就是一个有很多个点的多边形。

目前来说判断点是否在一个多边形内,江湖上流传的主要方法有射线法,面积法,叉积(凸多边形)等等很多方法。但就笔者看各种技术文章,以及多方探(bai)索(du)研究(google)的情况下来看,射线法是比较常用的一种,基于射线法又派生出奇偶规则(Odd-even Rule)非零环绕数规则(Nonzero Winding Number Rule)

全文可能就只会用到一个数学公式直线方程的两点式:

\(\frac{y-y2}{y1-y2} = \frac{x-x2}{x1-x2}\)

射线法

基本思想: 所谓射线法,就是指,从被测点引出一条射线,而后判断与多边形的交点。与边的交点的个数决定了当前点是否在多边形内,这是奇偶规则与非零环绕数规则的共同点,两者的不同点在于是否考虑被交边的方向以及对点的交点个数的判断。

奇偶规则(Odd-even Rule)

所谓奇偶规则是指,若交点数为奇数则点在多边形内,否则点在多边形外。

图示:

image

代码示例

/**
 * 
 * @param {*} param0 [number,number]
 * @param {*} points [number,number][]
 * @returns 
 */
function oddEvenRule([x,y], points) {

  let ans = false;
  if (points.length < 3) {
    return ans;
  }

  for (let i = 0, L = points.length - 1; i < L; i++ ) {
    const point1 = points[i];
    const point2 = points[i+1]

    const [x1, y1] = point1;
    const [x2, y2] = point2;

    if (y < Math.min(y1, y2) || y > Math.max(y1, y2)) { // 限定 y 在  y1 及y2之间
      continue;
    }
    // 在 point1及point2确定的直线上,根据待测点的y,求出交点坐标x
    // 直线方程。两点式(y-y2)/(y1-y2) = (x-x2)/(x1-x2)
    let crossoverX = (y - y2) * (x1 - x2) /(y1 - y2) + x2;
    if (y1 === y2) { // 水平边的交点即为待测点的坐标 暂时先不管了 感兴趣可以自己处理一下
      // crossoverX = x;
      continue;
    }
    if (crossoverX > x) { // 只考虑一个方向
      ans = !ans; 
    }

  }

  return ans;
    
}

判断一般的多边形,奇偶规则判断就够了,但是奇偶规则有个限制,就是一个多边形有多部分构成的时候,考虑如下场景:
image

此时再使用奇偶规则去判断就不能准确判断了,由此引出下面一个方法:

非零环绕数规则(Nonzero Winding Number Rule)

所谓非零环绕,是指,基于射线法,当射线穿过多边形边的时候,根据多边形边的方向,给总的交点数加1或者减1,最后判断总的节点数是不是0,不是0则为多边形内部,否则就是多边形外部。

图示:

image

可以看到非零环绕规则很好的解决了上边的问题

代码示例

这里援引一下zRender的contain源码

var EPSILON = 1e-8;
function isAroundEqual(a, b) {
    return Math.abs(a - b) < EPSILON;
}
function contain(points, x, y) {
    var w = 0;
    var p = points[0];
    if (!p) {
        return false;
    }
    for (var i = 1; i < points.length; i++) {
        var p2 = points[i];
        w += windingLine(p[0], p[1], p2[0], p2[1], x, y);
        p = p2;
    }
    // Close polygon
    var p0 = points[0];
    if (!isAroundEqual(p[0], p0[0]) || !isAroundEqual(p[1], p0[1])) {
        w += windingLine(p[0], p[1], p0[0], p0[1], x, y);
    }
    return w !== 0;
}
function windingLine(x0, y0, x1, y1, x, y) {
    if ((y > y0 && y > y1) || (y < y0 && y < y1)) {
        return 0;
    }
    // Ignore horizontal line
    if (y1 === y0) {
        return 0;
    }
    var t = (y - y0) / (y1 - y0);
    var dir = y1 < y0 ? 1 : -1;
    // Avoid winding error when intersection point is the connect point of two line of polygon
    if (t === 1 || t === 0) {
        dir = y1 < y0 ? 0.5 : -0.5;
    }
    var x_ = t * (x1 - x0) + x0;
    // If (x, y) on the line, considered as "contain".
    return x_ === x ? Infinity : x_ > x ? dir : 0;
}

与如何判断一个点在地图上?如何判断一个点在多边形内?相似的内容:

如何判断一个点在地图上?如何判断一个点在多边形内?

highlight: a11y-dark 近期,有接手到一个echarts地图图表项目,因为采集的散点数据很多打不到准确的地图点上,故有了这个问题。 一般而言,标题的两个问题其是同一个问题,因为对与一个地图数据,也就是geoJson来说,其实就是一个有很多个点的多边形。 目前来说判断点是否在一个多边

驱动开发:内核扫描SSDT挂钩状态

在笔者上一篇文章`《驱动开发:内核实现SSDT挂钩与摘钩》`中介绍了如何对`SSDT`函数进行`Hook`挂钩与摘钩的,本章将继续实现一个新功能,如何`检测SSDT`函数是否挂钩,要实现检测`挂钩状态`有两种方式,第一种方式则是类似于`《驱动开发:摘除InlineHook内核钩子》`文章中所演示的通过读取函数的前16个字节与`原始字节`做对比来判断挂钩状态,另一种方式则是通过对比函数的`当前地址`

基于SqlSugar的开发框架循序渐进介绍(17)-- 基于CSRedis实现缓存的处理

在一个应用系统的开发框架中,往往很多地方需要用到缓存的处理,有些地方是为了便于记录用户的数据,有些地方是为了提高系统的响应速度,如有时候我们在发送一个短信验证码的时候,可以在缓存中设置几分钟的过期时间,这样验证短信验证码的时候,就会自动判断是否过期了。本篇随笔结合CSRedis的使用,介绍如何实现缓存的初始化及使用的处理。

Spring源码:Bean的生命周期(二)

FactoryBean 和 BeanFactory 是两个不同的概念。前者是一个接口,我们可以在实现该接口时通过调用 getObject 方法来返回实例,同时 FactoryBean 本身也是一个实例。后者是 Spring 容器的工厂,通过其中的 bean 定义 Map 一个一个地实例化我们通过注解等方式注入进去的 bean 工厂。在判断 FactoryBean 时,如果当前 BeanFactor

umich cv-2-2

UMICH CV Linear Classifiers 在上一篇博文中,我们讨论了利用损失函数来判断一个权重矩阵的好坏,在这节中我们将讨论如何去找到最优的权重矩阵 想象我们要下到一个峡谷的底部,我们自然会选择下降最快的斜坡,换成我们这个问题就是要求权重矩阵相对于损失函数的梯度函数,最简单的方法就是使

如何让技术架构师具有预知未来业务发展的能力?

大家好,今天我们来分享业务架构,但是我们并不是以产品经理角度讲述一个业务架构是什么以及如何做?而是以一个技术架构师的角度,讲述如何承接业务架构或在没有业务架构的时候,如何判断业务变化趋势而对系统架构提前做出反应。

提取关键词作为标题---Java调用Python实现

[TOC] # 前景提示 * 一个朋友参加面试,在成都面的一家,问我如何给一篇没有标题的文章取个标题,是根据内容分析内容,然后获取标题,写个程序让程序分析内容,提炼出一个最适合的标题. * 提示:先找出高频率的关键词,然后再根据段首段尾段中的不同权重结合同一个关键词出现的频率来综合判断,最后取一个权

面试官:Java类是如何被加载到内存中的?

面试连环call Java类是如何被加载到内存中的? Java类的生命周期都有哪些阶段? JVM加载的class文件都有哪些来源? JVM在加载class文件时,何时判断class文件的格式是否符合要求? 类生命周期 一个类从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期将会经历加载、验

Jquery 将 JSON 列表的 某个属性值,添加到数组中,并判断一个值,在不在数据中

jquery 将 JSON 列表的 某个属性值,添加到数组中 如果你有一个JSON列表,并且想要将每个对象的某个属性值添加到数组中,你可以使用jQuery的$.each()函数来遍历JSON列表,并获取所需的属性值。以下是一个示例代码: var jsonList = [ { "name": "Joh

3.4 DLL注入:全局消息钩子注入

SetWindowHookEx 是`Windows`系统的一个函数,可用于让一个应用程序安装全局钩子,但读者需要格外注意该方法安装的钩子会由操作系统注入到所有可执行进程内,虽然该注入方式可以用于绕过游戏保护实现注入,但由于其属于全局注入所以所有的进程都会受到影响,而如果想要解决这个问题,则需要在`DllMain()`也就是动态链接库开头位置进行判断,如果是我们所需操作的进程则执行该DLL模块内的功