diff --git a/map/map_tests/map_tests.pro b/map/map_tests/map_tests.pro index 82bd924b51..a006d56c3d 100644 --- a/map/map_tests/map_tests.pro +++ b/map/map_tests/map_tests.pro @@ -7,7 +7,7 @@ TEMPLATE = app ROOT_DIR = ../.. DEPENDENCIES = map gui routing search storage graphics indexer platform anim geometry coding base \ - freetype fribidi expat protobuf tomcrypt jansson zlib + freetype fribidi expat protobuf tomcrypt jansson zlib osrm include($$ROOT_DIR/common.pri) diff --git a/map_server/map_server.pro b/map_server/map_server.pro index 3d3e36c2ae..edd7cc9f0a 100644 --- a/map_server/map_server.pro +++ b/map_server/map_server.pro @@ -6,7 +6,7 @@ TEMPLATE = app DEFINES += QJSONRPC_BUILD DEPENDENCIES = map gui routing search storage indexer graphics platform anim geometry coding base \ - bzip2 freetype expat fribidi tomcrypt jansson protobuf qjsonrpc gflags zlib + osrm bzip2 freetype expat fribidi tomcrypt jansson protobuf qjsonrpc gflags zlib ROOT_DIR = .. include($$ROOT_DIR/common.pri) diff --git a/qt/qt.pro b/qt/qt.pro index 4a2b608223..e01a4b1a00 100644 --- a/qt/qt.pro +++ b/qt/qt.pro @@ -1,7 +1,7 @@ # Main application in qt. ROOT_DIR = .. DEPENDENCIES = map gui routing search storage indexer graphics platform anim geometry coding base \ - bzip2 freetype expat fribidi tomcrypt jansson protobuf zlib + bzip2 freetype expat fribidi tomcrypt jansson protobuf zlib osrm include($$ROOT_DIR/common.pri) diff --git a/routing/osrm_data_facade.hpp b/routing/osrm_data_facade.hpp new file mode 100644 index 0000000000..ef19d1a0ce --- /dev/null +++ b/routing/osrm_data_facade.hpp @@ -0,0 +1,372 @@ +#pragma once + +#include "../std/string.hpp" +#include "../std/vector.hpp" +#include "../std/iostream.hpp" + +#include "../../../succinct/elias_fano.hpp" +#include "../../../succinct/gamma_vector.hpp" +#include "../../../succinct/bit_vector.hpp" +#include "../../../succinct/mapper.hpp" + +//#include "../3party/osrm/osrm-backend/DataStructures/RangeTable.h" +#include "../3party/osrm/osrm-backend/DataStructures/StaticRTree.h" +#include "../3party/osrm/osrm-backend/DataStructures/EdgeBasedNode.h" +#include "../3party/osrm/osrm-backend/Server/DataStructures/BaseDataFacade.h" +#include "../3party/osrm/osrm-backend/DataStructures/OriginalEdgeData.h" + +namespace routing +{ + +#define NOT_IMPLEMENTED { assert(false); } + +template class OsrmDataFacade : public BaseDataFacade +{ + typedef BaseDataFacade super; + typedef typename super::RTreeLeaf RTreeLeaf; + + boost::iostreams::mapped_file_source m_edgeDataSource; + succinct::gamma_vector m_edgeData; + boost::iostreams::mapped_file_source m_edgeIdSource; + succinct::gamma_vector m_edgeId; + boost::iostreams::mapped_file_source m_shortcutsSource; + succinct::bit_vector m_shortcuts; + + succinct::elias_fano m_fanoMatrix; + boost::iostreams::mapped_file_source m_fanoSource; + + unsigned m_numberOfNodes; + + // --- OSRM --- + std::shared_ptr< vector > m_coordinateList; + vector m_viaNodeList; + vector m_turnInstructionList; + vector m_nameIDList; + vector m_egdeIsCompressed; + vector m_geometryIndices; + vector m_geometryList; + //RangeTable<16, false> m_name_table; + //vector m_names_char_list; + StaticRTree > *m_staticRTree; + +public: + template + void loadFromFile(T & v, string const & fileName) + { + std::ifstream stream; + stream.open(fileName); + v.load(stream); + stream.close(); + } + + OsrmDataFacade(string const & fileName) + : m_staticRTree(0) + { + m_edgeDataSource.open(fileName + ".edgedata"); + succinct::mapper::map(m_edgeData, m_edgeDataSource); + + m_edgeIdSource.open(fileName + ".edgeid"); + succinct::mapper::map(m_edgeId, m_edgeIdSource); + + m_shortcutsSource.open(fileName + ".shortcuts"); + succinct::mapper::map(m_shortcuts, m_shortcutsSource); + + m_fanoSource.open(fileName + ".matrix"); + succinct::mapper::map(m_fanoMatrix, m_fanoSource); + + + //std::cout << m_fanoMatrix.size() << std::endl; + m_numberOfNodes = (unsigned)sqrt(m_fanoMatrix.size() / 2) + 1; + + // --- OSRM --- + // load data + LoadNodeAndEdgeInformation(fileName + ".nodes", fileName + ".edges"); + LoadGeometries(fileName + ".geometry"); + //LoadStreetNames(fileName + ".names"); + + boost::filesystem::path ram_index_path(fileName + ".ramIndex"); + boost::filesystem::path file_index_path(fileName + ".fileIndex"); + m_staticRTree = new StaticRTree >(ram_index_path, file_index_path, m_coordinateList); + } + + ~OsrmDataFacade() + { + delete m_staticRTree; + + m_edgeDataSource.close(); + m_edgeIdSource.close(); + m_shortcutsSource.close(); + m_fanoSource.close(); + } + + + unsigned GetNumberOfNodes() const + { + return m_numberOfNodes; + } + + unsigned GetNumberOfEdges() const + { + return m_edgeData.size(); + } + + unsigned GetOutDegree(const NodeID n) const + { + return EndEdges(n) - BeginEdges(n); + } + + NodeID GetTarget(const EdgeID e) const + { + return (m_fanoMatrix.select(e) / 2) % GetNumberOfNodes(); + } + + //! TODO: Remove static variable + EdgeDataT & GetEdgeData(const EdgeID e) + { + static EdgeDataT res; + + uint64_t data = m_edgeData[e]; + + res.id = m_edgeId[e]; + res.backward = data & 0x1; + data >>= 1; + res.forward = data & 0x1; + data >>= 1; + res.distance = data; + res.shortcut = m_shortcuts[e]; + + return res; + } + + //! TODO: Remove static variable + EdgeDataT const & GetEdgeData(const EdgeID e) const + { + static EdgeDataT res; + + uint64_t data = m_edgeData[e]; + + res.id = m_edgeId[e]; + res.backward = data & 0x1; + data >>= 1; + res.forward = data & 0x1; + data >>= 1; + res.distance = data; + res.shortcut = m_shortcuts[e]; + + return res; + } + + EdgeID BeginEdges(const NodeID n) const + { + return n == 0 ? 0 : m_fanoMatrix.rank(2 * n * (uint64_t)GetNumberOfNodes()); + } + + EdgeID EndEdges(const NodeID n) const + { + uint64_t const idx = 2 * (n + 1) * (uint64_t)GetNumberOfNodes(); + return m_fanoMatrix.rank(std::min(idx, m_fanoMatrix.size())); + } + + EdgeRange GetAdjacentEdgeRange(const NodeID node) const + { + return osrm::irange(BeginEdges(node), EndEdges(node)); + } + + // searches for a specific edge + EdgeID FindEdge(const NodeID from, const NodeID to) const + { + EdgeID smallest_edge = SPECIAL_EDGEID; + EdgeWeight smallest_weight = INVALID_EDGE_WEIGHT; + for (auto edge : GetAdjacentEdgeRange(from)) + { + const NodeID target = GetTarget(edge); + const EdgeWeight weight = GetEdgeData(edge).distance; + if (target == to && weight < smallest_weight) + { + smallest_edge = edge; + smallest_weight = weight; + } + } + return smallest_edge; + } + + EdgeID FindEdgeInEitherDirection(const NodeID from, const NodeID to) const + { + NOT_IMPLEMENTED; + return (EdgeID)0; + } + + EdgeID FindEdgeIndicateIfReverse(const NodeID from, const NodeID to, bool &result) const + { + NOT_IMPLEMENTED; + return (EdgeID)0; + } + + // node and edge information access + FixedPointCoordinate GetCoordinateOfNode(const unsigned id) const + { + return m_coordinateList->at(id); + } + + bool EdgeIsCompressed(const unsigned id) const + { + assert(id < m_egdeIsCompressed.size()); + return m_egdeIsCompressed[id]; + } + + unsigned GetGeometryIndexForEdgeID(const unsigned id) const + { + return m_viaNodeList.at(id); + } + + void GetUncompressedGeometry(const unsigned id, std::vector &result_nodes) const + { + const unsigned begin = m_geometryIndices.at(id); + const unsigned end = m_geometryIndices.at(id + 1); + + result_nodes.clear(); + result_nodes.insert(result_nodes.begin(), m_geometryList.begin() + begin, m_geometryList.begin() + end); + } + + TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const + { + return m_turnInstructionList[id]; + } + + bool LocateClosestEndPointForCoordinate(const FixedPointCoordinate &input_coordinate, + FixedPointCoordinate &result, + const unsigned zoom_level = 18) + { + return m_staticRTree->LocateClosestEndPointForCoordinate(input_coordinate, result, zoom_level); + } + + bool FindPhantomNodeForCoordinate(const FixedPointCoordinate &input_coordinate, + PhantomNode &resulting_phantom_node, + const unsigned zoom_level) + { + + return m_staticRTree->FindPhantomNodeForCoordinate(input_coordinate, resulting_phantom_node, zoom_level); + } + + bool IncrementalFindPhantomNodeForCoordinate(const FixedPointCoordinate &input_coordinate, + std::vector &resulting_phantom_node_vector, + const unsigned zoom_level, + const unsigned number_of_results) + { + NOT_IMPLEMENTED; + return false; + } + + unsigned GetCheckSum() const + { + NOT_IMPLEMENTED; + return 0; + } + + unsigned GetNameIndexFromEdgeID(const unsigned id) const + { + return m_nameIDList.at(id); + } + + void GetName(const unsigned name_id, std::string &result) const + { + NOT_IMPLEMENTED; + } + + std::string GetEscapedNameForNameID(const unsigned name_id) const + { + std::string temporary_string; + GetName(name_id, temporary_string); + return EscapeJSONString(temporary_string); + } + + std::string GetTimestamp() const + { + return ""; + } + + + // ------------------- OSRM ------------------------ + void LoadNodeAndEdgeInformation(string const & nodesFile, string const & edgesFile) + { + std::ifstream nodes_input_stream(nodesFile, std::ios::binary); + + NodeInfo current_node; + unsigned number_of_coordinates = 0; + nodes_input_stream.read((char *)&number_of_coordinates, sizeof(unsigned)); + m_coordinateList = std::make_shared< std::vector >(number_of_coordinates); + for (unsigned i = 0; i < number_of_coordinates; ++i) + { + nodes_input_stream.read((char *)¤t_node, sizeof(NodeInfo)); + m_coordinateList->at(i) = FixedPointCoordinate(current_node.lat, current_node.lon); + } + nodes_input_stream.close(); + + std::ifstream edges_input_stream(edgesFile, std::ios::binary); + unsigned number_of_edges = 0; + edges_input_stream.read((char *)&number_of_edges, sizeof(unsigned)); + m_viaNodeList.resize(number_of_edges); + m_nameIDList.resize(number_of_edges); + m_turnInstructionList.resize(number_of_edges); + m_egdeIsCompressed.resize(number_of_edges); + + unsigned compressed = 0; + + OriginalEdgeData current_edge_data; + for (unsigned i = 0; i < number_of_edges; ++i) + { + edges_input_stream.read((char *)&(current_edge_data), sizeof(OriginalEdgeData)); + m_viaNodeList[i] = current_edge_data.via_node; + m_nameIDList[i] = current_edge_data.name_id; + m_turnInstructionList[i] = current_edge_data.turn_instruction; + m_egdeIsCompressed[i] = current_edge_data.compressed_geometry; + if (m_egdeIsCompressed[i]) + { + ++compressed; + } + } + + edges_input_stream.close(); + } + + void LoadGeometries(string const & geometry_file) + { + std::ifstream geometry_stream(geometry_file, std::ios::binary); + unsigned number_of_indices = 0; + unsigned number_of_compressed_geometries = 0; + + geometry_stream.read((char *)&number_of_indices, sizeof(unsigned)); + + m_geometryIndices.resize(number_of_indices); + if (number_of_indices > 0) + geometry_stream.read((char *)&(m_geometryIndices[0]), number_of_indices * sizeof(unsigned)); + + geometry_stream.read((char *)&number_of_compressed_geometries, sizeof(unsigned)); + + BOOST_ASSERT(m_geometryIndices.back() == number_of_compressed_geometries); + m_geometryList.resize(number_of_compressed_geometries); + + if (number_of_compressed_geometries > 0) + geometry_stream.read((char *)&(m_geometryList[0]), number_of_compressed_geometries * sizeof(unsigned)); + + geometry_stream.close(); + } + + /*void LoadStreetNames(string const & namesFile) + { + std::ifstream name_stream(namesFile, std::ios::binary); + + name_stream >> m_name_table; + + unsigned number_of_chars = 0; + name_stream.read((char *)&number_of_chars, sizeof(unsigned)); + ASSERT(0 != number_of_chars, "name file broken"); + m_names_char_list.resize(number_of_chars + 1); //+1 gives sentinel element + name_stream.read((char *)&m_names_char_list[0], number_of_chars * sizeof(char)); + name_stream.close(); + }*/ + + +}; + +} diff --git a/routing/osrm_router.cpp b/routing/osrm_router.cpp new file mode 100644 index 0000000000..8bb4e39e8d --- /dev/null +++ b/routing/osrm_router.cpp @@ -0,0 +1,89 @@ +#include "osrm_router.hpp" +#include "osrm_data_facade.hpp" +#include "route.hpp" + +#include "../indexer/mercator.hpp" + +#include "../3party/osrm/osrm-backend/DataStructures/SearchEngine.h" +#include "../3party/osrm/osrm-backend/DataStructures/QueryEdge.h" +#include "../3party/osrm/osrm-backend/Descriptors/DescriptionFactory.h" + +namespace routing +{ + +#define FACADE_READ_ZOOM_LEVEL 13 + +string OsrmRouter::GetName() const +{ + return "mapsme"; +} + +void OsrmRouter::SetFinalPoint(m2::PointD const & finalPt) +{ + m_finalPt = finalPt; +} + +void OsrmRouter::CalculateRoute(m2::PointD const & startingPt, ReadyCallback const & callback) +{ + typedef OsrmDataFacade DataFacadeT; + DataFacadeT facade("/Users/deniskoronchik/Documents/develop/omim-maps/Belarus.osrm"); + SearchEngine engine(&facade); + + RawRouteData rawRoute; + PhantomNodes nodes; + FixedPointCoordinate startPoint((int)(MercatorBounds::YToLat(startingPt.y) * COORDINATE_PRECISION), + (int)(MercatorBounds::XToLon(startingPt.x) * COORDINATE_PRECISION)); + facade.FindPhantomNodeForCoordinate(startPoint, nodes.source_phantom, 18); + //FACADE_READ_ZOOM_LEVEL); + FixedPointCoordinate endPoint((int)(MercatorBounds::YToLat(m_finalPt.y) * COORDINATE_PRECISION), + (int)(MercatorBounds::XToLon(m_finalPt.x) * COORDINATE_PRECISION)); + facade.FindPhantomNodeForCoordinate(endPoint, nodes.target_phantom, 18); + //FACADE_READ_ZOOM_LEVEL); + + rawRoute.raw_via_node_coordinates = {startPoint, endPoint}; + rawRoute.segment_end_coordinates.push_back(nodes); + + engine.shortest_path({nodes}, {}, rawRoute); + + Route route(GetName()); + if (INVALID_EDGE_WEIGHT == rawRoute.shortest_path_length + || rawRoute.segment_end_coordinates.empty() + || rawRoute.source_traversed_in_reverse.empty()) + { + callback(route); + return; + } + + // restore route + DescriptionFactory factory; + factory.SetStartSegment(rawRoute.segment_end_coordinates.front().source_phantom, + rawRoute.source_traversed_in_reverse.front()); + + // for each unpacked segment add the leg to the description + for (auto const i : osrm::irange(0, rawRoute.unpacked_path_segments.size())) + { + unsigned added_element_count = 0; + // Get all the coordinates for the computed route + FixedPointCoordinate current_coordinate; + for (PathData const & path_data : rawRoute.unpacked_path_segments[i]) + { + current_coordinate = facade.GetCoordinateOfNode(path_data.node); + factory.AppendSegment(current_coordinate, path_data); + ++added_element_count; + } + factory.SetEndSegment(rawRoute.segment_end_coordinates[i].target_phantom, rawRoute.target_traversed_in_reverse[i]); + ++added_element_count; + ASSERT_EQUAL((rawRoute.unpacked_path_segments[i].size() + 1), added_element_count, ()); + } + factory.Run(&facade, FACADE_READ_ZOOM_LEVEL); + factory.BuildRouteSummary(factory.entireLength, rawRoute.shortest_path_length); + + vector points; + for (auto const si : factory.path_description) + points.push_back(m2::PointD(MercatorBounds::LonToX(si.location.lon / COORDINATE_PRECISION), MercatorBounds::LatToY(si.location.lat / COORDINATE_PRECISION))); // @todo: possible need convert to mercator + route.SetGeometry(points.begin(), points.end()); + + callback(route); +} + +} diff --git a/routing/osrm_router.hpp b/routing/osrm_router.hpp new file mode 100644 index 0000000000..d436a5603e --- /dev/null +++ b/routing/osrm_router.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "router.hpp" + +namespace routing +{ + +class OsrmRouter : public IRouter +{ + m2::PointD m_finalPt; + +public: + virtual string GetName() const; + virtual void SetFinalPoint(m2::PointD const & finalPt); + virtual void CalculateRoute(m2::PointD const & startingPt, ReadyCallback const & callback); +}; + + +} diff --git a/routing/routing.pro b/routing/routing.pro index e2fbf03c7a..995b084f09 100644 --- a/routing/routing.pro +++ b/routing/routing.pro @@ -7,13 +7,15 @@ ROOT_DIR = .. include($$ROOT_DIR/common.pri) -INCLUDEPATH += $$ROOT_DIR/3party/jansson/src +INCLUDEPATH += $$ROOT_DIR/3party/jansson/src \ + $$ROOT_DIR/3party/osrm/osrm-backend/Include SOURCES += \ route.cpp \ routing_engine.cpp \ road_graph.cpp \ helicopter_router.cpp \ + osrm_router.cpp \ osrm_online_router.cpp \ road_graph_router.cpp \ dijkstra_router.cpp \ @@ -26,7 +28,9 @@ HEADERS += \ router.hpp \ road_graph.hpp \ helicopter_router.hpp \ + osrm_router.hpp \ osrm_online_router.hpp \ + osrm_data_facade.hpp \ road_graph_router.hpp \ dijkstra_router.hpp \ features_road_graph.hpp \ diff --git a/routing/routing_tests/osrm_router_test.cpp b/routing/routing_tests/osrm_router_test.cpp new file mode 100644 index 0000000000..e531639576 --- /dev/null +++ b/routing/routing_tests/osrm_router_test.cpp @@ -0,0 +1,22 @@ +#include "../../testing/testing.hpp" +#include "../osrm_router.hpp" +#include "../../indexer/mercator.hpp" + +using namespace routing; + +namespace +{ + +void Callback(Route const & r) +{ + +} + +} + +UNIT_TEST(OsrmRouter_Test) +{ + OsrmRouter router; + router.SetFinalPoint(m2::PointD(MercatorBounds::LonToX(27.54334), MercatorBounds::LatToY(53.899338))); // офис Немига + router.CalculateRoute(m2::PointD(MercatorBounds::LonToX(27.6704144), MercatorBounds::LatToY(53.9456243)), Callback); // Городецкая +} diff --git a/routing/routing_tests/routing_tests.pro b/routing/routing_tests/routing_tests.pro index 4997550d70..dd1f7ce3e4 100644 --- a/routing/routing_tests/routing_tests.pro +++ b/routing/routing_tests/routing_tests.pro @@ -6,7 +6,7 @@ CONFIG -= app_bundle TEMPLATE = app ROOT_DIR = ../.. -DEPENDENCIES = routing indexer platform geometry coding base protobuf tomcrypt +DEPENDENCIES = routing indexer platform geometry coding base osrm protobuf tomcrypt macx-*: LIBS *= "-framework Foundation" "-framework IOKit" @@ -21,6 +21,7 @@ SOURCES += \ road_graph_builder.cpp \ road_graph_builder_test.cpp \ dijkstra_router_test.cpp \ + osrm_router_test.cpp \ vehicle_model_test.cpp \ HEADERS += \