GIS点在多边形内算法.docx
- 文档编号:5383045
- 上传时间:2022-12-15
- 格式:DOCX
- 页数:21
- 大小:48.83KB
GIS点在多边形内算法.docx
《GIS点在多边形内算法.docx》由会员分享,可在线阅读,更多相关《GIS点在多边形内算法.docx(21页珍藏版)》请在冰豆网上搜索。
GIS点在多边形内算法
1.判断点在多边形内外的简单算法--改进弧长法
今天学图形学的时候发现了一个求多边形内外的超简单算法,当时觉得非常惊喜,后来晚上上完选修的时候在走廊遇到bug,bug也是很惊喜地感慨道:
居然有甘简单既办法都捻唔到!
遂将其写下,供大家分享,希望不会太火星。
这个算法是源自《计算机图形学基础教程》(孙家广,清华大学出版社),在该书的48-49页,名字可称为“改进的弧长法”。
该算法只需O
(1)的附加空间,时间复杂度为O(n),但系数很小;最大的优点是具有很高的精度,只需做乘法和减法,若针对整数坐标则完全没有精度问题。
而且实现起来也非常简单,比转角法和射线法都要好写且不易出错。
首先从该收中摘抄一段弧长法的介绍:
“弧长法要求多边形是有向多边形,一般规定沿多边形的正向,边的左侧为多边形的内侧域。
以被测点为圆心作单位圆,将全部有向边向单位圆作径向投影,并计算其中单位圆上弧长的代数和。
若代数和为0,则点在多边形外部;若代数和为2π则点在多边形内部;若代数和为π,则点在多边形上。
”
按书上的这个介绍,其实弧长法就是转角法。
但它的改进方法比较厉害:
将坐标原点平移到被测点P,这个新坐标系将平面划分为4个象限,对每个多边形顶点P,只考虑其所在的象限,然后按邻接顺序访问多边形的各个顶点P,分析P和P[i+1],有下列三种情况:
(1)P[i+1]在P的下一象限。
此时弧长和加π/2;
(2)P[i+1]在P的上一象限。
此时弧长和减π/2;
(3)P[i+1]在Pi的相对象限。
首先计算f=y[i+1]*x-x[i+1]*y(叉积),若f=0,则点在多边形上;若f<0,弧长和减π;若f>0,弧长和加π。
最后对算出的代数和和上述的情况一样判断即可。
实现的时候还有两点要注意,第一个是若P的某个坐标为0时,一律当正号处理;第二点是若被测点和多边形的顶点重合时要特殊处理。
以上就是书上讲解的内容,其实还存在一个问题。
那就是当多边形的某条边在坐标轴上而且两个顶点分别在原点的两侧时会出错。
如边(3,0)-(-3,0),按以上的处理,象限分别是第一和第二,这样会使代数和加π/2,有可能导致最后结果是被测点在多边形外。
而实际上被测点是在多边形上(该边穿过该点)。
对于这点,我的处理办法是:
每次算P和P[i+1]时,就计算叉积和点积,判断该点是否在该边上,是则判断结束,否则继续上述过程。
这样牺牲了时间,但保证了正确性。
具体实现的时候,由于只需知道当前点和上一点的象限位置,所以附加空间只需O
(1)。
实现的时候可以把上述的“π/2”改成1,“π”改成2,这样便可以完全使用整数进行计算。
不必考虑顶点的顺序,逆时针和顺时针都可以处理,只是最后的代数和符号不同而已。
整个算法编写起来非常容易。
针对以上算法,我写了一个代码,拿ZOJ1081PointsWithin进行测试,顺利Accepted。
这证明该算法的正确性还是可以保障的。
以下附上我的代码:
//ZOJ1081,改进弧长法判点在形内形外
#include
#include
constintMAX=101;
structpoint{intx,y;}p[MAX];
intmain()
{
intn,m,i,sum,t1,t2,f,prob=0;
pointt;
while(scanf("%d",&n),n)
{
if(prob++)printf("\n");
printf("Problem%d:
\n",prob);
scanf("%d",&m);
for(i=0;i p[n]=p[0]; while(m--) { scanf("%d%d",&t.x,&t.y); for(i=0;i<=n;i++)p.x-=t.x,p.y-=t.y;//坐标平移 t1=p[0].x>=0? (p[0].y>=0? 0: 3): (p[0].y>=0? 1: 2); //计算象限 for(sum=0,i=1;i<=n;i++) { if(! p.x&&! p.y)break;//被测点为多边形顶点 f=p.y*p[i-1].x-p.x*p[i-1].y; //计算叉积 if(! f&&p[i-1].x*p.x<=0&&p[i-1].y*p[i].y<=0)break; //点在边上 t2=p.x>=0? (p.y>=0? 0: 3): (p.y>=0? 1: 2);//计算象限 if(t2==(t1+1)%4)sum+=1; //情况1 elseif(t2==(t1+3)%4)sum-=1; //情况2 elseif(t2==(t1+2)%4) //情况3 { if(f>0)sum+=2;elsesum-=2; } t1=t2; } if(i<=n||sum)printf("Within\n");elseprintf("Outside\n"); for(i=0;i<=n;i++)p.x+=t.x,p.y+=t.y; //恢复坐标 } } return0; } 2. C语言中实现点在多边形内算法 本文是采用射线法判断点是否在多边形内的C语言程序。 多年前,我自己实现了这样一个算法。 但是随着时间的推移,我决定重写这个代码。 参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。 这是个C语言的小算法的实现程序,本来不想放到这里。 可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。 我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。 也增加一下BLOG的点击量。 首先定义点结构如下: 以下是引用片段: Copy code /* Vertex structure */ typedef struct { double x, y; } vertex_t; 本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。 它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。 这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。 因此,定义多边形结构如下: 以下是引用片段: Copy code /* Vertex list structure – polygon */ typedef struct { int num_vertices; /* Number of vertices in list */ vertex_t *vertex; /* Vertex array pointer */ } vertexlist_t; 为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。 为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下: 以下是引用片段: Copy code /* bounding rectangle type */ typedef struct { double min_x, min_y, max_x, max_y; } rect_t; /* gets extent of vertices */ void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ rect_t* rc /* out extent*/ ) { int i; if (np > 0){ rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; }else{ rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ } for(i=1; i { if(vl[i].x < rc->min_x) rc->min_x = vl[i].x; if(vl[i].y < rc->min_y) rc->min_y = vl[i].y; if(vl[i].x > rc->max_x) rc->max_x = vl[i].x; if(vl[i].y > rc->max_y) rc->max_y = vl[i].y; } } 当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl: np)内。 本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B及vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。 具体原理就不多说。 为计算线段间是否存在交点,引入下面的函数: (1)is_same判断2(p、q)个点是 (1)否(0)在直线l(l_start,l_end)的同侧; (2)is_intersect用来判断2条线段(不是直线)s1、s2是 (1)否(0)相交; 以下是引用片段: Copy code /* p, q is on the same of line l */ static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ const vertex_t* p, const vertex_t* q) { double dx = l_end->x - l_start->x; double dy = l_end->y - l_start->y; double dx1= p->x - l_start->x; double dy1= p->y - l_start->y; double dx2= q->x - l_end->x; double dy2= q->y - l_end->y; return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); } /* 2 line segments (s1, s2) are intersect? */ static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, const vertex_t* s2_start, const vertex_t* s2_end) { return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; } 下面的函数pt_in_poly就是判断点(v)是 (1)否(0)在多边形(vl: np)内的程序: 以下是引用片段: Copy code int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ const vertex_t* v) { int i, j, k1, k2, c; rect_t rc; vertex_t w; if (np < 3) return 0; vertices_get_extent(vl, np, &rc); if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) return 0; /* Set a horizontal beam l(*v, w) from v to the ultra right */ w.x = rc.max_x + DBL_EPSILON; w.y = v->y; c = 0; /* Intersection points counter */ for(i=0; i { j = (i+1) % np; if(is_intersect(vl+i, vl+j, v, &w)) { c++; } else if(vl[i].y==w.y) { k1 = (np+i-1)%np; while(k1! =i && vl[k1].y==w.y) k1 = (np+k1-1)%np; k2 = (i+1)%np; while(k2! =i && vl[k2].y==w.y) k2 = (k2+1)%np; if(k1 ! = k2 && is_same(v, &w, vl+k1, vl+k2)==0) c++; if(k2 <= i) break; i = k2; } } return c%2; } 本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。 以后再试吧! 实践证明,本程序算法的适应性极强。 但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 3.判断一个几何点在多边形内的例子: #define X 0 #define Y 1 typedef enum { FALSE, TRUE } bool; #define DIM 2 /* Dimension of points */ typedef int tPointi[DIM]; /* type integer point */ typedef double tPointd[DIM]; /* type double point */ #define PMAX 10000 /* Max # of pts in polygon */ typedef tPointi tPolygoni[PMAX];/* type integer polygon */ char InPoly( tPointi q, tPolygoni P, int n ); void PrintPoly( int n, tPolygoni P ); void PrintPoint( tPointi p ); void Copy ( tPolygoni a, tPolygoni b , int n ); main() { int n; tPolygoni P, Porig; tPointi q; n = ReadPoly( P ); Copy( P, Porig, n ); while( scanf( "%d %d", &q[X], &q[Y]) ! = EOF ) { printf( "InPoly = %c\n", InPoly( q, P, n ) ); /* Refill the destroyed polygon with original. */ Copy( Porig, P, n ); } } /* InPoly returns a char in {i,o,v,e}. See above for definitions. */ char InPoly( tPointi q, tPolygoni P, int n ) { int i, i1; /* point index; i1 = i-1 mod n */ int d; /* dimension index */ double x; /* x intersection of e with ray */ int Rcross = 0; /* number of right edge/ray crossings */ int Lcross = 0; /* number of left edge/ray crossings */ printf("\n==>InPoly: q = "); PrintPoint(q); putchar('\n'); /* Shift so that q is the origin. Note this destroys the polygon. This is done for pedogical clarity. */ for( i = 0; i < n; i++ ) { for( d = 0; d < DIM; d++ ) P[i][d] = P[i][d] - q[d]; } /* For each edge e=(i-1,i), see if crosses ray. */ for( i = 0; i < n; i++ ) { /* First see if q=(0,0) is a vertex. */ if ( P[i][X]==0 && P[i][Y]==0 ) return 'v'; i1 = ( i + n - 1 ) % n; /* printf("e=(%d,%d)\t", i1, i); */ /* if e "straddles" the x-axis... */ /* The commented-out statement is logically equivalent to the one following. */
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- GIS 多边形 算法