Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorДобрый Ээх <bukharaev@gmail.com>2016-11-16 21:08:50 +0300
committerДобрый Ээх <bukharaev@gmail.com>2016-11-24 17:53:04 +0300
commitbe1213029222d42a7a230f6c165f668a1d0ffd8b (patch)
tree0c1f7122955d546be7bd772520171a7c8ef26f87
parentdd7599998201423b95a260ee6ff972bb310812c6 (diff)
indexed astar router
-rw-r--r--defines.hpp3
-rw-r--r--generator/generator.pro2
-rw-r--r--generator/generator_tool/generator_tool.cpp10
-rw-r--r--generator/routing_index_generator.cpp145
-rw-r--r--generator/routing_index_generator.hpp8
-rw-r--r--indexer/indexer.pro1
-rw-r--r--indexer/routing_section.hpp40
-rw-r--r--map/framework.cpp1
-rw-r--r--routing/astar_router.cpp193
-rw-r--r--routing/astar_router.hpp47
-rw-r--r--routing/car_router.cpp18
-rw-r--r--routing/car_router.hpp5
-rw-r--r--routing/directions_engine.cpp33
-rw-r--r--routing/directions_engine.hpp6
-rw-r--r--routing/features_road_graph.cpp8
-rw-r--r--routing/features_road_graph.hpp6
-rw-r--r--routing/fseg.hpp46
-rw-r--r--routing/fseg_index.cpp39
-rw-r--r--routing/fseg_index.hpp129
-rw-r--r--routing/geometry.cpp122
-rw-r--r--routing/geometry.hpp30
-rw-r--r--routing/index_graph.cpp197
-rw-r--r--routing/index_graph.hpp84
-rw-r--r--routing/joint.hpp76
-rw-r--r--routing/road_graph_router.cpp50
-rw-r--r--routing/road_graph_router.hpp5
-rw-r--r--routing/routing.pro10
-rw-r--r--routing/routing_integration_tests/routing_test_tools.cpp1
-rw-r--r--routing/routing_tests/index_graph_test.cpp219
-rw-r--r--routing/routing_tests/routing_tests.pro1
30 files changed, 1453 insertions, 82 deletions
diff --git a/defines.hpp b/defines.hpp
index af46f8f909..356b33d190 100644
--- a/defines.hpp
+++ b/defines.hpp
@@ -27,6 +27,7 @@
#define METADATA_FILE_TAG "meta"
#define METADATA_INDEX_FILE_TAG "metaidx"
#define ALTITUDES_FILE_TAG "altitudes"
+#define RESTRICTIONS_FILE_TAG "restrictions"
#define ROUTING_FILE_TAG "routing"
#define FEATURE_OFFSETS_FILE_TAG "offs"
#define RANKS_FILE_TAG "ranks"
@@ -40,8 +41,6 @@
#define ROUTING_SHORTCUTS_FILE_TAG "skoda"
#define ROUTING_CROSS_CONTEXT_TAG "chrysler"
-#define ROUTING_FILE_TAG "routing"
-
#define ROUTING_FTSEG_FILE_TAG "ftseg"
#define ROUTING_NODEIND_TO_FTSEGIND_FILE_TAG "node2ftseg"
diff --git a/generator/generator.pro b/generator/generator.pro
index 4c416eeed5..eab138f66a 100644
--- a/generator/generator.pro
+++ b/generator/generator.pro
@@ -40,6 +40,7 @@ SOURCES += \
restriction_generator.cpp \
restriction_writer.cpp \
routing_generator.cpp \
+ routing_index_generator.cpp \
search_index_builder.cpp \
sponsored_scoring.cpp \
srtm_parser.cpp \
@@ -80,6 +81,7 @@ HEADERS += \
restriction_generator.hpp \
restriction_writer.hpp \
routing_generator.hpp \
+ routing_index_generator.hpp \
search_index_builder.hpp \
sponsored_dataset.hpp \
sponsored_dataset_inl.hpp \
diff --git a/generator/generator_tool/generator_tool.cpp b/generator/generator_tool/generator_tool.cpp
index 817853d436..3242738bce 100644
--- a/generator/generator_tool/generator_tool.cpp
+++ b/generator/generator_tool/generator_tool.cpp
@@ -10,6 +10,7 @@
#include "generator/osm_source.hpp"
#include "generator/restriction_generator.hpp"
#include "generator/routing_generator.hpp"
+#include "generator/routing_index_generator.hpp"
#include "generator/search_index_builder.hpp"
#include "generator/statistics.hpp"
#include "generator/unpack_mwm.hpp"
@@ -87,6 +88,7 @@ DEFINE_string(restriction_name, "", "Name of file with relation restriction in o
DEFINE_string(feature_ids_to_osm_ids_name, "",
"Name of file with mapping from feature ids to osm ids.");
DEFINE_bool(generate_routing, false, "Generate section with routing information.");
+DEFINE_bool(generate_restrictions, false, "Generate section with road restrictions.");
int main(int argc, char ** argv)
{
@@ -150,7 +152,7 @@ int main(int argc, char ** argv)
FLAGS_generate_index || FLAGS_generate_search_index || FLAGS_calc_statistics ||
FLAGS_type_statistics || FLAGS_dump_types || FLAGS_dump_prefixes ||
FLAGS_dump_feature_names != "" || FLAGS_check_mwm || FLAGS_srtm_path != "" ||
- FLAGS_generate_routing)
+ FLAGS_generate_routing || FLAGS_generate_restrictions )
{
classificator::Load();
classif().SortClassificator();
@@ -237,13 +239,13 @@ int main(int argc, char ** argv)
if (!FLAGS_srtm_path.empty())
routing::BuildRoadAltitudes(datFile, FLAGS_srtm_path);
- // @TODO(bykoianko) generate_routing flag should be used for all routing information.
if (FLAGS_generate_routing)
- {
+ routing::BuildRoutingIndex(path, country);
+
+ if (FLAGS_generate_restrictions)
routing::BuildRoadRestrictions(
datFile, genInfo.GetIntermediateFileName(genInfo.m_restrictions, "" /* extention */),
genInfo.GetTargetFileName(country) + OSM2FEATURE_FILE_EXTENSION);
- }
}
string const datFile = my::JoinFoldersToPath(path, FLAGS_output + DATA_FILE_EXTENSION);
diff --git a/generator/routing_index_generator.cpp b/generator/routing_index_generator.cpp
new file mode 100644
index 0000000000..392553269b
--- /dev/null
+++ b/generator/routing_index_generator.cpp
@@ -0,0 +1,145 @@
+#include "generator/routing_index_generator.hpp"
+
+#include "routing/bicycle_model.hpp"
+#include "routing/car_model.hpp"
+#include "routing/index_graph.hpp"
+#include "routing/pedestrian_model.hpp"
+#include "routing/vehicle_model.hpp"
+
+#include "indexer/feature.hpp"
+#include "indexer/feature_processor.hpp"
+#include "indexer/index.hpp"
+#include "indexer/routing_section.hpp"
+#include "indexer/scales.hpp"
+
+#include "coding/file_name_utils.hpp"
+
+#include "platform/country_file.hpp"
+#include "platform/local_country_file.hpp"
+
+#include "base/logging.hpp"
+
+#include "std/shared_ptr.hpp"
+#include "std/unique_ptr.hpp"
+#include "std/unordered_map.hpp"
+#include "std/vector.hpp"
+
+using namespace feature;
+using namespace platform;
+using namespace routing;
+
+namespace
+{
+uint32_t constexpr kFixPointFactor = 100000;
+
+inline m2::PointI PointDToPointI(m2::PointD const & p) { return m2::PointI(p * kFixPointFactor); }
+
+uint64_t CalcLocationKey(m2::PointD const & point)
+{
+ m2::PointI const pointI(PointDToPointI(point));
+ return (static_cast<uint64_t>(pointI.y) << 32) + static_cast<uint64_t>(pointI.x);
+}
+
+class Processor final
+{
+public:
+ Processor(string const & dir, string const & country)
+ : m_pedestrianModel(make_unique<PedestrianModelFactory>()->GetVehicleModelForCountry(country))
+ , m_bicycleModel(make_unique<BicycleModelFactory>()->GetVehicleModelForCountry(country))
+ , m_carModel(make_unique<CarModelFactory>()->GetVehicleModelForCountry(country))
+ {
+ LocalCountryFile localCountryFile(dir, CountryFile(country), 1 /* version */);
+ m_index.RegisterMap(localCountryFile);
+ vector<shared_ptr<MwmInfo>> info;
+ m_index.GetMwmsInfo(info);
+ CHECK_EQUAL(info.size(), 1, ());
+ CHECK(info[0], ());
+ }
+
+ void operator()(FeatureType const & f)
+ {
+ if (!IsRoad(f))
+ return;
+
+ uint32_t const id = f.GetID().m_index;
+ f.ParseGeometry(FeatureType::BEST_GEOMETRY);
+ size_t const pointsCount = f.GetPointsCount();
+ if (pointsCount == 0)
+ return;
+
+ for (size_t fromSegId = 0; fromSegId < pointsCount; ++fromSegId)
+ {
+ uint64_t const locationKey = CalcLocationKey(f.GetPoint(fromSegId));
+ m_pos2Joint[locationKey].AddEntry(FSegId(id, fromSegId));
+ }
+ }
+
+ void ForEachFeature() { m_index.ForEachInScale(*this, scales::GetUpperScale()); }
+
+ bool IsRoad(FeatureType const & f) const
+ {
+ return m_pedestrianModel->IsRoad(f) || m_bicycleModel->IsRoad(f) || m_carModel->IsRoad(f);
+ }
+
+ void RemoveNonCrosses()
+ {
+ for (auto it = m_pos2Joint.begin(); it != m_pos2Joint.end();)
+ {
+ if (it->second.GetSize() < 2)
+ it = m_pos2Joint.erase(it);
+ else
+ ++it;
+ }
+ }
+
+ void BuildGraph(IndexGraph & graph) const
+ {
+ vector<Joint> joints;
+ joints.reserve(m_pos2Joint.size());
+ for (auto it = m_pos2Joint.begin(); it != m_pos2Joint.end(); ++it)
+ joints.emplace_back(it->second);
+
+ graph.Export(joints);
+ }
+
+private:
+ Index m_index;
+ shared_ptr<IVehicleModel> m_pedestrianModel;
+ shared_ptr<IVehicleModel> m_bicycleModel;
+ shared_ptr<IVehicleModel> m_carModel;
+ unordered_map<uint64_t, Joint> m_pos2Joint;
+};
+} // namespace
+
+namespace routing
+{
+void BuildRoutingIndex(string const & dir, string const & country)
+{
+ LOG(LINFO, ("dir =", dir, "country", country));
+ try
+ {
+ Processor processor(dir, country);
+ string const datFile = my::JoinFoldersToPath(dir, country + DATA_FILE_EXTENSION);
+ LOG(LINFO, ("datFile =", datFile));
+ processor.ForEachFeature();
+ processor.RemoveNonCrosses();
+
+ IndexGraph graph;
+ processor.BuildGraph(graph);
+ LOG(LINFO, ("roads =", graph.GetRoadsAmount()));
+ LOG(LINFO, ("joints =", graph.GetJointsAmount()));
+ LOG(LINFO, ("fsegs =", graph.GetFSegsAmount()));
+
+ FilesContainerW cont(datFile, FileWriter::OP_WRITE_EXISTING);
+ FileWriter writer = cont.GetWriter(ROUTING_FILE_TAG);
+
+ RoutingSectionHeader const header;
+ header.Serialize(writer);
+ graph.Serialize(writer);
+ }
+ catch (RootException const & e)
+ {
+ LOG(LERROR, ("An exception happened while creating", ROUTING_FILE_TAG, "section:", e.what()));
+ }
+}
+} // namespace routing
diff --git a/generator/routing_index_generator.hpp b/generator/routing_index_generator.hpp
new file mode 100644
index 0000000000..960ea46d87
--- /dev/null
+++ b/generator/routing_index_generator.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "std/string.hpp"
+
+namespace routing
+{
+void BuildRoutingIndex(string const & dir, string const & country);
+} // namespace routing
diff --git a/indexer/indexer.pro b/indexer/indexer.pro
index 69a8396265..57a5db455a 100644
--- a/indexer/indexer.pro
+++ b/indexer/indexer.pro
@@ -117,6 +117,7 @@ HEADERS += \
point_to_int64.hpp \
postcodes_matcher.hpp \ # it's in indexer due to editor wich is in indexer and depends on postcodes_marcher
rank_table.hpp \
+ routing_section.hpp \
scale_index.hpp \
scale_index_builder.hpp \
scales.hpp \
diff --git a/indexer/routing_section.hpp b/indexer/routing_section.hpp
new file mode 100644
index 0000000000..7c9544c755
--- /dev/null
+++ b/indexer/routing_section.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "coding/reader.hpp"
+#include "coding/write_to_sink.hpp"
+
+#include "std/cstdint.hpp"
+
+namespace feature
+{
+class RoutingSectionHeader final
+{
+public:
+ RoutingSectionHeader() : m_version(0), m_reserved16(0), m_reserved32(0) {}
+
+ uint16_t GetVersion() const { return m_version; }
+
+ template <class TSink>
+ void Serialize(TSink & sink) const
+ {
+ WriteToSink(sink, m_version);
+ WriteToSink(sink, m_reserved16);
+ WriteToSink(sink, m_reserved32);
+ }
+
+ template <class TSource>
+ void Deserialize(TSource & src)
+ {
+ m_version = ReadPrimitiveFromSource<decltype(m_version)>(src);
+ m_reserved16 = ReadPrimitiveFromSource<decltype(m_reserved16)>(src);
+ m_reserved32 = ReadPrimitiveFromSource<decltype(m_reserved32)>(src);
+ }
+
+private:
+ uint16_t m_version;
+ uint16_t m_reserved16;
+ uint32_t m_reserved32;
+};
+
+static_assert(sizeof(RoutingSectionHeader) == 8, "Wrong header size of routing section.");
+} // namespace feature
diff --git a/map/framework.cpp b/map/framework.cpp
index 9231dd0bbf..c0189eb642 100644
--- a/map/framework.cpp
+++ b/map/framework.cpp
@@ -8,6 +8,7 @@
#include "defines.hpp"
#include "private.h"
+#include "routing/astar_router.hpp"
#include "routing/car_router.hpp"
#include "routing/online_absent_fetcher.hpp"
#include "routing/road_graph_router.hpp"
diff --git a/routing/astar_router.cpp b/routing/astar_router.cpp
new file mode 100644
index 0000000000..51c04daf64
--- /dev/null
+++ b/routing/astar_router.cpp
@@ -0,0 +1,193 @@
+#include "astar_router.hpp"
+
+#include "routing/base/astar_algorithm.hpp"
+#include "routing/base/astar_progress.hpp"
+#include "routing/bicycle_directions.hpp"
+#include "routing/bicycle_model.hpp"
+#include "routing/car_model.hpp"
+#include "routing/features_road_graph.hpp"
+#include "routing/index_graph.hpp"
+#include "routing/pedestrian_model.hpp"
+#include "routing/route.hpp"
+#include "routing/turns_generator.hpp"
+
+#include "indexer/feature_altitude.hpp"
+#include "indexer/routing_section.hpp"
+
+#include "geometry/distance.hpp"
+#include "geometry/point2d.hpp"
+
+namespace
+{
+size_t constexpr kMaxRoadCandidates = 6;
+float constexpr kProgressInterval = 2;
+
+using namespace routing;
+
+vector<Junction> ConvertToJunctions(IndexGraph const & graph, vector<JointId> const & joints)
+{
+ vector<FSegId> const & fsegs = graph.RedressRoute(joints);
+
+ vector<Junction> junctions;
+ junctions.reserve(fsegs.size());
+
+ Geometry const & geometry = graph.GetGeometry();
+ // TODO: Use real altitudes for pedestrian and bicycle routing.
+ for (FSegId const & fseg : fsegs)
+ junctions.emplace_back(geometry.GetPoint(fseg), feature::kDefaultAltitudeMeters);
+
+ return junctions;
+}
+} // namespace
+
+namespace routing
+{
+AStarRouter::AStarRouter(const char * name, Index const & index,
+ TCountryFileFn const & countryFileFn,
+ shared_ptr<VehicleModelFactory> vehicleModelFactory,
+ unique_ptr<IDirectionsEngine> directionsEngine)
+ : m_name(name)
+ , m_index(index)
+ , m_countryFileFn(countryFileFn)
+ , m_roadGraph(
+ make_unique<FeaturesRoadGraph>(index, IRoadGraph::Mode::ObeyOnewayTag, vehicleModelFactory))
+ , m_vehicleModelFactory(vehicleModelFactory)
+ , m_directionsEngine(move(directionsEngine))
+{
+ ASSERT(name, ());
+ ASSERT(!m_name.empty(), ());
+ ASSERT(m_vehicleModelFactory, ());
+ ASSERT(m_directionsEngine, ());
+}
+
+IRouter::ResultCode AStarRouter::CalculateRoute(MwmSet::MwmId const & mwmId,
+ m2::PointD const & startPoint,
+ m2::PointD const & /* startDirection */,
+ m2::PointD const & finalPoint,
+ RouterDelegate const & delegate, Route & route)
+{
+ string const country = m_countryFileFn(startPoint);
+
+ Edge startEdge = Edge::MakeFake({} /* startJunction */, {} /* endJunction */);
+ if (!FindClosestEdge(mwmId, startPoint, startEdge))
+ return IRouter::StartPointNotFound;
+
+ Edge finishEdge = Edge::MakeFake({} /* startJunction */, {} /* endJunction */);
+ if (!FindClosestEdge(mwmId, finalPoint, finishEdge))
+ return IRouter::EndPointNotFound;
+
+ FSegId const start(startEdge.GetFeatureId().m_index, startEdge.GetSegId());
+ FSegId const finish(finishEdge.GetFeatureId().m_index, finishEdge.GetSegId());
+
+ IndexGraph graph(
+ CreateGeometry(m_index, mwmId, m_vehicleModelFactory->GetVehicleModelForCountry(country)));
+
+ if (!LoadIndex(mwmId, graph))
+ return IRouter::RouteFileNotExist;
+
+ AStarProgress progress(0, 100);
+
+ auto onVisitJunctionFn = [&delegate, &progress, &graph](JointId const & from,
+ JointId const & /*finish*/) {
+ m2::PointD const & point = graph.GetPoint(from);
+ auto const lastValue = progress.GetLastValue();
+ auto const newValue = progress.GetProgressForDirectedAlgo(point);
+ if (newValue - lastValue > kProgressInterval)
+ delegate.OnProgress(newValue);
+ delegate.OnPointCheck(point);
+ };
+
+ AStarAlgorithm<IndexGraph> algorithm;
+
+ RoutingResult<JointId> routingResult;
+ AStarAlgorithm<IndexGraph>::Result const resultCode =
+ algorithm.FindPathBidirectional(graph, graph.InsertJoint(start), graph.InsertJoint(finish),
+ routingResult, delegate, onVisitJunctionFn);
+
+ switch (resultCode)
+ {
+ case AStarAlgorithm<IndexGraph>::Result::NoPath: return IRouter::RouteNotFound;
+ case AStarAlgorithm<IndexGraph>::Result::Cancelled: return IRouter::Cancelled;
+ case AStarAlgorithm<IndexGraph>::Result::OK:
+ vector<Junction> path = ConvertToJunctions(graph, routingResult.path);
+ ReconstructRoute(m_directionsEngine.get(), *m_roadGraph, delegate, path, route);
+ return IRouter::NoError;
+ }
+}
+
+bool AStarRouter::FindClosestEdge(MwmSet::MwmId const & mwmId, m2::PointD const & point,
+ Edge & closestEdge) const
+{
+ vector<pair<Edge, Junction>> candidates;
+ m_roadGraph->FindClosestEdges(point, kMaxRoadCandidates, candidates);
+
+ double minDistance = numeric_limits<double>::max();
+ size_t minIndex = candidates.size();
+
+ for (size_t i = 0; i < candidates.size(); ++i)
+ {
+ Edge const & edge = candidates[i].first;
+ if (edge.GetFeatureId().m_mwmId != mwmId)
+ continue;
+
+ m2::DistanceToLineSquare<m2::PointD> squareDistance;
+ squareDistance.SetBounds(edge.GetStartJunction().GetPoint(), edge.GetEndJunction().GetPoint());
+ double const distance = squareDistance(point);
+ if (distance < minDistance)
+ {
+ minDistance = distance;
+ minIndex = i;
+ }
+ }
+
+ if (minIndex < candidates.size())
+ {
+ closestEdge = candidates[minIndex].first;
+ return true;
+ }
+
+ return false;
+}
+
+bool AStarRouter::LoadIndex(MwmSet::MwmId const & mwmId, IndexGraph & graph)
+{
+ MwmSet::MwmHandle mwmHandle = m_index.GetMwmHandleById(mwmId);
+ MwmValue const * mwmValue = mwmHandle.GetValue<MwmValue>();
+ if (!mwmValue)
+ {
+ LOG(LERROR, ("mwmValue == null"));
+ return false;
+ }
+
+ try
+ {
+ my::Timer timer;
+ FilesContainerR::TReader reader(mwmValue->m_cont.GetReader(ROUTING_FILE_TAG));
+ ReaderSource<FilesContainerR::TReader> src(reader);
+ feature::RoutingSectionHeader header;
+ header.Deserialize(src);
+ graph.Deserialize(src);
+ LOG(LINFO, (ROUTING_FILE_TAG, "section loaded in ", timer.ElapsedSeconds(), "seconds"));
+ return true;
+ }
+ catch (Reader::OpenException const & e)
+ {
+ LOG(LERROR, ("File", mwmValue->GetCountryFileName(), "Error while reading", ROUTING_FILE_TAG,
+ "section.", e.Msg()));
+ return false;
+ }
+}
+
+unique_ptr<AStarRouter> CreateCarAStarBidirectionalRouter(Index & index,
+ TCountryFileFn const & countryFileFn)
+{
+ shared_ptr<VehicleModelFactory> vehicleModelFactory = make_shared<CarModelFactory>();
+ // @TODO Bicycle turn generation engine is used now. It's ok for the time being.
+ // But later a special car turn generation engine should be implemented.
+ unique_ptr<IDirectionsEngine> directionsEngine = make_unique<BicycleDirectionsEngine>(index);
+ unique_ptr<AStarRouter> router =
+ make_unique<AStarRouter>("astar-bidirectional-car", index, countryFileFn,
+ move(vehicleModelFactory), move(directionsEngine));
+ return router;
+}
+} // namespace routing
diff --git a/routing/astar_router.hpp b/routing/astar_router.hpp
new file mode 100644
index 0000000000..58cbf4bfa3
--- /dev/null
+++ b/routing/astar_router.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "routing/directions_engine.hpp"
+#include "routing/road_graph.hpp"
+#include "routing/router.hpp"
+#include "routing/vehicle_model.hpp"
+
+#include "indexer/index.hpp"
+
+#include "std/shared_ptr.hpp"
+#include "std/unique_ptr.hpp"
+#include "std/vector.hpp"
+
+namespace routing
+{
+class IndexGraph;
+
+class AStarRouter
+{
+public:
+ AStarRouter(const char * name, Index const & index, TCountryFileFn const & countryFileFn,
+ shared_ptr<VehicleModelFactory> vehicleModelFactory,
+ unique_ptr<IDirectionsEngine> directionsEngine);
+
+ string const & GetName() const { return m_name; }
+
+ IRouter::ResultCode CalculateRoute(MwmSet::MwmId const & mwmId, m2::PointD const & startPoint,
+ m2::PointD const & startDirection,
+ m2::PointD const & finalPoint, RouterDelegate const & delegate,
+ Route & route);
+
+private:
+ bool FindClosestEdge(MwmSet::MwmId const & mwmId, m2::PointD const & point,
+ Edge & closestEdge) const;
+ bool LoadIndex(MwmSet::MwmId const & mwmId, IndexGraph & graph);
+
+ string m_name;
+ Index const & m_index;
+ TCountryFileFn m_countryFileFn;
+ unique_ptr<IRoadGraph> m_roadGraph;
+ shared_ptr<VehicleModelFactory> m_vehicleModelFactory;
+ unique_ptr<IDirectionsEngine> m_directionsEngine;
+};
+
+unique_ptr<AStarRouter> CreateCarAStarBidirectionalRouter(Index & index,
+ TCountryFileFn const & countryFileFn);
+} // namespace routing
diff --git a/routing/car_router.cpp b/routing/car_router.cpp
index 4ddd1260ba..08c13421a1 100644
--- a/routing/car_router.cpp
+++ b/routing/car_router.cpp
@@ -244,8 +244,8 @@ bool CarRouter::CheckRoutingAbility(m2::PointD const & startPoint, m2::PointD co
}
CarRouter::CarRouter(Index & index, TCountryFileFn const & countryFileFn,
- unique_ptr<IRouter> router)
- : m_index(index), m_indexManager(countryFileFn, index), m_router(move(router))
+ unique_ptr<AStarRouter> localRouter)
+ : m_index(index), m_indexManager(countryFileFn, index), m_router(move(localRouter))
{
}
@@ -256,11 +256,6 @@ void CarRouter::ClearState()
m_cachedTargets.clear();
m_cachedTargetPoint = m2::PointD::Zero();
m_indexManager.Clear();
-
- if (m_router)
- m_router->ClearState();
- else
- LOG(LERROR, ("m_router is not initialized."));
}
bool CarRouter::FindRouteMSMT(TFeatureGraphNodeVec const & sources,
@@ -570,10 +565,11 @@ IRouter::ResultCode CarRouter::FindSingleRouteDispatcher(FeatureGraphNode const
LOG(LERROR, ("m_router is not initialized."));
return IRouter::InternalError;
}
- LOG(LINFO, (m_router->GetName(), "route from", MercatorBounds::ToLatLon(source.segmentPoint), "to",
- MercatorBounds::ToLatLon(target.segmentPoint)));
- result = m_router->CalculateRoute(source.segmentPoint, m2::PointD(0, 0) /* direction */,
- target.segmentPoint, delegate, mwmRoute);
+ LOG(LINFO, (m_router->GetName(), "route from", MercatorBounds::ToLatLon(source.segmentPoint),
+ "to", MercatorBounds::ToLatLon(target.segmentPoint)));
+ result = m_router->CalculateRoute(source.mwmId, source.segmentPoint,
+ m2::PointD(0, 0) /* direction */, target.segmentPoint,
+ delegate, mwmRoute);
}
else
{
diff --git a/routing/car_router.hpp b/routing/car_router.hpp
index 60e4f23946..4f974869d5 100644
--- a/routing/car_router.hpp
+++ b/routing/car_router.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include "routing/astar_router.hpp"
#include "routing/osrm_data_facade.hpp"
#include "routing/osrm_engine.hpp"
#include "routing/route.hpp"
@@ -34,7 +35,7 @@ public:
typedef vector<double> GeomTurnCandidateT;
CarRouter(Index & index, TCountryFileFn const & countryFileFn,
- unique_ptr<IRouter> roadGraphRouter);
+ unique_ptr<AStarRouter> localRouter);
virtual string GetName() const override;
@@ -114,6 +115,6 @@ private:
RoutingIndexManager m_indexManager;
- unique_ptr<IRouter> m_router;
+ unique_ptr<AStarRouter> m_router;
};
} // namespace routing
diff --git a/routing/directions_engine.cpp b/routing/directions_engine.cpp
index 53d5ed602a..4d52e92ba6 100644
--- a/routing/directions_engine.cpp
+++ b/routing/directions_engine.cpp
@@ -87,4 +87,37 @@ bool IDirectionsEngine::ReconstructPath(IRoadGraph const & graph, vector<Junctio
return true;
}
+
+void ReconstructRoute(IDirectionsEngine * engine, IRoadGraph const & graph,
+ my::Cancellable const & cancellable, vector<Junction> & path, Route & route)
+{
+ CHECK(!path.empty(), ("Can't reconstruct route from an empty list of positions."));
+
+ // By some reason there're two adjacent positions on a road with
+ // the same end-points. This could happen, for example, when
+ // direction on a road was changed. But it doesn't matter since
+ // this code reconstructs only geometry of a route.
+ path.erase(unique(path.begin(), path.end()), path.end());
+ if (path.size() == 1)
+ path.emplace_back(path.back());
+
+ Route::TTimes times;
+ Route::TTurns turnsDir;
+ vector<Junction> junctions;
+ // @TODO(bykoianko) streetNames is not filled in Generate(). It should be done.
+ Route::TStreets streetNames;
+ if (engine)
+ engine->Generate(graph, path, times, turnsDir, junctions, cancellable);
+
+ vector<m2::PointD> routeGeometry;
+ JunctionsToPoints(junctions, routeGeometry);
+ feature::TAltitudes altitudes;
+ JunctionsToAltitudes(junctions, altitudes);
+
+ route.SetGeometry(routeGeometry.begin(), routeGeometry.end());
+ route.SetSectionTimes(move(times));
+ route.SetTurnInstructions(move(turnsDir));
+ route.SetStreetNames(move(streetNames));
+ route.SetAltitudes(move(altitudes));
+}
} // namespace routing
diff --git a/routing/directions_engine.hpp b/routing/directions_engine.hpp
index 54ec9cdd4e..c82b1c5d59 100644
--- a/routing/directions_engine.hpp
+++ b/routing/directions_engine.hpp
@@ -9,7 +9,6 @@
namespace routing
{
-
class IDirectionsEngine
{
public:
@@ -17,8 +16,7 @@ public:
virtual void Generate(IRoadGraph const & graph, vector<Junction> const & path,
Route::TTimes & times, Route::TTurns & turns,
- vector<Junction> & routeGeometry,
- my::Cancellable const & cancellable) = 0;
+ vector<Junction> & routeGeometry, my::Cancellable const & cancellable) = 0;
protected:
/// \brief constructs route based on |graph| and |path|. Fills |routeEdges| with the route.
@@ -31,4 +29,6 @@ protected:
Route::TTimes & times) const;
};
+void ReconstructRoute(IDirectionsEngine * engine, IRoadGraph const & graph,
+ my::Cancellable const & cancellable, vector<Junction> & path, Route & route);
} // namespace routing
diff --git a/routing/features_road_graph.cpp b/routing/features_road_graph.cpp
index 79c61a0a58..78f1bfc33c 100644
--- a/routing/features_road_graph.cpp
+++ b/routing/features_road_graph.cpp
@@ -47,8 +47,8 @@ FeaturesRoadGraph::Value::Value(MwmSet::MwmHandle handle) : m_mwmHandle(move(han
}
FeaturesRoadGraph::CrossCountryVehicleModel::CrossCountryVehicleModel(
- unique_ptr<VehicleModelFactory> vehicleModelFactory)
- : m_vehicleModelFactory(move(vehicleModelFactory))
+ shared_ptr<VehicleModelFactory> vehicleModelFactory)
+ : m_vehicleModelFactory(vehicleModelFactory)
, m_maxSpeedKMPH(m_vehicleModelFactory->GetVehicleModel()->GetMaxSpeed())
{
}
@@ -108,8 +108,8 @@ void FeaturesRoadGraph::RoadInfoCache::Clear()
m_cache.clear();
}
FeaturesRoadGraph::FeaturesRoadGraph(Index const & index, IRoadGraph::Mode mode,
- unique_ptr<VehicleModelFactory> vehicleModelFactory)
- : m_index(index), m_mode(mode), m_vehicleModel(move(vehicleModelFactory))
+ shared_ptr<VehicleModelFactory> vehicleModelFactory)
+ : m_index(index), m_mode(mode), m_vehicleModel(vehicleModelFactory)
{
}
diff --git a/routing/features_road_graph.hpp b/routing/features_road_graph.hpp
index 9e21db0d87..7657e7dc6f 100644
--- a/routing/features_road_graph.hpp
+++ b/routing/features_road_graph.hpp
@@ -26,7 +26,7 @@ private:
class CrossCountryVehicleModel : public IVehicleModel
{
public:
- CrossCountryVehicleModel(unique_ptr<VehicleModelFactory> vehicleModelFactory);
+ CrossCountryVehicleModel(shared_ptr<VehicleModelFactory> vehicleModelFactory);
// IVehicleModel overrides:
double GetSpeed(FeatureType const & f) const override;
@@ -39,7 +39,7 @@ private:
private:
IVehicleModel * GetVehicleModel(FeatureID const & featureId) const;
- unique_ptr<VehicleModelFactory> const m_vehicleModelFactory;
+ shared_ptr<VehicleModelFactory> const m_vehicleModelFactory;
double const m_maxSpeedKMPH;
mutable map<MwmSet::MwmId, shared_ptr<IVehicleModel>> m_cache;
@@ -59,7 +59,7 @@ private:
public:
FeaturesRoadGraph(Index const & index, IRoadGraph::Mode mode,
- unique_ptr<VehicleModelFactory> vehicleModelFactory);
+ shared_ptr<VehicleModelFactory> vehicleModelFactory);
static uint32_t GetStreetReadScale();
diff --git a/routing/fseg.hpp b/routing/fseg.hpp
new file mode 100644
index 0000000000..c868d13248
--- /dev/null
+++ b/routing/fseg.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "coding/reader.hpp"
+#include "coding/write_to_sink.hpp"
+
+#include "std/cstdint.hpp"
+#include "std/limits.hpp"
+
+namespace routing
+{
+class FSegId final
+{
+ static uint32_t constexpr kInvalidFeatureId = numeric_limits<uint32_t>::max();
+
+public:
+ static FSegId MakeInvalid() { return FSegId(kInvalidFeatureId, 0); }
+
+ FSegId() : m_featureId(kInvalidFeatureId), m_segId(0) {}
+
+ FSegId(uint32_t featureId, uint32_t segId) : m_featureId(featureId), m_segId(segId) {}
+
+ uint32_t GetFeatureId() const { return m_featureId; }
+
+ uint32_t GetSegId() const { return m_segId; }
+
+ bool IsValid() const { return m_featureId != kInvalidFeatureId; }
+
+ template <class TSink>
+ void Serialize(TSink & sink) const
+ {
+ WriteToSink(sink, m_featureId);
+ WriteToSink(sink, m_segId);
+ }
+
+ template <class TSource>
+ void Deserialize(TSource & src)
+ {
+ m_featureId = ReadPrimitiveFromSource<decltype(m_featureId)>(src);
+ m_segId = ReadPrimitiveFromSource<decltype(m_segId)>(src);
+ }
+
+private:
+ uint32_t m_featureId;
+ uint32_t m_segId;
+};
+} // namespace routing
diff --git a/routing/fseg_index.cpp b/routing/fseg_index.cpp
new file mode 100644
index 0000000000..e42ff4d68a
--- /dev/null
+++ b/routing/fseg_index.cpp
@@ -0,0 +1,39 @@
+#include "fseg_index.hpp"
+
+#include "std/utility.hpp"
+
+namespace routing
+{
+void FSegIndex::Export(vector<Joint> const & joints)
+{
+ for (JointId jointId = 0; jointId < joints.size(); ++jointId)
+ {
+ Joint const & joint = joints[jointId];
+ for (uint32_t i = 0; i < joint.GetSize(); ++i)
+ {
+ FSegId const & entry = joint.GetEntry(i);
+ RoadJointIds & roadJoints = m_roads[entry.GetFeatureId()];
+ roadJoints.AddJoint(entry.GetSegId(), jointId);
+ }
+ }
+}
+
+pair<JointId, uint32_t> FSegIndex::FindNeigbor(FSegId fseg, bool forward) const
+{
+ auto const it = m_roads.find(fseg.GetFeatureId());
+ if (it == m_roads.cend())
+ return make_pair(kInvalidJointId, 0);
+
+ RoadJointIds const & joints = it->second;
+ int32_t const step = forward ? 1 : -1;
+
+ for (uint32_t segId = fseg.GetSegId() + step; segId < joints.GetSize(); segId += step)
+ {
+ JointId const jointId = joints.GetJointId(segId);
+ if (jointId != kInvalidJointId)
+ return make_pair(jointId, segId);
+ }
+
+ return make_pair(kInvalidJointId, 0);
+}
+} // namespace routing
diff --git a/routing/fseg_index.hpp b/routing/fseg_index.hpp
new file mode 100644
index 0000000000..4655dc282d
--- /dev/null
+++ b/routing/fseg_index.hpp
@@ -0,0 +1,129 @@
+#pragma once
+
+#include "routing/joint.hpp"
+
+#include "coding/reader.hpp"
+#include "coding/write_to_sink.hpp"
+
+#include "std/cstdint.hpp"
+#include "std/unordered_map.hpp"
+#include "std/vector.hpp"
+
+namespace routing
+{
+class RoadJointIds final
+{
+public:
+ JointId GetJointId(uint32_t segId) const
+ {
+ if (segId < m_jointIds.size())
+ return m_jointIds[segId];
+
+ return kInvalidJointId;
+ }
+
+ void AddJoint(uint32_t segId, JointId jointId)
+ {
+ if (segId >= m_jointIds.size())
+ m_jointIds.insert(m_jointIds.end(), segId + 1 - m_jointIds.size(), kInvalidJointId);
+
+ ASSERT_EQUAL(m_jointIds[segId], kInvalidJointId, ());
+ m_jointIds[segId] = jointId;
+ }
+
+ template <typename F>
+ void ForEachJoint(F && f) const
+ {
+ for (uint32_t segId = 0; segId < m_jointIds.size(); ++segId)
+ {
+ JointId const jointId = m_jointIds[segId];
+ if (jointId != kInvalidJointId)
+ f(segId, jointId);
+ }
+ }
+
+ size_t GetSize() const { return m_jointIds.size(); }
+
+ template <class TSink>
+ void Serialize(TSink & sink) const
+ {
+ WriteToSink(sink, static_cast<JointId>(m_jointIds.size()));
+ for (JointId jointId : m_jointIds)
+ WriteToSink(sink, jointId);
+ }
+
+ template <class TSource>
+ void Deserialize(TSource & src)
+ {
+ JointId const jointsSize = ReadPrimitiveFromSource<JointId>(src);
+ m_jointIds.reserve(jointsSize);
+ for (JointId i = 0; i < jointsSize; ++i)
+ {
+ JointId const jointId = ReadPrimitiveFromSource<JointId>(src);
+ m_jointIds.emplace_back(jointId);
+ }
+ }
+
+private:
+ vector<JointId> m_jointIds;
+};
+
+class FSegIndex final
+{
+public:
+ void Export(vector<Joint> const & joints);
+
+ void AddJoint(FSegId fseg, JointId jointId)
+ {
+ RoadJointIds & road = m_roads[fseg.GetFeatureId()];
+ road.AddJoint(fseg.GetSegId(), jointId);
+ }
+
+ pair<JointId, uint32_t> FindNeigbor(FSegId fseg, bool forward) const;
+
+ template <class TSink>
+ void Serialize(TSink & sink) const
+ {
+ WriteToSink(sink, static_cast<uint32_t>(m_roads.size()));
+ for (auto it = m_roads.begin(); it != m_roads.end(); ++it)
+ {
+ uint32_t const featureId = it->first;
+ WriteToSink(sink, featureId);
+ it->second.Serialize(sink);
+ }
+ }
+
+ template <class TSource>
+ void Deserialize(TSource & src)
+ {
+ size_t const roadsSize = static_cast<size_t>(ReadPrimitiveFromSource<uint32_t>(src));
+ for (size_t i = 0; i < roadsSize; ++i)
+ {
+ uint32_t featureId = ReadPrimitiveFromSource<decltype(featureId)>(src);
+ m_roads[featureId].Deserialize(src);
+ }
+ }
+
+ uint32_t GetSize() const { return m_roads.size(); }
+
+ JointId GetJointId(FSegId fseg) const
+ {
+ auto const it = m_roads.find(fseg.GetFeatureId());
+ if (it == m_roads.end())
+ return kInvalidJointId;
+
+ return it->second.GetJointId(fseg.GetSegId());
+ }
+
+ template <typename F>
+ void ForEachRoad(F && f) const
+ {
+ for (auto it = m_roads.begin(); it != m_roads.end(); ++it)
+ f(it->first, it->second);
+ }
+
+private:
+ // map from feature id to RoadJointIds.
+ unordered_map<uint32_t, RoadJointIds> m_roads;
+};
+} // namespace routing
diff --git a/routing/geometry.cpp b/routing/geometry.cpp
new file mode 100644
index 0000000000..c7431f44bd
--- /dev/null
+++ b/routing/geometry.cpp
@@ -0,0 +1,122 @@
+#include "geometry.hpp"
+
+#include "geometry/mercator.hpp"
+
+#include "base/assert.hpp"
+
+namespace
+{
+using namespace routing;
+
+double constexpr kMPH2MPS = 1000.0 / (60 * 60);
+
+inline double TimeBetweenSec(m2::PointD const & from, m2::PointD const & to, double speedMPS)
+{
+ ASSERT_GREATER(speedMPS, 0.0, ());
+
+ double const distanceM = MercatorBounds::DistanceOnEarth(from, to);
+ return distanceM / speedMPS;
+}
+
+class GeometryImpl final : public Geometry
+{
+public:
+ GeometryImpl(Index const & index, MwmSet::MwmId const & mwmId,
+ shared_ptr<IVehicleModel> vehicleModel);
+
+ // Geometry overrides:
+ bool IsRoad(uint32_t featureId) const override;
+ bool IsOneWay(uint32_t featureId) const override;
+ m2::PointD const & GetPoint(FSegId fseg) const override;
+ double CalcEdgesWeight(uint32_t featureId, uint32_t pointStart,
+ uint32_t pointFinish) const override;
+ double CalcHeuristic(FSegId from, FSegId to) const override;
+
+private:
+ FeatureType const & GetFeature(uint32_t featureId) const;
+ FeatureType const & LoadFeature(uint32_t featureId) const;
+
+ Index const & m_index;
+ MwmSet::MwmId const m_mwmId;
+ shared_ptr<IVehicleModel> m_vehicleModel;
+ double const m_maxSpeedMPS;
+ mutable map<uint32_t, FeatureType> m_features;
+};
+
+GeometryImpl::GeometryImpl(Index const & index, MwmSet::MwmId const & mwmId,
+ shared_ptr<IVehicleModel> vehicleModel)
+ : m_index(index)
+ , m_mwmId(mwmId)
+ , m_vehicleModel(vehicleModel)
+ , m_maxSpeedMPS(vehicleModel->GetMaxSpeed() * kMPH2MPS)
+{
+}
+
+bool GeometryImpl::IsRoad(uint32_t featureId) const
+{
+ return m_vehicleModel->IsRoad(GetFeature(featureId));
+}
+
+bool GeometryImpl::IsOneWay(uint32_t featureId) const
+{
+ return m_vehicleModel->IsOneWay(GetFeature(featureId));
+}
+
+m2::PointD const & GeometryImpl::GetPoint(FSegId fseg) const
+{
+ return GetFeature(fseg.GetFeatureId()).GetPoint(fseg.GetSegId());
+}
+
+double GeometryImpl::CalcEdgesWeight(uint32_t featureId, uint32_t pointFrom, uint32_t pointTo) const
+{
+ uint32_t const start = min(pointFrom, pointTo);
+ uint32_t const finish = max(pointFrom, pointTo);
+ FeatureType const & feature = GetFeature(featureId);
+
+ ASSERT_LESS(finish, feature.GetPointsCount(), ());
+
+ double result = 0.0;
+ double const speedMPS = m_vehicleModel->GetSpeed(feature) * kMPH2MPS;
+ for (uint32_t i = start; i < finish; ++i)
+ {
+ result += TimeBetweenSec(feature.GetPoint(i), feature.GetPoint(i + 1), speedMPS);
+ }
+
+ return result;
+}
+
+double GeometryImpl::CalcHeuristic(FSegId from, FSegId to) const
+{
+ return TimeBetweenSec(GetPoint(from), GetPoint(to), m_maxSpeedMPS);
+}
+
+FeatureType const & GeometryImpl::GetFeature(uint32_t featureId) const
+{
+ auto it = m_features.find(featureId);
+ if (it != m_features.end())
+ return it->second;
+
+ return LoadFeature(featureId);
+}
+
+FeatureType const & GeometryImpl::LoadFeature(uint32_t featureId) const
+{
+ Index::FeaturesLoaderGuard guard(m_index, m_mwmId);
+ FeatureType & feature = m_features[featureId];
+ bool const isFound = guard.GetFeatureByIndex(featureId, feature);
+ ASSERT(isFound, ("Feature", featureId, "not found"));
+ if (isFound)
+ feature.ParseGeometry(FeatureType::BEST_GEOMETRY);
+
+ return feature;
+}
+} // namespace
+
+namespace routing
+{
+unique_ptr<Geometry> CreateGeometry(Index const & index, MwmSet::MwmId const & mwmId,
+ shared_ptr<IVehicleModel> vehicleModel)
+{
+ return make_unique<GeometryImpl>(index, mwmId, vehicleModel);
+}
+} // namespace routing
diff --git a/routing/geometry.hpp b/routing/geometry.hpp
new file mode 100644
index 0000000000..b4426c3fb8
--- /dev/null
+++ b/routing/geometry.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "routing/fseg.hpp"
+#include "routing/vehicle_model.hpp"
+
+#include "indexer/index.hpp"
+
+#include "geometry/point2d.hpp"
+
+#include "std/cstdint.hpp"
+#include "std/shared_ptr.hpp"
+#include "std/unique_ptr.hpp"
+
+namespace routing
+{
+class Geometry
+{
+public:
+ virtual ~Geometry() = default;
+ virtual bool IsRoad(uint32_t featureId) const = 0;
+ virtual bool IsOneWay(uint32_t featureId) const = 0;
+ virtual m2::PointD const & GetPoint(FSegId fseg) const = 0;
+ virtual double CalcEdgesWeight(uint32_t featureId, uint32_t pointFrom,
+ uint32_t pointTo) const = 0;
+ virtual double CalcHeuristic(FSegId from, FSegId to) const = 0;
+};
+
+unique_ptr<Geometry> CreateGeometry(Index const & index, MwmSet::MwmId const & mwmId,
+ shared_ptr<IVehicleModel> vehicleModel);
+} // namespace routing
diff --git a/routing/index_graph.cpp b/routing/index_graph.cpp
new file mode 100644
index 0000000000..36acd31717
--- /dev/null
+++ b/routing/index_graph.cpp
@@ -0,0 +1,197 @@
+#include "index_graph.hpp"
+
+#include "base/assert.hpp"
+
+namespace routing
+{
+IndexGraph::IndexGraph(unique_ptr<Geometry> geometry) : m_geometry(move(geometry)) {}
+
+void IndexGraph::GetOutgoingEdgesList(JointId jointId, vector<TEdgeType> & edges) const
+{
+ GetEdgesList(jointId, edges, true);
+}
+
+void IndexGraph::GetIngoingEdgesList(JointId jointId, vector<TEdgeType> & edges) const
+{
+ GetEdgesList(jointId, edges, false);
+}
+
+double IndexGraph::HeuristicCostEstimate(JointId jointFrom, JointId jointTo) const
+{
+ return m_geometry->CalcHeuristic(GetFSeg(jointFrom), GetFSeg(jointTo));
+}
+
+m2::PointD const & IndexGraph::GetPoint(JointId jointId) const
+{
+ return m_geometry->GetPoint(GetFSeg(jointId));
+}
+
+void IndexGraph::Export(vector<Joint> const & joints)
+{
+ m_fsegIndex.Export(joints);
+ BuildJoints(joints.size());
+}
+
+JointId IndexGraph::InsertJoint(FSegId fseg)
+{
+ JointId const existId = m_fsegIndex.GetJointId(fseg);
+ if (existId != kInvalidJointId)
+ return existId;
+
+ JointId const jointId = m_jointOffsets.size();
+ m_jointOffsets.emplace_back(JointOffset(m_fsegs.size(), m_fsegs.size() + 1));
+ m_fsegs.emplace_back(fseg);
+ m_fsegIndex.AddJoint(fseg, jointId);
+ return jointId;
+}
+
+vector<FSegId> IndexGraph::RedressRoute(vector<JointId> const & route) const
+{
+ vector<FSegId> fsegs;
+ if (route.size() < 2)
+ {
+ if (route.size() == 1)
+ fsegs.emplace_back(GetFSeg(route[0]));
+ return fsegs;
+ }
+
+ fsegs.reserve(route.size() * 2);
+
+ for (size_t i = 0; i < route.size() - 1; ++i)
+ {
+ JointId const prevJoint = route[i];
+ JointId const nextJoint = route[i + 1];
+
+ auto const & pair = FindSharedFeature(prevJoint, nextJoint);
+ if (!pair.first.IsValid())
+ {
+ fsegs.clear();
+ return fsegs;
+ }
+
+ if (i == 0)
+ fsegs.push_back(pair.first);
+
+ uint32_t const featureId = pair.first.GetFeatureId();
+ uint32_t const segFrom = pair.first.GetSegId();
+ uint32_t const segTo = pair.second.GetSegId();
+
+ ASSERT_NOT_EQUAL(segFrom, segTo, ());
+
+ if (segFrom < segTo)
+ {
+ for (uint32_t seg = segFrom + 1; seg < segTo; ++seg)
+ fsegs.push_back({featureId, seg});
+ }
+ else
+ {
+ for (uint32_t seg = segFrom - 1; seg > segTo; --seg)
+ fsegs.push_back({featureId, seg});
+ }
+
+ fsegs.push_back(pair.second);
+ }
+
+ return fsegs;
+}
+
+FSegId IndexGraph::GetFSeg(JointId jointId) const
+{
+ return m_fsegs[GetJointOffset(jointId).Begin()];
+}
+
+inline JointOffset const & IndexGraph::GetJointOffset(JointId jointId) const
+{
+ ASSERT_LESS(jointId, m_jointOffsets.size(), ("JointId out of bounds"));
+ return m_jointOffsets[jointId];
+}
+
+pair<FSegId, FSegId> IndexGraph::FindSharedFeature(JointId jointId0, JointId jointId1) const
+{
+ JointOffset const & offset0 = GetJointOffset(jointId0);
+ JointOffset const & offset1 = GetJointOffset(jointId1);
+
+ for (size_t i = offset0.Begin(); i < offset0.End(); ++i)
+ {
+ FSegId const & fseg0 = m_fsegs[i];
+ for (size_t j = offset1.Begin(); j < offset1.End(); ++j)
+ {
+ FSegId const & fseg1 = m_fsegs[j];
+ if (fseg0.GetFeatureId() == fseg1.GetFeatureId())
+ return make_pair(fseg0, fseg1);
+ }
+ }
+
+ LOG(LERROR, ("Can't find shared feature for joints", jointId0, jointId1));
+ return make_pair(FSegId::MakeInvalid(), FSegId::MakeInvalid());
+}
+
+void IndexGraph::BuildJoints(uint32_t jointsAmount)
+{
+ // +2 is reserved space for start and finish
+ m_jointOffsets.reserve(jointsAmount + 2);
+ m_jointOffsets.resize(jointsAmount, {0, 0});
+
+ m_fsegIndex.ForEachRoad([this](uint32_t featureId, RoadJointIds const & road) {
+ road.ForEachJoint([this](uint32_t segId, uint32_t jointId) {
+ ASSERT_LESS(jointId, m_jointOffsets.size(), ());
+ m_jointOffsets[jointId].IncSize();
+ });
+ });
+
+ uint32_t offset = 0;
+ for (size_t i = 0; i < m_jointOffsets.size(); ++i)
+ {
+ JointOffset & jointOffset = m_jointOffsets[i];
+ uint32_t const size = jointOffset.Size();
+ ASSERT_GREATER(size, 0, ());
+
+ jointOffset.Assign(offset);
+ offset += size;
+ }
+
+ // +2 is reserved space for start and finish
+ m_fsegs.reserve(offset + 2);
+ m_fsegs.resize(offset);
+
+ m_fsegIndex.ForEachRoad([this](uint32_t featureId, RoadJointIds const & road) {
+ road.ForEachJoint([this, featureId](uint32_t segId, uint32_t jointId) {
+ ASSERT_LESS(jointId, m_jointOffsets.size(), ());
+ JointOffset & jointOffset = m_jointOffsets[jointId];
+ m_fsegs[jointOffset.End()] = {featureId, segId};
+ jointOffset.IncSize();
+ });
+ });
+}
+
+void IndexGraph::AddNeigborEdge(vector<TEdgeType> & edges, FSegId fseg, bool forward) const
+{
+ pair<JointId, uint32_t> const & pair = m_fsegIndex.FindNeigbor(fseg, forward);
+ if (pair.first != kInvalidJointId)
+ {
+ double const distance =
+ m_geometry->CalcEdgesWeight(fseg.GetFeatureId(), fseg.GetSegId(), pair.second);
+ edges.push_back({pair.first, distance});
+ }
+}
+
+void IndexGraph::GetEdgesList(JointId jointId, vector<TEdgeType> & edges, bool forward) const
+{
+ edges.clear();
+
+ JointOffset const & offset = GetJointOffset(jointId);
+ for (size_t i = offset.Begin(); i < offset.End(); ++i)
+ {
+ FSegId const & fseg = m_fsegs[i];
+ if (!m_geometry->IsRoad(fseg.GetFeatureId()))
+ continue;
+
+ bool const twoWay = !m_geometry->IsOneWay(fseg.GetFeatureId());
+ if (!forward || twoWay)
+ AddNeigborEdge(edges, fseg, false);
+
+ if (forward || twoWay)
+ AddNeigborEdge(edges, fseg, true);
+ }
+}
+} // namespace routing
diff --git a/routing/index_graph.hpp b/routing/index_graph.hpp
new file mode 100644
index 0000000000..4e94af5ab5
--- /dev/null
+++ b/routing/index_graph.hpp
@@ -0,0 +1,84 @@
+#pragma once
+
+#include "routing/fseg.hpp"
+#include "routing/fseg_index.hpp"
+#include "routing/geometry.hpp"
+#include "routing/joint.hpp"
+
+#include "coding/reader.hpp"
+#include "coding/write_to_sink.hpp"
+
+#include "geometry/point2d.hpp"
+
+#include "std/cstdint.hpp"
+
+namespace routing
+{
+class JointEdge final
+{
+public:
+ JointEdge(JointId target, double weight) : target(target), weight(weight) {}
+ JointId GetTarget() const { return target; }
+ double GetWeight() const { return weight; }
+private:
+ JointId target;
+ double weight;
+};
+
+class IndexGraph final
+{
+public:
+ using TVertexType = JointId;
+ using TEdgeType = JointEdge;
+
+ IndexGraph() = default;
+ explicit IndexGraph(unique_ptr<Geometry> geometry);
+
+ // TGraph overloads:
+ void GetOutgoingEdgesList(TVertexType vertex, vector<TEdgeType> & edges) const;
+ void GetIngoingEdgesList(TVertexType vertex, vector<TEdgeType> & edges) const;
+ double HeuristicCostEstimate(TVertexType from, TVertexType to) const;
+
+ // Access methods.
+ Geometry const & GetGeometry() const { return *m_geometry; }
+ m2::PointD const & GetPoint(JointId jointId) const;
+ size_t GetRoadsAmount() const { return m_fsegIndex.GetSize(); }
+ size_t GetJointsAmount() const { return m_jointOffsets.size(); }
+ size_t GetFSegsAmount() const { return m_fsegs.size(); }
+ void Export(vector<Joint> const & joints);
+ JointId InsertJoint(FSegId fseg);
+ vector<FSegId> RedressRoute(vector<JointId> const & route) const;
+
+ template <class TSink>
+ void Serialize(TSink & sink) const
+ {
+ WriteToSink(sink, static_cast<JointId>(GetJointsAmount()));
+ m_fsegIndex.Serialize(sink);
+ }
+
+ template <class TSource>
+ void Deserialize(TSource & src)
+ {
+ uint32_t const jointsSize = ReadPrimitiveFromSource<uint32_t>(src);
+ m_fsegIndex.Deserialize(src);
+ BuildJoints(jointsSize);
+ }
+
+private:
+ // Access methods.
+ FSegId GetFSeg(JointId jointId) const;
+ JointOffset const & GetJointOffset(JointId jointId) const;
+ pair<FSegId, FSegId> FindSharedFeature(JointId jointId0, JointId jointId1) const;
+
+ void BuildJoints(uint32_t jointsAmount);
+
+ // Edge methods.
+ void AddNeigborEdge(vector<TEdgeType> & edges, FSegId fseg, bool forward) const;
+ void GetEdgesList(JointId jointId, vector<TEdgeType> & edges, bool forward) const;
+
+ unique_ptr<Geometry> m_geometry;
+ FSegIndex m_fsegIndex;
+ vector<JointOffset> m_jointOffsets;
+ vector<FSegId> m_fsegs;
+};
+} // namespace routing
diff --git a/routing/joint.hpp b/routing/joint.hpp
new file mode 100644
index 0000000000..cc645ce34c
--- /dev/null
+++ b/routing/joint.hpp
@@ -0,0 +1,76 @@
+#pragma once
+
+#include "routing/fseg.hpp"
+
+#include "coding/reader.hpp"
+#include "coding/write_to_sink.hpp"
+
+#include "std/cstdint.hpp"
+#include "std/limits.hpp"
+#include "std/vector.hpp"
+
+namespace routing
+{
+using JointId = uint32_t;
+JointId constexpr kInvalidJointId = numeric_limits<JointId>::max();
+
+class Joint final
+{
+public:
+ void AddEntry(FSegId entry) { m_entries.emplace_back(entry); }
+
+ size_t GetSize() const { return m_entries.size(); }
+
+ FSegId const & GetEntry(size_t i) const { return m_entries[i]; }
+
+ template <class TSink>
+ void Serialize(TSink & sink) const
+ {
+ WriteToSink(sink, static_cast<uint32_t>(m_entries.size()));
+ for (auto const & entry : m_entries)
+ {
+ entry.Serialize(sink);
+ }
+ }
+
+ template <class TSource>
+ void Deserialize(TSource & src)
+ {
+ size_t const size = static_cast<size_t>(ReadPrimitiveFromSource<uint32_t>(src));
+ m_entries.resize(size);
+ for (size_t i = 0; i < size; ++i)
+ {
+ m_entries[i].Deserialize(src);
+ }
+ }
+
+private:
+ vector<FSegId> m_entries;
+};
+
+class JointOffset final
+{
+public:
+ JointOffset() : m_begin(0), m_end(0) {}
+
+ JointOffset(uint32_t begin, uint32_t end) : m_begin(begin), m_end(end) {}
+
+ uint32_t Begin() const { return m_begin; }
+
+ uint32_t End() const { return m_end; }
+
+ uint32_t Size() const { return m_end - m_begin; }
+
+ void Assign(uint32_t offset)
+ {
+ m_begin = offset;
+ m_end = offset;
+ }
+
+ void IncSize() { ++m_end; }
+
+private:
+ uint32_t m_begin;
+ uint32_t m_end;
+};
+} // namespace routing
diff --git a/routing/road_graph_router.cpp b/routing/road_graph_router.cpp
index 06409c83ef..a4a8db19d7 100644
--- a/routing/road_graph_router.cpp
+++ b/routing/road_graph_router.cpp
@@ -1,5 +1,6 @@
#include "routing/road_graph_router.hpp"
+#include "routing/astar_router.hpp"
#include "routing/bicycle_directions.hpp"
#include "routing/bicycle_model.hpp"
#include "routing/car_model.hpp"
@@ -225,7 +226,7 @@ IRouter::ResultCode RoadGraphRouter::CalculateRoute(m2::PointD const & startPoin
ASSERT_EQUAL(result.path.front(), startPos, ());
ASSERT_EQUAL(result.path.back(), finalPos, ());
ASSERT_GREATER(result.distance, 0., ());
- ReconstructRoute(move(result.path), route, delegate);
+ ReconstructRoute(m_directionsEngine.get(), *m_roadGraph, delegate, result.path, route);
}
m_roadGraph->ResetFakes();
@@ -246,39 +247,6 @@ IRouter::ResultCode RoadGraphRouter::CalculateRoute(m2::PointD const & startPoin
return IRouter::RouteNotFound;
}
-void RoadGraphRouter::ReconstructRoute(vector<Junction> && path, Route & route,
- my::Cancellable const & cancellable) const
-{
- CHECK(!path.empty(), ("Can't reconstruct route from an empty list of positions."));
-
- // By some reason there're two adjacent positions on a road with
- // the same end-points. This could happen, for example, when
- // direction on a road was changed. But it doesn't matter since
- // this code reconstructs only geometry of a route.
- path.erase(unique(path.begin(), path.end()), path.end());
- if (path.size() == 1)
- path.emplace_back(path.back());
-
- Route::TTimes times;
- Route::TTurns turnsDir;
- vector<Junction> junctions;
- // @TODO(bykoianko) streetNames is not filled in Generate(). It should be done.
- Route::TStreets streetNames;
- if (m_directionsEngine)
- m_directionsEngine->Generate(*m_roadGraph, path, times, turnsDir, junctions, cancellable);
-
- vector<m2::PointD> routeGeometry;
- JunctionsToPoints(junctions, routeGeometry);
- feature::TAltitudes altitudes;
- JunctionsToAltitudes(junctions, altitudes);
-
- route.SetGeometry(routeGeometry.begin(), routeGeometry.end());
- route.SetSectionTimes(move(times));
- route.SetTurnInstructions(move(turnsDir));
- route.SetStreetNames(move(streetNames));
- route.SetAltitudes(move(altitudes));
-}
-
unique_ptr<IRouter> CreatePedestrianAStarRouter(Index & index, TCountryFileFn const & countryFileFn)
{
unique_ptr<VehicleModelFactory> vehicleModelFactory(new PedestrianModelFactory());
@@ -311,18 +279,4 @@ unique_ptr<IRouter> CreateBicycleAStarBidirectionalRouter(Index & index, TCountr
move(vehicleModelFactory), move(algorithm), move(directionsEngine)));
return router;
}
-
-unique_ptr<IRouter> CreateCarAStarBidirectionalRouter(Index & index,
- TCountryFileFn const & countryFileFn)
-{
- unique_ptr<VehicleModelFactory> vehicleModelFactory = make_unique<CarModelFactory>();
- unique_ptr<IRoutingAlgorithm> algorithm = make_unique<AStarBidirectionalRoutingAlgorithm>();
- // @TODO Bicycle turn generation engine is used now. It's ok for the time being.
- // But later a special car turn generation engine should be implemented.
- unique_ptr<IDirectionsEngine> directionsEngine = make_unique<BicycleDirectionsEngine>(index);
- unique_ptr<IRouter> router = make_unique<RoadGraphRouter>(
- "astar-bidirectional-car", index, countryFileFn, IRoadGraph::Mode::ObeyOnewayTag,
- move(vehicleModelFactory), move(algorithm), move(directionsEngine));
- return router;
-}
} // namespace routing
diff --git a/routing/road_graph_router.hpp b/routing/road_graph_router.hpp
index 14405a38d6..b7a9f00cd9 100644
--- a/routing/road_graph_router.hpp
+++ b/routing/road_graph_router.hpp
@@ -37,9 +37,6 @@ public:
Route & route) override;
private:
- void ReconstructRoute(vector<Junction> && junctions, Route & route,
- my::Cancellable const & cancellable) const;
-
/// Checks existance and add absent maps to route.
/// Returns true if map exists
bool CheckMapExistence(m2::PointD const & point, Route & route) const;
@@ -55,6 +52,4 @@ private:
unique_ptr<IRouter> CreatePedestrianAStarRouter(Index & index, TCountryFileFn const & countryFileFn);
unique_ptr<IRouter> CreatePedestrianAStarBidirectionalRouter(Index & index, TCountryFileFn const & countryFileFn);
unique_ptr<IRouter> CreateBicycleAStarBidirectionalRouter(Index & index, TCountryFileFn const & countryFileFn);
-unique_ptr<IRouter> CreateCarAStarBidirectionalRouter(Index & index,
- TCountryFileFn const & countryFileFn);
} // namespace routing
diff --git a/routing/routing.pro b/routing/routing.pro
index e4792d64ec..f354cefcb3 100644
--- a/routing/routing.pro
+++ b/routing/routing.pro
@@ -13,6 +13,7 @@ INCLUDEPATH += $$ROOT_DIR/3party/jansson/src \
$$ROOT_DIR/3party/osrm/osrm-backend/third_party
SOURCES += \
+ astar_router.cpp \
async_router.cpp \
base/followed_polyline.cpp \
bicycle_directions.cpp \
@@ -24,6 +25,9 @@ SOURCES += \
cross_routing_context.cpp \
directions_engine.cpp \
features_road_graph.cpp \
+ fseg_index.cpp \
+ geometry.cpp \
+ index_graph.cpp \
nearest_edge_finder.cpp \
online_absent_fetcher.cpp \
online_cross_fetcher.cpp \
@@ -53,6 +57,7 @@ SOURCES += \
HEADERS += \
+ astar_router.hpp \
async_router.hpp \
base/astar_algorithm.hpp \
base/followed_polyline.hpp \
@@ -65,6 +70,11 @@ HEADERS += \
cross_routing_context.hpp \
directions_engine.hpp \
features_road_graph.hpp \
+ fseg.hpp \
+ fseg_index.hpp \
+ geometry.hpp \
+ index_graph.hpp \
+ joint.hpp \
loaded_path_segment.hpp \
nearest_edge_finder.hpp \
online_absent_fetcher.hpp \
diff --git a/routing/routing_integration_tests/routing_test_tools.cpp b/routing/routing_integration_tests/routing_test_tools.cpp
index 378d8513ac..53bfb3a551 100644
--- a/routing/routing_integration_tests/routing_test_tools.cpp
+++ b/routing/routing_integration_tests/routing_test_tools.cpp
@@ -7,6 +7,7 @@
#include "geometry/distance_on_sphere.hpp"
#include "geometry/latlon.hpp"
+#include "routing/astar_router.hpp"
#include "routing/online_absent_fetcher.hpp"
#include "routing/online_cross_fetcher.hpp"
#include "routing/road_graph_router.hpp"
diff --git a/routing/routing_tests/index_graph_test.cpp b/routing/routing_tests/index_graph_test.cpp
new file mode 100644
index 0000000000..d5793ca1e2
--- /dev/null
+++ b/routing/routing_tests/index_graph_test.cpp
@@ -0,0 +1,219 @@
+#include "testing/testing.hpp"
+
+#include "routing/base/astar_algorithm.hpp"
+#include "routing/index_graph.hpp"
+
+#include "geometry/point2d.hpp"
+
+#include "base/assert.hpp"
+
+#include "std/algorithm.hpp"
+#include "std/map.hpp"
+#include "std/vector.hpp"
+
+namespace
+{
+using namespace routing;
+
+class TestGeometry : public Geometry
+{
+public:
+ // Geometry overrides:
+ bool IsRoad(uint32_t featureId) const override;
+ bool IsOneWay(uint32_t featureId) const override;
+ m2::PointD const & GetPoint(FSegId fseg) const override;
+ double CalcEdgesWeight(uint32_t featureId, uint32_t pointStart,
+ uint32_t pointFinish) const override;
+ double CalcHeuristic(FSegId from, FSegId to) const override;
+
+ void AddRoad(uint32_t featureId, vector<m2::PointD> const & points);
+
+private:
+ map<uint32_t, vector<m2::PointD>> m_features;
+};
+
+bool TestGeometry::IsRoad(uint32_t featureId) const
+{
+ return m_features.find(featureId) != m_features.end();
+}
+
+bool TestGeometry::IsOneWay(uint32_t featureId) const { return false; }
+
+m2::PointD const & TestGeometry::GetPoint(FSegId fseg) const
+{
+ auto it = m_features.find(fseg.GetFeatureId());
+ if (it == m_features.end())
+ {
+ ASSERT(false, ("Can't find feature", fseg.GetFeatureId()));
+ static m2::PointD invalidResult(-1.0, -1.0);
+ return invalidResult;
+ }
+
+ ASSERT_LESS(fseg.GetSegId(), it->second.size(), ("featureId =", fseg.GetFeatureId()));
+ return it->second[fseg.GetSegId()];
+}
+
+double TestGeometry::CalcEdgesWeight(uint32_t featureId, uint32_t pointFrom, uint32_t pointTo) const
+{
+ auto it = m_features.find(featureId);
+ if (it == m_features.end())
+ {
+ ASSERT(false, ("Can't find feature", featureId));
+ return 0.0;
+ }
+ vector<m2::PointD> const & points = it->second;
+
+ uint32_t const start = min(pointFrom, pointTo);
+ uint32_t const finish = max(pointFrom, pointTo);
+ ASSERT_LESS(finish, points.size(), ());
+
+ double result = 0.0;
+ for (uint32_t i = start; i < finish; ++i)
+ result += points[i].Length(points[i + 1]);
+
+ return result;
+}
+
+double TestGeometry::CalcHeuristic(FSegId from, FSegId to) const
+{
+ return GetPoint(from).Length(GetPoint(to));
+}
+
+void TestGeometry::AddRoad(uint32_t featureId, vector<m2::PointD> const & points)
+{
+ auto it = m_features.find(featureId);
+ if (it != m_features.end())
+ {
+ ASSERT(false, ("Already contains feature", featureId));
+ return;
+ }
+
+ m_features[featureId] = points;
+}
+
+Joint MakeJoint(vector<FSegId> const & points)
+{
+ Joint joint;
+ for (auto const & point : points)
+ {
+ joint.AddEntry(point);
+ }
+ return joint;
+}
+
+void CheckRoute(IndexGraph & graph, FSegId const & start, FSegId const & finish,
+ size_t expectedLength)
+{
+ LOG(LINFO, ("Check route", start.GetFeatureId(), ",", start.GetSegId(), "=>",
+ finish.GetFeatureId(), ",", finish.GetSegId()));
+
+ AStarAlgorithm<IndexGraph> algorithm;
+ RoutingResult<JointId> routingResult;
+
+ AStarAlgorithm<IndexGraph>::Result const resultCode = algorithm.FindPath(
+ graph, graph.InsertJoint(start), graph.InsertJoint(finish), routingResult, {}, {});
+ vector<FSegId> const & fsegs = graph.RedressRoute(routingResult.path);
+
+ TEST_EQUAL(resultCode, AStarAlgorithm<IndexGraph>::Result::OK, ());
+ TEST_EQUAL(fsegs.size(), expectedLength, ());
+}
+
+uint32_t AbsDelta(uint32_t v0, uint32_t v1) { return v0 > v1 ? v0 - v1 : v1 - v0; }
+} // namespace
+
+namespace routing_test
+{
+// R1:
+//
+// -2
+// -1
+// R0: -2 -1 0 1 2
+// 1
+// 2
+//
+UNIT_TEST(FindPathCross)
+{
+ unique_ptr<TestGeometry> geometry = make_unique<TestGeometry>();
+ geometry->AddRoad(0, {{-2.0, 0.0}, {-1.0, 0.0}, {0.0, 0.0}, {1.0, 0.0}, {2.0, 0.0}});
+ geometry->AddRoad(1, {{0.0, -2.0}, {-1.0, 0.0}, {0.0, 0.0}, {0.0, 1.0}, {0.0, 2.0}});
+
+ IndexGraph graph(move(geometry));
+
+ graph.Export({MakeJoint({{0, 2}, {1, 2}})});
+
+ vector<FSegId> points;
+ for (uint32_t i = 0; i < 5; ++i)
+ {
+ points.emplace_back(0, i);
+ points.emplace_back(1, i);
+ }
+
+ for (auto const & start : points)
+ {
+ for (auto const & finish : points)
+ {
+ uint32_t expectedLength;
+ if (start.GetFeatureId() == finish.GetFeatureId())
+ expectedLength = AbsDelta(start.GetSegId(), finish.GetSegId()) + 1;
+ else
+ expectedLength = AbsDelta(start.GetSegId(), 2) + AbsDelta(finish.GetSegId(), 2) + 1;
+ CheckRoute(graph, start, finish, expectedLength);
+ }
+ }
+}
+
+// R4 R5 R6 R7
+//
+// R0: 0 - * - * - *
+// | | | |
+// R1: * - 1 - * - *
+// | | | |
+// R2 * - * - 2 - *
+// | | | |
+// R3 * - * - * - 3
+//
+UNIT_TEST(FindPathManhattan)
+{
+ uint32_t constexpr kCitySize = 4;
+ unique_ptr<TestGeometry> geometry = make_unique<TestGeometry>();
+ for (uint32_t i = 0; i < kCitySize; ++i)
+ {
+ vector<m2::PointD> horizontalRoad;
+ vector<m2::PointD> verticalRoad;
+ for (uint32_t j = 0; j < kCitySize; ++j)
+ {
+ horizontalRoad.emplace_back((double)i, (double)j);
+ verticalRoad.emplace_back((double)j, (double)i);
+ }
+ geometry->AddRoad(i, horizontalRoad);
+ geometry->AddRoad(i + kCitySize, verticalRoad);
+ }
+
+ IndexGraph graph(move(geometry));
+
+ vector<Joint> joints;
+ for (uint32_t i = 0; i < kCitySize; ++i)
+ {
+ for (uint32_t j = 0; j < kCitySize; ++j)
+ {
+ joints.emplace_back(MakeJoint({{i, j}, {j + kCitySize, i}}));
+ }
+ }
+ graph.Export(joints);
+
+ for (uint32_t startY = 0; startY < kCitySize; ++startY)
+ {
+ for (uint32_t startX = 0; startX < kCitySize; ++startX)
+ {
+ for (uint32_t finishY = 0; finishY < kCitySize; ++finishY)
+ {
+ for (uint32_t finishX = 0; finishX < kCitySize; ++finishX)
+ {
+ CheckRoute(graph, {startX, startY}, {finishX, finishY},
+ AbsDelta(startX, finishX) + AbsDelta(startY, finishY) + 1);
+ }
+ }
+ }
+ }
+}
+} // namespace routing_test
diff --git a/routing/routing_tests/routing_tests.pro b/routing/routing_tests/routing_tests.pro
index 0f55d2ccba..100399f067 100644
--- a/routing/routing_tests/routing_tests.pro
+++ b/routing/routing_tests/routing_tests.pro
@@ -27,6 +27,7 @@ SOURCES += \
async_router_test.cpp \
cross_routing_tests.cpp \
followed_polyline_test.cpp \
+ index_graph_test.cpp \
nearest_edge_finder_tests.cpp \
online_cross_fetcher_test.cpp \
osrm_router_test.cpp \