mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-19 04:53:36 +00:00
[traffic] Calculate segment weight based on road ref
Signed-off-by: mvglasow <michael -at- vonglasow.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
#include "indexer/feature.hpp"
|
||||
#include "indexer/road_shields_parser.hpp"
|
||||
|
||||
// Only needed for OpenlrTraffDecoder, see below
|
||||
#if 0
|
||||
@@ -27,8 +28,17 @@
|
||||
|
||||
#include "traffic/traffic_cache.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
namespace traffxml
|
||||
{
|
||||
enum class RefParserState
|
||||
{
|
||||
Whitespace,
|
||||
Alpha,
|
||||
Numeric
|
||||
};
|
||||
|
||||
// Only needed for OpenlrTraffDecoder, see below
|
||||
#if 0
|
||||
// Number of worker threads for the OpenLR decoder
|
||||
@@ -54,13 +64,13 @@ auto constexpr kOneMpSInKmpH = 3.6;
|
||||
|
||||
/*
|
||||
* Penalty factor for using a fake segment to get to a nearby road.
|
||||
* Maximum penalty for roads is currently 16 (4 for ramps * 4 for road type), offroad penalty is
|
||||
* twice the maximum road penalty. We might need to increase that, since offroad penalty applies to
|
||||
* direct distance whereas road penalty applies to roads, which can be up to around 3 times the
|
||||
* direct distance (theoretically unlimited). That would imply multiplying maximum road penalty by
|
||||
* more than 3 (e.g. 4).
|
||||
* Maximum penalty for roads is currently 64 (4 for ramps * 4 for road type * 4 for ref), offroad
|
||||
* penalty is twice the maximum road penalty. We might need to increase that, since offroad penalty
|
||||
* applies to direct distance whereas road penalty applies to roads, which can be up to around 3
|
||||
* times the direct distance (theoretically unlimited). That would imply multiplying maximum road
|
||||
* penalty by more than 3 (e.g. 4).
|
||||
*/
|
||||
auto constexpr kOffroadPenalty = 32;
|
||||
auto constexpr kOffroadPenalty = 128;
|
||||
|
||||
/*
|
||||
* Penalty factor for non-matching attributes
|
||||
@@ -475,6 +485,67 @@ void OpenLrV3TraffDecoder::DecodeLocation(traffxml::TraffMessage & message, traf
|
||||
}
|
||||
#endif
|
||||
|
||||
double RoutingTraffDecoder::TraffEstimator::GetRoadRefPenalty(std::string & ref) const
|
||||
{
|
||||
// skip parsing if ref is empty
|
||||
if (ref.empty())
|
||||
{
|
||||
if (m_decoder.m_roadRef.empty())
|
||||
return 1;
|
||||
else if (!m_decoder.m_roadRef.empty())
|
||||
return kAttributePenalty;
|
||||
}
|
||||
|
||||
// TODO does caching results per ref improve performance?
|
||||
|
||||
std::vector<std::string> r = ParseRef(ref);
|
||||
|
||||
size_t matches = 0;
|
||||
|
||||
if (m_decoder.m_roadRef.empty() && r.empty())
|
||||
return 1;
|
||||
else if (m_decoder.m_roadRef.empty() || r.empty())
|
||||
return kAttributePenalty;
|
||||
|
||||
// work on a copy of `m_decoder.m_roadRef`
|
||||
std::vector<std::string> l = m_decoder.m_roadRef;
|
||||
|
||||
if ((l.size() > 1) && (r.size() > 1) && (l.front() == r.front()))
|
||||
{
|
||||
/*
|
||||
* Discard generic prefixes, which are often used to denote the road class.
|
||||
* This will turn `A1` and `A2` into `1` and `2`, causing them to be treated as a mismatch,
|
||||
* not a partial match.
|
||||
*/
|
||||
l.erase(l.begin());
|
||||
r.erase(r.begin());
|
||||
}
|
||||
|
||||
// for both sides, count items matched by the other side
|
||||
for (auto & litem : l)
|
||||
for (auto ritem : r)
|
||||
if (litem == ritem)
|
||||
{
|
||||
matches++;
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto ritem : r)
|
||||
for (auto & litem : l)
|
||||
if (litem == ritem)
|
||||
{
|
||||
matches++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (matches == 0)
|
||||
return kAttributePenalty;
|
||||
else if (matches == (l.size() + r.size()))
|
||||
return 1;
|
||||
else
|
||||
return kReducedAttributePenalty;
|
||||
}
|
||||
|
||||
double RoutingTraffDecoder::TraffEstimator::GetUTurnPenalty(Purpose /* purpose */) const
|
||||
{
|
||||
// Adds 2 minutes penalty for U-turn. The value is quite arbitrary
|
||||
@@ -517,6 +588,13 @@ double RoutingTraffDecoder::TraffEstimator::CalcOffroad(ms::LatLon const & from,
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Currently, the attribute penalty (kAttributePenalty or kReducedAttributePenalty) can be applied
|
||||
* up to 3 times:
|
||||
* - ramp attribute mismatch
|
||||
* - road class mismatch
|
||||
* - road ref mismatch
|
||||
*/
|
||||
double RoutingTraffDecoder::TraffEstimator::CalcSegmentWeight(routing::Segment const & segment, routing::RoadGeometry const & road, Purpose purpose) const
|
||||
{
|
||||
double result = road.GetDistance(segment.GetSegmentIdx());
|
||||
@@ -545,6 +623,28 @@ double RoutingTraffDecoder::TraffEstimator::CalcSegmentWeight(routing::Segment c
|
||||
result *= kAttributePenalty;
|
||||
}
|
||||
|
||||
if (!m_decoder.m_roadRef.empty())
|
||||
{
|
||||
auto const countryFile = m_decoder.m_numMwmIds->GetFile(segment.GetMwmId());
|
||||
auto const mwmId = m_decoder.m_dataSource.GetMwmIdByCountryFile(countryFile);
|
||||
FeaturesLoaderGuard g(m_decoder.m_dataSource, mwmId);
|
||||
auto f = g.GetOriginalFeatureByIndex(segment.GetFeatureId());
|
||||
auto refs = ftypes::GetRoadShieldsNames(*f);
|
||||
|
||||
auto penalty = kAttributePenalty;
|
||||
|
||||
for (auto & ref : refs)
|
||||
{
|
||||
auto newPenalty = GetRoadRefPenalty(ref);
|
||||
if (newPenalty < penalty)
|
||||
penalty = newPenalty;
|
||||
if (penalty == 1)
|
||||
break;
|
||||
}
|
||||
|
||||
result *= penalty;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -865,6 +965,11 @@ void RoutingTraffDecoder::DecodeLocation(traffxml::TraffMessage & message, traff
|
||||
|
||||
m_message = message;
|
||||
|
||||
if (m_message.value().m_location.value().m_roadRef)
|
||||
m_roadRef = ParseRef(m_message.value().m_location.value().m_roadRef.value());
|
||||
else
|
||||
m_roadRef.clear();
|
||||
|
||||
int dirs = (message.m_location.value().m_directionality == Directionality::BothDirections) ? 2 : 1;
|
||||
for (int dir = 0; dir < dirs; dir++)
|
||||
DecodeLocationDirection(message, decoded, dir == 0 ? false : true /* backwards */);
|
||||
@@ -966,4 +1071,61 @@ bool IsRamp(routing::HighwayType highwayType)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> ParseRef(std::string & ref)
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
std::string curr = "";
|
||||
RefParserState state = RefParserState::Whitespace;
|
||||
|
||||
for (size_t i = 0; i < ref.size(); i++)
|
||||
{
|
||||
// TODO this list of delimiters might not be exhaustive
|
||||
if ((ref[i] <= 0x20) || (ref[i] == ',') || (ref[i] == '-') || (ref[i] == '.') || (ref[i] == '/'))
|
||||
{
|
||||
// whitespace
|
||||
if (state != RefParserState::Whitespace)
|
||||
{
|
||||
if (state == RefParserState::Alpha)
|
||||
boost::to_lower(curr);
|
||||
res.push_back(curr);
|
||||
curr = "";
|
||||
}
|
||||
state = RefParserState::Whitespace;
|
||||
}
|
||||
/*
|
||||
* TODO adapt this to other number systems as well.
|
||||
* Roman numerals (or any use of letters as numbers) are a stupid idea. If they are at least
|
||||
* properly delimited, they are treated as a letter group, which sort of works for comparison.
|
||||
* However, `IVbis` will be treated as one group, whereas `IV bis` will be treated as two.
|
||||
*/
|
||||
else if ((ref[i] >= '0') && (ref[i] <= '9'))
|
||||
{
|
||||
// numeric
|
||||
if (state == RefParserState::Alpha)
|
||||
{
|
||||
boost::to_lower(curr);
|
||||
res.push_back(curr);
|
||||
curr = "";
|
||||
}
|
||||
curr += ref[i];
|
||||
state = RefParserState::Numeric;
|
||||
}
|
||||
// anything that is not a delimiter or a digit (as per the above rules) is considered a letter
|
||||
else
|
||||
{
|
||||
// alpha
|
||||
if (state == RefParserState::Numeric)
|
||||
{
|
||||
res.push_back(curr);
|
||||
curr = "";
|
||||
}
|
||||
curr += ref[i];
|
||||
state = RefParserState::Alpha;
|
||||
}
|
||||
}
|
||||
if (!curr.empty())
|
||||
res.push_back(curr);
|
||||
return res;
|
||||
}
|
||||
} // namespace traffxml
|
||||
|
||||
@@ -295,6 +295,20 @@ public:
|
||||
*/
|
||||
double CalcOffroad(ms::LatLon const & from, ms::LatLon const & to, Purpose purpose) const override;
|
||||
double CalcSegmentWeight(routing::Segment const & segment, routing::RoadGeometry const & road, Purpose purpose) const override;
|
||||
|
||||
/**
|
||||
* @brief Determines the penalty factor based on how two reference numbers match.
|
||||
*
|
||||
* Rules are subject to change.
|
||||
*
|
||||
* @param ref The reference number of the current segment, compared against `m_roadRef`.
|
||||
*
|
||||
* @return 1 for a perfect match (refs are assumed to refer to the same object), `kAttributePenalty`
|
||||
* for a mismatch (refs are assumed to refer to different objects) or`kReducedAttributePenalty` for
|
||||
* a partial match (unclear whether both refs refer to the same object).
|
||||
*/
|
||||
double GetRoadRefPenalty(std::string & ref) const;
|
||||
|
||||
double GetUTurnPenalty(Purpose /* purpose */) const override;
|
||||
double GetTurnPenalty(Purpose purpose, double angle, routing::RoadGeometry const & from_road,
|
||||
routing::RoadGeometry const & to_road, bool is_left_hand_traffic = false) const override;
|
||||
@@ -378,6 +392,11 @@ private:
|
||||
std::shared_ptr<routing::NumMwmIds> m_numMwmIds = std::make_shared<routing::NumMwmIds>();
|
||||
std::unique_ptr<routing::IRouter> m_router;
|
||||
std::optional<traffxml::TraffMessage> m_message = std::nullopt;
|
||||
|
||||
/**
|
||||
* @brief The road ref of `m_message`, parsed with `ParseRef()`
|
||||
*/
|
||||
std::vector<std::string> m_roadRef;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -389,4 +408,22 @@ using DefaultTraffDecoder = RoutingTraffDecoder;
|
||||
traffxml::RoadClass GetRoadClass(routing::HighwayType highwayType);
|
||||
double GetRoadClassPenalty(traffxml::RoadClass lhs, traffxml::RoadClass rhs);
|
||||
bool IsRamp(routing::HighwayType highwayType);
|
||||
|
||||
/**
|
||||
* @brief Breaks down a ref into groups for comparison.
|
||||
*
|
||||
* The result of this function can be used to determine if two reference numbers match partially
|
||||
* (such as `A4`, `A4bis` and `A4.1`).
|
||||
*
|
||||
* Implementation details may change; currently the following applies:
|
||||
*
|
||||
* A whitespace character (or sequence of whitespace characters), or a switch between letters and
|
||||
* digits, starts a new group.
|
||||
*
|
||||
* Letters are converted to lowercase.
|
||||
*
|
||||
* For example, each of `A42`, `A 42` and `-a42` would be broken down into `a, 42`, whereas `A4.2`
|
||||
* would be broken down into `a, 4, 2`.
|
||||
*/
|
||||
std::vector<std::string> ParseRef(std::string & ref);
|
||||
} // namespace traffxml
|
||||
|
||||
Reference in New Issue
Block a user