mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-20 13:23:59 +00:00
[traffic] Refactor methods for penalty calculation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
This commit is contained in:
@@ -509,67 +509,6 @@ void OpenLrV3TraffDecoder::DecodeLocation(traffxml::TraffMessage & message, traf
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
double RoutingTraffDecoder::TraffEstimator::GetUTurnPenalty(Purpose /* purpose */) const
|
||||||
{
|
{
|
||||||
// Adds 2 minutes penalty for U-turn. The value is quite arbitrary
|
// Adds 2 minutes penalty for U-turn. The value is quite arbitrary
|
||||||
@@ -665,26 +604,9 @@ double RoutingTraffDecoder::TraffEstimator::CalcSegmentWeight(routing::Segment c
|
|||||||
if (!m_decoder.m_message || !m_decoder.m_message.value().m_location.value().m_roadClass)
|
if (!m_decoder.m_message || !m_decoder.m_message.value().m_location.value().m_roadClass)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
std::optional<routing::HighwayType> highwayType = road.GetHighwayType();
|
result *= GetHighwayTypePenalty(road.GetHighwayType(),
|
||||||
|
m_decoder.m_message.value().m_location.value().m_roadClass,
|
||||||
if (highwayType)
|
m_decoder.m_message.value().m_location.value().m_ramps);
|
||||||
{
|
|
||||||
if (IsRamp(highwayType.value()) != (m_decoder.m_message.value().m_location.value().m_ramps != Ramps::None))
|
|
||||||
// if one is a ramp and the other is not, treat it as a mismatch
|
|
||||||
result *= kAttributePenalty;
|
|
||||||
if (m_decoder.m_message.value().m_location.value().m_roadClass)
|
|
||||||
// if the message specifies a road class, penalize mismatches
|
|
||||||
result *= GetRoadClassPenalty(m_decoder.m_message.value().m_location.value().m_roadClass.value(),
|
|
||||||
GetRoadClass(highwayType.value()));
|
|
||||||
}
|
|
||||||
else // road has no highway class
|
|
||||||
{
|
|
||||||
// we can’t determine if it is a ramp, penalize for mismatch
|
|
||||||
result *= kAttributePenalty;
|
|
||||||
if (m_decoder.m_message.value().m_location.value().m_roadClass)
|
|
||||||
// we can’t determine if the road matches the required road class, treat it as mismatch
|
|
||||||
result *= kAttributePenalty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_decoder.m_roadRef.empty())
|
if (!m_decoder.m_roadRef.empty())
|
||||||
{
|
{
|
||||||
@@ -694,18 +616,7 @@ double RoutingTraffDecoder::TraffEstimator::CalcSegmentWeight(routing::Segment c
|
|||||||
auto f = g.GetOriginalFeatureByIndex(segment.GetFeatureId());
|
auto f = g.GetOriginalFeatureByIndex(segment.GetFeatureId());
|
||||||
auto refs = ftypes::GetRoadShieldsNames(*f);
|
auto refs = ftypes::GetRoadShieldsNames(*f);
|
||||||
|
|
||||||
auto penalty = kAttributePenalty;
|
result *= m_decoder.GetRoadRefPenalty(refs);
|
||||||
|
|
||||||
for (auto & ref : refs)
|
|
||||||
{
|
|
||||||
auto newPenalty = GetRoadRefPenalty(ref);
|
|
||||||
if (newPenalty < penalty)
|
|
||||||
penalty = newPenalty;
|
|
||||||
if (penalty == 1)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result *= penalty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -747,6 +658,108 @@ RoutingTraffDecoder::RoutingTraffDecoder(DataSource & dataSource, CountryInfoGet
|
|||||||
InitRouter();
|
InitRouter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double RoutingTraffDecoder::GetHighwayTypePenalty(std::optional<routing::HighwayType> highwayType,
|
||||||
|
std::optional<RoadClass> roadClass,
|
||||||
|
Ramps ramps)
|
||||||
|
{
|
||||||
|
double result = 1.0;
|
||||||
|
if (highwayType)
|
||||||
|
{
|
||||||
|
if (IsRamp(highwayType.value()) != (ramps != Ramps::None))
|
||||||
|
// if one is a ramp and the other is not, treat it as a mismatch
|
||||||
|
result *= kAttributePenalty;
|
||||||
|
if (roadClass)
|
||||||
|
// if the message specifies a road class, penalize mismatches
|
||||||
|
result *= GetRoadClassPenalty(roadClass.value(), GetRoadClass(highwayType.value()));
|
||||||
|
}
|
||||||
|
else // road has no highway class
|
||||||
|
{
|
||||||
|
// we can’t determine if it is a ramp, penalize for mismatch
|
||||||
|
result *= kAttributePenalty;
|
||||||
|
if (roadClass)
|
||||||
|
// we can’t determine if the road matches the required road class, treat it as mismatch
|
||||||
|
result *= kAttributePenalty;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
double RoutingTraffDecoder::GetRoadRefPenalty(std::vector<std::string> & refs) const
|
||||||
|
{
|
||||||
|
double result = kAttributePenalty;
|
||||||
|
|
||||||
|
for (auto & ref : refs)
|
||||||
|
{
|
||||||
|
auto newResult = GetRoadRefPenalty(ref);
|
||||||
|
if (newResult < result)
|
||||||
|
result = newResult;
|
||||||
|
if (result == 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
double RoutingTraffDecoder::GetRoadRefPenalty(std::string const & ref) const
|
||||||
|
{
|
||||||
|
// skip parsing if ref is empty
|
||||||
|
if (ref.empty())
|
||||||
|
{
|
||||||
|
if (m_roadRef.empty())
|
||||||
|
return 1;
|
||||||
|
else if (!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_roadRef.empty() && r.empty())
|
||||||
|
return 1;
|
||||||
|
else if (m_roadRef.empty() || r.empty())
|
||||||
|
return kAttributePenalty;
|
||||||
|
|
||||||
|
// work on a copy of `m_decoder.m_roadRef`
|
||||||
|
std::vector<std::string> l = 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;
|
||||||
|
}
|
||||||
|
|
||||||
void RoutingTraffDecoder::OnMapRegistered(platform::LocalCountryFile const & localFile)
|
void RoutingTraffDecoder::OnMapRegistered(platform::LocalCountryFile const & localFile)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
@@ -1234,7 +1247,7 @@ bool IsRamp(routing::HighwayType highwayType)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> ParseRef(std::string & ref)
|
std::vector<std::string> ParseRef(std::string const & ref)
|
||||||
{
|
{
|
||||||
std::vector<std::string> res;
|
std::vector<std::string> res;
|
||||||
std::string curr = "";
|
std::string curr = "";
|
||||||
|
|||||||
@@ -303,19 +303,6 @@ public:
|
|||||||
double CalcOffroad(ms::LatLon const & from, ms::LatLon const & to, Purpose /* purpose */) const override;
|
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;
|
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 GetUTurnPenalty(Purpose /* purpose */) const override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -354,6 +341,63 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual void OnMapDeregistered(platform::LocalCountryFile const & /* localFile */) override {}
|
virtual void OnMapDeregistered(platform::LocalCountryFile const & /* localFile */) override {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Determines the penalty factor bases on how highway attributes match.
|
||||||
|
*
|
||||||
|
* This compares the highway type of the candidate feature (as retrieved from OSM) against the
|
||||||
|
* road class and ramps attributes of the location.
|
||||||
|
*
|
||||||
|
* Rules are subject to change but principles are:
|
||||||
|
*
|
||||||
|
* Penalties for ramp mismatch and road class mismatch are applied consecutively, thus the maximum
|
||||||
|
* penalty is `kAttributePenalty ^ 2`.
|
||||||
|
*
|
||||||
|
* If ramps mismatch (location specifies a ramp but candidate is not a ramp, or vice versa), the
|
||||||
|
* penalty is `kAttributePenalty`.
|
||||||
|
*
|
||||||
|
* If road classes are similar, the penalty is `kReducedAttributePenalty`. For a complete
|
||||||
|
* mismatch, the penalty is `kAttributePenalty`.
|
||||||
|
*
|
||||||
|
* @param highwayType The OSM highway type of the candidate feature.
|
||||||
|
* @param roadClass The TraFF road class of the location.
|
||||||
|
* @param ramps The ramps atribute of the TraFF location.
|
||||||
|
*
|
||||||
|
* @return 1 for a perfect match (same road class and ramp type), up to `kAttributePenalty ^ 2`
|
||||||
|
* for a mismatch.
|
||||||
|
*/
|
||||||
|
static double GetHighwayTypePenalty(std::optional<routing::HighwayType> highwayType,
|
||||||
|
std::optional<RoadClass> roadClass,
|
||||||
|
Ramps ramps);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Determines the penalty factor based on how two reference numbers match.
|
||||||
|
*
|
||||||
|
* Rules are subject to change.
|
||||||
|
*
|
||||||
|
* This method takes a vector as an argument, compares each element and returns the penalty for
|
||||||
|
* the best match.
|
||||||
|
*
|
||||||
|
* @param refs A vector of reference numbers 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::vector<std::string> & refs) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 const & ref) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief Initializes the router.
|
* @brief Initializes the router.
|
||||||
@@ -470,7 +514,7 @@ bool IsRamp(routing::HighwayType highwayType);
|
|||||||
* For example, each of `A42`, `A 42` and `-a42` would be broken down into `a, 42`, whereas `A4.2`
|
* 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`.
|
* would be broken down into `a, 4, 2`.
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> ParseRef(std::string & ref);
|
std::vector<std::string> ParseRef(std::string const & ref);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates the segments to truncate at the start of the route.
|
* @brief Calculates the segments to truncate at the start of the route.
|
||||||
|
|||||||
Reference in New Issue
Block a user