【问题标题】:Poor or incorrect results when using Boost Geometry for polygon buffering使用 Boost Geometry 进行多边形缓冲时结果不佳或不正确
【发布时间】:2017-12-17 11:26:09
【问题描述】:

我正在使用 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 会在如此简单的多边形上失败。为什么我得到那些无关的行?如果任何其他信息对您有所帮助,我将很乐意提供。

【问题讨论】:

  • 原始数据“以防万一”。什么。这是最有用的一点。
  • 天啊。谁有建设性。或者跳动。我很抱歉我花了时间。我猜?
  • @ChrisD 如果你知道问题出在哪里,为什么还要在这里问?
  • 我没有吹毛求疵。我指出,由于您对特定案例有疑问,因此特定案例是唯一真正重要的事情。如果您没有任何解释和可靠的独立代码问题,我会更喜欢。顺便说一句,这是写好问题的指南。即使您认为问题出在“可能对图书馆的误解”,我也可以告诉您,该站点上可能有 2 人知道。他们可能每月访问该网站一次。无需在怪异事件上押注。
  • 许多人愿意分享他们的精力来帮助解决问题,包括我在内。在你对我所说的话的判断中,我想知道你是怎么知道我的动机的。我不认为我知道你的。这是一个peek behind the scenes that may give a little perspective。我希望这可以消除人们对傲慢和不屑一顾的感觉。我真的希望我的回答对您有所帮助,无论使用不同的连接策略是否有意义。

标签: c++ boost buffer polygon boost-geometry


【解决方案1】:

我们无法确定,因为您未能包含源数据。您的“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,然后它就消失了:

Live On 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");
    }
}

【讨论】:

  • 感谢您的回复。我必须弄清楚我的方向概念与 Boost 的概念有何不同。不幸的是,我的用例需要斜接。有谁知道为什么斜接端不起作用?
  • 我没有。也许您可以就此提出一个简化的、有针对性的问题。我会考虑发布到图书馆邮件列表,因为那里的开发人员非常活跃(他们也偶尔为 SO 做出贡献)
【解决方案2】:

我无法使用 Boost 实现斜接。我转而使用Clipper Library,它可以顺利处理斜接端。

【讨论】:

    【解决方案3】:

    Boost::geometry::strategy::buffer::join_miter 在 1.71 版本之前存在导致此行为的错误。更新 boost 应该可以解决这个问题。

    相关的 GitHub 问题: https://github.com/boostorg/geometry/issues/596

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-04
      • 2010-11-29
      相关资源
      最近更新 更多