如何使用 C++ boost geometry 从两个多边形创建环(甜甜圈)?

How can I use C++ boost geometry to create ring (donut) from two polgons?

我正在尝试计算 2 个多边形之间的差异(实际上,为简单起见,是直线形状)。例如,shape1 有点列表 {0 0, 100, 0, 100 100, 0 100},shape2 有点列表 {25 25, 75 25, 75 75, 25 75}。 所以从概念上讲,我希望 "shape1 - shape2" 会为我提供 4 个矩形框,可以做成环形或甜甜圈形状。我不知道如何做到这一点,但我在网上找到了 "boost" 库并试了一下:

#include <iostream>
#include <list>

#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 100 0 100 100 0 100))", green);

    boost::geometry::read_wkt( "POLYGON((25 25 75 22 75 75 25 75))", blue);

    std::list<polygon> output;
    boost::geometry::difference(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;
    }


    output.clear();
    boost::geometry::difference(blue, green, output);

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


    return 0;
}

然而,什么也没有打印出来......我的计划是想办法将输出结果转换为矩形框 {0 0 100 0 100 25 0 25} {0 75 100 75 100 100 0 100} {0 25 25 25 25 75 0 75} 和 {75 25 100 25 100 75 75 75},但令我感到沮丧的是,上面的代码根本没有打印出任何内容。 谁能给我一些指导? 我附上了一张图片,展示了我想要做什么。事实上,我已经有了表示这个甜甜圈的所有点坐标,所以也许我可以跳过 "difference() function" ?如果是这种情况,困难的部分将是将甜甜圈转换为矩形段。 (让我们假设这里的所有多边形都是直线形状。) 谢谢

您的起始多边形没有所需的方向。您可以使用 is_valid 自行找出答案。方便的是,您可以使用 correct 来修复输入数据的最常见问题:

std::string reason;
if (!bg::is_valid(geometry, reason))
{
    std::cout << "Correcting " << bg::wkt(geometry) << " to ";
    bg::correct(geometry);
    std::cout << bg::wkt(geometry) << "\n";

    if (!bg::is_valid(geometry, reason))
        std::cout << "UNCORRECTIBLE: " << reason << "\n";
}

当你这样做时,你会发现:

Correcting POLYGON((0 0,100 0,100 100,0 100,0 0)) to POLYGON((0 0,0 100,100 100,100 0,0 0))
Correcting POLYGON((25 25,75 22,75 75,25 75,25 25)) to POLYGON((25 25,25 75,75 75,75 22,25 25))

现在,当你得到差异时,你将得到预期的结果:

auto do_diff = [](std::string caption, auto const& a, auto const& b) {
    multi output;
    bg::difference(a, b, output);

    std::cout << caption << bg::wkt(output) << std::endl;
};

do_diff("green - blue:", green, blue);
do_diff("blue - green:", blue, green);

完整演示

Live On Coliru

#include <iostream>
#include <fstream>

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

#include <boost/foreach.hpp>

namespace bg = boost::geometry; 

int main() {
    typedef bg::model::d2::point_xy<double> point;
    typedef bg::model::polygon<point> polygon;
    typedef bg::model::multi_polygon<polygon> multi;

    polygon green, blue;

    bg::read_wkt("POLYGON((0 0 100 0 100 100 0 100))", green);
    bg::read_wkt("POLYGON((25 25 75 22 75 75 25 75))", blue);

    auto test = [](polygon& geometry) {
        std::string reason;
        if (!bg::is_valid(geometry, reason))
        {
            std::cout << "Correcting " << bg::wkt(geometry) << " to ";
            bg::correct(geometry);
            std::cout << bg::wkt(geometry) << "\n";

            if (!bg::is_valid(geometry, reason))
                std::cout << "UNCORRECTIBLE: " << reason << "\n";
        }
    };

    test(green);
    test(blue);

    auto do_diff = [](std::string caption, polygon const& a, polygon const& b) {
        multi output;
        bg::difference(a, b, output);

        std::cout << caption << bg::wkt(output) << std::endl;
    };

    do_diff("green - blue:", green, blue);
    do_diff("blue - green:", blue, green);

    {
        std::ofstream svg("output.svg");
        boost::geometry::svg_mapper<point> mapper(svg, 400, 400);
        mapper.add(blue);
        mapper.add(green);

        mapper.map(blue, "fill-opacity:0.5;fill:rgb(0,0,153);stroke:rgb(0,0,200);stroke-width:2");
        mapper.map(green, "fill-opacity:0.5;fill:rgb(0,153,0);stroke:rgb(0,200,0);stroke-width:2");
    }
}

版画

Correcting POLYGON((0 0,100 0,100 100,0 100,0 0)) to POLYGON((0 0,0 100,100 100,100 0,0 0))
Correcting POLYGON((25 25,75 22,75 75,25 75,25 25)) to POLYGON((25 25,25 75,75 75,75 22,25 25))
green - blue:MULTIPOLYGON(((0 0,0 100,100 100,100 0,0 0),(25 25,75 22,75 75,25 75,25 25)))
blue - green:MULTIPOLYGON()

并为以下内容编写 svg:

The will end your frustration/confusion, this answer will make you more productive :)

而且,是的,您可以跳过差异函数:

bg::read_wkt("POLYGON((0 0 0 100 100 100 100 0 0 0) (25 25 75 22 75 75 25 75 25 25))", donut);

这指定了内圈(反方向!)。

结果:

由此程序生成:

Live On Coliru

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

#include <iostream>
#include <fstream>

namespace bg = boost::geometry; 

int main() {
    typedef bg::model::d2::point_xy<double> point;
    typedef bg::model::polygon<point> polygon;

    polygon donut;
    bg::read_wkt("POLYGON((0 0 0 100 100 100 100 0 0 0) (25 25 75 22 75 75 25 75 25 25))", donut);

    std::ofstream svg("output.svg");
    boost::geometry::svg_mapper<point> mapper(svg, 400, 400);
    mapper.add(donut);

    mapper.map(donut, "fill-opacity:0.5;fill:rgb(0,0,153);stroke:rgb(0,0,200);stroke-width:2");
}