Geometry, intersection - c++

I have a problem. I would like to intersect a quad with a quad.
int main(){
typedef boost::geometry::model::point_xy<double> TBoostPoint;
typedef boost::geometry::model::polygon<TBoostPoint> TBoostPoly;
TBoostPoint point;
TBoostPoly firstPoly, secondPoly;
boost::geometry::read_wkt("POLYGON(
(1.504477611940313, 3.761194029850755),
(1.504477611940305, 3.573134328358203),
(1.316417910447765, 3.573134328358206),
(1.316417910447769, 3.761194029850752))", firstPoly);
boost::geometry::read_wkt("POLYGON(
(1.504477611940313, 3.761194029850755),
(1.504477611940305, 3.573134328358203),
(1.316417910447765, 3.573134328358206),
(1.316417910447751, 3.761194029850769))", secondPoly);
std::vector<TBoostPoly> outPoly;
boost::geometry::intersection(firstPoly,secondPoly,outPoly);
}
outPoly - is empty, but it not so.

There were 2 main issues.
The output is undefined because the input is invalid.
The input WKT specifies a lot of invalid inner rings (consisting of single points), instead of what you expected, a single outer ring of 5 points (excl. closing point). Fix it:
bg::read_wkt("POLYGON(( 1.504477611940313 3.761194029850755, 1.504477611940305 3.573134328358203, 1.316417910447765 3.573134328358206, 1.316417910447769 3.761194029850752))", first);
bg::read_wkt("POLYGON(( 1.504477611940313 3.761194029850755, 1.504477611940305 3.573134328358203, 1.316417910447765 3.573134328358206, 1.316417910447751 3.761194029850769))", second);
Boost Geometry assumes throughout that you never make errors against the documented preconditions. If you read in the polygon concept page and preconditions for intersection you'll see thew full list¹.
If you don't, you get no friendly errors, just silent failure, corruption or just wrong answers. Yeah. That's bad.
What's worse, BGeo didn't even have a is_valid facility to test the bulk of requirements until Boost 1_57 (IIRC). The good news is, if you upgrade to this version or later your life will be much simpler.
In this case you would have learned that the polygons weren't properly closed:
Live On Coliru
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>
#include <boost/geometry/io/io.hpp>
#include <boost/geometry/algorithms/intersection.hpp>
#include <boost/geometry/algorithms/correct.hpp>
#include <boost/geometry/algorithms/is_valid.hpp>
namespace bg = boost::geometry;
int main(){
typedef bg::model::d2::point_xy<double> TPoint;
typedef bg::model::polygon<TPoint> TPoly;
TPoly first, second;
bg::read_wkt("POLYGON(( 1.504477611940313 3.761194029850755, 1.504477611940305 3.573134328358203, 1.316417910447765 3.573134328358206, 1.316417910447769 3.761194029850752))", first);
bg::read_wkt("POLYGON(( 1.504477611940313 3.761194029850755, 1.504477611940305 3.573134328358203, 1.316417910447765 3.573134328358206, 1.316417910447751 3.761194029850769))", second);
std::string reason;
// polys not closed!
if (!bg::is_valid(first, reason)) std::cout << "First polygon not valid: " << reason << "\n";
if (!bg::is_valid(second, reason)) std::cout << "Second polygon not valid: " << reason << "\n";
bg::correct(first);
bg::correct(second);
// no more output!
if (!bg::is_valid(first, reason)) std::cout << "First polygon not valid: " << reason << "\n";
if (!bg::is_valid(second, reason)) std::cout << "Second polygon not valid: " << reason << "\n";
std::vector<TPoly> out;
bg::intersection(first, second, out);
for (auto& g : out)
std::cout << "\nresult: " << bg::wkt(g) << "\n";
}
Prints:
First polygon not valid: Geometry is defined as closed but is open
Second polygon not valid: Geometry is defined as closed but is open
Oops. The geos weren't closed! correct(poly) fixes this for us on auto-pilot:
result: POLYGON((1.50448 3.57313,1.31642 3.57313,1.31642 3.76119,1.50448 3.76119,1.50448 3.57313))
¹ outer ring must be counter clockwise, inner cw, polys must be closed... stuff like that.

Related

Cgal - Point_set_3 object, functions fails to process?

Currently trying to build upon the surface reconstruction tutorial and noticed a potential major issue in the tutorial which, in my experience do generalised outside of it:
In the following tutorial: https://doc.cgal.org/latest/Manual/tuto_reconstruction.html (cgal 5.3), the author do some pre-processing before going into the mesh reconstruction, stuffs like outlier_removal, grid_simplify etc.
However I noticed that no points are being removed during these steps. So I tried multiple parameters in the outlier_removal/grid_simplify and still, everytime, no points gets removed.
However when working with a vector of point instead of a Point_set_3 object, I do manage to get points removed with the same parameters.
Am I the only one who is unable to remove a point with outlier_removal/grid_simplify on a Point_set_3 object?
If yes, can you show me an example how to make it work?
If no, should I avoid using Point_set_3 objects? Or should I convert into a std::vector before doing the pre-processing steps? And how so?
Issue Details
The code runs fine. No errors.
Source Code
This code subset comes straight out of the tutorial.
https://doc.cgal.org/latest/Manual/tuto_reconstruction.html
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Point_set_3.h>
#include <CGAL/Point_set_3/IO.h>
#include <CGAL/remove_outliers.h>
#include <CGAL/grid_simplify_point_set.h>
#include <CGAL/jet_smooth_point_set.h>
#include <CGAL/jet_estimate_normals.h>
#include <CGAL/mst_orient_normals.h>
#include <CGAL/poisson_surface_reconstruction.h>
#include <CGAL/Advancing_front_surface_reconstruction.h>
#include <CGAL/Scale_space_surface_reconstruction_3.h>
#include <CGAL/Scale_space_reconstruction_3/Jet_smoother.h>
#include <CGAL/Scale_space_reconstruction_3/Advancing_front_mesher.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include <cstdlib>
#include <vector>
#include <fstream>
// types
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::FT FT;
typedef Kernel::Point_3 Point_3;
typedef Kernel::Vector_3 Vector_3;
typedef Kernel::Sphere_3 Sphere_3;
typedef CGAL::Point_set_3<Point_3, Vector_3> Point_set;
int main(int argc, char*argv[])
{
Point_set points;
if (argc < 2)
{
std::cerr << "Usage: " << argv[0] << " [input.xyz/off/ply/las]" << std::endl;
return EXIT_FAILURE;
}
const char* input_file = argv[1];
std::ifstream stream (input_file, std::ios_base::binary);
if (!stream)
{
std::cerr << "Error: cannot read file " << input_file << std::endl;
return EXIT_FAILURE;
}
stream >> points;
std::cout << "Read " << points.size () << " point(s)" << std::endl;
if (points.empty())
return EXIT_FAILURE;
CGAL::remove_outliers<CGAL::Sequential_tag>
(points,
24, // Number of neighbors considered for evaluation
points.parameters().threshold_percent (5.0)); // Percentage of points to remove
std::cout << points.number_of_removed_points()
<< " point(s) are outliers." << std::endl;
// Applying point set processing algorithm to a CGAL::Point_set_3
// object does not erase the points from memory but place them in
// the garbage of the object: memory can be freeed by the user.
points.collect_garbage();
// Compute average spacing using neighborhood of 6 points
double spacing = CGAL::compute_average_spacing<CGAL::Sequential_tag> (points, 6);
// Simplify using a grid of size 2 * average spacing
CGAL::grid_simplify_point_set (points, 2. * spacing);
std::cout << points.number_of_removed_points()
<< " point(s) removed after simplification." << std::endl;
points.collect_garbage();
CGAL::jet_smooth_point_set<CGAL::Sequential_tag> (points, 24);
unsigned int reconstruction_choice
= (argc < 3 ? 0 : atoi(argv[2]));
if (reconstruction_choice == 0) // Poisson
{
CGAL::jet_estimate_normals<CGAL::Sequential_tag>
(points, 24); // Use 24 neighbors
// Orientation of normals, returns iterator to first unoriented point
typename Point_set::iterator unoriented_points_begin =
CGAL::mst_orient_normals(points, 24); // Use 24 neighbors
points.remove (unoriented_points_begin, points.end());
return EXIT_SUCCESS;
}
Environment
I've replicated that issue in a debian VM as well as in a docker environment in macos (debian based as well).
Pretty standard stuffs, I'm using the CMakeLists.txt already available in the tutorial_example.cpp folder and running:
Creates files that will show the compiler how to behave
cmake -DCGAL_DIR=/app/cgal -DCMAKE_BUILD_TYPE=Release .
Build the exe
make
I'm a self taught Python programmer so quite new to the C++ stuffs.

Boost convex hull with longitude and latitude

I am trying to use boost's convex_hull algorithm with longitude/latitude coordinates.
From here: https://archive.fosdem.org/2017/schedule/event/geo_boost_geography/attachments/slides/1748/export/events/attachments/geo_boost_geography/slides/1748/FOSDEM17_vissarion.pdf
I can see that we can calculate the distance between two points and even find the area using longitude/latitude coordinates (see page 19 and 22 of the PDF document).
Combining that with https://www.boost.org/doc/libs/1_75_0/libs/geometry/doc/html/geometry/reference/algorithms/convex_hull.html
I came up with this: https://wandbox.org/permlink/2AGPUtHPWrlGFMTf, but it does not compile, code here for convenience:
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>
namespace bg = boost::geometry;
int main()
{
typedef bg::model::point<double, 2, bg::cs::geographic<bg::degree>> point;
typedef boost::geometry::model::polygon<point> polygon;
polygon poly;
bg::read_wkt(" POLYGON ((4.346693 50.858306, 4.367945 50.852455, 4.366227 50.840809, 4.344961 50.833264, 4.338074 50.848677,4.346693 50.858306))",
poly );
polygon hull;
boost::geometry::convex_hull(poly, hull);
using boost::geometry::dsv;
std::cout << "polygon: " << dsv(poly) << std::endl << "hull: " << dsv(hull) << std::endl;
}
Any help is much appreciated.
Yeah, though you may be right that a strategy /can/ be made, that is not implemented.
A little side-by-side tester clearly shows that the strategy is not implemented for the geographical coordinate system:
template <typename cs> void test() {
using point = bg::model::point<double, 2, cs>;
using polygon = bg::model::polygon<point>;
polygon poly;
bg::read_wkt("POLYGON((4.346693 50.858306, 4.367945 50.852455, 4.366227 "
"50.840809, 4.344961 50.833264, 4.338074 50.848677,4.346693 "
"50.858306))",
poly);
std::cout << std::fixed;
std::cout << "Polygon: " << bg::dsv(poly) << std::endl;
std::cout << "Perimeter: " << bg::perimeter(poly) << std::endl;
std::cout << "Area: " << bg::area(poly) << std::endl;
using Strategy = typename bg::strategy_convex_hull<polygon, point>::type;
std::cout << "Strategy " << boost::core::demangle(typeid(Strategy).name()) << "\n";
if constexpr (not std::is_same_v<Strategy, bg::strategy::not_implemented>) {
polygon hull;
bg::convex_hull(poly, hull);
std::cout << "Hull: " << bg::dsv(hull) << std::endl;
}
}
It also demonstrates that some other strategies are implemented (e.g. distance).
See it Live On Coliru
int main() {
std::cout << "Cartesian:\n";
std::cout << "----------\n";
test<bg::cs::cartesian>();
std::cout << "\nGeographic:\n";
std::cout << "-----------\n";
test<bg::cs::geographic<bg::degree>>();
}
Simplifying the the typenames in the output:
Cartesian:
----------
Polygon: (((4.346693, 50.858306), (4.367945, 50.852455), (4.366227, 50.840809), (4.344961, 50.833264), (4.338074, 50.848677), (4.346693, 50.858306)))
Perimeter: 0.086184
Area: 0.000488
Strategy bg::strategy::convex_hull::graham_andrew<polygon, point>
Hull: (((4.338074, 50.848677), (4.346693, 50.858306), (4.367945, 50.852455), (4.366227, 50.840809), (4.344961, 50.833264), (4.338074, 50.848677)))
Geographic:
-----------
Polygon: (((4.346693, 50.858306), (4.367945, 50.852455), (4.366227, 50.840809), (4.344961, 50.833264), (4.338074, 50.848677), (4.346693, 50.858306)))
Perimeter: 7663.398262
Area: 3848183.734567
Strategy bg::strategy::not_implemented
A look at the documented strategies suggests that graham_andrew is in fact the only one available.
You should probably find out what tweaks are required to get things to work. it is technically possible to force convex_hull to use the Graham/Andrew strategy, but that seems ill-advised as the trait implies that the strategy is specifically deselected based on the coordinate system:
/*!
\brief Traits class binding a convex hull calculation strategy to a coordinate system
\ingroup convex_hull
\tparam Tag tag of coordinate system
\tparam Geometry the geometry type (hull operates internally per hull over geometry)
\tparam Point point-type of output points
*/
template
<
typename Geometry1,
typename Point,
typename CsTag = typename cs_tag<Point>::type
>
struct strategy_convex_hull
{
typedef strategy::not_implemented type;
};
Digging into the implementation of the strategy here's a hopeful hint:
// TODO: User-defiend CS-specific side strategy
typename strategy::side::services::default_strategy<cs_tag>::type side;
Perhaps we could be "done" with "just" specializing the Side Strategy for your coordinate systems? And more interestingly: a strategy::side::geographic exists. I'm out of my depth understanding the parameters (e.g. what the geodetic solution policy means?), but maybe yourself can take it from there?
I'm convinced that if you know what needs to be done, the helpful devs over at the mailing list will be very willing to guide the technical questions on how to fit it into the library.

Boost Polygon: Issue with euclidean_distance

I have the following code which is supposed to compute the Euclidean distance between two rectangles. I compiled using GCC 4.7.3 and Boost v1.58.0
#include <iostream>
#include <cmath>
#include <boost/polygon/polygon.hpp>
#include <boost/geometry.hpp>
namespace gtl = boost::polygon;
using namespace boost::polygon::operators;
typedef gtl::rectangle_data<int> LayoutRectangle;
int main(int argc, char** argv)
{
LayoutRectangle t(16740130,29759232,16740350,29760652);
LayoutRectangle n(16808130,29980632,16808350,29982052);
std::cout << gtl::euclidean_distance(t, n) << std::endl;
std::cout << gtl::euclidean_distance(t, n, gtl::HORIZONTAL) << " "
<< gtl::euclidean_distance(t, n, gtl::VERTICAL) << std::endl;
std::cout << gtl::square_euclidean_distance(t, n) << std::endl;
std::cout << std::sqrt(gtl::square_euclidean_distance(t, n)) << std::endl;
std::cout << (int) std::sqrt(gtl::square_euclidean_distance(t, n)) << std::endl;
return 0;
}
The code above produced the following output:
38022.6
67780 219980
52985328800
230185
230185
The correct answer is 230185. Now if I go look at the implementation of euclidean_distance() in the boost polygon library, I see this:
template <typename rectangle_type, typename rectangle_type_2>
typename enable_if< typename gtl_and_3<y_r_edist2, typename is_rectangle_concept<typename geometry_concept<rectangle_type>::type>::type,
typename is_rectangle_concept<typename geometry_concept<rectangle_type_2>::type>::type>::type,
typename rectangle_distance_type<rectangle_type>::type>::type
euclidean_distance(const rectangle_type& lvalue, const rectangle_type_2& rvalue) {
double val = (int)square_euclidean_distance(lvalue, rvalue);
return std::sqrt(val);
}
This looks identical to the std::sqrt(gtl::square_eclidean_distance(t,n)) line in my code which gives the correct answer (230185). So why am I getting 38022.6 with gtl::euclidean_distance()? What am I not seeing here?
Looks like the internal computation is overflowing.
I don't think this is a library bug, the library is used incorrectly with the underlying (unchecked) int type.
(However, there is a different bug in the library that I mention at the end.)
Try using a smaller "integer representation" of the problem:
For example:
LayoutRectangle t(167402,297592,167404,297607);
LayoutRectangle n(168082,299806,168084,299821);
Unfortunately there is no general solution of the problem in integer arithmetic, except 0) using higher precision can buy you something, 1) scaling the problem 2) using multiprecision, 3) using rational arithmetic and integer part
(For floating point the solution is simply normalizing the components, this is how std::abs for std::complex<double> works to avoid floating point overflow)
It is good to use large integers to represent a geometric problem BUT
for this reason, as a workaround, use coordinates that span distance of at most (int)std::sqrt((double)std::numeric_limits<int>::max()/2) = 2^15 = 32768.
Which is a surprisingly small number.
Complete code:
#include <iostream>
#include <cmath>
#include <boost/polygon/polygon.hpp>
#include <boost/geometry.hpp>
int main(){
namespace gtl = boost::polygon;
using namespace boost::polygon::operators;
typedef gtl::rectangle_data<int> LayoutRectangle;
LayoutRectangle t(167401,297592,167403,297606);
LayoutRectangle n(168081,299806,168083,299820);
std::cout << gtl::euclidean_distance(t, n) << std::endl;
std::cout << gtl::euclidean_distance(t, n, gtl::HORIZONTAL) << " "
<< gtl::euclidean_distance(t, n, gtl::VERTICAL) << std::endl;
std::cout << gtl::square_euclidean_distance(t, n) << std::endl;
std::cout << std::sqrt(gtl::square_euclidean_distance(t, n)) << std::endl;
std::cout << (int) std::sqrt(gtl::square_euclidean_distance(t, n)) << std::endl;
}
Output:
2302.1
678 2200
5299684
2302.1
2302
Which is the expected result.
Looking at the code, it seems that there is a bug in the library, not because it gives overflow but because an internal computation is casted to int and not the the underlying generic integer data type. This means that probably even if you use multiprecision integers the results will overflow.

Does it make sense to apply make_move_iterator together with vector::insert ?(C++)

I have a vector of vectors, and I want to connect them one by one to form a long vector. This could be done by inserting at the end. Inspired by this question, I was thinking that using make_move_iterator would replace copy with move and thus would be more efficient. But the following test demonstrates that make_move_iterator will cause a larger time consumption.
#include <iostream>
#include <string>
#include <vector>
#include <chrono>
using namespace std;
int main()
{
string a = "veryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylongstring";
vector<string> b(10,a);
vector<vector<string> > c(1000,b);
vector<string> d,e;
auto t1 = chrono::system_clock::now();
for(auto& item : c)
{
d.insert(d.end(),item.begin(),item.end());
}
cout << c[0][0].length() << endl;
auto t2 = chrono::system_clock::now();
for(auto& item:c)
{
e.insert(e.end(), std::make_move_iterator(item.begin()),std::make_move_iterator(item.end()));
}
auto t3 = chrono::system_clock::now();
cout << chrono::duration_cast<chrono::nanoseconds>(t2-t1).count() << endl;
cout << chrono::duration_cast<chrono::nanoseconds>(t3-t2).count() << endl;
cout << c[0][0].length() << endl;
cout << "To check that c has been moved from." <<endl;
}
//Output:
//122
//1212000
//1630000
//0
//To check that c has been moved from.
Thus I'm wondering, does this approach really help improve efficiency?
The test in the question description was conducted on cpp shell
I later tried on ideone and it turned out that make_move_iterator is obviously more efficient. So it seems to be a compiler-dependent thing.
122
320576
98434
0
To check that c has been moved from.

Boost geometry cartesian2d.hpp [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I tried to do this clipping example using Boost Geometry in the example here
But in the newest Boost library I can't find ...
boost/geometry/geometries/cartesian2d.hpp
Does anyone have any idea where I might find this file? Or is this tutorial too old, and the file now obsolete?
The tutorial is too old. Much has changed since that tutorial was written.
Anyhow, I've modified the code in your link above so that it compiles in Boost ver 1.53.0 ...
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/io/wkt/wkt.hpp>
#include <boost/geometry/multi/geometries/multi_polygon.hpp>
using namespace boost::geometry;
typedef model::d2::point_xy<double> point;
typedef model::ring< point > ring;
typedef model::polygon< point > polygon;
typedef model::multi_polygon< polygon > polygons;
typedef model::box< point > box;
// Define a polygon and fill the outer ring.
// In most cases you will read it from a file or database
polygon poly;
{
read_wkt("POLYGON((2.0 1.3, 2.4 1.7, 2.8 1.8, 3.4 1.2, 3.7 1.6, \
3.4 2.0, 4.1 3.0, 5.3 2.6, 5.4 1.2, 4.9 0.8, 2.9 0.7, \
2.0 1.3))", poly);
}
// Polygons should be closed, and directed clockwise. If you're not sure if that is the case,
// call the correct algorithm
correct(poly);
// Polygons can be streamed as text
// (or more precisely: as DSV (delimiter separated values))
std::cout << dsv(poly) << std::endl;
// As with lines, bounding box of polygons can be calculated
box b;
envelope(poly, b);
std::cout << dsv(b) << std::endl;
// The area of the polygon can be calulated
std::cout << "area: " << area(poly) << std::endl;
// And the centroid, which is the center of gravity
point cent;
centroid(poly, cent);
std::cout << "centroid: " << dsv(cent) << std::endl;
// The number of points have to called per ring separately
std::cout << "number of points in outer ring: " << poly.outer().size() << std::endl;
// Polygons can have one or more inner rings, also called holes, donuts, islands, interior rings.
// Let's add one
{
poly.inners().resize(1);
ring& inner = poly.inners().back();
read_wkt("POLYGON((4.0 2.0, 4.2 1.4, 4.8 1.9, 4.4 2.2, 4.0 2.0))", inner);
}
correct(poly);
std::cout << "with inner ring:" << dsv(poly) << std::endl;
// The area of the polygon is changed of course
std::cout << "new area of polygon: " << area(poly) << std::endl;
centroid(poly, cent);
std::cout << "new centroid: " << dsv(cent) << std::endl;
// You can test whether points are within a polygon
std::cout << "point in polygon:"
<< " p1: " << (within(make<point>(3.0, 2.0), poly)?"true":"false")
<< " p2: " << (within(make<point>(3.7, 2.0), poly)?"true":"false")
<< " p3: " << (within(make<point>(4.4, 2.0), poly)?"true":"false")
<< std::endl;
// As with linestrings and points, you can derive from polygon to add, for example,
// fill color and stroke color. Or SRID (spatial reference ID). Or Z-value. Or a property map.
// We don't show this here.
// Clip the polygon using a bounding box
box cb(make<point>(1.5, 1.5), make<point>(4.5, 2.5));
polygons v;
intersection(cb, poly, v);
std::cout << "Clipped output polygons" << std::endl;
for (polygons::const_iterator it = v.begin(); it != v.end(); ++it)
{
std::cout << dsv(*it) << std::endl;
}
union_(cb, poly, v);
polygon hull;
convex_hull(poly, hull);
std::cout << "Convex hull:" << dsv(hull) << std::endl;
// If you really want:
// You don't have to use a vector, you can define a polygon with a deque
// You can specify the container for the points and for the inner rings independently
typedef model::polygon<point, true, true, std::vector, std::deque> polygon_deq;
polygon_deq poly2;
polygon_deq::ring_type& r = poly2.outer();
append(r, make<point>(2.8, 1.9));
append(r, make<point>(2.9, 2.4));
append(r, make<point>(3.3, 2.2));
append(r, make<point>(3.2, 1.8));
append(r, make<point>(2.8, 1.9));
std::cout << dsv(poly2) << std::endl;