[routing] Extended navigation info about roads

2nd iteration for #849

Extended info about next street/road:
- for next street it will show "[ref] name" .
- for highway links - "[junction:ref]: [target:ref] > target".
If no target - it will be replaced by name of next street.
If no [target:ref] - it will be replaced by [ref] of next road.

Signed-off-by: Anton Makouski <anton.makouski@gmail.com>
This commit is contained in:
Anton Makouski 2022-06-01 20:13:57 +03:00
parent cd8c8c3da6
commit 08fdbde470
14 changed files with 156 additions and 46 deletions

View file

@ -55,7 +55,8 @@ public class Metadata implements Parcelable
FMD_CONTACT_VK(35),
FMD_CONTACT_LINE(36),
FMD_DESTINATION(37),
FMD_DESTINATION_REF(38);
FMD_DESTINATION_REF(38),
FMD_JUNCTION_REF(39);
private final int mMetaType;
MetadataType(int metadataType)

View file

@ -148,7 +148,15 @@ string MetadataTagProcessorImpl::ValidateAndFormat_ele(string const & v) const
string MetadataTagProcessorImpl::ValidateAndFormat_destination(string const & v) const
{
return v;
string r;
strings::Tokenize(v, ";", [&](std::string_view d)
{
if (r.empty())
r = d;
else
r += "; " + string(strings::Trim(d));
});
return r;
}
string MetadataTagProcessorImpl::ValidateAndFormat_destination_ref(string const & v) const
@ -156,6 +164,11 @@ string MetadataTagProcessorImpl::ValidateAndFormat_destination_ref(string const
return v;
}
string MetadataTagProcessorImpl::ValidateAndFormat_junction_ref(string const & v) const
{
return v;
}
string MetadataTagProcessorImpl::ValidateAndFormat_turn_lanes(string const & v) const
{
return v;
@ -458,6 +471,7 @@ void MetadataTagProcessor::operator()(std::string const & k, std::string const &
case Metadata::FMD_ELE: valid = ValidateAndFormat_ele(v); break;
case Metadata::FMD_DESTINATION: valid = ValidateAndFormat_destination(v); break;
case Metadata::FMD_DESTINATION_REF: valid = ValidateAndFormat_destination_ref(v); break;
case Metadata::FMD_JUNCTION_REF: valid = ValidateAndFormat_junction_ref(v); break;
case Metadata::FMD_TURN_LANES: valid = ValidateAndFormat_turn_lanes(v); break;
case Metadata::FMD_TURN_LANES_FORWARD: valid = ValidateAndFormat_turn_lanes_forward(v); break;
case Metadata::FMD_TURN_LANES_BACKWARD: valid = ValidateAndFormat_turn_lanes_backward(v); break;

View file

@ -18,6 +18,7 @@ struct MetadataTagProcessorImpl
std::string ValidateAndFormat_ele(std::string const & v) const;
std::string ValidateAndFormat_destination(std::string const & v) const;
std::string ValidateAndFormat_destination_ref(std::string const & v) const;
std::string ValidateAndFormat_junction_ref(std::string const & v) const;
std::string ValidateAndFormat_turn_lanes(std::string const & v) const;
std::string ValidateAndFormat_turn_lanes_forward(std::string const & v) const;
std::string ValidateAndFormat_turn_lanes_backward(std::string const & v) const;

View file

@ -76,6 +76,8 @@ bool Metadata::TypeFromString(string const & k, Metadata::EType & outType)
outType = Metadata::FMD_DESTINATION;
else if (k == "destination:ref")
outType = Metadata::FMD_DESTINATION_REF;
else if (k == "junction:ref")
outType = Metadata::FMD_JUNCTION_REF;
else if (k == "turn:lanes")
outType = Metadata::FMD_TURN_LANES;
else if (k == "turn:lanes:forward")
@ -173,8 +175,9 @@ string ToString(Metadata::EType type)
case Metadata::FMD_CONTACT_LINE: return "contact:line";
case Metadata::FMD_INTERNET: return "internet_access";
case Metadata::FMD_ELE: return "ele";
case Metadata::FMD_DESTINATION: return "desination";
case Metadata::FMD_DESTINATION_REF: return "desination:ref";
case Metadata::FMD_DESTINATION: return "destination";
case Metadata::FMD_DESTINATION_REF: return "destination:ref";
case Metadata::FMD_JUNCTION_REF: return "junction:ref";
case Metadata::FMD_TURN_LANES: return "turn:lanes";
case Metadata::FMD_TURN_LANES_FORWARD: return "turn:lanes:forward";
case Metadata::FMD_TURN_LANES_BACKWARD: return "turn:lanes:backward";

View file

@ -155,6 +155,7 @@ public:
FMD_CONTACT_LINE = 36,
FMD_DESTINATION = 37,
FMD_DESTINATION_REF = 38,
FMD_JUNCTION_REF = 39,
FMD_COUNT
};

View file

@ -181,6 +181,7 @@ std::vector<Props> MetadataToProps(std::vector<T> const & metadata)
case Metadata::FMD_DESTINATION:
case Metadata::FMD_DESTINATION_REF:
case Metadata::FMD_JUNCTION_REF:
case Metadata::FMD_TURN_LANES:
case Metadata::FMD_TURN_LANES_FORWARD:
case Metadata::FMD_TURN_LANES_BACKWARD:

View file

@ -418,8 +418,8 @@ void GetTurnDirectionBasic(IRoutingResult const & result, size_t const outgoingS
if (!GetTurnInfo(result, outgoingSegmentIndex, vehicleSettings, turnInfo))
return;
turn.m_sourceName = turnInfo.m_ingoing->m_name;
turn.m_targetName = turnInfo.m_outgoing->m_name;
turn.m_sourceName = turnInfo.m_ingoing->m_roadNameInfo.m_name;
turn.m_targetName = turnInfo.m_outgoing->m_roadNameInfo.m_name;
turn.m_turn = CarDirection::None;
ASSERT_GREATER(turnInfo.m_ingoing->m_path.size(), 1, ());
@ -531,7 +531,7 @@ size_t CheckUTurnOnRoute(IRoutingResult const & result, size_t const outgoingSeg
if (checkedSegment.m_path.size() < 2)
return 0;
if (checkedSegment.m_name == masterSegment.m_name &&
if (checkedSegment.m_roadNameInfo.m_name == masterSegment.m_roadNameInfo.m_name &&
checkedSegment.m_highwayClass == masterSegment.m_highwayClass &&
checkedSegment.m_isLink == masterSegment.m_isLink && !checkedSegment.m_onRoundabout)
{
@ -555,7 +555,7 @@ size_t CheckUTurnOnRoute(IRoutingResult const & result, size_t const outgoingSeg
}
// Avoid the UTurn on unnamed roads inside the rectangle based distinct.
if (checkedSegment.m_name.empty())
if (checkedSegment.m_roadNameInfo.m_name.empty())
return 0;
// Avoid returning to the same edge after uturn somewere else.

View file

@ -66,18 +66,12 @@ void DirectionsEngine::LoadPathAttributes(FeatureID const & featureId,
pathSegment.m_onRoundabout = ftypes::IsRoundAboutChecker::Instance()(*ft);
pathSegment.m_isOneWay = ftypes::IsOneWayChecker::Instance()(*ft);
if (pathSegment.m_isLink)
{
if (auto const & dst_number = ft->GetMetadata(feature::Metadata::FMD_DESTINATION_REF); !dst_number.empty())
pathSegment.m_name = "[" + string(dst_number) + "] ";
pathSegment.m_name += ft->GetMetadata(feature::Metadata::FMD_DESTINATION);
}
else
{
if (auto const & road_number = ft->GetRoadNumber(); !road_number.empty())
pathSegment.m_name = "[" + road_number + "] ";
pathSegment.m_name += ft->GetName(StringUtf8Multilang::kDefaultCode);
}
pathSegment.m_roadNameInfo.m_isLink = pathSegment.m_isLink;
pathSegment.m_roadNameInfo.m_junction_ref = ft->GetMetadata(feature::Metadata::FMD_JUNCTION_REF);
pathSegment.m_roadNameInfo.m_destination_ref = ft->GetMetadata(feature::Metadata::FMD_DESTINATION_REF);
pathSegment.m_roadNameInfo.m_destination = ft->GetMetadata(feature::Metadata::FMD_DESTINATION);
pathSegment.m_roadNameInfo.m_ref = ft->GetRoadNumber();
pathSegment.m_roadNameInfo.m_name = ft->GetName(StringUtf8Multilang::kDefaultCode);
}
void DirectionsEngine::GetSegmentRangeAndAdjacentEdges(IRoadGraph::EdgeListT const & outgoingEdges,
@ -355,7 +349,7 @@ RouterResultCode DirectionsEngine::MakeTurnAnnotation(IndexRoadGraph::EdgeVector
// Street names contain empty names too for avoiding of freezing of old street name while
// moving along unnamed street.
streets.emplace_back(max(junctions.size(), static_cast<size_t>(1)) - 1, loadedSegmentIt->m_name);
streets.emplace_back(max(junctions.size(), static_cast<size_t>(1)) - 1, loadedSegmentIt->m_roadNameInfo);
// Turns information.
if (!junctions.empty() && skipTurnSegments == 0)

View file

@ -22,7 +22,7 @@ struct LoadedPathSegment
{
std::vector<geometry::PointWithAltitude> m_path;
std::vector<turns::SingleLaneInfo> m_lanes;
std::string m_name;
RouteSegment::RoadNameInfo m_roadNameInfo;
double m_weight = 0.0; /*!< Time in seconds to pass the segment. */
SegmentRange m_segmentRange;
std::vector<Segment> m_segments; /*!< Traffic segments for |m_path|. */

View file

@ -30,8 +30,8 @@ size_t PedestrianDirectionsEngine::GetTurnDirection(IRoutingResult const & resul
double const turnAngle = CalcTurnAngle(result, outgoingSegmentIndex, numMwmIds, vehicleSettings);
turn.m_sourceName = turnInfo.m_ingoing->m_name;
turn.m_targetName = turnInfo.m_outgoing->m_name;
turn.m_sourceName = turnInfo.m_ingoing->m_roadNameInfo.m_name;
turn.m_targetName = turnInfo.m_outgoing->m_roadNameInfo.m_name;
turn.m_pedestrianTurn = PedestrianDirection::None;
ASSERT_GREATER(turnInfo.m_ingoing->m_path.size(), 1, ());

View file

@ -117,18 +117,41 @@ double Route::GetCurrentTimeToEndSec() const
m_poly.GetDistFromCurPointToRoutePointMeters() / curSegSpeedMPerS);
}
void Route::GetCurrentStreetName(string & name) const
void Route::GetCurrentStreetName(RouteSegment::RoadNameInfo & roadNameInfo) const
{
GetStreetNameAfterIdx(static_cast<uint32_t>(m_poly.GetCurrentIter().m_ind), name);
GetStreetNameAfterIdx(static_cast<uint32_t>(m_poly.GetCurrentIter().m_ind), roadNameInfo);
}
void Route::GetStreetNameAfterIdx(uint32_t idx, string & name) const
void Route::GetNextTurnStreetName(RouteSegment::RoadNameInfo & roadNameInfo) const
{
double distance;
TurnItem turn;
GetCurrentTurn(distance, turn);
GetStreetNameAfterIdx(turn.m_index, roadNameInfo);
}
// If exit is false, returns ref and name.
// If exit is true, returns m_junction_ref, m_destination_ref, m_destination, m_name.
// Handling of incomplete data or non-standard data:
// - We go trough 400m to find first segment with existing data.
// Bit for link we go through all segments till we reach non-link segment,
// Sometimes links are really long (e.g. 1km+ in USA) and they have no tags.
// - Normally for link both destination and destination:ref tags exist together.
// But sometimes only destination tag exists for link. And destination:ref can be calculated
// by checking next segments of route until link will end and normal r will start.
// Hopefully it will have ref tag. So we can show it instead of destination:ref.
// Also we can add info about it's name.
// - Sometimes exit is not tagged as link (e.g. if new r starts here).
// At the same time they can have all useful tags just like link.
// Usually destination:ref=ref in such cases, or only 1st part of destination:ref can match.
void Route::GetStreetNameAfterIdx(uint32_t idx, RouteSegment::RoadNameInfo & roadNameInfo) const
{
name.clear();
auto const iterIdx = m_poly.GetIterToIndex(idx);
if (!IsValid() || !iterIdx.IsValid())
return;
RouteSegment::RoadNameInfo roadNameInfoNext;
size_t i = idx;
for (; i < m_poly.GetPolyline().GetSize(); ++i)
{
@ -137,16 +160,33 @@ void Route::GetStreetNameAfterIdx(uint32_t idx, string & name) const
if (i == 0)
continue;
string const street = m_routeSegments[ConvertPointIdxToSegmentIdx(i)].GetStreet();
if (!street.empty())
auto const & r = m_routeSegments[ConvertPointIdxToSegmentIdx(i)].GetRoadNameInfo();
if (r.HasBasicTextInfo())
{
name = street;
return;
if (roadNameInfo.HasExitInfo())
roadNameInfoNext = r;
else
roadNameInfo = r;
break;
}
else if (r.HasExitInfo() && !roadNameInfo.HasExitInfo())
roadNameInfo = r;
// For exit wait for non-exit
else if (roadNameInfo.HasExitInfo() && !r.m_isLink)
continue;
auto const furtherIter = m_poly.GetIterToIndex(i);
CHECK(furtherIter.IsValid(), ());
if (m_poly.GetDistanceM(iterIdx, furtherIter) > kSteetNameLinkMeters)
return;
break;
}
if (roadNameInfo.HasExitInfo())
{
if (roadNameInfo.m_destination_ref.empty())
roadNameInfo.m_destination_ref = roadNameInfoNext.m_ref;
roadNameInfo.m_name = roadNameInfoNext.m_name;
}
}

View file

@ -64,15 +64,30 @@ public:
uint8_t m_maxSpeedKmPH = 0;
};
struct RoadNameInfo
{
bool m_isLink;
// This is for street/road. |m_ref| |m_name|.
std::string m_ref; // Number of street/road .g. "CA 85".
std::string m_name; // E.g "Johnson Ave.".
// This is for 1st segment of link after junction. Exit |junction_ref| to |m_destination_ref| for |m_destination|.
std::string m_junction_ref; // Number of junction e.g. "398B".
std::string m_destination_ref; // Number of next road, e.g. "CA 85", Sometimes "CA 85 South".
std::string m_destination; // E.g. "Cupertino".
bool HasBasicTextInfo() const { return !m_ref.empty() || !m_name.empty(); }
bool HasExitInfo() const { return m_isLink || !m_junction_ref.empty() || !m_destination_ref.empty() || !m_destination.empty(); }
RoadNameInfo() { m_isLink = false; }
};
RouteSegment(Segment const & segment, turns::TurnItem const & turn,
geometry::PointWithAltitude const & junction, std::string const & street,
geometry::PointWithAltitude const & junction, RoadNameInfo const & roadNameInfo,
double distFromBeginningMeters, double distFromBeginningMerc,
double timeFromBeginningS, traffic::SpeedGroup traffic,
std::unique_ptr<TransitInfo> transitInfo)
: m_segment(segment)
, m_turn(turn)
, m_junction(junction)
, m_street(street)
, m_roadNameInfo(roadNameInfo)
, m_distFromBeginningMeters(distFromBeginningMeters)
, m_distFromBeginningMerc(distFromBeginningMerc)
, m_timeFromBeginningS(timeFromBeginningS)
@ -89,7 +104,7 @@ public:
Segment const & GetSegment() const { return m_segment; }
Segment & GetSegment() { return m_segment; }
geometry::PointWithAltitude const & GetJunction() const { return m_junction; }
std::string const & GetStreet() const { return m_street; }
RoadNameInfo const & GetRoadNameInfo() const { return m_roadNameInfo; }
traffic::SpeedGroup GetTraffic() const { return m_traffic; }
turns::TurnItem const & GetTurn() const { return m_turn; }
@ -116,8 +131,8 @@ private:
/// The furthest point of the segment from the beginning of the route along the route.
geometry::PointWithAltitude m_junction;
/// Street name of |m_segment| if any. Otherwise |m_street| is empty.
std::string m_street;
/// RoadNameInfo of |m_segment| if any. Otherwise |m_roadInfo| is empty.
RoadNameInfo m_roadNameInfo;
/// Distance from the route (not the subroute) beginning to the farthest end of |m_segment| in meters.
double m_distFromBeginningMeters = 0.0;
@ -147,7 +162,7 @@ public:
using TTurns = std::vector<turns::TurnItem>;
using TTimeItem = std::pair<uint32_t, double>;
using TTimes = std::vector<TTimeItem>;
using TStreetItem = std::pair<uint32_t, std::string>;
using TStreetItem = std::pair<uint32_t, RouteSegment::RoadNameInfo>;
using TStreets = std::vector<TStreetItem>;
class SubrouteAttrs final
@ -299,11 +314,11 @@ public:
/// set with MoveIterator() method. If it's not possible returns nullopt.
std::optional<turns::TurnItem> GetCurrentIteratorTurn() const;
/// \brief Returns a name of a street where the user rides at this moment.
void GetCurrentStreetName(std::string & name) const;
/// \brief Returns name info of a street where the user rides at this moment.
void GetCurrentStreetName(RouteSegment::RoadNameInfo & roadNameInfo) const;
/// \brief Returns a name of a street next to idx point of the path. Function avoids short unnamed links.
void GetStreetNameAfterIdx(uint32_t idx, std::string & name) const;
/// \brief Return name info of a street according to next turn.
void GetNextTurnStreetName(RouteSegment::RoadNameInfo & roadNameInfo) const;
/// \brief Gets turn information after the turn next to the nearest one.
/// \param distanceToTurnMeters is a distance from current position to the second turn.
@ -390,6 +405,9 @@ private:
void GetClosestTurn(size_t segIdx, turns::TurnItem & turn) const;
size_t ConvertPointIdxToSegmentIdx(size_t pointIdx) const;
/// \brief Returns a name info of a street next to idx point of the path.
void GetStreetNameAfterIdx(uint32_t idx, RouteSegment::RoadNameInfo & roadNameInfo) const;
/// \returns Estimated time to pass the route segment with |segIdx|.
double GetTimeToPassSegSec(size_t segIdx) const;

View file

@ -4,6 +4,7 @@
#include "routing/fake_feature_ids.hpp"
#include "routing/index_graph_starter.hpp"
#include "routing/road_point.hpp"
#include "routing/route.hpp"
#include "routing/segment.hpp"
#include "routing/traffic_stash.hpp"
#include "routing/world_graph.hpp"
@ -68,7 +69,7 @@ void FillSegmentInfo(vector<Segment> const & segments,
++turnIdx;
}
string curStreet;
RouteSegment::RoadNameInfo curStreet;
if (!streets.empty())
{
CHECK_LESS_OR_EQUAL(streetIdx, streets.size(), ());

View file

@ -10,6 +10,8 @@
#include "geometry/angles.hpp"
#include "geometry/mercator.hpp"
#include "indexer/road_shields_parser.hpp"
#include <utility>
using namespace location;
@ -339,6 +341,36 @@ SessionState RoutingSession::OnLocationPositionChanged(GpsInfo const & info)
return m_state;
}
void GetFullRoadName(RouteSegment::RoadNameInfo & road, string & name)
{
if (auto const & sh = ftypes::GetRoadShields(road.m_ref); !sh.empty())
road.m_ref = sh[0].m_name;
if (auto const & sh = ftypes::GetRoadShields(road.m_destination_ref); !sh.empty())
road.m_destination_ref = sh[0].m_name;
name.clear();
if (road.HasExitInfo())
{
if (!road.m_junction_ref.empty())
name = "[" + road.m_junction_ref + "]";
if (!road.m_destination_ref.empty())
name += string(name.empty() ? "" : ": ") + "[" + road.m_destination_ref + "]";
if (!road.m_destination.empty())
name += string(name.empty() ? "" : " ") + "> " + road.m_destination;
else if (!road.m_name.empty())
name += (road.m_destination_ref.empty() ? " : " : " ") + road.m_name;
}
else
{
if (!road.m_ref.empty())
name = "[" + road.m_ref + "]";
if (!road.m_name.empty())
name += (name.empty() ? "" : " ") + road.m_name;
}
}
void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const
{
CHECK_THREAD_CHECKER(m_threadChecker, ());
@ -376,8 +408,12 @@ void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const
info.m_exitNum = turn.m_exitNum;
info.m_time = static_cast<int>(max(kMinimumETASec, m_route->GetCurrentTimeToEndSec()));
m_route->GetCurrentStreetName(info.m_sourceName);
m_route->GetStreetNameAfterIdx(turn.m_index, info.m_targetName);
RouteSegment::RoadNameInfo sourceRoadNameInfo, targetRoadNameInfo;
m_route->GetCurrentStreetName(sourceRoadNameInfo);
GetFullRoadName(sourceRoadNameInfo, info.m_sourceName);
m_route->GetNextTurnStreetName(targetRoadNameInfo);
GetFullRoadName(targetRoadNameInfo, info.m_targetName);
info.m_completionPercent = GetCompletionPercent();
// Lane information and next street name.