The change clarifies the conditions for forcing routing steps and simplifies the codebase to support it. - Makes explicity the search runtime condition for forcing a routing step. Namely, the node is a source of the forward and reverse searches, and it's one of the pre-identified nodes that requires a step to be forced. - Consolidate the two lists of force nodes into one. Not only is there no algorithmic value in separating the nodes by geometric direction, the improvements to via-routes with u-turns mean atleast one of these lists will be empty for any search. - Rename 'force loop' to 'force step'. This moves the code away from the original CH-specific language for checking for self-loops in the case where this condition is met. MLD does not have loops. Additional cucumber tests are added to cover the logic related to negative search weights and forcing routing steps on via-route paths.
403 lines
13 KiB
C++
403 lines
13 KiB
C++
#include "engine/routing_algorithms/routing_base_mld.hpp"
|
|
#include "engine/routing_algorithms/shortest_path_impl.hpp"
|
|
#include "engine/search_engine_data.hpp"
|
|
#include "util/integer_range.hpp"
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
namespace osrm::engine
|
|
{
|
|
namespace routing_algorithms::offline
|
|
{
|
|
struct Algorithm final
|
|
{
|
|
};
|
|
} // namespace routing_algorithms::offline
|
|
|
|
// Define engine data for offline data facade
|
|
template <> struct SearchEngineData<routing_algorithms::offline::Algorithm>
|
|
{
|
|
using QueryHeap = SearchEngineData<routing_algorithms::mld::Algorithm>::QueryHeap;
|
|
|
|
using SearchEngineHeapPtr = std::unique_ptr<QueryHeap>;
|
|
|
|
SearchEngineHeapPtr forward_heap_1;
|
|
SearchEngineHeapPtr reverse_heap_1;
|
|
|
|
void InitializeOrClearFirstThreadLocalStorage(unsigned number_of_nodes)
|
|
{
|
|
if (forward_heap_1.get())
|
|
{
|
|
forward_heap_1->Clear();
|
|
}
|
|
else
|
|
{
|
|
forward_heap_1.reset(new QueryHeap(number_of_nodes, 0));
|
|
}
|
|
|
|
if (reverse_heap_1.get())
|
|
{
|
|
reverse_heap_1->Clear();
|
|
}
|
|
else
|
|
{
|
|
reverse_heap_1.reset(new QueryHeap(number_of_nodes, 0));
|
|
}
|
|
}
|
|
};
|
|
|
|
// Define offline multilevel partition
|
|
namespace datafacade
|
|
{
|
|
struct ExternalMultiLevelPartition
|
|
{
|
|
CellID GetCell(LevelID /*l*/, NodeID /*node*/) const { return 0; }
|
|
LevelID GetQueryLevel(NodeID /*start*/, NodeID /*target*/, NodeID /*node*/) const { return 0; }
|
|
LevelID GetHighestDifferentLevel(NodeID /*first*/, NodeID /*second*/) const { return 0; }
|
|
std::uint8_t GetNumberOfLevels() const { return 0; }
|
|
std::uint32_t GetNumberOfCells(LevelID /*level*/) const { return 0; }
|
|
CellID BeginChildren(LevelID /*level*/, CellID /*cell*/) const { return 0; }
|
|
CellID EndChildren(LevelID /*level*/, CellID /*cell*/) const { return 0; }
|
|
};
|
|
|
|
struct ExternalCellMetric
|
|
{
|
|
};
|
|
|
|
// Define external cell storage
|
|
struct ExternalCellStorage
|
|
{
|
|
struct CellImpl
|
|
{
|
|
auto GetOutWeight(NodeID /*node*/) const
|
|
{
|
|
return boost::make_iterator_range((EdgeWeight *)0, (EdgeWeight *)0);
|
|
}
|
|
|
|
auto GetInWeight(NodeID /*node*/) const
|
|
{
|
|
return boost::make_iterator_range((EdgeWeight *)0, (EdgeWeight *)0);
|
|
}
|
|
|
|
auto GetSourceNodes() const { return boost::make_iterator_range((NodeID *)0, (NodeID *)0); }
|
|
|
|
auto GetDestinationNodes() const
|
|
{
|
|
return boost::make_iterator_range((NodeID *)0, (NodeID *)0);
|
|
}
|
|
};
|
|
|
|
using Cell = CellImpl;
|
|
using ConstCell = const CellImpl;
|
|
|
|
ConstCell GetCell(ExternalCellMetric, LevelID /*level*/, CellID /*id*/) const { return Cell{}; }
|
|
};
|
|
|
|
// Define external data facade
|
|
template <>
|
|
class ContiguousInternalMemoryDataFacade<routing_algorithms::offline::Algorithm> final
|
|
: public BaseDataFacade
|
|
{
|
|
ExternalMultiLevelPartition external_partition;
|
|
ExternalCellStorage external_cell_storage;
|
|
ExternalCellMetric external_cell_metric;
|
|
|
|
public:
|
|
using EdgeData = extractor::EdgeBasedEdge::EdgeData;
|
|
// using RTreeLeaf = extractor::EdgeBasedNode;
|
|
|
|
ContiguousInternalMemoryDataFacade() {}
|
|
|
|
~ContiguousInternalMemoryDataFacade() override {}
|
|
|
|
unsigned GetNumberOfNodes() const { return 0; }
|
|
|
|
NodeID GetTarget(const EdgeID /*edgeID*/) const { return 0; }
|
|
|
|
const EdgeData &GetEdgeData(const EdgeID /*edgeID*/) const
|
|
{
|
|
static EdgeData outData;
|
|
return outData;
|
|
}
|
|
|
|
const auto &GetMultiLevelPartition() const { return external_partition; }
|
|
|
|
const auto &GetCellStorage() const { return external_cell_storage; }
|
|
|
|
const auto &GetCellMetric() const { return external_cell_metric; }
|
|
|
|
auto GetBorderEdgeRange(const LevelID /*level*/, const NodeID /*node*/) const
|
|
{
|
|
return util::irange<EdgeID>(0, 0);
|
|
}
|
|
|
|
EdgeID FindEdge(const NodeID /*from*/, const NodeID /*to*/) const { return SPECIAL_EDGEID; }
|
|
|
|
unsigned GetCheckSum() const override { return 0; }
|
|
|
|
// node and edge information access
|
|
util::Coordinate GetCoordinateOfNode(const NodeID /*id*/) const override
|
|
{
|
|
return {osrm::util::FloatLongitude{7.437069}, osrm::util::FloatLatitude{43.749249}};
|
|
}
|
|
|
|
OSMNodeID GetOSMNodeIDOfNode(const NodeID /*id*/) const override { return OSMNodeID(); }
|
|
|
|
GeometryID GetGeometryIndex(const NodeID /*id*/) const override { return GeometryID{0, false}; }
|
|
|
|
NodeForwardRange GetUncompressedForwardGeometry(const EdgeID /*id*/) const override
|
|
{
|
|
return {};
|
|
}
|
|
|
|
NodeReverseRange GetUncompressedReverseGeometry(const EdgeID /*id*/) const override
|
|
{
|
|
return NodeReverseRange(NodeForwardRange());
|
|
}
|
|
|
|
TurnPenalty GetWeightPenaltyForEdgeID(const unsigned /*id*/) const override
|
|
{
|
|
return INVALID_TURN_PENALTY;
|
|
}
|
|
|
|
TurnPenalty GetDurationPenaltyForEdgeID(const unsigned /*id*/) const override
|
|
{
|
|
return INVALID_TURN_PENALTY;
|
|
}
|
|
|
|
WeightForwardRange GetUncompressedForwardWeights(const EdgeID /*id*/) const override
|
|
{
|
|
return {};
|
|
}
|
|
|
|
WeightReverseRange GetUncompressedReverseWeights(const EdgeID /*id*/) const override
|
|
{
|
|
return WeightReverseRange(WeightForwardRange());
|
|
}
|
|
|
|
DurationForwardRange GetUncompressedForwardDurations(const EdgeID /*geomID*/) const override
|
|
{
|
|
return {};
|
|
}
|
|
|
|
DurationReverseRange GetUncompressedReverseDurations(const EdgeID /*geomID*/) const override
|
|
{
|
|
return DurationReverseRange(DurationForwardRange());
|
|
}
|
|
|
|
DatasourceForwardRange GetUncompressedForwardDatasources(const EdgeID /*id*/) const override
|
|
{
|
|
return {};
|
|
}
|
|
|
|
DatasourceReverseRange GetUncompressedReverseDatasources(const EdgeID /*id*/) const override
|
|
{
|
|
return DatasourceReverseRange(DatasourceForwardRange());
|
|
}
|
|
|
|
std::string_view GetDatasourceName(const DatasourceID /*id*/) const override
|
|
{
|
|
return std::string_view{};
|
|
}
|
|
|
|
guidance::TurnInstruction GetTurnInstructionForEdgeID(const EdgeID /*id*/) const override
|
|
{
|
|
return guidance::TurnInstruction{};
|
|
}
|
|
|
|
extractor::TravelMode GetTravelMode(const NodeID /*id*/) const override
|
|
{
|
|
return extractor::TRAVEL_MODE_DRIVING;
|
|
}
|
|
|
|
std::vector<RTreeLeaf> GetEdgesInBox(const util::Coordinate /*south_west*/,
|
|
const util::Coordinate /*north_east*/) const override
|
|
{
|
|
return {};
|
|
}
|
|
|
|
std::vector<engine::PhantomNodeWithDistance>
|
|
NearestPhantomNodesInRange(const util::Coordinate /*input_coordinate*/,
|
|
const double /*max_distance*/,
|
|
const boost::optional<engine::Bearing> /*bearing*/,
|
|
const engine::Approach /*approach*/,
|
|
const bool /*use_all_edges*/) const override
|
|
{
|
|
return {};
|
|
};
|
|
|
|
std::vector<engine::PhantomNodeWithDistance>
|
|
NearestPhantomNodes(const util::Coordinate /*input_coordinate*/,
|
|
const size_t /*max_results*/,
|
|
const boost::optional<double> /*max_distance*/,
|
|
const boost::optional<engine::Bearing> /*bearing*/,
|
|
const engine::Approach /*approach*/) const override
|
|
{
|
|
return {};
|
|
};
|
|
|
|
engine::PhantomCandidateAlternatives NearestCandidatesWithAlternativeFromBigComponent(
|
|
const util::Coordinate /*input_coordinate*/,
|
|
const boost::optional<double> /*max_distance*/,
|
|
const boost::optional<engine::Bearing> /*bearing*/,
|
|
const engine::Approach /*approach*/,
|
|
const bool /*use_all_edges*/) const override
|
|
{
|
|
return {};
|
|
};
|
|
|
|
util::guidance::LaneTupleIdPair GetLaneData(const EdgeID /*id*/) const override
|
|
{
|
|
return util::guidance::LaneTupleIdPair{};
|
|
}
|
|
|
|
extractor::TurnLaneDescription
|
|
GetTurnDescription(const LaneDescriptionID /*laneDescriptionID*/) const override
|
|
{
|
|
return {};
|
|
}
|
|
|
|
EdgeWeight GetNodeWeight(const NodeID /*node*/) const { return {0}; }
|
|
|
|
bool IsForwardEdge(const NodeID /*edge*/) const { return true; }
|
|
|
|
bool IsBackwardEdge(const NodeID /*edge*/) const { return true; }
|
|
|
|
bool HasLaneData(const EdgeID /*id*/) const override { return false; }
|
|
NameID GetNameIndex(const NodeID /*nodeID*/) const override { return EMPTY_NAMEID; }
|
|
std::string_view GetNameForID(const NameID /*id*/) const override { return std::string_view{}; }
|
|
std::string_view GetRefForID(const NameID /*id*/) const override { return std::string_view{}; }
|
|
std::string_view GetPronunciationForID(const NameID /*id*/) const override
|
|
{
|
|
return std::string_view{};
|
|
}
|
|
std::string_view GetDestinationsForID(const NameID /*id*/) const override
|
|
{
|
|
return std::string_view{};
|
|
}
|
|
std::string_view GetExitsForID(const NameID /*id*/) const override
|
|
{
|
|
return std::string_view{};
|
|
}
|
|
bool GetContinueStraightDefault() const override { return false; }
|
|
std::string GetTimestamp() const override { return ""; }
|
|
double GetMapMatchingMaxSpeed() const override { return 0; }
|
|
const char *GetWeightName() const override { return ""; }
|
|
unsigned GetWeightPrecision() const override { return 0; }
|
|
double GetWeightMultiplier() const override { return 1; }
|
|
ComponentID GetComponentID(NodeID) const override { return ComponentID{}; }
|
|
bool ExcludeNode(const NodeID) const override { return false; }
|
|
|
|
guidance::TurnBearing PreTurnBearing(const EdgeID /*eid*/) const override
|
|
{
|
|
return guidance::TurnBearing(0);
|
|
}
|
|
|
|
guidance::TurnBearing PostTurnBearing(const EdgeID /*eid*/) const override
|
|
{
|
|
return guidance::TurnBearing(0);
|
|
}
|
|
|
|
util::guidance::BearingClass
|
|
GetBearingClass(const BearingClassID /*bearing_class_id*/) const override
|
|
{
|
|
return util::guidance::BearingClass{};
|
|
}
|
|
|
|
osrm::extractor::ClassData GetClassData(const NodeID /*id*/) const override { return 0; }
|
|
std::vector<std::string> GetClasses(const extractor::ClassData /*class_data*/) const override
|
|
{
|
|
return {};
|
|
}
|
|
|
|
util::guidance::EntryClass GetEntryClass(const EdgeID /*turn_id*/) const override { return {}; }
|
|
bool IsLeftHandDriving(const NodeID /*id*/) const override { return false; }
|
|
bool IsSegregated(const NodeID /*id*/) const override { return false; }
|
|
|
|
std::vector<extractor::ManeuverOverride>
|
|
GetOverridesThatStartAt(const NodeID /* edge_based_node_id */) const override
|
|
{
|
|
return {};
|
|
}
|
|
};
|
|
|
|
} // namespace datafacade
|
|
|
|
// Fallback to MLD algorithm: requires from data facade MLD specific members
|
|
namespace routing_algorithms::offline
|
|
{
|
|
|
|
template <typename PhantomT>
|
|
inline void search(SearchEngineData<Algorithm> &engine_working_data,
|
|
const datafacade::ContiguousInternalMemoryDataFacade<Algorithm> &facade,
|
|
typename SearchEngineData<Algorithm>::QueryHeap &forward_heap,
|
|
typename SearchEngineData<Algorithm>::QueryHeap &reverse_heap,
|
|
EdgeWeight &weight,
|
|
std::vector<NodeID> &packed_leg,
|
|
const std::vector<NodeID> &loop_nodes,
|
|
const PhantomT &endpoints,
|
|
const EdgeWeight weight_upper_bound = INVALID_EDGE_WEIGHT)
|
|
{
|
|
mld::search(engine_working_data,
|
|
facade,
|
|
forward_heap,
|
|
reverse_heap,
|
|
weight,
|
|
packed_leg,
|
|
loop_nodes,
|
|
endpoints,
|
|
weight_upper_bound);
|
|
}
|
|
|
|
template <typename RandomIter, typename FacadeT>
|
|
void unpackPath(const FacadeT &facade,
|
|
RandomIter packed_path_begin,
|
|
RandomIter packed_path_end,
|
|
const PhantomEndpoints &endpoints,
|
|
std::vector<PathData> &unpacked_path)
|
|
{
|
|
mld::unpackPath(facade, packed_path_begin, packed_path_end, endpoints, unpacked_path);
|
|
}
|
|
|
|
} // namespace routing_algorithms::offline
|
|
|
|
} // namespace osrm::engine
|
|
|
|
BOOST_AUTO_TEST_SUITE(offline_facade)
|
|
|
|
BOOST_AUTO_TEST_CASE(shortest_path)
|
|
{
|
|
using Algorithm = osrm::engine::routing_algorithms::offline::Algorithm;
|
|
|
|
osrm::engine::SearchEngineData<Algorithm> heaps;
|
|
osrm::engine::datafacade::ContiguousInternalMemoryDataFacade<Algorithm> facade;
|
|
|
|
std::vector<osrm::engine::PhantomNodeCandidates> waypoints;
|
|
waypoints.push_back({osrm::engine::PhantomNode{}});
|
|
waypoints.push_back({osrm::engine::PhantomNode{}});
|
|
|
|
auto route =
|
|
osrm::engine::routing_algorithms::shortestPathSearch(heaps, facade, waypoints, false);
|
|
|
|
BOOST_CHECK_EQUAL(route.shortest_path_weight, INVALID_EDGE_WEIGHT);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(facade_uncompressed_methods)
|
|
{
|
|
using Algorithm = osrm::engine::routing_algorithms::offline::Algorithm;
|
|
|
|
osrm::engine::SearchEngineData<Algorithm> heaps;
|
|
osrm::engine::datafacade::ContiguousInternalMemoryDataFacade<Algorithm> facade;
|
|
|
|
BOOST_CHECK_EQUAL(facade.GetUncompressedForwardGeometry(0).size(), 0);
|
|
BOOST_CHECK_EQUAL(facade.GetUncompressedReverseGeometry(0).size(), 0);
|
|
BOOST_CHECK_EQUAL(facade.GetUncompressedForwardWeights(0).size(), 0);
|
|
BOOST_CHECK_EQUAL(facade.GetUncompressedReverseWeights(0).size(), 0);
|
|
BOOST_CHECK_EQUAL(facade.GetUncompressedForwardDurations(0).size(), 0);
|
|
BOOST_CHECK_EQUAL(facade.GetUncompressedReverseDurations(0).size(), 0);
|
|
BOOST_CHECK_EQUAL(facade.GetUncompressedForwardDatasources(0).size(), 0);
|
|
BOOST_CHECK_EQUAL(facade.GetUncompressedReverseDatasources(0).size(), 0);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|