Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.80+ - Buffer out-then-in for round polygon produces multiple polygons with small "stubs" #1281

Open
AndrewLipscomb opened this issue May 13, 2024 · 3 comments
Assignees
Labels

Comments

@AndrewLipscomb
Copy link

AndrewLipscomb commented May 13, 2024

The following program takes its embedded WKT string, buffers it out by 9m, then back in my 9m.

The geometry itself isn't particularly odd, its a circle-ish blob.

Buffering the geometry out then in in previous versions of Boost geometry (circa 1.71) produced a single polygon.

1.80+ both now produce a small unexpected "stub" - see the picture. This one has 3 of these. This should be a fairly simple operation - can't see any clear reason why it would fail. I tested this with a few versions - 1.79 was the first that didn't do it.

#include <iostream>
#include <string>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/io/wkt/wkt.hpp>
#include <boost/geometry/geometries/adapted/boost_polygon.hpp>

namespace bg = boost::geometry;
typedef bg::model::d2::point_xy<double> Point;
typedef bg::model::polygon<Point, false, true> Polygon;
typedef bg::model::multi_polygon<Polygon> MultiPolygon;


template <typename Geometry>
bg::model::multi_polygon<Polygon> expand_geometry(const Geometry& geometry, const double buffer_distance,
	const unsigned int points_per_circle = 36)
{
	boost::geometry::strategy::buffer::distance_symmetric<double> distance_strategy(
		buffer_distance);
	boost::geometry::strategy::buffer::join_round join_strategy(points_per_circle);
	boost::geometry::strategy::buffer::end_round end_strategy(points_per_circle);
	boost::geometry::strategy::buffer::point_circle circle_strategy(
		points_per_circle);
	boost::geometry::strategy::buffer::side_straight side_strategy;

	MultiPolygon expanded_geometry;
	bg::buffer(geometry, expanded_geometry, distance_strategy, side_strategy, join_strategy, end_strategy,
		circle_strategy);
	bg::buffer(geometry, expanded_geometry, distance_strategy, side_strategy, join_strategy, end_strategy,
		circle_strategy);

	return expanded_geometry;
}


int main() {
    // Static WKT polygon string
    std::string wkt_polygon = "POLYGON((787548.79037 6761841.1758,787548.23202 6761841.051,787548.0522 6761841.0048,787546.73484 6761840.5551,787546.47923 6761840.4451,787545.25775 6761839.8044,787543.53468 6761838.7246,787543.02465 6761838.3801,787542.86219 6761838.2621,787541.72249 6761837.2787,787541.51191 6761837.0637,787540.91258 6761836.3871,787539.65998 6761834.8211,787539.14107 6761834.1025,787538.9803 6761833.8551,787538.53459 6761833.0889,787538.16641 6761832.2826,787538.0572 6761832.0084,787537.77012 6761831.1698,787537.56692 6761830.307,787537.5135 6761830.0168,787537.39616 6761829.1382,787537.06642 6761825.1988,787537.03615 6761824.3081,787537.04077 6761824.0115,787537.09873 6761823.1221,787537.24436 6761822.2429,787537.30728 6761821.953,787537.74056 6761820.5334,787537.81377 6761820.3494,787538.32492 6761819.2772,787538.42172 6761819.1044,787538.73666 6761818.5845,787539.88378 6761816.8276,787540.77081 6761815.6823,787540.9678 6761815.4664,787542.03777 6761814.4697,787542.19081 6761814.3488,787543.15431 6761813.6847,787543.32174 6761813.5847,787544.36327 6761813.0511,787544.54227 6761812.9737,787545.08683 6761812.7588,787548.77705 6761811.4397,787549.29059 6761811.2733,787549.46327 6761811.223,787550.51562 6761810.984,787550.69308 6761810.9548,787551.24349 6761810.8814,787553.08462 6761810.6937,787554.24014 6761810.6505,787554.43537 6761810.6558,787555.89091 6761810.8141,787556.17834 6761810.8694,787557.58894 6761811.2616,787557.77222 6761811.3291,787558.8432 6761811.8045,787559.01616 6761811.8952,787559.53303 6761812.1884,787562.50792 6761814.0082,787563.45014 6761814.671,787563.60083 6761814.7921,787564.45625 6761815.5757,787564.5901 6761815.7152,787565.33763 6761816.6022,787565.45243 6761816.7578,787566.07966 6761817.7336,787566.17349 6761817.9027,787566.44487 6761818.432,787567.24177 6761820.1218,787567.74219 6761821.4429,787567.822 6761821.7168,787568.11137 6761823.1143,787568.13506 6761823.3032,787568.20504 6761824.4432,787568.20462 6761824.6336,787568.18523 6761825.2046,787567.93912 6761828.9459,787567.87888 6761829.5499,787567.85204 6761829.7503,787567.61034 6761830.939,787567.5568 6761831.134,787567.15742 6761832.2795,787567.07814 6761832.4655,787566.52833 6761833.5469,787566.42476 6761833.7205,787566.0881 6761834.2431,787564.88687 6761835.9732,787563.92644 6761837.1351,787563.71363 6761837.3525,787562.56085 6761838.3465,787562.39641 6761838.4656,787561.36372 6761839.1118,787561.18471 6761839.2076,787560.08158 6761839.7057,787556.39673 6761841.0794,787555.51148 6761841.3583,787555.21492 6761841.4352,787553.70396 6761841.6921,787553.50023 6761841.7091,787552.27372 6761841.7274,787552.06957 6761841.7166,787550.86654 6761841.5707,787548.79037 6761841.1758))";

    // Declare polygon and buffer
    Polygon polygon;
    MultiPolygon buffered_polygon;

    // Parse WKT polygon
    bg::read_wkt(wkt_polygon, polygon);
	std::string error;
	if (!bg::is_valid(polygon, error))
	{
		std::cerr << error << std::endl;
		throw std::runtime_error(error);
	}

    auto buffered_a = expand_geometry(polygon, 9.0);
	auto buffered_b = expand_geometry(buffered_a, -9.0);

	std::cout << boost::geometry::exterior_ring(polygon).size() << std::endl;
	std::cout << buffered_a.size() << std::endl;
	std::cout << buffered_b.size() << std::endl;

	std::cout << boost::geometry::area(polygon) << std::endl;
	std::cout << boost::geometry::area(buffered_b) << std::endl;

    return 0;
}

image

For compiler info and invocation (via CMake)

/usr/bin/c++ -I/home/andrew.lipscomb/code/cpp_sandbox/include -isystem /home/andrew.lipscomb/code/boost_1_85_0_focal/include  -Wall -fcoroutines -std=gnu++2a -o CMakeFiles/cpp-sandbox.dir/src/bad_buffer.cpp.o -c /home/andrew.lipscomb/code/cpp_sandbox/src/bad_buffer.cpp
$ g++ --version
g++ (Ubuntu 10.5.0-1ubuntu1~20.04) 10.5.0
@AndrewLipscomb
Copy link
Author

AndrewLipscomb commented May 13, 2024

I've added BOOST_GEOMETRY_NO_ROBUSTNESS as a define for the system and its gone good now.

Far as I can see - thats the new recommended way to run the lib?

Correction - this did not solve the issue. I was accidentally using an old version when switching Boost versions in CMake.

1.85 and BOOST_GEOMETRY_NO_ROBUSTNESS don't change the result here.

@AndrewLipscomb AndrewLipscomb changed the title 1.83/1.85 - Buffer out-then-in for round polygon produces multiple polygons with small "stubs" 1.80+ - Buffer out-then-in for round polygon produces multiple polygons with small "stubs" May 13, 2024
@barendgehrels
Copy link
Collaborator

‘ BOOST_GEOMETRY_NO_ROBUSTNESS’ is deprecated and should not be used.

1.85 has improvemente so thanks for trying that version.

I will look soon.

@barendgehrels barendgehrels self-assigned this May 14, 2024
@barendgehrels
Copy link
Collaborator

Sorry for the delay.

Not fixed by my concept fix for #1295 and others. But it's another problem.

Added:

    for (const auto& sub : buffered_b)
    {
        std::cout << " -> " << bg::num_points(sub) << " " << bg::area(sub) << std::endl;
    }

Results:

104
1
4
765.094
764.882
 -> 139 764.882
 -> 4 9.82254e-07
 -> 4 9.96362e-07
 -> 4 1.01431e-06

So indeed there are three very small polygons, which are not removed because they are valid and having the minimum number of points.

As a workaround you could easily remove them. But it's indeed still classified as a bug and should be looked at.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants