How to get a polygon from boost::geometry::model::polygon? - c++

I'm trying to calculate a difference of two polygons using boost::geometry::difference with boost::geometry::model::polygon representing my polygons.
In case when the first polygon contains the second one result of the operation is a single boost::geometry::model::polygon with the inner and outer rings populated with coordinates of the source polygons.
How do I get a polygon (in the elementary geometry sense) from boost::geometry::model::polygon?
Clarification:
In elementary geometry, a polygon is a plane figure that is bounded by a finite chain of straight line segments closing in a loop to form a closed chain or circuit.
The outer ring of boost::geometry::model::polygon is a polygon, the inner rings are polygons too. As a whole boost::geometry::model::polygon is not a polygon.
So, what I'm asking: How to convert boost::geometry::model::polygon to a normal polygon (having a single chain of straight line segments), which represents the same area on a plane.
Here's what I'm trying to achieve:
polygon1 = (0,0), (0,8), (8,8), (8,0), (0,0)
polygon2 = (2,2), (2,6), (6,6), (6,2), (2,2)
Polygons 1 & 2 in green / oker:
difference = (0,0), (0,4), (2,4), (2,2), (6,2), (6,6), (2,6), (2,4), (0,4), (0,8), (8,8), (8,0), (0,0)
Expected difference in grey:
I know the same boost::geometry::model::polygon having inner rings could be represented by infinitely many different normal polygons. I don't care which one I get.

You can easily construct a ring that models your expected weak simple polygon. First:
Caveat
Note though that the result isn't valid for further use with the Boost Geometry library's algorithms.
Take your literal example:
std::string reason;
poly expected;
bg::read_wkt("POLYGON((0 0, 0 4, 2 4, 2 2, 6 2, 6 6, 2 6, 2 4, 0 4, 0 8, 8 8, 8 0, 0 0))", expected);
bool ok = bg::is_valid(expected, reason);
std::cout << "Expected: " << bg::dsv(expected) << (ok?" valid":" invalid: '" + reason + "'") << "\n";
Prints
Expected: (((0, 0), (0, 4), (2, 4), (2, 2), (6, 2), (6, 6), (2, 6), (2, 4), (0, 4), (0, 8), (8, 8), (8, 0), (0, 0))) invalid: 'Geometry has invalid self-intersections. A self-intersection point was found at (0, 4); method: t; operations: x/u; segment IDs {source, multi, ring, segment}: {0, -1, -1, 0}/{0, -1, -1, 7}'
Algorithm implementation
With that out of the way, here's a simple algorithm to construct the simple weak polygon from a given polygon:
ring weak_simple_ring(poly& p) {
ring r = p.outer();
for (auto& i: p.inners()) {
auto last = r.back();
r.insert(r.end(), i.rbegin(), i.rend());
r.insert(r.end(), last);
}
return r;
}
The only subtler point there is to reverse the direction (CW/CCW) of the inner rings to match that of the outer ring.
The algorithm doesn't attempt to be smart about finding a cut-point to the inner ring, which probably also means that it won't work nicely for the generic case with multiple inner rings.
DEMO
Here's a full live demo
Live On Coliru
Where the input is
bg::read_wkt("POLYGON((0 0,0 10,10 10,10 0,0 0))", a);
bg::read_wkt("POLYGON((2 2, 2 6, 6 6, 6 2, 2 2))", b);
The transformation is
std::vector<poly> output;
bg::difference(a, b, output);
for (auto& p : output) {
ring r = weak_simple_ring(p);
bg::convert(r, p);
}
And the result becomes
More complicated sample
Consider when b had a hole:
bg::read_wkt("POLYGON((0 0,0 10,10 10,10 0,0 0))", a);
bg::read_wkt("POLYGON((2 2, 2 6, 6 6, 6 2, 2 2)(3 3, 5 3, 5 5, 3 5, 3 3))", b);
The output with the same code becomes

This is the old answer. After edits to the question I've posted a simple implementation of an algorithm that suits the sample given
It already is.
If you mean, a "simple" non-holey polygon, the outer ring is all you want. Just discard the inner rings.
However, in the most generic case you can end up with multiple entirely disjunct polygons, which is why the output is a collection of polygons. You will have to consider this possibility too (optionally joining the different result polygons into one and discarding inner rings if that's what you desire, functionally).
A sample:
bg::read_wkt("POLYGON((0 0,0 10,10 10,10 0,0 0))", a);
bg::read_wkt("POLYGON((2 -2,2 12,5 12,5 -2,2 -2))", b);
Here, b cuts a into two separate pieces. So you must be prepared to handle multiple, disjunct, output polygons:
Live On Coliru
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/io/io.hpp>
#include <iostream>
#include <fstream>
namespace bg = boost::geometry;
using pt = bg::model::d2::point_xy<int>;
using poly = bg::model::polygon<pt>;
int main() {
poly a, b;
bg::read_wkt("POLYGON((0 0,0 10,10 10,10 0,0 0))", a);
bg::read_wkt("POLYGON((2 -2,2 12,5 12,5 -2,2 -2))", b);
std::cout << bg::dsv(a) << "\n";
std::cout << bg::dsv(b) << "\n";
{
std::ofstream svg("/tmp/input.svg");
boost::geometry::svg_mapper<pt> mapper(svg, 400, 400);
mapper.add(a);
mapper.add(b);
mapper.map(a, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
mapper.map(b, "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(202,153,0);stroke-width:2");
}
std::vector<poly> output;
bg::difference(a, b, output);
for (auto& p : output)
std::cout << "\n\t" << bg::dsv(p);
{
std::ofstream svg("/tmp/output.svg");
boost::geometry::svg_mapper<pt> mapper(svg, 400, 400);
assert(output.size() == 2);
mapper.add(output[0]);
mapper.add(output[1]);
mapper.add(b);
mapper.map(output[0], "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
mapper.map(output[1], "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
mapper.map(b, "fill-opacity:0.15;fill:rgb(153,153,153);stroke-width:0");
}
}
Prints:
(((0, 0), (0, 10), (10, 10), (10, 0), (0, 0)))
(((2, -2), (2, 12), (5, 12), (5, -2), (2, -2)))
(((5, 10), (10, 10), (10, 0), (5, 0), (5, 10)))
(((2, 10), (2, 0), (0, 0), (0, 10), (2, 10)))
SVGs rendered:

Related

Extract indices forming a level of RTree

I have a grid made of squares, where each element has of course its own (integer) index. I used boost to create an RTree of bounding boxes for this grid, and I am able to extract a level of the RTree, which consists of a std::vector<BoundingBox> containing all the bounding boxes that make the given level of the rtree.
So far so good.
My goal is now: for a given level of the RTree, how can I know the indices of the cell that are inside each BoundingBox?
For instance, say that my third level is made of 10 bounding boxes. For each one of these 10 bounding boxes, I'd like to know the indices of the original grid that are inside each bounding box.
I think that logically it should be possible to retrieve such an information from the tree structure, but I can't find any example around. Any help is highly appreciated.
There is no external tree traversal (just geometrical queries and enumeration).
I am able to extract a level of the RTree, which consists of a std::vector containing all the bounding boxes that make the given level of the rtree
If you have the bounding boxes, you can of course do a geometrical query for those on the tree. Note that you can composite indexables as tree value-types. For example std::pair<Geom, Index> is built-in:
using Entry = std::pair<Box, Index>;
using Tree = bgi::rtree<Entry, bgi::rstar<16>>;
When you get query results, you'll get the entire entry, so both the cell and the index.
Live On Coliru
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <random>
static std::minstd_rand s_prng{std::random_device{}()};
namespace bg = boost::geometry;
namespace bgi = bg::index;
namespace bgm = bg::model;
using Coord = int;
using Index = unsigned;
using Box = bgm::box<bgm::d2::point_xy<Coord>>;
template <unsigned Width, unsigned Height = Width> struct Grid {
static_assert(Width >= 1);
static_assert(Height >= 1);
static auto constexpr Size = Width * Height;
static auto pick_bounding_box() {
using C = std::uniform_int_distribution<Coord>;
auto between = [gen = C{}](Coord lower, Coord upper) mutable {
return gen(s_prng, C::param_type{lower, upper});
};
Coord c = between(0, Width-1), r = between(0, Height-1);
return Box{
{ c, r },
{ between(c + 1, Width), between(r + 1, Height) },
};
}
static auto level3() { return std::vector{pick_bounding_box(), pick_bounding_box(), pick_bounding_box()}; }
using Entry = std::pair<Box, Index>;
using Tree = bgi::rtree<Entry, bgi::rstar<16>>;
static Box cell(Index idx) {
assert(idx < Size);
Coord row = idx / Width;
Coord col = idx % Width;
return Box{{col + 0, row + 0}, {col + 1, row + 1}};
}
static Tree build() {
Tree tree;
for (Index idx = 0; idx < Size; ++idx)
tree.insert({cell(idx), idx});
return tree;
}
};
#include <fmt/ostream.h>
#include <fmt/ranges.h>
template <typename Geom>
struct fmt::formatter<bg::detail::dsv::dsv_manipulator<Geom>> : fmt::ostream_formatter {};
using bg::dsv;
using bgi::adaptors::queried;
int main() {
using G = Grid<12, 6>;
using Cells = std::set<Index>;
auto const tree = G::build();
Cells cells;
for (auto&& bbox : G::level3()) {
fmt::print("Within bbox {}:\n", dsv(bbox));
for (auto& [cell, idx] : tree | queried(bgi::within(bbox))) {
cells.insert(idx);
fmt::print(" - cell {} with index {}\n", dsv(cell), idx);
}
}
fmt::print("All {} level-3 cells: {}\n", cells.size(), cells);
}
With sample output:
Within bbox ((8, 0), (12, 3)):
- cell ((8, 0), (9, 1)) with index 8
- cell ((8, 1), (9, 2)) with index 20
- cell ((8, 2), (9, 3)) with index 32
- cell ((9, 0), (10, 1)) with index 9
- cell ((11, 0), (12, 1)) with index 11
- cell ((9, 2), (10, 3)) with index 33
- cell ((10, 1), (11, 2)) with index 22
- cell ((11, 2), (12, 3)) with index 35
- cell ((10, 2), (11, 3)) with index 34
- cell ((11, 1), (12, 2)) with index 23
- cell ((10, 0), (11, 1)) with index 10
- cell ((9, 1), (10, 2)) with index 21
Within bbox ((2, 3), (5, 5)):
- cell ((3, 3), (4, 4)) with index 39
- cell ((2, 3), (3, 4)) with index 38
- cell ((4, 3), (5, 4)) with index 40
- cell ((2, 4), (3, 5)) with index 50
- cell ((4, 4), (5, 5)) with index 52
- cell ((3, 4), (4, 5)) with index 51
Within bbox ((2, 5), (6, 6)):
- cell ((3, 5), (4, 6)) with index 63
- cell ((2, 5), (3, 6)) with index 62
- cell ((4, 5), (5, 6)) with index 64
- cell ((5, 5), (6, 6)) with index 65
All 22 level-3 cells: {8, 9, 10, 11, 20, 21, 22, 23, 32, 33, 34, 35, 38, 39, 40, 50, 51, 52, 62, 63, 64, 65}

C++ 2D algorithm code is reading the array wrong

Like the title said, my code is reading a 2D array entirely wrong.
const int WINNING_ROWS[8][3] = { (0, 1, 2),
(3, 4, 5),
(6, 7, 8),
(0, 3, 6),
(1, 4, 7),
(2, 5, 8),
(0, 4, 8),
(2, 4, 6) };
Above is my 2D array of numbers.
My program doesn't seem to be able to read it properly.
For example, if I were to ask for row 2, item 1, I would expect 7, it, however, instead gives me 6.
Here is a list of rows and item requests I have done to try and figure out what has gone wrong here.
row 0, item 0, expected outcome 0, actual outcome 2
row 3, item 2, expected outcome 6, actual outcome 0
row 1, item 0, expected outcome 3, actual outcome 6
row 5, item 1, expected outcome 5, actual outcome 0
row 8, item 2, expected outcome 6, actual outcome 13629648
row 7, item 2, expected outcome 6, actual outcome 0
for reference, here is the code I have been using to call the items from the array
cout << WINNING_ROWS[7][2] << endl;
Edit 1: ignore the item in bold, that was a mistake on my part when testing my code.
Edit 2: my question has been answered.
const int WINNING_ROWS[8][3] = { (0, 1, 2),
(3, 4, 5),
(6, 7, 8),
(0, 3, 6),
(1, 4, 7),
(2, 5, 8),
(0, 4, 8),
(2, 4, 6) };
That doesn't mean what you think it does. The (0,1,2) is not a row of three elements, but a single integer computed using the comma operator. 0,1,2 evaluates to 2.
You need to use the proper {...} braces instead of parenthesis, or leave them out completely.
Suggest also you change const to constexpr.
WINNING_ROWS[8][2] is out of the array bounds, which means it will cause undefined behavior. If you want to get the last element, you should try cout << WINNING_ROWS[7][2] << endl; since they are 0-indexed.

Dataset of invalid geometries in boost::geometry

Exists a dataset of all posible invalid geometries using c++ and boost::geometry libraries? or at least polygon coordinates of that invalid geomtries that i can to translate to boost::geometry
Example: Selfintersection, etc
I would like to test my application with a least all posibles invalid geometries.
Something like this:
https://knowledge.safe.com/articles/21674/invalid-ogc-geometry-examples.html
but with more test cases with inners and outer polygons.
I have created a library 'boost_geometry_make_valid' which allows correcting the errors described in this dataset:
https://github.com/kleunen/boost_geometry_make_valid
I use the dataset now for testing, the library is able to correct all the mentioned failures. Most important, remove self-intersection from polygons.
The Boost Geometry library implements the OGC standard. From the intro
The library follows existing conventions:
conventions from Boost
conventions from the std library
conventions and names from one of the OGC standards on geometry and, more specificly, from the OGC Simple Feature Specification
So the list that you used is relevant.
Besides, you can use the is_valid function with a reason parameter to interrogate the library about your geometry. I have several examples on this site showing how to do that. (Note: Not all constraints might be validatable)
Your Samples, Live
Let's adopt the outer ring orientation from the samples (not the BG default):
namespace bg = boost::geometry;
using pt = bg::model::d2::point_xy<double>;
using poly = bg::model::polygon<pt, false>;
using multi = bg::model::multi_polygon<poly>;
Let's create a generalized checker:
template <typename Geo = poly> void check(std::string wkt) {
Geo g;
bg::read_wkt(wkt, g);
std::string reason;
bool ok = bg::is_valid(g, reason);
std::cout << "Valid: " << std::boolalpha << ok << " (" << reason << ")\n";
bg::correct(g);
if (bg::is_valid(g, reason)) {
std::cout << "Autocorrected: " << bg::wkt(g) << "\n";
}
}
And run it for all the test cases:
//Hole Outside Shell
check("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0), (15 15, 15 20, 20 20, 20 15, 15 15))");
//Nested Holes
check("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 2 8, 8 8, 8 2, 2 2), (3 3, 3 7, 7 7, 7 3, 3 3))");
//Disconnected Interior
check("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0), (5 0, 10 5, 5 10, 0 5, 5 0))");
//Self Intersection
check("POLYGON((0 0, 10 10, 0 10, 10 0, 0 0))");
//Ring Self Intersection
check("POLYGON((5 0, 10 0, 10 10, 0 10, 0 0, 5 0, 3 3, 5 6, 7 3, 5 0))");
//Nested Shells
check<multi>("MULTIPOLYGON(((0 0, 10 0, 10 10, 0 10, 0 0)),(( 2 2, 8 2, 8 8, 2 8, 2 2)))");
//Duplicated Rings
check<multi>("MULTIPOLYGON(((0 0, 10 0, 10 10, 0 10, 0 0)),((0 0, 10 0, 10 10, 0 10, 0 0)))");
//Too Few Points
check("POLYGON((2 2, 8 2))");
//Invalid Coordinate
check("POLYGON((NaN 3, 3 4, 4 4, 4 3, 3 3))");
//Ring Not Closed
check("POLYGON((0 0, 0 10, 10 10, 10 0))");
Output
Live On Coliru
Prints
Valid: false (Geometry has interior rings defined outside the outer boundary)
Valid: false (Geometry has nested interior rings)
Valid: false (Geometry has wrong orientation)
Valid: false (Geometry has wrong orientation)
Valid: false (Geometry has invalid self-intersections. A self-intersection point was found at (5, 0); method: t; operations: i/i; segment IDs {source, multi, ring, segment}: {0, -1, -1, 4}/{0, -1, -1, 8})
Valid: false (Multi-polygon has intersecting interiors)
Valid: false (Geometry has invalid self-intersections. A self-intersection point was found at (10, 0); method: e; operations: c/c; segment IDs {source, multi, ring, segment}: {0, 0, -1, 0}/{0, 1, -1, 0})
Valid: false (Geometry has too few points)
Valid: false (Geometry has point(s) with invalid coordinate(s))
Valid: false (Geometry is defined as closed but is open)
Autocorrected: POLYGON((0 0,10 0,10 10,0 10,0 0))
Note: the bg::correct might in cases correct /part/ of the problem, but leave other issues, and this check function doesn't report on that.

Sorted Float Vectors for Scatter Plotting (C++ / QCustomPlot)

Questions:
1 - Sort multiple float vectors in the same order (keeping correspondance)
2 - QCustomPlot (QCP) plots ONLY outer boundary of a scatter plot.
(answering either of these 2 questions would solve my problem)
Situation:
I have 3 vectors for plotting:
std::vector<float> x, y;
std::vector<int> hits;
The resulting plot is a hit or miss scatter plot. The resulting plot is used by QCustomPlot's curve, which ends up as a circular "scribble." It just needs to look similar to a circle with no "scribbling" inside. I need this plot to overlay over another plot.
I don't have much control over the initial order of x, y, or hits.
x and y are sorted in a traditional grid indexing:
x = -8, -8, -8, -8, -8, -4, -4, -4, -4, -4, ... 8
y = -8, -4, 0, 4, 8, -8, -4, 0, 4, 8, ... 8
The hits are based on wether (let's just say archer's arrow) a hit was successful based on a range and speed of a fast target (let's just say bird).
The resulting plot is the outer boudary of hits based on the bird as a center reference.
The data vectors can be very large.
Method 1: I can calculate range and angle. Then doing a sort on float vectors: Sort the angles in order, so that when QCustomPlot plots the outer boundary without 'scribbles' inside. However, I need to know how to keep the corresponding x & y values together based on sorting the angle.
// Make range and angle vectors for sorting
std::vector<float> range, angle;
for(int i = 0; i < x.size(); ++i {
float r = sqrt(x[i]*x[i] + y[i]*y[i]);
range.push_back(r);
float a = 0;
if(y < 0)
a = -acos(x[i]/r);
else
a = acos(x[i]/r);
angle.push_back(a);
}
// Sort all vectors by ascending angle vector.
/* Do stuff here! */
// Set up boundary plot data
QVector<float> plot_x, plot_y;
for(int i = 0; i < x.size(); ++i {
if(hits[i]) {
plot_x.push_back(x[i]);
plot_y.push_back(y[i]);
}
}
// curve is a QCPCurve object already existing.
curve->addData(plot_x, plot_y); // Already sorted QVectors
Method 2: Get QCustomPlot curve->addData(x, y) member to only plot an "perimeter line" of the scatter plot's hits. I have tried using QCPScatterStyle, .setCustomPath, but have not been successful.
Thank you in advance!
-John
If you want to order several vectors using some criteria and all the indices correspond create a new vector that are the indices, and order it, then use those indices to create the new vectors:
#include <cmath>
#include <QDebug>
static float calc_angle(float x, float y){
float r = sqrt(x*x + y*y);
float angle = acos(x/r);
return y<0 ? -angle : angle;
}
int main(int argc, char *argv[])
{
std::vector<int> hits{0, 1, 2, 1, 0, 1, 2, 1, 0, 1};
std::vector<float> x{-8, -8, -8, -8, -8, -4, -4, -4, -4, -4};
std::vector<float> y{-8, -4, 0, 4, 8, -8, -4, 0, 4, 8};
Q_ASSERT(x.size() == y.size() && y.size() == hits.size());
std::vector<int> indexes(x.size());
std::iota(indexes.begin(), indexes.end(), 0);
std::sort(indexes.begin(), indexes.end(), [&](const int & i, const int & j) -> bool{
return calc_angle(x[i], y[i]) < calc_angle(x[j], y[i]);
});
QVector<float> plot_x, plot_y;
QVector<int> new_hits;
for(const int & index : indexes){
plot_x<<x[index];
plot_y<<y[index];
new_hits<<hits[index];
}
qDebug()<<indexes;
qDebug()<< plot_x;
qDebug()<<plot_y;
qDebug()<<new_hits;
return 0;//a.exec();
}
Output:
std::vector(8, 0, 1, 2, 3, 4, 5, 6, 7, 9)
QVector(-4, -8, -8, -8, -8, -8, -4, -4, -4, -4)
QVector(4, -8, -4, 0, 4, 8, -8, -4, 0, 8)
QVector(0, 0, 1, 2, 1, 0, 1, 2, 1, 1)

How should the data of an index array of a VBO be formatted?

I am trying to implement a VBO in my application. Now I have understood how they work, and how to use them, yet I don't have a single clue on how to fill the accompanying index buffer. Strangely enough I haven't found any tutorial explaining this at all.
Say that I have 3 buffers like this:
vertex buffer: (3, 4, 7), (2, 4, 7), (2, 4, 2), ...
texture buffer: (1, 1), (0, 1), (1, 0), (1, 1), ..
normal buffer: (4, 2, 6), (3, 2, 7), (2, 4, 5) ...
How do I tell openGL that I want it to draw a vertex at (3, 4, 7) with a texture coordinate of (1, 1) and a normal of (4, 2, 6) and so on? And how do I put this data in the right format in the elements index buffer?
The index buffer just contains integer offsets into your VBO arrays. For your example your first index buffer element should be 0, since you want to draw the zeroth element of your vertex, texture, and normal arrays.
Note that it's the same offset into all three arrays.