I am trying to create a box using multiple linestrings. Of course these linestrings are connected with each other and are able to make a box. Are there any functions in boost can do this? Thanks!
I am trying to create a box using multiple linestrings. Of course these linestrings are connected with each other and are able to make a box. Are there any functions in boost can do this? Thanks!
I'm not sure what you're looking for exactly. So let me just go both ways.
If you have a box you can use segment iterators to get the segments:
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <iostream>
namespace bg = boost::geometry;
namespace bgm = bg::model;
using Point = bgm::d2::point_xy<int>;
using Box = bgm::box<Point>;
using Poly = bgm::polygon<Point>;
int main()
{
Box const box { Point {0,1}, {1,2} };
std::cout << "box: " << bg::wkt(box) << "\n";
// now get the segments:
Poly p;
bg::assign(p, box);
for (auto f = bg::segments_begin(p), l = bg::segments_end(p); f != l; ++f) {
std::cout << bg::wkt(*f) << "\n";
}
}
Prints
box: POLYGON((0 1,0 2,1 2,1 1,0 1))
LINESTRING(0 1,0 2)
LINESTRING(0 2,1 2)
LINESTRING(1 2,1 1)
LINESTRING(1 1,0 1)
The most flexible is to use the envelope algorithm to get a box:
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <iostream>
namespace bg = boost::geometry;
namespace bgm = bg::model;
using Point = bgm::d2::point_xy<int>;
using Box = bgm::box<Point>;
using LineString = bgm::linestring<Point>;
int main()
{
LineString const ls{{1, 1}, {1, 2}, {0, 2}, {0, 1}};
std::cout << "ls: " << bg::wkt(ls) << "\n";
Box box;
bg::envelope(ls, box);
std::cout << "box: " << bg::wkt(box) << "\n";
}
Prints
ls: LINESTRING(1 1,1 2,0 2,0 1)
box: POLYGON((0 1,0 2,1 2,1 1,0 1))
(Note that the point ordering is corrected to make a valid ring)
To get less flexibility, in other words, more validation on the input, I would manually create a polygon from the points.
After validating, you could create a box via the WKT... That's not ideal, but probably beats writing your own logic. However, reading Box from WKT appears to only apply min/max algorithm on points without any actual validation - thus effectively the same as getting bg::envelope.
So, the best way to approach this, IMO, would be to compare the resulting box with the manual polygon to check that the input was already a box.
bool is_box(Poly const& p) {
Box env;
bg::envelope(p, env);
return bg::equals(p, env);
}
For example testing with a few inputs:
LineString{{1, 1}, {0, 1}, {0, 2}, {1, 2}, {1, 1}},
LineString{{1, 1}, {1, 2}, {0, 2}, {0, 1}},
LineString{{1, 1}, {1, 2}, {0, 2}, {0, 1}, {1, 1}},
LineString{{1, 1}, {0, 2}, {0, 1}, {1, 1}}, // triangle
is_box will return false for the last one (even after correcting the polygon)
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <iostream>
namespace bg = boost::geometry;
namespace bgm = bg::model;
using Point = bgm::d2::point_xy<int>;
using Box = bgm::box<Point>;
using Poly = bgm::polygon<Point>;
using LineString = bgm::linestring<Point>;
bool is_box(Poly const& p) {
Box env;
bg::envelope(p, env);
return bg::equals(p, env);
}
int main()
{
for (auto& ls : {
LineString{{1, 1}, {0, 1}, {0, 2}, {1, 2}, {1, 1}},
LineString{{1, 1}, {1, 2}, {0, 2}, {0, 1}},
LineString{{1, 1}, {1, 2}, {0, 2}, {0, 1}, {1, 1}},
LineString{{1, 1}, {0, 2}, {0, 1}, {1, 1}}, // triangle
})
{
std::cout << " --\nls: " << bg::wkt(ls) << "\n";
Poly p;
bg::assign_points(p, ls);
if (std::string reason; !bg::is_valid(p, reason)) {
std::cout << "Trying to correct...: " << reason << "\n";
bg::correct(p);
if (!bg::is_valid(p))
continue;
}
std::cout << "poly: " << bg::wkt(p) << "\n";
std::cout << "is a box? " << (is_box(p) ? "yes" : "no") << "\n";
}
}
Prints
--
ls: LINESTRING(1 1,0 1,0 2,1 2,1 1)
poly: POLYGON((1 1,0 1,0 2,1 2,1 1))
is a box? yes
--
ls: LINESTRING(1 1,1 2,0 2,0 1)
Trying to correct...: Geometry is defined as closed but is open
poly: POLYGON((1 1,0 1,0 2,1 2,1 1))
is a box? yes
--
ls: LINESTRING(1 1,1 2,0 2,0 1,1 1)
Trying to correct...: Geometry has wrong orientation
poly: POLYGON((1 1,0 1,0 2,1 2,1 1))
is a box? yes
--
ls: LINESTRING(1 1,0 2,0 1,1 1)
Trying to correct...: Geometry has wrong orientation
poly: POLYGON((1 1,0 1,0 2,1 1))
is a box? no