Плохие или неправильные результаты при использовании Boost Geometry для буферизации полигонов
Я использую 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-интерфейсы не смогут работать с таким простым полигоном. Почему я получаю эти посторонние линии? Если какая-либо дополнительная информация будет полезна, я буду рад предоставить ее.
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
и это ушло
#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 :: geometry :: strategy :: buffer :: join_miter имел ошибку, которая вызывала такое поведение до версии 1.71. Обновление ускорения должно решить эту проблему.
Соответствующая проблема GitHub: https://github.com/boostorg/geometry/issues/596
Я не мог сводить концы с ума, работая с Boost. Я вместо этого переключился на Библиотеку Clipper, которая обработала срезанные концы без заминки.