判断⼀个坐标点是否在不规则多边形内部的算法
在GIS(地理信息管理系统)中,判断⼀个坐标是否在多边形内部是个经常要遇到的问题。乍听起来还挺复杂。根据W. Randolph Franklin 提出的PNPoly算法,只需区区⼏⾏代码就解决了这个问题。
假设多边形的坐标存放在⼀个数组⾥,⾸先我们需要取得该数组在横坐标和纵坐标的最⼤值和最⼩值,根据这四个点算出⼀个四边型,⾸先判断⽬标坐标点是否在这个四边型之内,如果在这个四边型之外,那可以跳过后⾯较为复杂的计算,直接返回fal。
if (p.x < minX || p.x > maxX || p.y < minY || p.y > maxY) {
// 这个测试都过不了。。。直接返回fal;
}椰蓉馅的做法
燥咳接下来是核⼼算法部分:
int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {边潇潇个人资料及简介
if ( ((verty[i]>testy) != (verty[j]>testy)) &&
(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
c = !c;
}
return c;
}
Argument Meaning
nvert Number of vertices in the polygon. Whether to repeat the first vertex at the end is discusd below.
vertx, verty Arrays containing the x- and y-coordinates of the polygon's vertices.
testx, testy X- and y-coordinate of the test point.
额,代码就这么简单,但到底啥意思呢:
⾸先,参数nvert 代表多边形有⼏个点。浮点数testx, testy代表待测试点的横坐标和纵坐标,*vertx,*verty分别指向储存多边形横纵坐标数组的⾸地址。
我们注意到,每次计算都涉及到相邻的两个点和待测试点,然后考虑两个问题:
1. 被测试点的纵坐标testy是否在本次循环所测试的两个相邻点纵坐标范围之内?即
verty[i] <testy < verty[j]
或者
verty[j] <testy < verty[i]
2. 待测点test是否在i,j两点之间的连线之下?看不懂后半短if statement的朋友请⾃⾏在纸上写下i,j两点间的斜率公式,要⽤到⼀点初中解析⼏何和不等式的知识范畴,对⼴⼤码农来说⼩菜⼀碟。
然后每次这两个条件同时满⾜的时候我们把返回的布尔量取反。
可这到底是啥意思啊?
这个表达式的意思是说,随便画个多边形,随便定⼀个点,然后通过这个点⽔平划⼀条线,先数数看这条横线和多边形的边相交⼏次,(或者说先排除那些不相交的边,第⼀个判断条件),然后再数这条横线穿越多边形的次数是否为奇数,如果是奇数,那么该点在多边形内,如果是偶数,则在多边形外。详细的数学证明这⾥就不做了,不过读者可以⾃⾏画多边形进⾏验证。
判断⼀个点是否在多边形内部 - 射线法思路
⽐如说,我就随便涂了⼀个多边形和⼀个点,现在我要给出⼀种通⽤的⽅法来判断这个点是不是在多边形内部(别告诉我⽤⾁眼观察……)。
⾸先想到的⼀个解法是从这个点做⼀条射线,计算它跟多边形边界的交点个数,如果交点个数为奇数,那么点在多边形内部,否则点在多边形外。
这个结论很简单,那它是怎么来的?下⾯就简单讲解⼀下。
⾸先,对于平⾯内任意闭合曲线,我们都可以直观地认为,曲线把平⾯分割成了内、外两部分,其中“内”就是我们所谓的多边形区域。
基于这⼀认识,对于平⾯内任意⼀条直线,我们可以得出下⾯这些结论:
直线穿越多边形边界时,有且只有两种情况:进⼊多边形或穿出多边形。
在不考虑⾮欧空间的情况下,直线不可能从内部再次进⼊多边形,或从外部再次穿出多边形,即连续两次穿越边界的情况必然成对。
直线可以⽆限延伸,⽽闭合曲线包围的区域是有限的,因此最后⼀次穿越多边形边界,⼀定是穿出多边形,到达外部。
现在回到我们最初的题⽬。假如我们从⼀个给定的点做射线,还可以得出下⾯两条结论:
如果点在多边形内部,射线第⼀次穿越边界⼀定是穿出多边形。
工作正能量句子励志短句子如果点在多边形外部,射线第⼀次穿越边界⼀定是进⼊多边形。
把上⾯这些结论综合起来,我们可以归纳出:
当射线穿越多边形边界的次数为偶数时,所有第偶数次(包括最后⼀次)穿越都是穿出,因此所有第奇数次(包括第⼀次)穿越为穿⼊,由此可推断点在多边形外部。
当射线穿越多边形边界的次数为奇数时,所有第奇数次(包括第⼀次和最后⼀次)穿越都是穿出,由此可推断点在多边形内部。
到这⾥,我们已经了解了这个解法的思路,⼤家可以试着⾃⼰写⼀个实现出来。
不知道⼤家思考得怎么样,有没有遇到⼀些不好处理的特殊情况。今天就来讲讲射线法在实际应⽤中的⼀些问题和解决⽅案。
1点在多边形的边上
前⾯我们讲到,射线法的主要思路就是计算射线穿越多边形边界的次数。那么对于点在多边形的边上这种特殊情况,射线出发的这⼀次,是否应该算作穿越呢?
看了上⾯的图就会发现,不管算不算穿越,都会陷⼊两难的境地——同样落在多边形边上的点,可能会得到相反的结果。这显然是不正确的,因此对这种特殊情况需要特殊处理。
2点和多边形的顶点重合
这其实是第⼀种情况的⼀个特例。
3射线经过多边形顶点
射线刚好经过多边形顶点的时候,应该算⼀次还是两次穿越?这种情况⽐前两种复杂,也是实现中的难点,后⾯会讲解它的解决⽅案。
关于猫的作文
4射线刚好经过多边形的⼀条边
这是上⼀种情况的特例,也就是说,射线连续经过了多边形的两个相邻顶点。
解决⽅案:
1判断点是否在线上的⽅法有很多,⽐较简单直接的就是计算点与两个多边形顶点的连线斜率是否相等,中学数学都学过。
2点和多边形顶点重合的情况更简单,直接⽐较点的坐标就⾏了。
3顶点穿越看似棘⼿,其实我们换⼀个⾓度,思路会⼤不相同。先来回答⼀个问题,射线穿越⼀条线段需要什么前提条件?没错,就是线段两个端点分别在射线两侧。只要想通这⼀点,顶点穿越就迎刃⽽解了。这样⼀来,我们只需要规定被射线穿越的点都算作其中⼀侧。
杜鹃花寓意
南洋女子中学如上图,假如我们规定射线经过的点都属于射线以上的⼀侧,显然点D和发⽣顶点穿越的点C都位于射线Y的同⼀侧,所以射线Y其实并没有穿越CD这条边。⽽点C和点B则分别位于射线Y的两侧,所以射线Y和BC发⽣了穿越,由此我们可以断定点Y在多边形内。同理,射线X分别与AD和CD都发⽣了穿越,因此点X在多边形外,⽽射线Z没有和多边形发⽣穿越,点Z位于多边形外。
解决了第三点,这⼀点就毫⽆难度了。根据上⾯的假设,射线连续经过的两个顶点显然都位于射线以上的⼀侧,因此这种情况看作没有发⽣穿越就可以了。由于第三点的解决⽅案实际上已经覆盖到这种
特例,因此不需要再做特别的处理。
学校精细化管理