Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
366
generator/restriction_collector.cpp
Normal file
366
generator/restriction_collector.cpp
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
#include "generator/restriction_collector.hpp"
|
||||
|
||||
#include "generator/routing_helpers.hpp"
|
||||
|
||||
#include "routing/restriction_loader.hpp"
|
||||
#include "routing/road_graph.hpp"
|
||||
|
||||
#include "routing_common/car_model.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/geo_object_id.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/scope_guard.hpp"
|
||||
#include "base/stl_helpers.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace routing_builder
|
||||
{
|
||||
using namespace routing;
|
||||
|
||||
char const kNo[] = "No";
|
||||
char const kOnly[] = "Only";
|
||||
char const kNoUTurn[] = "NoUTurn";
|
||||
char const kOnlyUTurn[] = "OnlyUTurn";
|
||||
|
||||
template <class TokenizerT>
|
||||
bool ParseLineOfWayIds(TokenizerT & iter, std::vector<base::GeoObjectId> & numbers)
|
||||
{
|
||||
uint64_t number = 0;
|
||||
for (; iter; ++iter)
|
||||
{
|
||||
if (!strings::to_uint(*iter, number))
|
||||
return false;
|
||||
numbers.push_back(base::MakeOsmWay(number));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
m2::PointD constexpr RestrictionCollector::kNoCoords;
|
||||
|
||||
RestrictionCollector::RestrictionCollector(std::string const & osmIdsToFeatureIdPath, IndexGraph & graph)
|
||||
: m_indexGraph(graph)
|
||||
{
|
||||
ParseWaysOsmIdToFeatureIdMapping(osmIdsToFeatureIdPath, m_osmIdToFeatureIds);
|
||||
}
|
||||
|
||||
bool RestrictionCollector::Process(std::string const & restrictionPath)
|
||||
{
|
||||
SCOPE_GUARD(clean, [this]()
|
||||
{
|
||||
m_osmIdToFeatureIds.clear();
|
||||
m_restrictions.clear();
|
||||
});
|
||||
|
||||
if (!ParseRestrictions(restrictionPath))
|
||||
{
|
||||
LOG(LWARNING, ("An error happened while parsing restrictions from file:", restrictionPath));
|
||||
return false;
|
||||
}
|
||||
|
||||
clean.release();
|
||||
|
||||
base::SortUnique(m_restrictions);
|
||||
|
||||
LOG(LDEBUG, ("Number of loaded restrictions:", m_restrictions.size()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RestrictionCollector::ParseRestrictions(std::string const & path)
|
||||
{
|
||||
std::ifstream stream(path);
|
||||
if (stream.fail())
|
||||
return false;
|
||||
|
||||
std::string line;
|
||||
while (std::getline(stream, line))
|
||||
{
|
||||
strings::SimpleTokenizer iter(line, ", \t\r\n");
|
||||
if (!iter) // the line is empty
|
||||
return false;
|
||||
|
||||
Restriction::Type restrictionType;
|
||||
auto viaType = RestrictionWriter::ViaType::Count;
|
||||
FromString(*iter, restrictionType);
|
||||
++iter;
|
||||
|
||||
FromString(*iter, viaType);
|
||||
++iter;
|
||||
|
||||
m2::PointD coords = kNoCoords;
|
||||
if (viaType == RestrictionWriter::ViaType::Node)
|
||||
{
|
||||
FromString(*iter, coords.x);
|
||||
++iter;
|
||||
FromString(*iter, coords.y);
|
||||
++iter;
|
||||
}
|
||||
|
||||
std::vector<base::GeoObjectId> osmIds;
|
||||
if (!ParseLineOfWayIds(iter, osmIds))
|
||||
{
|
||||
LOG(LWARNING, ("Cannot parse osm ids from", path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (viaType == RestrictionWriter::ViaType::Node)
|
||||
CHECK_EQUAL(osmIds.size(), 2, ("Only |from| and |to| osmId."));
|
||||
|
||||
CHECK_NOT_EQUAL(viaType, RestrictionWriter::ViaType::Count, ());
|
||||
AddRestriction(coords, restrictionType, osmIds);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Joint::Id RestrictionCollector::GetFirstCommonJoint(uint32_t firstFeatureId, uint32_t secondFeatureId) const
|
||||
{
|
||||
uint32_t const firstLen = m_indexGraph.GetRoadGeometry(firstFeatureId).GetPointsCount();
|
||||
uint32_t const secondLen = m_indexGraph.GetRoadGeometry(secondFeatureId).GetPointsCount();
|
||||
|
||||
auto const firstRoad = m_indexGraph.GetRoad(firstFeatureId);
|
||||
auto const secondRoad = m_indexGraph.GetRoad(secondFeatureId);
|
||||
|
||||
std::unordered_set<Joint::Id> used;
|
||||
for (uint32_t i = 0; i < firstLen; ++i)
|
||||
if (firstRoad.GetJointId(i) != Joint::kInvalidId)
|
||||
used.emplace(firstRoad.GetJointId(i));
|
||||
|
||||
for (uint32_t i = 0; i < secondLen; ++i)
|
||||
if (used.count(secondRoad.GetJointId(i)) != 0)
|
||||
return secondRoad.GetJointId(i);
|
||||
|
||||
return Joint::kInvalidId;
|
||||
}
|
||||
|
||||
bool RestrictionCollector::FeatureHasPointWithCoords(uint32_t featureId, m2::PointD const & coords) const
|
||||
{
|
||||
auto const & roadGeometry = m_indexGraph.GetRoadGeometry(featureId);
|
||||
uint32_t const pointsCount = roadGeometry.GetPointsCount();
|
||||
for (uint32_t i = 0; i < pointsCount; ++i)
|
||||
{
|
||||
static double constexpr kEps = 1e-5;
|
||||
if (AlmostEqualAbs(mercator::FromLatLon(roadGeometry.GetPoint(i)), coords, kEps))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RestrictionCollector::FeaturesAreCross(m2::PointD const & coords, uint32_t prev, uint32_t cur) const
|
||||
{
|
||||
if (coords == kNoCoords)
|
||||
return GetFirstCommonJoint(prev, cur) != Joint::kInvalidId;
|
||||
|
||||
return FeatureHasPointWithCoords(prev, coords) && FeatureHasPointWithCoords(cur, coords);
|
||||
}
|
||||
|
||||
Restriction::Type ConvertUTurnToSimpleRestriction(Restriction::Type type)
|
||||
{
|
||||
CHECK(IsUTurnType(type), ());
|
||||
|
||||
// Some people create no_u_turn not in the way we expect.
|
||||
// For example: https://www.openstreetmap.org/relation/9511182
|
||||
// https://www.openstreetmap.org/relation/2532732
|
||||
// https://www.openstreetmap.org/relation/7606104
|
||||
// OsmId of |from| member is differ from |to| member.
|
||||
// So we "convert" such no_u_turn to any no_* restriction.
|
||||
// And we do the same thing with only_u_turn.
|
||||
return type == Restriction::Type::NoUTurn ? Restriction::Type::No : Restriction::Type::Only;
|
||||
}
|
||||
|
||||
void ConvertToUTurnIfPossible(Restriction::Type & type, m2::PointD const & coords,
|
||||
std::vector<uint32_t> const & featureIds)
|
||||
{
|
||||
if (IsUTurnType(type))
|
||||
return;
|
||||
|
||||
// Some people create u_turn not in the way we expect.
|
||||
// For example:
|
||||
// At 31.05.2019 there is no_left_turn by url:
|
||||
// https://www.openstreetmap.org/relation/1431150
|
||||
// with the same |from| and |to| member with node as |via|):
|
||||
//
|
||||
// So we "convert" such relations to no_u_turn or only_u_turn restrictions.
|
||||
if (featureIds.size() == 2 && featureIds.front() == featureIds.back() && coords != RestrictionCollector::kNoCoords)
|
||||
type = type == Restriction::Type::No ? Restriction::Type::NoUTurn : Restriction::Type::OnlyUTurn;
|
||||
}
|
||||
|
||||
bool RestrictionCollector::CheckAndProcessUTurn(Restriction::Type & restrictionType, m2::PointD const & coords,
|
||||
std::vector<uint32_t> & featureIds) const
|
||||
{
|
||||
CHECK(IsUTurnType(restrictionType), ());
|
||||
|
||||
// UTurn with via as way.
|
||||
if (coords == kNoCoords)
|
||||
{
|
||||
// featureIds = {from, via_1, ..., via_N, to}, size must be greater or equal 3.
|
||||
CHECK_GREATER_OR_EQUAL(featureIds.size(), 3, ());
|
||||
restrictionType = ConvertUTurnToSimpleRestriction(restrictionType);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only |from| and |to| member must be here, because via is node.
|
||||
if (featureIds.size() != 2)
|
||||
return false;
|
||||
|
||||
// Typical no_u_turn with node as via, for example:
|
||||
// {
|
||||
// from(featureId = 123),
|
||||
// via (node at feature 123 = 2),
|
||||
// to(featureId = 123)
|
||||
// }
|
||||
if (featureIds[0] == featureIds[1])
|
||||
{
|
||||
featureIds.pop_back();
|
||||
CHECK_EQUAL(featureIds.size(), 1, ());
|
||||
|
||||
uint32_t & featureId = featureIds.back();
|
||||
|
||||
auto const & road = m_indexGraph.GetRoadGeometry(featureId);
|
||||
// Can not do UTurn from feature to the same feature if it is one way.
|
||||
if (road.IsOneWay())
|
||||
return false;
|
||||
|
||||
uint32_t const n = road.GetPointsCount();
|
||||
|
||||
// According to the wiki: via must be at the end or at the beginning of feature.
|
||||
// https://wiki.openstreetmap.org/wiki/Relation:restriction
|
||||
static auto constexpr kEps = 1e-5;
|
||||
bool const viaIsFirstNode = AlmostEqualAbs(coords, mercator::FromLatLon(road.GetPoint(0)), kEps);
|
||||
bool const viaIsLastNode = AlmostEqualAbs(coords, mercator::FromLatLon(road.GetPoint(n - 1)), kEps);
|
||||
|
||||
if (viaIsFirstNode)
|
||||
{
|
||||
featureId |= RestrictionSerializer::kUTurnAtTheBeginMask;
|
||||
}
|
||||
else if (viaIsLastNode)
|
||||
{
|
||||
CHECK_EQUAL(featureId & RestrictionSerializer::kUTurnAtTheBeginMask, 0,
|
||||
("The first bit of featureId must be zero, too big value "
|
||||
"of featureId, invariant violated."));
|
||||
}
|
||||
|
||||
return viaIsFirstNode || viaIsLastNode;
|
||||
}
|
||||
|
||||
restrictionType = ConvertUTurnToSimpleRestriction(restrictionType);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RestrictionCollector::IsRestrictionValid(Restriction::Type & restrictionType, m2::PointD const & coords,
|
||||
std::vector<uint32_t> & featureIds) const
|
||||
{
|
||||
if (featureIds.empty() || !m_indexGraph.IsRoad(featureIds[0]))
|
||||
return false;
|
||||
|
||||
for (size_t i = 1; i < featureIds.size(); ++i)
|
||||
{
|
||||
auto prev = featureIds[i - 1];
|
||||
auto cur = featureIds[i];
|
||||
if (!m_indexGraph.IsRoad(cur))
|
||||
return false;
|
||||
|
||||
if (!FeaturesAreCross(coords, prev, cur))
|
||||
return false;
|
||||
}
|
||||
|
||||
ConvertToUTurnIfPossible(restrictionType, coords, featureIds);
|
||||
|
||||
if (!IsUTurnType(restrictionType))
|
||||
return true;
|
||||
|
||||
return CheckAndProcessUTurn(restrictionType, coords, featureIds);
|
||||
}
|
||||
|
||||
bool RestrictionCollector::AddRestriction(m2::PointD const & coords, Restriction::Type restrictionType,
|
||||
std::vector<base::GeoObjectId> const & osmIds)
|
||||
{
|
||||
std::vector<uint32_t> featureIds(osmIds.size());
|
||||
for (size_t i = 0; i < osmIds.size(); ++i)
|
||||
{
|
||||
auto const result = m_osmIdToFeatureIds.find(osmIds[i]);
|
||||
if (result == m_osmIdToFeatureIds.cend())
|
||||
{
|
||||
// It happens near mwm border when one of a restriction lines is not included in mwm
|
||||
// but the restriction is included.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only one feature id is found for |osmIds[i]|.
|
||||
// @TODO(bykoianko) All the feature ids should be used instead of |result->second.front()|.
|
||||
CHECK(!result->second.empty(), ());
|
||||
featureIds[i] = result->second.front();
|
||||
}
|
||||
|
||||
if (!IsRestrictionValid(restrictionType, coords, featureIds))
|
||||
return false;
|
||||
|
||||
m_restrictions.emplace_back(restrictionType, featureIds);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RestrictionCollector::AddFeatureId(uint32_t featureId, base::GeoObjectId osmId)
|
||||
{
|
||||
::routing::AddFeatureId(osmId, featureId, m_osmIdToFeatureIds);
|
||||
}
|
||||
|
||||
void FromString(std::string_view str, Restriction::Type & type)
|
||||
{
|
||||
if (str == kNo)
|
||||
{
|
||||
type = Restriction::Type::No;
|
||||
return;
|
||||
}
|
||||
|
||||
if (str == kOnly)
|
||||
{
|
||||
type = Restriction::Type::Only;
|
||||
return;
|
||||
}
|
||||
|
||||
if (str == kNoUTurn)
|
||||
{
|
||||
type = Restriction::Type::NoUTurn;
|
||||
return;
|
||||
}
|
||||
|
||||
if (str == kOnlyUTurn)
|
||||
{
|
||||
type = Restriction::Type::OnlyUTurn;
|
||||
return;
|
||||
}
|
||||
|
||||
CHECK(false, ("Invalid line:", str, "expected:", kNo, "or", kOnly, "or", kNoUTurn, "or", kOnlyUTurn));
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void FromString(std::string_view str, RestrictionWriter::ViaType & type)
|
||||
{
|
||||
if (str == RestrictionWriter::kNodeString)
|
||||
{
|
||||
type = RestrictionWriter::ViaType::Node;
|
||||
return;
|
||||
}
|
||||
|
||||
if (str == RestrictionWriter::kWayString)
|
||||
{
|
||||
type = RestrictionWriter::ViaType::Way;
|
||||
return;
|
||||
}
|
||||
|
||||
CHECK(false,
|
||||
("Invalid line:", str, "expected:", RestrictionWriter::kNodeString, "or", RestrictionWriter::kWayString));
|
||||
}
|
||||
|
||||
void FromString(std::string_view str, double & number)
|
||||
{
|
||||
CHECK(strings::to_double(str, number), ());
|
||||
}
|
||||
} // namespace routing_builder
|
||||
Loading…
Add table
Add a link
Reference in a new issue