关键词不能为空

当前您在: 主页 > 英语 >

GIS点在多边形内算法

作者:高考题库网
来源:https://www.bjmy2z.cn/gaokao
2021-02-01 21:14
tags:

-

2021年2月1日发(作者:决定)


1.


判断点在多边形内外的简单算法



--


改进弧长法


< br>今天学图形学的时候发现了一个求多边形内外的超简单算法,


当时觉得非常惊喜,


后来


晚上上完选修的时候在走廊遇到


b ug



bug


也是很惊喜地感慨道:居 然有甘简单既办法


都捻唔到!遂将其写下,供大家分享,希望不会太火星。




这个算法是源自《计算机图形学基础教程 》(孙家广,清华大学出版社),在该书



48-49


页,


名字可称为“改进的弧长法”。


该算法只需


O(1)


的附加空间,


时间复杂度



O(n)



但系 数很小;


最大的优点是具有很高的精度,


只需做乘法和减法,< /p>


若针对整数


坐标则完全没有精度问题。


而 且实现起来也非常简单,


比转角法和射线法都要好写且不


易出错 。




首先从该收中摘抄一段弧 长法的介绍:


“弧长法要求多边形是有向多边形,


一般规


定沿多边形的正向,边的左侧为多边形的内侧域。以被测点为圆心作单位圆,将全部有


向边向单位圆作径向投影,并计算其中单位圆上弧长的代数和。若代数和为


0


,则点在


多边形外部;


若代数和为


2


π


则点在多边形内部;


若代数和为


π



则点在多边 形上。




< br>按书上的这个介绍,其实弧长法就是转角法。但它的改进方法比较厉害:将坐标原


点平移到被测点


P


,这个新坐标系将平面划分为


4


个象限,对每个多边形顶点


P

,只考


虑其所在的象限,然后按邻接顺序访问多边形的各个顶点

P


,分析


P


P[i+1]


,有下列


三种情况:




1



P[i +1]



P


的下一象限。此时弧长和加


π


/2



< /p>



2



P[i+ 1]



P


的上一象限。此时弧长和减< /p>


π


/2





3



P[i+1 ]



Pi


的相对象限。首先计算


f=y[i+1]*x-x[i+1]*y


(叉积),若


f=0


,则


点在多边形上;若


f<0


,弧长和减


π


;若

< p>
f>0


,弧长和加


π


。< /p>




最后对算出的代数和和上述的情况一样判断即可。




实现的时候还有两点要注意,第一个是若


P


的某个坐标为


0


时,一律 当正号处理;


第二点是若被测点和多边形的顶点重合时要特殊处理。



以上就是书上讲解的内容,

其实还存在一个问题。


那就是当多边形的某条边在坐标


轴上 而且两个顶点分别在原点的两侧时会出错。如边


(3,0)-(-3,0)


,按以上的处理,象


限分别是第一和第二,


这样会使 代数和加


π


/2


有可能导致最后结果是被测点在多边形


外。而实际上被测点是在多边形上(该边穿过 该点)。




对于这点,


我的处理办法是:


每次算


P

< br>和


P[i+1]


时,


就计算叉积 和点积,


判断该点


是否在该边上,


是则 判断结束,


否则继续上述过程。


这样牺牲了时间,


但保证了正确性。




具体实现的时候,由于只需知道当前点和上一点的象限位置,所以附加空间只需


O(1 )


。实现的时候可以把上述的“


π


/2 ”改成


1


,“


π


”改成


2


,这样便可以完全使用


整数 进行计算。不必考虑顶点的顺序,逆时针和顺时针都可以处理,只是最后的代数和


符号不 同而已。整个算法编写起来非常容易。




针对以上算法,我写了一个代码,拿


ZOJ 1081 Points Within


进行测试,顺利


Accepted


。这证明该算法的正确性还是可以保障的。



以下附上我的代码:



// ZOJ 1081 ,


改进弧长法判点在形内形外



#include


#include


const int MAX = 101


struct point { int x , y } p[MAX]


int main()


{


int n , m , i , sum , t1 , t2 , f , prob = 0


point t


while ( scanf(


{


if( prob ++ ) printf (


printf (


scanf (


for ( i = 0 i < n i ++ ) scanf (


p[n] = p[0]


while ( m -- )


{


scanf (


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


else


if


(


t2


==


(


t1


+


3


)


%


4


)


sum


-=


1


; //


情况


2


else if ( t2 == ( t1 + 2 ) % 4 ) //


情况


3


{


if ( f > 0 ) sum += 2 else sum -= 2;


}


t1 = t2


}


if ( i<=n || sum ) printf(


printf(


for


(


i


=


0


;


i


<=


n


;


i


++


)


p.x


+=


t.x


,


p.y


+=


t.y


; //


恢复坐标



}


}


return 0;


}






2.


本文是采用射线法判断点是否 在多边形内的


C


语言程序。多年前,我自己实现了这样一个


算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合< /p>


我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。







这是个


C


语 言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样


一个算法的时候 ,


想在网上找个现成的,


考察下来竟然一个符合需要的也没有。


我对自己大


学读书时写的代码没有信心,所以,决定重新写一个 ,并把它放到这里,以飨读者。也增加


一下


BLOG

< p>
的点击量。







首先定义点结构如下:





以下是引用片段:




Copy code




/* Vertex structure */





typedef struct





{





double x, y;





} vertex_t;






本算法里所指的多边形,


是指由一系列点序列组成的封闭简单多边形 。


它的首尾点可以


是或不是同一个点


(


不强制要求首尾点是同一个点


)


。这样 的多边形可以是任意形状的,包括


C



多条边在一条绝对直线上。因此,定义多边形结构如下:





实现



以下是引用片段:




点在


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)



判断点是否落在外包矩形内,

< p>


有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外 包矩形结构


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)

< p>
水平引出一条射线


B(v



w)


,计算


B


vl


边线的交点数


目,记为


c


,根据奇内偶外原则


(c


为奇数说明


v



vl


内,否则< /p>


v


不在


vl


内< /p>


)


判断点是否在多


边形内。







具体原理就不多说。为计算线段间是否存在交点,引入下面的 函数:







(1)is_same


判断


2(p



q)


个点是< /p>


(1)



(0)


在直线


l(l_start



l_en d)


的同侧


;






(2) is_intersect


用来判断


2


条线段


(


不是直线


)s1



s2



(1)

< p>


(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< /p>


就是判断点


(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 < _x || v->x > _x || v->y < _y || v->y > _y)



return 0;





/* Set a horizontal beam l(*v, w) from v to the ultra right */





w.x = _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;





}







本想配些插图说明问题,但是,


CS DN


的文章里放图片我还没用过。以后再试吧


!


实践


证明,


本程序算法的适应性极强。


但是,


对于点正好落在多边形边上的极端情形,


有可能 得



2


种不同的结果。



3.


判断一个几何点在多边形内的例子:

< p>











#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



&q[X],



&q[Y])



!=



EOF



)



{









printf(





=



%cn



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(



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(



i1,



i);



*/
















/*



if



e





the



x-axis...



*/









/*



The



commented-out



statement



is



logically



equivalent



to



the



one













following.



*/









/*



if(



(



(



P[i][Y]



>



0



)



&&



(



P[i1][Y]



<=



0



)



)



||












(



(



P[i1][Y]



>



0



)



&&



(



P[i]



[Y]



<=



0



)



)



)



{



*/
















if(



(



P[i][Y]



>



0



)



!=



(



P[i1][Y]



>



0



)



)



{




















/*



e



straddles



ray


,



so



compute



intersection



with



ray


.



*/











x



=



(P[i][X]



*



(double)P[i1][Y]



-



P[i1][X]



*



(double)P[i][Y])






/



(double)(P[i1][Y]



-



P[i][Y]);











/*



printf(



x



=



%gt



x);



*/




















/*



crosses



ray



if



strictly



positive



intersection.



*/











if



(x



>



0)



Rcross++;





















}





/*



printf(



cross=%dt



Rcross);



*/








/*



if



e



straddles



the



x-axis



when



reversed...



*/









/*



if(



(



(



P[i]



[Y]



<



0



)



&&



(



P[i1][Y]



>=



0



)



)



||












(



(



P[i1][Y]



<



0



)



&&



(



P[i]



[Y]



>=



0



)



)



)




{



*/
















if



(



(



P[i][Y]



<



0



)



!=



(



P[i1][Y]



<



0



)



)



{





















/*



e



straddles



ray


,



so



compute



intersection



with



ray


.



*/











x



=



(P[i][X]



*



P[i1][Y]



-



P[i1][X]



*



P[i][Y])















/



(double)(P[i1][Y]



-



P[i][Y]);











/*



printf(



x



=



%gt



x);



*/














/*



crosses



ray



if



strictly



positive



intersection.



*/











if



(x



<



0)



Lcross++;









}









/*



printf(



cross=%dn



Lcross);



*/







}













/*



q



on



the



edge



if



left



and



right



cross



are



not



the



same



parity.



*/








if(



(



Rcross



%



2



)



!=



(Lcross



%



2



)



)









return



'e';












/*



q



inside



iff



an



odd



number



of



crossings.



*/







if(



(Rcross



%



2)



==



1



)









return



'i';







else



return



'o';





}








void



PrintPoint(



tPointi



p



)





{







int



i;












putchar('(');







for



(



i




=



0;



i



<



DIM;



i++



)



{









printf(



p[i]);









if



(



i



!=



DIM-1



)



putchar(',');







}







putchar(')');





}








/*








Reads



in



the



coordinates



of



the



vertices



of



a



polygon



from






puts



them



into



P


,



and



returns



n,



the



number



of



vertices.








Formatting



conventions:



etc.








*/





int



ReadPoly(



tPolygoni



P



)





{







int



i,



n;












do



{









printf(





the



number



of



vertices:n









scanf(





&n



);









if



(



n



<=



PMAX



)











break;









printf(



in



read_poly:




too



many



points;



max



is



%dn





}







while



(



1



);










printf(





);







printf(







i





x





yn







for



(



i




=



0;



i



<



n;



i++



)



{




stdin,




PMAX);











scanf(





%d



&P[i][0],



&P[i][1]



);









printf(



i,



P[i][0],



P[i][1]);







}







printf(



=



%3d



vertices



readn







putchar('n');










return



n;





}








void



PrintPoly(



int



n,



tPolygoni



P



)





{







int



i;












printf(







printf(




i





x





yn







for(



i



=



0;



i



<



n;



i++



)









printf(



i,



P[i][0],



P[i][1]);





}








/*



Copy



polygon



a



to



b



(overwriting



b).



*/





void



Copy(



tPolygoni



a,



tPolygoni



b,



int



n



)





{







int



i,



j;












for



(



i=0;



i



<



n;



i++)









for



(



j




=



0;



j



<



DIM;



j++



)











b[i][j]



=



a[i][j];





}








bool




EqPoint(



tPointi



a,



tPointi



b



)





{







int







i;







for



(



i




=



0;



i



<



DIM;



i++



)









if



(



a[i]



!=



b[i])











return




FALSE;







return




TRUE;





}













1.


已知点


point(x,y)


和多边形


Polyg on



x1,y1;x2,y2;….xn,yn;

< p>
);



2.

< br>以


point


为起点,以无穷远为终点作平行于


X


轴的直线


line(


x,y; -



,y)




3.


循环取得


(f or(i=0;i


多边形的每一条边


side


(xi,yi;xi+1,yi+1)



且判断是否平行于


X


轴,


如果平行< /p>


continue



否则,


i++




4.


同时判断


point(x,y)


是否在


side


上,如果是,则返回


1(


点在多边形




)


,否则继续下面的判断;



5.


判断线


side


line


是否有交点,如果有则


count++

< p>
,否则,


i++




6.


判断交点的总数,如果为奇数则返回< /p>


0


(点在多边形内),偶数则返回


2


(点在多边形外)。



代码:



/*


射线法判断点


q


与多边形


polyg on


的位置关系,要求


polygon


为简单多边形,顶点逆时针排列




如果点在多边形内:



返回


0




如果点在多边形边上:



返回


1




如果点在多边形外:



返回


2



*/



const double INFINITY = 1e10;



const double ESP = 1e-5;



const int MAX_N = 1000;




-


-


-


-


-


-


-


-



本文更新与2021-02-01 21:14,由作者提供,不代表本网站立场,转载请注明出处:https://www.bjmy2z.cn/gaokao/595101.html

GIS点在多边形内算法的相关文章

  • 爱心与尊严的高中作文题库

    1.关于爱心和尊严的作文八百字 我们不必怀疑富翁的捐助,毕竟普施爱心,善莫大焉,它是一 种美;我们也不必指责苛求受捐者的冷漠的拒绝,因为人总是有尊 严的,这也是一种美。

    小学作文
  • 爱心与尊严高中作文题库

    1.关于爱心和尊严的作文八百字 我们不必怀疑富翁的捐助,毕竟普施爱心,善莫大焉,它是一 种美;我们也不必指责苛求受捐者的冷漠的拒绝,因为人总是有尊 严的,这也是一种美。

    小学作文
  • 爱心与尊重的作文题库

    1.作文关爱与尊重议论文 如果说没有爱就没有教育的话,那么离开了尊重同样也谈不上教育。 因为每一位孩子都渴望得到他人的尊重,尤其是教师的尊重。可是在现实生活中,不时会有

    小学作文
  • 爱心责任100字作文题库

    1.有关爱心,坚持,责任的作文题库各三个 一则150字左右 (要事例) “胜不骄,败不馁”这句话我常听外婆说起。 这句名言的意思是说胜利了抄不骄傲,失败了不气馁。我真正体会到它

    小学作文
  • 爱心责任心的作文题库

    1.有关爱心,坚持,责任的作文题库各三个 一则150字左右 (要事例) “胜不骄,败不馁”这句话我常听外婆说起。 这句名言的意思是说胜利了抄不骄傲,失败了不气馁。我真正体会到它

    小学作文
  • 爱心责任作文题库

    1.有关爱心,坚持,责任的作文题库各三个 一则150字左右 (要事例) “胜不骄,败不馁”这句话我常听外婆说起。 这句名言的意思是说胜利了抄不骄傲,失败了不气馁。我真正体会到它

    小学作文