1 #include <boost/version.hpp>
5 #if BOOST_VERSION < 106300 && BOOST_VERSION >= 106200
9 #include <boost/geometry/algorithms/disjoint.hpp>
10 #include <boost/geometry/geometries/box.hpp>
11 #include <boost/geometry/geometries/pointing_segment.hpp>
12 #include <boost/geometry/index/rtree.hpp>
28 constexpr
double SmallNum = 1.e-10;
29 constexpr std::size_t RtreeThres = 50;
32 inline LineParams calculateLineParams(
double a,
double b,
double c,
double d,
double e,
double den) {
37 return {0.0, 1.0, e, c};
43 lp.sN = (b * e - c *
d);
44 lp.tN = (a * e - b *
d);
49 }
else if (lp.sN > lp.sD) {
58 template <
typename Po
intT>
60 std::pair<PointT, PointT> projectedPoint(PointT
p1, PointT
p2, PointT q1, PointT q2) {
70 auto den = a * c - b * b;
72 auto lp = calculateLineParams(a, b, c,
d, e, den);
85 }
else if (lp.tN > lp.tD) {
90 }
else if ((-
d + b) > a) {
98 double sc = (std::abs(lp.sN) < SmallNum ? 0.0 : lp.sN / lp.sD);
99 double tc = (std::abs(lp.tN) < SmallNum ? 0.0 : lp.tN / lp.tD);
101 return {
p1 + (sc * u), q1 + (tc * v)};
104 template <
typename Po
intT>
106 PointT projectedPoint(PointT l1, PointT l2, PointT
p) {
110 const auto c1 = w.dot(v);
114 const auto c2 = v.dot(v);
118 const auto b = c1 / c2;
122 template <
typename RangeT,
typename Func>
123 void distForAllSegments(
const RangeT& r, Func f) {
125 f(r.front(), r.front());
127 for (
auto first = r.begin(), second = r.begin() + 1; second != r.end(); ++first, ++second) {
128 if (f(*first, *second) == 0.) {
135 namespace bgi = bg::index;
136 namespace bgm = boost::geometry::model;
138 template <
typename Po
int1T,
typename Po
int2T,
typename BasicPo
intT>
139 struct ProjectedPoint2L2Result {
140 using Point1 = Point1T;
141 using Point2 = Point2T;
142 using Segm1 = Segment<Point1T>;
143 using Segm2 = Segment<Point2T>;
146 bool valid()
const {
return !!
distance; }
150 const auto dNew = (projPair.first - projPair.second).norm();
155 this->p1 = projPair.first;
156 this->p2 = projPair.second;
160 std::pair<BasicPoint, BasicPoint> projectedPoints()
const {
return std::make_pair(
p1,
p2); }
171 template <
typename LineString1T,
typename LineString2T>
172 using ProjectedPointL2LOnLinestring = ProjectedPoint2L2Result<traits::ConstPointT<traits::PointType<LineString1T>>,
173 traits::ConstPointT<traits::PointType<LineString2T>>,
174 traits::BasicPointT<traits::PointType<LineString1T>>>;
176 template <
typename Po
intT,
typename BasicPo
intT>
177 struct ProjectedPointL2PResult {
178 using Point = PointT;
179 using Segm = Segment<PointT>;
182 bool valid()
const {
return !!
distance; }
183 double update(Segm
segm, BasicPoint
p) {
185 const auto dNew = (projP -
p).norm();
193 Segment<Point> segment()
const {
return segm; }
200 template <
typename LineStringT>
201 using ProjectedPointL2POnLinestring = ProjectedPointL2PResult<traits::ConstPointT<traits::PointType<LineStringT>>,
202 traits::BasicPointT<traits::PointType<LineStringT>>>;
204 template <
typename Po
intT>
205 auto toSegment(
const bgm::pointing_segment<const PointT>& s) {
206 return std::make_pair(*s.first, *s.second);
209 template <
typename Po
intT>
210 auto toBasicSegment(
const bgm::pointing_segment<const PointT>& s) {
214 template <
typename LineString1T,
typename LineString2T>
215 ProjectedPointL2LOnLinestring<LineString1T, LineString2T> projectedPointL2LWithTree(
const LineString1T& smallerRange,
216 const LineString2T& greaterRange) {
217 using TreePointT = traits::ConstPointT<traits::PointType<LineString2T>>;
218 using TreeSegmT = Segment<TreePointT>;
220 using Box = bgm::box<bgm::point<double, static_cast<int>(Dim), boost::geometry::cs::cartesian>>;
221 using Node = std::pair<Box, TreeSegmT>;
222 using RTree = bgi::rtree<Node, bgi::linear<8>>;
224 utils::transform(bg::segments_begin(greaterRange), bg::segments_end(greaterRange), [](
const auto&
segm) {
226 boost::geometry::envelope(
segm, box);
227 return Node(box, toSegment(
segm));
229 RTree tree(values.begin(), values.end());
231 using PointT = traits::ConstPointT<traits::PointType<LineString1T>>;
232 auto result = ProjectedPointL2LOnLinestring<LineString1T, LineString2T>{};
233 distForAllSegments(smallerRange, [&](
auto&& psmall1,
auto&& psmall2) {
236 bg::envelope(segsmall, queryBox);
237 for (
auto qIt = tree.qbegin(bgi::nearest(queryBox,
unsigned(tree.size()))); qIt != tree.qend(); ++qIt) {
238 const auto& nearest = *qIt;
240 if (!!result.distance && *(result.distance) < dBox) {
243 result.update(std::make_pair(psmall1, psmall2), nearest.second);
245 return *result.distance;
250 template <
typename LineString1T,
typename LineString2T>
251 ProjectedPointL2LOnLinestring<LineString1T, LineString2T> projectedPointL2LBruteForce(
252 const LineString1T& smallerRange,
const LineString2T& greaterRange) {
253 auto result = ProjectedPointL2LOnLinestring<LineString1T, LineString2T>{};
254 distForAllSegments(smallerRange, [&](
auto&& psmall1,
auto&& psmall2) {
255 distForAllSegments(greaterRange, [&](
auto&& pgreat1,
auto&& pgreat2) {
256 return result.update(std::make_pair(psmall1, psmall2), std::make_pair(pgreat1, pgreat2));
258 return *result.distance;
263 template <
typename LineStringT>
264 ProjectedPointL2POnLinestring<LineStringT> projectedPointL2PBruteForce(
266 auto result = ProjectedPointL2POnLinestring<LineStringT>{};
267 distForAllSegments(ls, [&](
auto&& pl1,
auto&& pl2) {
return result.update(std::make_pair(pl1, pl2), point); });
271 template <
typename LineStringT>
272 ProjectedPointL2POnLinestring<LineStringT> projectedPointL2PWithTree(
274 using TreePointT = traits::ConstPointT<traits::PointType<LineStringT>>;
275 using TreeSegmT = Segment<TreePointT>;
277 using Box = bgm::box<bgm::point<double, static_cast<int>(Dim), boost::geometry::cs::cartesian>>;
278 using Node = std::pair<Box, TreeSegmT>;
279 using RTree = bgi::rtree<Node, bgi::linear<8>>;
281 const auto values =
utils::transform(bg::segments_begin(ls), bg::segments_end(ls), [](
const auto&
segm) {
284 return Node(box, toSegment(
segm));
286 RTree tree(values.begin(), values.end());
288 auto result = ProjectedPointL2POnLinestring<LineStringT>{};
289 for (
auto qIt = tree.qbegin(bgi::nearest(
p,
unsigned(tree.size()))); qIt != tree.qend(); ++qIt) {
290 const auto& nearest = *qIt;
292 if (!!result.distance && *(result.distance) < dBox) {
295 if (result.update(nearest.second,
p) == 0.) {
302 template <
typename LineString1T,
typename LineString2T>
303 auto projectedPointOrdered(
const LineString1T& smallerRange,
const LineString2T& greaterRange) {
304 if (smallerRange.size() == 0) {
305 throw InvalidInputError(
"ProjectedPoint called with empty linestring as input!");
307 if (greaterRange.size() < RtreeThres) {
308 return projectedPointL2LBruteForce(smallerRange, greaterRange);
310 return projectedPointL2LWithTree(smallerRange, greaterRange);
313 template <
typename LineString1T,
typename LineString2T>
314 auto projectedPointL2LImpl(
const LineString1T& l1,
const LineString2T& l2) {
315 if (l1.size() < l2.size()) {
316 return projectedPointOrdered(l1, l2);
318 return projectedPointOrdered(l2, l1).swap();
321 template <
typename LineStringT>
322 auto projectedPointL2PImpl(
const LineStringT& ls,
const traits::BasicPointT<traits::PointType<LineStringT>>&
p) {
323 if (ls.size() < RtreeThres) {
324 return projectedPointL2PBruteForce(ls,
p);
326 return projectedPointL2PWithTree(ls,
p);
334 return projectedPointL2LImpl(l1, l2).projectedPoints();
339 return projectedPointL2LImpl(l1, l2).projectedPoints();
344 return projectedPointL2LImpl(l1, l2).projectedPoints();
349 return projectedPointL2LImpl(l1, l2).projectedPoints();
353 return projectedPointL2LImpl(l1, l2).projectedPoints();
357 return projectedPointL2LImpl(l1, l2).projectedPoints();
361 return projectedPointL2LImpl(l1, l2).projectedPoints();
366 return projectedPointL2LImpl(l1, l2).projectedPoints();
371 return projectedPointL2LImpl(l1, l2).projectedPoints();
377 return projectedPointL2LImpl(l1, l2).projectedPoints();
382 return projectedPointL2LImpl(l1, l2).projectedPoints();
387 return projectedPointL2LImpl(l1, l2).projectedPoints();
392 return projectedPointL2LImpl(l1, l2).projectedPoints();
396 return projectedPointL2LImpl(l1, l2).projectedPoints();
400 return projectedPointL2LImpl(l1, l2).projectedPoints();
404 return projectedPointL2LImpl(l1, l2).projectedPoints();
409 return projectedPointL2LImpl(l1, l2).projectedPoints();
414 return projectedPointL2LImpl(l1, l2).projectedPoints();
419 return projectedPointL2PImpl(lineString, pointToProject).p;
422 return projectedPointL2PImpl(lineString, pointToProject).p;
426 return projectedPointL2PImpl(lineString, pointToProject).p;
429 return projectedPointL2PImpl(lineString, pointToProject).p;
433 return projectedPointL2PImpl(lineString, pointToProject).p;
436 return projectedPointL2PImpl(lineString, pointToProject).p;
443 return projectedPointL2PImpl(lineString, pointToProject).segment();
446 return projectedPointL2PImpl(lineString, pointToProject).segment();
450 return projectedPointL2PImpl(lineString, pointToProject).segment();
453 return projectedPointL2PImpl(lineString, pointToProject).segment();
457 return projectedPointL2PImpl(lineString, pointToProject).segment();
460 return projectedPointL2PImpl(lineString, pointToProject).segment();
464 return projectedPointL2PImpl(lineString, pointToProject).segment();
467 return projectedPointL2PImpl(lineString, pointToProject).segment();
471 return projectedPointL2PImpl(lineString, pointToProject).segment();
474 return projectedPointL2PImpl(lineString, pointToProject).segment();
478 return projectedPoint(segment.first, segment.second, pointToProject);
481 return projectedPoint(segment.first, segment.second, pointToProject);