GIS面裁减(二)

来源:互联网 发布:淘宝商品采集软件 编辑:程序博客网 时间:2024/05/02 00:59

 

李泉   陈玉进(南京跬步科技有限公司http://www.creable.cn )

图中S1…….S13是主多边形的节点序列(注意:主多边形的两个环是逆时针方向的),C1……C4是裁剪多边形的节点序列,I1……I4是交点序列,其中I1I3是入点,I2I4是出点。算法开始时从主多边形的节点序列中开始寻找入点(I1),I1标记为已读,沿主多边形顺时针继续(S4S5),发现一个出点(I2),然后跳跃到裁剪多边形序列上,发现下一个入点(I3),跳跃到主多边形(S7),发现一个出现(I4),跳跃到裁剪多边形,发现起始点(I1)裁剪出一个小多边形(I1S4S5I2I3S7I4I1)。从头搜索没有未读入点则算法结束。有一个环与裁剪多边形没有交点,需要判断该环是在裁剪框多边形还是外(判断环的每个节点是否都在裁切多边形内部)。如果在裁剪框内部,则再次判断在刚刚裁剪出来的哪个多边形(裁剪之后的多边形会有多个)的内部,这样这个环的归属问题就解决了。

 

该算法可以应用在任意多边形与任意多边形的情况。在算法描述上矩形与任意多边形也就节点数量的区别,其他基本一样。具体算法实施步骤不在详述。

一、线段求交问题

由于在我们地理信息系统中的面裁剪应用中,裁剪多边形为矩形的情况比较多,这里讨论在这种情况下的优化算法。交点必然出现在矩形边上,求交时必然已知一个交点的横坐标或纵坐标,比用解方程组求交先利用先用向量来判断是否有交点再求交这种方式来的高(裁剪多边形不是矩形是则需要先利用向量判断是否有交点,再解方程组来计算出交点坐标)。下面以计算图1I1为例说明求交的算法。设裁剪框的最小横坐标为x,最小纵坐标为ymin,最大纵坐标为ymax,且为已知,交点的x坐标必然等于x,只需求解交点的y值即可,设S1的坐标为(x1,y1),S2的坐标为(x2,y2),那么有这样的等式成立 ,可以转换为 。很快即可计算出y值。(如果 ,那么 ,不用代入公式计算,如果 ,说明该主多边形上的线段与裁剪框的边平行,直接返回无交点)。需要判断该交点是否在上述两个线段上,满足如下不等式则交点在线段上,

二、算法实现流程

Weiler-Atherton算法相对比较复杂,对数据结构的依赖性很明显,好的数据结构是算法高效的基础。

1. 主多边形不包含环的情况。

设计这么一个结构PointInfo用于保存节点相关信息,其中pos记录点坐标,flag表示节点类型(0为正常的节点,1为入交点,-1为出交点),pos1为此交点后面一个主多边形上节点的序号,pos2为此交点后面一个裁剪多边形上节点的序号。

class PointInfo

{

    public IPoint pos;

    public byte flag;//0为正常的节点,1为入交点,-1为出交点

    public int pos1;

    public int pos2;

   

    public PointInfo(float x,float y)

    {

       pos=new Point(x,y);

    }

   

    public PointInfo(IPoint pt)

    {

       pos=pt;

    }

}

     使用两个能够管理上述结构的链表(list1list2)分别存放主多边形和裁剪多边形节点序列,再使用一个链表(list3)来存放计算出来的交点。

     将交点按照上述结构的pos1升序排序,之后反向插入到list1中(注意:为什么要反向插入?如果从正向插入list1,那么插入第一个交点之后,list1后面节点的序号就变了,insert函数需要一个插入到那里的参数,这样插入的次序就会错乱。反之,从距离主多边形节点序列最后的交点首先插入到list1中,则不会影响list1后面节点的序号。)。有的朋友会问为什么不降序排序,这样就不用反向插入list1了。计算交点的代码使用一个循环,该循环体的变量是主多边形的节点,所以计算出来的交点序列基本上是按照pos1升序的,升序排序的效率比较高,而反向插入的效率和正向差不多。

     将交点按照上述结构的pos2升序排序,之后反向插入到list2中。

     建立一个链表result存放结果多边形节点序列。

     遍历list1,发现flag=1的入点时开始算法,添加该点到result,并将flag设置为0(表示该入点已读,记录该点为起始点S,沿着list1取点,发现flag=-1(出点)时则跳跃到list2上,跳跃的位置在当前点的pos2中标识了,沿着list2取点,若发现起始点则算法结束,发现flag=1(入点)则将该flag设置为0,并跳跃到list1上,跳跃位置在当前点的pos1中标识了。

2. 主多边形包含环的情况。

如果主多边形保护环,则需要使用两个数组来存放上述链表list1list2的指针,数组的成员个数等于主多边形内部环的个数加上1,这样可以形成一个横向可变的锯齿型二维数组。将每个环和外边界的坐标序列放在在这个二维数组中。交点也采用这样的锯齿型二维数组来存放。PointInfo中添加一个成员index,用于标识该点位于哪个环中。还是采用上述的步骤,不过需要进行多次排序插入操作。在遍历查找过程中,需要借助index成员的值来在不同的环中跳转。

原创粉丝点击