【问题标题】:How to get a polygon from boost::geometry::model::polygon?如何从 boost::geometry::model::polygon 获取多边形?
【发布时间】:2016-05-06 11:17:13
【问题描述】:

我正在尝试使用boost::geometry::difference 计算两个多边形的差异,boost::geometry::model::polygon 代表我的多边形。

如果第一个多边形包含第二个多边形,则操作的结果是单个 boost::geometry::model::polygon,内环和外环填充有源多边形的坐标。

如何从boost::geometry::model::polygon 获取多边形 (in the elementary geometry sense)?

澄清:

在初等几何中,多边形是一个平面图形,其边界是有限的直线段链,这些直线段闭合成一个环,形成一个闭合的链或回路。

boost::geometry::model::polygon 的外环是多边形,内环也是多边形。整体而言,boost::geometry::model::polygon 不是多边形。

所以,我要问的是:如何将boost::geometry::model::polygon 转换为 正常 多边形(具有单条直线段链),它表示平面上的同一区域。

这是我想要实现的目标:

polygon1   = (0,0), (0,8), (8,8), (8,0), (0,0)
polygon2   = (2,2), (2,6), (6,6), (6,2), (2,2)

绿色/oker 的多边形 1 和 2:

difference = (0,0), (0,4), (2,4), (2,2), (6,2), (6,6), (2,6), (2,4), (0,4), (0,8), (8,8), (8,0), (0,0)

预期的灰色差异:

我知道具有内环的相同boost::geometry::model::polygon 可以由无限多个不同的正常多边形表示。我不在乎我得到哪一个。

【问题讨论】:

  • 你能澄清你的问题吗?您已经有一个表示多边形的对象,您想要获取的另一种多边形形式是什么,您真正想要实现的是什么?也许你想绘制多边形?
  • 回复。您的编辑“我知道具有内环的相同 boost::geometry::model::polygon 可以由无限多个不同的法线多边形表示。我不在乎我得到哪一个。” ——这似乎恰恰与你的真实要求背道而驰:你似乎很在乎。这完全正确,因为具有一个或多个内环的一个外环不能可能以另一种方式表示为“正常多边形¹”。 (¹您的定义:“(具有一条直线段链)”)
  • 正如您在我的示例中看到的,为了表示两个多边形的差异,我引入了一条直线段 (0,4)-(2,4) 创建 a weakly simple polygon,显然是其他任何可以改为使用连接内外边界的直线段,从而产生无限多种解决这种差异的方法。而且定义不是我的,它来自Wikipedia
  • 我想我已经理解你的方法了。我不会说您期望的结果在拓扑/数学上是等价的,但我确实看到它们如何被认为是实用等价的。我很快就会发布一个自定义算法。
  • 感谢您的插图,虽然看起来 polygon1 被渲染为边长为 10 而不是 8 的正方形。

标签: c++ boost boost-geometry


【解决方案1】:

您可以轻松构建一个环来模拟您预期的弱简单多边形。第一:

警告

请注意,该结果对于进一步使用 Boost Geometry 库的算法无效。

以你的文字为例:

std::string reason;
poly expected;
bg::read_wkt("POLYGON((0 0, 0 4, 2 4, 2 2, 6 2, 6 6, 2 6, 2 4, 0 4, 0 8, 8 8, 8 0, 0 0))", expected);
bool ok = bg::is_valid(expected, reason);
std::cout << "Expected: " << bg::dsv(expected) << (ok?" valid":" invalid: '" + reason + "'") << "\n";

打印

预期:(((0, 0), (0, 4), (2, 4), (2, 2), (6, 2), (6, 6), (2, 6), ( 2, 4), (0, 4), (0, 8), (8, 8), (8, 0), (0, 0))) invalid: '几何有无效的自相交。在 (0, 4) 处发现了一个自交点;方法:t;操作:x/u;分段 ID {source, multi, ring, segment}:{0, -1, -1, 0}/{0, -1, -1, 7}'

算法实现

除此之外,这是一个从给定多边形构造简单弱多边形的简单算法:

ring weak_simple_ring(poly& p) {
    ring r = p.outer();

    for (auto& i: p.inners()) {
        auto last = r.back();
        r.insert(r.end(), i.rbegin(), i.rend());
        r.insert(r.end(), last);
    }

    return r;
}

唯一更微妙的一点是反转内环的方向(CW/CCW)以匹配外环的方向。

该算法不会聪明地寻找内环的切点,这可能也意味着它不适用于具有多个内环的通用情况。

演示

这是一个完整的现场演示

Live On Coliru

输入是

bg::read_wkt("POLYGON((0 0,0 10,10 10,10 0,0 0))", a);
bg::read_wkt("POLYGON((2 2, 2 6, 6 6, 6 2, 2 2))", b);

变换是

std::vector<poly> output;
bg::difference(a, b, output);

for (auto& p : output) {
    ring r = weak_simple_ring(p);
    bg::convert(r, p);
}

结果变成

更复杂的示例

考虑b 有洞时:

bg::read_wkt("POLYGON((0 0,0 10,10 10,10 0,0 0))", a);
bg::read_wkt("POLYGON((2 2, 2 6, 6 6, 6 2, 2 2)(3 3, 5 3, 5 5, 3 5, 3 3))", b);

相同代码的输出变成

【讨论】:

  • 是的,我已经想到了类似的解决方案。不幸的是,它通常不起作用:所选顶点之间可能有其他“洞”,或者顶点顺序不同(请参阅modified demo)。我知道这个问题可以通过搜索最近的一对顶点并一次集成一个内环来解决,尽管我希望有一种性能更高的算法。
【解决方案2】:

这是旧答案。对我发布的问题进行编辑后a simple implementation of an algorithm that suits the sample given

已经是了。

如果您的意思是“简单”的无孔多边形,外环就是您想要的。只需丢弃内圈即可。

但是,在最一般的情况下,您最终可能会得到多个完全不相交的多边形,这就是the output is a collection of polygons 的原因。您也必须考虑这种可能性(可以选择将不同的结果多边形合并为一个,并在功能上根据需要丢弃内环)。

一个样本:

bg::read_wkt("POLYGON((0 0,0 10,10 10,10 0,0 0))", a);
bg::read_wkt("POLYGON((2 -2,2 12,5 12,5 -2,2 -2))", b);

在这里,ba 分成两个单独的部分。所以你必须准备好处理多个分离的输出多边形:

Live On Coliru

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

namespace bg = boost::geometry;
using pt   = bg::model::d2::point_xy<int>;
using poly = bg::model::polygon<pt>;

int main() {
    poly a, b;
    bg::read_wkt("POLYGON((0 0,0 10,10 10,10 0,0 0))", a);
    bg::read_wkt("POLYGON((2 -2,2 12,5 12,5 -2,2 -2))", b);

    std::cout << bg::dsv(a) << "\n";
    std::cout << bg::dsv(b) << "\n";

    {
        std::ofstream svg("/tmp/input.svg");
        boost::geometry::svg_mapper<pt> mapper(svg, 400, 400);
        mapper.add(a);
        mapper.add(b);

        mapper.map(a, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
        mapper.map(b, "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(202,153,0);stroke-width:2");
    }

    std::vector<poly> output;
    bg::difference(a, b, output);
    for (auto& p : output)
        std::cout << "\n\t" << bg::dsv(p);

    {
        std::ofstream svg("/tmp/output.svg");
        boost::geometry::svg_mapper<pt> mapper(svg, 400, 400);
        assert(output.size() == 2);
        mapper.add(output[0]);
        mapper.add(output[1]);
        mapper.add(b);

        mapper.map(output[0], "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
        mapper.map(output[1], "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
        mapper.map(b, "fill-opacity:0.15;fill:rgb(153,153,153);stroke-width:0");
    }
}

打印:

(((0, 0), (0, 10), (10, 10), (10, 0), (0, 0)))
(((2, -2), (2, 12), (5, 12), (5, -2), (2, -2)))

    (((5, 10), (10, 10), (10, 0), (5, 0), (5, 10)))
    (((2, 10), (2, 0), (0, 0), (0, 10), (2, 10)))

渲染的 SVG:

【讨论】:

  • 我使用boost::geometry::difference 的全部目的是排除第二个多边形所代表的区域,因此我没有必要丢弃内环。是的,我知道结果可能是一个多边形列表,我完全可以接受。
  • 我只能说,也许您的问题应该更清楚一点(例如,像我的示例一样清楚)。这样可以避免浪费很多时间。
  • 抱歉,我认为基本几何意义上的多边形已经足够清晰了。
猜你喜欢
  • 1970-01-01
  • 2011-12-05
  • 2013-10-21
  • 1970-01-01
  • 1970-01-01
  • 2013-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多