使用 Boost Geometry 进行多边形缓冲时结果不佳或不正确
Poor or incorrect results when using Boost Geometry for polygon buffering
我正在使用 Boost::Geometry::Buffer 为不规则形状的多边形创建内部偏移或膨胀。下图显示了示例输入和输出。原始多边形显示为白色,偏移多边形显示为紫色。紫色多边形右侧有两组无关的线(视为 thicker/brighter 区域),左侧有一个长的无关尖峰。
示例中使用的多边形非常基础。它缺乏任何对称性,但没有急转弯或锯齿状边缘。输入多边形的原始数据是这个笛卡尔点列表:
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 会在如此简单的多边形上失败。为什么我会收到那些无关的线路?如果有任何其他信息对您有帮助,我将很乐意提供。
我们无法判断,因为您没有包含源数据。您的 "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);
验证失败,因为方向错误:
I can't tell whether your orientation was correctly managed by the clockwise flag, so check it as follows:
{
std::string reason;
if (!bg::is_valid(input, reason))
std::cout << "Input is not valid: " << reason << "\n";
}
如果您需要修复任何可修复的错误:
bg::correct(input);
在那之后我得到了一个干净的缓冲区,但我看到了峰值。由于不熟悉 buffer
的所有选项,我 "randomly" 将 join_miter
更改为 join_round
然后它消失了:
#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 进行斜接。我改为切换到 Clipper Library,它可以顺利处理斜接端。
Boost::geometry::strategy::buffer::join_miter 在 1.71 版本之前有一个导致此行为的错误。更新 boost 应该可以解决这个问题。
相关GitHub问题:
https://github.com/boostorg/geometry/issues/596
我正在使用 Boost::Geometry::Buffer 为不规则形状的多边形创建内部偏移或膨胀。下图显示了示例输入和输出。原始多边形显示为白色,偏移多边形显示为紫色。紫色多边形右侧有两组无关的线(视为 thicker/brighter 区域),左侧有一个长的无关尖峰。
示例中使用的多边形非常基础。它缺乏任何对称性,但没有急转弯或锯齿状边缘。输入多边形的原始数据是这个笛卡尔点列表:
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 会在如此简单的多边形上失败。为什么我会收到那些无关的线路?如果有任何其他信息对您有帮助,我将很乐意提供。
我们无法判断,因为您没有包含源数据。您的 "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);
验证失败,因为方向错误:
I can't tell whether your orientation was correctly managed by the clockwise flag, so check it as follows:
{
std::string reason;
if (!bg::is_valid(input, reason))
std::cout << "Input is not valid: " << reason << "\n";
}
如果您需要修复任何可修复的错误:
bg::correct(input);
在那之后我得到了一个干净的缓冲区,但我看到了峰值。由于不熟悉 buffer
的所有选项,我 "randomly" 将 join_miter
更改为 join_round
然后它消失了:
#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 进行斜接。我改为切换到 Clipper Library,它可以顺利处理斜接端。
Boost::geometry::strategy::buffer::join_miter 在 1.71 版本之前有一个导致此行为的错误。更新 boost 应该可以解决这个问题。
相关GitHub问题: https://github.com/boostorg/geometry/issues/596