2 #include <boost/geometry/algorithms/equals.hpp>
3 #include <boost/geometry/algorithms/intersection.hpp>
32 struct GetGeometry<T,
IfLS<T, void>> {
42 inline auto crossProd(
const Eigen::Matrix<double, 2, 1>&
p1,
const Eigen::Matrix<double, 2, 1>&
p2) {
46 template <
typename LineStringT,
typename BasicPo
intT>
48 return std::find_if(ls.begin(), ls.end(), [&
p](
const auto& elem) { return boost::geometry::equals(elem, p); });
51 template <
typename Po
intT>
52 bool pointIsLeftOf(
const PointT& pSeg1,
const PointT& pSeg2,
const PointT&
p) {
53 return crossProd(PointT(pSeg2 - pSeg1), PointT(
p - pSeg1)).z() > 0;
56 template <
typename LineStringT>
57 LineStringT
invert(
const LineStringT& ls) {
71 template <
typename Po
intT>
77 template <
typename LineStringT,
typename BasicPo
intT>
83 auto nextSegPointIt = std::next(
findPoint(ls, pSeg2));
84 if (nextSegPointIt != ls.end()) {
87 boost::geometry::convert(*nextSegPointIt, nextSegPoint);
127 template <
typename LineStringT,
typename Po
intT>
128 std::pair<double, ProjectedPointInfo<traits::BasicPointT<PointT>>>
signedDistanceImpl(
const LineStringT lineString,
135 const auto isLeft =
isLeftOf(lineString,
p, ppInfo);
136 return {isLeft ?
d : -
d, ppInfo};
139 template <
typename Hybr
idLineStringT>
141 const size_t startIdx,
142 const size_t endIdx,
const double distance) {
143 if (hLineString.size() < startIdx) {
144 throw InvalidInputError(std::string(
"Linestring point out of bounds. Linestring size ") +
145 std::to_string(hLineString.size()) +
", startIdx " + std::to_string(startIdx));
147 if (hLineString.size() < endIdx) {
148 throw InvalidInputError(std::string(
"Linestring point out of bounds. Linestring size ") +
149 std::to_string(hLineString.size()) +
", endIdx " + std::to_string(endIdx));
151 if (startIdx == endIdx) {
153 std::string(
"Can't determine shift direction from two identical points on linestring. Point index ") +
154 std::to_string(startIdx));
156 const auto dx(hLineString[endIdx](0) - hLineString[startIdx](0));
157 const auto dy(hLineString[endIdx](1) - hLineString[startIdx](1));
159 return projStart + Eigen::Vector2d(-dy, dx).normalized() *
distance;
168 template <
typename LineString2dT>
174 if (idx + 1 == lineString.size()) {
176 BasicPoint2d::Zero()};
190 template <
typename LineString2dT>
193 Eigen::Vector2d perpendicular;
194 double realOffset =
offset;
195 const auto epsilon{1.e-5};
198 }
else if (idx + 1 == lineString.size()) {
202 auto minussin2 = perpendicular.norm() / 2;
203 realOffset = (minussin2 > epsilon) ?
offset / minussin2 : 0;
206 Eigen::Vector2d direction(-perpendicular(1), perpendicular(0));
213 template <
typename LineString2dT>
214 bool isConvex(
const LineString2dT& lineString,
const size_t idx,
const bool offsetPositive) {
215 if (idx != 0 && idx + 1 != lineString.size()) {
216 auto isLeft = pointIsLeftOf<BasicPoint2d>(lineString[idx - 1], lineString[idx], lineString[idx + 1]);
217 return offsetPositive ? !isLeft : isLeft;
224 template <
typename LineString2dT>
226 const bool offsetPositive) {
227 if (!
isConvex(lineString, idx, offsetPositive)) {
236 template <
typename LineStringT>
238 auto idxs = sortAlongSIdxs(ls, points);
240 ret.reserve(idxs.size());
241 for (
const auto& i : idxs) {
242 ret.emplace_back(points.at(i));
254 template <
typename LineString2dT>
256 if (std::abs(idx) + 1 > lineString.size()) {
259 int startIdx = (idx >= 0) ? std::max(0, idx - 1) : std::max(0,
static_cast<int>(lineString.size()) + idx - 1);
260 int endIdx = (idx >= 0)
261 ? std::min(idx + 1,
static_cast<int>(lineString.size()) - 1)
262 : std::min(
static_cast<int>(lineString.size()) + idx + 1,
static_cast<int>(lineString.size()) - 1);
263 int pIdx = (idx >= 0) ? idx :
static_cast<int>(lineString.size()) + idx;
268 namespace bgi = boost::geometry::index;
284 template <
typename LineString2dT>
286 if (lineString.size() < 2) {
290 std::vector<BasicSegment2d> segContainer;
291 segContainer.reserve(lineString.size() - 1);
292 for (
size_t i = 0; i < lineString.size() - 1; ++i) {
293 segContainer.emplace_back(
296 tree.insert(segContainer);
308 if (lineString.size() < 2) {
312 std::vector<std::pair<BasicSegment2d, size_t>> segContainer;
313 segContainer.reserve(lineString.size() - 1);
314 for (
size_t i = 0; i < lineString.size() - 1; ++i) {
315 segContainer.emplace_back(std::make_pair(
BasicSegment2d{lineString.at(i), lineString.at(i + 1)}, i));
317 tree.insert(segContainer);
328 double curMinS = std::numeric_limits<double>::max();
330 for (
size_t i = 0; i < selfIntersections.size(); ++i) {
331 const auto& ci = selfIntersections.at(i);
332 if (ci.firstSegment.s > minS && ci.firstSegment.s < curMinS) {
334 curMinS = ci.firstSegment.s;
350 for (
auto it = tree.qbegin(bgi::intersects(seg)); it != tree.qend(); ++it) {
351 const auto& otherSegIdx = it->second;
352 const auto& otherSeg = it->first;
353 if (otherSeg.first != seg.second && otherSeg.second != seg.first && otherSeg.first != seg.first &&
354 otherSegIdx > segToCheck) {
356 boost::geometry::intersection(seg, otherSeg, intersectionPoints);
357 const auto intersectionPoint = intersectionPoints.front();
358 auto firstS = (intersectionPoint - seg.first).norm();
359 auto lastS = (intersectionPoint - otherSeg.first).norm();
365 return curSegIntersections;
383 const double lastS,
const size_t i) {
384 if (!curSegIntersections.empty()) {
385 auto possibeNextIntersection =
getLowestSAbove(curSegIntersections, lastS);
386 if (possibeNextIntersection) {
387 const auto& ni = curSegIntersections.at(*possibeNextIntersection);
388 return PointSearchResult{ni.intersectionPoint, ni.lastSegment.idx, ni.lastSegment.s};
395 if (lineString.size() <= 3) {
402 while (i < lineString.size() - 2) {
405 auto np =
findNextPoint(curSegIntersections, curSeg, lastS, i);
406 newLS.emplace_back(np.nextPoint);
410 newLS.push_back(lineString.back());
422 template <
typename LineString2dT>
424 const double epsilon = 1.e-7) {
426 for (
const auto&
p : offsetLS) {
427 auto it = tree.qbegin(internal::bgi::nearest(
p, 1));
444 template <
typename LineString2dT>
447 if (idx == 0 && asLast) {
448 throw GeometryError(
"Can't shift first point of line string as endpoint of segment");
450 if (idx + 1 == lineString.size() && !asLast) {
451 throw GeometryError(
"Can't shift last point of line string as start point of segment");
453 Eigen::Vector2d perpendicular = asLast ? pv.
preceding.normalized() : pv.
following.normalized();
454 Eigen::Vector2d norm(-perpendicular(1), perpendicular(0));
467 template <
typename LineString2dT>
471 throw GeometryError(
"Can't shift first point of line string as sharp convex");
473 if (idx + 1 == lineString.size()) {
474 throw GeometryError(
"Can't shift last point of line string as sharp convex");
479 auto overshoot =
distance * std::tan(M_PI_4 - alpha / 4);
480 return {firstP + pv.
preceding.normalized() * overshoot, lastP - pv.
following.normalized() * overshoot};
491 template <
typename LineString2dT>
492 inline std::pair<BasicLineString2d, bool>
shiftPoint(
const LineString2dT& lineString,
const double distance,
513 template <
typename LineString2dT>
515 std::vector<BasicLineString2d> offsets;
517 while (curIdx + 1 < lineString.size()) {
520 for (
size_t i = curIdx + 1; i < lineString.size(); ++i) {
524 ls.insert(ls.end(), shiftResult.first.begin(), shiftResult.first.end());
525 if (!shiftResult.second) {
529 offsets.push_back(ls);
551 for (
size_t i = 0; i < convexSubStrings.size(); ++i) {
552 const auto& ss = convexSubStrings.at(i);
553 for (
size_t j = 0; j + 1 < ss.size(); ++j) {
555 tree.insert(std::make_pair(seg, idx));
574 if (convexSubStrings.empty()) {
577 if (convexSubStrings.size() == 1) {
578 return convexSubStrings.front();
584 while (i < convexSubStrings.size()) {
585 const auto& ls = convexSubStrings.at(i);
586 for (
size_t j = 0; j + 1 < ls.size(); ++j) {
589 auto np =
findNextPoint(curSegIntersections, curSeg, lastS, idx);
590 newLS.emplace_back(np.nextPoint);
592 if (idx >= segMap.size()) {
593 i = convexSubStrings.size();
597 if (segMap.at(idx).lineStringIdx != i) {
598 i = segMap.at(idx).lineStringIdx;
603 newLS.push_back(convexSubStrings.back().back());
609 template <
typename LineStringIterator>
610 double rangedLength(LineStringIterator start, LineStringIterator end) {
616 template <
typename LineStringT>
618 std::vector<double> lengths;
619 if (lineString.size() <= 1) {
622 if (lineString.size() == 2) {
625 const auto totalLength = length(lineString);
626 lengths.reserve(lineString.size() - 1);
627 helper::forEachPair(lineString.begin(), lineString.end(), [&lengths, &totalLength](
const auto&
p1,
const auto&
p2) {
628 lengths.push_back(distance(p1, p2) / totalLength);
633 template <
typename LineStringT>
636 helper::forEachPair(lengths.begin(), lengths.end(), [](
const auto& l1,
auto& l2) { l2 += l1; });
640 template <
typename LineStringT>
642 assert(!lineString.empty());
648 double currentCumulativeLength = 0.0;
649 for (
auto first = lineString.begin(), second = std::next(lineString.begin()); second != lineString.end();
654 currentCumulativeLength += currentLength;
655 if (currentCumulativeLength >= dist) {
656 double remainingDistance = dist - (currentCumulativeLength - currentLength);
657 if (remainingDistance < 1.e-8) {
660 return p1 + remainingDistance / currentLength * (
p2 -
p1);
666 template <
typename LineStringT>
669 assert(!lineString.empty());
674 double currentCumulativeLength = 0.0;
675 for (
auto first = lineString.begin(), second = std::next(lineString.begin()); second != lineString.end();
677 const auto&
p1 = *first;
678 const auto&
p2 = *second;
680 currentCumulativeLength += currentLength;
681 if (currentCumulativeLength >= dist) {
682 double remainingDistance = dist - (currentCumulativeLength - currentLength);
683 if (remainingDistance > currentLength / 2.0) {
689 return lineString.back();
692 template <
typename LineString3dT>
694 static_assert(traits::is3D<LineString3dT>(),
"Please call this function with a 3D type!");
698 template <
typename LineString2dT>
700 static_assert(traits::is2D<LineString2dT>(),
"Please call this function with a 2D type!");
704 template <
typename Po
int2dT>
709 template <
typename Po
int2dT>
712 const double area = 0.5 * ((
p2.x() -
p1.x()) * (p3.y() -
p1.y()) - (
p2.y() -
p1.y()) * (p3.x() -
p1.x()));
716 const double product = side1 * side2 * side3;
717 if (product < 1e-20) {
718 return std::numeric_limits<double>::infinity();
720 return 4 * area / product;
723 template <
typename LineString2dT>
726 auto dist = res.first;
727 const auto& projectedPoint = res.second;
730 auto accumulateLength = [&](
const auto& first,
const auto& second) {
731 if (boost::geometry::equals(first, projectedPoint.closestSegment.first)) {
738 length +=
distance(projectedPoint.closestSegment.first, projectedPoint.projectedPoint);
739 return {length, dist};
742 template <
typename LineString3dT>
744 static_assert(traits::is3D<LineString3dT>(),
"Please call this function with a 3D type!");
746 for (
const auto&
p : lineString) {
752 template <
typename LineString2dT>
761 template <
typename LineString3dT,
typename>
763 static_assert(traits::is3D<LineString3dT>(),
"Please call this function with a 3D type!");
767 template <
typename LineString2dT,
typename>
769 static_assert(traits::is2D<LineString2dT>(),
"Please call this function with a 2D type!");
773 template <
typename LineString3dT>
775 double heightTolerance) {
779 boost::geometry::intersection(ls2d, ols2d, intersections);
780 auto distanceSmallerTolerance = [heightTolerance, &linestring, &otherLinestring](
const auto& point) {
783 return distance(pProj1, pProj2) < heightTolerance;
785 return std::any_of(intersections.begin(), intersections.end(), distanceSmallerTolerance);
788 template <
typename LineString2dT>
790 const LineString2dT& l2) {
791 static_assert(traits::is2D<LineString2dT>(),
"Please call this function with a 2D type!");
795 template <
typename LineString3dT>
797 const LineString3dT& l2) {
798 static_assert(traits::is3D<LineString3dT>(),
"Please call this function with a 3D type!");
802 template <
typename LineString3d1T,
typename LineString3d2T>
805 return (projPoint.first - projPoint.second).norm();
808 template <
typename LineString1T,
typename LineString2T>
809 std::pair<LineString1T, LineString2T>
align(LineString1T left, LineString2T right) {
812 if ((left.size() <= 1 && right.size() <= 1) || right.empty() || left.empty()) {
813 return {left, right};
815 auto getMiddlePoint = [](
auto& ls) {
819 bool rightOfLeft =
signedDistance(left, getMiddlePoint(right)) < 0;
820 if (!rightOfLeft && left.size() > 1) {
821 left = left.invert();
824 bool leftOfRight =
signedDistance(right, getMiddlePoint(left)) > 0;
825 if (!leftOfRight && right.size() > 1) {
826 right = right.invert();
828 return {left, right};
831 template <
typename LineString2dT>
833 if (lineString.size() < 2) {
838 const auto llength = length(lineString);
841 for (
size_t i = 0; i < ratios.size(); ++i) {
842 if (ratios.at(i) * llength > arcCoords.
length) {
849 endIdx = lineString.size() - 1;
850 startIdx = endIdx - 1;
853 startIdx, endIdx, arcCoords.
distance);
856 template <
typename LineString2dT>
864 template <
typename LineString2dT>