LineStringGeometry.cpp
Go to the documentation of this file.
1 #include <boost/version.hpp>
2 #include <cstddef>
3 #include <utility>
4 
5 #if BOOST_VERSION < 106300 && BOOST_VERSION >= 106200
6 // Boost 1.62 is missing an iostream include...
7 #include <iostream>
8 #endif
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>
13 
17 
18 namespace lanelet {
19 namespace geometry {
20 namespace {
21 struct LineParams {
22  double sN;
23  double sD;
24  double tN;
25  double tD;
26 };
27 
28 constexpr double SmallNum = 1.e-10;
29 constexpr std::size_t RtreeThres = 50;
30 
31 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
32 inline LineParams calculateLineParams(double a, double b, double c, double d, double e, double den) {
33  // compute the line parameters of the two closest points
34  if (den < SmallNum) { // the lines are almost parallel
35  // force using point P0 on segment S1
36  // to prevent possible division by 0.0 later
37  return {0.0, 1.0, e, c};
38  }
39  LineParams lp{};
40  lp.sD = den;
41  lp.tD = den;
42  // get the closest points on the infinite lines
43  lp.sN = (b * e - c * d);
44  lp.tN = (a * e - b * d);
45  if (lp.sN < 0.0) { // sc < 0 => the s=0 edge is visible
46  lp.sN = 0.0;
47  lp.tN = e;
48  lp.tD = c;
49  } else if (lp.sN > lp.sD) { // sc > 1 => the s=1 edge is visible
50  lp.sN = lp.sD;
51  lp.tN = e + b;
52  lp.tD = c;
53  }
54 
55  return lp;
56 }
57 
58 template <typename PointT>
59 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
60 std::pair<PointT, PointT> projectedPoint(PointT p1, PointT p2, PointT q1, PointT q2) {
61  // see http://geomalgorithms.com/a07-_distance.html
62  PointT w = p1 - q1;
63  PointT u = p2 - p1;
64  PointT v = q2 - q1;
65  double a = u.dot(u);
66  double b = u.dot(v);
67  double c = v.dot(v);
68  double d = u.dot(w);
69  double e = v.dot(w);
70  auto den = a * c - b * b;
71 
72  auto lp = calculateLineParams(a, b, c, d, e, den);
73 
74  if (lp.tN < 0.0) { // tc < 0 => the t=0 edge is visible
75  lp.tN = 0.0;
76  // recompute sc for this edge
77  if (-d < 0.0) {
78  lp.sN = 0.0;
79  } else if (-d > a) {
80  lp.sN = lp.sD;
81  } else {
82  lp.sN = -d;
83  lp.sD = a;
84  }
85  } else if (lp.tN > lp.tD) { // tc > 1 => the t=1 edge is visible
86  lp.tN = lp.tD;
87  // recompute sc for this edge
88  if ((-d + b) < 0.0) {
89  lp.sN = 0;
90  } else if ((-d + b) > a) {
91  lp.sN = lp.sD;
92  } else {
93  lp.sN = (-d + b);
94  lp.sD = a;
95  }
96  }
97  // finally do the division to get sc and tc
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);
100 
101  return {p1 + (sc * u), q1 + (tc * v)}; // return the closest distance
102 }
103 
104 template <typename PointT>
105 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
106 PointT projectedPoint(PointT l1, PointT l2, PointT p) {
107  PointT v = l2 - l1;
108  PointT w = p - l1;
109 
110  const auto c1 = w.dot(v);
111  if (c1 <= 0) {
112  return l1;
113  }
114  const auto c2 = v.dot(v);
115  if (c2 <= c1) {
116  return l2;
117  }
118  const auto b = c1 / c2;
119  return v * b + l1;
120 }
121 
122 template <typename RangeT, typename Func>
123 void distForAllSegments(const RangeT& r, Func f) {
124  if (r.size() == 1) {
125  f(r.front(), r.front());
126  }
127  for (auto first = r.begin(), second = r.begin() + 1; second != r.end(); ++first, ++second) {
128  if (f(*first, *second) == 0.) {
129  break;
130  }
131  }
132 }
133 
134 namespace bg = boost::geometry;
135 namespace bgi = bg::index;
136 namespace bgm = boost::geometry::model;
137 
138 template <typename Point1T, typename Point2T, typename BasicPointT>
139 struct ProjectedPoint2L2Result {
140  using Point1 = Point1T;
141  using Point2 = Point2T;
142  using Segm1 = Segment<Point1T>;
143  using Segm2 = Segment<Point2T>;
144  using BasicPoint = BasicPointT;
145 
146  bool valid() const { return !!distance; }
147  double update(Segm1 segm1, Segm2 segm2) {
148  auto projPair = projectedPoint(utils::toBasicPoint(segm1.first), utils::toBasicPoint(segm1.second),
149  utils::toBasicPoint(segm2.first), utils::toBasicPoint(segm2.second));
150  const auto dNew = (projPair.first - projPair.second).norm();
151  if (!distance || *distance > dNew) {
152  distance = dNew;
153  this->segm1 = segm1;
154  this->segm2 = segm2;
155  this->p1 = projPair.first;
156  this->p2 = projPair.second;
157  }
158  return *distance;
159  }
160  std::pair<BasicPoint, BasicPoint> projectedPoints() const { return std::make_pair(p1, p2); }
161 
162  ProjectedPoint2L2Result swap() const { return {segm2, segm1, p2, p1, distance}; }
163 
164  Segm1 segm1;
165  Segm2 segm2;
166  BasicPoint p1;
167  BasicPoint p2;
168  Optional<double> distance;
169 };
170 
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>>>;
175 
176 template <typename PointT, typename BasicPointT>
177 struct ProjectedPointL2PResult {
178  using Point = PointT;
179  using Segm = Segment<PointT>;
180  using BasicPoint = BasicPointT;
181 
182  bool valid() const { return !!distance; }
183  double update(Segm segm, BasicPoint p) {
184  auto projP = projectedPoint(utils::toBasicPoint(segm.first), utils::toBasicPoint(segm.second), p);
185  const auto dNew = (projP - p).norm();
186  if (!distance || *distance > dNew) {
187  distance = dNew;
188  this->segm = segm;
189  this->p = projP;
190  }
191  return *distance;
192  }
193  Segment<Point> segment() const { return segm; }
194 
195  Segm segm{};
196  BasicPoint p{};
197  Optional<double> distance;
198 };
199 
200 template <typename LineStringT>
201 using ProjectedPointL2POnLinestring = ProjectedPointL2PResult<traits::ConstPointT<traits::PointType<LineStringT>>,
202  traits::BasicPointT<traits::PointType<LineStringT>>>;
203 
204 template <typename PointT>
205 auto toSegment(const bgm::pointing_segment<const PointT>& s) {
206  return std::make_pair(*s.first, *s.second);
207 }
208 
209 template <typename PointT>
210 auto toBasicSegment(const bgm::pointing_segment<const PointT>& s) {
211  return std::make_pair(utils::toBasicPoint(*s.first), utils::toBasicPoint(*s.second));
212 }
213 
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>;
219  constexpr auto Dim = traits::PointTraits<TreePointT>::Dimension;
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>>;
223  const auto values =
224  utils::transform(bg::segments_begin(greaterRange), bg::segments_end(greaterRange), [](const auto& segm) {
225  Box box;
226  boost::geometry::envelope(segm, box);
227  return Node(box, toSegment(segm));
228  });
229  RTree tree(values.begin(), values.end());
230 
231  using PointT = traits::ConstPointT<traits::PointType<LineString1T>>;
232  auto result = ProjectedPointL2LOnLinestring<LineString1T, LineString2T>{};
233  distForAllSegments(smallerRange, [&](auto&& psmall1, auto&& psmall2) {
234  Box queryBox;
235  auto segsmall = utils::toBasicSegment(std::make_pair(psmall1, 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;
239  auto dBox = boost::geometry::distance(nearest.first, queryBox);
240  if (!!result.distance && *(result.distance) < dBox) {
241  break;
242  }
243  result.update(std::make_pair(psmall1, psmall2), nearest.second);
244  }
245  return *result.distance;
246  });
247  return result;
248 }
249 
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));
257  });
258  return *result.distance;
259  });
260  return result;
261 }
262 
263 template <typename LineStringT>
264 ProjectedPointL2POnLinestring<LineStringT> projectedPointL2PBruteForce(
265  const LineStringT& ls, const traits::BasicPointT<traits::PointType<LineStringT>>& point) {
266  auto result = ProjectedPointL2POnLinestring<LineStringT>{};
267  distForAllSegments(ls, [&](auto&& pl1, auto&& pl2) { return result.update(std::make_pair(pl1, pl2), point); });
268  return result;
269 }
270 
271 template <typename LineStringT>
272 ProjectedPointL2POnLinestring<LineStringT> projectedPointL2PWithTree(
273  const LineStringT& ls, const traits::BasicPointT<traits::PointType<LineStringT>>& p) {
274  using TreePointT = traits::ConstPointT<traits::PointType<LineStringT>>;
275  using TreeSegmT = Segment<TreePointT>;
276  constexpr auto Dim = traits::PointTraits<TreePointT>::Dimension;
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>>;
280 
281  const auto values = utils::transform(bg::segments_begin(ls), bg::segments_end(ls), [](const auto& segm) {
282  Box box;
283  boost::geometry::envelope(toBasicSegment(segm), box);
284  return Node(box, toSegment(segm));
285  });
286  RTree tree(values.begin(), values.end());
287 
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;
291  auto dBox = boost::geometry::distance(nearest.first, p);
292  if (!!result.distance && *(result.distance) < dBox) {
293  break;
294  }
295  if (result.update(nearest.second, p) == 0.) {
296  break;
297  }
298  }
299  return result;
300 }
301 
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!");
306  }
307  if (greaterRange.size() < RtreeThres) {
308  return projectedPointL2LBruteForce(smallerRange, greaterRange);
309  }
310  return projectedPointL2LWithTree(smallerRange, greaterRange);
311 }
312 
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);
317  }
318  return projectedPointOrdered(l2, l1).swap();
319 }
320 
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);
325  }
326  return projectedPointL2PWithTree(ls, p);
327 }
328 } // namespace
329 
330 namespace internal {
331 // 2d
332 std::pair<BasicPoint2d, BasicPoint2d> projectedPoint2d(const CompoundHybridLineString2d& l1,
333  const CompoundHybridLineString2d& l2) {
334  return projectedPointL2LImpl(l1, l2).projectedPoints();
335 }
336 
337 std::pair<BasicPoint2d, BasicPoint2d> projectedPoint2d(const ConstHybridLineString2d& l1,
338  const CompoundHybridLineString2d& l2) {
339  return projectedPointL2LImpl(l1, l2).projectedPoints();
340 }
341 
342 std::pair<BasicPoint2d, BasicPoint2d> projectedPoint2d(const CompoundHybridLineString2d& l1,
343  const ConstHybridLineString2d& l2) {
344  return projectedPointL2LImpl(l1, l2).projectedPoints();
345 }
346 
347 std::pair<BasicPoint2d, BasicPoint2d> projectedPoint2d(const ConstHybridLineString2d& l1,
348  const ConstHybridLineString2d& l2) {
349  return projectedPointL2LImpl(l1, l2).projectedPoints();
350 }
351 
352 std::pair<BasicPoint2d, BasicPoint2d> projectedPoint2d(const ConstHybridLineString2d& l1, const BasicLineString2d& l2) {
353  return projectedPointL2LImpl(l1, l2).projectedPoints();
354 }
355 
356 std::pair<BasicPoint2d, BasicPoint2d> projectedPoint2d(const BasicLineString2d& l1, const ConstHybridLineString2d& l2) {
357  return projectedPointL2LImpl(l1, l2).projectedPoints();
358 }
359 
360 std::pair<BasicPoint2d, BasicPoint2d> projectedPoint2d(const BasicLineString2d& l1, const BasicLineString2d& l2) {
361  return projectedPointL2LImpl(l1, l2).projectedPoints();
362 }
363 
364 std::pair<BasicPoint2d, BasicPoint2d> projectedBorderPoint2d(const ConstHybridPolygon2d& l1,
365  const ConstHybridPolygon2d& l2) {
366  return projectedPointL2LImpl(l1, l2).projectedPoints();
367 }
368 
369 std::pair<BasicPoint2d, BasicPoint2d> projectedBorderPoint2d(const CompoundHybridPolygon2d& l1,
370  const CompoundHybridPolygon2d& l2) {
371  return projectedPointL2LImpl(l1, l2).projectedPoints();
372 }
373 
374 // 3d
375 std::pair<BasicPoint3d, BasicPoint3d> projectedPoint3d(const CompoundHybridLineString3d& l1,
376  const CompoundHybridLineString3d& l2) {
377  return projectedPointL2LImpl(l1, l2).projectedPoints();
378 }
379 
380 std::pair<BasicPoint3d, BasicPoint3d> projectedPoint3d(const ConstHybridLineString3d& l1,
381  const CompoundHybridLineString3d& l2) {
382  return projectedPointL2LImpl(l1, l2).projectedPoints();
383 }
384 
385 std::pair<BasicPoint3d, BasicPoint3d> projectedPoint3d(const CompoundHybridLineString3d& l1,
386  const ConstHybridLineString3d& l2) {
387  return projectedPointL2LImpl(l1, l2).projectedPoints();
388 }
389 
390 std::pair<BasicPoint3d, BasicPoint3d> projectedPoint3d(const ConstHybridLineString3d& l1,
391  const ConstHybridLineString3d& l2) {
392  return projectedPointL2LImpl(l1, l2).projectedPoints();
393 }
394 
395 std::pair<BasicPoint3d, BasicPoint3d> projectedPoint3d(const ConstHybridLineString3d& l1, const BasicLineString3d& l2) {
396  return projectedPointL2LImpl(l1, l2).projectedPoints();
397 }
398 
399 std::pair<BasicPoint3d, BasicPoint3d> projectedPoint3d(const BasicLineString3d& l1, const ConstHybridLineString3d& l2) {
400  return projectedPointL2LImpl(l1, l2).projectedPoints();
401 }
402 
403 std::pair<BasicPoint3d, BasicPoint3d> projectedPoint3d(const BasicLineString3d& l1, const BasicLineString3d& l2) {
404  return projectedPointL2LImpl(l1, l2).projectedPoints();
405 }
406 
407 std::pair<BasicPoint3d, BasicPoint3d> projectedBorderPoint3d(const ConstHybridPolygon3d& l1,
408  const ConstHybridPolygon3d& l2) {
409  return projectedPointL2LImpl(l1, l2).projectedPoints();
410 }
411 
412 std::pair<BasicPoint3d, BasicPoint3d> projectedBorderPoint3d(const CompoundHybridPolygon3d& l1,
413  const CompoundHybridPolygon3d& l2) {
414  return projectedPointL2LImpl(l1, l2).projectedPoints();
415 }
416 
417 // project
418 BasicPoint2d project(const BasicLineString2d& lineString, const BasicPoint2d& pointToProject) {
419  return projectedPointL2PImpl(lineString, pointToProject).p;
420 }
421 BasicPoint3d project(const BasicLineString3d& lineString, const BasicPoint3d& pointToProject) {
422  return projectedPointL2PImpl(lineString, pointToProject).p;
423 }
424 
425 BasicPoint2d project(const ConstHybridLineString2d& lineString, const BasicPoint2d& pointToProject) {
426  return projectedPointL2PImpl(lineString, pointToProject).p;
427 }
428 BasicPoint3d project(const ConstHybridLineString3d& lineString, const BasicPoint3d& pointToProject) {
429  return projectedPointL2PImpl(lineString, pointToProject).p;
430 }
431 
432 BasicPoint2d project(const CompoundHybridLineString2d& lineString, const BasicPoint2d& pointToProject) {
433  return projectedPointL2PImpl(lineString, pointToProject).p;
434 }
435 BasicPoint3d project(const CompoundHybridLineString3d& lineString, const BasicPoint3d& pointToProject) {
436  return projectedPointL2PImpl(lineString, pointToProject).p;
437 }
438 
439 } // namespace internal
440 
441 // closestsegment
442 Segment<BasicPoint2d> closestSegment(const BasicLineString2d& lineString, const BasicPoint2d& pointToProject) {
443  return projectedPointL2PImpl(lineString, pointToProject).segment();
444 }
445 Segment<BasicPoint3d> closestSegment(const BasicLineString3d& lineString, const BasicPoint3d& pointToProject) {
446  return projectedPointL2PImpl(lineString, pointToProject).segment();
447 }
448 
449 Segment<ConstPoint2d> closestSegment(const ConstLineString2d& lineString, const BasicPoint2d& pointToProject) {
450  return projectedPointL2PImpl(lineString, pointToProject).segment();
451 }
452 Segment<ConstPoint3d> closestSegment(const ConstLineString3d& lineString, const BasicPoint3d& pointToProject) {
453  return projectedPointL2PImpl(lineString, pointToProject).segment();
454 }
455 
457  return projectedPointL2PImpl(lineString, pointToProject).segment();
458 }
460  return projectedPointL2PImpl(lineString, pointToProject).segment();
461 }
462 
463 Segment<ConstPoint2d> closestSegment(const CompoundLineString2d& lineString, const BasicPoint2d& pointToProject) {
464  return projectedPointL2PImpl(lineString, pointToProject).segment();
465 }
466 Segment<ConstPoint3d> closestSegment(const CompoundLineString3d& lineString, const BasicPoint3d& pointToProject) {
467  return projectedPointL2PImpl(lineString, pointToProject).segment();
468 }
469 
471  return projectedPointL2PImpl(lineString, pointToProject).segment();
472 }
474  return projectedPointL2PImpl(lineString, pointToProject).segment();
475 }
476 
477 BasicPoint2d project(const BasicSegment2d& segment, const BasicPoint2d& pointToProject) {
478  return projectedPoint(segment.first, segment.second, pointToProject);
479 }
480 BasicPoint3d project(const BasicSegment3d& segment, const BasicPoint3d& pointToProject) {
481  return projectedPoint(segment.first, segment.second, pointToProject);
482 }
483 } // namespace geometry
484 } // namespace lanelet
Polygon with access to primitive points.
double sD
BasicPoint p
Segm segm
Segm2 segm2
BasicPoints3d BasicLineString3d
A Linestring that returns BasicPoint2d instead of Point2d.
typename PointTraits< PointT >::BasicPoint BasicPointT
Definition: Traits.h:156
double tN
A Compound linestring in 3d (returns Point3d)
auto transform(Iterator begin, Iterator end, const Func f)
Definition: Utilities.h:176
Eigen::Vector3d BasicPoint3d
a simple 3d-point
Combines multiple linestrings to one polygon in 3d that returns BasicPoint3d.
std::pair< PointT, PointT > Segment
Segment< BasicPoint3d > BasicSegment3d
A hybrid compound linestring in 2d (returns BasicPoint2d)
BasicPoint3d project(const LineString3dT &lineString, const BasicPoint3d &pointToProject)
Projects the given point in 3d to the LineString.
A normal 3d linestring with immutable data.
IfLS< LineString2dT, std::pair< BasicPoint2d, BasicPoint2d > > projectedPoint2d(const LineString2dT &l1, const LineString2dT &l2)
Computes the projected points on the two linestrings for the shortest distance.
BasicPoint p2
Segm1 segm1
std::pair< BasicPoint2d, BasicPoint2d > projectedBorderPoint2d(const CompoundHybridPolygon2d &l1, const CompoundHybridPolygon2d &l2)
double tD
Polygon with access to primitive points.
A Compound linestring in 2d (returns Point2d)
BasicPoints2d BasicLineString2d
Optional< double > distance
A Linestring that returns BasicPoint3d instead of Point3d.
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > BasicPoint2d
a simple 2d-point
auto toBasicSegment(const Segment< PointT > &s)
BasicPoint p1
Segment< BasicPoint2d > closestSegment(const BasicLineString2d &lineString, const BasicPoint2d &pointToProject)
find the segment on a 2d line string that is closest to a given point, determined by boost::geometry:...
A hybrid compound linestring in 3d (returns BasicPoint3d)
Combines multiple linestrings to one polygon in 2d that returns BasicPoint2d.
auto toBasicPoint(const PointT &point) -> std::enable_if_t< PointTraits< PointT >::IsPrimitive, BasicPointT< PointT >>
Definition: Traits.h:165
IfLS< LineString3dT, std::pair< BasicPoint3d, BasicPoint3d > > projectedPoint3d(const LineString3dT &l1, const LineString3dT &l2)
Computes the projected points on the two linestrings for the shortest distance.
IfPoly< Polygon3dT, std::pair< BasicPoint3d, BasicPoint3d > > projectedBorderPoint3d(const Polygon3dT &l1, const Polygon3dT &l2)
A normal 2d linestring with immutable data.
double sN
Segment< BasicPoint2d > BasicSegment2d
static constexpr Dimensions Dimension
Definition: Traits.h:138


lanelet2_core
Author(s): Fabian Poggenhans
autogenerated on Tue Jun 6 2023 02:23:32