如何组合复杂的多边形?

Posted

技术标签:

【中文标题】如何组合复杂的多边形?【英文标题】:How do I combine complex polygons? 【发布时间】:2011-02-09 16:45:23 【问题描述】:

给定两个多边形:

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's example 使用 SQL Server 生成联合,但我需要在代码中完成相同的操作。我正在寻找公开实际数学的任何语言的数学公式或代码示例。我正在尝试制作将国家动态组合成区域的地图。我在这里问了一个相关的问题:Grouping geographical shapes

【问题讨论】:

【参考方案1】:

这是一个很好的问题。前段时间我在 c# 上实现了相同的算法。该算法构造两个多边形的共同轮廓(即构造一个没有孔的联合)。在这里。


第 1 步。创建描述多边形的图表。

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

我们应该找到交叉点。遍历两个多边形 [O(n*m)] 中的所有多边形边并找到任何交点。

如果没有找到交点,只需添加顶点并将它们连接起来 到边缘。

如果找到任何交叉点,将它们按长度排序到它们的起点,添加所有 顶点(起点、终点和交点)并将它们连接起来(已经在 排序顺序)到边缘。

步骤 2. 检查构造图

如果我们在构建图的时候没有找到任何交点,我们有以下情况之一:

    多边形 1 包含多边形 2 - 返回多边形 1 Polygon2 包含 polygon1 - 返回 polygon2 Polygon1 和 polygon2 不相交。返回多边形 1 和多边形 2。

第 3 步。找到左下顶点。

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

Step 4. 构建公共轮廓。

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

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

我计算两个向量:vector1 代表当前边,vector2 代表每个下一个未访问边(如图所示)。

对于我计算的向量:

    标量积(点积)。它返回一个与向量之间的角度相关的值。 向量积(叉积)。它返回一个新向量。如果这个的z坐标 向量是正数,标量积给了我直角 逆时针方向。否则(z 坐标为负),我 计算得到向量之间的角度为 360 - 来自标量的角度 产品。

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

我将每个通过的顶点添加到结果列表中。结果列表是联合多边形。

备注

    此算法允许我们合并多个多边形 - 以 使用多边形对迭代应用。 如果您的路径由许多贝塞尔曲线和直线组成,则应先展平该路径。

【讨论】:

我认为应该提到的是,为了比较标量积,向量应该在计算它们的标量积之前进行归一化(即,将向量坐标除以它的长度)。不管怎样,谢谢你的回答。 这个算法有名字还是你自己创造的? 我在某处读过,但现在我不记得何时何地=) 注意:参见polygon union without holes,它显示了不同的图表:两个多边形重叠,但是有一个它们都没有覆盖的“洞”。根据@xtmq 的评论,该算法“填充”了那个洞(即使它不是 输入多边形的一部分)。如果您希望将这些孔“保留”为孔,则 (a) 计算孔,并 (b) 返回“孔集”[在某些图形系统/模式上,这些孔可以包含在输出多边形集中, 并且在绘制时会产生孔洞。] ... ... 要执行“(a) 计算孔洞”,请查找“步骤 4。构建公共轮廓”从未访问过的点。使用这些点之一来“开始”这个洞。执行类似的“轮廓”算法,不包括主输出多边形已经使用的任何点。生成的多边形是一个“洞”。重复直到所有点都包含在某个多边形或孔中。【参考方案2】:

这是一个具有挑战性但易于理解的话题,经常会出现 在“多边形上的正则化布尔运算”的名称下。 你可能会看 this MathOverflow answer, 其中包括下图 (来自Alan Murta's clipping library), 与粉红色联合 OP 的 combine



【讨论】:

这家伙确实写了这本书;)【参考方案3】:

您需要determine which points lie inside。删除这些点后,您可以将一组“外部”点插入另一组。您的插入点(例如右图中箭头所在的位置)是您必须从输入集中删除点的位置。

【讨论】:

+1 用于链接到 Bourke。慢了 30 秒,我会打败你的 :)【参考方案4】:

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

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

然后:制作联合形状。您可以取 A 和 B 中的所有顶点以及交点,然后排除最终形状中包含的顶点。要找到这些点,看起来你可以找到 A 在 B 内部的任何顶点,以及 B 在 A 内部的任何顶点。

【讨论】:

是的,真正的问题是我们如何计算这两个相交点【参考方案5】:

试试gpc。

【讨论】:

这看起来很有希望。我已经给作者发了电子邮件,因为他们的下载链接都返回 403。 源代码链接对我有用:ftp.cs.man.ac.uk/pub/toby/gpc/gpc232-release.zip【参考方案6】:

我也遇到了同样的问题,我通过以下方式解决了这个问题

用于 Angus Johnson 的 Clipper 库的 C++ 翻译的 Cython 包装器(版本 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【参考方案7】:

here 描述了我见过的使用 BSP 树的解决方案。

基本上,它根据多边形 B 内的多边形 A 的边的联合来描述交集(包括部分边,并使用 BSP tree 计算)。然后,您可以将 A / B 定义为 ~(~A /\ ~B),其中 ~ 表示反转多边形的缠绕,/表示并集,/\表示交集。

【讨论】:

【参考方案8】:

这是一个非常古老的问题,但来自 Boost 的 Union_ 函数对我有用。

请看下面这个sn-p:

#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;

【讨论】:

记得在必要时“纠正”你的多边形。见***.com/questions/22258784/…【参考方案9】:

在对国家进行分组时,我希望没有重叠——您可以采用一种相当简单的算法来查找共享顶点——一个简单的视图是遍历一个多边形上的点,看看它是否在任何一个多边形上您的其他多边形,并共享相同的下一个或上一个点以查看是否匹配。然后只需删除共享顶点即可创建您的联合

【讨论】:

“在对国家进行分组时,我希望没有重叠”...并非所有国家都同意自己或邻国的边界,但如果他们同意就好了。 @FrustratedWithFormsDesigner 确实如此,但大多数制图师要么将有争议的地区分配给他们的政治盟友,要么将其作为一个独立的实体——这也是我将我的算法描述为幼稚的原因......跨度> 【参考方案10】:

我今天需要解决同样的问题,并通过这个库找到了解决方案:http://www.cs.man.ac.uk/~toby/alan/software/。

它有很多语言实现the list here,包括 Java、Obj-C、C#、Lua、python 等等。

【讨论】:

以上是关于如何组合复杂的多边形?的主要内容,如果未能解决你的问题,请参考以下文章

组合按特定顺序的相邻 3D 多边形

是否有用于查找复杂多边形的凸包的线性时间算法?

您能否通过数据框中的值组合SpatialPolygonsDataFrame中的多边形?

如何确定一个点是否在多边形内仅由水平线和垂直线组成?

检测地理位置是不是在复杂多边形中

如何重绘一个完全自相交的多边形?