如何合并复杂的多边形?


81

给定两个多边形:

POLYGON((1 0, 1 8, 6 4, 1 0))
POLYGON((4 1, 3 5, 4 9, 9 5, 4 1),(4 5, 5 7, 6 7, 4 4, 4 5))

如何计算联合(组合多边形)?

在此处输入图片说明

Dave的示例使用SQL Server生成联合,但是我需要在代码中完成相同的操作。我正在寻找可以公开实际数学的任何语言的数学公式或代码示例。我正在尝试制作将国家动态地组合成地区的地图。我在这里问了一个相关的问题:对地理形状进行分组

Answers:


67

这个问题问得好。我前一段时间在c#上实现了相同的算法。该算法构造两个多边形的公共轮廓(即构造不带孔的并集)。这里是。


目标

步骤1.创建描述多边形的图形。

输入:第一个多边形(n个点),第二个多边形(m个点)。输出:图形。顶点-相交点的多边形点。

我们应该找到路口。遍历两个面[O(n * m)]中的所有面,并找到任何交点。

  • 如果找不到相交,只需添加顶点并将其连接到边即可。

  • 如果找到任何相交,则按长度对其起点进行排序,添加所有顶点(起点,终点和相交)并将其连接(已按排序顺序)到边缘。 图形

步骤2.检查构造图

如果在构建图形时未找到任何交点,则我们具有以下条件之一:

  1. 多边形1包含多边形2-返回多边形1
  2. 多边形2包含多边形1-返回多边形2
  3. 多边形1和多边形2不相交。返回多边形1和多边形2。

步骤3.找到左下角顶点。

找到最小的x和y坐标(minx,miny)。然后找到(minx,miny)与多边形点之间的最小距离。该点将是左下角。

左下点

步骤4.构造公共轮廓。

我们从左下角开始遍历该图,并继续直到回到该图为止。在开始时,我们将所有边缘标记为未访问。在每次迭代中,您应该选择下一个点并将其标记为已访问。

要选择下一个点,请选择一个沿逆时针方向具有最大内角的边。

我计算了两个向量:当前边缘的vector1和每个下一个未访问边缘的vector2(如图所示)。

对于向量,我计算:

  1. 标量积(点积)。它返回与向量之间的角度有关的值。
  2. 向量乘积(叉积)。它返回一个新的向量。如果此向量的z坐标为正,则标量积使我沿逆时针方向成直角。否则(z坐标为负),我计算矢量之间的获取角度为360-来自标量积的角度。

结果,我得到了具有最大角度的边(以及对应的下一个顶点)。

我将每个传递的顶点添加到结果列表中。结果列表是联合多边形。 向量

备注

  1. 该算法允许我们合并多个多边形-迭代地应用多边形对。
  2. 如果您的路径包含许多贝塞尔曲线和线,则应首先展平该路径。

2
我认为应该提到的是,在比较标量积时,应在计算其标量积之前对矢量进行归一化(即,将矢量坐标除以其长度)。无论如何,感谢您的回答。
eyalzba'3

这个算法有名称还是您自己创建的?
安德烈斯·奥维耶多

我在某处阅读,但现在我不记得在何时何地=)
xtmq

注意:请参见不带孔的多边形并集,该图显示了不同的图:两个多边形重叠,但是有一个“孔”,它们都没有覆盖。根据@xtmq的注释,此算法“填充”了该孔(即使它既不是输入多边形的一部分也不是)。如果您反而希望将这些孔“保留”为孔,则(a)计算孔,然后(b)返回“孔组” [在某些图形系统/模式下,这些孔可以包含在输出多边形集中,并且在绘制时会产生孔。] ...
ToolmakerSteve

2
...要执行“(a)计算孔”,请查找“步骤4。构造公共轮廓”从未访问过的点。使用这些点之一来“开始”孔。执行类似的“轮廓”算法,排除主要输出多边形已经使用的任何点。所得的多边形为“孔”。重复直到所有点都包含在某个多边形或孔中。
ToolmakerSteve



4

好问题!我以前从未尝试过,但是现在我会尝试一下。

首先:您需要知道这两个形状在哪里重叠。为此,您可以查看多边形A中的每条边,并查看它们相交的位置以及多边形B中的边。在此示例中,应该有两个相交点。

然后:制作并集形状。您可以获取A和B中的所有顶点以及相交点,然后排除最终形状所包含的顶点。为了找到这些点,您似乎可以找到位于B内的A的任何顶点,以及位于A内的B的任何顶点。


是的,真正的问题是我们如何计算两个相交的相交点
起搏器

4

尝试gpc


这看起来很有希望。我已经给作者发了电子邮件,因为他们的下载链接都返回了403。
手榴弹

1
源代码的链接对我有用
lhf 2010年

2

这里介绍我使用BSP树看到的解决方案

基本上,它根据多边形B内的多边形A的边(包括部分边,并使用BSP树计算)的并集来描述交集。然后,可以定义 / 作为〜(〜 / \〜),其中〜代表反转多边形的绕组,/表示联合和/ \表示交叉点。


1

将国家/地区分组时,我希望不会有重叠-您可以采用一种相当幼稚的算法来查找共享顶点-一个简单的视图是遍历一个多边形上的点,看看它是否在其他任何多边形上,并共享相同的下一个或上一个点,以查看是否存在匹配项。然后只需删除共享顶点即可创建并集


2
“当将国家分组时,我希望不会有重叠”……并非所有国家都同意自己或邻国的边界,尽管这样做会很好。
FrustratedWithFormsDesigner 2010年

2
@FrustratedWithFormsDesigner确实是,但是大多数制图师要么将有争议的地区分配给他们的政治盟友,要么单独作为一个单独的实体-这就是为什么我将我的算法描述为幼稚的原因
罗兰·肖


1

这是一个很老的问题,但是Union_Boost的函数为我工作。

请参见下面的代码片段:

#include <iostream>
#include <vector>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>

#include <boost/foreach.hpp>


int main()
{
    typedef boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > polygon;

    polygon green, blue;

    boost::geometry::read_wkt(
        "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))", green);

    boost::geometry::read_wkt(
        "POLYGON((5 5, 5 15, 15 15, 15 5, 5 5))", blue);

    std::vector<polygon> output;
    boost::geometry::union_(green, blue, output);

    int i = 0;
    std::cout << "green || blue:" << std::endl;
    BOOST_FOREACH(polygon const& p, output)
    {
        std::cout << i++ << ": " << boost::geometry::area(p) << std::endl;

        for (int i = 0; i < p.outer().size(); i++)
        {
            std::cout << p.outer().at(i).x() << " " << p.outer().at(i).y() << std::endl;
        }
    }



    return 0;
}

如果需要,请记住要“校正”多边形。参见stackoverflow.com/questions/22258784/…–
anumi

1

我遇到了同样的问题,并通过以下方式解决了该问题

Cython包装器,用于Angus Johnson's Clipper库的C ++翻译(版本6.4.2) https://github.com/fonttools/pyclipper

pc = pyclipper.Pyclipper()
def get_poly_union(polygons):
    pc.AddPaths(polygons, pyclipper.PT_SUBJECT, True)
    solution = pc.Execute(pyclipper.CT_UNION, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO)
    return solution[0]

print_image = image.copy()
solution = get_poly_union(polygons_array) 
#polygons_array=[polygon,polygon,polygon, ...,polygon] and polygon=[point,point,point...,point]

cv2.drawContours(print_image, [np.asarray(solution)], -1, (0, 255, 0), 2)

plt.imshow(print_image)

Clipper可以直接在C ++中获得:angusj.com/delphi/clipper.php
Catskul
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.