diff --git a/transit/world_feed/feed_helpers.cpp b/transit/world_feed/feed_helpers.cpp index 32c54dfa36..1eabc94365 100644 --- a/transit/world_feed/feed_helpers.cpp +++ b/transit/world_feed/feed_helpers.cpp @@ -283,4 +283,104 @@ gtfs::StopTimes GetStopTimesForTrip(gtfs::StopTimes const & allStopTimes, }); return res; } + +void UpdateLinePart(LineParts & lineParts, LineSegment const & segment, + m2::PointD const & startPoint, TransitId commonLineId, + m2::PointD const & startPointParallel) +{ + if (auto it = FindLinePart(lineParts, segment); it == lineParts.end()) + { + LinePart lp; + lp.m_segment = segment; + lp.m_commonLines[commonLineId] = startPointParallel; + lp.m_firstPoint = startPoint; + lineParts.push_back(lp); + } + else + { + it->m_commonLines[commonLineId] = startPointParallel; + } +} + +std::pair FindIntersections(std::vector const & line1, + std::vector const & line2) +{ + double const eps = 1e-5; + size_t constexpr minIntersection = 2; + + CHECK_GREATER_OR_EQUAL(line1.size(), minIntersection, ()); + CHECK_GREATER_OR_EQUAL(line2.size(), minIntersection, ()); + + std::pair intersections; + + // Find start indexes of line1 and line2 intersections. + size_t i = 0; + + while (i < line1.size() - minIntersection + 1) + { + size_t j = 0; + size_t delta = 1; + + while (j < line2.size() - minIntersection + 1) + { + size_t intersection = 0; + size_t const len = std::min(line1.size() - i, line2.size() - j); + + for (size_t k = 0; k < len; ++k) + { + if (!base::AlmostEqualAbs(line1[i + k], line2[j + k], eps)) + break; + ++intersection; + } + + if (intersection >= minIntersection) + { + intersections.first.emplace_back(i, i + intersection - 1); + intersections.second.emplace_back(j, j + intersection - 1); + delta = intersection; + break; + } + + ++j; + } + + i += delta; + } + + CHECK_EQUAL(intersections.first.size(), intersections.second.size(), ()); + + return intersections; +} + +LineParts::iterator FindLinePart(LineParts & lineParts, LineSegment const & segment) +{ + return std::find_if(lineParts.begin(), lineParts.end(), [&segment](LinePart const & linePart) { + return linePart.m_segment == segment; + }); +} + +std::optional GetIntersection(size_t start1, size_t finish1, size_t start2, + size_t finish2) +{ + int const maxStart = static_cast(std::max(start1, start2)); + int const minFinish = static_cast(std::min(finish1, finish2)); + + size_t const intersectionLen = std::max(minFinish - maxStart, 0); + + if (intersectionLen == 0) + return std::nullopt; + + return LineSegment(maxStart, maxStart + intersectionLen); +} + +int CalcSegmentOrder(size_t segIndex, size_t totalSegCount) +{ + int constexpr shapeOffsetIncrement = 2; + + int const shapeOffset = + -static_cast(totalSegCount / 2) * 2 - static_cast(totalSegCount % 2) + 1; + int const curSegOffset = shapeOffset + shapeOffsetIncrement * static_cast(segIndex); + + return curSegOffset; +} } // namespace transit diff --git a/transit/world_feed/feed_helpers.hpp b/transit/world_feed/feed_helpers.hpp index c41665be0a..0c39aef67f 100644 --- a/transit/world_feed/feed_helpers.hpp +++ b/transit/world_feed/feed_helpers.hpp @@ -1,8 +1,10 @@ #pragma once +#include "transit/transit_entities.hpp" #include "geometry/point2d.hpp" #include +#include #include #include #include @@ -71,4 +73,53 @@ void DeleteAllEntriesByIds(C & container, S const & keysForDel) inline double KmphToMps(double kmph) { return kmph * 1'000.0 / (60.0 * 60.0); } inline double MpsToKmph(double mps) { return mps / 1'000.0 * 60.0 * 60.0; } + +// We have routes with multiple lines. Each line corresponds to the geometric polyline. Lines may +// be parallel in some segments. |LinePart| represents these operlapping segments for each line. +struct LinePart +{ + // Start and end indexes on polyline. + LineSegment m_segment; + // Parallel line ids to its start points on the segment. + std::map m_commonLines; + // First coordinate of current line on the segment. It is used for determining if the line is + // co-directional or reversed regarding the main line on the segment. + m2::PointD m_firstPoint; +}; + +using LineParts = std::vector; + +// Returns iterator to the line part with equivalent segment. +LineParts::iterator FindLinePart(LineParts & lineParts, LineSegment const & segment); + +// Data required for finding parallel polyline segments and calculating offsets for each line on the +// segment. +struct LineSchemeData +{ + TransitId m_lineId = 0; + TransitId m_routeId = 0; + ShapeLink m_shapeLink; + + LineParts m_lineParts; +}; + +// Returns overlapping segments between two polylines. +std::pair FindIntersections(std::vector const & line1, + std::vector const & line2); + +// Finds item in |lineParts| equal to |segment| and updates it. If it doesn't exist it is added to +// the |lineParts|. +void UpdateLinePart(LineParts & lineParts, LineSegment const & segment, + m2::PointD const & startPoint, TransitId commonLineId, + m2::PointD const & startPointParallel); + +// Calculates start and end indexes of intersection of two segments: [start1, finish1] and [start2, +// finish2]. +std::optional GetIntersection(size_t start1, size_t finish1, size_t start2, + size_t finish2); + +// Calculates line order on segment based on two parameters: line index between all parallel lines, +// total parallel lines count. Line order must be symmetrical with respect to the сentral axis of +// the polyline. +int CalcSegmentOrder(size_t segIndex, size_t totalSegCount); } // namespace transit