使用Boost Geometry进行多边形缓冲时,结果不佳或不正确

3
我正在使用Boost :: Geometry :: Buffer创建不规则多边形的内部偏移或膨胀。下面的图像显示了一个示例输入和输出。原始多边形以白色显示,偏移多边形以紫色显示。在紫色多边形的右侧有两组多余的线(作为较粗 / 更亮的区域看到),左侧有一根长长的多余尖刺。

Example output from Boost::Geometry::Buffer

示例中使用的多边形非常基本。它缺乏任何对称性,但没有尖锐的转角或锯齿状边缘。输入多边形的原始数据是这些笛卡尔坐标点的列表:

x: 61.2101898, y: 81.9854202
x: 61.3715706, y: 82.0616913
x: 61.4335442, y: 82.1924744
x: 61.4778328, y: 82.2606735
x: 61.5202942, y: 82.3236465
x: 61.5283432, y: 82.3527832
x: 61.5431557, y: 82.4063950
x: 61.5221367, y: 82.4381790
x: 61.3944855, y: 82.4706116
x: 61.3497124, y: 82.4679184
x: 61.3284111, y: 82.4674301
x: 61.1539803, y: 82.3401947
x: 61.1297760, y: 82.2854843
x: 61.0671043, y: 82.1489639
x: 61.0682831, y: 82.0264740
x: 61.0667953, y: 82.0112915
x: 61.0663414, y: 82.0066376
x: 61.0707321, y: 81.9976196
x: 61.0998306, y: 81.9980850
x: 61.2101898, y: 81.9854202

这是我用来生成偏移多边形的代码:
namespace bg = boost::geometry;
typedef bg::model::d2::point_xy<float> BoostPoint;
typedef bg::model::polygon<BoostPoint> BoostPolygon;
typedef bg::model::multi_polygon<BoostPolygon> BoostMultipolygon;

std::vector<BoostPoint> points;
BoostPoint tmpPoint;
BoostPolygon input;
BoostMultipolygon output;

/* currentContour is a pointer to a non-Boost specialized polygon
*  structure. It contains a bool indicating clockwise/counterclockwise
*  direction and a list of lines, each line defined by two x-y points.
*  For each line, point 2 follows point 1 in the clockwise/counterclockwise
*  direction of that polygon.
*/

if (currentContour->clockwise) {
    for (int line = 0; line < currentContour->lines.size(); line++) {
        bg::set<0>(tmpPoint, currentContour->lines[line].x1);
        bg::set<1>(tmpPoint, currentContour->lines[line].y1);
        points.push_back(tmpPoint);
    }
    // Add last point to wrap back around to starting point.
    bg::set<0>(tmpPoint, currentContour->lines.back().x2);
    bg::set<1>(tmpPoint, currentContour->lines.back().y2);
    points.push_back(tmpPoint);
}
else {
    for (int line = currentContour->lines.size() - 1; line >= 0; line--) {
        bg::set<0>(tmpPoint, currentContour->lines[line].x2);
        bg::set<1>(tmpPoint, currentContour->lines[line].y2);
        points.push_back(tmpPoint);
    }
    // Add last point to wrap back around to starting point.
    bg::set<0>(tmpPoint, currentContour->lines.front().x1);
    bg::set<1>(tmpPoint, currentContour->lines.front().y1);
    points.push_back(tmpPoint);
}

// Transfer points to polygon object.
bg::assign_points(input, points);
// Declare boost strategies for buffer function.
bg::strategy::buffer::distance_symmetric<double> distance_strategy(-0.05);
bg::strategy::buffer::join_miter join_strategy;
bg::strategy::buffer::end_round end_strategy;
bg::strategy::buffer::point_circle point_strategy;
bg::strategy::buffer::side_straight side_strategy;
// Perform polygon buffering.
bg::buffer(input, output, distance_strategy, side_strategy, join_strategy,
    end_strategy, point_strategy);

Boost是一个很有声望的库,所以我很难相信它的几何API会在一个如此简单的多边形上失败。为什么会出现这些无关的线条?如果需要任何其他信息,我很乐意提供。

1
原始数据“如果有用的话”。什么?那是最有用的部分。 - sehe
2
天啊,谁在建设性地表达意见呢?还是有点紧张了。很抱歉我浪费了时间。我猜? - sehe
1
@ChrisD 如果你知道问题是什么,为什么还要在这里问呢? - Bartek Banachewicz
我并不是在挑剔那个条款。我只是指出,既然你有一个特定的问题,那么这个特定的问题才是真正重要的。如果你没有任何解释,而是提出一个完整的自包含的代码问题,那就更好了。顺便说一句,这也是编写好问题的指南。即使你认为问题出在“可能误解库”,我可以告诉你,在这个网站上,可能只有两个人知道。他们可能每个月只访问一次该网站。没有必要把你的赌注压在一个偶发事件上。 - sehe
很多人都愿意分享他们的精力来帮助解决问题,我也是其中之一。在你对我所说的话做出判断时,我想知道你是如何知道我的动机的。我不认为我知道你的动机。这里有一个幕后故事,可能会给你一些视角。我希望这能澄清一下那种被认为是傲慢和轻蔑的态度。无论是否使用不同的连接策略,我真心希望我的回答能够有所帮助。 - sehe
3个回答

3
我们无法确定原因,因为您没有包含源数据。您的"currentContour"可能包含任何内容。
相反,使用您幸运地提供的原始数据,我从WKT读取了多边形:
boost::geometry::read_wkt("POLYGON((61.2101898 81.9854202, 61.3715706 82.0616913, 61.4335442 82.1924744, 61.4778328 82.2606735, 61.5202942 82.3236465, 61.5283432 82.3527832, 61.5431557 82.4063950, 61.5221367 82.4381790, 61.3944855 82.4706116, 61.3497124 82.4679184, 61.3284111 82.4674301, 61.1539803 82.3401947, 61.1297760 82.2854843, 61.0671043 82.1489639, 61.0682831 82.0264740, 61.0667953 82.0112915, 61.0663414 82.0066376, 61.0707321 81.9976196, 61.0998306 81.9980850, 61.2101898 81.9854202))", input);

验证失败,因为方向错误:

我无法确定您的方向是否由顺时针标志正确管理,请按以下方式检查:

{
    std::string reason;
    if (!bg::is_valid(input, reason))
        std::cout << "Input is not valid: " << reason << "\n";
}

如果您需要修复任何可修复的错误:

bg::correct(input);

之后我得到了一个干净的缓冲区,但我看到有一个突起。由于不熟悉所有buffer选项,我“随机”地将join_miter更改为join_round,问题解决了:

在 Wandbox 上查看

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

namespace bg = boost::geometry;
typedef bg::model::d2::point_xy<float> BoostPoint;
typedef bg::model::polygon<BoostPoint> BoostPolygon;
typedef bg::model::multi_polygon<BoostPolygon> BoostMultipolygon;

int main() {
    BoostPolygon input;
    BoostMultipolygon output;

    boost::geometry::read_wkt("POLYGON((61.2101898 81.9854202, 61.3715706 82.0616913, 61.4335442 82.1924744, 61.4778328 82.2606735, 61.5202942 82.3236465, 61.5283432 82.3527832, 61.5431557 82.4063950, 61.5221367 82.4381790, 61.3944855 82.4706116, 61.3497124 82.4679184, 61.3284111 82.4674301, 61.1539803 82.3401947, 61.1297760 82.2854843, 61.0671043 82.1489639, 61.0682831 82.0264740, 61.0667953 82.0112915, 61.0663414 82.0066376, 61.0707321 81.9976196, 61.0998306 81.9980850, 61.2101898 81.9854202))", input);
    {
        std::string reason;
        if (!bg::is_valid(input, reason))
            std::cout << "Input is not valid: " << reason << "\n";
    }
    bg::correct(input);
    {
        std::string reason;
        if (!bg::is_valid(input, reason))
            std::cout << "Input is not valid: " << reason << "\n";
        else
            std::cout << "Input is valid";
    }

    // Declare boost strategies for buffer function.
    bg::strategy::buffer::distance_symmetric<double> distance_strategy(-0.05);
    bg::strategy::buffer::join_round join_strategy;
    bg::strategy::buffer::end_round end_strategy;
    bg::strategy::buffer::point_circle point_strategy;
    bg::strategy::buffer::side_straight side_strategy;
    // Perform polygon buffering.
    bg::buffer(input, output, distance_strategy, side_strategy, join_strategy, end_strategy, point_strategy);

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

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

enter image description here


感谢回复。我需要弄清楚我的方向概念与Boost的区别在哪里。不幸的是,我的用例需要斜接端点。有人知道为什么斜接端点无法正常工作吗? - Chris D
我不知道。也许你可以提出一个简化、有针对性的问题。我建议你在图书馆邮件列表上发布,因为开发人员在那里非常活跃(他们偶尔也会在SO上贡献)。 - sehe

1

0
我无法在Boost中实现斜切端点。我转向了Clipper Library,它可以轻松处理斜切端点。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接