The following code generates the output I expect:
MULTILINESTRING((5 5,4 4),(2 2,1 1))
However, if I remove the call to boost::geometry::correct() it returns the incorrect result:
MULTILINESTRING((5 5,1 1))
Code below:
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/multi/geometries/multi_linestring.hpp>
#include <iostream>
namespace bg = boost::geometry;
namespace bgm = boost::geometry::model;
using point = bgm::point<double, 2, bg::cs::cartesian>;
using polygon = bgm::polygon<point>;
using polyline = bgm::linestring<point>;
using polylines = bgm::multi_linestring<polyline>;
int main()
{
polygon poly = {
{ {1,5}, {5,5}, {5,1}, {1,1} },
{ {2,4}, {2,2}, {4,2}, {4,4} }
};
polyline line = { {6,6},{0,0} };
bg::correct(poly);
polylines result;
bg::intersection(poly, line, result);
std::cout << bg::wkt(result) << "\n";
return 0;
}
The geometry defined in the above looks like the following. The red line segment and blue polygon with a hole should intersect to the green line segments.
I defined the vertices of the hole in counter-clockwise order as it is typical in computational geometry for holes to have reverse orientation of non-holes (It's also typical for non-holes to have counter-clockwise orientation but boost::geometry seems to default to clockwise). If I flip the orientation of the hole it does not fix the problem (although I do get a different wrong result). I am not sure what else correct could be doing.
boost::correct() is closing both the inner and outer polygons.
That is, the following returns the expected output:
namespace bg = boost::geometry;
namespace bgm = boost::geometry::model;
using point = bgm::point<double, 2, bg::cs::cartesian>;
using polygon = bgm::polygon<point>;
using polyline = bgm::linestring<point>;
using polylines = bgm::multi_linestring<polyline>;
int main()
{
polygon poly = {
{ {1,5}, {5,5}, {5,1}, {1, 1}, {1,5}},
{ {2,4}, {2,2}, {4,2}, {4,4}, {2,4}}
};
polyline line = { {6,6},{0,0} };
polylines result;
bg::intersection(poly, line, result);
std::cout << bg::wkt(result) << "\n";
return 0;
}
Related
Using boost::geometry::line_interpolate with boost::geometry::srs::spheroid, I'm calculating great circle navigation points along the shortest distance between 2 geographic points. The code below calculates the navigation points for the shortest distance around the great circle. In some rare cases, I need to generate the longer distance that wraps around the globe in the wrong direction. For example, when interpolating between a lon/lat of (20, 20) to (30, 20), there only 10 degrees of difference in the shorter direction and 350 degrees in the other. In some cases I would like the ability to want to interpolate in the longer direction (e.g. 350 deg).
This 2d map shows shows the 10 degree longitude difference in red, and 350 degrees green. I drew the green line by hand to the line is only an approximation. How can I get the points for this green line?
This code is based on the example from boost.org, line_interpolate_4_with_strategy
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
int main()
{
typedef boost::geometry::model::d2::point_xy<double, boost::geometry::cs::geographic<boost::geometry::degree> > Point_Type;
using Segment_Type = boost::geometry::model::segment<Point_Type>;
using Multipoint_Type = boost::geometry::model::multi_point<Point_Type>;
boost::geometry::srs::spheroid<double> spheroid(6378137.0, 6356752.3142451793);
boost::geometry::strategy::line_interpolate::geographic<boost::geometry::strategy::vincenty> str(spheroid);
Segment_Type const start_end_points { {20, 20}, {30, 20} }; // lon/lat, interpolate between these two points
double distance { 50000 }; // plot a point ever 50km
Multipoint_Type mp;
boost::geometry::line_interpolate(start_end_points, distance, mp, str);
std::cout << "on segment : " << wkt(mp) << "\n";
return 0;
}
Note that line_interpolate interpolates points on a linestring where a segment between two points follows a geodesic.
Therefore, one workaround could be to create an antipodal point to the centroid of the original segment and create a linestring that follows the requested path. Then call line_interpolate with this linestring. The following code could do the trick.
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
int main()
{
namespace bg = boost::geometry;
using Point_Type = bg::model::d2::point_xy<double, bg::cs::geographic<bg::degree>>;
using Segment_Type = boost::geometry::model::segment<Point_Type>;
using Linstring_Type = bg::model::linestring<Point_Type>;
using Multipoint_Type = bg::model::multi_point<Point_Type>;
bg::srs::spheroid<double> spheroid(6378137.0, 6356752.3142451793);
bg::strategy::line_interpolate::geographic<bg::strategy::vincenty> str(spheroid);
Segment_Type const start_end_points { {20, 20}, {30, 20} };
Point_Type centroid;
bg::centroid(start_end_points, centroid);
Point_Type antipodal_centroid;
bg::set<0>(antipodal_centroid, bg::get<0>(centroid) + 180);
bg::set<1>(antipodal_centroid, bg::get<1>(centroid) * -1);
Linstring_Type line;
line.push_back(start_end_points.first);
line.push_back(antipodal_centroid);
line.push_back(start_end_points.second);
double distance { 50000 }; // plot a point ever 50km
Multipoint_Type mp;
bg::line_interpolate(line, distance, mp, str);
std::cout << "on segment : " << wkt(mp) << "\n";
return 0;
}
The result looks like this:
Note that since the spheroid you are constructing is non-spherical then there is no great circle (apart from equator and meridians) and the geodesic segment is not closed but looks like this. Therefore you will notice that the last interpolated point will be different from the segment's endpoint.
I am using boost::geometry to handle some geometrical tasks. I have two requirements that I need to cover:
Handle point -> polygon intersection (inside or not). This works great with boost::geometry::within so thats good
Get the distance of an arbitrary point to the closest edge of the polygon. While points outside of the polygon are handled correctly by boost::geometry::distance, however it seems that it considers polygons solid. So every point inside the polygon obviously has a distance of 0 to the polygon.
I tried experimenting with inner/outer stuff and was wondering if there is a possbility to get the distance for both inside and outside points of a polygon.
In case where point is inside polygon you may speed your code up using comparable_distance instead of distance algorithm. You don't need to calculate the exact distance for every segment-point pair. Find the nearest segment of polygon to the given point using comparable_distance and then calculate the real distance using distance algorithm.
auto distance = std::numeric_limits<float>::max();
if(boost::geometry::within(pt, mPolygon))
{
Segment nearestSegment;
boost::geometry::for_each_segment(mPolygon,
[&distance, &pt, &nearestSegment](const auto& segment)
{
double cmpDst = boost::geometry::comparable_distance(segment,pt);
if (cmpDst < distance)
{
distance = cmpDst;
nearestSegment = segment; // UPDATE NEAREST SEGMENT
}
});
// CALCULATE EXACT DST
distance = boost::geometry::distance(nearestSegment,pt);
} else {
distance = boost::geometry::distance(pt, mPolygon);
}
I have decided to use the following approach which seems to provide the right results so far:
const TPolygonPoint pt{ x, y };
auto distance = std::numeric_limits<float>::max();
if(boost::geometry::within(pt, mPolygon)) {
boost::geometry::for_each_segment(mPolygon, [&distance, &pt](const auto& segment) {
distance = std::min<float>(distance, boost::geometry::distance(segment, pt));
});
} else {
distance = boost::geometry::distance(pt, mPolygon);
}
return distance;
If anyone knows a faster or nicer way, please leave a comment :)
For best performances you should use an RTree with boost::geometry::index. Creating the RTree has a cost, but then computing the ditance of a point to any of the (multi)polygon ring will be much fast. Example code:
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <iostream>
#include <vector>
int main()
{
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;
typedef bg::model::point<double, 2, bg::cs::cartesian> point;
typedef bg::model::polygon<point> polygon;
point p{ 0, 0 };
// create some polygon and fill it with data
polygon poly;
double a = 0;
double as = bg::math::two_pi<double>() / 100;
for (int i = 0; i < 100; ++i, a += as)
{
double c = cos(a);
double s = sin(a);
poly.outer().push_back(point{10 * c, 10 * s});
poly.inners().resize(1);
poly.inners()[0].push_back(point{5 * c, 5 * s});
}
// make sure it is valid
bg::correct(poly);
// create rtree containing objects of type bg::model::pointing_segment
typedef bg::segment_iterator<polygon const> segment_iterator;
typedef std::iterator_traits<segment_iterator>::value_type segment_type;
bgi::rtree<segment_type, bgi::rstar<4> > rtree(bg::segments_begin(poly),
bg::segments_end(poly));
// get 1 nearest segment
std::vector<segment_type> result;
rtree.query(bgi::nearest(p, 1), std::back_inserter(result));
BOOST_ASSERT(!result.empty());
std::cout << bg::wkt(result[0]) << ", " << bg::distance(p, result[0]) << std::endl;
return 0;
}
You can directly use boost::geometry::distance if you add an inner boundary to the polygon coinciding with the outer boundary [Polygon Concept].
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
namespace bg = boost::geometry;
int main() {
typedef bg::model::point<int, 2, bg::cs::cartesian> point_t;
typedef bg::model::polygon<point_t> polygon_t;
polygon_t poly1;
bg::append (poly1.outer(), point_t (1, -1));
bg::append (poly1.outer(), point_t (1, 1));
bg::append (poly1.outer(), point_t (-1, 1));
bg::append (poly1.outer(), point_t (-1, -1));
bg::append (poly1.outer(), point_t (1, -1));
poly1.inners().resize (1);
bg::append (poly1.inners()[0], point_t (1, -1));
bg::append (poly1.inners()[0], point_t (1, 1));
bg::append (poly1.inners()[0], point_t (-1, 1));
bg::append (poly1.inners()[0], point_t (-1, -1));
bg::append (poly1.inners()[0], point_t (1, -1));
point_t myPoint (0, 0);
std::cout << "Minimal distance: " << bg::distance (poly1, myPoint) << std::endl;
std::cout << "Is within: " << bg::within (myPoint, poly1) << std::endl;
return 0;
}
-> Will return:
Minimal distance: 1
Is within: 0
However, if you do that, points strictly inside the polygon will be considered to lie 'outside' the polygon by boost::geometry::within. If you want both functionalities, you can maintain two separate polygons- one with an inner boundary and one without.
I have been using boost::geometry library in a program, mostly for handling polygon objects.
I am now trying to optimize my code to scale better with larger polygons. One my functions checks for a given polygon and a given point (usually inside the polygon) the minimum and maximum distance between the point and polygon outer ring.
I do it by looping on the polygon edges:
polygon pol;
point myPoint;
double min = 9999999, max = 0;
for(auto it1 = boost::begin(bg::exterior_ring(pol)); it1 != boost::end(bg::exterior_ring(pol)); ++it1){
double distance = bg::distance(*it1, myPoint);
if(max < distance)
max = distance;
if(min > distance)
min = distance;
}
I am hoping that there are algorithms faster than this one, linear in the polygon number of edges. Is there such a thing already inside the boost::geometry library?
I'd suggest you can use the builtin strategies for finding the minimum distance between the polygon and the point:
Live On Coliru
#include <boost/geometry.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/io/io.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/algorithms/distance.hpp>
namespace bg = boost::geometry;
using point = bg::model::d2::point_xy<double>;
using polygon = bg::model::polygon<point>;
int main() {
polygon pol;
boost::geometry::read_wkt(
"POLYGON((2 1.3,2.4 1.7,2.8 1.8,3.4 1.2,3.7 1.6,3.4 2,4.1 3,5.3 2.6,5.4 1.2,4.9 0.8,2.9 0.7,2 1.3)"
"(4.0 2.0, 4.2 1.4, 4.8 1.9, 4.4 2.2, 4.0 2.0))", pol);
point myPoint(7, 7);
double min = 9999999, max = 0;
std::cout << "Minimal distance: " << bg::distance(pol, myPoint);
}
Prints
Minimal distance: 4.71699
Further hints:
You should consider ranking the distances first using comparable_distance. As you can see the sample there suggests looping over all the sampled distances... so I don't think the library has a better offering at this time.
More sophisticated algorithms are planned, of which a number may be related to this problem:
http://boost-geometry.203548.n3.nabble.com/distance-between-geometries-td4025549.html
mailing list thread http://lists.boost.org/geometry/2013/08/2446.php
another here http://lists.boost.org/geometry/2013/09/2494.php
Note also that Boost Geometry Index has a related predicate comparable_distance_far but it's not exposed as of yet.
Summary
You can improve at least a bit by using comparable_distance here for now.
Features have been planned and it looks like there is a good chance that requesting them on the mailing list/Boost Trac will help getting them there.
For best performances you should use an RTree with boost::geometry::index. Creating the RTree has a cost, but then computing the ditance of a point to any of the (multi)polygon ring will be much faster. Example code:
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <iostream>
#include <vector>
int main()
{
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;
typedef bg::model::point<double, 2, bg::cs::cartesian> point;
typedef bg::model::polygon<point> polygon;
point p{ 0, 0 };
// create some polygon and fill it with data
polygon poly;
double a = 0;
double as = bg::math::two_pi<double>() / 100;
for (int i = 0; i < 100; ++i, a += as)
{
double c = cos(a);
double s = sin(a);
poly.outer().push_back(point{10 * c, 10 * s});
poly.inners().resize(1);
poly.inners()[0].push_back(point{5 * c, 5 * s});
}
// make sure it is valid
bg::correct(poly);
// create rtree containing objects of type bg::model::pointing_segment
typedef bg::segment_iterator<polygon const> segment_iterator;
typedef std::iterator_traits<segment_iterator>::value_type segment_type;
bgi::rtree<segment_type, bgi::rstar<4> > rtree(bg::segments_begin(poly),
bg::segments_end(poly));
// get 1 nearest segment
std::vector<segment_type> result;
rtree.query(bgi::nearest(p, 1), std::back_inserter(result));
BOOST_ASSERT(!result.empty());
std::cout << bg::wkt(result[0]) << ", " << bg::distance(p, result[0]) << std::endl;
return 0;
}
I am visualising different sorts of geometries using Qt 5.
There I have a QRect that is either visualised as filled or not.
Now I want to calculate the distance of a QPoint to that rectangle using boost::geometry.
A point within the rectangle should have a distance of 0 when filled, and the distance to the next line when not filled.
Since the documentation of Box does not mention that it is a shape I thought I could use it for this case and adapted the Box concept to QRect.
The following example does not work though, since a Box is treated as shape and therefore always "filled".
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <QtCore/QPoint>
#include <QtCore/QRect>
BOOST_GEOMETRY_REGISTER_POINT_2D_GET_SET(QPoint, int, boost::geometry::cs::cartesian, x, y, setX, setY);
namespace boost { namespace geometry {
namespace traits
{
template <> struct tag<QRect> { typedef box_tag type; };
template <> struct point_type<QRect> { typedef QPoint type; };
template <std::size_t Index, std::size_t Dimension>
struct indexed_access<QRect, Index, Dimension>
{
typedef typename geometry::coordinate_type<QRect>::type coordinate_type;
static inline coordinate_type get(const QRect &r)
{
if (Index == boost::geometry::min_corner)
return geometry::get<Dimension>(r.topLeft());
else
return geometry::get<Dimension>(r.bottomRight());
}
};
}
}}
double distance(const QPoint &p, const QRect &r, const bool filled)
{
if (filled && r.contains(p))
return 0.0;
else
return boost::geometry::distance(p, r);
}
int main()
{
QRect r(QPoint(0, 0), QPoint(20, 10));
QPoint p(5, 5); // whithin rect
// 0, instead of 5
std::cout << "not filled: " << distance(p, r, false) << '\n';
// 0, as expected
std::cout << "filled: " << distance(p, r, true) << '\n';
}
Run g++ -Wall -O2 -fPIC main.cpp -I/usr/include/qt -lQtCore to build this on Linux.
I could of course use the LineString for the not filled case, though then there would be dynamic allocations.
Unless I create a manualy adaption which uses an underlying QRect, which would be quite some work.
How do I best tackle this issue?
Indeed you're right line-string is required because Box implies a filled shape. Same thing for polygons, actually, in my quick test.
You could of course create a fake "holey" polygon that has an edge of some small width. But that's cheating and certainly less efficient
Indeed, you can use linestring here:
Live On Coliru
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>
using namespace boost::geometry;
int main()
{
using Point = model::d2::point_xy<double>;
using Rect = model::linestring<Point>;
Rect rect;
rect.insert(rect.end(), {
Point { 0, 0 },
Point { 10, 0 },
Point { 10, 20 },
Point { 0, 20 },
Point { 0, 0 },
});
std::cout << "distance point within: " << distance(rect, Point(5, 5)) << '\n'; // 0
std::cout << "distance point not within: " << distance(rect, Point(15, 5)) << '\n'; // 5
}
Which prints
distance point within: 5
distance point not within: 5
I don't see any reason to believe that the linestring is less efficient than the polygon (it's basically the same as just the outer ring of a polygon).
However, indeed box testing might be faster. I suggest you profile it. If it's faster, just use the box in case the shape is known to be "filled" and a linestring otherwise.
A relative easy way to support non-filled QRect is to use the LineString concept.
To avoid overhead of a allocations std::array could be used.
Based on the intial code, the following parts need to be added:
#include <array>
using RectLineString = std::array<QPoint, 5>;
BOOST_GEOMETRY_REGISTER_LINESTRING(RectLineString)
double distance(const QPoint &p, const QRect &r, const bool filled)
{
if (filled && r.contains(p))
return 0.0;
else
{
RectLineString rls;
fillRectLineString(rls, rect);
return boost::geometry::distance(p, rls);
}
}
What fillrectLineString should look like depends on how you want to handle the issue that QRect::bottomRight() returns QPoint(rect.x() + rect.width() - 1, rect.y() + rect.height() - 1).
So I provide two versions here:
// bottomRight() is QPoint(rect.x() + rect.width() - 1, rect.y() + rect.height() - 1)
void fillRectLineString1(RectLineString &rls, const QRect &rect)
{
rls[0] = rect.topLeft();
rls[1] = rect.topRight();
rls[2] = rect.bottomRight();
rls[3] = rect.bottomLeft();
rls[4] = rect.topLeft();
}
// bottomRight() is QPoint(rect.x() + rect.width(), rect.y() + rect.height())
void fillRectLineString2(RectLineString &rls, const QRect &rect)
{
rls[0] = QPoint(rect.x(), rect.y());
rls[1] = QPoint(rect.x() + rect.width(), rect.y());
rls[2] = QPoint(rect.x() + rect.width(), rect.y() + rect.height());
rls[3] = QPoint(rect.x(), rect.y() + rect.height());
rls[4] = QPoint(rect.x(), rect.y());
}
I have two transformers, a translation and a rotation as follows:
namespace bg = boost::geometry;
namespace trans = bg::strategy::transform;
trans::translate_transformer<point, point> translate(px, py);
trans::rotate_transformer<point, point, bg::radian> rotate(rz);
How do I combine them into one, so that I don't have to call bg::transform twice each time and use an intermediate variable?
Both translate and rotate are affine transformations, i.e., they can be represented using a matrix. Therefore, all you have to do is to create a new transformer whose matrix is equal to the product of the matrices of the two transforms.
trans::ublas_transformer<point, point, 2, 2> translateRotate(prod(rotate.matrix(), translate.matrix()));
Here is a full working example:
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/strategies/transform/matrix_transformers.hpp>
namespace bg = boost::geometry;
namespace trans = bg::strategy::transform;
typedef bg::model::d2::point_xy<double> point;
int main()
{
trans::translate_transformer<point, point> translate(0, 1);
trans::rotate_transformer<point, point, bg::degree> rotate(90);
trans::ublas_transformer<point, point, 2, 2> translateRotate(prod(rotate.matrix(), translate.matrix()));
point p;
translateRotate.apply(point(0, 0), p);
std::cout << bg::get<0>(p) << " " << bg::get<1>(p) << std::endl;
}
Be very careful regarding the order of the matrices in the multiplication. The example above first translates, then rotates.