Compare commits
179 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e1e76d04d1 | |||
| 451bbe3021 | |||
| 88a76aaecf | |||
| 87428f8fc9 | |||
| 6c9ddde682 | |||
| b4b148e3dc | |||
| 4a84ca3197 | |||
| df182ebc38 | |||
| c86864976c | |||
| c3dd77d32b | |||
| 2357d1c095 | |||
| 7cd70679f1 | |||
| 08c1ac0daf | |||
| 48237b30ea | |||
| cac1298864 | |||
| 83e6679d61 | |||
| f3ea86b611 | |||
| f3de53c363 | |||
| 19434d42b4 | |||
| ea24ea64f2 | |||
| 39f5cf1c22 | |||
| 91384ecd8c | |||
| 5c28eabbe0 | |||
| 42dd45b29b | |||
| 6f4095f23f | |||
| 9c6c02d85b | |||
| 47afe60e5f | |||
| 0471ace55d | |||
| 49e2a4ec07 | |||
| 991c5f5d5d | |||
| 87c77e7d77 | |||
| 2cf120fc49 | |||
| d4eaea1e7d | |||
| 51a13675d4 | |||
| af4cb96aa5 | |||
| 439a7ba661 | |||
| a0ff0b1258 | |||
| 1cc9a2f4a1 | |||
| a3cf7f09e8 | |||
| 831a536224 | |||
| 7df00683e1 | |||
| 0c0803c77b | |||
| 7c5d56afcb | |||
| 892b9dff4a | |||
| 04a8c1833c | |||
| c217ff815c | |||
| 893c9f7326 | |||
| 84fa7ae353 | |||
| bb3256573b | |||
| 96d9ef3951 | |||
| 11572702e9 | |||
| 6d16bb5b25 | |||
| 89eb2a3778 | |||
| 87408cfd73 | |||
| dd4066c46e | |||
| 3f559211a4 | |||
| 5892c6ea09 | |||
| dea0063a56 | |||
| eec270968e | |||
| 056a7422e0 | |||
| 3d3fea768c | |||
| 1a6c16fea1 | |||
| 3c56385ba8 | |||
| 1947671ae3 | |||
| 3189f24a17 | |||
| dd5c730a01 | |||
| da1dbcfb34 | |||
| 8f5091f711 | |||
| 0da7302d62 | |||
| f0b40c1948 | |||
| ecefb4a20e | |||
| 9b1861e017 | |||
| 4fb041391f | |||
| 64cc7d8aac | |||
| 556223d43b | |||
| 0b1e82105e | |||
| c9ad8736b0 | |||
| cded2501a5 | |||
| 4d94ec2792 | |||
| 7970bc4ad6 | |||
| f84ce976ea | |||
| ce9a4666f3 | |||
| ff55b4e001 | |||
| 8040af329b | |||
| b8c059b5c2 | |||
| 4e2e2059b1 | |||
| 922599a8ca | |||
| 5bc0595591 | |||
| 0f600c9262 | |||
| 9338d418c8 | |||
| 4d56b8ff0d | |||
| 92b4f14db6 | |||
| a1f1da4c16 | |||
| c337d55d45 | |||
| be5f231641 | |||
| c19fae4a43 | |||
| 0831e71438 | |||
| c44a370dd3 | |||
| 9f5e79501e | |||
| cf5e722578 | |||
| f12bb2fa2f | |||
| 28d878ed23 | |||
| 6db16e950a | |||
| 862f8d6ff3 | |||
| 8c67dd4504 | |||
| 23d79bde54 | |||
| 410d561b4c | |||
| 8ab38b126c | |||
| c13f1aa89d | |||
| b4bf0ec5fe | |||
| f7b7dbf51a | |||
| 41c9f94e5f | |||
| 77adbca138 | |||
| f3e06b41dd | |||
| ee4b8618f8 | |||
| 58973a4fd1 | |||
| 4b8017d412 | |||
| 64d4d58a0d | |||
| 5d83fa1e4c | |||
| 7a6aa6f1a7 | |||
| e5d964492a | |||
| bb2baa82aa | |||
| 0b5875a412 | |||
| 9a2b8cb16d | |||
| 5fb6db7a9a | |||
| c1647c99c8 | |||
| 39d6c33b32 | |||
| 9b75f618e8 | |||
| 225049ffa7 | |||
| 2df832a95e | |||
| 75912b3662 | |||
| c9a19c954e | |||
| 9a19086926 | |||
| 90e8a3e1dc | |||
| 0270ee59b4 | |||
| 5728af4a2a | |||
| 6cf19ac4cf | |||
| 3f81f6b441 | |||
| 669aa9e672 | |||
| 1d3afd5c38 | |||
| c53a448589 | |||
| cb82376083 | |||
| 9d10490613 | |||
| 1cc7d3ddfb | |||
| aad3d8aa1e | |||
| 9258791811 | |||
| 19d5912a93 | |||
| a4770feac4 | |||
| c8c6f8c2e7 | |||
| d7f9c31d0b | |||
| 44b802c053 | |||
| 99f18051f6 | |||
| 99f94969aa | |||
| bc6d5bbde2 | |||
| 6aa5ab5904 | |||
| 0c64503218 | |||
| e15ba88c0b | |||
| 2f9d1d3db2 | |||
| 42e6e974ac | |||
| 5a97e7a7ce | |||
| 8c54794d5a | |||
| e377a19c10 | |||
| 0fb6e9bf3f | |||
| d6a8690425 | |||
| cc65446785 | |||
| 487df70eb3 | |||
| aa79c41804 | |||
| 7faadb1233 | |||
| 5c8a895471 | |||
| e932a8253a | |||
| 84097964b7 | |||
| c2a35b35ad | |||
| f6612e2afa | |||
| 09378f28fd | |||
| 01ddfbcba3 | |||
| 0468d7a7c5 | |||
| 39bc0fd330 | |||
| 53dbb1eac2 | |||
| f5bc843fe6 |
@@ -40,6 +40,7 @@ Thumbs.db
|
||||
# build related files #
|
||||
#######################
|
||||
/build/
|
||||
/example/build/
|
||||
/cmake/postinst
|
||||
|
||||
# Eclipse related files #
|
||||
|
||||
+14
-7
@@ -45,7 +45,7 @@ add_custom_target(FingerPrintConfigure ALL ${CMAKE_COMMAND}
|
||||
COMMENT "Configuring revision fingerprint"
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(tests DEPENDS engine-tests extractor-tests util-tests)
|
||||
add_custom_target(tests DEPENDS engine-tests extractor-tests util-tests server-tests)
|
||||
add_custom_target(benchmarks DEPENDS rtree-bench)
|
||||
|
||||
set(BOOST_COMPONENTS date_time filesystem iostreams program_options regex system thread unit_test_framework)
|
||||
@@ -55,7 +55,7 @@ configure_file(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/util/version.hpp
|
||||
)
|
||||
file(GLOB UtilGlob src/util/*.cpp)
|
||||
file(GLOB ExtractorGlob src/extractor/*.cpp)
|
||||
file(GLOB ExtractorGlob src/extractor/*.cpp src/extractor/*/*.cpp)
|
||||
file(GLOB ContractorGlob src/contractor/*.cpp)
|
||||
file(GLOB StorageGlob src/storage/*.cpp)
|
||||
file(GLOB ServerGlob src/server/*.cpp src/server/**/*.cpp)
|
||||
@@ -63,6 +63,7 @@ file(GLOB EngineGlob src/engine/*.cpp src/engine/**/*.cpp)
|
||||
file(GLOB ExtractorTestsGlob unit_tests/extractor/*.cpp)
|
||||
file(GLOB EngineTestsGlob unit_tests/engine/*.cpp)
|
||||
file(GLOB UtilTestsGlob unit_tests/util/*.cpp)
|
||||
file(GLOB ServerTestsGlob unit_tests/server/*.cpp)
|
||||
file(GLOB IOTestsGlob unit_tests/io/*.cpp)
|
||||
|
||||
add_library(UTIL OBJECT ${UtilGlob})
|
||||
@@ -79,7 +80,7 @@ add_executable(osrm-extract src/tools/extract.cpp)
|
||||
add_executable(osrm-contract src/tools/contract.cpp)
|
||||
add_executable(osrm-routed src/tools/routed.cpp $<TARGET_OBJECTS:SERVER> $<TARGET_OBJECTS:UTIL>)
|
||||
add_executable(osrm-datastore src/tools/store.cpp $<TARGET_OBJECTS:UTIL>)
|
||||
add_library(osrm src/osrm/osrm.cpp $<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:UTIL>)
|
||||
add_library(osrm src/osrm/osrm.cpp $<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:UTIL> $<TARGET_OBJECTS:STORAGE>)
|
||||
add_library(osrm_extract $<TARGET_OBJECTS:EXTRACTOR> $<TARGET_OBJECTS:UTIL>)
|
||||
add_library(osrm_contract $<TARGET_OBJECTS:CONTRACTOR> $<TARGET_OBJECTS:UTIL>)
|
||||
add_library(osrm_store $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:UTIL>)
|
||||
@@ -88,10 +89,12 @@ add_library(osrm_store $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:UTIL>)
|
||||
add_executable(engine-tests EXCLUDE_FROM_ALL unit_tests/engine_tests.cpp ${EngineTestsGlob} $<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:UTIL>)
|
||||
add_executable(extractor-tests EXCLUDE_FROM_ALL unit_tests/extractor_tests.cpp ${ExtractorTestsGlob} $<TARGET_OBJECTS:EXTRACTOR> $<TARGET_OBJECTS:UTIL>)
|
||||
add_executable(util-tests EXCLUDE_FROM_ALL unit_tests/util_tests.cpp ${UtilTestsGlob} $<TARGET_OBJECTS:UTIL>)
|
||||
add_executable(server-tests EXCLUDE_FROM_ALL unit_tests/server_tests.cpp ${ServerTestsGlob} $<TARGET_OBJECTS:UTIL> $<TARGET_OBJECTS:SERVER>)
|
||||
|
||||
# Benchmarks
|
||||
add_executable(rtree-bench EXCLUDE_FROM_ALL src/benchmarks/static_rtree.cpp $<TARGET_OBJECTS:UTIL>)
|
||||
|
||||
target_include_directories(engine-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/unit_tests)
|
||||
target_include_directories(util-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/unit_tests)
|
||||
target_include_directories(rtree-bench PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/unit_tests)
|
||||
|
||||
@@ -327,6 +330,7 @@ target_link_libraries(osrm_extract ${EXTRACTOR_LIBRARIES})
|
||||
target_link_libraries(osrm_store ${STORAGE_LIBRARIES})
|
||||
# Tests
|
||||
target_link_libraries(engine-tests ${ENGINE_LIBRARIES})
|
||||
target_link_libraries(server-tests osrm ${Boost_LIBRARIES})
|
||||
target_link_libraries(extractor-tests ${EXTRACTOR_LIBRARIES})
|
||||
target_link_libraries(rtree-bench ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${TBB_LIBRARIES})
|
||||
target_link_libraries(util-tests ${UTIL_LIBRARIES})
|
||||
@@ -373,16 +377,19 @@ set_property(TARGET osrm-routed PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
|
||||
file(GLOB VariantGlob third_party/variant/*.hpp)
|
||||
file(GLOB LibraryGlob include/osrm/*.hpp)
|
||||
set(EngineHeader include/engine/engine.hpp include/engine/engine_config.hpp include/engine/route_parameters.hpp)
|
||||
set(UtilHeader include/util/coordinate.hpp include/util/json_container.hpp)
|
||||
set(ExtractorHeader include/extractor/extractor.hpp include/extractor/extractor_config.hpp)
|
||||
file(GLOB ParametersGlob include/engine/api/*_parameters.hpp)
|
||||
set(EngineHeader include/engine/status.hpp include/engine/engine_config.hpp include/engine/hint.hpp include/engine/bearing.hpp include/engine/phantom_node.hpp)
|
||||
set(UtilHeader include/util/coordinate.hpp include/util/json_container.hpp include/util/typedefs.hpp include/util/strong_typedef.hpp)
|
||||
set(ExtractorHeader include/extractor/extractor.hpp include/extractor/extractor_config.hpp include/extractor/travel_mode.hpp)
|
||||
set(ContractorHeader include/contractor/contractor.hpp include/contractor/contractor_config.hpp)
|
||||
#set(StorageHeader include/storage/storage.hpp include/storage/storage_config.hpp)
|
||||
set(StorageHeader include/storage/storage.hpp include/storage/storage_config.hpp)
|
||||
install(FILES ${EngineHeader} DESTINATION include/osrm/engine)
|
||||
install(FILES ${UtilHeader} DESTINATION include/osrm/util)
|
||||
install(FILES ${StorageHeader} DESTINATION include/osrm/storage)
|
||||
install(FILES ${ExtractorHeader} DESTINATION include/osrm/extractor)
|
||||
install(FILES ${ContractorHeader} DESTINATION include/osrm/contractor)
|
||||
install(FILES ${LibraryGlob} DESTINATION include/osrm)
|
||||
install(FILES ${ParametersGlob} DESTINATION include/osrm/engine/api)
|
||||
install(FILES ${VariantGlob} DESTINATION include/variant)
|
||||
install(TARGETS osrm-extract DESTINATION bin)
|
||||
install(TARGETS osrm-contract DESTINATION bin)
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
includedir=${prefix}/include
|
||||
includedir=${prefix}/include ${prefix}/include/osrm
|
||||
libdir=${prefix}/lib
|
||||
|
||||
Name: libOSRM
|
||||
|
||||
@@ -19,7 +19,7 @@ find_path(LibOSRM_INCLUDE_DIR osrm/osrm.hpp
|
||||
/opt/local
|
||||
/opt)
|
||||
|
||||
set(LibOSRM_INCLUDE_DIRS ${LibOSRM_INCLUDE_DIR})
|
||||
set(LibOSRM_INCLUDE_DIRS ${LibOSRM_INCLUDE_DIR} ${LibOSRM_INCLUDE_DIR}/osrm)
|
||||
|
||||
find_library(TEST_LibOSRM_STATIC_LIBRARY Names osrm.lib libosrm.a
|
||||
PATH_SUFFIXES osrm lib/osrm lib
|
||||
|
||||
+60
-36
@@ -1,61 +1,85 @@
|
||||
#include "osrm/json_container.hpp"
|
||||
#include "osrm/engine_config.hpp"
|
||||
#include "osrm/route_parameters.hpp"
|
||||
#include "osrm/table_parameters.hpp"
|
||||
#include "osrm/nearest_parameters.hpp"
|
||||
#include "osrm/trip_parameters.hpp"
|
||||
#include "osrm/match_parameters.hpp"
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
#include "osrm/engine_config.hpp"
|
||||
#include "osrm/json_container.hpp"
|
||||
|
||||
#include "osrm/status.hpp"
|
||||
#include "osrm/osrm.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
int main(int argc, const char *argv[]) try
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
std::cerr << "Error: Not enough arguments." << std::endl
|
||||
<< "Run " << argv[0] << " data.osrm" << std::endl;
|
||||
std::cerr << "Usage: " << argv[0] << " data.osrm\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
osrm::EngineConfig engine_config;
|
||||
std::string base_path(argv[1]);
|
||||
engine_config.server_paths["ramindex"] = base_path + ".ramIndex";
|
||||
engine_config.server_paths["fileindex"] = base_path + ".fileIndex";
|
||||
engine_config.server_paths["hsgrdata"] = base_path + ".hsgr";
|
||||
engine_config.server_paths["nodesdata"] = base_path + ".nodes";
|
||||
engine_config.server_paths["edgesdata"] = base_path + ".edges";
|
||||
engine_config.server_paths["coredata"] = base_path + ".core";
|
||||
engine_config.server_paths["geometries"] = base_path + ".geometry";
|
||||
engine_config.server_paths["timestamp"] = base_path + ".timestamp";
|
||||
engine_config.server_paths["namesdata"] = base_path + ".names";
|
||||
engine_config.use_shared_memory = false;
|
||||
using namespace osrm;
|
||||
|
||||
osrm::OSRM routing_machine(engine_config);
|
||||
// Configure based on a .osrm base path, and no datasets in shared mem from osrm-datastore
|
||||
EngineConfig config;
|
||||
config.storage_config = {argv[1]};
|
||||
config.use_shared_memory = false;
|
||||
|
||||
osrm::RouteParameters route_parameters;
|
||||
// route is in Monaco
|
||||
route_parameters.service = "viaroute";
|
||||
route_parameters.AddCoordinate(43.731142, 7.419758);
|
||||
route_parameters.AddCoordinate(43.736825, 7.419505);
|
||||
// Routing machine with several services (such as Route, Table, Nearest, Trip, Match)
|
||||
OSRM osrm{config};
|
||||
|
||||
osrm::json::Object json_result;
|
||||
const int result_code = routing_machine.RunQuery(route_parameters, json_result);
|
||||
std::cout << "result code: " << result_code << std::endl;
|
||||
// 2xx code
|
||||
if (result_code / 100 == 2)
|
||||
// The following shows how to use the Route service; configure this service
|
||||
RouteParameters params;
|
||||
|
||||
// Route in monaco
|
||||
params.coordinates.push_back({util::FloatLongitude(7.419758), util::FloatLatitude(43.731142)});
|
||||
params.coordinates.push_back({util::FloatLongitude(7.419505), util::FloatLatitude(43.736825)});
|
||||
|
||||
// Response is in JSON format
|
||||
json::Object result;
|
||||
|
||||
// Execute routing request, this does the heavy lifting
|
||||
const auto status = osrm.Route(params, result);
|
||||
|
||||
if (status == Status::Ok)
|
||||
{
|
||||
// Extract data out of JSON structure
|
||||
auto& summary = json_result.values["route_summary"].get<osrm::json::Object>();
|
||||
auto duration = summary.values["total_time"].get<osrm::json::Number>().value;
|
||||
auto distance = summary.values["total_distance"].get<osrm::json::Number>().value;
|
||||
std::cout << "duration: " << duration << std::endl;
|
||||
std::cout << "distance: " << distance << std::endl;
|
||||
auto &routes = result.values["routes"].get<json::Array>();
|
||||
|
||||
// Let's just use the first route
|
||||
auto &route = routes.values.at(0).get<json::Object>();
|
||||
const auto distance = route.values["distance"].get<json::Number>().value;
|
||||
const auto duration = route.values["duration"].get<json::Number>().value;
|
||||
|
||||
// Warn users if extract does not contain the default Berlin coordinates from above
|
||||
if (distance == 0 or duration == 0)
|
||||
{
|
||||
std::cout << "Note: distance or duration is zero. ";
|
||||
std::cout << "You are probably doing a query outside of the OSM extract.\n\n";
|
||||
}
|
||||
|
||||
std::cout << "Distance: " << distance << " meter\n";
|
||||
std::cout << "Duration: " << duration << " seconds\n";
|
||||
}
|
||||
else if (status == Status::Error)
|
||||
{
|
||||
const auto code = result.values["code"].get<json::String>().value;
|
||||
const auto message = result.values["message"].get<json::String>().value;
|
||||
|
||||
std::cout << "Code: " << code << "\n";
|
||||
std::cout << "Message: " << code << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch (const std::exception ¤t_exception)
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cout << "exception: " << current_exception.what();
|
||||
std::cerr << "Error: " << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
@routing @guidance
|
||||
Feature: Basic Roundabout
|
||||
|
||||
Background:
|
||||
Given the profile "testbot"
|
||||
Given a grid size of 10 meters
|
||||
|
||||
Scenario: Ramp Exit Right
|
||||
Given the node map
|
||||
| a | b | c | d | e |
|
||||
| | | | f | g |
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abcde | motorway |
|
||||
| bfg | motorway_link |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,e | abcde, abcde | depart, arrive |
|
||||
| a,g | abcde, bfg, bfg | depart, ramp-slight-right, arrive |
|
||||
|
||||
Scenario: Ramp Exit Right Curved Right
|
||||
Given the node map
|
||||
| a | b | c | | |
|
||||
| | | f | d | |
|
||||
| | | | g | e |
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abcde | motorway |
|
||||
| bfg | motorway_link |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,e | abcde, abcde | depart, arrive |
|
||||
| a,g | abcde, bfg, bfg | depart, ramp-slight-right, arrive |
|
||||
|
||||
Scenario: Ramp Exit Right Curved Left
|
||||
Given the node map
|
||||
| | | | | e |
|
||||
| | | | d | g |
|
||||
| a | b | c | f | |
|
||||
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abcde | motorway |
|
||||
| cfg | motorway_link |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,e | abcde, abcde | depart, arrive |
|
||||
| a,g | abcde, cfg, cfg | depart, ramp-slight-right, arrive |
|
||||
|
||||
|
||||
Scenario: Ramp Exit Left
|
||||
Given the node map
|
||||
| | | | f | g |
|
||||
| a | b | c | d | e |
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abcde | motorway |
|
||||
| bfg | motorway_link |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,e | abcde, abcde | depart, arrive |
|
||||
| a,g | abcde, bfg, bfg | depart, ramp-slight-left, arrive |
|
||||
|
||||
Scenario: Ramp Exit Left Curved Left
|
||||
Given the node map
|
||||
| | | | g | e |
|
||||
| | | f | d | |
|
||||
| a | b | c | | |
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abcde | motorway |
|
||||
| bfg | motorway_link |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,e | abcde, abcde | depart, arrive |
|
||||
| a,g | abcde, bfg, bfg | depart, ramp-slight-left, arrive |
|
||||
|
||||
Scenario: Ramp Exit Left Curved Right
|
||||
Given the node map
|
||||
| a | b | c | f | |
|
||||
| | | | d | g |
|
||||
| | | | | e |
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abcde | motorway |
|
||||
| cfg | motorway_link |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,e | abcde, abcde | depart, arrive |
|
||||
| a,g | abcde, cfg, cfg | depart, ramp-slight-left, arrive |
|
||||
|
||||
Scenario: On Ramp Right
|
||||
Given the node map
|
||||
| a | b | c | d | e |
|
||||
| f | g | | | |
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abcde | motorway |
|
||||
| fgd | motorway_link |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,e | abcde, abcde | depart, arrive |
|
||||
| f,e | abcde, fgd, fgd | depart, merge-slight-left, arrive |
|
||||
|
||||
Scenario: On Ramp Left
|
||||
Given the node map
|
||||
| f | g | | | |
|
||||
| a | b | c | d | e |
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abcde | motorway |
|
||||
| fgd | motorway_link |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,e | abcde, abcde | depart, arrive |
|
||||
| f,e | abcde, fgd, fgd | depart, merge-slight-right, arrive |
|
||||
|
||||
Scenario: Highway Fork
|
||||
Given the node map
|
||||
| | | | | d | e |
|
||||
| a | b | c | | | |
|
||||
| | | | | f | g |
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abcde | motorway |
|
||||
| cfg | motorway |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,e | abcde, abcde, abcde | depart, fork-left, arrive |
|
||||
| a,g | abcde, cfg, cfg | depart, fork-right, arrive |
|
||||
|
||||
Scenario: Fork After Ramp
|
||||
Given the node map
|
||||
| | | | | d | e |
|
||||
| a | b | c | | | |
|
||||
| | | | | f | g |
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abc | motorway_link |
|
||||
| cde | motorway |
|
||||
| cfg | motorway |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,e | abc, cde, cde | depart, fork-left, arrive |
|
||||
| a,g | abc, cfg, cfg | depart, fork-right, arrive |
|
||||
|
||||
Scenario: On And Off Ramp Right
|
||||
Given the node map
|
||||
| a | b | | c | | d | e |
|
||||
| f | g | | | | h | i |
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abcde | motorway |
|
||||
| fgc | motorway_link |
|
||||
| chi | motorway_link |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,e | abcde, abcde | depart, arrive |
|
||||
| f,e | fgc, abcde, abcde | depart, merge-slight-left, arrive |
|
||||
| a,i | abcde, chi, chi | depart, ramp-slight-right, arrive |
|
||||
| f,i | fgc, chi, chi | depart, turn-slight-right, arrive |
|
||||
|
||||
Scenario: On And Off Ramp Left
|
||||
Given the node map
|
||||
| f | g | | | | h | i |
|
||||
| a | b | | c | | d | e |
|
||||
|
||||
And the ways
|
||||
| nodes | highway |
|
||||
| abcde | motorway |
|
||||
| fgc | motorway_link |
|
||||
| chi | motorway_link |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,e | abcde, abcde | depart, arrive |
|
||||
| f,e | fgc, abcde, abcde | depart, merge-slight-right, arrive |
|
||||
| a,i | abcde, chi, chi | depart, ramp-slight-left, arrive |
|
||||
| f,i | fgc, chi, chi | depart, turn-slight-left, arrive |
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
@routing @guidance
|
||||
Feature: Basic Roundabout
|
||||
|
||||
Background:
|
||||
Given the profile "testbot"
|
||||
Given a grid size of 10 meters
|
||||
|
||||
Scenario: Enter and Exit
|
||||
Given the node map
|
||||
| | | a | | |
|
||||
| | | b | | |
|
||||
| h | g | | c | d |
|
||||
| | | e | | |
|
||||
| | | f | | |
|
||||
|
||||
And the ways
|
||||
| nodes | roundabout |
|
||||
| ab | no |
|
||||
| cd | no |
|
||||
| ef | no |
|
||||
| gh | no |
|
||||
| bcegb | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,d | ab,cd,cd | depart, roundabout-exit-1, arrive |
|
||||
| a,f | ab,ef,ef | depart, roundabout-exit-2, arrive |
|
||||
| a,h | ab,gh,gh | depart, roundabout-exit-3, arrive |
|
||||
| d,f | cd,ef,ef | depart, roundabout-exit-1, arrive |
|
||||
| d,h | cd,gh,gh | depart, roundabout-exit-2, arrive |
|
||||
| d,a | cd,ab,ab | depart, roundabout-exit-3, arrive |
|
||||
| f,h | ef,gh,gh | depart, roundabout-exit-1, arrive |
|
||||
| f,a | ef,ab,ab | depart, roundabout-exit-2, arrive |
|
||||
| f,d | ef,cd,cd | depart, roundabout-exit-3, arrive |
|
||||
| h,a | gh,ab,ab | depart, roundabout-exit-1, arrive |
|
||||
| h,d | gh,cd,cd | depart, roundabout-exit-2, arrive |
|
||||
| h,f | gh,ef,ef | depart, roundabout-exit-3, arrive |
|
||||
|
||||
Scenario: Only Enter
|
||||
Given the node map
|
||||
| | | a | | |
|
||||
| | | b | | |
|
||||
| h | g | | c | d |
|
||||
| | | e | | |
|
||||
| | | f | | |
|
||||
|
||||
And the ways
|
||||
| nodes | roundabout |
|
||||
| ab | no |
|
||||
| cd | no |
|
||||
| ef | no |
|
||||
| gh | no |
|
||||
| bcegb | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,b | ab,ab | depart, arrive |
|
||||
| a,c | ab,bcegb | depart, roundabout-enter, arrive |
|
||||
| a,e | ab,bcegb | depart, roundabout-enter, arrive |
|
||||
| a,g | ab,bcegb | depart, roundabout-enter, arrive |
|
||||
| d,c | cd,cd | depart, arrive |
|
||||
| d,e | cd,bcegb | depart, roundabout-enter, arrive |
|
||||
| d,g | cd,bcegb | depart, roundabout-enter, arrive |
|
||||
| d,b | cd,bcegb | depart, roundabout-enter, arrive |
|
||||
| f,e | ef,ef | depart, arrive |
|
||||
| f,g | ef,bcegb | depart, roundabout-enter, arrive |
|
||||
| f,b | ef,bcegb | depart, roundabout-enter, arrive |
|
||||
| f,c | ef,bcegb | depart, roundabout-enter, arrive |
|
||||
| h,g | gh,gh | depart, arrive |
|
||||
| h,b | gh,bcegb | depart, roundabout-enter, arrive |
|
||||
| h,c | gh,bcegb | depart, roundabout-enter, arrive |
|
||||
| h,e | gh,bcegb | depart, roundabout-enter, arrive |
|
||||
|
||||
Scenario: Only Exit
|
||||
Given the node map
|
||||
| | | a | | |
|
||||
| | | b | | |
|
||||
| h | g | | c | d |
|
||||
| | | e | | |
|
||||
| | | f | | |
|
||||
|
||||
And the ways
|
||||
| nodes | roundabout |
|
||||
| ab | no |
|
||||
| cd | no |
|
||||
| ef | no |
|
||||
| gh | no |
|
||||
| bcegb | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| b,a | ab,ab | depart, arrive |
|
||||
| b,d | bcegb,cd,cd | depart, roundabout-exit-1, arrive |
|
||||
| b,f | bcegb,ef,ef | depart, roundabout-exit-2, arrive |
|
||||
| b,h | bcegb,gh,gh | depart, roundabout-exit-3, arrive |
|
||||
| c,d | cd,cd | depart, arrive |
|
||||
| c,f | bcegb,ef,ef | depart, roundabout-exit-1, arrive |
|
||||
| c,h | bcegb,gh,gh | depart, roundabout-exit-2, arrive |
|
||||
| c,a | bcegb,ab,ab | depart, roundabout-exit-3, arrive |
|
||||
| e,f | ef,ef | depart, arrive |
|
||||
| e,h | bcegb,gh,gh | depart, roundabout-exit-1, arrive |
|
||||
| e,a | bcegb,ab,ab | depart, roundabout-exit-2, arrive |
|
||||
| e,d | bcegb,cd,cd | depart, roundabout-exit-3, arrive |
|
||||
| g,h | gh,gh | depart, arrive |
|
||||
| g,a | bcegb,ab,ab | depart, roundabout-exit-1, arrive |
|
||||
| g,d | bcegb,cd,cd | depart, roundabout-exit-2, arrive |
|
||||
| g,f | bcegb,ef,ef | depart, roundabout-exit-3, arrive |
|
||||
|
||||
Scenario: Drive Around
|
||||
Given the node map
|
||||
| | | a | | |
|
||||
| | | b | | |
|
||||
| h | g | | c | d |
|
||||
| | | e | | |
|
||||
| | | f | | |
|
||||
|
||||
And the ways
|
||||
| nodes | roundabout |
|
||||
| ab | no |
|
||||
| cd | no |
|
||||
| ef | no |
|
||||
| gh | no |
|
||||
| bcegb | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| b,c | bcegb,bcegb | depart, arrive |
|
||||
| b,e | bcegb,bcegb | depart, arrive |
|
||||
| b,g | bcegb,bcegb | depart, arrive |
|
||||
| c,e | bcegb,bcegb | depart, arrive |
|
||||
| c,g | bcegb,bcegb | depart, arrive |
|
||||
| c,b | bcegb,bcegb | depart, arrive |
|
||||
| e,g | bcegb,bcegb | depart, arrive |
|
||||
| e,b | bcegb,bcegb | depart, arrive |
|
||||
| e,c | bcegb,bcegb | depart, arrive |
|
||||
| g,b | bcegb,bcegb | depart, arrive |
|
||||
| g,c | bcegb,bcegb | depart, arrive |
|
||||
| g,e | bcegb,bcegb | depart, arrive |
|
||||
|
||||
Scenario: Mixed Entry and Exit
|
||||
Given the node map
|
||||
| | a | | c | |
|
||||
| l | | b | | d |
|
||||
| | k | | e | |
|
||||
| j | | h | | f |
|
||||
| | i | | g | |
|
||||
|
||||
And the ways
|
||||
| nodes | roundabout | oneway |
|
||||
| abc | no | yes |
|
||||
| def | no | yes |
|
||||
| ghi | no | yes |
|
||||
| jkl | no | yes |
|
||||
| behkb | yes | yes |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns |
|
||||
| a,c | abc,abc,abc | depart, roundabout-exit-1, arrive |
|
||||
| a,f | abc,def,def | depart, roundabout-exit-2, arrive |
|
||||
| a,i | abc,ghi,ghi | depart, roundabout-exit-3, arrive |
|
||||
| a,l | abc,jkl,jkl | depart, roundabout-exit-4, arrive |
|
||||
| d,f | def,def,def | depart, roundabout-exit-1, arrive |
|
||||
| d,i | def,ghi,ghi | depart, roundabout-exit-2, arrive |
|
||||
| d,l | def,jkl,jkl | depart, roundabout-exit-3, arrive |
|
||||
| d,c | def,abc,abc | depart, roundabout-exit-4, arrive |
|
||||
| g,i | ghi,ghi,ghi | depart, roundabout-exit-1, arrive |
|
||||
| g,l | ghi,jkl,jkl | depart, roundabout-exit-2, arrive |
|
||||
| g,c | ghi,abc,abc | depart, roundabout-exit-3, arrive |
|
||||
| g,f | ghi,edf,edf | depart, roundabout-exit-4, arrive |
|
||||
| j,l | jkl,jkl,jkl | depart, roundabout-exit-1, arrive |
|
||||
| j,c | jkl,abc,abc | depart, roundabout-exit-2, arrive |
|
||||
| j,f | jkl,def,def | depart, roundabout-exit-3, arrive |
|
||||
| j,i | jkl,ghi,ghi | depart, roundabout-exit-4, arrive |
|
||||
@@ -5,6 +5,23 @@ Feature: Basic Map Matching
|
||||
Given the profile "testbot"
|
||||
Given a grid size of 10 meters
|
||||
|
||||
Scenario: Testbot - Map matching with outlier that has no candidate
|
||||
Given a grid size of 100 meters
|
||||
Given the node map
|
||||
| a | b | c | d |
|
||||
| | | | |
|
||||
| | | | |
|
||||
| | | | |
|
||||
| | | 1 | |
|
||||
|
||||
And the ways
|
||||
| nodes | oneway |
|
||||
| abcd | no |
|
||||
|
||||
When I match I should get
|
||||
| trace | timestamps | matchings |
|
||||
| ab1d | 0 1 2 3 | abcd |
|
||||
|
||||
Scenario: Testbot - Map matching with trace splitting
|
||||
Given the node map
|
||||
| a | b | c | d |
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef CONTRACTOR_CONTRACTOR_HPP
|
||||
#define CONTRACTOR_CONTRACTOR_HPP
|
||||
|
||||
@@ -56,9 +83,11 @@ class Contractor
|
||||
util::DeallocatingVector<extractor::EdgeBasedEdge> &edge_based_edge_list,
|
||||
const std::string &edge_segment_lookup_path,
|
||||
const std::string &edge_penalty_path,
|
||||
const std::string &segment_speed_path,
|
||||
const std::vector<std::string> &segment_speed_path,
|
||||
const std::string &nodes_filename,
|
||||
const std::string &geometry_filename,
|
||||
const std::string &datasource_names_filename,
|
||||
const std::string &datasource_indexes_filename,
|
||||
const std::string &rtree_leaf_filename);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef CONTRACTOR_OPTIONS_HPP
|
||||
#define CONTRACTOR_OPTIONS_HPP
|
||||
|
||||
@@ -26,6 +53,8 @@ struct ContractorConfig
|
||||
node_based_graph_path = osrm_input_path.string() + ".nodes";
|
||||
geometry_path = osrm_input_path.string() + ".geometry";
|
||||
rtree_leaf_path = osrm_input_path.string() + ".fileIndex";
|
||||
datasource_names_path = osrm_input_path.string() + ".datasource_names";
|
||||
datasource_indexes_path = osrm_input_path.string() + ".datasource_indexes";
|
||||
}
|
||||
|
||||
boost::filesystem::path config_file_path;
|
||||
@@ -51,11 +80,9 @@ struct ContractorConfig
|
||||
//(e.g. 0.8 contracts 80 percent of the hierarchy, leaving a core of 20%)
|
||||
double core_factor;
|
||||
|
||||
std::string segment_speed_lookup_path;
|
||||
|
||||
#ifdef DEBUG_GEOMETRY
|
||||
std::string debug_geometry_path;
|
||||
#endif
|
||||
std::vector<std::string> segment_speed_lookup_paths;
|
||||
std::string datasource_indexes_path;
|
||||
std::string datasource_names_path;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,9 +142,9 @@ class GraphContractor
|
||||
|
||||
template <class ContainerT>
|
||||
GraphContractor(int nodes,
|
||||
ContainerT &input_edge_list,
|
||||
std::vector<float> &&node_levels_,
|
||||
std::vector<EdgeWeight> &&node_weights_)
|
||||
ContainerT &input_edge_list,
|
||||
std::vector<float> &&node_levels_,
|
||||
std::vector<EdgeWeight> &&node_weights_)
|
||||
: node_levels(std::move(node_levels_)), node_weights(std::move(node_weights_))
|
||||
{
|
||||
std::vector<ContractorEdge> edges;
|
||||
@@ -239,7 +239,8 @@ class GraphContractor
|
||||
}
|
||||
}
|
||||
}
|
||||
util::SimpleLogger().Write() << "merged " << edges.size() - edge << " edges out of " << edges.size();
|
||||
util::SimpleLogger().Write() << "merged " << edges.size() - edge << " edges out of "
|
||||
<< edges.size();
|
||||
edges.resize(edge);
|
||||
contractor_graph = std::make_shared<ContractorGraph>(nodes, edges);
|
||||
edges.clear();
|
||||
@@ -696,7 +697,7 @@ class GraphContractor
|
||||
// New Node discovered -> Add to Heap + Node Info Storage
|
||||
if (!heap.WasInserted(to))
|
||||
{
|
||||
heap.Insert(to, to_distance, ContractorHeapData {current_hop, false});
|
||||
heap.Insert(to, to_distance, ContractorHeapData{current_hop, false});
|
||||
}
|
||||
// Found a shorter Path -> Update distance
|
||||
else if (to_distance < heap.GetKey(to))
|
||||
@@ -803,7 +804,7 @@ class GraphContractor
|
||||
}
|
||||
|
||||
heap.Clear();
|
||||
heap.Insert(source, 0, ContractorHeapData {});
|
||||
heap.Insert(source, 0, ContractorHeapData{});
|
||||
int max_distance = 0;
|
||||
unsigned number_of_targets = 0;
|
||||
|
||||
@@ -858,7 +859,7 @@ class GraphContractor
|
||||
max_distance = std::max(max_distance, path_distance);
|
||||
if (!heap.WasInserted(target))
|
||||
{
|
||||
heap.Insert(target, INVALID_EDGE_WEIGHT, ContractorHeapData {0, true});
|
||||
heap.Insert(target, INVALID_EDGE_WEIGHT, ContractorHeapData{0, true});
|
||||
++number_of_targets;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef ENGINE_API_BASE_API_HPP
|
||||
#define ENGINE_API_BASE_API_HPP
|
||||
|
||||
#include "engine/api/base_parameters.hpp"
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
|
||||
#include "engine/api/json_factory.hpp"
|
||||
#include "engine/hint.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/range/algorithm/transform.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
|
||||
class BaseAPI
|
||||
{
|
||||
public:
|
||||
BaseAPI(const datafacade::BaseDataFacade &facade_, const BaseParameters ¶meters_)
|
||||
: facade(facade_), parameters(parameters_)
|
||||
{
|
||||
}
|
||||
|
||||
util::json::Array MakeWaypoints(const std::vector<PhantomNodes> &segment_end_coordinates) const
|
||||
{
|
||||
BOOST_ASSERT(parameters.coordinates.size() > 0);
|
||||
BOOST_ASSERT(parameters.coordinates.size() == segment_end_coordinates.size() + 1);
|
||||
|
||||
util::json::Array waypoints;
|
||||
waypoints.values.resize(parameters.coordinates.size());
|
||||
waypoints.values[0] = MakeWaypoint(segment_end_coordinates.front().source_phantom);
|
||||
|
||||
auto out_iter = std::next(waypoints.values.begin());
|
||||
boost::range::transform(segment_end_coordinates, out_iter,
|
||||
[this](const PhantomNodes &phantom_pair)
|
||||
{
|
||||
return MakeWaypoint(phantom_pair.target_phantom);
|
||||
});
|
||||
return waypoints;
|
||||
}
|
||||
|
||||
// FIXME gcc 4.8 doesn't support for lambdas to call protected member functions
|
||||
// protected:
|
||||
util::json::Object MakeWaypoint(const PhantomNode &phantom) const
|
||||
{
|
||||
return json::makeWaypoint(phantom.location, facade.GetNameForID(phantom.name_id),
|
||||
Hint{phantom, facade.GetCheckSum()});
|
||||
}
|
||||
|
||||
const datafacade::BaseDataFacade &facade;
|
||||
const BaseParameters ¶meters;
|
||||
};
|
||||
|
||||
} // ns api
|
||||
} // ns engine
|
||||
} // ns osrm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ENGINE_API_BASE_PARAMETERS_HPP
|
||||
#define ENGINE_API_BASE_PARAMETERS_HPP
|
||||
|
||||
#include "engine/hint.hpp"
|
||||
#include "engine/bearing.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
struct BaseParameters
|
||||
{
|
||||
std::vector<util::Coordinate> coordinates;
|
||||
std::vector<boost::optional<Hint>> hints;
|
||||
std::vector<boost::optional<double>> radiuses;
|
||||
std::vector<boost::optional<Bearing>> bearings;
|
||||
|
||||
// FIXME add validation for invalid bearing values
|
||||
bool IsValid() const
|
||||
{
|
||||
return (hints.empty() || hints.size() == coordinates.size()) &&
|
||||
(bearings.empty() || bearings.size() == coordinates.size()) &&
|
||||
(radiuses.empty() || radiuses.size() == coordinates.size()) &&
|
||||
std::all_of(bearings.begin(), bearings.end(),
|
||||
[](const boost::optional<Bearing> bearing_and_range)
|
||||
{
|
||||
if (bearing_and_range)
|
||||
{
|
||||
return bearing_and_range->IsValid();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ROUTE_PARAMETERS_HPP
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef ENGINE_RESPONSE_OBJECTS_HPP_
|
||||
#define ENGINE_RESPONSE_OBJECTS_HPP_
|
||||
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "engine/polyline_compressor.hpp"
|
||||
#include "engine/guidance/route_step.hpp"
|
||||
#include "engine/guidance/step_maneuver.hpp"
|
||||
#include "engine/guidance/route_leg.hpp"
|
||||
#include "engine/guidance/route.hpp"
|
||||
#include "engine/guidance/leg_geometry.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/json_container.hpp"
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
|
||||
struct Hint;
|
||||
|
||||
namespace api
|
||||
{
|
||||
namespace json
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
std::string instructionTypeToString(extractor::guidance::TurnType type);
|
||||
std::string instructionModifierToString(extractor::guidance::DirectionModifier modifier);
|
||||
|
||||
util::json::Array coordinateToLonLat(const util::Coordinate coordinate);
|
||||
|
||||
std::string modeToString(const extractor::TravelMode mode);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename ForwardIter> util::json::String makePolyline(ForwardIter begin, ForwardIter end)
|
||||
{
|
||||
return {encodePolyline(begin, end)};
|
||||
}
|
||||
|
||||
template <typename ForwardIter>
|
||||
util::json::Object makeGeoJSONLineString(ForwardIter begin, ForwardIter end)
|
||||
{
|
||||
util::json::Object geojson;
|
||||
geojson.values["type"] = "LineString";
|
||||
util::json::Array coordinates;
|
||||
std::transform(begin, end, std::back_inserter(coordinates.values), &detail::coordinateToLonLat);
|
||||
geojson.values["coordinates"] = std::move(coordinates);
|
||||
return geojson;
|
||||
}
|
||||
|
||||
util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver);
|
||||
|
||||
util::json::Object makeRouteStep(guidance::RouteStep step,
|
||||
boost::optional<util::json::Value> geometry);
|
||||
|
||||
util::json::Object makeRoute(const guidance::Route &route,
|
||||
util::json::Array legs,
|
||||
boost::optional<util::json::Value> geometry);
|
||||
|
||||
util::json::Object
|
||||
makeWaypoint(const util::Coordinate location, std::string name, const Hint &hint);
|
||||
|
||||
util::json::Object makeRouteLeg(guidance::RouteLeg leg, util::json::Array steps);
|
||||
|
||||
util::json::Array makeRouteLegs(std::vector<guidance::RouteLeg> legs,
|
||||
std::vector<util::json::Value> step_geometries);
|
||||
}
|
||||
}
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif // ENGINE_GUIDANCE_API_RESPONSE_GENERATOR_HPP_
|
||||
@@ -0,0 +1,119 @@
|
||||
#ifndef ENGINE_API_MATCH_HPP
|
||||
#define ENGINE_API_MATCH_HPP
|
||||
|
||||
#include "engine/api/route_api.hpp"
|
||||
#include "engine/api/match_parameters.hpp"
|
||||
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
|
||||
#include "engine/internal_route_result.hpp"
|
||||
#include "engine/map_matching/sub_matching.hpp"
|
||||
|
||||
#include "util/integer_range.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
|
||||
class MatchAPI final : public RouteAPI
|
||||
{
|
||||
public:
|
||||
MatchAPI(const datafacade::BaseDataFacade &facade_, const MatchParameters ¶meters_)
|
||||
: RouteAPI(facade_, parameters_), parameters(parameters_)
|
||||
{
|
||||
}
|
||||
|
||||
void MakeResponse(const std::vector<map_matching::SubMatching> &sub_matchings,
|
||||
const std::vector<InternalRouteResult> &sub_routes,
|
||||
util::json::Object &response) const
|
||||
{
|
||||
auto number_of_routes = sub_matchings.size();
|
||||
util::json::Array routes;
|
||||
routes.values.reserve(number_of_routes);
|
||||
BOOST_ASSERT(sub_matchings.size() == sub_routes.size());
|
||||
for (auto index : util::irange<std::size_t>(0UL, sub_matchings.size()))
|
||||
{
|
||||
auto route = MakeRoute(sub_routes[index].segment_end_coordinates,
|
||||
sub_routes[index].unpacked_path_segments,
|
||||
sub_routes[index].source_traversed_in_reverse,
|
||||
sub_routes[index].target_traversed_in_reverse);
|
||||
route.values["confidence"] = sub_matchings[index].confidence;
|
||||
routes.values.push_back(std::move(route));
|
||||
}
|
||||
response.values["tracepoints"] = MakeTracepoints(sub_matchings);
|
||||
response.values["matchings"] = std::move(routes);
|
||||
response.values["code"] = "ok";
|
||||
}
|
||||
|
||||
// FIXME gcc 4.8 doesn't support for lambdas to call protected member functions
|
||||
// protected:
|
||||
|
||||
// FIXME this logic is a little backwards. We should change the output format of the
|
||||
// map_matching
|
||||
// routing algorithm to be easier to consume here.
|
||||
util::json::Array
|
||||
MakeTracepoints(const std::vector<map_matching::SubMatching> &sub_matchings) const
|
||||
{
|
||||
util::json::Array waypoints;
|
||||
waypoints.values.reserve(parameters.coordinates.size());
|
||||
|
||||
struct MatchingIndex
|
||||
{
|
||||
MatchingIndex() = default;
|
||||
MatchingIndex(unsigned sub_matching_index_, unsigned point_index_)
|
||||
: sub_matching_index(sub_matching_index_), point_index(point_index_)
|
||||
{
|
||||
}
|
||||
|
||||
unsigned sub_matching_index = std::numeric_limits<unsigned>::max();
|
||||
unsigned point_index = std::numeric_limits<unsigned>::max();
|
||||
|
||||
bool NotMatched()
|
||||
{
|
||||
return sub_matching_index == std::numeric_limits<unsigned>::max() &&
|
||||
point_index == std::numeric_limits<unsigned>::max();
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<MatchingIndex> trace_idx_to_matching_idx(parameters.coordinates.size());
|
||||
for (auto sub_matching_index :
|
||||
util::irange(0u, static_cast<unsigned>(sub_matchings.size())))
|
||||
{
|
||||
for (auto point_index : util::irange(
|
||||
0u, static_cast<unsigned>(sub_matchings[sub_matching_index].indices.size())))
|
||||
{
|
||||
trace_idx_to_matching_idx[sub_matchings[sub_matching_index].indices[point_index]] =
|
||||
MatchingIndex{sub_matching_index, point_index};
|
||||
}
|
||||
}
|
||||
|
||||
for (auto trace_index : util::irange(0UL, parameters.coordinates.size()))
|
||||
{
|
||||
auto matching_index = trace_idx_to_matching_idx[trace_index];
|
||||
if (matching_index.NotMatched())
|
||||
{
|
||||
waypoints.values.push_back(util::json::Null());
|
||||
continue;
|
||||
}
|
||||
const auto &phantom =
|
||||
sub_matchings[matching_index.sub_matching_index].nodes[matching_index.point_index];
|
||||
auto waypoint = BaseAPI::MakeWaypoint(phantom);
|
||||
waypoint.values["matchings_index"] = matching_index.sub_matching_index;
|
||||
waypoint.values["waypoint_index"] = matching_index.point_index;
|
||||
waypoints.values.push_back(std::move(waypoint));
|
||||
}
|
||||
|
||||
return waypoints;
|
||||
}
|
||||
|
||||
const MatchParameters ¶meters;
|
||||
};
|
||||
|
||||
} // ns api
|
||||
} // ns engine
|
||||
} // ns osrm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ENGINE_API_MATCH_PARAMETERS_HPP
|
||||
#define ENGINE_API_MATCH_PARAMETERS_HPP
|
||||
|
||||
#include "engine/api/route_parameters.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
|
||||
struct MatchParameters : public RouteParameters
|
||||
{
|
||||
MatchParameters()
|
||||
: RouteParameters(false,
|
||||
false,
|
||||
RouteParameters::GeometriesType::Polyline,
|
||||
RouteParameters::OverviewType::Simplified,
|
||||
{})
|
||||
{
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
MatchParameters(std::vector<unsigned> timestamps_, Args... args_)
|
||||
: RouteParameters{std::forward<Args>(args_)...}, timestamps{std::move(timestamps_)}
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<unsigned> timestamps;
|
||||
bool IsValid() const
|
||||
{
|
||||
return RouteParameters::IsValid() &&
|
||||
(timestamps.empty() || timestamps.size() == coordinates.size());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef ENGINE_API_NEAREST_API_HPP
|
||||
#define ENGINE_API_NEAREST_API_HPP
|
||||
|
||||
#include "engine/api/base_api.hpp"
|
||||
#include "engine/api/nearest_parameters.hpp"
|
||||
|
||||
#include "engine/api/json_factory.hpp"
|
||||
#include "engine/phantom_node.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
|
||||
class NearestAPI final : public BaseAPI
|
||||
{
|
||||
public:
|
||||
NearestAPI(const datafacade::BaseDataFacade &facade_, const NearestParameters ¶meters_)
|
||||
: BaseAPI(facade_, parameters_), parameters(parameters_)
|
||||
{
|
||||
}
|
||||
|
||||
void MakeResponse(const std::vector<std::vector<PhantomNodeWithDistance>> &phantom_nodes,
|
||||
util::json::Object &response) const
|
||||
{
|
||||
BOOST_ASSERT(phantom_nodes.size() == 1);
|
||||
BOOST_ASSERT(parameters.coordinates.size() == 1);
|
||||
|
||||
util::json::Array waypoints;
|
||||
waypoints.values.resize(phantom_nodes.front().size());
|
||||
std::transform(phantom_nodes.front().begin(), phantom_nodes.front().end(),
|
||||
waypoints.values.begin(),
|
||||
[this](const PhantomNodeWithDistance &phantom_with_distance)
|
||||
{
|
||||
auto waypoint = MakeWaypoint(phantom_with_distance.phantom_node);
|
||||
waypoint.values["distance"] = phantom_with_distance.distance;
|
||||
return waypoint;
|
||||
});
|
||||
|
||||
response.values["code"] = "ok";
|
||||
response.values["waypoints"] = std::move(waypoints);
|
||||
}
|
||||
|
||||
const NearestParameters ¶meters;
|
||||
};
|
||||
|
||||
} // ns api
|
||||
} // ns engine
|
||||
} // ns osrm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ENGINE_API_NEAREST_PARAMETERS_HPP
|
||||
#define ENGINE_API_NEAREST_PARAMETERS_HPP
|
||||
|
||||
#include "engine/api/base_parameters.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
|
||||
struct NearestParameters : public BaseParameters
|
||||
{
|
||||
unsigned number_of_results = 1;
|
||||
|
||||
bool IsValid() const { return BaseParameters::IsValid() && number_of_results >= 1; }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ENGINE_API_NEAREST_PARAMETERS_HPP
|
||||
@@ -0,0 +1,183 @@
|
||||
#ifndef ENGINE_API_ROUTE_HPP
|
||||
#define ENGINE_API_ROUTE_HPP
|
||||
|
||||
#include "engine/api/base_api.hpp"
|
||||
#include "engine/api/route_parameters.hpp"
|
||||
#include "engine/api/json_factory.hpp"
|
||||
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
|
||||
#include "engine/guidance/assemble_leg.hpp"
|
||||
#include "engine/guidance/assemble_route.hpp"
|
||||
#include "engine/guidance/assemble_geometry.hpp"
|
||||
#include "engine/guidance/assemble_overview.hpp"
|
||||
#include "engine/guidance/assemble_steps.hpp"
|
||||
#include "engine/guidance/post_processing.hpp"
|
||||
|
||||
#include "engine/internal_route_result.hpp"
|
||||
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
|
||||
class RouteAPI : public BaseAPI
|
||||
{
|
||||
public:
|
||||
RouteAPI(const datafacade::BaseDataFacade &facade_, const RouteParameters ¶meters_)
|
||||
: BaseAPI(facade_, parameters_), parameters(parameters_)
|
||||
{
|
||||
}
|
||||
|
||||
void MakeResponse(const InternalRouteResult &raw_route, util::json::Object &response) const
|
||||
{
|
||||
auto number_of_routes = raw_route.has_alternative() ? 2UL : 1UL;
|
||||
util::json::Array routes;
|
||||
routes.values.resize(number_of_routes);
|
||||
routes.values[0] =
|
||||
MakeRoute(raw_route.segment_end_coordinates, raw_route.unpacked_path_segments,
|
||||
raw_route.source_traversed_in_reverse, raw_route.target_traversed_in_reverse);
|
||||
if (raw_route.has_alternative())
|
||||
{
|
||||
std::vector<std::vector<PathData>> wrapped_leg(1);
|
||||
wrapped_leg.front() = std::move(raw_route.unpacked_alternative);
|
||||
routes.values[1] = MakeRoute(raw_route.segment_end_coordinates, wrapped_leg,
|
||||
raw_route.alt_source_traversed_in_reverse,
|
||||
raw_route.alt_target_traversed_in_reverse);
|
||||
}
|
||||
response.values["waypoints"] = BaseAPI::MakeWaypoints(raw_route.segment_end_coordinates);
|
||||
response.values["routes"] = std::move(routes);
|
||||
response.values["code"] = "ok";
|
||||
}
|
||||
|
||||
// FIXME gcc 4.8 doesn't support for lambdas to call protected member functions
|
||||
// protected:
|
||||
template <typename ForwardIter>
|
||||
util::json::Value MakeGeometry(ForwardIter begin, ForwardIter end) const
|
||||
{
|
||||
if (parameters.geometries == RouteParameters::GeometriesType::Polyline)
|
||||
{
|
||||
return json::makePolyline(begin, end);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON);
|
||||
return json::makeGeoJSONLineString(begin, end);
|
||||
}
|
||||
|
||||
util::json::Object MakeRoute(const std::vector<PhantomNodes> &segment_end_coordinates,
|
||||
const std::vector<std::vector<PathData>> &unpacked_path_segments,
|
||||
const std::vector<bool> &source_traversed_in_reverse,
|
||||
const std::vector<bool> &target_traversed_in_reverse) const
|
||||
{
|
||||
std::vector<guidance::RouteLeg> legs;
|
||||
std::vector<guidance::LegGeometry> leg_geometries;
|
||||
auto number_of_legs = segment_end_coordinates.size();
|
||||
legs.reserve(number_of_legs);
|
||||
leg_geometries.reserve(number_of_legs);
|
||||
|
||||
for (auto idx : util::irange(0UL, number_of_legs))
|
||||
{
|
||||
const auto &phantoms = segment_end_coordinates[idx];
|
||||
const auto &path_data = unpacked_path_segments[idx];
|
||||
|
||||
const bool reversed_source = source_traversed_in_reverse[idx];
|
||||
const bool reversed_target = target_traversed_in_reverse[idx];
|
||||
|
||||
auto leg_geometry = guidance::assembleGeometry(
|
||||
BaseAPI::facade, path_data, phantoms.source_phantom, phantoms.target_phantom);
|
||||
auto leg = guidance::assembleLeg(BaseAPI::facade, path_data, leg_geometry,
|
||||
phantoms.target_phantom, reversed_target);
|
||||
|
||||
if (parameters.steps)
|
||||
{
|
||||
auto steps = guidance::assembleSteps(
|
||||
BaseAPI::facade, path_data, leg_geometry, phantoms.source_phantom,
|
||||
phantoms.target_phantom, reversed_source, reversed_target);
|
||||
|
||||
/* Perform step-based post-processing.
|
||||
*
|
||||
* Using post-processing on basis of route-steps for a single leg at a time
|
||||
* comes at the cost that we cannot count the correct exit for roundabouts.
|
||||
* We can only emit the exit nr/intersections up to/starting at a part of the leg.
|
||||
* If a roundabout is not terminated in a leg, we will end up with a enter-roundabout
|
||||
* and exit-roundabout-nr where the exit nr is out of sync with the previous enter.
|
||||
*
|
||||
* | S |
|
||||
* * *
|
||||
* ----* * ----
|
||||
* T
|
||||
* ----* * ----
|
||||
* V * *
|
||||
* | |
|
||||
* | |
|
||||
*
|
||||
* Coming from S via V to T, we end up with the legs S->V and V->T. V-T will say to take
|
||||
* the second exit, even though counting from S it would be the third.
|
||||
* For S, we only emit `roundabout` without an exit number, showing that we enter a roundabout
|
||||
* to find a via point.
|
||||
* The same exit will be emitted, though, if we should start routing at S, making
|
||||
* the overall response consistent.
|
||||
*/
|
||||
|
||||
leg.steps = guidance::postProcess(std::move(steps));
|
||||
leg_geometry = guidance::resyncGeometry(std::move(leg_geometry),leg.steps);
|
||||
}
|
||||
|
||||
leg_geometries.push_back(std::move(leg_geometry));
|
||||
legs.push_back(std::move(leg));
|
||||
}
|
||||
|
||||
auto route = guidance::assembleRoute(legs);
|
||||
boost::optional<util::json::Value> json_overview;
|
||||
if (parameters.overview != RouteParameters::OverviewType::False)
|
||||
{
|
||||
const auto use_simplification =
|
||||
parameters.overview == RouteParameters::OverviewType::Simplified;
|
||||
BOOST_ASSERT(use_simplification ||
|
||||
parameters.overview == RouteParameters::OverviewType::Full);
|
||||
|
||||
auto overview = guidance::assembleOverview(leg_geometries, use_simplification);
|
||||
json_overview = MakeGeometry(overview.begin(), overview.end());
|
||||
}
|
||||
|
||||
std::vector<util::json::Value> step_geometries;
|
||||
for (const auto idx : util::irange(0UL, legs.size()))
|
||||
{
|
||||
auto &leg_geometry = leg_geometries[idx];
|
||||
std::transform(
|
||||
legs[idx].steps.begin(), legs[idx].steps.end(), std::back_inserter(step_geometries),
|
||||
[this, &leg_geometry](const guidance::RouteStep &step)
|
||||
{
|
||||
if (parameters.geometries == RouteParameters::GeometriesType::Polyline)
|
||||
{
|
||||
return static_cast<util::json::Value>(
|
||||
json::makePolyline(leg_geometry.locations.begin() + step.geometry_begin,
|
||||
leg_geometry.locations.begin() + step.geometry_end));
|
||||
}
|
||||
BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON);
|
||||
return static_cast<util::json::Value>(json::makeGeoJSONLineString(
|
||||
leg_geometry.locations.begin() + step.geometry_begin,
|
||||
leg_geometry.locations.begin() + step.geometry_end));
|
||||
});
|
||||
}
|
||||
|
||||
return json::makeRoute(route,
|
||||
json::makeRouteLegs(std::move(legs), std::move(step_geometries)),
|
||||
std::move(json_overview));
|
||||
}
|
||||
|
||||
const RouteParameters ¶meters;
|
||||
};
|
||||
|
||||
} // ns api
|
||||
} // ns engine
|
||||
} // ns osrm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ENGINE_API_ROUTE_PARAMETERS_HPP
|
||||
#define ENGINE_API_ROUTE_PARAMETERS_HPP
|
||||
|
||||
#include "engine/api/base_parameters.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
|
||||
struct RouteParameters : public BaseParameters
|
||||
{
|
||||
enum class GeometriesType
|
||||
{
|
||||
Polyline,
|
||||
GeoJSON
|
||||
};
|
||||
enum class OverviewType
|
||||
{
|
||||
Simplified,
|
||||
Full,
|
||||
False
|
||||
};
|
||||
|
||||
RouteParameters() = default;
|
||||
|
||||
template <typename... Args>
|
||||
RouteParameters(const bool steps_,
|
||||
const bool alternatives_,
|
||||
const GeometriesType geometries_,
|
||||
const OverviewType overview_,
|
||||
const boost::optional<bool> uturns_,
|
||||
Args... args_)
|
||||
: BaseParameters{std::forward<Args>(args_)...}, steps{steps_}, alternatives{alternatives_},
|
||||
geometries{geometries_}, overview{overview_}, uturns{uturns_}
|
||||
{
|
||||
}
|
||||
|
||||
bool steps = true;
|
||||
bool alternatives = true;
|
||||
GeometriesType geometries = GeometriesType::Polyline;
|
||||
OverviewType overview = OverviewType::Simplified;
|
||||
boost::optional<bool> uturns;
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return coordinates.size() >= 2 && BaseParameters::IsValid();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,133 @@
|
||||
#ifndef ENGINE_API_TABLE_HPP
|
||||
#define ENGINE_API_TABLE_HPP
|
||||
|
||||
#include "engine/api/base_api.hpp"
|
||||
#include "engine/api/table_parameters.hpp"
|
||||
#include "engine/api/json_factory.hpp"
|
||||
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
|
||||
#include "engine/guidance/assemble_leg.hpp"
|
||||
#include "engine/guidance/assemble_route.hpp"
|
||||
#include "engine/guidance/assemble_geometry.hpp"
|
||||
#include "engine/guidance/assemble_overview.hpp"
|
||||
#include "engine/guidance/assemble_steps.hpp"
|
||||
|
||||
#include "engine/internal_route_result.hpp"
|
||||
|
||||
#include "util/integer_range.hpp"
|
||||
|
||||
#include <boost/range/algorithm/transform.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
|
||||
class TableAPI final : public BaseAPI
|
||||
{
|
||||
public:
|
||||
TableAPI(const datafacade::BaseDataFacade &facade_, const TableParameters ¶meters_)
|
||||
: BaseAPI(facade_, parameters_), parameters(parameters_)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void MakeResponse(const std::vector<EdgeWeight> &durations,
|
||||
const std::vector<PhantomNode> &phantoms,
|
||||
util::json::Object &response) const
|
||||
{
|
||||
auto number_of_sources = parameters.sources.size();
|
||||
auto number_of_destinations = parameters.destinations.size();
|
||||
;
|
||||
|
||||
// symmetric case
|
||||
if (parameters.sources.empty())
|
||||
{
|
||||
response.values["sources"] = MakeWaypoints(phantoms);
|
||||
number_of_sources = phantoms.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
response.values["sources"] = MakeWaypoints(phantoms, parameters.sources);
|
||||
}
|
||||
|
||||
if (parameters.destinations.empty())
|
||||
{
|
||||
response.values["destinations"] = MakeWaypoints(phantoms);
|
||||
number_of_destinations = phantoms.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
response.values["destinations"] = MakeWaypoints(phantoms, parameters.destinations);
|
||||
}
|
||||
|
||||
response.values["durations"] =
|
||||
MakeTable(durations, number_of_sources, number_of_destinations);
|
||||
response.values["code"] = "ok";
|
||||
}
|
||||
|
||||
// FIXME gcc 4.8 doesn't support for lambdas to call protected member functions
|
||||
// protected:
|
||||
virtual util::json::Array MakeWaypoints(const std::vector<PhantomNode> &phantoms) const
|
||||
{
|
||||
util::json::Array json_waypoints;
|
||||
json_waypoints.values.reserve(phantoms.size());
|
||||
BOOST_ASSERT(phantoms.size() == parameters.coordinates.size());
|
||||
|
||||
boost::range::transform(phantoms, std::back_inserter(json_waypoints.values),
|
||||
[this](const PhantomNode &phantom)
|
||||
{
|
||||
return BaseAPI::MakeWaypoint(phantom);
|
||||
});
|
||||
return json_waypoints;
|
||||
}
|
||||
|
||||
virtual util::json::Array MakeWaypoints(const std::vector<PhantomNode> &phantoms,
|
||||
const std::vector<std::size_t> &indices) const
|
||||
{
|
||||
util::json::Array json_waypoints;
|
||||
json_waypoints.values.reserve(indices.size());
|
||||
boost::range::transform(indices, std::back_inserter(json_waypoints.values),
|
||||
[this, phantoms](const std::size_t idx)
|
||||
{
|
||||
BOOST_ASSERT(idx < phantoms.size());
|
||||
return BaseAPI::MakeWaypoint(phantoms[idx]);
|
||||
});
|
||||
return json_waypoints;
|
||||
}
|
||||
|
||||
virtual util::json::Array MakeTable(const std::vector<EdgeWeight> &values,
|
||||
std::size_t number_of_rows,
|
||||
std::size_t number_of_columns) const
|
||||
{
|
||||
util::json::Array json_table;
|
||||
for (const auto row : util::irange<std::size_t>(0, number_of_rows))
|
||||
{
|
||||
util::json::Array json_row;
|
||||
auto row_begin_iterator = values.begin() + (row * number_of_columns);
|
||||
auto row_end_iterator = values.begin() + ((row + 1) * number_of_columns);
|
||||
json_row.values.resize(number_of_columns);
|
||||
std::transform(row_begin_iterator, row_end_iterator, json_row.values.begin(),
|
||||
[](const EdgeWeight duration)
|
||||
{
|
||||
if (duration == INVALID_EDGE_WEIGHT)
|
||||
{
|
||||
return util::json::Value(util::json::Null());
|
||||
}
|
||||
return util::json::Value(util::json::Number(duration / 10.));
|
||||
});
|
||||
json_table.values.push_back(std::move(json_row));
|
||||
}
|
||||
return json_table;
|
||||
}
|
||||
|
||||
const TableParameters ¶meters;
|
||||
};
|
||||
|
||||
} // ns api
|
||||
} // ns engine
|
||||
} // ns osrm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ENGINE_API_TABLE_PARAMETERS_HPP
|
||||
#define ENGINE_API_TABLE_PARAMETERS_HPP
|
||||
|
||||
#include "engine/api/base_parameters.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
|
||||
struct TableParameters : public BaseParameters
|
||||
{
|
||||
std::vector<std::size_t> sources;
|
||||
std::vector<std::size_t> destinations;
|
||||
|
||||
TableParameters() = default;
|
||||
template <typename... Args>
|
||||
TableParameters(std::vector<std::size_t> sources_,
|
||||
std::vector<std::size_t> destinations_,
|
||||
Args... args_)
|
||||
: BaseParameters{std::forward<Args>(args_)...}, sources{std::move(sources_)},
|
||||
destinations{std::move(destinations_)}
|
||||
{
|
||||
}
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
if (!BaseParameters::IsValid())
|
||||
return false;
|
||||
|
||||
// Distance Table makes only sense with 2+ coodinates
|
||||
if (coordinates.size() < 2)
|
||||
return false;
|
||||
|
||||
// 1/ The user is able to specify duplicates in srcs and dsts, in that case it's her fault
|
||||
|
||||
// 2/ len(srcs) and len(dsts) smaller or equal to len(locations)
|
||||
if (sources.size() > coordinates.size())
|
||||
return false;
|
||||
|
||||
if (destinations.size() > coordinates.size())
|
||||
return false;
|
||||
|
||||
// 3/ 0 <= index < len(locations)
|
||||
const auto not_in_range = [this](const std::size_t x)
|
||||
{
|
||||
return x >= coordinates.size();
|
||||
};
|
||||
|
||||
if (std::any_of(begin(sources), end(sources), not_in_range))
|
||||
return false;
|
||||
|
||||
if (std::any_of(begin(destinations), end(destinations), not_in_range))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ENGINE_API_TABLE_PARAMETERS_HPP
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ENGINE_API_TILE_PARAMETERS_HPP
|
||||
#define ENGINE_API_TILE_PARAMETERS_HPP
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
|
||||
struct TileParameters final
|
||||
{
|
||||
unsigned x;
|
||||
unsigned y;
|
||||
unsigned z;
|
||||
|
||||
// FIXME check if x and y work with z
|
||||
bool IsValid() { return z < 20; };
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,111 @@
|
||||
#ifndef ENGINE_API_TRIP_HPP
|
||||
#define ENGINE_API_TRIP_HPP
|
||||
|
||||
#include "engine/api/route_api.hpp"
|
||||
#include "engine/api/trip_parameters.hpp"
|
||||
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
|
||||
#include "engine/internal_route_result.hpp"
|
||||
|
||||
#include "util/integer_range.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
|
||||
class TripAPI final : public RouteAPI
|
||||
{
|
||||
public:
|
||||
TripAPI(const datafacade::BaseDataFacade &facade_, const TripParameters ¶meters_)
|
||||
: RouteAPI(facade_, parameters_), parameters(parameters_)
|
||||
{
|
||||
}
|
||||
|
||||
void MakeResponse(const std::vector<std::vector<NodeID>> &sub_trips,
|
||||
const std::vector<InternalRouteResult> &sub_routes,
|
||||
const std::vector<PhantomNode> &phantoms,
|
||||
util::json::Object &response) const
|
||||
{
|
||||
auto number_of_routes = sub_trips.size();
|
||||
util::json::Array routes;
|
||||
routes.values.reserve(number_of_routes);
|
||||
BOOST_ASSERT(sub_trips.size() == sub_routes.size());
|
||||
for (auto index : util::irange<std::size_t>(0UL, sub_trips.size()))
|
||||
{
|
||||
auto route = MakeRoute(sub_routes[index].segment_end_coordinates,
|
||||
sub_routes[index].unpacked_path_segments,
|
||||
sub_routes[index].source_traversed_in_reverse,
|
||||
sub_routes[index].target_traversed_in_reverse);
|
||||
routes.values.push_back(std::move(route));
|
||||
}
|
||||
response.values["waypoints"] = MakeWaypoints(sub_trips, phantoms);
|
||||
response.values["trips"] = std::move(routes);
|
||||
response.values["code"] = "ok";
|
||||
}
|
||||
|
||||
// FIXME gcc 4.8 doesn't support for lambdas to call protected member functions
|
||||
// protected:
|
||||
|
||||
// FIXME this logic is a little backwards. We should change the output format of the
|
||||
// trip plugin routing algorithm to be easier to consume here.
|
||||
util::json::Array MakeWaypoints(const std::vector<std::vector<NodeID>> &sub_trips,
|
||||
const std::vector<PhantomNode> &phantoms) const
|
||||
{
|
||||
util::json::Array waypoints;
|
||||
waypoints.values.reserve(parameters.coordinates.size());
|
||||
|
||||
struct TripIndex
|
||||
{
|
||||
TripIndex() = default;
|
||||
TripIndex(unsigned sub_trip_index_, unsigned point_index_)
|
||||
: sub_trip_index(sub_trip_index_), point_index(point_index_)
|
||||
{
|
||||
}
|
||||
|
||||
unsigned sub_trip_index = std::numeric_limits<unsigned>::max();
|
||||
unsigned point_index = std::numeric_limits<unsigned>::max();
|
||||
|
||||
bool NotUsed()
|
||||
{
|
||||
return sub_trip_index == std::numeric_limits<unsigned>::max() &&
|
||||
point_index == std::numeric_limits<unsigned>::max();
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<TripIndex> input_idx_to_trip_idx(parameters.coordinates.size());
|
||||
for (auto sub_trip_index : util::irange(0u, static_cast<unsigned>(sub_trips.size())))
|
||||
{
|
||||
for (auto point_index :
|
||||
util::irange(0u, static_cast<unsigned>(sub_trips[sub_trip_index].size())))
|
||||
{
|
||||
input_idx_to_trip_idx[sub_trips[sub_trip_index][point_index]] =
|
||||
TripIndex{sub_trip_index, point_index};
|
||||
}
|
||||
}
|
||||
|
||||
for (auto input_index : util::irange(0UL, parameters.coordinates.size()))
|
||||
{
|
||||
auto trip_index = input_idx_to_trip_idx[input_index];
|
||||
BOOST_ASSERT(!trip_index.NotUsed());
|
||||
|
||||
auto waypoint = BaseAPI::MakeWaypoint(phantoms[input_index]);
|
||||
waypoint.values["trips_index"] = trip_index.sub_trip_index;
|
||||
waypoint.values["waypoint_index"] = trip_index.point_index;
|
||||
waypoints.values.push_back(std::move(waypoint));
|
||||
}
|
||||
|
||||
return waypoints;
|
||||
}
|
||||
|
||||
const TripParameters ¶meters;
|
||||
};
|
||||
|
||||
} // ns api
|
||||
} // ns engine
|
||||
} // ns osrm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ENGINE_API_TRIP_PARAMETERS_HPP
|
||||
#define ENGINE_API_TRIP_PARAMETERS_HPP
|
||||
|
||||
#include "engine/api/route_parameters.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
|
||||
struct TripParameters : public RouteParameters
|
||||
{
|
||||
// bool IsValid() const; Falls back to base class
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,299 +0,0 @@
|
||||
#ifndef ENGINE_GUIDANCE_API_RESPONSE_GENERATOR_HPP_
|
||||
#define ENGINE_GUIDANCE_API_RESPONSE_GENERATOR_HPP_
|
||||
|
||||
#include "guidance/segment_list.hpp"
|
||||
#include "guidance/textual_route_annotation.hpp"
|
||||
|
||||
#include "engine/internal_route_result.hpp"
|
||||
#include "engine/object_encoder.hpp"
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "engine/polyline_formatter.hpp"
|
||||
#include "engine/route_name_extraction.hpp"
|
||||
#include "engine/segment_information.hpp"
|
||||
#include "extractor/turn_instructions.hpp"
|
||||
#include "osrm/coordinate.hpp"
|
||||
#include "osrm/json_container.hpp"
|
||||
#include "osrm/route_parameters.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cmath>
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
struct Segment
|
||||
{
|
||||
uint32_t name_id;
|
||||
int32_t length;
|
||||
std::size_t position;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename DataFacadeT> class ApiResponseGenerator
|
||||
{
|
||||
public:
|
||||
using DataFacade = DataFacadeT;
|
||||
using Segments = guidance::SegmentList<DataFacade>;
|
||||
using Segment = detail::Segment;
|
||||
|
||||
ApiResponseGenerator(DataFacade *facade);
|
||||
|
||||
// This runs a full annotation, according to config.
|
||||
// The output is tailored to the viaroute plugin.
|
||||
void DescribeRoute(const RouteParameters &config,
|
||||
const InternalRouteResult &raw_route,
|
||||
util::json::Object &json_result);
|
||||
|
||||
// The following functions allow access to the different parts of the Describe Route
|
||||
// functionality.
|
||||
// For own responses, they can be used to generate only subsets of the information.
|
||||
// In the normal situation, Describe Route is the desired usecase.
|
||||
|
||||
// generate an overview of a raw route
|
||||
util::json::Object SummarizeRoute(const InternalRouteResult &raw_route,
|
||||
const Segments &segment_list) const;
|
||||
|
||||
// create an array containing all via-points/-indices used in the query
|
||||
util::json::Array ListViaPoints(const InternalRouteResult &raw_route) const;
|
||||
util::json::Array ListViaIndices(const Segments &segment_list) const;
|
||||
|
||||
util::json::Value GetGeometry(const bool return_encoded, const Segments &segments) const;
|
||||
|
||||
// TODO this dedicated creation seems unnecessary? Only used for route names
|
||||
std::vector<Segment> BuildRouteSegments(const Segments &segment_list) const;
|
||||
|
||||
// adds checksum and locations
|
||||
util::json::Object BuildHintData(const InternalRouteResult &raw_route) const;
|
||||
|
||||
private:
|
||||
// data access to translate ids back into names
|
||||
DataFacade *facade;
|
||||
};
|
||||
|
||||
template <typename DataFacadeT>
|
||||
ApiResponseGenerator<DataFacadeT>::ApiResponseGenerator(DataFacadeT *facade_)
|
||||
: facade(facade_)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
void ApiResponseGenerator<DataFacadeT>::DescribeRoute(const RouteParameters &config,
|
||||
const InternalRouteResult &raw_route,
|
||||
util::json::Object &json_result)
|
||||
{
|
||||
if (!raw_route.is_valid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
const constexpr bool ALLOW_SIMPLIFICATION = true;
|
||||
const constexpr bool EXTRACT_ROUTE = false;
|
||||
const constexpr bool EXTRACT_ALTERNATIVE = true;
|
||||
Segments segment_list(raw_route, EXTRACT_ROUTE, config.zoom_level, ALLOW_SIMPLIFICATION,
|
||||
facade);
|
||||
json_result.values["route_summary"] = SummarizeRoute(raw_route, segment_list);
|
||||
json_result.values["via_points"] = ListViaPoints(raw_route);
|
||||
json_result.values["via_indices"] = ListViaIndices(segment_list);
|
||||
|
||||
if (config.geometry)
|
||||
{
|
||||
json_result.values["route_geometry"] = GetGeometry(config.compression, segment_list);
|
||||
}
|
||||
|
||||
if (config.print_instructions)
|
||||
{
|
||||
json_result.values["route_instructions"] =
|
||||
guidance::AnnotateRoute(segment_list.Get(), facade);
|
||||
}
|
||||
|
||||
RouteNames route_names;
|
||||
|
||||
if (raw_route.has_alternative())
|
||||
{
|
||||
Segments alternate_segment_list(raw_route, EXTRACT_ALTERNATIVE, config.zoom_level,
|
||||
ALLOW_SIMPLIFICATION, facade);
|
||||
|
||||
// Alternative Route Summaries are stored in an array to (down the line) allow multiple
|
||||
// alternatives
|
||||
util::json::Array json_alternate_route_summary_array;
|
||||
json_alternate_route_summary_array.values.emplace_back(
|
||||
SummarizeRoute(raw_route, alternate_segment_list));
|
||||
json_result.values["alternative_summaries"] = json_alternate_route_summary_array;
|
||||
json_result.values["alternative_indices"] = ListViaIndices(alternate_segment_list);
|
||||
|
||||
if (config.geometry)
|
||||
{
|
||||
auto alternate_geometry_string =
|
||||
GetGeometry(config.compression, alternate_segment_list);
|
||||
util::json::Array json_alternate_geometries_array;
|
||||
json_alternate_geometries_array.values.emplace_back(
|
||||
std::move(alternate_geometry_string));
|
||||
json_result.values["alternative_geometries"] = json_alternate_geometries_array;
|
||||
}
|
||||
|
||||
if (config.print_instructions)
|
||||
{
|
||||
util::json::Array json_alternate_annotations_array;
|
||||
json_alternate_annotations_array.values.emplace_back(
|
||||
guidance::AnnotateRoute(alternate_segment_list.Get(), facade));
|
||||
json_result.values["alternative_instructions"] = json_alternate_annotations_array;
|
||||
}
|
||||
|
||||
// generate names for both the main path and the alternative route
|
||||
auto path_segments = BuildRouteSegments(segment_list);
|
||||
auto alternate_segments = BuildRouteSegments(alternate_segment_list);
|
||||
route_names = extractRouteNames(path_segments, alternate_segments, facade);
|
||||
|
||||
util::json::Array json_alternate_names_array;
|
||||
util::json::Array json_alternate_names;
|
||||
json_alternate_names.values.push_back(route_names.alternative_path_name_1);
|
||||
json_alternate_names.values.push_back(route_names.alternative_path_name_2);
|
||||
json_alternate_names_array.values.emplace_back(std::move(json_alternate_names));
|
||||
json_result.values["alternative_names"] = json_alternate_names_array;
|
||||
json_result.values["found_alternative"] = util::json::True();
|
||||
}
|
||||
else
|
||||
{
|
||||
json_result.values["found_alternative"] = util::json::False();
|
||||
// generate names for the main route on its own
|
||||
auto path_segments = BuildRouteSegments(segment_list);
|
||||
std::vector<detail::Segment> alternate_segments;
|
||||
route_names = extractRouteNames(path_segments, alternate_segments, facade);
|
||||
}
|
||||
|
||||
util::json::Array json_route_names;
|
||||
json_route_names.values.push_back(route_names.shortest_path_name_1);
|
||||
json_route_names.values.push_back(route_names.shortest_path_name_2);
|
||||
json_result.values["route_name"] = json_route_names;
|
||||
|
||||
json_result.values["hint_data"] = BuildHintData(raw_route);
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
util::json::Object
|
||||
ApiResponseGenerator<DataFacadeT>::SummarizeRoute(const InternalRouteResult &raw_route,
|
||||
const Segments &segment_list) const
|
||||
{
|
||||
util::json::Object json_route_summary;
|
||||
if (!raw_route.segment_end_coordinates.empty())
|
||||
{
|
||||
const auto start_name_id = raw_route.segment_end_coordinates.front().source_phantom.name_id;
|
||||
json_route_summary.values["start_point"] = facade->get_name_for_id(start_name_id);
|
||||
const auto destination_name_id =
|
||||
raw_route.segment_end_coordinates.back().target_phantom.name_id;
|
||||
json_route_summary.values["end_point"] = facade->get_name_for_id(destination_name_id);
|
||||
}
|
||||
json_route_summary.values["total_time"] = segment_list.GetDuration();
|
||||
json_route_summary.values["total_distance"] = segment_list.GetDistance();
|
||||
return json_route_summary;
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
util::json::Array
|
||||
ApiResponseGenerator<DataFacadeT>::ListViaPoints(const InternalRouteResult &raw_route) const
|
||||
{
|
||||
util::json::Array json_via_points_array;
|
||||
util::json::Array json_first_coordinate;
|
||||
json_first_coordinate.values.emplace_back(
|
||||
raw_route.segment_end_coordinates.front().source_phantom.location.lat /
|
||||
COORDINATE_PRECISION);
|
||||
json_first_coordinate.values.emplace_back(
|
||||
raw_route.segment_end_coordinates.front().source_phantom.location.lon /
|
||||
COORDINATE_PRECISION);
|
||||
json_via_points_array.values.emplace_back(std::move(json_first_coordinate));
|
||||
for (const PhantomNodes &nodes : raw_route.segment_end_coordinates)
|
||||
{
|
||||
std::string tmp;
|
||||
util::json::Array json_coordinate;
|
||||
json_coordinate.values.emplace_back(nodes.target_phantom.location.lat /
|
||||
COORDINATE_PRECISION);
|
||||
json_coordinate.values.emplace_back(nodes.target_phantom.location.lon /
|
||||
COORDINATE_PRECISION);
|
||||
json_via_points_array.values.emplace_back(std::move(json_coordinate));
|
||||
}
|
||||
return json_via_points_array;
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
util::json::Array
|
||||
ApiResponseGenerator<DataFacadeT>::ListViaIndices(const Segments &segment_list) const
|
||||
{
|
||||
util::json::Array via_indices;
|
||||
via_indices.values.insert(via_indices.values.end(), segment_list.GetViaIndices().begin(),
|
||||
segment_list.GetViaIndices().end());
|
||||
return via_indices;
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
util::json::Value ApiResponseGenerator<DataFacadeT>::GetGeometry(const bool return_encoded,
|
||||
const Segments &segments) const
|
||||
{
|
||||
if (return_encoded)
|
||||
return polylineEncodeAsJSON(segments.Get());
|
||||
else
|
||||
return polylineUnencodedAsJSON(segments.Get());
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
std::vector<detail::Segment>
|
||||
ApiResponseGenerator<DataFacadeT>::BuildRouteSegments(const Segments &segment_list) const
|
||||
{
|
||||
std::vector<detail::Segment> result;
|
||||
for (const auto &segment : segment_list.Get())
|
||||
{
|
||||
const auto current_turn = segment.turn_instruction;
|
||||
if (extractor::isTurnNecessary(current_turn) &&
|
||||
(extractor::TurnInstruction::EnterRoundAbout != current_turn))
|
||||
{
|
||||
|
||||
detail::Segment seg = {segment.name_id,
|
||||
static_cast<int32_t>(segment.length),
|
||||
static_cast<std::size_t>(result.size())};
|
||||
result.emplace_back(std::move(seg));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
util::json::Object
|
||||
ApiResponseGenerator<DataFacadeT>::BuildHintData(const InternalRouteResult &raw_route) const
|
||||
{
|
||||
util::json::Object json_hint_object;
|
||||
json_hint_object.values["checksum"] = facade->GetCheckSum();
|
||||
util::json::Array json_location_hint_array;
|
||||
std::string hint;
|
||||
for (const auto i : util::irange<std::size_t>(0, raw_route.segment_end_coordinates.size()))
|
||||
{
|
||||
hint = encodeBase64(raw_route.segment_end_coordinates[i].source_phantom);
|
||||
json_location_hint_array.values.push_back(std::move(hint));
|
||||
}
|
||||
hint = encodeBase64(raw_route.segment_end_coordinates.back().target_phantom);
|
||||
json_location_hint_array.values.emplace_back(std::move(hint));
|
||||
json_hint_object.values["locations"] = json_location_hint_array;
|
||||
|
||||
return json_hint_object;
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
ApiResponseGenerator<DataFacadeT> MakeApiResponseGenerator(DataFacadeT *facade)
|
||||
{
|
||||
return ApiResponseGenerator<DataFacadeT>(facade);
|
||||
}
|
||||
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif // ENGINE_GUIDANCE_API_RESPONSE_GENERATOR_HPP_
|
||||
@@ -0,0 +1,126 @@
|
||||
#ifndef OSRM_BASE64_HPP
|
||||
#define OSRM_BASE64_HPP
|
||||
|
||||
#include <string>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
#include <cstddef>
|
||||
#include <climits>
|
||||
|
||||
#include <boost/archive/iterators/binary_from_base64.hpp>
|
||||
#include <boost/archive/iterators/base64_from_binary.hpp>
|
||||
#include <boost/archive/iterators/transform_width.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/range/algorithm/copy.hpp>
|
||||
|
||||
// RFC 4648 "The Base16, Base32, and Base64 Data Encodings"
|
||||
// See: https://tools.ietf.org/html/rfc4648
|
||||
// Implementation adapted from: http://stackoverflow.com/a/28471421
|
||||
|
||||
// The C++ standard guarantees none of this by default, but we need it in the following.
|
||||
static_assert(CHAR_BIT == 8u, "we assume a byte holds 8 bits");
|
||||
static_assert(sizeof(char) == 1u, "we assume a char is one byte large");
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
|
||||
// Encoding Implementation
|
||||
|
||||
// Encodes a chunk of memory to Base64.
|
||||
inline std::string encodeBase64(const unsigned char *first, std::size_t size)
|
||||
{
|
||||
using namespace boost::archive::iterators;
|
||||
|
||||
const std::string bytes{first, first + size};
|
||||
|
||||
using Iter = base64_from_binary<transform_width<std::string::const_iterator, 6, 8>>;
|
||||
|
||||
Iter view_first{begin(bytes)};
|
||||
Iter view_last{end(bytes)};
|
||||
|
||||
std::string encoded{view_first, view_last};
|
||||
|
||||
return encoded.append((3 - size % 3) % 3, '=');
|
||||
}
|
||||
|
||||
// C++11 standard 3.9.1/1: Plain char, signed char, and unsigned char are three distinct types
|
||||
|
||||
// Overload for signed char catches (not only but also) C-string literals.
|
||||
inline std::string encodeBase64(const signed char *first, std::size_t size)
|
||||
{
|
||||
return encodeBase64(reinterpret_cast<const unsigned char *>(first), size);
|
||||
}
|
||||
|
||||
// Overload for char catches (not only but also) C-string literals.
|
||||
inline std::string encodeBase64(const char *first, std::size_t size)
|
||||
{
|
||||
return encodeBase64(reinterpret_cast<const unsigned char *>(first), size);
|
||||
}
|
||||
|
||||
// Convenience specialization, encoding from string instead of byte-dumping it.
|
||||
inline std::string encodeBase64(const std::string &x) { return encodeBase64(x.data(), x.size()); }
|
||||
|
||||
// Encode any sufficiently trivial object to Base64.
|
||||
template <typename T> std::string encodeBase64Bytewise(const T &x)
|
||||
{
|
||||
#if not defined __GNUC__ or __GNUC__ > 4
|
||||
static_assert(std::is_trivially_copyable<T>::value, "requires a trivially copyable type");
|
||||
#endif
|
||||
|
||||
return encodeBase64(reinterpret_cast<const unsigned char *>(&x), sizeof(T));
|
||||
}
|
||||
|
||||
// Decoding Implementation
|
||||
|
||||
// Decodes into a chunk of memory that is at least as large as the input.
|
||||
template <typename OutputIter> void decodeBase64(const std::string &encoded, OutputIter out)
|
||||
{
|
||||
using namespace boost::archive::iterators;
|
||||
using namespace boost::algorithm;
|
||||
|
||||
using Iter = transform_width<binary_from_base64<std::string::const_iterator>, 8, 6>;
|
||||
|
||||
Iter view_first{begin(encoded)};
|
||||
Iter view_last{end(encoded)};
|
||||
|
||||
const auto null = [](const unsigned char c)
|
||||
{
|
||||
return c == '\0';
|
||||
};
|
||||
|
||||
const auto bytes = trim_right_copy_if(std::string{view_first, view_last}, null);
|
||||
|
||||
boost::copy(bytes, out);
|
||||
}
|
||||
|
||||
// Convenience specialization, filling string instead of byte-dumping into it.
|
||||
inline std::string decodeBase64(const std::string &encoded)
|
||||
{
|
||||
std::string rv;
|
||||
|
||||
decodeBase64(encoded, std::back_inserter(rv));
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Decodes from Base 64 to any sufficiently trivial object.
|
||||
template <typename T> T decodeBase64Bytewise(const std::string &encoded)
|
||||
{
|
||||
#if not defined __GNUC__ or __GNUC__ > 4
|
||||
static_assert(std::is_trivially_copyable<T>::value, "requires a trivially copyable type");
|
||||
#endif
|
||||
|
||||
T x;
|
||||
|
||||
decodeBase64(encoded, reinterpret_cast<unsigned char *>(&x));
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
} // ns engine
|
||||
} // ns osrm
|
||||
|
||||
#endif /* OSRM_BASE64_HPP */
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef OSRM_ENGINE_BEARING_HPP
|
||||
#define OSRM_ENGINE_BEARING_HPP
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
|
||||
struct Bearing
|
||||
{
|
||||
short bearing;
|
||||
short range;
|
||||
|
||||
bool IsValid() const { return bearing >= 0 && bearing <= 360 && range >= 0 && range <= 180; }
|
||||
};
|
||||
|
||||
inline bool operator==(const Bearing lhs, const Bearing rhs)
|
||||
{
|
||||
return lhs.bearing == rhs.bearing && lhs.range == rhs.range;
|
||||
}
|
||||
inline bool operator!=(const Bearing lhs, const Bearing rhs) { return !(lhs == rhs); }
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -5,8 +5,9 @@
|
||||
|
||||
#include "extractor/edge_based_node.hpp"
|
||||
#include "extractor/external_memory_node.hpp"
|
||||
#include "contractor/query_edge.hpp"
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "extractor/turn_instructions.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/exception.hpp"
|
||||
#include "util/string_util.hpp"
|
||||
@@ -29,11 +30,11 @@ namespace datafacade
|
||||
|
||||
using EdgeRange = util::range<EdgeID>;
|
||||
|
||||
template <class EdgeDataT> class BaseDataFacade
|
||||
class BaseDataFacade
|
||||
{
|
||||
public:
|
||||
using EdgeData = contractor::QueryEdge::EdgeData;
|
||||
using RTreeLeaf = extractor::EdgeBasedNode;
|
||||
using EdgeData = EdgeDataT;
|
||||
BaseDataFacade() {}
|
||||
virtual ~BaseDataFacade() {}
|
||||
|
||||
@@ -46,7 +47,7 @@ template <class EdgeDataT> class BaseDataFacade
|
||||
|
||||
virtual NodeID GetTarget(const EdgeID e) const = 0;
|
||||
|
||||
virtual const EdgeDataT &GetEdgeData(const EdgeID e) const = 0;
|
||||
virtual const EdgeData &GetEdgeData(const EdgeID e) const = 0;
|
||||
|
||||
virtual EdgeID BeginEdges(const NodeID n) const = 0;
|
||||
|
||||
@@ -63,9 +64,7 @@ template <class EdgeDataT> class BaseDataFacade
|
||||
FindEdgeIndicateIfReverse(const NodeID from, const NodeID to, bool &result) const = 0;
|
||||
|
||||
// node and edge information access
|
||||
virtual util::FixedPointCoordinate GetCoordinateOfNode(const unsigned id) const = 0;
|
||||
|
||||
virtual bool EdgeIsCompressed(const unsigned id) const = 0;
|
||||
virtual util::Coordinate GetCoordinateOfNode(const unsigned id) const = 0;
|
||||
|
||||
virtual unsigned GetGeometryIndexForEdgeID(const unsigned id) const = 0;
|
||||
|
||||
@@ -75,31 +74,63 @@ template <class EdgeDataT> class BaseDataFacade
|
||||
// Gets the weight values for each segment in an uncompressed geometry.
|
||||
// Should always be 1 shorter than GetUncompressedGeometry
|
||||
virtual void GetUncompressedWeights(const EdgeID id,
|
||||
std::vector<EdgeWeight> &result_weights) const = 0;
|
||||
std::vector<EdgeWeight> &result_weights) const = 0;
|
||||
|
||||
virtual extractor::TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const = 0;
|
||||
// Returns the data source ids that were used to supply the edge
|
||||
// weights. Will return an empty array when only the base profile is used.
|
||||
virtual void GetUncompressedDatasources(const EdgeID id,
|
||||
std::vector<uint8_t> &data_sources) const = 0;
|
||||
|
||||
// Gets the name of a datasource
|
||||
virtual std::string GetDatasourceName(const uint8_t datasource_name_id) const = 0;
|
||||
|
||||
virtual extractor::guidance::TurnInstruction
|
||||
GetTurnInstructionForEdgeID(const unsigned id) const = 0;
|
||||
|
||||
virtual extractor::TravelMode GetTravelModeForEdgeID(const unsigned id) const = 0;
|
||||
|
||||
virtual std::vector<RTreeLeaf> GetEdgesInBox(const util::FixedPointCoordinate &south_west,
|
||||
const util::FixedPointCoordinate &north_east) = 0;
|
||||
virtual std::vector<RTreeLeaf> GetEdgesInBox(const util::Coordinate south_west,
|
||||
const util::Coordinate north_east) = 0;
|
||||
|
||||
virtual std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::FixedPointCoordinate input_coordinate,
|
||||
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
|
||||
const float max_distance,
|
||||
const int bearing = 0,
|
||||
const int bearing_range = 180) = 0;
|
||||
const int bearing,
|
||||
const int bearing_range) = 0;
|
||||
virtual std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
|
||||
const float max_distance) = 0;
|
||||
|
||||
virtual std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate,
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const int bearing = 0,
|
||||
const int bearing_range = 180) = 0;
|
||||
const double max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range) = 0;
|
||||
virtual std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const int bearing,
|
||||
const int bearing_range) = 0;
|
||||
virtual std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate, const unsigned max_results) = 0;
|
||||
virtual std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const double max_distance) = 0;
|
||||
|
||||
virtual std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate) = 0;
|
||||
virtual std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const double max_distance) = 0;
|
||||
virtual std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const double max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range) = 0;
|
||||
virtual std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
const util::FixedPointCoordinate input_coordinate,
|
||||
const int bearing = 0,
|
||||
const int bearing_range = 180) = 0;
|
||||
const util::Coordinate input_coordinate, const int bearing, const int bearing_range) = 0;
|
||||
|
||||
virtual unsigned GetCheckSum() const = 0;
|
||||
|
||||
@@ -107,11 +138,13 @@ template <class EdgeDataT> class BaseDataFacade
|
||||
|
||||
virtual unsigned GetNameIndexFromEdgeID(const unsigned id) const = 0;
|
||||
|
||||
virtual std::string get_name_for_id(const unsigned name_id) const = 0;
|
||||
virtual std::string GetNameForID(const unsigned name_id) const = 0;
|
||||
|
||||
virtual std::size_t GetCoreSize() const = 0;
|
||||
|
||||
virtual std::string GetTimestamp() const = 0;
|
||||
|
||||
virtual bool GetUTurnsDefault() const = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
|
||||
#include "engine/geospatial_query.hpp"
|
||||
#include "extractor/original_edge_data.hpp"
|
||||
#include "extractor/profile_properties.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
#include "contractor/query_edge.hpp"
|
||||
#include "util/shared_memory_vector_wrapper.hpp"
|
||||
@@ -46,17 +49,17 @@ namespace engine
|
||||
namespace datafacade
|
||||
{
|
||||
|
||||
template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacade<EdgeDataT>
|
||||
class InternalDataFacade final : public BaseDataFacade
|
||||
{
|
||||
|
||||
private:
|
||||
using super = BaseDataFacade<EdgeDataT>;
|
||||
using super = BaseDataFacade;
|
||||
using QueryGraph = util::StaticGraph<typename super::EdgeData>;
|
||||
using InputEdge = typename QueryGraph::InputEdge;
|
||||
using RTreeLeaf = typename super::RTreeLeaf;
|
||||
using InternalRTree =
|
||||
util::StaticRTree<RTreeLeaf, util::ShM<util::FixedPointCoordinate, false>::vector, false>;
|
||||
using InternalGeospatialQuery = GeospatialQuery<InternalRTree, BaseDataFacade<EdgeDataT>>;
|
||||
util::StaticRTree<RTreeLeaf, util::ShM<util::Coordinate, false>::vector, false>;
|
||||
using InternalGeospatialQuery = GeospatialQuery<InternalRTree, BaseDataFacade>;
|
||||
|
||||
InternalDataFacade() {}
|
||||
|
||||
@@ -65,17 +68,19 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
std::unique_ptr<QueryGraph> m_query_graph;
|
||||
std::string m_timestamp;
|
||||
|
||||
std::shared_ptr<util::ShM<util::FixedPointCoordinate, false>::vector> m_coordinate_list;
|
||||
std::shared_ptr<util::ShM<util::Coordinate, false>::vector> m_coordinate_list;
|
||||
util::ShM<NodeID, false>::vector m_via_node_list;
|
||||
util::ShM<unsigned, false>::vector m_name_ID_list;
|
||||
util::ShM<extractor::TurnInstruction, false>::vector m_turn_instruction_list;
|
||||
util::ShM<extractor::guidance::TurnInstruction, false>::vector m_turn_instruction_list;
|
||||
util::ShM<extractor::TravelMode, false>::vector m_travel_mode_list;
|
||||
util::ShM<char, false>::vector m_names_char_list;
|
||||
util::ShM<bool, false>::vector m_edge_is_compressed;
|
||||
util::ShM<unsigned, false>::vector m_geometry_indices;
|
||||
util::ShM<extractor::CompressedEdgeContainer::CompressedEdge, false>::vector m_geometry_list;
|
||||
util::ShM<bool, false>::vector m_is_core_node;
|
||||
util::ShM<unsigned, false>::vector m_segment_weights;
|
||||
util::ShM<uint8_t, false>::vector m_datasource_list;
|
||||
util::ShM<std::string, false>::vector m_datasource_names;
|
||||
extractor::ProfileProperties m_profile_properties;
|
||||
|
||||
boost::thread_specific_ptr<InternalRTree> m_static_rtree;
|
||||
boost::thread_specific_ptr<InternalGeospatialQuery> m_geospatial_query;
|
||||
@@ -83,26 +88,26 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
boost::filesystem::path file_index_path;
|
||||
util::RangeTable<16, false> m_name_table;
|
||||
|
||||
void LoadProfileProperties(const boost::filesystem::path &properties_path)
|
||||
{
|
||||
boost::filesystem::ifstream in_stream(properties_path);
|
||||
if (!in_stream)
|
||||
{
|
||||
throw util::exception("Could not open " + properties_path.string() + " for reading.");
|
||||
}
|
||||
|
||||
in_stream.read(reinterpret_cast<char*>(&m_profile_properties), sizeof(m_profile_properties));
|
||||
}
|
||||
|
||||
void LoadTimestamp(const boost::filesystem::path ×tamp_path)
|
||||
{
|
||||
if (boost::filesystem::exists(timestamp_path))
|
||||
util::SimpleLogger().Write() << "Loading Timestamp";
|
||||
boost::filesystem::ifstream timestamp_stream(timestamp_path);
|
||||
if (!timestamp_stream)
|
||||
{
|
||||
util::SimpleLogger().Write() << "Loading Timestamp";
|
||||
boost::filesystem::ifstream timestamp_stream(timestamp_path);
|
||||
if (!timestamp_stream)
|
||||
{
|
||||
util::SimpleLogger().Write(logWARNING) << timestamp_path << " not found";
|
||||
}
|
||||
getline(timestamp_stream, m_timestamp);
|
||||
}
|
||||
if (m_timestamp.empty())
|
||||
{
|
||||
m_timestamp = "n/a";
|
||||
}
|
||||
if (25 < m_timestamp.length())
|
||||
{
|
||||
m_timestamp.resize(25);
|
||||
throw util::exception("Could not open " + timestamp_path.string() + " for reading.");
|
||||
}
|
||||
getline(timestamp_stream, m_timestamp);
|
||||
}
|
||||
|
||||
void LoadGraph(const boost::filesystem::path &hsgr_path)
|
||||
@@ -133,15 +138,12 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
extractor::QueryNode current_node;
|
||||
unsigned number_of_coordinates = 0;
|
||||
nodes_input_stream.read((char *)&number_of_coordinates, sizeof(unsigned));
|
||||
m_coordinate_list =
|
||||
std::make_shared<std::vector<util::FixedPointCoordinate>>(number_of_coordinates);
|
||||
m_coordinate_list = std::make_shared<std::vector<util::Coordinate>>(number_of_coordinates);
|
||||
for (unsigned i = 0; i < number_of_coordinates; ++i)
|
||||
{
|
||||
nodes_input_stream.read((char *)¤t_node, sizeof(extractor::QueryNode));
|
||||
m_coordinate_list->at(i) =
|
||||
util::FixedPointCoordinate(current_node.lat, current_node.lon);
|
||||
BOOST_ASSERT((std::abs(m_coordinate_list->at(i).lat) >> 30) == 0);
|
||||
BOOST_ASSERT((std::abs(m_coordinate_list->at(i).lon) >> 30) == 0);
|
||||
m_coordinate_list->at(i) = util::Coordinate(current_node.lon, current_node.lat);
|
||||
BOOST_ASSERT(m_coordinate_list->at(i).IsValid());
|
||||
}
|
||||
|
||||
boost::filesystem::ifstream edges_input_stream(edges_file, std::ios::binary);
|
||||
@@ -151,9 +153,6 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
m_name_ID_list.resize(number_of_edges);
|
||||
m_turn_instruction_list.resize(number_of_edges);
|
||||
m_travel_mode_list.resize(number_of_edges);
|
||||
m_edge_is_compressed.resize(number_of_edges);
|
||||
|
||||
unsigned compressed = 0;
|
||||
|
||||
extractor::OriginalEdgeData current_edge_data;
|
||||
for (unsigned i = 0; i < number_of_edges; ++i)
|
||||
@@ -164,11 +163,6 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
m_name_ID_list[i] = current_edge_data.name_id;
|
||||
m_turn_instruction_list[i] = current_edge_data.turn_instruction;
|
||||
m_travel_mode_list[i] = current_edge_data.travel_mode;
|
||||
m_edge_is_compressed[i] = current_edge_data.compressed_geometry;
|
||||
if (m_edge_is_compressed[i])
|
||||
{
|
||||
++compressed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +212,41 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
if (number_of_compressed_geometries > 0)
|
||||
{
|
||||
geometry_stream.read((char *)&(m_geometry_list[0]),
|
||||
number_of_compressed_geometries * sizeof(extractor::CompressedEdgeContainer::CompressedEdge));
|
||||
number_of_compressed_geometries *
|
||||
sizeof(extractor::CompressedEdgeContainer::CompressedEdge));
|
||||
}
|
||||
}
|
||||
|
||||
void LoadDatasourceInfo(const boost::filesystem::path &datasource_names_file,
|
||||
const boost::filesystem::path &datasource_indexes_file)
|
||||
{
|
||||
boost::filesystem::ifstream datasources_stream(datasource_indexes_file, std::ios::binary);
|
||||
if (!datasources_stream)
|
||||
{
|
||||
throw util::exception("Could not open " + datasource_indexes_file.string() + " for reading!");
|
||||
}
|
||||
BOOST_ASSERT(datasources_stream);
|
||||
|
||||
std::size_t number_of_datasources = 0;
|
||||
datasources_stream.read(reinterpret_cast<char *>(&number_of_datasources),
|
||||
sizeof(std::size_t));
|
||||
if (number_of_datasources > 0)
|
||||
{
|
||||
m_datasource_list.resize(number_of_datasources);
|
||||
datasources_stream.read(reinterpret_cast<char *>(&(m_datasource_list[0])),
|
||||
number_of_datasources * sizeof(uint8_t));
|
||||
}
|
||||
|
||||
boost::filesystem::ifstream datasourcenames_stream(datasource_names_file, std::ios::binary);
|
||||
if (!datasourcenames_stream)
|
||||
{
|
||||
throw util::exception("Could not open " + datasource_names_file.string() + " for reading!");
|
||||
}
|
||||
BOOST_ASSERT(datasourcenames_stream);
|
||||
std::string name;
|
||||
while (std::getline(datasourcenames_stream, name))
|
||||
{
|
||||
m_datasource_names.push_back(std::move(name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +255,8 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
BOOST_ASSERT_MSG(!m_coordinate_list->empty(), "coordinates must be loaded before r-tree");
|
||||
|
||||
m_static_rtree.reset(new InternalRTree(ram_index_path, file_index_path, m_coordinate_list));
|
||||
m_geospatial_query.reset(new InternalGeospatialQuery(*m_static_rtree, m_coordinate_list, *this));
|
||||
m_geospatial_query.reset(
|
||||
new InternalGeospatialQuery(*m_static_rtree, m_coordinate_list, *this));
|
||||
}
|
||||
|
||||
void LoadStreetNames(const boost::filesystem::path &names_file)
|
||||
@@ -254,40 +283,35 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
m_geospatial_query.reset();
|
||||
}
|
||||
|
||||
explicit InternalDataFacade(
|
||||
const std::unordered_map<std::string, boost::filesystem::path> &server_paths)
|
||||
explicit InternalDataFacade(const storage::StorageConfig& config)
|
||||
{
|
||||
// cache end iterator to quickly check .find against
|
||||
const auto end_it = end(server_paths);
|
||||
|
||||
const auto file_for = [&server_paths, &end_it](const std::string &path)
|
||||
{
|
||||
const auto it = server_paths.find(path);
|
||||
if (it == end_it || !boost::filesystem::is_regular_file(it->second))
|
||||
throw util::exception("no valid " + path + " file given in ini file");
|
||||
return it->second;
|
||||
};
|
||||
|
||||
ram_index_path = file_for("ramindex");
|
||||
file_index_path = file_for("fileindex");
|
||||
ram_index_path = config.ram_index_path;
|
||||
file_index_path = config.file_index_path;
|
||||
|
||||
util::SimpleLogger().Write() << "loading graph data";
|
||||
LoadGraph(file_for("hsgrdata"));
|
||||
LoadGraph(config.hsgr_data_path);
|
||||
|
||||
util::SimpleLogger().Write() << "loading edge information";
|
||||
LoadNodeAndEdgeInformation(file_for("nodesdata"), file_for("edgesdata"));
|
||||
LoadNodeAndEdgeInformation(config.nodes_data_path, config.edges_data_path);
|
||||
|
||||
util::SimpleLogger().Write() << "loading core information";
|
||||
LoadCoreInformation(file_for("coredata"));
|
||||
LoadCoreInformation(config.core_data_path);
|
||||
|
||||
util::SimpleLogger().Write() << "loading geometries";
|
||||
LoadGeometries(file_for("geometries"));
|
||||
LoadGeometries(config.geometries_path);
|
||||
|
||||
util::SimpleLogger().Write() << "loading datasource info";
|
||||
LoadDatasourceInfo(config.datasource_names_path,
|
||||
config.datasource_indexes_path);
|
||||
|
||||
util::SimpleLogger().Write() << "loading timestamp";
|
||||
LoadTimestamp(file_for("timestamp"));
|
||||
LoadTimestamp(config.timestamp_path);
|
||||
|
||||
util::SimpleLogger().Write() << "loading profile properties";
|
||||
LoadProfileProperties(config.properties_path);
|
||||
|
||||
util::SimpleLogger().Write() << "loading street names";
|
||||
LoadStreetNames(file_for("namesdata"));
|
||||
LoadStreetNames(config.names_data_path);
|
||||
}
|
||||
|
||||
// search graph access
|
||||
@@ -302,7 +326,7 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
|
||||
NodeID GetTarget(const EdgeID e) const override final { return m_query_graph->GetTarget(e); }
|
||||
|
||||
EdgeDataT &GetEdgeData(const EdgeID e) const override final
|
||||
EdgeData &GetEdgeData(const EdgeID e) const override final
|
||||
{
|
||||
return m_query_graph->GetEdgeData(e);
|
||||
}
|
||||
@@ -334,17 +358,13 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
}
|
||||
|
||||
// node and edge information access
|
||||
util::FixedPointCoordinate GetCoordinateOfNode(const unsigned id) const override final
|
||||
util::Coordinate GetCoordinateOfNode(const unsigned id) const override final
|
||||
{
|
||||
return m_coordinate_list->at(id);
|
||||
}
|
||||
|
||||
bool EdgeIsCompressed(const unsigned id) const override final
|
||||
{
|
||||
return m_edge_is_compressed.at(id);
|
||||
}
|
||||
|
||||
extractor::TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const override final
|
||||
extractor::guidance::TurnInstruction
|
||||
GetTurnInstructionForEdgeID(const unsigned id) const override final
|
||||
{
|
||||
return m_turn_instruction_list.at(id);
|
||||
}
|
||||
@@ -354,25 +374,37 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
return m_travel_mode_list.at(id);
|
||||
}
|
||||
|
||||
std::vector<RTreeLeaf>
|
||||
GetEdgesInBox(const util::FixedPointCoordinate &south_west,
|
||||
const util::FixedPointCoordinate &north_east) override final
|
||||
std::vector<RTreeLeaf> GetEdgesInBox(const util::Coordinate south_west,
|
||||
const util::Coordinate north_east) override final
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
const util::RectangleInt2D bbox{
|
||||
south_west.lon, north_east.lon, south_west.lat, north_east.lat};
|
||||
const util::RectangleInt2D bbox{south_west.lon, north_east.lon, south_west.lat,
|
||||
north_east.lat};
|
||||
return m_geospatial_query->Search(bbox);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::FixedPointCoordinate input_coordinate,
|
||||
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
|
||||
const float max_distance) override final
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
|
||||
const float max_distance,
|
||||
const int bearing = 0,
|
||||
const int bearing_range = 180) override final
|
||||
const int bearing,
|
||||
const int bearing_range) override final
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
@@ -385,10 +417,37 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate,
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results) override final
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const int bearing = 0,
|
||||
const int bearing_range = 180) override final
|
||||
const double max_distance) override final
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const int bearing,
|
||||
const int bearing_range) override final
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
@@ -400,10 +459,70 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
bearing_range);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const double max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range) override final
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance,
|
||||
bearing, bearing_range);
|
||||
}
|
||||
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const double max_distance) override final
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
input_coordinate, max_distance);
|
||||
}
|
||||
|
||||
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
const util::FixedPointCoordinate input_coordinate,
|
||||
const int bearing = 0,
|
||||
const int bearing_range = 180) override final
|
||||
const util::Coordinate input_coordinate) override final
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
input_coordinate);
|
||||
}
|
||||
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const double max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range) override final
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
input_coordinate, max_distance, bearing, bearing_range);
|
||||
}
|
||||
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const int bearing,
|
||||
const int bearing_range) override final
|
||||
{
|
||||
if (!m_static_rtree.get())
|
||||
{
|
||||
@@ -422,7 +541,7 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
return m_name_ID_list.at(id);
|
||||
}
|
||||
|
||||
std::string get_name_for_id(const unsigned name_id) const override final
|
||||
std::string GetNameForID(const unsigned name_id) const override final
|
||||
{
|
||||
if (std::numeric_limits<unsigned>::max() == name_id)
|
||||
{
|
||||
@@ -468,22 +587,69 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
|
||||
result_nodes.clear();
|
||||
result_nodes.reserve(end - begin);
|
||||
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge){ result_nodes.emplace_back(edge.node_id); });
|
||||
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
|
||||
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
|
||||
{
|
||||
result_nodes.emplace_back(edge.node_id);
|
||||
});
|
||||
}
|
||||
|
||||
virtual void GetUncompressedWeights(const EdgeID id,
|
||||
std::vector<EdgeWeight> &result_weights) const override final
|
||||
virtual void
|
||||
GetUncompressedWeights(const EdgeID id,
|
||||
std::vector<EdgeWeight> &result_weights) const override final
|
||||
{
|
||||
const unsigned begin = m_geometry_indices.at(id);
|
||||
const unsigned end = m_geometry_indices.at(id + 1);
|
||||
|
||||
result_weights.clear();
|
||||
result_weights.reserve(end - begin);
|
||||
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge){ result_weights.emplace_back(edge.weight); });
|
||||
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
|
||||
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
|
||||
{
|
||||
result_weights.emplace_back(edge.weight);
|
||||
});
|
||||
}
|
||||
|
||||
// Returns the data source ids that were used to supply the edge
|
||||
// weights.
|
||||
virtual void
|
||||
GetUncompressedDatasources(const EdgeID id,
|
||||
std::vector<uint8_t> &result_datasources) const override final
|
||||
{
|
||||
const unsigned begin = m_geometry_indices.at(id);
|
||||
const unsigned end = m_geometry_indices.at(id + 1);
|
||||
|
||||
result_datasources.clear();
|
||||
result_datasources.reserve(end - begin);
|
||||
|
||||
// If there was no datasource info, return an array of 0's.
|
||||
if (m_datasource_list.empty())
|
||||
{
|
||||
for (unsigned i = 0; i < end - begin; ++i)
|
||||
{
|
||||
result_datasources.push_back(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::for_each(m_datasource_list.begin() + begin, m_datasource_list.begin() + end,
|
||||
[&](const uint8_t &datasource_id)
|
||||
{
|
||||
result_datasources.push_back(datasource_id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string GetDatasourceName(const uint8_t datasource_name_id) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_datasource_names.size() >= 1);
|
||||
BOOST_ASSERT(m_datasource_names.size() > datasource_name_id);
|
||||
return m_datasource_names[datasource_name_id];
|
||||
}
|
||||
|
||||
std::string GetTimestamp() const override final { return m_timestamp; }
|
||||
|
||||
bool GetUTurnsDefault() const override final { return m_profile_properties.allow_u_turn_at_via; }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#include "storage/shared_datatype.hpp"
|
||||
#include "storage/shared_memory.hpp"
|
||||
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "extractor/profile_properties.hpp"
|
||||
|
||||
#include "engine/geospatial_query.hpp"
|
||||
#include "util/range_table.hpp"
|
||||
#include "util/static_graph.hpp"
|
||||
@@ -36,12 +39,11 @@ namespace engine
|
||||
namespace datafacade
|
||||
{
|
||||
|
||||
template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<EdgeDataT>
|
||||
class SharedDataFacade final : public BaseDataFacade
|
||||
{
|
||||
|
||||
private:
|
||||
using EdgeData = EdgeDataT;
|
||||
using super = BaseDataFacade<EdgeData>;
|
||||
using super = BaseDataFacade;
|
||||
using QueryGraph = util::StaticGraph<EdgeData, true>;
|
||||
using GraphNode = typename QueryGraph::NodeArrayEntry;
|
||||
using GraphEdge = typename QueryGraph::EdgeArrayEntry;
|
||||
@@ -49,8 +51,8 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
using InputEdge = typename QueryGraph::InputEdge;
|
||||
using RTreeLeaf = typename super::RTreeLeaf;
|
||||
using SharedRTree =
|
||||
util::StaticRTree<RTreeLeaf, util::ShM<util::FixedPointCoordinate, true>::vector, true>;
|
||||
using SharedGeospatialQuery = GeospatialQuery<SharedRTree, BaseDataFacade<EdgeDataT>>;
|
||||
util::StaticRTree<RTreeLeaf, util::ShM<util::Coordinate, true>::vector, true>;
|
||||
using SharedGeospatialQuery = GeospatialQuery<SharedRTree, BaseDataFacade>;
|
||||
using TimeStampedRTreePair = std::pair<unsigned, std::shared_ptr<SharedRTree>>;
|
||||
using RTreeNode = typename SharedRTree::TreeNode;
|
||||
|
||||
@@ -67,18 +69,23 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
std::unique_ptr<storage::SharedMemory> m_layout_memory;
|
||||
std::unique_ptr<storage::SharedMemory> m_large_memory;
|
||||
std::string m_timestamp;
|
||||
extractor::ProfileProperties* m_profile_properties;
|
||||
|
||||
std::shared_ptr<util::ShM<util::FixedPointCoordinate, true>::vector> m_coordinate_list;
|
||||
std::shared_ptr<util::ShM<util::Coordinate, true>::vector> m_coordinate_list;
|
||||
util::ShM<NodeID, true>::vector m_via_node_list;
|
||||
util::ShM<unsigned, true>::vector m_name_ID_list;
|
||||
util::ShM<extractor::TurnInstruction, true>::vector m_turn_instruction_list;
|
||||
util::ShM<extractor::guidance::TurnInstruction, true>::vector m_turn_instruction_list;
|
||||
util::ShM<extractor::TravelMode, true>::vector m_travel_mode_list;
|
||||
util::ShM<char, true>::vector m_names_char_list;
|
||||
util::ShM<unsigned, true>::vector m_name_begin_indices;
|
||||
util::ShM<bool, true>::vector m_edge_is_compressed;
|
||||
util::ShM<unsigned, true>::vector m_geometry_indices;
|
||||
util::ShM<extractor::CompressedEdgeContainer::CompressedEdge, true>::vector m_geometry_list;
|
||||
util::ShM<bool, true>::vector m_is_core_node;
|
||||
util::ShM<uint8_t, true>::vector m_datasource_list;
|
||||
|
||||
util::ShM<char, true>::vector m_datasource_name_data;
|
||||
util::ShM<std::size_t, true>::vector m_datasource_name_offsets;
|
||||
util::ShM<std::size_t, true>::vector m_datasource_name_lengths;
|
||||
|
||||
boost::thread_specific_ptr<std::pair<unsigned, std::shared_ptr<SharedRTree>>> m_static_rtree;
|
||||
boost::thread_specific_ptr<SharedGeospatialQuery> m_geospatial_query;
|
||||
@@ -93,6 +100,12 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
util::SimpleLogger().Write() << "set checksum: " << m_check_sum;
|
||||
}
|
||||
|
||||
void LoadProfileProperties()
|
||||
{
|
||||
m_profile_properties =
|
||||
data_layout->GetBlockPtr<extractor::ProfileProperties>(shared_memory, storage::SharedDataLayout::PROPERTIES);
|
||||
}
|
||||
|
||||
void LoadTimestamp()
|
||||
{
|
||||
auto timestamp_ptr =
|
||||
@@ -135,9 +148,9 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
|
||||
void LoadNodeAndEdgeInformation()
|
||||
{
|
||||
auto coordinate_list_ptr = data_layout->GetBlockPtr<util::FixedPointCoordinate>(
|
||||
auto coordinate_list_ptr = data_layout->GetBlockPtr<util::Coordinate>(
|
||||
shared_memory, storage::SharedDataLayout::COORDINATE_LIST);
|
||||
m_coordinate_list = util::make_unique<util::ShM<util::FixedPointCoordinate, true>::vector>(
|
||||
m_coordinate_list = util::make_unique<util::ShM<util::Coordinate, true>::vector>(
|
||||
coordinate_list_ptr,
|
||||
data_layout->num_entries[storage::SharedDataLayout::COORDINATE_LIST]);
|
||||
|
||||
@@ -147,11 +160,13 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
travel_mode_list_ptr, data_layout->num_entries[storage::SharedDataLayout::TRAVEL_MODE]);
|
||||
m_travel_mode_list = std::move(travel_mode_list);
|
||||
|
||||
auto turn_instruction_list_ptr = data_layout->GetBlockPtr<extractor::TurnInstruction>(
|
||||
shared_memory, storage::SharedDataLayout::TURN_INSTRUCTION);
|
||||
typename util::ShM<extractor::TurnInstruction, true>::vector turn_instruction_list(
|
||||
turn_instruction_list_ptr,
|
||||
data_layout->num_entries[storage::SharedDataLayout::TURN_INSTRUCTION]);
|
||||
auto turn_instruction_list_ptr =
|
||||
data_layout->GetBlockPtr<extractor::guidance::TurnInstruction>(
|
||||
shared_memory, storage::SharedDataLayout::TURN_INSTRUCTION);
|
||||
typename util::ShM<extractor::guidance::TurnInstruction, true>::vector
|
||||
turn_instruction_list(
|
||||
turn_instruction_list_ptr,
|
||||
data_layout->num_entries[storage::SharedDataLayout::TURN_INSTRUCTION]);
|
||||
m_turn_instruction_list = std::move(turn_instruction_list);
|
||||
|
||||
auto name_id_list_ptr = data_layout->GetBlockPtr<unsigned>(
|
||||
@@ -207,13 +222,6 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
|
||||
void LoadGeometries()
|
||||
{
|
||||
auto geometries_compressed_ptr = data_layout->GetBlockPtr<unsigned>(
|
||||
shared_memory, storage::SharedDataLayout::GEOMETRIES_INDICATORS);
|
||||
typename util::ShM<bool, true>::vector edge_is_compressed(
|
||||
geometries_compressed_ptr,
|
||||
data_layout->num_entries[storage::SharedDataLayout::GEOMETRIES_INDICATORS]);
|
||||
m_edge_is_compressed = std::move(edge_is_compressed);
|
||||
|
||||
auto geometries_index_ptr = data_layout->GetBlockPtr<unsigned>(
|
||||
shared_memory, storage::SharedDataLayout::GEOMETRIES_INDEX);
|
||||
typename util::ShM<unsigned, true>::vector geometry_begin_indices(
|
||||
@@ -223,11 +231,39 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
|
||||
auto geometries_list_ptr =
|
||||
data_layout->GetBlockPtr<extractor::CompressedEdgeContainer::CompressedEdge>(
|
||||
shared_memory, storage::SharedDataLayout::GEOMETRIES_LIST);
|
||||
typename util::ShM<extractor::CompressedEdgeContainer::CompressedEdge, true>::vector geometry_list(
|
||||
geometries_list_ptr,
|
||||
data_layout->num_entries[storage::SharedDataLayout::GEOMETRIES_LIST]);
|
||||
shared_memory, storage::SharedDataLayout::GEOMETRIES_LIST);
|
||||
typename util::ShM<extractor::CompressedEdgeContainer::CompressedEdge, true>::vector
|
||||
geometry_list(geometries_list_ptr,
|
||||
data_layout->num_entries[storage::SharedDataLayout::GEOMETRIES_LIST]);
|
||||
m_geometry_list = std::move(geometry_list);
|
||||
|
||||
auto datasources_list_ptr = data_layout->GetBlockPtr<uint8_t>(
|
||||
shared_memory, storage::SharedDataLayout::DATASOURCES_LIST);
|
||||
typename util::ShM<uint8_t, true>::vector datasources_list(
|
||||
datasources_list_ptr,
|
||||
data_layout->num_entries[storage::SharedDataLayout::DATASOURCES_LIST]);
|
||||
m_datasource_list = std::move(datasources_list);
|
||||
|
||||
auto datasource_name_data_ptr = data_layout->GetBlockPtr<char>(
|
||||
shared_memory, storage::SharedDataLayout::DATASOURCE_NAME_DATA);
|
||||
typename util::ShM<char, true>::vector datasource_name_data(
|
||||
datasource_name_data_ptr,
|
||||
data_layout->num_entries[storage::SharedDataLayout::DATASOURCE_NAME_DATA]);
|
||||
m_datasource_name_data = std::move(datasource_name_data);
|
||||
|
||||
auto datasource_name_offsets_ptr = data_layout->GetBlockPtr<std::size_t>(
|
||||
shared_memory, storage::SharedDataLayout::DATASOURCE_NAME_OFFSETS);
|
||||
typename util::ShM<std::size_t, true>::vector datasource_name_offsets(
|
||||
datasource_name_offsets_ptr,
|
||||
data_layout->num_entries[storage::SharedDataLayout::DATASOURCE_NAME_OFFSETS]);
|
||||
m_datasource_name_offsets = std::move(datasource_name_offsets);
|
||||
|
||||
auto datasource_name_lengths_ptr = data_layout->GetBlockPtr<std::size_t>(
|
||||
shared_memory, storage::SharedDataLayout::DATASOURCE_NAME_LENGTHS);
|
||||
typename util::ShM<std::size_t, true>::vector datasource_name_lengths(
|
||||
datasource_name_lengths_ptr,
|
||||
data_layout->num_entries[storage::SharedDataLayout::DATASOURCE_NAME_LENGTHS]);
|
||||
m_datasource_name_lengths = std::move(datasource_name_lengths);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -244,7 +280,8 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
}
|
||||
data_timestamp_ptr = static_cast<storage::SharedDataTimestamp *>(
|
||||
storage::makeSharedMemory(storage::CURRENT_REGIONS,
|
||||
sizeof(storage::SharedDataTimestamp), false, false)->Ptr());
|
||||
sizeof(storage::SharedDataTimestamp), false, false)
|
||||
->Ptr());
|
||||
CURRENT_LAYOUT = storage::LAYOUT_NONE;
|
||||
CURRENT_DATA = storage::DATA_NONE;
|
||||
CURRENT_TIMESTAMP = 0;
|
||||
@@ -314,9 +351,10 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
LoadViaNodeList();
|
||||
LoadNames();
|
||||
LoadCoreInformation();
|
||||
LoadProfileProperties();
|
||||
|
||||
util::SimpleLogger().Write()
|
||||
<< "number of geometries: " << m_coordinate_list->size();
|
||||
util::SimpleLogger().Write() << "number of geometries: "
|
||||
<< m_coordinate_list->size();
|
||||
for (unsigned i = 0; i < m_coordinate_list->size(); ++i)
|
||||
{
|
||||
if (!GetCoordinateOfNode(i).IsValid())
|
||||
@@ -341,7 +379,7 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
|
||||
NodeID GetTarget(const EdgeID e) const override final { return m_query_graph->GetTarget(e); }
|
||||
|
||||
EdgeDataT &GetEdgeData(const EdgeID e) const override final
|
||||
EdgeData &GetEdgeData(const EdgeID e) const override final
|
||||
{
|
||||
return m_query_graph->GetEdgeData(e);
|
||||
}
|
||||
@@ -373,16 +411,11 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
}
|
||||
|
||||
// node and edge information access
|
||||
util::FixedPointCoordinate GetCoordinateOfNode(const NodeID id) const override final
|
||||
util::Coordinate GetCoordinateOfNode(const NodeID id) const override final
|
||||
{
|
||||
return m_coordinate_list->at(id);
|
||||
}
|
||||
|
||||
virtual bool EdgeIsCompressed(const unsigned id) const override final
|
||||
{
|
||||
return m_edge_is_compressed.at(id);
|
||||
}
|
||||
|
||||
virtual void GetUncompressedGeometry(const EdgeID id,
|
||||
std::vector<NodeID> &result_nodes) const override final
|
||||
{
|
||||
@@ -391,19 +424,27 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
|
||||
result_nodes.clear();
|
||||
result_nodes.reserve(end - begin);
|
||||
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge){ result_nodes.emplace_back(edge.node_id); });
|
||||
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
|
||||
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
|
||||
{
|
||||
result_nodes.emplace_back(edge.node_id);
|
||||
});
|
||||
}
|
||||
|
||||
virtual void GetUncompressedWeights(const EdgeID id,
|
||||
std::vector<EdgeWeight> &result_weights) const override final
|
||||
virtual void
|
||||
GetUncompressedWeights(const EdgeID id,
|
||||
std::vector<EdgeWeight> &result_weights) const override final
|
||||
{
|
||||
const unsigned begin = m_geometry_indices.at(id);
|
||||
const unsigned end = m_geometry_indices.at(id + 1);
|
||||
|
||||
result_weights.clear();
|
||||
result_weights.reserve(end - begin);
|
||||
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge){ result_weights.emplace_back(edge.weight); });
|
||||
|
||||
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
|
||||
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
|
||||
{
|
||||
result_weights.emplace_back(edge.weight);
|
||||
});
|
||||
}
|
||||
|
||||
virtual unsigned GetGeometryIndexForEdgeID(const unsigned id) const override final
|
||||
@@ -411,7 +452,8 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
return m_via_node_list.at(id);
|
||||
}
|
||||
|
||||
extractor::TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const override final
|
||||
extractor::guidance::TurnInstruction
|
||||
GetTurnInstructionForEdgeID(const unsigned id) const override final
|
||||
{
|
||||
return m_turn_instruction_list.at(id);
|
||||
}
|
||||
@@ -421,25 +463,37 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
return m_travel_mode_list.at(id);
|
||||
}
|
||||
|
||||
std::vector<RTreeLeaf>
|
||||
GetEdgesInBox(const util::FixedPointCoordinate &south_west,
|
||||
const util::FixedPointCoordinate &north_east) override final
|
||||
std::vector<RTreeLeaf> GetEdgesInBox(const util::Coordinate south_west,
|
||||
const util::Coordinate north_east) override final
|
||||
{
|
||||
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
const util::RectangleInt2D bbox{
|
||||
south_west.lon, north_east.lon, south_west.lat, north_east.lat};
|
||||
const util::RectangleInt2D bbox{south_west.lon, north_east.lon, south_west.lat,
|
||||
north_east.lat};
|
||||
return m_geospatial_query->Search(bbox);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::FixedPointCoordinate input_coordinate,
|
||||
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
|
||||
const float max_distance) override final
|
||||
{
|
||||
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
|
||||
const float max_distance,
|
||||
const int bearing = 0,
|
||||
const int bearing_range = 180) override final
|
||||
const int bearing,
|
||||
const int bearing_range) override final
|
||||
{
|
||||
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
|
||||
{
|
||||
@@ -452,10 +506,37 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate,
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results) override final
|
||||
{
|
||||
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const int bearing = 0,
|
||||
const int bearing_range = 180) override final
|
||||
const double max_distance) override final
|
||||
{
|
||||
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const int bearing,
|
||||
const int bearing_range) override final
|
||||
{
|
||||
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
|
||||
{
|
||||
@@ -467,10 +548,70 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
bearing_range);
|
||||
}
|
||||
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const double max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range) override final
|
||||
{
|
||||
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance,
|
||||
bearing, bearing_range);
|
||||
}
|
||||
|
||||
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
const util::FixedPointCoordinate input_coordinate,
|
||||
const int bearing = 0,
|
||||
const int bearing_range = 180) override final
|
||||
const util::Coordinate input_coordinate) override final
|
||||
{
|
||||
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
input_coordinate);
|
||||
}
|
||||
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const double max_distance) override final
|
||||
{
|
||||
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
input_coordinate, max_distance);
|
||||
}
|
||||
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const double max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range) override final
|
||||
{
|
||||
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
|
||||
{
|
||||
LoadRTree();
|
||||
BOOST_ASSERT(m_geospatial_query.get());
|
||||
}
|
||||
|
||||
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
input_coordinate, max_distance, bearing, bearing_range);
|
||||
}
|
||||
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const int bearing,
|
||||
const int bearing_range) override final
|
||||
{
|
||||
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
|
||||
{
|
||||
@@ -489,7 +630,7 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
return m_name_ID_list.at(id);
|
||||
}
|
||||
|
||||
std::string get_name_for_id(const unsigned name_id) const override final
|
||||
std::string GetNameForID(const unsigned name_id) const override final
|
||||
{
|
||||
if (std::numeric_limits<unsigned>::max() == name_id)
|
||||
{
|
||||
@@ -520,7 +661,54 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
|
||||
virtual std::size_t GetCoreSize() const override final { return m_is_core_node.size(); }
|
||||
|
||||
// Returns the data source ids that were used to supply the edge
|
||||
// weights.
|
||||
virtual void
|
||||
GetUncompressedDatasources(const EdgeID id,
|
||||
std::vector<uint8_t> &result_datasources) const override final
|
||||
{
|
||||
const unsigned begin = m_geometry_indices.at(id);
|
||||
const unsigned end = m_geometry_indices.at(id + 1);
|
||||
|
||||
result_datasources.clear();
|
||||
result_datasources.reserve(end - begin);
|
||||
|
||||
// If there was no datasource info, return an array of 0's.
|
||||
if (m_datasource_list.empty())
|
||||
{
|
||||
for (unsigned i = 0; i < end - begin; ++i)
|
||||
{
|
||||
result_datasources.push_back(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::for_each(m_datasource_list.begin() + begin, m_datasource_list.begin() + end,
|
||||
[&](const uint8_t &datasource_id)
|
||||
{
|
||||
result_datasources.push_back(datasource_id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string GetDatasourceName(const uint8_t datasource_name_id) const override final
|
||||
{
|
||||
BOOST_ASSERT(m_datasource_name_offsets.size() >= 1);
|
||||
BOOST_ASSERT(m_datasource_name_offsets.size() > datasource_name_id);
|
||||
|
||||
std::string result;
|
||||
result.reserve(m_datasource_name_lengths[datasource_name_id]);
|
||||
std::copy(m_datasource_name_data.begin() + m_datasource_name_offsets[datasource_name_id],
|
||||
m_datasource_name_data.begin() + m_datasource_name_offsets[datasource_name_id] +
|
||||
m_datasource_name_lengths[datasource_name_id],
|
||||
std::back_inserter(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetTimestamp() const override final { return m_timestamp; }
|
||||
|
||||
bool GetUTurnsDefault() const override final { return m_profile_properties->allow_u_turn_at_via; }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef DOUGLAS_PEUCKER_HPP_
|
||||
#define DOUGLAS_PEUCKER_HPP_
|
||||
|
||||
#include "engine/segment_information.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
@@ -44,14 +44,15 @@ const constexpr auto DOUGLAS_PEUCKER_THRESHOLDS_SIZE =
|
||||
// Input is vector of pairs. Each pair consists of the point information and a
|
||||
// bit indicating if the points is present in the generalization.
|
||||
// Note: points may also be pre-selected*/
|
||||
void douglasPeucker(std::vector<SegmentInformation>::iterator begin,
|
||||
std::vector<SegmentInformation>::iterator end,
|
||||
const unsigned zoom_level);
|
||||
std::vector<util::Coordinate> douglasPeucker(std::vector<util::Coordinate>::const_iterator begin,
|
||||
std::vector<util::Coordinate>::const_iterator end,
|
||||
const unsigned zoom_level);
|
||||
|
||||
// Convenience range-based function
|
||||
inline void douglasPeucker(std::vector<SegmentInformation> &geometry, const unsigned zoom_level)
|
||||
inline std::vector<util::Coordinate> douglasPeucker(const std::vector<util::Coordinate> &geometry,
|
||||
const unsigned zoom_level)
|
||||
{
|
||||
douglasPeucker(begin(geometry), end(geometry), zoom_level);
|
||||
return douglasPeucker(begin(geometry), end(geometry), zoom_level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+45
-28
@@ -1,10 +1,9 @@
|
||||
#ifndef ENGINE_HPP
|
||||
#define ENGINE_HPP
|
||||
|
||||
#include "contractor/query_edge.hpp"
|
||||
|
||||
#include "osrm/json_container.hpp"
|
||||
#include "osrm/osrm.hpp"
|
||||
#include "engine/status.hpp"
|
||||
#include "storage/shared_barriers.hpp"
|
||||
#include "util/json_container.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
@@ -13,11 +12,6 @@
|
||||
namespace osrm
|
||||
{
|
||||
|
||||
namespace storage
|
||||
{
|
||||
struct SharedBarriers;
|
||||
}
|
||||
|
||||
namespace util
|
||||
{
|
||||
namespace json
|
||||
@@ -26,44 +20,67 @@ struct Object;
|
||||
}
|
||||
}
|
||||
|
||||
// Fwd decls
|
||||
namespace engine
|
||||
{
|
||||
struct EngineConfig;
|
||||
namespace api
|
||||
{
|
||||
struct RouteParameters;
|
||||
struct TableParameters;
|
||||
struct NearestParameters;
|
||||
struct TripParameters;
|
||||
struct MatchParameters;
|
||||
struct TileParameters;
|
||||
}
|
||||
namespace plugins
|
||||
{
|
||||
class BasePlugin;
|
||||
class ViaRoutePlugin;
|
||||
class TablePlugin;
|
||||
class NearestPlugin;
|
||||
class TripPlugin;
|
||||
class MatchPlugin;
|
||||
class TilePlugin;
|
||||
}
|
||||
// End fwd decls
|
||||
|
||||
namespace datafacade
|
||||
{
|
||||
template <class EdgeDataT> class BaseDataFacade;
|
||||
class BaseDataFacade;
|
||||
}
|
||||
|
||||
class Engine final
|
||||
{
|
||||
private:
|
||||
using PluginMap = std::unordered_map<std::string, std::unique_ptr<plugins::BasePlugin>>;
|
||||
|
||||
public:
|
||||
Engine(EngineConfig &config_);
|
||||
// Needs to be public
|
||||
struct EngineLock;
|
||||
|
||||
Engine(const Engine &) = delete;
|
||||
Engine &operator=(const Engine &) = delete;
|
||||
explicit Engine(EngineConfig &config);
|
||||
|
||||
int RunQuery(const RouteParameters &route_parameters, util::json::Object &json_result);
|
||||
Engine(Engine &&) noexcept;
|
||||
Engine &operator=(Engine &&) noexcept;
|
||||
|
||||
// Impl. in cpp since for unique_ptr of incomplete types
|
||||
~Engine();
|
||||
|
||||
Status Route(const api::RouteParameters ¶meters, util::json::Object &result);
|
||||
Status Table(const api::TableParameters ¶meters, util::json::Object &result);
|
||||
Status Nearest(const api::NearestParameters ¶meters, util::json::Object &result);
|
||||
Status Trip(const api::TripParameters ¶meters, util::json::Object &result);
|
||||
Status Match(const api::MatchParameters ¶meters, util::json::Object &result);
|
||||
Status Tile(const api::TileParameters ¶meters, std::string &result);
|
||||
|
||||
private:
|
||||
void RegisterPlugin(plugins::BasePlugin *plugin);
|
||||
PluginMap plugin_map;
|
||||
// will only be initialized if shared memory is used
|
||||
std::unique_ptr<storage::SharedBarriers> barrier;
|
||||
// base class pointer to the objects
|
||||
datafacade::BaseDataFacade<contractor::QueryEdge::EdgeData> *query_data_facade;
|
||||
std::unique_ptr<EngineLock> lock;
|
||||
|
||||
// decrease number of concurrent queries
|
||||
void decrease_concurrent_query_count();
|
||||
// increase number of concurrent queries
|
||||
void increase_concurrent_query_count();
|
||||
std::unique_ptr<plugins::ViaRoutePlugin> route_plugin;
|
||||
std::unique_ptr<plugins::TablePlugin> table_plugin;
|
||||
std::unique_ptr<plugins::NearestPlugin> nearest_plugin;
|
||||
std::unique_ptr<plugins::TripPlugin> trip_plugin;
|
||||
std::unique_ptr<plugins::MatchPlugin> match_plugin;
|
||||
std::unique_ptr<plugins::TilePlugin> tile_plugin;
|
||||
|
||||
std::unique_ptr<datafacade::BaseDataFacade> query_data_facade;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#ifndef ENGINE_CONFIG_HPP
|
||||
#define ENGINE_CONFIG_HPP
|
||||
|
||||
#include "storage/storage_config.hpp"
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
namespace osrm
|
||||
@@ -41,14 +42,15 @@ namespace engine
|
||||
|
||||
struct EngineConfig
|
||||
{
|
||||
std::unordered_map<std::string, boost::filesystem::path> server_paths;
|
||||
bool IsValid() const;
|
||||
|
||||
storage::StorageConfig storage_config;
|
||||
int max_locations_trip = -1;
|
||||
int max_locations_viaroute = -1;
|
||||
int max_locations_distance_table = -1;
|
||||
int max_locations_map_matching = -1;
|
||||
bool use_shared_memory = true;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,9 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
using CoordinateList = typename RTreeT::CoordinateList;
|
||||
|
||||
public:
|
||||
GeospatialQuery(RTreeT &rtree_, std::shared_ptr<CoordinateList> coordinates_, DataFacadeT &datafacade_)
|
||||
GeospatialQuery(RTreeT &rtree_,
|
||||
std::shared_ptr<CoordinateList> coordinates_,
|
||||
DataFacadeT &datafacade_)
|
||||
: rtree(rtree_), coordinates(std::move(coordinates_)), datafacade(datafacade_)
|
||||
{
|
||||
}
|
||||
@@ -41,10 +43,28 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
// Returns nearest PhantomNodes in the given bearing range within max_distance.
|
||||
// Does not filter by small/big component!
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::FixedPointCoordinate input_coordinate,
|
||||
NearestPhantomNodesInRange(const util::Coordinate input_coordinate, const double max_distance)
|
||||
{
|
||||
auto results = rtree.Nearest(input_coordinate,
|
||||
[](const EdgeData &)
|
||||
{
|
||||
return std::make_pair(true, true);
|
||||
},
|
||||
[max_distance](const std::size_t, const double min_dist)
|
||||
{
|
||||
return min_dist > max_distance;
|
||||
});
|
||||
|
||||
return MakePhantomNodes(input_coordinate, results);
|
||||
}
|
||||
|
||||
// Returns nearest PhantomNodes in the given bearing range within max_distance.
|
||||
// Does not filter by small/big component!
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
|
||||
const double max_distance,
|
||||
const int bearing = 0,
|
||||
const int bearing_range = 180)
|
||||
const int bearing,
|
||||
const int bearing_range)
|
||||
{
|
||||
auto results =
|
||||
rtree.Nearest(input_coordinate,
|
||||
@@ -63,10 +83,10 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
// Returns max_results nearest PhantomNodes in the given bearing range.
|
||||
// Does not filter by small/big component!
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate,
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const int bearing = 0,
|
||||
const int bearing_range = 180)
|
||||
const int bearing,
|
||||
const int bearing_range)
|
||||
{
|
||||
auto results = rtree.Nearest(input_coordinate,
|
||||
[this, bearing, bearing_range](const EdgeData &data)
|
||||
@@ -81,12 +101,144 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
return MakePhantomNodes(input_coordinate, results);
|
||||
}
|
||||
|
||||
// Returns max_results nearest PhantomNodes in the given bearing range within the maximum
|
||||
// distance.
|
||||
// Does not filter by small/big component!
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const double max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range)
|
||||
{
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[this, bearing, bearing_range](const EdgeData &data)
|
||||
{
|
||||
return checkSegmentBearing(data, bearing, bearing_range);
|
||||
},
|
||||
[max_results, max_distance](const std::size_t num_results, const double min_dist)
|
||||
{
|
||||
return num_results >= max_results || min_dist > max_distance;
|
||||
});
|
||||
|
||||
return MakePhantomNodes(input_coordinate, results);
|
||||
}
|
||||
|
||||
// Returns max_results nearest PhantomNodes.
|
||||
// Does not filter by small/big component!
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate, const unsigned max_results)
|
||||
{
|
||||
auto results = rtree.Nearest(input_coordinate,
|
||||
[](const EdgeData &)
|
||||
{
|
||||
return std::make_pair(true, true);
|
||||
},
|
||||
[max_results](const std::size_t num_results, const double)
|
||||
{
|
||||
return num_results >= max_results;
|
||||
});
|
||||
|
||||
return MakePhantomNodes(input_coordinate, results);
|
||||
}
|
||||
|
||||
// Returns max_results nearest PhantomNodes in the given max distance.
|
||||
// Does not filter by small/big component!
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
NearestPhantomNodes(const util::Coordinate input_coordinate,
|
||||
const unsigned max_results,
|
||||
const double max_distance)
|
||||
{
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[](const EdgeData &)
|
||||
{
|
||||
return std::make_pair(true, true);
|
||||
},
|
||||
[max_results, max_distance](const std::size_t num_results, const double min_dist)
|
||||
{
|
||||
return num_results >= max_results || min_dist > max_distance;
|
||||
});
|
||||
|
||||
return MakePhantomNodes(input_coordinate, results);
|
||||
}
|
||||
|
||||
// Returns the nearest phantom node. If this phantom node is not from a big component
|
||||
// a second phantom node is return that is the nearest coordinate in a big component.
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const double max_distance)
|
||||
{
|
||||
bool has_small_component = false;
|
||||
bool has_big_component = false;
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[&has_big_component, &has_small_component](const EdgeData &data)
|
||||
{
|
||||
auto use_segment =
|
||||
(!has_small_component || (!has_big_component && !data.component.is_tiny));
|
||||
auto use_directions = std::make_pair(use_segment, use_segment);
|
||||
|
||||
has_big_component = has_big_component || !data.component.is_tiny;
|
||||
has_small_component = has_small_component || data.component.is_tiny;
|
||||
|
||||
return use_directions;
|
||||
},
|
||||
[&has_big_component, max_distance](const std::size_t num_results, const double min_dist)
|
||||
{
|
||||
return (num_results > 0 && has_big_component) || min_dist > max_distance;
|
||||
});
|
||||
|
||||
if (results.size() == 0)
|
||||
{
|
||||
return std::make_pair(PhantomNode{}, PhantomNode{});
|
||||
}
|
||||
|
||||
BOOST_ASSERT(results.size() == 1 || results.size() == 2);
|
||||
return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node,
|
||||
MakePhantomNode(input_coordinate, results.back()).phantom_node);
|
||||
}
|
||||
|
||||
// Returns the nearest phantom node. If this phantom node is not from a big component
|
||||
// a second phantom node is return that is the nearest coordinate in a big component.
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate)
|
||||
{
|
||||
bool has_small_component = false;
|
||||
bool has_big_component = false;
|
||||
auto results =
|
||||
rtree.Nearest(input_coordinate,
|
||||
[&has_big_component, &has_small_component](const EdgeData &data)
|
||||
{
|
||||
auto use_segment = (!has_small_component ||
|
||||
(!has_big_component && !data.component.is_tiny));
|
||||
auto use_directions = std::make_pair(use_segment, use_segment);
|
||||
|
||||
has_big_component = has_big_component || !data.component.is_tiny;
|
||||
has_small_component = has_small_component || data.component.is_tiny;
|
||||
|
||||
return use_directions;
|
||||
},
|
||||
[&has_big_component](const std::size_t num_results, const double)
|
||||
{
|
||||
return num_results > 0 && has_big_component;
|
||||
});
|
||||
|
||||
if (results.size() == 0)
|
||||
{
|
||||
return std::make_pair(PhantomNode{}, PhantomNode{});
|
||||
}
|
||||
|
||||
BOOST_ASSERT(results.size() == 1 || results.size() == 2);
|
||||
return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node,
|
||||
MakePhantomNode(input_coordinate, results.back()).phantom_node);
|
||||
}
|
||||
|
||||
// Returns the nearest phantom node. If this phantom node is not from a big component
|
||||
// a second phantom node is return that is the nearest coordinate in a big component.
|
||||
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
const util::FixedPointCoordinate input_coordinate,
|
||||
const int bearing = 0,
|
||||
const int bearing_range = 180)
|
||||
const util::Coordinate input_coordinate, const int bearing, const int bearing_range)
|
||||
{
|
||||
bool has_small_component = false;
|
||||
bool has_big_component = false;
|
||||
@@ -126,9 +278,55 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
MakePhantomNode(input_coordinate, results.back()).phantom_node);
|
||||
}
|
||||
|
||||
// Returns the nearest phantom node. If this phantom node is not from a big component
|
||||
// a second phantom node is return that is the nearest coordinate in a big component.
|
||||
std::pair<PhantomNode, PhantomNode>
|
||||
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
|
||||
const double max_distance,
|
||||
const int bearing,
|
||||
const int bearing_range)
|
||||
{
|
||||
bool has_small_component = false;
|
||||
bool has_big_component = false;
|
||||
auto results = rtree.Nearest(
|
||||
input_coordinate,
|
||||
[this, bearing, bearing_range, &has_big_component, &has_small_component](
|
||||
const EdgeData &data)
|
||||
{
|
||||
auto use_segment =
|
||||
(!has_small_component || (!has_big_component && !data.component.is_tiny));
|
||||
auto use_directions = std::make_pair(use_segment, use_segment);
|
||||
|
||||
if (use_segment)
|
||||
{
|
||||
use_directions = checkSegmentBearing(data, bearing, bearing_range);
|
||||
if (use_directions.first || use_directions.second)
|
||||
{
|
||||
has_big_component = has_big_component || !data.component.is_tiny;
|
||||
has_small_component = has_small_component || data.component.is_tiny;
|
||||
}
|
||||
}
|
||||
|
||||
return use_directions;
|
||||
},
|
||||
[&has_big_component, max_distance](const std::size_t num_results, const double min_dist)
|
||||
{
|
||||
return (num_results > 0 && has_big_component) || min_dist > max_distance;
|
||||
});
|
||||
|
||||
if (results.size() == 0)
|
||||
{
|
||||
return std::make_pair(PhantomNode{}, PhantomNode{});
|
||||
}
|
||||
|
||||
BOOST_ASSERT(results.size() > 0);
|
||||
return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node,
|
||||
MakePhantomNode(input_coordinate, results.back()).phantom_node);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<PhantomNodeWithDistance>
|
||||
MakePhantomNodes(const util::FixedPointCoordinate input_coordinate,
|
||||
MakePhantomNodes(const util::Coordinate input_coordinate,
|
||||
const std::vector<EdgeData> &results) const
|
||||
{
|
||||
std::vector<PhantomNodeWithDistance> distance_and_phantoms(results.size());
|
||||
@@ -140,10 +338,10 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
return distance_and_phantoms;
|
||||
}
|
||||
|
||||
PhantomNodeWithDistance MakePhantomNode(const util::FixedPointCoordinate input_coordinate,
|
||||
PhantomNodeWithDistance MakePhantomNode(const util::Coordinate input_coordinate,
|
||||
const EdgeData &data) const
|
||||
{
|
||||
util::FixedPointCoordinate point_on_segment;
|
||||
util::Coordinate point_on_segment;
|
||||
double ratio;
|
||||
const auto current_perpendicular_distance =
|
||||
util::coordinate_calculation::perpendicularDistance(
|
||||
@@ -156,10 +354,11 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
int forward_offset = 0, forward_weight = 0;
|
||||
int reverse_offset = 0, reverse_weight = 0;
|
||||
|
||||
if (data.forward_packed_geometry_id != SPECIAL_EDGEID) {
|
||||
if (data.forward_packed_geometry_id != SPECIAL_EDGEID)
|
||||
{
|
||||
std::vector<EdgeWeight> forward_weight_vector;
|
||||
datafacade.GetUncompressedWeights(data.forward_packed_geometry_id,
|
||||
forward_weight_vector);
|
||||
forward_weight_vector);
|
||||
for (std::size_t i = 0; i < data.fwd_segment_position; i++)
|
||||
{
|
||||
forward_offset += forward_weight_vector[i];
|
||||
@@ -167,31 +366,40 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
|
||||
forward_weight = forward_weight_vector[data.fwd_segment_position];
|
||||
}
|
||||
|
||||
if (data.reverse_packed_geometry_id != SPECIAL_EDGEID) {
|
||||
if (data.reverse_packed_geometry_id != SPECIAL_EDGEID)
|
||||
{
|
||||
std::vector<EdgeWeight> reverse_weight_vector;
|
||||
datafacade.GetUncompressedWeights(data.reverse_packed_geometry_id,
|
||||
reverse_weight_vector);
|
||||
|
||||
BOOST_ASSERT(data.fwd_segment_position < reverse_weight_vector.size());
|
||||
|
||||
for (std::size_t i = 0; i < reverse_weight_vector.size() - data.fwd_segment_position - 1; i++)
|
||||
for (std::size_t i = 0;
|
||||
i < reverse_weight_vector.size() - data.fwd_segment_position - 1; i++)
|
||||
{
|
||||
reverse_offset += reverse_weight_vector[i];
|
||||
}
|
||||
reverse_weight = reverse_weight_vector[reverse_weight_vector.size() -
|
||||
data.fwd_segment_position - 1];
|
||||
reverse_weight =
|
||||
reverse_weight_vector[reverse_weight_vector.size() - data.fwd_segment_position - 1];
|
||||
}
|
||||
|
||||
ratio = std::min(1.0, std::max(0.0, ratio));
|
||||
if (SPECIAL_NODEID != data.forward_edge_based_node_id) {
|
||||
if (SPECIAL_NODEID != data.forward_edge_based_node_id)
|
||||
{
|
||||
forward_weight *= ratio;
|
||||
}
|
||||
if (SPECIAL_NODEID != data.reverse_edge_based_node_id) {
|
||||
if (SPECIAL_NODEID != data.reverse_edge_based_node_id)
|
||||
{
|
||||
reverse_weight *= 1.0 - ratio;
|
||||
}
|
||||
|
||||
auto transformed = PhantomNodeWithDistance{PhantomNode{data, forward_weight, forward_offset,
|
||||
reverse_weight, reverse_offset, point_on_segment},
|
||||
auto transformed = PhantomNodeWithDistance{PhantomNode{data,
|
||||
forward_weight,
|
||||
forward_offset,
|
||||
reverse_weight,
|
||||
reverse_offset,
|
||||
point_on_segment,
|
||||
input_coordinate},
|
||||
current_perpendicular_distance};
|
||||
|
||||
return transformed;
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef ENGINE_GUIDANCE_ASSEMBLE_GEOMETRY_HPP
|
||||
#define ENGINE_GUIDANCE_ASSEMBLE_GEOMETRY_HPP
|
||||
|
||||
#include "engine/internal_route_result.hpp"
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "engine/guidance/route_step.hpp"
|
||||
#include "engine/guidance/leg_geometry.hpp"
|
||||
#include "engine/guidance/toolkit.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "extractor/travel_mode.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
// Extracts the geometry for each segment and calculates the traveled distance
|
||||
// Combines the geometry form the phantom node with the PathData
|
||||
// to the full route geometry.
|
||||
//
|
||||
// turn 0 1 2 3 4
|
||||
// s...x...y...z...t
|
||||
// |---|segment 0
|
||||
// |---| segment 1
|
||||
// |---| segment 2
|
||||
// |---| segment 3
|
||||
template <typename DataFacadeT>
|
||||
LegGeometry assembleGeometry(const DataFacadeT &facade,
|
||||
const std::vector<PathData> &leg_data,
|
||||
const PhantomNode &source_node,
|
||||
const PhantomNode &target_node)
|
||||
{
|
||||
LegGeometry geometry;
|
||||
|
||||
// segment 0 first and last
|
||||
geometry.segment_offsets.push_back(0);
|
||||
geometry.locations.push_back(source_node.location);
|
||||
|
||||
auto current_distance = 0.;
|
||||
auto prev_coordinate = geometry.locations.front();
|
||||
for (const auto &path_point : leg_data)
|
||||
{
|
||||
auto coordinate = facade.GetCoordinateOfNode(path_point.turn_via_node);
|
||||
current_distance +=
|
||||
util::coordinate_calculation::haversineDistance(prev_coordinate, coordinate);
|
||||
|
||||
// all changes to this check have to be matched with assemble_steps
|
||||
if (path_point.turn_instruction.type != extractor::guidance::TurnType::NoTurn)
|
||||
{
|
||||
geometry.segment_distances.push_back(current_distance);
|
||||
geometry.segment_offsets.push_back(geometry.locations.size());
|
||||
current_distance = 0.;
|
||||
}
|
||||
|
||||
prev_coordinate = coordinate;
|
||||
geometry.locations.push_back(std::move(coordinate));
|
||||
}
|
||||
current_distance +=
|
||||
util::coordinate_calculation::haversineDistance(prev_coordinate, target_node.location);
|
||||
// segment leading to the target node
|
||||
geometry.segment_distances.push_back(current_distance);
|
||||
geometry.segment_offsets.push_back(geometry.locations.size());
|
||||
geometry.locations.push_back(target_node.location);
|
||||
|
||||
BOOST_ASSERT(geometry.segment_distances.size() == geometry.segment_offsets.size() - 1);
|
||||
BOOST_ASSERT(geometry.locations.size() > geometry.segment_distances.size());
|
||||
|
||||
return geometry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,159 @@
|
||||
#ifndef ENGINE_GUIDANCE_ASSEMBLE_LEG_HPP_
|
||||
#define ENGINE_GUIDANCE_ASSEMBLE_LEG_HPP_
|
||||
|
||||
#include "engine/guidance/route_leg.hpp"
|
||||
#include "engine/guidance/route_step.hpp"
|
||||
#include "engine/guidance/leg_geometry.hpp"
|
||||
#include "engine/internal_route_result.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
const constexpr std::size_t MAX_USED_SEGMENTS = 2;
|
||||
struct NamedSegment
|
||||
{
|
||||
double duration;
|
||||
std::uint32_t position;
|
||||
std::uint32_t name_id;
|
||||
};
|
||||
|
||||
template <std::size_t SegmentNumber>
|
||||
std::array<std::uint32_t, SegmentNumber> summarizeRoute(const std::vector<PathData> &route_data)
|
||||
{
|
||||
// merges segments with same name id
|
||||
const auto collapse_segments = [](std::vector<NamedSegment> &segments)
|
||||
{
|
||||
auto out = segments.begin();
|
||||
auto end = segments.end();
|
||||
for (auto in = segments.begin(); in != end; ++in)
|
||||
{
|
||||
if (in->name_id == out->name_id)
|
||||
{
|
||||
out->duration += in->duration;
|
||||
}
|
||||
else
|
||||
{
|
||||
++out;
|
||||
BOOST_ASSERT(out != end);
|
||||
*out = *in;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
std::vector<NamedSegment> segments(route_data.size());
|
||||
std::uint32_t index = 0;
|
||||
std::transform(
|
||||
route_data.begin(), route_data.end(), segments.begin(), [&index](const PathData &point)
|
||||
{
|
||||
return NamedSegment{point.duration_until_turn / 10.0, index++, point.name_id};
|
||||
});
|
||||
// this makes sure that the segment with the lowest position comes first
|
||||
std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs)
|
||||
{
|
||||
return lhs.name_id < rhs.name_id ||
|
||||
(lhs.name_id == rhs.name_id && lhs.position < rhs.position);
|
||||
});
|
||||
auto new_end = collapse_segments(segments);
|
||||
segments.resize(new_end - segments.begin());
|
||||
// sort descending
|
||||
std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs)
|
||||
{
|
||||
return lhs.duration > rhs.duration;
|
||||
});
|
||||
|
||||
// make sure the segments are sorted by position
|
||||
segments.resize(std::min(segments.size(), SegmentNumber));
|
||||
std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs)
|
||||
{
|
||||
return lhs.position < rhs.position;
|
||||
});
|
||||
|
||||
std::array<std::uint32_t, SegmentNumber> summary;
|
||||
std::fill(summary.begin(), summary.end(), 0);
|
||||
std::transform(segments.begin(), segments.end(), summary.begin(),
|
||||
[](const NamedSegment &segment)
|
||||
{
|
||||
return segment.name_id;
|
||||
});
|
||||
return summary;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
RouteLeg assembleLeg(const DataFacadeT &facade,
|
||||
const std::vector<PathData> &route_data,
|
||||
const LegGeometry &leg_geometry,
|
||||
const PhantomNode &target_node,
|
||||
const bool target_traversed_in_reverse)
|
||||
{
|
||||
const auto target_duration =
|
||||
(target_traversed_in_reverse ? target_node.reverse_weight
|
||||
: target_node.forward_weight) /
|
||||
10.;
|
||||
|
||||
auto distance = std::accumulate(leg_geometry.segment_distances.begin(),
|
||||
leg_geometry.segment_distances.end(), 0.);
|
||||
auto duration = std::accumulate(route_data.begin(), route_data.end(), 0.,
|
||||
[](const double sum, const PathData &data)
|
||||
{
|
||||
return sum + data.duration_until_turn;
|
||||
}) /
|
||||
10.;
|
||||
|
||||
// s
|
||||
// |
|
||||
// Given a route a---b---c where there is a right turn at b.
|
||||
// |
|
||||
// d
|
||||
// |--t
|
||||
// e
|
||||
// (a, b, c) gets compressed to (a,c)
|
||||
// (c, d, e) gets compressed to (c,e)
|
||||
// The duration of the turn (a,c) -> (c,e) will be the duration of (a,c) (e.g. the duration
|
||||
// of (a,b,c)).
|
||||
// The phantom node of s will contain:
|
||||
// `forward_weight`: duration of (a,s)
|
||||
// `forward_offset`: 0 (its the first segment)
|
||||
// The phantom node of t will contain:
|
||||
// `forward_weight`: duration of (d,t)
|
||||
// `forward_offset`: duration of (c, d)
|
||||
// path_data will have entries for (s,b), (b, c), (c, d) but (d, t) is only
|
||||
// caputed by the phantom node. So we need to add the target duration here.
|
||||
duration = duration + target_duration;
|
||||
auto summary_array = detail::summarizeRoute<detail::MAX_USED_SEGMENTS>(route_data);
|
||||
|
||||
BOOST_ASSERT(detail::MAX_USED_SEGMENTS > 0);
|
||||
BOOST_ASSERT(summary_array.begin() != summary_array.end());
|
||||
std::string summary =
|
||||
std::accumulate(std::next(summary_array.begin()), summary_array.end(),
|
||||
facade.GetNameForID(summary_array.front()),
|
||||
[&facade](std::string previous, const std::uint32_t name_id)
|
||||
{
|
||||
if (name_id != 0)
|
||||
{
|
||||
previous += ", " + facade.GetNameForID(name_id);
|
||||
}
|
||||
return previous;
|
||||
});
|
||||
|
||||
return RouteLeg{duration, distance, summary, {}};
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP
|
||||
#define ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP
|
||||
|
||||
#include "engine/guidance/leg_geometry.hpp"
|
||||
|
||||
#include "util/coordinate.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
std::vector<util::Coordinate> assembleOverview(const std::vector<LegGeometry> &leg_geometries,
|
||||
const bool use_simplification);
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef ENGINE_GUIDANCE_ASSEMBLE_ROUTE_HPP
|
||||
#define ENGINE_GUIDANCE_ASSEMBLE_ROUTE_HPP
|
||||
|
||||
#include "engine/guidance/route_leg.hpp"
|
||||
#include "engine/guidance/route.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
Route assembleRoute(const std::vector<RouteLeg> &route_legs);
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,184 @@
|
||||
#ifndef ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_
|
||||
#define ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_
|
||||
|
||||
#include "engine/guidance/route_step.hpp"
|
||||
#include "engine/guidance/step_maneuver.hpp"
|
||||
#include "engine/guidance/leg_geometry.hpp"
|
||||
#include "engine/guidance/toolkit.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "engine/internal_route_result.hpp"
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "extractor/travel_mode.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction,
|
||||
const LegGeometry &leg_geometry,
|
||||
const std::size_t segment_index);
|
||||
|
||||
StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction,
|
||||
const WaypointType waypoint_type,
|
||||
const LegGeometry &leg_geometry);
|
||||
|
||||
} // ns detail
|
||||
|
||||
template <typename DataFacadeT>
|
||||
std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
|
||||
const std::vector<PathData> &leg_data,
|
||||
const LegGeometry &leg_geometry,
|
||||
const PhantomNode &source_node,
|
||||
const PhantomNode &target_node,
|
||||
const bool source_traversed_in_reverse,
|
||||
const bool target_traversed_in_reverse)
|
||||
{
|
||||
const double constexpr ZERO_DURATION = 0., ZERO_DISTANCE = 0.;
|
||||
const EdgeWeight source_duration =
|
||||
source_traversed_in_reverse ? source_node.reverse_weight : source_node.forward_weight;
|
||||
const auto source_mode = source_traversed_in_reverse ? source_node.backward_travel_mode
|
||||
: source_node.forward_travel_mode;
|
||||
|
||||
const EdgeWeight target_duration =
|
||||
target_traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight;
|
||||
const auto target_mode = target_traversed_in_reverse ? target_node.backward_travel_mode
|
||||
: target_node.forward_travel_mode;
|
||||
|
||||
const auto number_of_segments = leg_geometry.GetNumberOfSegments();
|
||||
|
||||
std::vector<RouteStep> steps;
|
||||
steps.reserve(number_of_segments);
|
||||
|
||||
std::size_t segment_index = 0;
|
||||
BOOST_ASSERT(leg_geometry.locations.size() >= 2);
|
||||
|
||||
// We report the relative position of source/target to the road only within a range that is
|
||||
// sufficiently different but not full of the path
|
||||
const constexpr double MINIMAL_RELATIVE_DISTANCE = 5., MAXIMAL_RELATIVE_DISTANCE = 300.;
|
||||
const auto distance_to_start = util::coordinate_calculation::haversineDistance(
|
||||
source_node.input_location, leg_geometry.locations[0]);
|
||||
const auto initial_modifier =
|
||||
distance_to_start >= MINIMAL_RELATIVE_DISTANCE &&
|
||||
distance_to_start <= MAXIMAL_RELATIVE_DISTANCE
|
||||
? angleToDirectionModifier(util::coordinate_calculation::computeAngle(
|
||||
source_node.input_location, leg_geometry.locations[0], leg_geometry.locations[1]))
|
||||
: extractor::guidance::DirectionModifier::UTurn;
|
||||
|
||||
if (leg_data.size() > 0)
|
||||
{
|
||||
|
||||
StepManeuver maneuver = detail::stepManeuverFromGeometry(
|
||||
extractor::guidance::TurnInstruction{extractor::guidance::TurnType::NoTurn,
|
||||
initial_modifier},
|
||||
WaypointType::Depart, leg_geometry);
|
||||
maneuver.location = source_node.location;
|
||||
|
||||
// PathData saves the information we need of the segment _before_ the turn,
|
||||
// but a RouteStep is with regard to the segment after the turn.
|
||||
// We need to skip the first segment because it is already covered by the
|
||||
// initial start of a route
|
||||
int segment_duration = 0;
|
||||
for (const auto &path_point : leg_data)
|
||||
{
|
||||
segment_duration += path_point.duration_until_turn;
|
||||
|
||||
// all changes to this check have to be matched with assemble_geometry
|
||||
if (path_point.turn_instruction.type != extractor::guidance::TurnType::NoTurn)
|
||||
{
|
||||
BOOST_ASSERT(segment_duration >= 0);
|
||||
const auto name = facade.GetNameForID(path_point.name_id);
|
||||
const auto distance = leg_geometry.segment_distances[segment_index];
|
||||
steps.push_back(RouteStep{path_point.name_id,
|
||||
name,
|
||||
segment_duration / 10.0,
|
||||
distance,
|
||||
path_point.travel_mode,
|
||||
maneuver,
|
||||
leg_geometry.FrontIndex(segment_index),
|
||||
leg_geometry.BackIndex(segment_index) + 1});
|
||||
maneuver = detail::stepManeuverFromGeometry(path_point.turn_instruction,
|
||||
leg_geometry, segment_index);
|
||||
segment_index++;
|
||||
segment_duration = 0;
|
||||
}
|
||||
}
|
||||
const auto distance = leg_geometry.segment_distances[segment_index];
|
||||
const int duration = segment_duration + target_duration;
|
||||
BOOST_ASSERT(duration >= 0);
|
||||
steps.push_back(RouteStep{target_node.name_id,
|
||||
facade.GetNameForID(target_node.name_id),
|
||||
duration / 10.,
|
||||
distance,
|
||||
target_mode,
|
||||
maneuver,
|
||||
leg_geometry.FrontIndex(segment_index),
|
||||
leg_geometry.BackIndex(segment_index) + 1});
|
||||
}
|
||||
// In this case the source + target are on the same edge segment
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(source_node.fwd_segment_position == target_node.fwd_segment_position);
|
||||
// s t
|
||||
// u-------------v
|
||||
// |---| source_duration
|
||||
// |---------| target_duration
|
||||
|
||||
StepManeuver maneuver = detail::stepManeuverFromGeometry(
|
||||
extractor::guidance::TurnInstruction{extractor::guidance::TurnType::NoTurn,
|
||||
initial_modifier},
|
||||
WaypointType::Depart, leg_geometry);
|
||||
int duration = target_duration - source_duration;
|
||||
BOOST_ASSERT(duration >= 0);
|
||||
|
||||
steps.push_back(RouteStep{source_node.name_id,
|
||||
facade.GetNameForID(source_node.name_id),
|
||||
duration / 10.,
|
||||
leg_geometry.segment_distances[segment_index],
|
||||
source_mode,
|
||||
std::move(maneuver),
|
||||
leg_geometry.FrontIndex(segment_index),
|
||||
leg_geometry.BackIndex(segment_index) + 1});
|
||||
}
|
||||
|
||||
BOOST_ASSERT(segment_index == number_of_segments - 1);
|
||||
const auto distance_from_end = util::coordinate_calculation::haversineDistance(
|
||||
target_node.input_location, leg_geometry.locations.back());
|
||||
const auto final_modifier =
|
||||
distance_from_end >= MINIMAL_RELATIVE_DISTANCE &&
|
||||
distance_from_end <= MAXIMAL_RELATIVE_DISTANCE
|
||||
? angleToDirectionModifier(util::coordinate_calculation::computeAngle(
|
||||
leg_geometry.locations[leg_geometry.locations.size() - 2],
|
||||
leg_geometry.locations[leg_geometry.locations.size() - 1],
|
||||
target_node.input_location))
|
||||
: extractor::guidance::DirectionModifier::UTurn;
|
||||
// This step has length zero, the only reason we need it is the target location
|
||||
auto final_maneuver = detail::stepManeuverFromGeometry(
|
||||
extractor::guidance::TurnInstruction{extractor::guidance::TurnType::NoTurn, final_modifier},
|
||||
WaypointType::Arrive, leg_geometry);
|
||||
steps.push_back(RouteStep{target_node.name_id,
|
||||
facade.GetNameForID(target_node.name_id),
|
||||
ZERO_DURATION,
|
||||
ZERO_DISTANCE,
|
||||
target_mode,
|
||||
final_maneuver,
|
||||
leg_geometry.locations.size(),
|
||||
leg_geometry.locations.size()});
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef ENGINE_GUIDANCE_LEG_GEOMETRY_HPP
|
||||
#define ENGINE_GUIDANCE_LEG_GEOMETRY_HPP
|
||||
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
// locations 0---1---2-...-n-1---n
|
||||
// turns s x y t
|
||||
// segment | 0 | 1 | 2 | senitel
|
||||
// offsets 0 2 n-1 n
|
||||
struct LegGeometry
|
||||
{
|
||||
std::vector<util::Coordinate> locations;
|
||||
// segment_offset[i] .. segment_offset[i+1] (inclusive)
|
||||
// contains the geometry of segment i
|
||||
std::vector<std::size_t> segment_offsets;
|
||||
// length of the segment in meters
|
||||
std::vector<double> segment_distances;
|
||||
|
||||
std::size_t FrontIndex(std::size_t segment_index) const
|
||||
{
|
||||
return segment_offsets[segment_index];
|
||||
}
|
||||
|
||||
std::size_t BackIndex(std::size_t segment_index) const
|
||||
{
|
||||
return segment_offsets[segment_index + 1];
|
||||
}
|
||||
|
||||
std::size_t GetNumberOfSegments() const
|
||||
{
|
||||
BOOST_ASSERT(segment_offsets.size() > 0);
|
||||
return segment_offsets.size() - 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef ENGINE_GUIDANCE_POST_PROCESSING_HPP
|
||||
#define ENGINE_GUIDANCE_POST_PROCESSING_HPP
|
||||
|
||||
#include "engine/guidance/route_step.hpp"
|
||||
#include "engine/guidance/leg_geometry.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
// passed as none-reference to modify in-place and move out again
|
||||
std::vector<RouteStep> postProcess(std::vector<RouteStep> steps);
|
||||
|
||||
// postProcess will break the connection between the leg geometry
|
||||
// for which a segment is supposed to represent exactly the coordinates
|
||||
// between routing maneuvers and the route steps itself.
|
||||
// If required, we can get both in sync again using this function.
|
||||
// Move in LegGeometry for modification in place.
|
||||
LegGeometry resyncGeometry(LegGeometry leg_geometry, const std::vector<RouteStep> &steps);
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif // ENGINE_GUIDANCE_POST_PROCESSING_HPP
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef ROUTE_HPP
|
||||
#define ROUTE_HPP
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
struct Route
|
||||
{
|
||||
double duration;
|
||||
double distance;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef ROUTE_LEG_HPP
|
||||
#define ROUTE_LEG_HPP
|
||||
|
||||
#include "engine/guidance/route_step.hpp"
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
struct RouteLeg
|
||||
{
|
||||
double duration;
|
||||
double distance;
|
||||
std::string summary;
|
||||
std::vector<RouteStep> steps;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef ROUTE_STEP_HPP
|
||||
#define ROUTE_STEP_HPP
|
||||
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "engine/guidance/step_maneuver.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
// Given the following turn from a,b to b,c over b:
|
||||
// a --> b --> c
|
||||
// this struct saves the information of the segment b,c.
|
||||
// Notable exceptions are Departure and Arrival steps.
|
||||
// Departue: s --> a --> b. Represents the segment s,a with location being s.
|
||||
// Arrive: a --> b --> t. The segment (b,t) is already covered by the previous segment.
|
||||
struct RouteStep
|
||||
{
|
||||
unsigned name_id;
|
||||
std::string name;
|
||||
double duration;
|
||||
double distance;
|
||||
extractor::TravelMode mode;
|
||||
StepManeuver maneuver;
|
||||
// indices into the locations array stored the LegGeometry
|
||||
std::size_t geometry_begin;
|
||||
std::size_t geometry_end;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,66 +0,0 @@
|
||||
#ifndef ENGINE_GUIDANCE_PROCESSING_SEGMENT_COMPRESSION_HPP_
|
||||
#define ENGINE_GUIDANCE_PROCESSING_SEGMENT_COMPRESSION_HPP_
|
||||
|
||||
#include "engine/segment_inforamtion.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
/*
|
||||
Simplify turn instructions
|
||||
Input :
|
||||
10. Turn left on B 36 for 20 km
|
||||
11. Continue on B 35; B 36 for 2 km
|
||||
12. Continue on B 36 for 13 km
|
||||
|
||||
Output:
|
||||
10. Turn left on B 36 for 35 km
|
||||
*/
|
||||
|
||||
inline void CombineSimilarSegments(std::vector<SegmentInformation> &segments)
|
||||
{
|
||||
// TODO: rework to check only end and start of string.
|
||||
// stl string is way to expensive
|
||||
// unsigned lastTurn = 0;
|
||||
// for(unsigned i = 1; i < path_description.size(); ++i) {
|
||||
// string1 = sEngine.GetEscapedNameForNameID(path_description[i].name_id);
|
||||
// if(TurnInstruction::GoStraight == path_description[i].turn_instruction) {
|
||||
// if(std::string::npos != string0.find(string1+";")
|
||||
// || std::string::npos != string0.find(";"+string1)
|
||||
// || std::string::npos != string0.find(string1+" ;")
|
||||
// || std::string::npos != string0.find("; "+string1)
|
||||
// ){
|
||||
// SimpleLogger().Write() << "->next correct: " << string0 << " contains " <<
|
||||
// string1;
|
||||
// for(; lastTurn != i; ++lastTurn)
|
||||
// path_description[lastTurn].name_id = path_description[i].name_id;
|
||||
// path_description[i].turn_instruction = TurnInstruction::NoTurn;
|
||||
// } else if(std::string::npos != string1.find(string0+";")
|
||||
// || std::string::npos != string1.find(";"+string0)
|
||||
// || std::string::npos != string1.find(string0+" ;")
|
||||
// || std::string::npos != string1.find("; "+string0)
|
||||
// ){
|
||||
// SimpleLogger().Write() << "->prev correct: " << string1 << " contains " <<
|
||||
// string0;
|
||||
// path_description[i].name_id = path_description[i-1].name_id;
|
||||
// path_description[i].turn_instruction = TurnInstruction::NoTurn;
|
||||
// }
|
||||
// }
|
||||
// if (TurnInstruction::NoTurn != path_description[i].turn_instruction) {
|
||||
// lastTurn = i;
|
||||
// }
|
||||
// string0 = string1;
|
||||
// }
|
||||
//
|
||||
}
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif // ENGINE_GUIDANCE_PROCESSING_SEGMENT_COMPRESSION_HPP_
|
||||
@@ -1,347 +0,0 @@
|
||||
#ifndef ENGINE_GUIDANCE_SEGMENT_LIST_HPP_
|
||||
#define ENGINE_GUIDANCE_SEGMENT_LIST_HPP_
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
|
||||
#include "engine/douglas_peucker.hpp"
|
||||
#include "engine/internal_route_result.hpp"
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "engine/segment_information.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/for_each_pair.hpp"
|
||||
#include "extractor/turn_instructions.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
// transfers the internal edge based data structures to a more useable format
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
template <typename DataFacadeT> class SegmentList
|
||||
{
|
||||
public:
|
||||
using DataFacade = DataFacadeT;
|
||||
SegmentList(const InternalRouteResult &raw_route,
|
||||
const bool extract_alternative,
|
||||
const unsigned zoom_level,
|
||||
const bool allow_simplification,
|
||||
const DataFacade *facade);
|
||||
|
||||
const std::vector<std::uint32_t> &GetViaIndices() const;
|
||||
std::uint32_t GetDistance() const;
|
||||
std::uint32_t GetDuration() const;
|
||||
|
||||
const std::vector<SegmentInformation> &Get() const;
|
||||
|
||||
private:
|
||||
void InitRoute(const PhantomNode &phantom_node, const bool traversed_in_reverse);
|
||||
void AddLeg(const std::vector<PathData> &leg_data,
|
||||
const PhantomNode &target_node,
|
||||
const bool traversed_in_reverse,
|
||||
const bool is_via_leg,
|
||||
const DataFacade *facade);
|
||||
|
||||
void AppendSegment(const FixedPointCoordinate coordinate, const PathData &path_point);
|
||||
void Finalize(const bool extract_alternative,
|
||||
const InternalRouteResult &raw_route,
|
||||
const unsigned zoom_level,
|
||||
const bool allow_simplification);
|
||||
|
||||
// journey length in tenth of a second
|
||||
std::uint32_t total_distance;
|
||||
// journey distance in meter (TODO: verify)
|
||||
std::uint32_t total_duration;
|
||||
|
||||
// segments that are required to keep
|
||||
std::vector<std::uint32_t> via_indices;
|
||||
|
||||
// a list of node based segments
|
||||
std::vector<SegmentInformation> segments;
|
||||
};
|
||||
|
||||
template <typename DataFacadeT>
|
||||
SegmentList<DataFacadeT>::SegmentList(const InternalRouteResult &raw_route,
|
||||
const bool extract_alternative,
|
||||
const unsigned zoom_level,
|
||||
const bool allow_simplification,
|
||||
const DataFacade *facade)
|
||||
: total_distance(0), total_duration(0)
|
||||
{
|
||||
if (!raw_route.is_valid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (extract_alternative)
|
||||
{
|
||||
BOOST_ASSERT(raw_route.has_alternative());
|
||||
InitRoute(raw_route.segment_end_coordinates.front().source_phantom,
|
||||
raw_route.alt_source_traversed_in_reverse.front());
|
||||
AddLeg(raw_route.unpacked_alternative,
|
||||
raw_route.segment_end_coordinates.back().target_phantom,
|
||||
raw_route.alt_source_traversed_in_reverse.back(), false, facade);
|
||||
}
|
||||
else
|
||||
{
|
||||
InitRoute(raw_route.segment_end_coordinates.front().source_phantom,
|
||||
raw_route.source_traversed_in_reverse.front());
|
||||
for (std::size_t raw_index = 0; raw_index < raw_route.segment_end_coordinates.size();
|
||||
++raw_index)
|
||||
{
|
||||
AddLeg(raw_route.unpacked_path_segments[raw_index],
|
||||
raw_route.segment_end_coordinates[raw_index].target_phantom,
|
||||
raw_route.target_traversed_in_reverse[raw_index],
|
||||
raw_route.is_via_leg(raw_index), facade);
|
||||
if (raw_route.is_via_leg(raw_index))
|
||||
{
|
||||
const auto &source_phantom =
|
||||
raw_route.segment_end_coordinates[raw_index].target_phantom;
|
||||
if (raw_route.target_traversed_in_reverse[raw_index] !=
|
||||
raw_route.source_traversed_in_reverse[raw_index + 1])
|
||||
{
|
||||
bool traversed_in_reverse = raw_route.target_traversed_in_reverse[raw_index];
|
||||
const extractor::TravelMode travel_mode =
|
||||
(traversed_in_reverse ? source_phantom.backward_travel_mode
|
||||
: source_phantom.forward_travel_mode);
|
||||
const bool constexpr IS_NECESSARY = true;
|
||||
const bool constexpr IS_VIA_LOCATION = true;
|
||||
segments.emplace_back(source_phantom.location, source_phantom.name_id, 0, 0.f,
|
||||
extractor::TurnInstruction::UTurn, IS_NECESSARY,
|
||||
IS_VIA_LOCATION, travel_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!allow_simplification)
|
||||
{
|
||||
// to prevent any simplifications, we mark all segments as necessary
|
||||
for (auto &segment : segments)
|
||||
{
|
||||
segment.necessary = true;
|
||||
}
|
||||
}
|
||||
|
||||
Finalize(extract_alternative, raw_route, zoom_level, allow_simplification);
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
void SegmentList<DataFacadeT>::InitRoute(const PhantomNode &node, const bool traversed_in_reverse)
|
||||
{
|
||||
const auto segment_duration =
|
||||
(traversed_in_reverse ? node.reverse_weight : node.forward_weight);
|
||||
const auto travel_mode =
|
||||
(traversed_in_reverse ? node.backward_travel_mode : node.forward_travel_mode);
|
||||
|
||||
AppendSegment(node.location, PathData(0, node.name_id, extractor::TurnInstruction::HeadOn,
|
||||
segment_duration, travel_mode));
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
void SegmentList<DataFacadeT>::AddLeg(const std::vector<PathData> &leg_data,
|
||||
const PhantomNode &target_node,
|
||||
const bool traversed_in_reverse,
|
||||
const bool is_via_leg,
|
||||
const DataFacade *facade)
|
||||
{
|
||||
for (const auto &path_data : leg_data)
|
||||
{
|
||||
AppendSegment(facade->GetCoordinateOfNode(path_data.node), path_data);
|
||||
}
|
||||
|
||||
const EdgeWeight segment_duration =
|
||||
(traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight);
|
||||
const extractor::TravelMode travel_mode =
|
||||
(traversed_in_reverse ? target_node.backward_travel_mode : target_node.forward_travel_mode);
|
||||
const bool constexpr IS_NECESSARY = true;
|
||||
const bool constexpr IS_VIA_LOCATION = true;
|
||||
segments.emplace_back(target_node.location, target_node.name_id, segment_duration, 0.f,
|
||||
is_via_leg ? extractor::TurnInstruction::ReachViaLocation
|
||||
: extractor::TurnInstruction::NoTurn,
|
||||
IS_NECESSARY, IS_VIA_LOCATION, travel_mode);
|
||||
}
|
||||
|
||||
template <typename DataFacadeT> std::uint32_t SegmentList<DataFacadeT>::GetDistance() const
|
||||
{
|
||||
return total_distance;
|
||||
}
|
||||
template <typename DataFacadeT> std::uint32_t SegmentList<DataFacadeT>::GetDuration() const
|
||||
{
|
||||
return total_duration;
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
std::vector<std::uint32_t> const &SegmentList<DataFacadeT>::GetViaIndices() const
|
||||
{
|
||||
return via_indices;
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
std::vector<SegmentInformation> const &SegmentList<DataFacadeT>::Get() const
|
||||
{
|
||||
return segments;
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
void SegmentList<DataFacadeT>::AppendSegment(const FixedPointCoordinate coordinate,
|
||||
const PathData &path_point)
|
||||
{
|
||||
const auto turn = path_point.turn_instruction;
|
||||
|
||||
segments.emplace_back(coordinate, path_point.name_id, path_point.segment_duration, 0.f, turn,
|
||||
path_point.travel_mode);
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
void SegmentList<DataFacadeT>::Finalize(const bool extract_alternative,
|
||||
const InternalRouteResult &raw_route,
|
||||
const unsigned zoom_level,
|
||||
const bool allow_simplification)
|
||||
{
|
||||
if (segments.empty())
|
||||
return;
|
||||
|
||||
// check if first two segments can be merged
|
||||
BOOST_ASSERT(segments.size() >= 2);
|
||||
if (segments[0].location == segments[1].location &&
|
||||
segments[1].turn_instruction == extractor::TurnInstruction::NoTurn)
|
||||
{
|
||||
segments[0].travel_mode = segments[1].travel_mode;
|
||||
segments[0].name_id = segments[1].name_id;
|
||||
// Other data??
|
||||
segments.erase(segments.begin() + 1);
|
||||
}
|
||||
|
||||
// announce mode changes
|
||||
for (std::size_t i = 0; i + 1 < segments.size(); ++i)
|
||||
{
|
||||
auto &segment = segments[i];
|
||||
const auto next_mode = segments[i + 1].travel_mode;
|
||||
if (segment.travel_mode != next_mode &&
|
||||
segment.turn_instruction == extractor::TurnInstruction::NoTurn)
|
||||
{
|
||||
segment.turn_instruction = extractor::TurnInstruction::GoStraight;
|
||||
segment.necessary = true;
|
||||
}
|
||||
}
|
||||
|
||||
segments[0].length = 0.f;
|
||||
for (const auto i : util::irange<std::size_t>(1, segments.size()))
|
||||
{
|
||||
// move down names by one, q&d hack
|
||||
segments[i - 1].name_id = segments[i].name_id;
|
||||
segments[i].length = util::coordinate_calculation::greatCircleDistance(
|
||||
segments[i - 1].location, segments[i].location);
|
||||
}
|
||||
|
||||
float segment_length = 0.;
|
||||
EdgeWeight segment_duration = 0;
|
||||
std::size_t segment_start_index = 0;
|
||||
|
||||
double path_length = 0;
|
||||
|
||||
for (const auto i : util::irange<std::size_t>(1, segments.size()))
|
||||
{
|
||||
path_length += segments[i].length;
|
||||
segment_length += segments[i].length;
|
||||
segment_duration += segments[i].duration;
|
||||
segments[segment_start_index].length = segment_length;
|
||||
segments[segment_start_index].duration = segment_duration;
|
||||
|
||||
if (extractor::TurnInstruction::NoTurn != segments[i].turn_instruction)
|
||||
{
|
||||
BOOST_ASSERT(segments[i].necessary);
|
||||
segment_length = 0;
|
||||
segment_duration = 0;
|
||||
segment_start_index = i;
|
||||
|
||||
if (segments[i].turn_instruction == extractor::TurnInstruction::NameChanges)
|
||||
{
|
||||
segments[i].turn_instruction =
|
||||
extractor::TurnInstruction::GoStraight; // to not break the api
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
total_distance = static_cast<std::uint32_t>(std::round(path_length));
|
||||
total_duration = static_cast<std::uint32_t>(std::round(
|
||||
(extract_alternative ? raw_route.alternative_path_length : raw_route.shortest_path_length) /
|
||||
10.));
|
||||
|
||||
// Post-processing to remove empty or nearly empty path segments
|
||||
if (segments.size() > 2 && std::numeric_limits<float>::epsilon() > segments.back().length &&
|
||||
!(segments.end() - 2)->is_via_location)
|
||||
{
|
||||
segments.pop_back();
|
||||
segments.back().necessary = true;
|
||||
segments.back().turn_instruction = extractor::TurnInstruction::NoTurn;
|
||||
}
|
||||
|
||||
if (segments.size() > 2 && std::numeric_limits<float>::epsilon() > segments.front().length &&
|
||||
!(segments.begin() + 1)->is_via_location)
|
||||
{
|
||||
segments.erase(segments.begin());
|
||||
segments.front().turn_instruction = extractor::TurnInstruction::HeadOn;
|
||||
segments.front().necessary = true;
|
||||
}
|
||||
|
||||
if (allow_simplification)
|
||||
{
|
||||
douglasPeucker(segments, zoom_level);
|
||||
}
|
||||
|
||||
std::uint32_t necessary_segments = 0; // a running index that counts the necessary pieces
|
||||
via_indices.push_back(0);
|
||||
const auto markNecessarySegments = [this, &necessary_segments](SegmentInformation &first,
|
||||
const SegmentInformation &second)
|
||||
{
|
||||
if (!first.necessary)
|
||||
return;
|
||||
|
||||
// mark the end of a leg (of several segments)
|
||||
if (first.is_via_location)
|
||||
via_indices.push_back(necessary_segments);
|
||||
|
||||
const double post_turn_bearing =
|
||||
util::coordinate_calculation::bearing(first.location, second.location);
|
||||
const double pre_turn_bearing =
|
||||
util::coordinate_calculation::bearing(second.location, first.location);
|
||||
first.post_turn_bearing = static_cast<short>(post_turn_bearing * 10);
|
||||
first.pre_turn_bearing = static_cast<short>(pre_turn_bearing * 10);
|
||||
|
||||
++necessary_segments;
|
||||
};
|
||||
|
||||
// calculate which segments are necessary and update segments for bearings
|
||||
util::for_each_pair(segments, markNecessarySegments);
|
||||
via_indices.push_back(necessary_segments);
|
||||
|
||||
BOOST_ASSERT(via_indices.size() >= 2);
|
||||
}
|
||||
|
||||
template <typename DataFacadeT>
|
||||
SegmentList<DataFacadeT> MakeSegmentList(const InternalRouteResult &raw_route,
|
||||
const bool extract_alternative,
|
||||
const unsigned zoom_level,
|
||||
const bool allow_simplification,
|
||||
const DataFacadeT *facade)
|
||||
{
|
||||
return SegmentList<DataFacadeT>(raw_route, extract_alternative, zoom_level,
|
||||
allow_simplification, facade);
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef ENGINE_GUIDANCE_STEP_MANEUVER_HPP
|
||||
#define ENGINE_GUIDANCE_STEP_MANEUVER_HPP
|
||||
|
||||
#include "util/coordinate.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
enum class WaypointType : std::uint8_t
|
||||
{
|
||||
None,
|
||||
Arrive,
|
||||
Depart,
|
||||
};
|
||||
|
||||
//A represenetation of intermediate intersections
|
||||
struct IntermediateIntersection
|
||||
{
|
||||
double duration;
|
||||
double distance;
|
||||
util::Coordinate location;
|
||||
};
|
||||
|
||||
struct StepManeuver
|
||||
{
|
||||
util::Coordinate location;
|
||||
double bearing_before;
|
||||
double bearing_after;
|
||||
extractor::guidance::TurnInstruction instruction;
|
||||
WaypointType waypoint_type;
|
||||
unsigned exit;
|
||||
std::vector<IntermediateIntersection> intersections;
|
||||
};
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrmn
|
||||
#endif
|
||||
@@ -1,147 +0,0 @@
|
||||
#ifndef ENGINE_GUIDANCE_TEXTUAL_ROUTE_ANNOTATIONS_HPP_
|
||||
#define ENGINE_GUIDANCE_TEXTUAL_ROUTE_ANNOTATIONS_HPP_
|
||||
|
||||
#include "engine/segment_information.hpp"
|
||||
#include "engine/guidance/segment_list.hpp"
|
||||
#include "extractor/turn_instructions.hpp"
|
||||
#include "osrm/json_container.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/cast.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
template <typename DataFacadeT>
|
||||
inline util::json::Array AnnotateRoute(const std::vector<SegmentInformation> &route_segments,
|
||||
DataFacadeT *facade)
|
||||
{
|
||||
util::json::Array json_instruction_array;
|
||||
if (route_segments.empty())
|
||||
return json_instruction_array;
|
||||
// Segment information has following format:
|
||||
//["instruction id","streetname",length,position,time,"length","earth_direction",azimuth]
|
||||
std::int32_t necessary_segments_running_index = 0;
|
||||
|
||||
struct RoundAbout
|
||||
{
|
||||
std::int32_t start_index;
|
||||
std::uint32_t name_id;
|
||||
std::int32_t leave_at_exit;
|
||||
} round_about;
|
||||
|
||||
round_about = {std::numeric_limits<std::int32_t>::max(), 0, 0};
|
||||
std::string temp_dist, temp_length, temp_duration, temp_bearing, temp_instruction;
|
||||
extractor::TravelMode last_travel_mode = TRAVEL_MODE_DEFAULT;
|
||||
|
||||
// Generate annotations for every segment
|
||||
for (std::size_t i = 0; i < route_segments.size(); ++i)
|
||||
{
|
||||
const auto &segment = route_segments[i];
|
||||
util::json::Array json_instruction_row;
|
||||
extractor::TurnInstruction current_instruction = segment.turn_instruction;
|
||||
if (extractor::isTurnNecessary(current_instruction))
|
||||
{
|
||||
if (extractor::TurnInstruction::EnterRoundAbout == current_instruction)
|
||||
{
|
||||
round_about.name_id = segment.name_id;
|
||||
round_about.start_index = necessary_segments_running_index;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string current_turn_instruction;
|
||||
if (extractor::TurnInstruction::LeaveRoundAbout == current_instruction)
|
||||
{
|
||||
temp_instruction = std::to_string(util::cast::enum_to_underlying(
|
||||
extractor::TurnInstruction::EnterRoundAbout));
|
||||
current_turn_instruction += temp_instruction;
|
||||
current_turn_instruction += "-";
|
||||
temp_instruction = std::to_string(round_about.leave_at_exit + 1);
|
||||
current_turn_instruction += temp_instruction;
|
||||
round_about.leave_at_exit = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_instruction =
|
||||
std::to_string(util::cast::enum_to_underlying(current_instruction));
|
||||
current_turn_instruction += temp_instruction;
|
||||
}
|
||||
json_instruction_row.values.emplace_back(std::move(current_turn_instruction));
|
||||
|
||||
json_instruction_row.values.push_back(facade->get_name_for_id(segment.name_id));
|
||||
json_instruction_row.values.push_back(std::round(segment.length));
|
||||
json_instruction_row.values.push_back(necessary_segments_running_index);
|
||||
json_instruction_row.values.push_back(std::round(segment.duration / 10.));
|
||||
json_instruction_row.values.push_back(
|
||||
std::to_string(static_cast<std::uint32_t>(segment.length)) + "m");
|
||||
|
||||
// post turn bearing
|
||||
const double post_turn_bearing_value = (segment.post_turn_bearing / 10.);
|
||||
json_instruction_row.values.push_back(util::bearing::get(post_turn_bearing_value));
|
||||
json_instruction_row.values.push_back(
|
||||
static_cast<std::uint32_t>(std::round(post_turn_bearing_value)));
|
||||
|
||||
if (i + 1 < route_segments.size())
|
||||
{
|
||||
// anounce next travel mode with turn
|
||||
json_instruction_row.values.push_back(route_segments[i + 1].travel_mode);
|
||||
last_travel_mode = segment.travel_mode;
|
||||
}
|
||||
else
|
||||
{
|
||||
json_instruction_row.values.push_back(segment.travel_mode);
|
||||
last_travel_mode = segment.travel_mode;
|
||||
}
|
||||
|
||||
// pre turn bearing
|
||||
const double pre_turn_bearing_value = (segment.pre_turn_bearing / 10.);
|
||||
json_instruction_row.values.push_back(util::bearing::get(pre_turn_bearing_value));
|
||||
json_instruction_row.values.push_back(
|
||||
static_cast<std::uint32_t>(std::round(pre_turn_bearing_value)));
|
||||
|
||||
json_instruction_array.values.push_back(json_instruction_row);
|
||||
}
|
||||
}
|
||||
else if (extractor::TurnInstruction::StayOnRoundAbout == current_instruction)
|
||||
{
|
||||
++round_about.leave_at_exit;
|
||||
}
|
||||
if (segment.necessary)
|
||||
{
|
||||
++necessary_segments_running_index;
|
||||
}
|
||||
}
|
||||
|
||||
util::json::Array json_last_instruction_row;
|
||||
temp_instruction = std::to_string(
|
||||
util::cast::enum_to_underlying(extractor::TurnInstruction::ReachedYourDestination));
|
||||
json_last_instruction_row.values.emplace_back(std::move(temp_instruction));
|
||||
json_last_instruction_row.values.push_back("");
|
||||
json_last_instruction_row.values.push_back(0);
|
||||
json_last_instruction_row.values.push_back(necessary_segments_running_index - 1);
|
||||
json_last_instruction_row.values.push_back(0);
|
||||
json_last_instruction_row.values.push_back("0m");
|
||||
json_last_instruction_row.values.push_back(util::bearing::get(0.0));
|
||||
json_last_instruction_row.values.push_back(0.);
|
||||
json_last_instruction_row.values.push_back(last_travel_mode);
|
||||
json_last_instruction_row.values.push_back(util::bearing::get(0.0));
|
||||
json_last_instruction_row.values.push_back(0.);
|
||||
json_instruction_array.values.emplace_back(std::move(json_last_instruction_row));
|
||||
|
||||
return json_instruction_array;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_
|
||||
#define OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_
|
||||
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
// Silent Turn Instructions are not to be mentioned to the outside world but
|
||||
inline bool isSilent(const extractor::guidance::TurnInstruction instruction)
|
||||
{
|
||||
return instruction.type == extractor::guidance::TurnType::NoTurn ||
|
||||
instruction.type == extractor::guidance::TurnType::Suppressed ||
|
||||
instruction.type == extractor::guidance::TurnType::StayOnRoundabout;
|
||||
}
|
||||
|
||||
inline bool entersRoundabout(const extractor::guidance::TurnInstruction instruction)
|
||||
{
|
||||
return (instruction.type == extractor::guidance::TurnType::EnterRoundabout ||
|
||||
instruction.type == extractor::guidance::TurnType::EnterRotary ||
|
||||
instruction.type == extractor::guidance::TurnType::EnterRoundaboutAtExit ||
|
||||
instruction.type == extractor::guidance::TurnType::EnterRotaryAtExit ||
|
||||
instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
|
||||
instruction.type == extractor::guidance::TurnType::EnterAndExitRotary);
|
||||
}
|
||||
|
||||
inline bool leavesRoundabout(const extractor::guidance::TurnInstruction instruction)
|
||||
{
|
||||
return (instruction.type == extractor::guidance::TurnType::ExitRoundabout ||
|
||||
instruction.type == extractor::guidance::TurnType::ExitRotary ||
|
||||
instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
|
||||
instruction.type == extractor::guidance::TurnType::EnterAndExitRotary);
|
||||
}
|
||||
|
||||
inline bool staysOnRoundabout(const extractor::guidance::TurnInstruction instruction)
|
||||
{
|
||||
return instruction.type == extractor::guidance::TurnType::StayOnRoundabout;
|
||||
}
|
||||
|
||||
inline extractor::guidance::DirectionModifier angleToDirectionModifier(const double bearing)
|
||||
{
|
||||
if (bearing < 135)
|
||||
{
|
||||
return extractor::guidance::DirectionModifier::Right;
|
||||
}
|
||||
|
||||
if (bearing <= 225)
|
||||
{
|
||||
return extractor::guidance::DirectionModifier::Straight;
|
||||
}
|
||||
return extractor::guidance::DirectionModifier::Left;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif /* OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_ */
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ENGINE_HINT_HPP
|
||||
#define ENGINE_HINT_HPP
|
||||
|
||||
#include "engine/phantom_node.hpp"
|
||||
|
||||
#include "util/coordinate.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
|
||||
// Fwd. decls.
|
||||
namespace datafacade
|
||||
{
|
||||
struct BaseDataFacade;
|
||||
}
|
||||
|
||||
// Is returned as a temporary identifier for snapped coodinates
|
||||
struct Hint
|
||||
{
|
||||
PhantomNode phantom;
|
||||
std::uint32_t data_checksum;
|
||||
|
||||
bool IsValid(const util::Coordinate new_input_coordinates,
|
||||
const datafacade::BaseDataFacade &facade) const;
|
||||
|
||||
std::string ToBase64() const;
|
||||
static Hint FromBase64(const std::string &base64Hint);
|
||||
|
||||
friend bool operator==(const Hint &, const Hint &);
|
||||
friend std::ostream &operator<<(std::ostream &, const Hint &);
|
||||
};
|
||||
|
||||
#ifndef _MSC_VER
|
||||
static_assert(sizeof(Hint) == 60 + 4, "Hint is bigger than expected");
|
||||
constexpr std::size_t ENCODED_HINT_SIZE = 88;
|
||||
static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint),
|
||||
"ENCODED_HINT_SIZE does not match size of Hint");
|
||||
#else
|
||||
// PhantomNode is bigger under windows because MSVC does not support bit packing
|
||||
static_assert(sizeof(Hint) == 64 + 4, "Hint is bigger than expected");
|
||||
constexpr std::size_t ENCODED_HINT_SIZE = 92;
|
||||
static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint),
|
||||
"ENCODED_HINT_SIZE does not match size of Hint");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "extractor/turn_instructions.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
@@ -15,28 +15,19 @@ namespace osrm
|
||||
namespace engine
|
||||
{
|
||||
|
||||
const constexpr unsigned INVALID_EXIT_NR = 0;
|
||||
|
||||
struct PathData
|
||||
{
|
||||
PathData()
|
||||
: node(SPECIAL_NODEID), name_id(INVALID_EDGE_WEIGHT), segment_duration(INVALID_EDGE_WEIGHT),
|
||||
turn_instruction(extractor::TurnInstruction::NoTurn),
|
||||
travel_mode(TRAVEL_MODE_INACCESSIBLE)
|
||||
{
|
||||
}
|
||||
|
||||
PathData(NodeID node,
|
||||
unsigned name_id,
|
||||
extractor::TurnInstruction turn_instruction,
|
||||
EdgeWeight segment_duration,
|
||||
extractor::TravelMode travel_mode)
|
||||
: node(node), name_id(name_id), segment_duration(segment_duration),
|
||||
turn_instruction(turn_instruction), travel_mode(travel_mode)
|
||||
{
|
||||
}
|
||||
NodeID node;
|
||||
// id of via node of the turn
|
||||
NodeID turn_via_node;
|
||||
// name of the street that leads to the turn
|
||||
unsigned name_id;
|
||||
EdgeWeight segment_duration;
|
||||
extractor::TurnInstruction turn_instruction;
|
||||
// duration that is traveled on the segment until the turn is reached
|
||||
EdgeWeight duration_until_turn;
|
||||
// instruction to execute at the turn
|
||||
extractor::guidance::TurnInstruction turn_instruction;
|
||||
// travel mode of the street that leads to the turn
|
||||
extractor::TravelMode travel_mode : 4;
|
||||
};
|
||||
|
||||
|
||||
@@ -52,23 +52,21 @@ template <class CandidateLists> struct HiddenMarkovModel
|
||||
{
|
||||
std::vector<std::vector<double>> viterbi;
|
||||
std::vector<std::vector<std::pair<unsigned, unsigned>>> parents;
|
||||
std::vector<std::vector<float>> path_lengths;
|
||||
std::vector<std::vector<float>> path_distances;
|
||||
std::vector<std::vector<bool>> pruned;
|
||||
std::vector<std::vector<bool>> suspicious;
|
||||
std::vector<bool> breakage;
|
||||
|
||||
const CandidateLists &candidates_list;
|
||||
const EmissionLogProbability &emission_log_probability;
|
||||
const std::vector<std::vector<double>> &emission_log_probabilities;
|
||||
|
||||
HiddenMarkovModel(const CandidateLists &candidates_list,
|
||||
const EmissionLogProbability &emission_log_probability)
|
||||
const std::vector<std::vector<double>> &emission_log_probabilities)
|
||||
: breakage(candidates_list.size()), candidates_list(candidates_list),
|
||||
emission_log_probability(emission_log_probability)
|
||||
emission_log_probabilities(emission_log_probabilities)
|
||||
{
|
||||
viterbi.resize(candidates_list.size());
|
||||
parents.resize(candidates_list.size());
|
||||
path_lengths.resize(candidates_list.size());
|
||||
suspicious.resize(candidates_list.size());
|
||||
path_distances.resize(candidates_list.size());
|
||||
pruned.resize(candidates_list.size());
|
||||
breakage.resize(candidates_list.size());
|
||||
for (const auto i : util::irange<std::size_t>(0u, candidates_list.size()))
|
||||
@@ -79,8 +77,7 @@ template <class CandidateLists> struct HiddenMarkovModel
|
||||
{
|
||||
viterbi[i].resize(num_candidates);
|
||||
parents[i].resize(num_candidates);
|
||||
path_lengths[i].resize(num_candidates);
|
||||
suspicious[i].resize(num_candidates);
|
||||
path_distances[i].resize(num_candidates);
|
||||
pruned[i].resize(num_candidates);
|
||||
}
|
||||
}
|
||||
@@ -90,15 +87,14 @@ template <class CandidateLists> struct HiddenMarkovModel
|
||||
|
||||
void clear(std::size_t initial_timestamp)
|
||||
{
|
||||
BOOST_ASSERT(viterbi.size() == parents.size() && parents.size() == path_lengths.size() &&
|
||||
path_lengths.size() == pruned.size() && pruned.size() == breakage.size());
|
||||
BOOST_ASSERT(viterbi.size() == parents.size() && parents.size() == path_distances.size() &&
|
||||
path_distances.size() == pruned.size() && pruned.size() == breakage.size());
|
||||
|
||||
for (const auto t : util::irange(initial_timestamp, viterbi.size()))
|
||||
{
|
||||
std::fill(viterbi[t].begin(), viterbi[t].end(), IMPOSSIBLE_LOG_PROB);
|
||||
std::fill(parents[t].begin(), parents[t].end(), std::make_pair(0u, 0u));
|
||||
std::fill(path_lengths[t].begin(), path_lengths[t].end(), 0);
|
||||
std::fill(suspicious[t].begin(), suspicious[t].end(), true);
|
||||
std::fill(path_distances[t].begin(), path_distances[t].end(), 0);
|
||||
std::fill(pruned[t].begin(), pruned[t].end(), true);
|
||||
}
|
||||
std::fill(breakage.begin() + initial_timestamp, breakage.end(), true);
|
||||
@@ -113,11 +109,9 @@ template <class CandidateLists> struct HiddenMarkovModel
|
||||
|
||||
for (const auto s : util::irange<std::size_t>(0u, viterbi[initial_timestamp].size()))
|
||||
{
|
||||
viterbi[initial_timestamp][s] =
|
||||
emission_log_probability(candidates_list[initial_timestamp][s].distance);
|
||||
viterbi[initial_timestamp][s] = emission_log_probabilities[initial_timestamp][s];
|
||||
parents[initial_timestamp][s] = std::make_pair(initial_timestamp, s);
|
||||
pruned[initial_timestamp][s] = viterbi[initial_timestamp][s] < MINIMAL_LOG_PROB;
|
||||
suspicious[initial_timestamp][s] = false;
|
||||
|
||||
breakage[initial_timestamp] =
|
||||
breakage[initial_timestamp] && pruned[initial_timestamp][s];
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef ENGINE_MAP_MATCHING_CONFIDENCE_HPP
|
||||
#define ENGINE_MAP_MATCHING_CONFIDENCE_HPP
|
||||
|
||||
#include "engine/map_matching/bayes_classifier.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace map_matching
|
||||
{
|
||||
|
||||
struct MatchingConfidence
|
||||
{
|
||||
private:
|
||||
using ClassifierT = BayesClassifier<LaplaceDistribution, LaplaceDistribution, double>;
|
||||
using TraceClassification = ClassifierT::ClassificationT;
|
||||
|
||||
public:
|
||||
MatchingConfidence()
|
||||
: // the values were derived from fitting a laplace distribution
|
||||
// to the values of manually classified traces
|
||||
classifier(map_matching::LaplaceDistribution(0.005986, 0.016646),
|
||||
map_matching::LaplaceDistribution(0.054385, 0.458432),
|
||||
0.696774) // valid apriori probability
|
||||
{
|
||||
}
|
||||
|
||||
double operator()(const float trace_length, const float matched_length) const
|
||||
{
|
||||
const double distance_feature = -std::log(trace_length) + std::log(matched_length);
|
||||
|
||||
// matched to the same point
|
||||
if (!std::isfinite(distance_feature))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto label_with_confidence = classifier.classify(distance_feature);
|
||||
if (label_with_confidence.first == ClassifierT::ClassLabel::POSITIVE)
|
||||
{
|
||||
return label_with_confidence.second;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(label_with_confidence.first == ClassifierT::ClassLabel::NEGATIVE);
|
||||
return 1 - label_with_confidence.second;
|
||||
}
|
||||
|
||||
private:
|
||||
ClassifierT classifier;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef MAP_MATCHING_SUB_MATCHING_HPP
|
||||
#define MAP_MATCHING_SUB_MATCHING_HPP
|
||||
|
||||
#include "engine/phantom_node.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace map_matching
|
||||
{
|
||||
|
||||
struct SubMatching
|
||||
{
|
||||
std::vector<PhantomNode> nodes;
|
||||
std::vector<unsigned> indices;
|
||||
double confidence;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,91 +0,0 @@
|
||||
#ifndef OBJECT_ENCODER_HPP
|
||||
#define OBJECT_ENCODER_HPP
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/archive/iterators/base64_from_binary.hpp>
|
||||
#include <boost/archive/iterators/binary_from_base64.hpp>
|
||||
#include <boost/archive/iterators/transform_width.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
#include <climits>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
static_assert(CHAR_BIT == 8u, "we assume a byte holds 8 bits");
|
||||
static_assert(sizeof(char) == 1u, "we assume a char is one byte large");
|
||||
|
||||
using Base64FromBinary = boost::archive::iterators::base64_from_binary<
|
||||
boost::archive::iterators::transform_width<const char *, // sequence of chars
|
||||
6, // get view of 6 bit
|
||||
8 // from sequence of 8 bit
|
||||
>>;
|
||||
|
||||
using BinaryFromBase64 = boost::archive::iterators::transform_width<
|
||||
boost::archive::iterators::binary_from_base64<std::string::const_iterator>,
|
||||
8, // get a view of 8 bit
|
||||
6 // from a sequence of 6 bit
|
||||
>;
|
||||
} // ns detail
|
||||
|
||||
template <typename T> std::string encodeBase64(const T &x)
|
||||
{
|
||||
#if not defined __GNUC__ or __GNUC__ > 4
|
||||
static_assert(std::is_trivially_copyable<T>::value, "requires a trivially copyable type");
|
||||
#endif
|
||||
|
||||
std::vector<unsigned char> bytes{reinterpret_cast<const char *>(&x),
|
||||
reinterpret_cast<const char *>(&x) + sizeof(T)};
|
||||
BOOST_ASSERT(!bytes.empty());
|
||||
|
||||
std::size_t bytes_to_pad{0};
|
||||
|
||||
while (bytes.size() % 3 != 0)
|
||||
{
|
||||
bytes_to_pad += 1;
|
||||
bytes.push_back(0);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(bytes_to_pad == 0 || bytes_to_pad == 1 || bytes_to_pad == 2);
|
||||
BOOST_ASSERT_MSG(0 == bytes.size() % 3, "base64 input data size is not a multiple of 3");
|
||||
|
||||
std::string encoded{detail::Base64FromBinary{bytes.data()},
|
||||
detail::Base64FromBinary{bytes.data() + (bytes.size() - bytes_to_pad)}};
|
||||
|
||||
std::replace(begin(encoded), end(encoded), '+', '-');
|
||||
std::replace(begin(encoded), end(encoded), '/', '_');
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
template <typename T> T decodeBase64(std::string encoded)
|
||||
{
|
||||
#if not defined __GNUC__ or __GNUC__ > 4
|
||||
static_assert(std::is_trivially_copyable<T>::value, "requires a trivially copyable type");
|
||||
#endif
|
||||
|
||||
std::replace(begin(encoded), end(encoded), '-', '+');
|
||||
std::replace(begin(encoded), end(encoded), '_', '/');
|
||||
|
||||
T rv;
|
||||
|
||||
std::copy(detail::BinaryFromBase64{begin(encoded)},
|
||||
detail::BinaryFromBase64{begin(encoded) + encoded.length()},
|
||||
reinterpret_cast<char *>(&rv));
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // ns engine
|
||||
} // ns osrm
|
||||
|
||||
#endif /* OBJECT_ENCODER_HPP */
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef PHANTOM_NODES_H
|
||||
#define PHANTOM_NODES_H
|
||||
|
||||
@@ -28,7 +55,8 @@ struct PhantomNode
|
||||
unsigned reverse_packed_geometry_id_,
|
||||
bool is_tiny_component,
|
||||
unsigned component_id,
|
||||
util::FixedPointCoordinate location,
|
||||
util::Coordinate location,
|
||||
util::Coordinate input_location,
|
||||
unsigned short fwd_segment_position,
|
||||
extractor::TravelMode forward_travel_mode,
|
||||
extractor::TravelMode backward_travel_mode)
|
||||
@@ -37,8 +65,8 @@ struct PhantomNode
|
||||
forward_offset(forward_offset), reverse_offset(reverse_offset),
|
||||
forward_packed_geometry_id(forward_packed_geometry_id_),
|
||||
reverse_packed_geometry_id(reverse_packed_geometry_id_),
|
||||
component{component_id, is_tiny_component},
|
||||
location(std::move(location)), fwd_segment_position(fwd_segment_position),
|
||||
component{component_id, is_tiny_component}, location(std::move(location)),
|
||||
input_location(std::move(input_location)), fwd_segment_position(fwd_segment_position),
|
||||
forward_travel_mode(forward_travel_mode), backward_travel_mode(backward_travel_mode)
|
||||
{
|
||||
}
|
||||
@@ -47,10 +75,9 @@ struct PhantomNode
|
||||
: forward_node_id(SPECIAL_NODEID), reverse_node_id(SPECIAL_NODEID),
|
||||
name_id(std::numeric_limits<unsigned>::max()), forward_weight(INVALID_EDGE_WEIGHT),
|
||||
reverse_weight(INVALID_EDGE_WEIGHT), forward_offset(0), reverse_offset(0),
|
||||
forward_packed_geometry_id(SPECIAL_EDGEID),
|
||||
reverse_packed_geometry_id(SPECIAL_EDGEID),
|
||||
component{INVALID_COMPONENTID, false},
|
||||
fwd_segment_position(0), forward_travel_mode(TRAVEL_MODE_INACCESSIBLE),
|
||||
forward_packed_geometry_id(SPECIAL_EDGEID), reverse_packed_geometry_id(SPECIAL_EDGEID),
|
||||
component{INVALID_COMPONENTID, false}, fwd_segment_position(0),
|
||||
forward_travel_mode(TRAVEL_MODE_INACCESSIBLE),
|
||||
backward_travel_mode(TRAVEL_MODE_INACCESSIBLE)
|
||||
{
|
||||
}
|
||||
@@ -89,12 +116,23 @@ struct PhantomNode
|
||||
(component.id != INVALID_COMPONENTID) && (name_id != INVALID_NAMEID);
|
||||
}
|
||||
|
||||
bool IsValid(const unsigned number_of_nodes, const util::Coordinate queried_coordinate) const
|
||||
{
|
||||
return queried_coordinate == input_location && IsValid(number_of_nodes);
|
||||
}
|
||||
|
||||
bool IsValid() const { return location.IsValid() && (name_id != INVALID_NAMEID); }
|
||||
|
||||
bool operator==(const PhantomNode &other) const { return location == other.location; }
|
||||
|
||||
template <class OtherT>
|
||||
PhantomNode(const OtherT &other, int forward_weight_, int forward_offset_, int reverse_weight_, int reverse_offset_, const util::FixedPointCoordinate foot_point)
|
||||
explicit PhantomNode(const OtherT &other,
|
||||
int forward_weight_,
|
||||
int forward_offset_,
|
||||
int reverse_weight_,
|
||||
int reverse_offset_,
|
||||
const util::Coordinate location_,
|
||||
const util::Coordinate input_location_)
|
||||
{
|
||||
forward_node_id = other.forward_edge_based_node_id;
|
||||
reverse_node_id = other.reverse_edge_based_node_id;
|
||||
@@ -112,7 +150,8 @@ struct PhantomNode
|
||||
component.id = other.component.id;
|
||||
component.is_tiny = other.component.is_tiny;
|
||||
|
||||
location = foot_point;
|
||||
location = location_;
|
||||
input_location = input_location_;
|
||||
fwd_segment_position = other.fwd_segment_position;
|
||||
|
||||
forward_travel_mode = other.forward_travel_mode;
|
||||
@@ -137,7 +176,8 @@ struct PhantomNode
|
||||
#ifndef _MSC_VER
|
||||
static_assert(sizeof(ComponentType) == 4, "ComponentType needs to 4 bytes big");
|
||||
#endif
|
||||
util::FixedPointCoordinate location;
|
||||
util::Coordinate location;
|
||||
util::Coordinate input_location;
|
||||
unsigned short fwd_segment_position;
|
||||
// note 4 bits would suffice for each,
|
||||
// but the saved byte would be padding anyway
|
||||
@@ -146,7 +186,9 @@ struct PhantomNode
|
||||
};
|
||||
|
||||
#ifndef _MSC_VER
|
||||
static_assert(sizeof(PhantomNode) == 52, "PhantomNode has more padding then expected");
|
||||
static_assert(sizeof(PhantomNode) == 60, "PhantomNode has more padding then expected");
|
||||
#else
|
||||
static_assert(sizeof(PhantomNode) == 64, "PhantomNode has more padding then expected");
|
||||
#endif
|
||||
|
||||
using PhantomNodePair = std::pair<PhantomNode, PhantomNode>;
|
||||
|
||||
@@ -1,250 +0,0 @@
|
||||
#ifndef DISTANCE_TABLE_HPP
|
||||
#define DISTANCE_TABLE_HPP
|
||||
|
||||
#include "engine/plugins/plugin_base.hpp"
|
||||
|
||||
#include "engine/object_encoder.hpp"
|
||||
#include "engine/search_engine.hpp"
|
||||
#include "util/make_unique.hpp"
|
||||
#include "util/string_util.hpp"
|
||||
#include "osrm/json_container.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace plugins
|
||||
{
|
||||
|
||||
template <class DataFacadeT> class DistanceTablePlugin final : public BasePlugin
|
||||
{
|
||||
private:
|
||||
std::unique_ptr<SearchEngine<DataFacadeT>> search_engine_ptr;
|
||||
int max_locations_distance_table;
|
||||
|
||||
public:
|
||||
explicit DistanceTablePlugin(DataFacadeT *facade, const int max_locations_distance_table)
|
||||
: max_locations_distance_table(max_locations_distance_table), descriptor_string("table"),
|
||||
facade(facade)
|
||||
{
|
||||
search_engine_ptr = util::make_unique<SearchEngine<DataFacadeT>>(facade);
|
||||
}
|
||||
|
||||
virtual ~DistanceTablePlugin() {}
|
||||
|
||||
const std::string GetDescriptor() const override final { return descriptor_string; }
|
||||
|
||||
Status HandleRequest(const RouteParameters &route_parameters,
|
||||
util::json::Object &json_result) override final
|
||||
{
|
||||
return NonConstHandleRequest(route_parameters, json_result);
|
||||
}
|
||||
|
||||
// XXX: should be const-ref, but we need to artificially source, destination values
|
||||
// so consider this a hack for 4.9, in 5.0 we refactored and handle it beautifully!
|
||||
Status NonConstHandleRequest(RouteParameters route_parameters, util::json::Object &json_result)
|
||||
{
|
||||
if (!check_all_coordinates(route_parameters.coordinates))
|
||||
{
|
||||
json_result.values["status_message"] = "Coordinates are invalid";
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
const auto &input_bearings = route_parameters.bearings;
|
||||
if (input_bearings.size() > 0 &&
|
||||
route_parameters.coordinates.size() != input_bearings.size())
|
||||
{
|
||||
json_result.values["status_message"] =
|
||||
"Number of bearings does not match number of coordinates";
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
const auto number_of_coordinates = route_parameters.coordinates.size();
|
||||
|
||||
BOOST_ASSERT(route_parameters.is_source.size() <= number_of_coordinates);
|
||||
BOOST_ASSERT(route_parameters.is_destination.size() <= number_of_coordinates);
|
||||
|
||||
// The check_all_coordinates guard above makes sure we have at least 2 coordinates.
|
||||
// This establishes the parallel array invariant for is_source, is_destination, coordinates
|
||||
if (route_parameters.is_source.size() == 0)
|
||||
{
|
||||
const auto where = route_parameters.is_source.end();
|
||||
const auto n = number_of_coordinates - route_parameters.is_source.size();
|
||||
route_parameters.is_source.insert(where, n, true);
|
||||
}
|
||||
|
||||
if (route_parameters.is_destination.size() == 0)
|
||||
{
|
||||
const auto where = route_parameters.is_destination.end();
|
||||
const auto n = number_of_coordinates - route_parameters.is_destination.size();
|
||||
route_parameters.is_destination.insert(where, n, true);
|
||||
}
|
||||
|
||||
// parallel array invariant
|
||||
BOOST_ASSERT(route_parameters.coordinates.size() == route_parameters.is_source.size());
|
||||
BOOST_ASSERT(route_parameters.coordinates.size() == route_parameters.is_destination.size());
|
||||
|
||||
const auto number_of_sources = std::count(route_parameters.is_source.begin(), //
|
||||
route_parameters.is_source.end(), true);
|
||||
const auto number_of_destination = std::count(route_parameters.is_destination.begin(), //
|
||||
route_parameters.is_destination.end(), true);
|
||||
|
||||
if (max_locations_distance_table > 0 &&
|
||||
(number_of_sources * number_of_destination >
|
||||
max_locations_distance_table * max_locations_distance_table))
|
||||
{
|
||||
json_result.values["status_message"] =
|
||||
"Number of entries " + std::to_string(number_of_sources * number_of_destination) +
|
||||
" is higher than current maximum (" +
|
||||
std::to_string(max_locations_distance_table * max_locations_distance_table) + ")";
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
const bool checksum_OK = (route_parameters.check_sum == facade->GetCheckSum());
|
||||
|
||||
std::vector<PhantomNodePair> phantom_node_source_vector(number_of_sources);
|
||||
std::vector<PhantomNodePair> phantom_node_target_vector(number_of_destination);
|
||||
auto phantom_node_source_out_iter = phantom_node_source_vector.begin();
|
||||
auto phantom_node_target_out_iter = phantom_node_target_vector.begin();
|
||||
for (const auto i : util::irange<std::size_t>(0u, route_parameters.coordinates.size()))
|
||||
{
|
||||
if (checksum_OK && i < route_parameters.hints.size() &&
|
||||
!route_parameters.hints[i].empty())
|
||||
{
|
||||
auto current_phantom_node = decodeBase64<PhantomNode>(route_parameters.hints[i]);
|
||||
if (current_phantom_node.IsValid(facade->GetNumberOfNodes()))
|
||||
{
|
||||
if (route_parameters.is_source[i])
|
||||
{
|
||||
*phantom_node_source_out_iter =
|
||||
std::make_pair(current_phantom_node, current_phantom_node);
|
||||
if (route_parameters.is_destination[i])
|
||||
{
|
||||
*phantom_node_target_out_iter = *phantom_node_source_out_iter;
|
||||
phantom_node_target_out_iter++;
|
||||
}
|
||||
phantom_node_source_out_iter++;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(route_parameters.is_destination[i] &&
|
||||
!route_parameters.is_source[i]);
|
||||
*phantom_node_target_out_iter =
|
||||
std::make_pair(current_phantom_node, current_phantom_node);
|
||||
phantom_node_target_out_iter++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const int bearing = input_bearings.size() > 0 ? input_bearings[i].first : 0;
|
||||
const int range = input_bearings.size() > 0
|
||||
? (input_bearings[i].second ? *input_bearings[i].second : 10)
|
||||
: 180;
|
||||
|
||||
if (route_parameters.is_source[i])
|
||||
{
|
||||
*phantom_node_source_out_iter =
|
||||
facade->NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
route_parameters.coordinates[i], bearing, range);
|
||||
// we didn't found a fitting node, return error
|
||||
if (!phantom_node_source_out_iter->first.IsValid(facade->GetNumberOfNodes()))
|
||||
{
|
||||
json_result.values["status_message"] =
|
||||
std::string("Could not find a matching segment for coordinate ") +
|
||||
std::to_string(i);
|
||||
return Status::NoSegment;
|
||||
}
|
||||
|
||||
if (route_parameters.is_destination[i])
|
||||
{
|
||||
*phantom_node_target_out_iter = *phantom_node_source_out_iter;
|
||||
phantom_node_target_out_iter++;
|
||||
}
|
||||
phantom_node_source_out_iter++;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(route_parameters.is_destination[i] && !route_parameters.is_source[i]);
|
||||
|
||||
*phantom_node_target_out_iter =
|
||||
facade->NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
route_parameters.coordinates[i], bearing, range);
|
||||
// we didn't found a fitting node, return error
|
||||
if (!phantom_node_target_out_iter->first.IsValid(facade->GetNumberOfNodes()))
|
||||
{
|
||||
json_result.values["status_message"] =
|
||||
std::string("Could not find a matching segment for coordinate ") +
|
||||
std::to_string(i);
|
||||
return Status::NoSegment;
|
||||
}
|
||||
phantom_node_target_out_iter++;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_ASSERT((phantom_node_source_out_iter - phantom_node_source_vector.begin()) ==
|
||||
number_of_sources);
|
||||
BOOST_ASSERT((phantom_node_target_out_iter - phantom_node_target_vector.begin()) ==
|
||||
number_of_destination);
|
||||
|
||||
// FIXME we should clear phantom_node_source_vector and phantom_node_target_vector after
|
||||
// this
|
||||
auto snapped_source_phantoms = snapPhantomNodes(phantom_node_source_vector);
|
||||
auto snapped_target_phantoms = snapPhantomNodes(phantom_node_target_vector);
|
||||
|
||||
auto result_table =
|
||||
search_engine_ptr->distance_table(snapped_source_phantoms, snapped_target_phantoms);
|
||||
|
||||
if (!result_table)
|
||||
{
|
||||
json_result.values["status_message"] = "No distance table found";
|
||||
return Status::EmptyResult;
|
||||
}
|
||||
|
||||
util::json::Array matrix_json_array;
|
||||
for (const auto row : util::irange<std::size_t>(0, number_of_sources))
|
||||
{
|
||||
util::json::Array json_row;
|
||||
auto row_begin_iterator = result_table->begin() + (row * number_of_destination);
|
||||
auto row_end_iterator = result_table->begin() + ((row + 1) * number_of_destination);
|
||||
json_row.values.insert(json_row.values.end(), row_begin_iterator, row_end_iterator);
|
||||
matrix_json_array.values.push_back(json_row);
|
||||
}
|
||||
json_result.values["distance_table"] = std::move(matrix_json_array);
|
||||
|
||||
util::json::Array target_coord_json_array;
|
||||
for (const auto &phantom : snapped_target_phantoms)
|
||||
{
|
||||
util::json::Array json_coord;
|
||||
json_coord.values.push_back(phantom.location.lat / COORDINATE_PRECISION);
|
||||
json_coord.values.push_back(phantom.location.lon / COORDINATE_PRECISION);
|
||||
target_coord_json_array.values.push_back(json_coord);
|
||||
}
|
||||
json_result.values["destination_coordinates"] = std::move(target_coord_json_array);
|
||||
util::json::Array source_coord_json_array;
|
||||
for (const auto &phantom : snapped_source_phantoms)
|
||||
{
|
||||
util::json::Array json_coord;
|
||||
json_coord.values.push_back(phantom.location.lat / COORDINATE_PRECISION);
|
||||
json_coord.values.push_back(phantom.location.lon / COORDINATE_PRECISION);
|
||||
source_coord_json_array.values.push_back(json_coord);
|
||||
}
|
||||
json_result.values["source_coordinates"] = std::move(source_coord_json_array);
|
||||
return Status::Ok;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string descriptor_string;
|
||||
DataFacadeT *facade;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DISTANCE_TABLE_HPP
|
||||
@@ -2,25 +2,13 @@
|
||||
#define MATCH_HPP
|
||||
|
||||
#include "engine/plugins/plugin_base.hpp"
|
||||
#include "engine/api/match_parameters.hpp"
|
||||
|
||||
#include "engine/map_matching/bayes_classifier.hpp"
|
||||
#include "engine/object_encoder.hpp"
|
||||
#include "engine/search_engine.hpp"
|
||||
#include "engine/guidance/textual_route_annotation.hpp"
|
||||
#include "engine/guidance/segment_list.hpp"
|
||||
#include "engine/api_response_generator.hpp"
|
||||
#include "engine/routing_algorithms/map_matching.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/json_logger.hpp"
|
||||
#include "engine/routing_algorithms/shortest_path.hpp"
|
||||
#include "util/json_util.hpp"
|
||||
#include "util/string_util.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
@@ -30,356 +18,28 @@ namespace engine
|
||||
namespace plugins
|
||||
{
|
||||
|
||||
template <class DataFacadeT> class MapMatchingPlugin : public BasePlugin
|
||||
class MatchPlugin : public BasePlugin
|
||||
{
|
||||
std::shared_ptr<SearchEngine<DataFacadeT>> search_engine_ptr;
|
||||
|
||||
using SubMatching = routing_algorithms::SubMatching;
|
||||
public:
|
||||
using SubMatching = map_matching::SubMatching;
|
||||
using SubMatchingList = routing_algorithms::SubMatchingList;
|
||||
using CandidateLists = routing_algorithms::CandidateLists;
|
||||
using ClassifierT = map_matching::BayesClassifier<map_matching::LaplaceDistribution,
|
||||
map_matching::LaplaceDistribution,
|
||||
double>;
|
||||
using TraceClassification = ClassifierT::ClassificationT;
|
||||
static const constexpr double DEFAULT_GPS_PRECISION = 5;
|
||||
static const constexpr double RADIUS_MULTIPLIER = 3;
|
||||
|
||||
public:
|
||||
MapMatchingPlugin(DataFacadeT *facade, const int max_locations_map_matching)
|
||||
: descriptor_string("match"), facade(facade),
|
||||
max_locations_map_matching(max_locations_map_matching),
|
||||
// the values were derived from fitting a laplace distribution
|
||||
// to the values of manually classified traces
|
||||
classifier(map_matching::LaplaceDistribution(0.005986, 0.016646),
|
||||
map_matching::LaplaceDistribution(0.054385, 0.458432),
|
||||
0.696774) // valid apriori probability
|
||||
MatchPlugin(datafacade::BaseDataFacade &facade_, const int max_locations_map_matching)
|
||||
: BasePlugin(facade_), map_matching(&facade_, heaps, DEFAULT_GPS_PRECISION),
|
||||
shortest_path(&facade_, heaps), max_locations_map_matching(max_locations_map_matching)
|
||||
{
|
||||
search_engine_ptr = std::make_shared<SearchEngine<DataFacadeT>>(facade);
|
||||
}
|
||||
|
||||
virtual ~MapMatchingPlugin() {}
|
||||
|
||||
const std::string GetDescriptor() const final override { return descriptor_string; }
|
||||
|
||||
TraceClassification
|
||||
classify(const float trace_length, const float matched_length, const int removed_points) const
|
||||
{
|
||||
(void)removed_points; // unused
|
||||
|
||||
const double distance_feature = -std::log(trace_length) + std::log(matched_length);
|
||||
|
||||
// matched to the same point
|
||||
if (!std::isfinite(distance_feature))
|
||||
{
|
||||
return std::make_pair(ClassifierT::ClassLabel::NEGATIVE, 1.0);
|
||||
}
|
||||
|
||||
const auto label_with_confidence = classifier.classify(distance_feature);
|
||||
|
||||
return label_with_confidence;
|
||||
}
|
||||
|
||||
CandidateLists getCandidates(
|
||||
const std::vector<util::FixedPointCoordinate> &input_coords,
|
||||
const std::vector<std::pair<const int, const boost::optional<int>>> &input_bearings,
|
||||
const double gps_precision,
|
||||
std::vector<double> &sub_trace_lengths)
|
||||
{
|
||||
CandidateLists candidates_lists;
|
||||
|
||||
// assuming gps_precision is the standard deviation of a normal distribution that
|
||||
// models GPS noise (in this model), this should give us the correct search radius
|
||||
// with > 99% confidence
|
||||
double query_radius = 3 * gps_precision;
|
||||
double last_distance =
|
||||
util::coordinate_calculation::haversineDistance(input_coords[0], input_coords[1]);
|
||||
|
||||
sub_trace_lengths.resize(input_coords.size());
|
||||
sub_trace_lengths[0] = 0;
|
||||
for (const auto current_coordinate : util::irange<std::size_t>(0, input_coords.size()))
|
||||
{
|
||||
bool allow_uturn = false;
|
||||
if (0 < current_coordinate)
|
||||
{
|
||||
last_distance = util::coordinate_calculation::haversineDistance(
|
||||
input_coords[current_coordinate - 1], input_coords[current_coordinate]);
|
||||
|
||||
sub_trace_lengths[current_coordinate] +=
|
||||
sub_trace_lengths[current_coordinate - 1] + last_distance;
|
||||
}
|
||||
|
||||
if (input_coords.size() - 1 > current_coordinate && 0 < current_coordinate)
|
||||
{
|
||||
double turn_angle = util::coordinate_calculation::computeAngle(
|
||||
input_coords[current_coordinate - 1], input_coords[current_coordinate],
|
||||
input_coords[current_coordinate + 1]);
|
||||
|
||||
// sharp turns indicate a possible uturn
|
||||
if (turn_angle <= 90.0 || turn_angle >= 270.0)
|
||||
{
|
||||
allow_uturn = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Use bearing values if supplied, otherwise fallback to 0,180 defaults
|
||||
auto bearing = input_bearings.size() > 0 ? input_bearings[current_coordinate].first : 0;
|
||||
auto range = input_bearings.size() > 0
|
||||
? (input_bearings[current_coordinate].second
|
||||
? *input_bearings[current_coordinate].second
|
||||
: 10)
|
||||
: 180;
|
||||
auto candidates = facade->NearestPhantomNodesInRange(input_coords[current_coordinate],
|
||||
query_radius, bearing, range);
|
||||
|
||||
if (candidates.size() == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// sort by forward id, then by reverse id and then by distance
|
||||
std::sort(
|
||||
candidates.begin(), candidates.end(),
|
||||
[](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs)
|
||||
{
|
||||
return lhs.phantom_node.forward_node_id < rhs.phantom_node.forward_node_id ||
|
||||
(lhs.phantom_node.forward_node_id == rhs.phantom_node.forward_node_id &&
|
||||
(lhs.phantom_node.reverse_node_id < rhs.phantom_node.reverse_node_id ||
|
||||
(lhs.phantom_node.reverse_node_id ==
|
||||
rhs.phantom_node.reverse_node_id &&
|
||||
lhs.distance < rhs.distance)));
|
||||
});
|
||||
|
||||
auto new_end = std::unique(
|
||||
candidates.begin(), candidates.end(),
|
||||
[](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs)
|
||||
{
|
||||
return lhs.phantom_node.forward_node_id == rhs.phantom_node.forward_node_id &&
|
||||
lhs.phantom_node.reverse_node_id == rhs.phantom_node.reverse_node_id;
|
||||
});
|
||||
candidates.resize(new_end - candidates.begin());
|
||||
|
||||
if (!allow_uturn)
|
||||
{
|
||||
const auto compact_size = candidates.size();
|
||||
for (const auto i : util::irange<std::size_t>(0, compact_size))
|
||||
{
|
||||
// Split edge if it is bidirectional and append reverse direction to end of list
|
||||
if (candidates[i].phantom_node.forward_node_id != SPECIAL_NODEID &&
|
||||
candidates[i].phantom_node.reverse_node_id != SPECIAL_NODEID)
|
||||
{
|
||||
PhantomNode reverse_node(candidates[i].phantom_node);
|
||||
reverse_node.forward_node_id = SPECIAL_NODEID;
|
||||
candidates.push_back(
|
||||
PhantomNodeWithDistance{reverse_node, candidates[i].distance});
|
||||
|
||||
candidates[i].phantom_node.reverse_node_id = SPECIAL_NODEID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort by distance to make pruning effective
|
||||
std::sort(candidates.begin(), candidates.end(),
|
||||
[](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs)
|
||||
{
|
||||
return lhs.distance < rhs.distance;
|
||||
});
|
||||
|
||||
candidates_lists.push_back(std::move(candidates));
|
||||
}
|
||||
|
||||
return candidates_lists;
|
||||
}
|
||||
|
||||
util::json::Object submatchingToJSON(const SubMatching &sub,
|
||||
const RouteParameters &route_parameters,
|
||||
const InternalRouteResult &raw_route)
|
||||
{
|
||||
util::json::Object subtrace;
|
||||
|
||||
if (route_parameters.classify)
|
||||
{
|
||||
subtrace.values["confidence"] = sub.confidence;
|
||||
}
|
||||
|
||||
auto response_generator = MakeApiResponseGenerator(facade);
|
||||
|
||||
subtrace.values["hint_data"] = response_generator.BuildHintData(raw_route);
|
||||
|
||||
if (route_parameters.geometry || route_parameters.print_instructions)
|
||||
{
|
||||
using SegmentList = guidance::SegmentList<DataFacadeT>;
|
||||
// Passing false to extract_alternative extracts the route.
|
||||
const constexpr bool EXTRACT_ROUTE = false;
|
||||
// by passing false to segment_list, we skip the douglas peucker simplification
|
||||
// and mark all segments as necessary within the generation process
|
||||
const constexpr bool NO_ROUTE_SIMPLIFICATION = false;
|
||||
SegmentList segment_list(raw_route, EXTRACT_ROUTE, route_parameters.zoom_level,
|
||||
NO_ROUTE_SIMPLIFICATION, facade);
|
||||
|
||||
if (route_parameters.geometry)
|
||||
{
|
||||
subtrace.values["geometry"] =
|
||||
response_generator.GetGeometry(route_parameters.compression, segment_list);
|
||||
}
|
||||
|
||||
if (route_parameters.print_instructions)
|
||||
{
|
||||
subtrace.values["instructions"] =
|
||||
guidance::AnnotateRoute<DataFacadeT>(segment_list.Get(), facade);
|
||||
}
|
||||
|
||||
util::json::Object json_route_summary;
|
||||
json_route_summary.values["total_distance"] = segment_list.GetDistance();
|
||||
json_route_summary.values["total_time"] = segment_list.GetDuration();
|
||||
subtrace.values["route_summary"] = json_route_summary;
|
||||
}
|
||||
|
||||
subtrace.values["indices"] = util::json::make_array(sub.indices);
|
||||
|
||||
util::json::Array points;
|
||||
for (const auto &node : sub.nodes)
|
||||
{
|
||||
points.values.emplace_back(
|
||||
util::json::make_array(node.location.lat / COORDINATE_PRECISION,
|
||||
node.location.lon / COORDINATE_PRECISION));
|
||||
}
|
||||
subtrace.values["matched_points"] = points;
|
||||
|
||||
util::json::Array names;
|
||||
for (const auto &node : sub.nodes)
|
||||
{
|
||||
names.values.emplace_back(facade->get_name_for_id(node.name_id));
|
||||
}
|
||||
subtrace.values["matched_names"] = names;
|
||||
|
||||
return subtrace;
|
||||
}
|
||||
|
||||
Status HandleRequest(const RouteParameters &route_parameters,
|
||||
util::json::Object &json_result) final override
|
||||
{
|
||||
// enforce maximum number of locations for performance reasons
|
||||
if (max_locations_map_matching > 0 &&
|
||||
static_cast<int>(route_parameters.coordinates.size()) > max_locations_map_matching)
|
||||
{
|
||||
json_result.values["status_message"] = "Too many coordinates";
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
// check number of parameters
|
||||
if (!check_all_coordinates(route_parameters.coordinates))
|
||||
{
|
||||
json_result.values["status_message"] = "Invalid coordinates";
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
std::vector<double> sub_trace_lengths;
|
||||
const auto &input_coords = route_parameters.coordinates;
|
||||
const auto &input_timestamps = route_parameters.timestamps;
|
||||
const auto &input_bearings = route_parameters.bearings;
|
||||
if (input_timestamps.size() > 0 && input_coords.size() != input_timestamps.size())
|
||||
{
|
||||
json_result.values["status_message"] =
|
||||
"Number of timestamps does not match number of coordinates";
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
if (input_bearings.size() > 0 && input_coords.size() != input_bearings.size())
|
||||
{
|
||||
json_result.values["status_message"] =
|
||||
"Number of bearings does not match number of coordinates";
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
// at least two coordinates are needed for map matching
|
||||
if (static_cast<int>(input_coords.size()) < 2)
|
||||
{
|
||||
json_result.values["status_message"] = "At least two coordinates needed";
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
const auto candidates_lists = getCandidates(
|
||||
input_coords, input_bearings, route_parameters.gps_precision, sub_trace_lengths);
|
||||
if (candidates_lists.size() != input_coords.size())
|
||||
{
|
||||
BOOST_ASSERT(candidates_lists.size() < input_coords.size());
|
||||
json_result.values["status_message"] =
|
||||
std::string("Could not find a matching segment for coordinate ") +
|
||||
std::to_string(candidates_lists.size());
|
||||
return Status::NoSegment;
|
||||
}
|
||||
|
||||
// setup logging if enabled
|
||||
if (util::json::Logger::get())
|
||||
util::json::Logger::get()->initialize("matching");
|
||||
|
||||
// call the actual map matching
|
||||
SubMatchingList sub_matchings;
|
||||
search_engine_ptr->map_matching(candidates_lists, input_coords, input_timestamps,
|
||||
route_parameters.matching_beta,
|
||||
route_parameters.gps_precision, sub_matchings);
|
||||
|
||||
util::json::Array matchings;
|
||||
for (auto &sub : sub_matchings)
|
||||
{
|
||||
// classify result
|
||||
if (route_parameters.classify)
|
||||
{
|
||||
double trace_length =
|
||||
sub_trace_lengths[sub.indices.back()] - sub_trace_lengths[sub.indices.front()];
|
||||
TraceClassification classification =
|
||||
classify(trace_length, sub.length,
|
||||
(sub.indices.back() - sub.indices.front() + 1) - sub.nodes.size());
|
||||
if (classification.first == ClassifierT::ClassLabel::POSITIVE)
|
||||
{
|
||||
sub.confidence = classification.second;
|
||||
}
|
||||
else
|
||||
{
|
||||
sub.confidence = 1 - classification.second;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_ASSERT(sub.nodes.size() > 1);
|
||||
|
||||
// FIXME we only run this to obtain the geometry
|
||||
// The clean way would be to get this directly from the map matching plugin
|
||||
InternalRouteResult raw_route;
|
||||
PhantomNodes current_phantom_node_pair;
|
||||
for (unsigned i = 0; i < sub.nodes.size() - 1; ++i)
|
||||
{
|
||||
current_phantom_node_pair.source_phantom = sub.nodes[i];
|
||||
current_phantom_node_pair.target_phantom = sub.nodes[i + 1];
|
||||
BOOST_ASSERT(current_phantom_node_pair.source_phantom.IsValid());
|
||||
BOOST_ASSERT(current_phantom_node_pair.target_phantom.IsValid());
|
||||
raw_route.segment_end_coordinates.emplace_back(current_phantom_node_pair);
|
||||
}
|
||||
search_engine_ptr->shortest_path(
|
||||
raw_route.segment_end_coordinates,
|
||||
std::vector<bool>(raw_route.segment_end_coordinates.size() + 1, true), raw_route);
|
||||
|
||||
BOOST_ASSERT(raw_route.shortest_path_length != INVALID_EDGE_WEIGHT);
|
||||
|
||||
matchings.values.emplace_back(submatchingToJSON(sub, route_parameters, raw_route));
|
||||
}
|
||||
|
||||
if (util::json::Logger::get())
|
||||
util::json::Logger::get()->render("matching", json_result);
|
||||
json_result.values["matchings"] = matchings;
|
||||
|
||||
if (sub_matchings.empty())
|
||||
{
|
||||
json_result.values["status_message"] = "Cannot find matchings";
|
||||
return Status::EmptyResult;
|
||||
}
|
||||
|
||||
json_result.values["status_message"] = "Found matchings";
|
||||
return Status::Ok;
|
||||
}
|
||||
Status HandleRequest(const api::MatchParameters ¶meters, util::json::Object &json_result);
|
||||
|
||||
private:
|
||||
std::string descriptor_string;
|
||||
DataFacadeT *facade;
|
||||
SearchEngineData heaps;
|
||||
routing_algorithms::MapMatching<datafacade::BaseDataFacade> map_matching;
|
||||
routing_algorithms::ShortestPathRouting<datafacade::BaseDataFacade> shortest_path;
|
||||
int max_locations_map_matching;
|
||||
ClassifierT classifier;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,9 @@
|
||||
#define NEAREST_HPP
|
||||
|
||||
#include "engine/plugins/plugin_base.hpp"
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "engine/api/nearest_parameters.hpp"
|
||||
#include "osrm/json_container.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
@@ -15,90 +12,12 @@ namespace engine
|
||||
namespace plugins
|
||||
{
|
||||
|
||||
/*
|
||||
* This Plugin locates the nearest point on a street in the road network for a given coordinate.
|
||||
*/
|
||||
|
||||
template <class DataFacadeT> class NearestPlugin final : public BasePlugin
|
||||
class NearestPlugin final : public BasePlugin
|
||||
{
|
||||
public:
|
||||
explicit NearestPlugin(DataFacadeT *facade) : facade(facade), descriptor_string("nearest") {}
|
||||
explicit NearestPlugin(datafacade::BaseDataFacade &facade);
|
||||
|
||||
const std::string GetDescriptor() const override final { return descriptor_string; }
|
||||
|
||||
Status HandleRequest(const RouteParameters &route_parameters,
|
||||
util::json::Object &json_result) override final
|
||||
{
|
||||
// check number of parameters
|
||||
if (route_parameters.coordinates.empty() || !route_parameters.coordinates.front().IsValid())
|
||||
{
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
const auto &input_bearings = route_parameters.bearings;
|
||||
if (input_bearings.size() > 0 &&
|
||||
route_parameters.coordinates.size() != input_bearings.size())
|
||||
{
|
||||
json_result.values["status_message"] =
|
||||
"Number of bearings does not match number of coordinates";
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
auto number_of_results = static_cast<std::size_t>(route_parameters.num_results);
|
||||
const int bearing = input_bearings.size() > 0 ? input_bearings.front().first : 0;
|
||||
const int range =
|
||||
input_bearings.size() > 0
|
||||
? (input_bearings.front().second ? *input_bearings.front().second : 10)
|
||||
: 180;
|
||||
auto phantom_node_vector = facade->NearestPhantomNodes(route_parameters.coordinates.front(),
|
||||
number_of_results, bearing, range);
|
||||
|
||||
if (phantom_node_vector.empty())
|
||||
{
|
||||
json_result.values["status_message"] =
|
||||
std::string("Could not find a matching segments for coordinate");
|
||||
return Status::NoSegment;
|
||||
}
|
||||
else
|
||||
{
|
||||
json_result.values["status_message"] = "Found nearest edge";
|
||||
if (number_of_results > 1)
|
||||
{
|
||||
util::json::Array results;
|
||||
|
||||
auto vector_length = phantom_node_vector.size();
|
||||
for (const auto i :
|
||||
util::irange<std::size_t>(0, std::min(number_of_results, vector_length)))
|
||||
{
|
||||
const auto &node = phantom_node_vector[i].phantom_node;
|
||||
util::json::Array json_coordinate;
|
||||
util::json::Object result;
|
||||
json_coordinate.values.push_back(node.location.lat / COORDINATE_PRECISION);
|
||||
json_coordinate.values.push_back(node.location.lon / COORDINATE_PRECISION);
|
||||
result.values["mapped coordinate"] = json_coordinate;
|
||||
result.values["name"] = facade->get_name_for_id(node.name_id);
|
||||
results.values.push_back(result);
|
||||
}
|
||||
json_result.values["results"] = results;
|
||||
}
|
||||
else
|
||||
{
|
||||
util::json::Array json_coordinate;
|
||||
json_coordinate.values.push_back(
|
||||
phantom_node_vector.front().phantom_node.location.lat / COORDINATE_PRECISION);
|
||||
json_coordinate.values.push_back(
|
||||
phantom_node_vector.front().phantom_node.location.lon / COORDINATE_PRECISION);
|
||||
json_result.values["mapped_coordinate"] = json_coordinate;
|
||||
json_result.values["name"] =
|
||||
facade->get_name_for_id(phantom_node_vector.front().phantom_node.name_id);
|
||||
}
|
||||
}
|
||||
return Status::Ok;
|
||||
}
|
||||
|
||||
private:
|
||||
DataFacadeT *facade;
|
||||
std::string descriptor_string;
|
||||
Status HandleRequest(const api::NearestParameters ¶ms, util::json::Object &result);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
#ifndef BASE_PLUGIN_HPP
|
||||
#define BASE_PLUGIN_HPP
|
||||
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
#include "engine/api/base_parameters.hpp"
|
||||
#include "engine/phantom_node.hpp"
|
||||
#include "engine/status.hpp"
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
#include "osrm/json_container.hpp"
|
||||
#include "osrm/route_parameters.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/json_container.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
@@ -20,38 +24,32 @@ namespace plugins
|
||||
|
||||
class BasePlugin
|
||||
{
|
||||
public:
|
||||
enum class Status : int
|
||||
{
|
||||
Ok = 200,
|
||||
EmptyResult = 207,
|
||||
NoSegment = 208,
|
||||
Error = 400
|
||||
};
|
||||
protected:
|
||||
datafacade::BaseDataFacade &facade;
|
||||
BasePlugin(datafacade::BaseDataFacade &facade_) : facade(facade_) {}
|
||||
|
||||
BasePlugin() {}
|
||||
// Maybe someone can explain the pure virtual destructor thing to me (dennis)
|
||||
virtual ~BasePlugin() {}
|
||||
virtual const std::string GetDescriptor() const = 0;
|
||||
virtual Status HandleRequest(const RouteParameters &, util::json::Object &) = 0;
|
||||
virtual bool check_all_coordinates(const std::vector<util::FixedPointCoordinate> &coordinates,
|
||||
const unsigned min = 2) const final
|
||||
bool CheckAllCoordinates(const std::vector<util::Coordinate> &coordinates)
|
||||
{
|
||||
if (min > coordinates.size() || std::any_of(std::begin(coordinates), std::end(coordinates),
|
||||
[](const util::FixedPointCoordinate coordinate)
|
||||
{
|
||||
return !coordinate.IsValid();
|
||||
}))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return !std::any_of(std::begin(coordinates), std::end(coordinates),
|
||||
[](const util::Coordinate coordinate)
|
||||
{
|
||||
return !coordinate.IsValid();
|
||||
});
|
||||
}
|
||||
|
||||
Status Error(const std::string &code,
|
||||
const std::string &message,
|
||||
util::json::Object &json_result) const
|
||||
{
|
||||
json_result.values["code"] = code;
|
||||
json_result.values["message"] = message;
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
// Decides whether to use the phantom node from a big or small component if both are found.
|
||||
// Returns true if all phantom nodes are in the same component after snapping.
|
||||
std::vector<PhantomNode> snapPhantomNodes(
|
||||
const std::vector<std::pair<PhantomNode, PhantomNode>> &phantom_node_pair_list) const
|
||||
std::vector<PhantomNode>
|
||||
SnapPhantomNodes(const std::vector<PhantomNodePair> &phantom_node_pair_list) const
|
||||
{
|
||||
const auto check_component_id_is_tiny =
|
||||
[](const std::pair<PhantomNode, PhantomNode> &phantom_pair)
|
||||
@@ -111,6 +109,173 @@ class BasePlugin
|
||||
|
||||
return snapped_phantoms;
|
||||
}
|
||||
|
||||
// Falls back to default_radius for non-set radii
|
||||
std::vector<std::vector<PhantomNodeWithDistance>>
|
||||
GetPhantomNodesInRange(const api::BaseParameters ¶meters,
|
||||
const std::vector<double> radiuses) const
|
||||
{
|
||||
std::vector<std::vector<PhantomNodeWithDistance>> phantom_nodes(
|
||||
parameters.coordinates.size());
|
||||
BOOST_ASSERT(radiuses.size() == parameters.coordinates.size());
|
||||
|
||||
const bool use_hints = !parameters.hints.empty();
|
||||
const bool use_bearings = !parameters.bearings.empty();
|
||||
|
||||
for (const auto i : util::irange<std::size_t>(0, parameters.coordinates.size()))
|
||||
{
|
||||
if (use_hints && parameters.hints[i] &&
|
||||
parameters.hints[i]->IsValid(parameters.coordinates[i], facade))
|
||||
{
|
||||
phantom_nodes[i].push_back(PhantomNodeWithDistance{
|
||||
parameters.hints[i]->phantom,
|
||||
util::coordinate_calculation::haversineDistance(
|
||||
parameters.coordinates[i], parameters.hints[i]->phantom.location),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (use_bearings && parameters.bearings[i])
|
||||
{
|
||||
phantom_nodes[i] = facade.NearestPhantomNodesInRange(
|
||||
parameters.coordinates[i], radiuses[i], parameters.bearings[i]->bearing,
|
||||
parameters.bearings[i]->range);
|
||||
}
|
||||
else
|
||||
{
|
||||
phantom_nodes[i] =
|
||||
facade.NearestPhantomNodesInRange(parameters.coordinates[i], radiuses[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return phantom_nodes;
|
||||
}
|
||||
|
||||
std::vector<std::vector<PhantomNodeWithDistance>>
|
||||
GetPhantomNodes(const api::BaseParameters ¶meters, unsigned number_of_results)
|
||||
{
|
||||
std::vector<std::vector<PhantomNodeWithDistance>> phantom_nodes(
|
||||
parameters.coordinates.size());
|
||||
|
||||
const bool use_hints = !parameters.hints.empty();
|
||||
const bool use_bearings = !parameters.bearings.empty();
|
||||
const bool use_radiuses = !parameters.radiuses.empty();
|
||||
|
||||
BOOST_ASSERT(parameters.IsValid());
|
||||
for (const auto i : util::irange<std::size_t>(0, parameters.coordinates.size()))
|
||||
{
|
||||
if (use_hints && parameters.hints[i] &&
|
||||
parameters.hints[i]->IsValid(parameters.coordinates[i], facade))
|
||||
{
|
||||
phantom_nodes[i].push_back(PhantomNodeWithDistance{
|
||||
parameters.hints[i]->phantom,
|
||||
util::coordinate_calculation::haversineDistance(
|
||||
parameters.coordinates[i], parameters.hints[i]->phantom.location),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (use_bearings && parameters.bearings[i])
|
||||
{
|
||||
if (use_radiuses && parameters.radiuses[i])
|
||||
{
|
||||
phantom_nodes[i] = facade.NearestPhantomNodes(
|
||||
parameters.coordinates[i], number_of_results, *parameters.radiuses[i],
|
||||
parameters.bearings[i]->bearing, parameters.bearings[i]->range);
|
||||
}
|
||||
else
|
||||
{
|
||||
phantom_nodes[i] = facade.NearestPhantomNodes(
|
||||
parameters.coordinates[i], number_of_results,
|
||||
parameters.bearings[i]->bearing, parameters.bearings[i]->range);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (use_radiuses && parameters.radiuses[i])
|
||||
{
|
||||
phantom_nodes[i] = facade.NearestPhantomNodes(
|
||||
parameters.coordinates[i], number_of_results, *parameters.radiuses[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
phantom_nodes[i] =
|
||||
facade.NearestPhantomNodes(parameters.coordinates[i], number_of_results);
|
||||
}
|
||||
}
|
||||
|
||||
// we didn't find a fitting node, return error
|
||||
if (phantom_nodes[i].empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return phantom_nodes;
|
||||
}
|
||||
|
||||
std::vector<PhantomNodePair> GetPhantomNodes(const api::BaseParameters ¶meters)
|
||||
{
|
||||
std::vector<PhantomNodePair> phantom_node_pairs(parameters.coordinates.size());
|
||||
|
||||
const bool use_hints = !parameters.hints.empty();
|
||||
const bool use_bearings = !parameters.bearings.empty();
|
||||
const bool use_radiuses = !parameters.radiuses.empty();
|
||||
|
||||
BOOST_ASSERT(parameters.IsValid());
|
||||
for (const auto i : util::irange<std::size_t>(0, parameters.coordinates.size()))
|
||||
{
|
||||
if (use_hints && parameters.hints[i] &&
|
||||
parameters.hints[i]->IsValid(parameters.coordinates[i], facade))
|
||||
{
|
||||
phantom_node_pairs[i].first = parameters.hints[i]->phantom;
|
||||
// we don't set the second one - it will be marked as invalid
|
||||
continue;
|
||||
}
|
||||
|
||||
if (use_bearings && parameters.bearings[i])
|
||||
{
|
||||
if (use_radiuses && parameters.radiuses[i])
|
||||
{
|
||||
phantom_node_pairs[i] =
|
||||
facade.NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
parameters.coordinates[i], *parameters.radiuses[i],
|
||||
parameters.bearings[i]->bearing, parameters.bearings[i]->range);
|
||||
}
|
||||
else
|
||||
{
|
||||
phantom_node_pairs[i] =
|
||||
facade.NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
parameters.coordinates[i], parameters.bearings[i]->bearing,
|
||||
parameters.bearings[i]->range);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (use_radiuses && parameters.radiuses[i])
|
||||
{
|
||||
phantom_node_pairs[i] =
|
||||
facade.NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
parameters.coordinates[i], *parameters.radiuses[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
phantom_node_pairs[i] =
|
||||
facade.NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
parameters.coordinates[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// we didn't find a fitting node, return error
|
||||
if (!phantom_node_pairs[i].first.IsValid(facade.GetNumberOfNodes()))
|
||||
{
|
||||
// TODO document why?
|
||||
phantom_node_pairs.pop_back();
|
||||
break;
|
||||
}
|
||||
BOOST_ASSERT(phantom_node_pairs[i].first.IsValid(facade.GetNumberOfNodes()));
|
||||
BOOST_ASSERT(phantom_node_pairs[i].second.IsValid(facade.GetNumberOfNodes()));
|
||||
}
|
||||
return phantom_node_pairs;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef TABLE_HPP
|
||||
#define TABLE_HPP
|
||||
|
||||
#include "engine/plugins/plugin_base.hpp"
|
||||
|
||||
#include "engine/api/table_parameters.hpp"
|
||||
#include "engine/routing_algorithms/many_to_many.hpp"
|
||||
#include "engine/search_engine_data.hpp"
|
||||
#include "util/json_container.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace plugins
|
||||
{
|
||||
|
||||
class TablePlugin final : public BasePlugin
|
||||
{
|
||||
public:
|
||||
explicit TablePlugin(datafacade::BaseDataFacade &facade,
|
||||
const int max_locations_distance_table);
|
||||
|
||||
Status HandleRequest(const api::TableParameters ¶ms, util::json::Object &result);
|
||||
|
||||
private:
|
||||
SearchEngineData heaps;
|
||||
routing_algorithms::ManyToManyRouting<datafacade::BaseDataFacade> distance_table;
|
||||
int max_locations_distance_table;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // TABLE_HPP
|
||||
@@ -2,17 +2,9 @@
|
||||
#define TILEPLUGIN_HPP
|
||||
|
||||
#include "engine/plugins/plugin_base.hpp"
|
||||
#include "osrm/json_container.hpp"
|
||||
|
||||
#include <protozero/varint.hpp>
|
||||
#include <protozero/pbf_writer.hpp>
|
||||
#include "engine/api/tile_parameters.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
/*
|
||||
* This plugin generates Mapbox Vector tiles that show the internal
|
||||
@@ -29,405 +21,12 @@ namespace engine
|
||||
namespace plugins
|
||||
{
|
||||
|
||||
// from mapnik/well_known_srs.hpp
|
||||
const constexpr double EARTH_RADIUS = 6378137.0;
|
||||
const constexpr double EARTH_DIAMETER = EARTH_RADIUS * 2.0;
|
||||
const constexpr double EARTH_CIRCUMFERENCE = EARTH_DIAMETER * M_PI;
|
||||
const constexpr double MAXEXTENT = EARTH_CIRCUMFERENCE / 2.0;
|
||||
const constexpr double M_PI_by2 = M_PI / 2.0;
|
||||
const constexpr double D2R = M_PI / 180.0;
|
||||
const constexpr double R2D = 180.0 / M_PI;
|
||||
const constexpr double M_PIby360 = M_PI / 360.0;
|
||||
const constexpr double MAXEXTENTby180 = MAXEXTENT / 180.0;
|
||||
const double MAX_LATITUDE = R2D * (2.0 * std::atan(std::exp(180.0 * D2R)) - M_PI_by2);
|
||||
// ^ math functions are not constexpr since they have side-effects (setting errno) :(
|
||||
|
||||
// from mapnik-vector-tile
|
||||
namespace detail_pbf
|
||||
{
|
||||
|
||||
inline unsigned encode_length(const unsigned len) { return (len << 3u) | 2u; }
|
||||
}
|
||||
|
||||
// Converts a regular WSG84 lon/lat pair into
|
||||
// a mercator coordinate
|
||||
inline void lonlat2merc(double &x, double &y)
|
||||
{
|
||||
if (x > 180)
|
||||
x = 180;
|
||||
else if (x < -180)
|
||||
x = -180;
|
||||
if (y > MAX_LATITUDE)
|
||||
y = MAX_LATITUDE;
|
||||
else if (y < -MAX_LATITUDE)
|
||||
y = -MAX_LATITUDE;
|
||||
x = x * MAXEXTENTby180;
|
||||
y = std::log(std::tan((90 + y) * M_PIby360)) * R2D;
|
||||
y = y * MAXEXTENTby180;
|
||||
}
|
||||
|
||||
// This is the global default tile size for all Mapbox Vector Tiles
|
||||
const constexpr double tile_size_ = 256.0;
|
||||
|
||||
//
|
||||
inline void from_pixels(const double shift, double &x, double &y)
|
||||
{
|
||||
const double b = shift / 2.0;
|
||||
x = (x - b) / (shift / 360.0);
|
||||
const double g = (y - b) / -(shift / (2 * M_PI));
|
||||
y = R2D * (2.0 * std::atan(std::exp(g)) - M_PI_by2);
|
||||
}
|
||||
|
||||
// Converts a WMS tile coordinate (z,x,y) into a mercator bounding box
|
||||
inline void xyz2mercator(
|
||||
const int x, const int y, const int z, double &minx, double &miny, double &maxx, double &maxy)
|
||||
{
|
||||
minx = x * tile_size_;
|
||||
miny = (y + 1.0) * tile_size_;
|
||||
maxx = (x + 1.0) * tile_size_;
|
||||
maxy = y * tile_size_;
|
||||
const double shift = std::pow(2.0, z) * tile_size_;
|
||||
from_pixels(shift, minx, miny);
|
||||
from_pixels(shift, maxx, maxy);
|
||||
lonlat2merc(minx, miny);
|
||||
lonlat2merc(maxx, maxy);
|
||||
}
|
||||
|
||||
// Converts a WMS tile coordinate (z,x,y) into a wsg84 bounding box
|
||||
inline void xyz2wsg84(
|
||||
const int x, const int y, const int z, double &minx, double &miny, double &maxx, double &maxy)
|
||||
{
|
||||
minx = x * tile_size_;
|
||||
miny = (y + 1.0) * tile_size_;
|
||||
maxx = (x + 1.0) * tile_size_;
|
||||
maxy = y * tile_size_;
|
||||
const double shift = std::pow(2.0, z) * tile_size_;
|
||||
from_pixels(shift, minx, miny);
|
||||
from_pixels(shift, maxx, maxy);
|
||||
}
|
||||
|
||||
// emulates mapbox::box2d, just a simple container for
|
||||
// a box
|
||||
struct bbox final
|
||||
{
|
||||
bbox(const double _minx, const double _miny, const double _maxx, const double _maxy)
|
||||
: minx(_minx), miny(_miny), maxx(_maxx), maxy(_maxy)
|
||||
{
|
||||
}
|
||||
|
||||
double width() const { return maxx - minx; }
|
||||
double height() const { return maxy - miny; }
|
||||
|
||||
const double minx;
|
||||
const double miny;
|
||||
const double maxx;
|
||||
const double maxy;
|
||||
};
|
||||
|
||||
// Simple container class for WSG84 coordinates
|
||||
struct point_type_d final
|
||||
{
|
||||
point_type_d(double _x, double _y) : x(_x), y(_y) {}
|
||||
|
||||
const double x;
|
||||
const double y;
|
||||
};
|
||||
|
||||
// Simple container for integer coordinates (i.e. pixel coords)
|
||||
struct point_type_i final
|
||||
{
|
||||
point_type_i(std::int64_t _x, std::int64_t _y) : x(_x), y(_y) {}
|
||||
|
||||
const std::int64_t x;
|
||||
const std::int64_t y;
|
||||
};
|
||||
|
||||
using line_type = std::vector<point_type_i>;
|
||||
using line_typed = std::vector<point_type_d>;
|
||||
|
||||
// from mapnik-vector-tile
|
||||
// Encodes a linestring using protobuf zigzag encoding
|
||||
inline bool encode_linestring(line_type line,
|
||||
protozero::packed_field_uint32 &geometry,
|
||||
std::int32_t &start_x,
|
||||
std::int32_t &start_y)
|
||||
{
|
||||
const std::size_t line_size = line.size();
|
||||
// line_size -= detail_pbf::repeated_point_count(line);
|
||||
if (line_size < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const unsigned line_to_length = static_cast<const unsigned>(line_size) - 1;
|
||||
|
||||
auto pt = line.begin();
|
||||
geometry.add_element(9); // move_to | (1 << 3)
|
||||
geometry.add_element(protozero::encode_zigzag32(pt->x - start_x));
|
||||
geometry.add_element(protozero::encode_zigzag32(pt->y - start_y));
|
||||
start_x = pt->x;
|
||||
start_y = pt->y;
|
||||
geometry.add_element(detail_pbf::encode_length(line_to_length));
|
||||
for (++pt; pt != line.end(); ++pt)
|
||||
{
|
||||
const std::int32_t dx = pt->x - start_x;
|
||||
const std::int32_t dy = pt->y - start_y;
|
||||
/*if (dx == 0 && dy == 0)
|
||||
{
|
||||
continue;
|
||||
}*/
|
||||
geometry.add_element(protozero::encode_zigzag32(dx));
|
||||
geometry.add_element(protozero::encode_zigzag32(dy));
|
||||
start_x = pt->x;
|
||||
start_y = pt->y;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class DataFacadeT> class TilePlugin final : public BasePlugin
|
||||
class TilePlugin final : public BasePlugin
|
||||
{
|
||||
public:
|
||||
explicit TilePlugin(DataFacadeT *facade) : facade(facade), descriptor_string("tile") {}
|
||||
TilePlugin(datafacade::BaseDataFacade &facade) : BasePlugin(facade) {}
|
||||
|
||||
const std::string GetDescriptor() const override final { return descriptor_string; }
|
||||
|
||||
Status HandleRequest(const RouteParameters &route_parameters,
|
||||
util::json::Object &json_result) override final
|
||||
{
|
||||
|
||||
// Vector tiles are 4096 virtual pixels on each side
|
||||
const double tile_extent = 4096.0;
|
||||
double min_lon, min_lat, max_lon, max_lat;
|
||||
|
||||
// Convert the z,x,y mercator tile coordinates into WSG84 lon/lat values
|
||||
xyz2wsg84(route_parameters.x, route_parameters.y, route_parameters.z, min_lon, min_lat,
|
||||
max_lon, max_lat);
|
||||
|
||||
FixedPointCoordinate southwest{static_cast<std::int32_t>(min_lat * COORDINATE_PRECISION),
|
||||
static_cast<std::int32_t>(min_lon * COORDINATE_PRECISION)};
|
||||
FixedPointCoordinate northeast{static_cast<std::int32_t>(max_lat * COORDINATE_PRECISION),
|
||||
static_cast<std::int32_t>(max_lon * COORDINATE_PRECISION)};
|
||||
|
||||
// Fetch all the segments that are in our bounding box.
|
||||
// This hits the OSRM StaticRTree
|
||||
const auto edges = facade->GetEdgesInBox(southwest, northeast);
|
||||
|
||||
// TODO: extract speed values for compressed and uncompressed geometries
|
||||
|
||||
// Convert tile coordinates into mercator coordinates
|
||||
xyz2mercator(route_parameters.x, route_parameters.y, route_parameters.z, min_lon, min_lat,
|
||||
max_lon, max_lat);
|
||||
const bbox tile_bbox{min_lon, min_lat, max_lon, max_lat};
|
||||
|
||||
// Protobuf serialized blocks when objects go out of scope, hence
|
||||
// the extra scoping below.
|
||||
std::string buffer;
|
||||
protozero::pbf_writer tile_writer(buffer);
|
||||
{
|
||||
// Add a layer object to the PBF stream. 3=='layer' from the vector tile spec (2.1)
|
||||
protozero::pbf_writer layer_writer(tile_writer, 3);
|
||||
// TODO: don't write a layer if there are no features
|
||||
// Field 15 is the "version field, and it's a uint32
|
||||
layer_writer.add_uint32(15, 2); // version
|
||||
// Field 1 is the "layer name" field, it's a string
|
||||
layer_writer.add_string(1, "speeds"); // name
|
||||
// Field 5 is the tile extent. It's a uint32 and should be set to 4096
|
||||
// for normal vector tiles.
|
||||
layer_writer.add_uint32(5, 4096); // extent
|
||||
|
||||
// Begin the layer features block
|
||||
{
|
||||
// Each feature gets a unique id, starting at 1
|
||||
unsigned id = 1;
|
||||
for (const auto &edge : edges)
|
||||
{
|
||||
// Get coordinates for start/end nodes of segmet (NodeIDs u and v)
|
||||
const auto a = facade->GetCoordinateOfNode(edge.u);
|
||||
const auto b = facade->GetCoordinateOfNode(edge.v);
|
||||
// Calculate the length in meters, using the same calculation used to set the
|
||||
// weight, so we can back-calculate the speed value that was set.
|
||||
const double length = osrm::util::coordinate_calculation::greatCircleDistance(
|
||||
a.lat, a.lon, b.lat, b.lon);
|
||||
|
||||
int forward_weight = 0;
|
||||
int reverse_weight = 0;
|
||||
|
||||
if (edge.forward_packed_geometry_id != SPECIAL_EDGEID) {
|
||||
std::vector<EdgeWeight> forward_weight_vector;
|
||||
facade->GetUncompressedWeights(edge.forward_packed_geometry_id,
|
||||
forward_weight_vector);
|
||||
forward_weight = forward_weight_vector[edge.fwd_segment_position];
|
||||
}
|
||||
|
||||
if (edge.reverse_packed_geometry_id != SPECIAL_EDGEID) {
|
||||
std::vector<EdgeWeight> reverse_weight_vector;
|
||||
facade->GetUncompressedWeights(edge.reverse_packed_geometry_id,
|
||||
reverse_weight_vector);
|
||||
|
||||
BOOST_ASSERT(edge.fwd_segment_position < reverse_weight_vector.size());
|
||||
|
||||
reverse_weight = reverse_weight_vector[reverse_weight_vector.size() -
|
||||
edge.fwd_segment_position - 1];
|
||||
}
|
||||
|
||||
// If this is a valid forward edge, go ahead and add it to the tile
|
||||
if (forward_weight != 0 &&
|
||||
edge.forward_edge_based_node_id != SPECIAL_NODEID)
|
||||
{
|
||||
std::int32_t start_x = 0;
|
||||
std::int32_t start_y = 0;
|
||||
|
||||
line_typed geo_line;
|
||||
geo_line.emplace_back(a.lon / COORDINATE_PRECISION,
|
||||
a.lat / COORDINATE_PRECISION);
|
||||
geo_line.emplace_back(b.lon / COORDINATE_PRECISION,
|
||||
b.lat / COORDINATE_PRECISION);
|
||||
|
||||
// Calculate the speed for this line
|
||||
std::uint32_t speed = static_cast<std::uint32_t>(
|
||||
round(length / forward_weight * 10 * 3.6));
|
||||
|
||||
line_type tile_line;
|
||||
for (auto const &pt : geo_line)
|
||||
{
|
||||
double px_merc = pt.x;
|
||||
double py_merc = pt.y;
|
||||
lonlat2merc(px_merc, py_merc);
|
||||
// convert lon/lat to tile coordinates
|
||||
const auto px = std::round(((px_merc - tile_bbox.minx) * tile_extent /
|
||||
16.0 / tile_bbox.width()) *
|
||||
tile_extent / 256.0);
|
||||
const auto py = std::round(((tile_bbox.maxy - py_merc) * tile_extent /
|
||||
16.0 / tile_bbox.height()) *
|
||||
tile_extent / 256.0);
|
||||
tile_line.emplace_back(px, py);
|
||||
}
|
||||
|
||||
// Here, we save the two attributes for our feature: the speed and the
|
||||
// is_small
|
||||
// boolean. We onl serve up speeds from 0-139, so all we do is save the
|
||||
// first
|
||||
protozero::pbf_writer feature_writer(layer_writer, 2);
|
||||
// Field 3 is the "geometry type" field. Value 2 is "line"
|
||||
feature_writer.add_enum(3, 2); // geometry type
|
||||
// Field 1 for the feature is the "id" field.
|
||||
feature_writer.add_uint64(1, id++); // id
|
||||
{
|
||||
// When adding attributes to a feature, we have to write
|
||||
// pairs of numbers. The first value is the index in the
|
||||
// keys array (written later), and the second value is the
|
||||
// index into the "values" array (also written later). We're
|
||||
// not writing the actual speed or bool value here, we're saving
|
||||
// an index into the "values" array. This means many features
|
||||
// can share the same value data, leading to smaller tiles.
|
||||
protozero::packed_field_uint32 field(feature_writer, 2);
|
||||
|
||||
field.add_element(0); // "speed" tag key offset
|
||||
field.add_element(
|
||||
std::min(speed, 127u)); // save the speed value, capped at 127
|
||||
field.add_element(1); // "is_small" tag key offset
|
||||
field.add_element(edge.component.is_tiny ? 0 : 1); // is_small feature
|
||||
}
|
||||
{
|
||||
// Encode the geometry for the feature
|
||||
protozero::packed_field_uint32 geometry(feature_writer, 4);
|
||||
encode_linestring(tile_line, geometry, start_x, start_y);
|
||||
}
|
||||
}
|
||||
|
||||
// Repeat the above for the coordinates reversed and using the `reverse`
|
||||
// properties
|
||||
if (reverse_weight != 0 &&
|
||||
edge.reverse_edge_based_node_id != SPECIAL_NODEID)
|
||||
{
|
||||
std::int32_t start_x = 0;
|
||||
std::int32_t start_y = 0;
|
||||
|
||||
line_typed geo_line;
|
||||
geo_line.emplace_back(b.lon / COORDINATE_PRECISION,
|
||||
b.lat / COORDINATE_PRECISION);
|
||||
geo_line.emplace_back(a.lon / COORDINATE_PRECISION,
|
||||
a.lat / COORDINATE_PRECISION);
|
||||
|
||||
const auto speed = static_cast<const std::uint32_t>(
|
||||
round(length / reverse_weight * 10 * 3.6));
|
||||
|
||||
line_type tile_line;
|
||||
for (auto const &pt : geo_line)
|
||||
{
|
||||
double px_merc = pt.x;
|
||||
double py_merc = pt.y;
|
||||
lonlat2merc(px_merc, py_merc);
|
||||
// convert to integer tile coordinat
|
||||
const auto px = std::round(((px_merc - tile_bbox.minx) * tile_extent /
|
||||
16.0 / tile_bbox.width()) *
|
||||
tile_extent / 256.0);
|
||||
const auto py = std::round(((tile_bbox.maxy - py_merc) * tile_extent /
|
||||
16.0 / tile_bbox.height()) *
|
||||
tile_extent / 256.0);
|
||||
tile_line.emplace_back(px, py);
|
||||
}
|
||||
|
||||
protozero::pbf_writer feature_writer(layer_writer, 2);
|
||||
feature_writer.add_enum(3, 2); // geometry type
|
||||
feature_writer.add_uint64(1, id++); // id
|
||||
{
|
||||
protozero::packed_field_uint32 field(feature_writer, 2);
|
||||
field.add_element(0); // "speed" tag key offset
|
||||
field.add_element(
|
||||
std::min(speed, 127u)); // save the speed value, capped at 127
|
||||
field.add_element(1); // "is_small" tag key offset
|
||||
field.add_element(edge.component.is_tiny ? 0 : 1); // is_small feature
|
||||
}
|
||||
{
|
||||
protozero::packed_field_uint32 geometry(feature_writer, 4);
|
||||
encode_linestring(tile_line, geometry, start_x, start_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Field id 3 is the "keys" attribute
|
||||
// We need two "key" fields, these are referred to with 0 and 1 (their array indexes)
|
||||
// earlier
|
||||
layer_writer.add_string(3, "speed");
|
||||
layer_writer.add_string(3, "is_small");
|
||||
|
||||
// Now, we write out the possible speed value arrays and possible is_tiny
|
||||
// values. Field type 4 is the "values" field. It's a variable type field,
|
||||
// so requires a two-step write (create the field, then write its value)
|
||||
for (std::size_t i = 0; i < 128; i++)
|
||||
{
|
||||
{
|
||||
// Writing field type 4 == variant type
|
||||
protozero::pbf_writer values_writer(layer_writer, 4);
|
||||
// Attribute value 5 == uin64 type
|
||||
values_writer.add_uint64(5, i);
|
||||
}
|
||||
}
|
||||
{
|
||||
protozero::pbf_writer values_writer(layer_writer, 4);
|
||||
// Attribute value 7 == bool type
|
||||
values_writer.add_bool(7, true);
|
||||
}
|
||||
{
|
||||
protozero::pbf_writer values_writer(layer_writer, 4);
|
||||
// Attribute value 7 == bool type
|
||||
values_writer.add_bool(7, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the PBF result as a special Buffer object on the response.
|
||||
// This will allow downstream consumers to handle this type differently
|
||||
// to the String type.
|
||||
json_result.values["pbf"] = osrm::util::json::Buffer(buffer);
|
||||
|
||||
return Status::Ok;
|
||||
}
|
||||
|
||||
private:
|
||||
DataFacadeT *const facade;
|
||||
const std::string descriptor_string;
|
||||
Status HandleRequest(const api::TileParameters ¶meters, std::string &pbf_buffer);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+16
-330
@@ -3,16 +3,10 @@
|
||||
|
||||
#include "engine/plugins/plugin_base.hpp"
|
||||
|
||||
#include "engine/object_encoder.hpp"
|
||||
#include "extractor/tarjan_scc.hpp"
|
||||
#include "engine/trip/trip_nearest_neighbour.hpp"
|
||||
#include "engine/trip/trip_farthest_insertion.hpp"
|
||||
#include "engine/trip/trip_brute_force.hpp"
|
||||
#include "engine/search_engine.hpp"
|
||||
#include "util/matrix_graph_wrapper.hpp" // wrapper to use tarjan scc on dist table
|
||||
#include "engine/api_response_generator.hpp"
|
||||
#include "util/make_unique.hpp"
|
||||
#include "util/dist_table_wrapper.hpp" // to access the dist table more easily
|
||||
#include "engine/api/trip_parameters.hpp"
|
||||
#include "engine/routing_algorithms/shortest_path.hpp"
|
||||
#include "engine/routing_algorithms/many_to_many.hpp"
|
||||
|
||||
#include "osrm/json_container.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
@@ -32,334 +26,26 @@ namespace engine
|
||||
namespace plugins
|
||||
{
|
||||
|
||||
template <class DataFacadeT> class RoundTripPlugin final : public BasePlugin
|
||||
class TripPlugin final : public BasePlugin
|
||||
{
|
||||
private:
|
||||
std::string descriptor_string;
|
||||
DataFacadeT *facade;
|
||||
std::unique_ptr<SearchEngine<DataFacadeT>> search_engine_ptr;
|
||||
SearchEngineData heaps;
|
||||
routing_algorithms::ShortestPathRouting<datafacade::BaseDataFacade> shortest_path;
|
||||
routing_algorithms::ManyToManyRouting<datafacade::BaseDataFacade> duration_table;
|
||||
int max_locations_trip;
|
||||
|
||||
public:
|
||||
explicit RoundTripPlugin(DataFacadeT *facade, int max_locations_trip)
|
||||
: descriptor_string("trip"), facade(facade), max_locations_trip(max_locations_trip)
|
||||
{
|
||||
search_engine_ptr = util::make_unique<SearchEngine<DataFacadeT>>(facade);
|
||||
}
|
||||
|
||||
const std::string GetDescriptor() const override final { return descriptor_string; }
|
||||
|
||||
std::vector<PhantomNode> GetPhantomNodes(const RouteParameters &route_parameters)
|
||||
{
|
||||
const bool checksum_OK = (route_parameters.check_sum == facade->GetCheckSum());
|
||||
const auto &input_bearings = route_parameters.bearings;
|
||||
|
||||
std::vector<PhantomNode> phantom_node_list;
|
||||
phantom_node_list.reserve(route_parameters.coordinates.size());
|
||||
|
||||
// find phantom nodes for all input coords
|
||||
for (const auto i : util::irange<std::size_t>(0, route_parameters.coordinates.size()))
|
||||
{
|
||||
// if client hints are helpful, encode hints
|
||||
if (checksum_OK && i < route_parameters.hints.size() &&
|
||||
!route_parameters.hints[i].empty())
|
||||
{
|
||||
auto current_phantom_node = decodeBase64<PhantomNode>(route_parameters.hints[i]);
|
||||
if (current_phantom_node.IsValid(facade->GetNumberOfNodes()))
|
||||
{
|
||||
phantom_node_list.push_back(std::move(current_phantom_node));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const int bearing = input_bearings.size() > 0 ? input_bearings[i].first : 0;
|
||||
const int range = input_bearings.size() > 0
|
||||
? (input_bearings[i].second ? *input_bearings[i].second : 10)
|
||||
: 180;
|
||||
auto results =
|
||||
facade->NearestPhantomNodes(route_parameters.coordinates[i], 1, bearing, range);
|
||||
if (results.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
phantom_node_list.push_back(std::move(results.front().phantom_node));
|
||||
BOOST_ASSERT(phantom_node_list.back().IsValid(facade->GetNumberOfNodes()));
|
||||
}
|
||||
|
||||
return phantom_node_list;
|
||||
}
|
||||
|
||||
// Object to hold all strongly connected components (scc) of a graph
|
||||
// to access all graphs with component ID i, get the iterators by:
|
||||
// auto start = std::begin(scc_component.component) + scc_component.range[i];
|
||||
// auto end = std::begin(scc_component.component) + scc_component.range[i+1];
|
||||
struct SCC_Component
|
||||
{
|
||||
// in_component: all NodeIDs sorted by component ID
|
||||
// in_range: index where a new component starts
|
||||
//
|
||||
// example: NodeID 0, 1, 2, 4, 5 are in component 0
|
||||
// NodeID 3, 6, 7, 8 are in component 1
|
||||
// => in_component = [0, 1, 2, 4, 5, 3, 6, 7, 8]
|
||||
// => in_range = [0, 5]
|
||||
SCC_Component(std::vector<NodeID> in_component_nodes, std::vector<size_t> in_range)
|
||||
: component(std::move(in_component_nodes)), range(std::move(in_range))
|
||||
{
|
||||
BOOST_ASSERT_MSG(component.size() > 0, "there's no scc component");
|
||||
BOOST_ASSERT_MSG(*std::max_element(range.begin(), range.end()) == component.size(),
|
||||
"scc component ranges are out of bound");
|
||||
BOOST_ASSERT_MSG(*std::min_element(range.begin(), range.end()) == 0,
|
||||
"invalid scc component range");
|
||||
BOOST_ASSERT_MSG(std::is_sorted(std::begin(range), std::end(range)),
|
||||
"invalid component ranges");
|
||||
}
|
||||
|
||||
std::size_t GetNumberOfComponents() const
|
||||
{
|
||||
BOOST_ASSERT_MSG(range.size() > 0, "there's no range");
|
||||
return range.size() - 1;
|
||||
}
|
||||
|
||||
const std::vector<NodeID> component;
|
||||
std::vector<std::size_t> range;
|
||||
};
|
||||
|
||||
// takes the number of locations and its distance matrix,
|
||||
// identifies and splits the graph in its strongly connected components (scc)
|
||||
// and returns an SCC_Component
|
||||
SCC_Component SplitUnaccessibleLocations(const std::size_t number_of_locations,
|
||||
const util::DistTableWrapper<EdgeWeight> &result_table)
|
||||
{
|
||||
|
||||
if (std::find(std::begin(result_table), std::end(result_table), INVALID_EDGE_WEIGHT) ==
|
||||
std::end(result_table))
|
||||
{
|
||||
// whole graph is one scc
|
||||
std::vector<NodeID> location_ids(number_of_locations);
|
||||
std::iota(std::begin(location_ids), std::end(location_ids), 0);
|
||||
std::vector<size_t> range = {0, location_ids.size()};
|
||||
return SCC_Component(std::move(location_ids), std::move(range));
|
||||
}
|
||||
|
||||
// Run TarjanSCC
|
||||
auto wrapper = std::make_shared<util::MatrixGraphWrapper<EdgeWeight>>(
|
||||
result_table.GetTable(), number_of_locations);
|
||||
auto scc = extractor::TarjanSCC<util::MatrixGraphWrapper<EdgeWeight>>(wrapper);
|
||||
scc.run();
|
||||
|
||||
const auto number_of_components = scc.get_number_of_components();
|
||||
|
||||
std::vector<std::size_t> range_insertion;
|
||||
std::vector<std::size_t> range;
|
||||
range_insertion.reserve(number_of_components);
|
||||
range.reserve(number_of_components);
|
||||
|
||||
std::vector<NodeID> components(number_of_locations, 0);
|
||||
|
||||
std::size_t prefix = 0;
|
||||
for (std::size_t j = 0; j < number_of_components; ++j)
|
||||
{
|
||||
range_insertion.push_back(prefix);
|
||||
range.push_back(prefix);
|
||||
prefix += scc.get_component_size(j);
|
||||
}
|
||||
// senitel
|
||||
range.push_back(components.size());
|
||||
|
||||
for (std::size_t i = 0; i < number_of_locations; ++i)
|
||||
{
|
||||
components[range_insertion[scc.get_component_id(i)]] = i;
|
||||
++range_insertion[scc.get_component_id(i)];
|
||||
}
|
||||
|
||||
return SCC_Component(std::move(components), std::move(range));
|
||||
}
|
||||
|
||||
void SetLocPermutationOutput(const std::vector<NodeID> &permutation,
|
||||
util::json::Object &json_result)
|
||||
{
|
||||
util::json::Array json_permutation;
|
||||
json_permutation.values.insert(std::end(json_permutation.values), std::begin(permutation),
|
||||
std::end(permutation));
|
||||
json_result.values["permutation"] = json_permutation;
|
||||
}
|
||||
|
||||
InternalRouteResult ComputeRoute(const std::vector<PhantomNode> &phantom_node_list,
|
||||
const RouteParameters &route_parameters,
|
||||
const std::vector<NodeID> &trip)
|
||||
const api::TripParameters ¶meters,
|
||||
const std::vector<NodeID> &trip);
|
||||
|
||||
public:
|
||||
explicit TripPlugin(datafacade::BaseDataFacade &facade_, const int max_locations_trip_)
|
||||
: BasePlugin(facade_), shortest_path(&facade_, heaps), duration_table(&facade_, heaps),
|
||||
max_locations_trip(max_locations_trip_)
|
||||
{
|
||||
InternalRouteResult min_route;
|
||||
// given he final trip, compute total distance and return the route and location permutation
|
||||
PhantomNodes viapoint;
|
||||
const auto start = std::begin(trip);
|
||||
const auto end = std::end(trip);
|
||||
// computes a roundtrip from the nodes in trip
|
||||
for (auto it = start; it != end; ++it)
|
||||
{
|
||||
const auto from_node = *it;
|
||||
// if from_node is the last node, compute the route from the last to the first location
|
||||
const auto to_node = std::next(it) != end ? *std::next(it) : *start;
|
||||
|
||||
viapoint = PhantomNodes{phantom_node_list[from_node], phantom_node_list[to_node]};
|
||||
min_route.segment_end_coordinates.emplace_back(viapoint);
|
||||
}
|
||||
BOOST_ASSERT(min_route.segment_end_coordinates.size() == trip.size());
|
||||
|
||||
std::vector<bool> uturns(trip.size() + 1);
|
||||
BOOST_ASSERT(route_parameters.uturns.size() > 0);
|
||||
std::transform(trip.begin(), trip.end(), uturns.begin(),
|
||||
[&route_parameters](const NodeID idx)
|
||||
{
|
||||
return route_parameters.uturns[idx];
|
||||
});
|
||||
BOOST_ASSERT(uturns.size() > 0);
|
||||
uturns.back() = route_parameters.uturns[trip.front()];
|
||||
|
||||
search_engine_ptr->shortest_path(min_route.segment_end_coordinates, uturns, min_route);
|
||||
|
||||
BOOST_ASSERT_MSG(min_route.shortest_path_length < INVALID_EDGE_WEIGHT, "unroutable route");
|
||||
return min_route;
|
||||
}
|
||||
|
||||
Status HandleRequest(const RouteParameters &route_parameters,
|
||||
util::json::Object &json_result) override final
|
||||
{
|
||||
if (max_locations_trip > 0 &&
|
||||
(static_cast<int>(route_parameters.coordinates.size()) > max_locations_trip))
|
||||
{
|
||||
json_result.values["status_message"] =
|
||||
"Number of entries " + std::to_string(route_parameters.coordinates.size()) +
|
||||
" is higher than current maximum (" + std::to_string(max_locations_trip) + ")";
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
// check if all inputs are coordinates
|
||||
if (!check_all_coordinates(route_parameters.coordinates))
|
||||
{
|
||||
json_result.values["status_message"] = "Invalid coordinates";
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
const auto &input_bearings = route_parameters.bearings;
|
||||
if (input_bearings.size() > 0 &&
|
||||
route_parameters.coordinates.size() != input_bearings.size())
|
||||
{
|
||||
json_result.values["status_message"] =
|
||||
"Number of bearings does not match number of coordinates";
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
// get phantom nodes
|
||||
auto phantom_node_list = GetPhantomNodes(route_parameters);
|
||||
if (phantom_node_list.size() != route_parameters.coordinates.size())
|
||||
{
|
||||
BOOST_ASSERT(phantom_node_list.size() < route_parameters.coordinates.size());
|
||||
json_result.values["status_message"] =
|
||||
std::string("Could not find a matching segment for coordinate ") +
|
||||
std::to_string(phantom_node_list.size());
|
||||
return Status::NoSegment;
|
||||
}
|
||||
|
||||
const auto number_of_locations = phantom_node_list.size();
|
||||
|
||||
// compute the distance table of all phantom nodes
|
||||
const auto result_table = util::DistTableWrapper<EdgeWeight>(
|
||||
*search_engine_ptr->distance_table(phantom_node_list, phantom_node_list),
|
||||
number_of_locations);
|
||||
|
||||
if (result_table.size() == 0)
|
||||
{
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
const constexpr std::size_t BF_MAX_FEASABLE = 10;
|
||||
BOOST_ASSERT_MSG(result_table.size() == number_of_locations * number_of_locations,
|
||||
"Distance Table has wrong size");
|
||||
|
||||
// get scc components
|
||||
SCC_Component scc = SplitUnaccessibleLocations(number_of_locations, result_table);
|
||||
|
||||
using NodeIDIterator = typename std::vector<NodeID>::const_iterator;
|
||||
|
||||
std::vector<std::vector<NodeID>> route_result;
|
||||
route_result.reserve(scc.GetNumberOfComponents());
|
||||
// run Trip computation for every SCC
|
||||
for (std::size_t k = 0; k < scc.GetNumberOfComponents(); ++k)
|
||||
{
|
||||
const auto component_size = scc.range[k + 1] - scc.range[k];
|
||||
|
||||
BOOST_ASSERT_MSG(component_size > 0, "invalid component size");
|
||||
|
||||
std::vector<NodeID> scc_route;
|
||||
NodeIDIterator start = std::begin(scc.component) + scc.range[k];
|
||||
NodeIDIterator end = std::begin(scc.component) + scc.range[k + 1];
|
||||
|
||||
if (component_size > 1)
|
||||
{
|
||||
|
||||
if (component_size < BF_MAX_FEASABLE)
|
||||
{
|
||||
scc_route = trip::BruteForceTrip(start, end, number_of_locations, result_table);
|
||||
}
|
||||
else
|
||||
{
|
||||
scc_route =
|
||||
trip::FarthestInsertionTrip(start, end, number_of_locations, result_table);
|
||||
}
|
||||
|
||||
// use this output if debugging of route is needed:
|
||||
// util::SimpleLogger().Write() << "Route #" << k << ": " << [&scc_route]()
|
||||
// {
|
||||
// std::string s = "";
|
||||
// for (auto x : scc_route)
|
||||
// {
|
||||
// s += std::to_string(x) + " ";
|
||||
// }
|
||||
// return s;
|
||||
// }();
|
||||
}
|
||||
else
|
||||
{
|
||||
scc_route = std::vector<NodeID>(start, end);
|
||||
}
|
||||
|
||||
route_result.push_back(std::move(scc_route));
|
||||
}
|
||||
|
||||
// compute all round trip routes
|
||||
std::vector<InternalRouteResult> comp_route;
|
||||
comp_route.reserve(route_result.size());
|
||||
for (auto &elem : route_result)
|
||||
{
|
||||
comp_route.push_back(ComputeRoute(phantom_node_list, route_parameters, elem));
|
||||
}
|
||||
|
||||
// prepare JSON output
|
||||
// create a json object for every trip
|
||||
util::json::Array trip;
|
||||
for (std::size_t i = 0; i < route_result.size(); ++i)
|
||||
{
|
||||
util::json::Object scc_trip;
|
||||
|
||||
// annotate comp_route[i] as a json trip
|
||||
auto generator = MakeApiResponseGenerator(facade);
|
||||
generator.DescribeRoute(route_parameters, comp_route[i], scc_trip);
|
||||
|
||||
// set permutation output
|
||||
SetLocPermutationOutput(route_result[i], scc_trip);
|
||||
// set viaroute output
|
||||
trip.values.push_back(std::move(scc_trip));
|
||||
}
|
||||
|
||||
if (trip.values.empty())
|
||||
{
|
||||
json_result.values["status_message"] = "Cannot find trips";
|
||||
return Status::EmptyResult;
|
||||
}
|
||||
|
||||
json_result.values["trips"] = std::move(trip);
|
||||
json_result.values["status_message"] = "Found trips";
|
||||
return Status::Ok;
|
||||
}
|
||||
Status HandleRequest(const api::TripParameters ¶meters, util::json::Object &json_result);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
#ifndef VIA_ROUTE_HPP
|
||||
#define VIA_ROUTE_HPP
|
||||
|
||||
#include "engine/datafacade/datafacade_base.hpp"
|
||||
#include "engine/plugins/plugin_base.hpp"
|
||||
#include "engine/api/route_api.hpp"
|
||||
|
||||
#include "engine/api_response_generator.hpp"
|
||||
#include "engine/object_encoder.hpp"
|
||||
#include "engine/search_engine.hpp"
|
||||
#include "util/for_each_pair.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/make_unique.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/timing_util.hpp"
|
||||
#include "osrm/json_container.hpp"
|
||||
#include "engine/search_engine_data.hpp"
|
||||
#include "engine/routing_algorithms/shortest_path.hpp"
|
||||
#include "engine/routing_algorithms/alternative_path.hpp"
|
||||
#include "engine/routing_algorithms/direct_shortest_path.hpp"
|
||||
#include "util/json_container.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
@@ -27,146 +25,20 @@ namespace engine
|
||||
namespace plugins
|
||||
{
|
||||
|
||||
template <class DataFacadeT> class ViaRoutePlugin final : public BasePlugin
|
||||
class ViaRoutePlugin final : public BasePlugin
|
||||
{
|
||||
private:
|
||||
std::string descriptor_string;
|
||||
std::unique_ptr<SearchEngine<DataFacadeT>> search_engine_ptr;
|
||||
DataFacadeT *facade;
|
||||
SearchEngineData heaps;
|
||||
routing_algorithms::ShortestPathRouting<datafacade::BaseDataFacade> shortest_path;
|
||||
routing_algorithms::AlternativeRouting<datafacade::BaseDataFacade> alternative_path;
|
||||
routing_algorithms::DirectShortestPathRouting<datafacade::BaseDataFacade> direct_shortest_path;
|
||||
int max_locations_viaroute;
|
||||
|
||||
public:
|
||||
explicit ViaRoutePlugin(DataFacadeT *facade, int max_locations_viaroute)
|
||||
: descriptor_string("viaroute"), facade(facade),
|
||||
max_locations_viaroute(max_locations_viaroute)
|
||||
{
|
||||
search_engine_ptr = util::make_unique<SearchEngine<DataFacadeT>>(facade);
|
||||
}
|
||||
explicit ViaRoutePlugin(datafacade::BaseDataFacade &facade, int max_locations_viaroute);
|
||||
|
||||
virtual ~ViaRoutePlugin() {}
|
||||
|
||||
const std::string GetDescriptor() const override final { return descriptor_string; }
|
||||
|
||||
Status HandleRequest(const RouteParameters &route_parameters,
|
||||
util::json::Object &json_result) override final
|
||||
{
|
||||
if (max_locations_viaroute > 0 &&
|
||||
(static_cast<int>(route_parameters.coordinates.size()) > max_locations_viaroute))
|
||||
{
|
||||
json_result.values["status_message"] =
|
||||
"Number of entries " + std::to_string(route_parameters.coordinates.size()) +
|
||||
" is higher than current maximum (" + std::to_string(max_locations_viaroute) + ")";
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
if (!check_all_coordinates(route_parameters.coordinates))
|
||||
{
|
||||
json_result.values["status_message"] = "Invalid coordinates";
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
const auto &input_bearings = route_parameters.bearings;
|
||||
if (input_bearings.size() > 0 &&
|
||||
route_parameters.coordinates.size() != input_bearings.size())
|
||||
{
|
||||
json_result.values["status_message"] =
|
||||
"Number of bearings does not match number of coordinate";
|
||||
return Status::Error;
|
||||
}
|
||||
|
||||
std::vector<PhantomNodePair> phantom_node_pair_list(route_parameters.coordinates.size());
|
||||
const bool checksum_OK = (route_parameters.check_sum == facade->GetCheckSum());
|
||||
|
||||
for (const auto i : util::irange<std::size_t>(0, route_parameters.coordinates.size()))
|
||||
{
|
||||
if (checksum_OK && i < route_parameters.hints.size() &&
|
||||
!route_parameters.hints[i].empty())
|
||||
{
|
||||
phantom_node_pair_list[i].first =
|
||||
decodeBase64<PhantomNode>(route_parameters.hints[i]);
|
||||
if (phantom_node_pair_list[i].first.IsValid(facade->GetNumberOfNodes()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const int bearing = input_bearings.size() > 0 ? input_bearings[i].first : 0;
|
||||
const int range = input_bearings.size() > 0
|
||||
? (input_bearings[i].second ? *input_bearings[i].second : 10)
|
||||
: 180;
|
||||
phantom_node_pair_list[i] = facade->NearestPhantomNodeWithAlternativeFromBigComponent(
|
||||
route_parameters.coordinates[i], bearing, range);
|
||||
// we didn't found a fitting node, return error
|
||||
if (!phantom_node_pair_list[i].first.IsValid(facade->GetNumberOfNodes()))
|
||||
{
|
||||
json_result.values["status_message"] =
|
||||
std::string("Could not find a matching segment for coordinate ") +
|
||||
std::to_string(i);
|
||||
return Status::NoSegment;
|
||||
}
|
||||
BOOST_ASSERT(phantom_node_pair_list[i].first.IsValid(facade->GetNumberOfNodes()));
|
||||
BOOST_ASSERT(phantom_node_pair_list[i].second.IsValid(facade->GetNumberOfNodes()));
|
||||
}
|
||||
|
||||
auto snapped_phantoms = snapPhantomNodes(phantom_node_pair_list);
|
||||
|
||||
InternalRouteResult raw_route;
|
||||
auto build_phantom_pairs =
|
||||
[&raw_route](const PhantomNode &first_node, const PhantomNode &second_node)
|
||||
{
|
||||
raw_route.segment_end_coordinates.push_back(PhantomNodes{first_node, second_node});
|
||||
};
|
||||
util::for_each_pair(snapped_phantoms, build_phantom_pairs);
|
||||
|
||||
if (1 == raw_route.segment_end_coordinates.size())
|
||||
{
|
||||
if (route_parameters.alternate_route && facade->GetCoreSize() == 0)
|
||||
{
|
||||
search_engine_ptr->alternative_path(raw_route.segment_end_coordinates.front(),
|
||||
raw_route);
|
||||
}
|
||||
else
|
||||
{
|
||||
search_engine_ptr->direct_shortest_path(raw_route.segment_end_coordinates,
|
||||
route_parameters.uturns, raw_route);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
search_engine_ptr->shortest_path(raw_route.segment_end_coordinates,
|
||||
route_parameters.uturns, raw_route);
|
||||
}
|
||||
|
||||
// we can only know this after the fact, different SCC ids still
|
||||
// allow for connection in one direction.
|
||||
if (raw_route.is_valid())
|
||||
{
|
||||
auto generator = MakeApiResponseGenerator(facade);
|
||||
generator.DescribeRoute(route_parameters, raw_route, json_result);
|
||||
json_result.values["status_message"] = "Found route between points";
|
||||
}
|
||||
else
|
||||
{
|
||||
auto first_component_id = snapped_phantoms.front().component.id;
|
||||
auto not_in_same_component =
|
||||
std::any_of(snapped_phantoms.begin(), snapped_phantoms.end(),
|
||||
[first_component_id](const PhantomNode &node)
|
||||
{
|
||||
return node.component.id != first_component_id;
|
||||
});
|
||||
|
||||
if (not_in_same_component)
|
||||
{
|
||||
json_result.values["status_message"] = "Impossible route between points";
|
||||
return Status::EmptyResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
json_result.values["status_message"] = "No route found between points";
|
||||
return Status::Error;
|
||||
}
|
||||
}
|
||||
return Status::Ok;
|
||||
}
|
||||
Status HandleRequest(const api::RouteParameters &route_parameters,
|
||||
util::json::Object &json_result);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#ifndef POLYLINECOMPRESSOR_H_
|
||||
#define POLYLINECOMPRESSOR_H_
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
#include "engine/segment_information.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -11,13 +10,21 @@ namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
constexpr double POLYLINE_PRECISION = 1e5;
|
||||
constexpr double COORDINATE_TO_POLYLINE = POLYLINE_PRECISION / COORDINATE_PRECISION;
|
||||
constexpr double POLYLINE_TO_COORDINATE = COORDINATE_PRECISION / POLYLINE_PRECISION;
|
||||
}
|
||||
|
||||
using CoordVectorForwardIter = std::vector<util::Coordinate>::const_iterator;
|
||||
// Encodes geometry into polyline format.
|
||||
// See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm
|
||||
std::string polylineEncode(const std::vector<SegmentInformation> &geometry);
|
||||
std::string encodePolyline(CoordVectorForwardIter begin, CoordVectorForwardIter end);
|
||||
|
||||
// Decodes geometry from polyline format
|
||||
// See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm
|
||||
std::vector<util::FixedPointCoordinate> polylineDecode(const std::string &polyline);
|
||||
std::vector<util::Coordinate> decodePolyline(const std::string &polyline);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
#ifndef POLYLINE_FORMATTER_HPP
|
||||
#define POLYLINE_FORMATTER_HPP
|
||||
|
||||
#include "engine/segment_information.hpp"
|
||||
#include "osrm/json_container.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
|
||||
// Encodes geometry into polyline format, returning an encoded JSON object
|
||||
// See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm
|
||||
util::json::String polylineEncodeAsJSON(const std::vector<SegmentInformation> &geometry);
|
||||
|
||||
// Does not encode the geometry in polyline format, instead returning an unencoded JSON object
|
||||
util::json::Array polylineUnencodedAsJSON(const std::vector<SegmentInformation> &geometry);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* POLYLINE_FORMATTER_HPP */
|
||||
@@ -1,149 +0,0 @@
|
||||
#ifndef EXTRACT_ROUTE_NAMES_H
|
||||
#define EXTRACT_ROUTE_NAMES_H
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
|
||||
struct RouteNames
|
||||
{
|
||||
std::string shortest_path_name_1;
|
||||
std::string shortest_path_name_2;
|
||||
std::string alternative_path_name_1;
|
||||
std::string alternative_path_name_2;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <class SegmentT>
|
||||
SegmentT pickNextLongestSegment(const std::vector<SegmentT> &segment_list,
|
||||
const unsigned blocked_name_id)
|
||||
{
|
||||
SegmentT result_segment;
|
||||
result_segment.name_id = blocked_name_id; // make sure we get a valid name
|
||||
result_segment.length = 0;
|
||||
|
||||
for (const SegmentT &segment : segment_list)
|
||||
{
|
||||
if (segment.name_id != blocked_name_id && segment.length > result_segment.length &&
|
||||
segment.name_id != 0)
|
||||
{
|
||||
result_segment = segment;
|
||||
}
|
||||
}
|
||||
return result_segment;
|
||||
}
|
||||
|
||||
} // ns detail
|
||||
|
||||
template <class DataFacadeT, class SegmentT>
|
||||
RouteNames extractRouteNames(std::vector<SegmentT> &shortest_path_segments,
|
||||
std::vector<SegmentT> &alternative_path_segments,
|
||||
const DataFacadeT *facade)
|
||||
{
|
||||
RouteNames route_names;
|
||||
|
||||
if (shortest_path_segments.empty())
|
||||
{
|
||||
return route_names;
|
||||
}
|
||||
|
||||
SegmentT shortest_segment_1, shortest_segment_2;
|
||||
SegmentT alternative_segment_1, alternative_segment_2;
|
||||
|
||||
const auto length_comperator = [](const SegmentT &a, const SegmentT &b)
|
||||
{
|
||||
return a.length > b.length;
|
||||
};
|
||||
const auto name_id_comperator = [](const SegmentT &a, const SegmentT &b)
|
||||
{
|
||||
return a.name_id < b.name_id;
|
||||
};
|
||||
|
||||
// pick the longest segment for the shortest path.
|
||||
std::sort(shortest_path_segments.begin(), shortest_path_segments.end(), length_comperator);
|
||||
shortest_segment_1 = shortest_path_segments[0];
|
||||
|
||||
if (!alternative_path_segments.empty())
|
||||
{
|
||||
std::sort(alternative_path_segments.begin(), alternative_path_segments.end(),
|
||||
length_comperator);
|
||||
|
||||
// also pick the longest segment for the alternative path
|
||||
alternative_segment_1 = alternative_path_segments[0];
|
||||
}
|
||||
|
||||
// compute the set difference (for shortest path) depending on names between shortest and
|
||||
// alternative
|
||||
std::vector<SegmentT> shortest_path_set_difference(shortest_path_segments.size());
|
||||
std::sort(shortest_path_segments.begin(), shortest_path_segments.end(), name_id_comperator);
|
||||
std::sort(alternative_path_segments.begin(), alternative_path_segments.end(),
|
||||
name_id_comperator);
|
||||
std::set_difference(shortest_path_segments.begin(), shortest_path_segments.end(),
|
||||
alternative_path_segments.begin(), alternative_path_segments.end(),
|
||||
shortest_path_set_difference.begin(), name_id_comperator);
|
||||
|
||||
std::sort(shortest_path_set_difference.begin(), shortest_path_set_difference.end(),
|
||||
length_comperator);
|
||||
shortest_segment_2 =
|
||||
pickNextLongestSegment(shortest_path_set_difference, shortest_segment_1.name_id);
|
||||
|
||||
// compute the set difference (for alternative path) depending on names between shortest and
|
||||
// alternative
|
||||
// vectors are still sorted, no need to do again
|
||||
BOOST_ASSERT(std::is_sorted(shortest_path_segments.begin(), shortest_path_segments.end(),
|
||||
name_id_comperator));
|
||||
BOOST_ASSERT(std::is_sorted(alternative_path_segments.begin(), alternative_path_segments.end(),
|
||||
name_id_comperator));
|
||||
|
||||
std::vector<SegmentT> alternative_path_set_difference(alternative_path_segments.size());
|
||||
std::set_difference(alternative_path_segments.begin(), alternative_path_segments.end(),
|
||||
shortest_path_segments.begin(), shortest_path_segments.end(),
|
||||
alternative_path_set_difference.begin(), name_id_comperator);
|
||||
|
||||
std::sort(alternative_path_set_difference.begin(), alternative_path_set_difference.end(),
|
||||
length_comperator);
|
||||
|
||||
if (!alternative_path_segments.empty())
|
||||
{
|
||||
alternative_segment_2 =
|
||||
pickNextLongestSegment(alternative_path_set_difference, alternative_segment_1.name_id);
|
||||
}
|
||||
|
||||
// move the segments into the order in which they occur.
|
||||
if (shortest_segment_1.position > shortest_segment_2.position)
|
||||
{
|
||||
std::swap(shortest_segment_1, shortest_segment_2);
|
||||
}
|
||||
if (alternative_segment_1.position > alternative_segment_2.position)
|
||||
{
|
||||
std::swap(alternative_segment_1, alternative_segment_2);
|
||||
}
|
||||
|
||||
// fetching names for the selected segments
|
||||
route_names.shortest_path_name_1 = facade->get_name_for_id(shortest_segment_1.name_id);
|
||||
route_names.shortest_path_name_2 = facade->get_name_for_id(shortest_segment_2.name_id);
|
||||
|
||||
if (!alternative_path_segments.empty())
|
||||
{
|
||||
route_names.alternative_path_name_1 =
|
||||
facade->get_name_for_id(alternative_segment_1.name_id);
|
||||
route_names.alternative_path_name_2 =
|
||||
facade->get_name_for_id(alternative_segment_2.name_id);
|
||||
}
|
||||
|
||||
return route_names;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // EXTRACT_ROUTE_NAMES_H
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ROUTE_PARAMETERS_HPP
|
||||
#define ROUTE_PARAMETERS_HPP
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
|
||||
#include <boost/optional/optional.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
|
||||
struct RouteParameters
|
||||
{
|
||||
RouteParameters();
|
||||
|
||||
void SetZoomLevel(const short level);
|
||||
|
||||
void SetNumberOfResults(const short number);
|
||||
|
||||
void SetAlternateRouteFlag(const bool flag);
|
||||
|
||||
void SetUTurn(const bool flag);
|
||||
|
||||
void SetAllUTurns(const bool flag);
|
||||
|
||||
void SetClassify(const bool classify);
|
||||
|
||||
void SetMatchingBeta(const double beta);
|
||||
|
||||
void SetGPSPrecision(const double precision);
|
||||
|
||||
void SetDeprecatedAPIFlag(const std::string &);
|
||||
|
||||
void SetChecksum(const unsigned check_sum);
|
||||
|
||||
void SetInstructionFlag(const bool flag);
|
||||
|
||||
void SetService(const std::string &service);
|
||||
|
||||
void SetOutputFormat(const std::string &format);
|
||||
|
||||
void SetJSONpParameter(const std::string ¶meter);
|
||||
|
||||
void AddHint(const std::string &hint);
|
||||
|
||||
void AddTimestamp(const unsigned timestamp);
|
||||
|
||||
bool AddBearing(int bearing, boost::optional<int> range);
|
||||
|
||||
void SetLanguage(const std::string &language);
|
||||
|
||||
void SetGeometryFlag(const bool flag);
|
||||
|
||||
void SetCompressionFlag(const bool flag);
|
||||
|
||||
void AddCoordinate(const double latitude, const double longitude);
|
||||
|
||||
void AddDestination(const double latitude, const double longitude);
|
||||
|
||||
void AddSource(const double latitude, const double longitude);
|
||||
|
||||
void SetCoordinatesFromGeometry(const std::string &geometry_string);
|
||||
|
||||
bool SetX(const int x);
|
||||
bool SetZ(const int z);
|
||||
bool SetY(const int y);
|
||||
|
||||
short zoom_level;
|
||||
bool print_instructions;
|
||||
bool alternate_route;
|
||||
bool geometry;
|
||||
bool compression;
|
||||
bool deprecatedAPI;
|
||||
bool uturn_default;
|
||||
bool classify;
|
||||
double matching_beta;
|
||||
double gps_precision;
|
||||
unsigned check_sum;
|
||||
short num_results;
|
||||
std::string service;
|
||||
std::string output_format;
|
||||
std::string jsonp_parameter;
|
||||
std::string language;
|
||||
std::vector<std::string> hints;
|
||||
std::vector<unsigned> timestamps;
|
||||
std::vector<std::pair<const int, const boost::optional<int>>> bearings;
|
||||
std::vector<bool> uturns;
|
||||
std::vector<FixedPointCoordinate> coordinates;
|
||||
std::vector<bool> is_destination;
|
||||
std::vector<bool> is_source;
|
||||
int z;
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ROUTE_PARAMETERS_HPP
|
||||
@@ -40,11 +40,8 @@ class DirectShortestPathRouting final
|
||||
~DirectShortestPathRouting() {}
|
||||
|
||||
void operator()(const std::vector<PhantomNodes> &phantom_nodes_vector,
|
||||
const std::vector<bool> &uturn_indicators,
|
||||
InternalRouteResult &raw_route_data) const
|
||||
{
|
||||
(void)uturn_indicators; // unused
|
||||
|
||||
// Get distance to next pair of target nodes.
|
||||
BOOST_ASSERT_MSG(1 == phantom_nodes_vector.size(),
|
||||
"Direct Shortest Path Query only accepts a single source and target pair. "
|
||||
|
||||
@@ -36,6 +36,8 @@ class ManyToManyRouting final
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME This should be replaced by an std::unordered_multimap, though this needs benchmarking
|
||||
using SearchSpaceWithBuckets = std::unordered_map<NodeID, std::vector<NodeBucket>>;
|
||||
|
||||
public:
|
||||
@@ -44,17 +46,17 @@ class ManyToManyRouting final
|
||||
{
|
||||
}
|
||||
|
||||
~ManyToManyRouting() {}
|
||||
|
||||
std::shared_ptr<std::vector<EdgeWeight>>
|
||||
operator()(const std::vector<PhantomNode> &phantom_sources_array,
|
||||
const std::vector<PhantomNode> &phantom_targets_array) const
|
||||
std::vector<EdgeWeight> operator()(const std::vector<PhantomNode> &phantom_nodes,
|
||||
const std::vector<std::size_t> &source_indices,
|
||||
const std::vector<std::size_t> &target_indices) const
|
||||
{
|
||||
const auto number_of_sources = phantom_sources_array.size();
|
||||
const auto number_of_targets = phantom_targets_array.size();
|
||||
std::shared_ptr<std::vector<EdgeWeight>> result_table =
|
||||
std::make_shared<std::vector<EdgeWeight>>(number_of_targets * number_of_sources,
|
||||
std::numeric_limits<EdgeWeight>::max());
|
||||
const auto number_of_sources =
|
||||
source_indices.empty() ? phantom_nodes.size() : source_indices.size();
|
||||
const auto number_of_targets =
|
||||
target_indices.empty() ? phantom_nodes.size() : target_indices.size();
|
||||
const auto number_of_entries = number_of_sources * number_of_targets;
|
||||
std::vector<EdgeWeight> result_table(number_of_entries,
|
||||
std::numeric_limits<EdgeWeight>::max());
|
||||
|
||||
engine_working_data.InitializeOrClearFirstThreadLocalStorage(
|
||||
super::facade->GetNumberOfNodes());
|
||||
@@ -63,8 +65,8 @@ class ManyToManyRouting final
|
||||
|
||||
SearchSpaceWithBuckets search_space_with_buckets;
|
||||
|
||||
unsigned target_id = 0;
|
||||
for (const auto &phantom : phantom_targets_array)
|
||||
unsigned column_idx = 0;
|
||||
const auto search_target_phantom = [&](const PhantomNode &phantom)
|
||||
{
|
||||
query_heap.Clear();
|
||||
// insert target(s) at distance 0
|
||||
@@ -83,14 +85,14 @@ class ManyToManyRouting final
|
||||
// explore search space
|
||||
while (!query_heap.Empty())
|
||||
{
|
||||
BackwardRoutingStep(target_id, query_heap, search_space_with_buckets);
|
||||
BackwardRoutingStep(column_idx, query_heap, search_space_with_buckets);
|
||||
}
|
||||
++target_id;
|
||||
}
|
||||
++column_idx;
|
||||
};
|
||||
|
||||
// for each source do forward search
|
||||
unsigned source_id = 0;
|
||||
for (const auto &phantom : phantom_sources_array)
|
||||
unsigned row_idx = 0;
|
||||
const auto search_source_phantom = [&](const PhantomNode &phantom)
|
||||
{
|
||||
query_heap.Clear();
|
||||
// insert target(s) at distance 0
|
||||
@@ -109,21 +111,52 @@ class ManyToManyRouting final
|
||||
// explore search space
|
||||
while (!query_heap.Empty())
|
||||
{
|
||||
ForwardRoutingStep(source_id, number_of_targets, query_heap,
|
||||
ForwardRoutingStep(row_idx, number_of_targets, query_heap,
|
||||
search_space_with_buckets, result_table);
|
||||
}
|
||||
++row_idx;
|
||||
};
|
||||
|
||||
++source_id;
|
||||
if (target_indices.empty())
|
||||
{
|
||||
for (const auto &phantom : phantom_nodes)
|
||||
{
|
||||
search_target_phantom(phantom);
|
||||
}
|
||||
}
|
||||
// BOOST_ASSERT(source_id == target_id);
|
||||
else
|
||||
{
|
||||
for (const auto index : target_indices)
|
||||
{
|
||||
const auto &phantom = phantom_nodes[index];
|
||||
search_target_phantom(phantom);
|
||||
}
|
||||
}
|
||||
|
||||
if (source_indices.empty())
|
||||
{
|
||||
for (const auto &phantom : phantom_nodes)
|
||||
{
|
||||
search_source_phantom(phantom);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto index : source_indices)
|
||||
{
|
||||
const auto &phantom = phantom_nodes[index];
|
||||
search_source_phantom(phantom);
|
||||
}
|
||||
}
|
||||
|
||||
return result_table;
|
||||
}
|
||||
|
||||
void ForwardRoutingStep(const unsigned source_id,
|
||||
void ForwardRoutingStep(const unsigned row_idx,
|
||||
const unsigned number_of_targets,
|
||||
QueryHeap &query_heap,
|
||||
const SearchSpaceWithBuckets &search_space_with_buckets,
|
||||
std::shared_ptr<std::vector<EdgeWeight>> result_table) const
|
||||
std::vector<EdgeWeight> &result_table) const
|
||||
{
|
||||
const NodeID node = query_heap.DeleteMin();
|
||||
const int source_distance = query_heap.GetKey(node);
|
||||
@@ -137,9 +170,9 @@ class ManyToManyRouting final
|
||||
for (const NodeBucket ¤t_bucket : bucket_list)
|
||||
{
|
||||
// get target id from bucket entry
|
||||
const unsigned target_id = current_bucket.target_id;
|
||||
const unsigned column_idx = current_bucket.target_id;
|
||||
const int target_distance = current_bucket.distance;
|
||||
auto ¤t_distance = (*result_table)[source_id * number_of_targets + target_id];
|
||||
auto ¤t_distance = result_table[row_idx * number_of_targets + column_idx];
|
||||
// check if new distance is better
|
||||
const EdgeWeight new_distance = source_distance + target_distance;
|
||||
if (new_distance < 0)
|
||||
@@ -153,7 +186,7 @@ class ManyToManyRouting final
|
||||
}
|
||||
else if (new_distance < current_distance)
|
||||
{
|
||||
(*result_table)[source_id * number_of_targets + target_id] = new_distance;
|
||||
result_table[row_idx * number_of_targets + column_idx] = new_distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,7 +197,7 @@ class ManyToManyRouting final
|
||||
RelaxOutgoingEdges<true>(node, source_distance, query_heap);
|
||||
}
|
||||
|
||||
void BackwardRoutingStep(const unsigned target_id,
|
||||
void BackwardRoutingStep(const unsigned column_idx,
|
||||
QueryHeap &query_heap,
|
||||
SearchSpaceWithBuckets &search_space_with_buckets) const
|
||||
{
|
||||
@@ -172,7 +205,7 @@ class ManyToManyRouting final
|
||||
const int target_distance = query_heap.GetKey(node);
|
||||
|
||||
// store settled nodes in search space bucket
|
||||
search_space_with_buckets[node].emplace_back(target_id, target_distance);
|
||||
search_space_with_buckets[node].emplace_back(column_idx, target_distance);
|
||||
|
||||
if (StallAtNode<false>(node, target_distance, query_heap))
|
||||
{
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
|
||||
#include "engine/routing_algorithms/routing_base.hpp"
|
||||
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "engine/map_matching/hidden_markov_model.hpp"
|
||||
#include "engine/map_matching/sub_matching.hpp"
|
||||
#include "engine/map_matching/matching_confidence.hpp"
|
||||
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/json_logger.hpp"
|
||||
#include "util/matching_debug_info.hpp"
|
||||
#include "util/for_each_pair.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
@@ -24,22 +27,14 @@ namespace engine
|
||||
namespace routing_algorithms
|
||||
{
|
||||
|
||||
struct SubMatching
|
||||
{
|
||||
std::vector<PhantomNode> nodes;
|
||||
std::vector<unsigned> indices;
|
||||
double length;
|
||||
double confidence;
|
||||
};
|
||||
|
||||
using CandidateList = std::vector<PhantomNodeWithDistance>;
|
||||
using CandidateLists = std::vector<CandidateList>;
|
||||
using HMM = map_matching::HiddenMarkovModel<CandidateLists>;
|
||||
using SubMatchingList = std::vector<SubMatching>;
|
||||
using SubMatchingList = std::vector<map_matching::SubMatching>;
|
||||
|
||||
constexpr static const unsigned MAX_BROKEN_STATES = 10;
|
||||
constexpr static const double MAX_SPEED = 180 / 3.6; // 180km -> m/s
|
||||
constexpr static const unsigned SUSPICIOUS_DISTANCE_DELTA = 100;
|
||||
static const constexpr double MATCHING_BETA = 10;
|
||||
constexpr static const double MAX_DISTANCE_DELTA = 2000.;
|
||||
|
||||
// implements a hidden markov model map matching algorithm
|
||||
@@ -49,6 +44,9 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
|
||||
using super = BasicRoutingInterface<DataFacadeT, MapMatching<DataFacadeT>>;
|
||||
using QueryHeap = SearchEngineData::QueryHeap;
|
||||
SearchEngineData &engine_working_data;
|
||||
map_matching::EmissionLogProbability default_emission_log_probability;
|
||||
map_matching::TransitionLogProbability transition_log_probability;
|
||||
map_matching::MatchingConfidence confidence;
|
||||
|
||||
unsigned GetMedianSampleTime(const std::vector<unsigned> ×tamps) const
|
||||
{
|
||||
@@ -66,24 +64,29 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
|
||||
}
|
||||
|
||||
public:
|
||||
MapMatching(DataFacadeT *facade, SearchEngineData &engine_working_data)
|
||||
: super(facade), engine_working_data(engine_working_data)
|
||||
MapMatching(DataFacadeT *facade,
|
||||
SearchEngineData &engine_working_data,
|
||||
const double default_gps_precision)
|
||||
: super(facade), engine_working_data(engine_working_data),
|
||||
default_emission_log_probability(default_gps_precision),
|
||||
transition_log_probability(MATCHING_BETA)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(const CandidateLists &candidates_list,
|
||||
const std::vector<util::FixedPointCoordinate> &trace_coordinates,
|
||||
const std::vector<unsigned> &trace_timestamps,
|
||||
const double matching_beta,
|
||||
const double gps_precision,
|
||||
SubMatchingList &sub_matchings) const
|
||||
SubMatchingList
|
||||
operator()(const CandidateLists &candidates_list,
|
||||
const std::vector<util::Coordinate> &trace_coordinates,
|
||||
const std::vector<unsigned> &trace_timestamps,
|
||||
const std::vector<boost::optional<double>> &trace_gps_precision) const
|
||||
{
|
||||
SubMatchingList sub_matchings;
|
||||
|
||||
BOOST_ASSERT(candidates_list.size() == trace_coordinates.size());
|
||||
BOOST_ASSERT(candidates_list.size() > 1);
|
||||
|
||||
const bool use_timestamps = trace_timestamps.size() > 1;
|
||||
|
||||
const auto median_sample_time = [&]()
|
||||
const auto median_sample_time = [&]
|
||||
{
|
||||
if (use_timestamps)
|
||||
{
|
||||
@@ -95,7 +98,7 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
|
||||
}
|
||||
}();
|
||||
const auto max_broken_time = median_sample_time * MAX_BROKEN_STATES;
|
||||
const auto max_distance_delta = [&]()
|
||||
const auto max_distance_delta = [&]
|
||||
{
|
||||
if (use_timestamps)
|
||||
{
|
||||
@@ -107,21 +110,57 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
|
||||
}
|
||||
}();
|
||||
|
||||
// TODO replace default values with table lookup based on sampling frequency
|
||||
map_matching::EmissionLogProbability emission_log_probability(gps_precision);
|
||||
map_matching::TransitionLogProbability transition_log_probability(matching_beta);
|
||||
std::vector<std::vector<double>> emission_log_probabilities(trace_coordinates.size());
|
||||
if (trace_gps_precision.empty())
|
||||
{
|
||||
for (auto t = 0UL; t < candidates_list.size(); ++t)
|
||||
{
|
||||
emission_log_probabilities[t].resize(candidates_list[t].size());
|
||||
std::transform(candidates_list[t].begin(), candidates_list[t].end(),
|
||||
emission_log_probabilities[t].begin(),
|
||||
[this](const PhantomNodeWithDistance &candidate)
|
||||
{
|
||||
return default_emission_log_probability(candidate.distance);
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto t = 0UL; t < candidates_list.size(); ++t)
|
||||
{
|
||||
emission_log_probabilities[t].resize(candidates_list[t].size());
|
||||
if (trace_gps_precision[t])
|
||||
{
|
||||
map_matching::EmissionLogProbability emission_log_probability(
|
||||
*trace_gps_precision[t]);
|
||||
std::transform(
|
||||
candidates_list[t].begin(), candidates_list[t].end(),
|
||||
emission_log_probabilities[t].begin(),
|
||||
[&emission_log_probability](const PhantomNodeWithDistance &candidate)
|
||||
{
|
||||
return emission_log_probability(candidate.distance);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
std::transform(candidates_list[t].begin(), candidates_list[t].end(),
|
||||
emission_log_probabilities[t].begin(),
|
||||
[this](const PhantomNodeWithDistance &candidate)
|
||||
{
|
||||
return default_emission_log_probability(candidate.distance);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HMM model(candidates_list, emission_log_probability);
|
||||
HMM model(candidates_list, emission_log_probabilities);
|
||||
|
||||
std::size_t initial_timestamp = model.initialize(0);
|
||||
if (initial_timestamp == map_matching::INVALID_STATE)
|
||||
{
|
||||
return;
|
||||
return sub_matchings;
|
||||
}
|
||||
|
||||
util::MatchingDebugInfo matching_debug(util::json::Logger::get());
|
||||
matching_debug.initialize(candidates_list);
|
||||
|
||||
engine_working_data.InitializeOrClearFirstThreadLocalStorage(
|
||||
super::facade->GetNumberOfNodes());
|
||||
engine_working_data.InitializeOrClearSecondThreadLocalStorage(
|
||||
@@ -193,16 +232,16 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
|
||||
|
||||
auto ¤t_viterbi = model.viterbi[t];
|
||||
auto ¤t_pruned = model.pruned[t];
|
||||
auto ¤t_suspicious = model.suspicious[t];
|
||||
auto ¤t_parents = model.parents[t];
|
||||
auto ¤t_lengths = model.path_lengths[t];
|
||||
auto ¤t_lengths = model.path_distances[t];
|
||||
const auto ¤t_timestamps_list = candidates_list[t];
|
||||
const auto ¤t_coordinate = trace_coordinates[t];
|
||||
|
||||
const auto haversine_distance = util::coordinate_calculation::haversineDistance(
|
||||
prev_coordinate, current_coordinate);
|
||||
// assumes minumum of 0.1 m/s
|
||||
const int duration_uppder_bound = ((haversine_distance + max_distance_delta) * 0.25) * 10;
|
||||
const int duration_uppder_bound =
|
||||
((haversine_distance + max_distance_delta) * 0.25) * 10;
|
||||
|
||||
// compute d_t for this timestamp and the next one
|
||||
for (const auto s : util::irange<std::size_t>(0u, prev_viterbi.size()))
|
||||
@@ -214,10 +253,7 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
|
||||
|
||||
for (const auto s_prime : util::irange<std::size_t>(0u, current_viterbi.size()))
|
||||
{
|
||||
// how likely is candidate s_prime at time t to be emitted?
|
||||
// FIXME this can be pre-computed
|
||||
const double emission_pr =
|
||||
emission_log_probability(candidates_list[t][s_prime].distance);
|
||||
const double emission_pr = emission_log_probabilities[t][s_prime];
|
||||
double new_value = prev_viterbi[s] + emission_pr;
|
||||
if (current_viterbi[s_prime] > new_value)
|
||||
{
|
||||
@@ -235,8 +271,7 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
|
||||
network_distance = super::GetNetworkDistanceWithCore(
|
||||
forward_heap, reverse_heap, forward_core_heap, reverse_core_heap,
|
||||
prev_unbroken_timestamps_list[s].phantom_node,
|
||||
current_timestamps_list[s_prime].phantom_node,
|
||||
duration_uppder_bound);
|
||||
current_timestamps_list[s_prime].phantom_node, duration_uppder_bound);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -258,17 +293,12 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
|
||||
const double transition_pr = transition_log_probability(d_t);
|
||||
new_value += transition_pr;
|
||||
|
||||
matching_debug.add_transition_info(prev_unbroken_timestamp, t, s, s_prime,
|
||||
prev_viterbi[s], emission_pr, transition_pr,
|
||||
network_distance, haversine_distance);
|
||||
|
||||
if (new_value > current_viterbi[s_prime])
|
||||
{
|
||||
current_viterbi[s_prime] = new_value;
|
||||
current_parents[s_prime] = std::make_pair(prev_unbroken_timestamp, s);
|
||||
current_lengths[s_prime] = network_distance;
|
||||
current_pruned[s_prime] = false;
|
||||
current_suspicious[s_prime] = d_t > SUSPICIOUS_DISTANCE_DELTA;
|
||||
model.breakage[t] = false;
|
||||
}
|
||||
}
|
||||
@@ -292,8 +322,6 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
|
||||
}
|
||||
}
|
||||
|
||||
matching_debug.set_viterbi(model.viterbi, model.pruned, model.suspicious);
|
||||
|
||||
if (!prev_unbroken_timestamps.empty())
|
||||
{
|
||||
split_points.push_back(prev_unbroken_timestamps.back() + 1);
|
||||
@@ -302,7 +330,7 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
|
||||
std::size_t sub_matching_begin = initial_timestamp;
|
||||
for (const auto sub_matching_end : split_points)
|
||||
{
|
||||
SubMatching matching;
|
||||
map_matching::SubMatching matching;
|
||||
|
||||
std::size_t parent_timestamp_index = sub_matching_end - 1;
|
||||
while (parent_timestamp_index >= sub_matching_begin &&
|
||||
@@ -355,25 +383,36 @@ class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<
|
||||
continue;
|
||||
}
|
||||
|
||||
matching.length = 0.0;
|
||||
matching.nodes.resize(reconstructed_indices.size());
|
||||
matching.indices.resize(reconstructed_indices.size());
|
||||
for (const auto i : util::irange<std::size_t>(0u, reconstructed_indices.size()))
|
||||
auto matching_distance = 0.0;
|
||||
auto trace_distance = 0.0;
|
||||
matching.nodes.reserve(reconstructed_indices.size());
|
||||
matching.indices.reserve(reconstructed_indices.size());
|
||||
for (const auto idx : reconstructed_indices)
|
||||
{
|
||||
const auto timestamp_index = reconstructed_indices[i].first;
|
||||
const auto location_index = reconstructed_indices[i].second;
|
||||
const auto timestamp_index = idx.first;
|
||||
const auto location_index = idx.second;
|
||||
|
||||
matching.indices[i] = timestamp_index;
|
||||
matching.nodes[i] = candidates_list[timestamp_index][location_index].phantom_node;
|
||||
matching.length += model.path_lengths[timestamp_index][location_index];
|
||||
|
||||
matching_debug.add_chosen(timestamp_index, location_index);
|
||||
matching.indices.push_back(timestamp_index);
|
||||
matching.nodes.push_back(
|
||||
candidates_list[timestamp_index][location_index].phantom_node);
|
||||
matching_distance += model.path_distances[timestamp_index][location_index];
|
||||
}
|
||||
util::for_each_pair(
|
||||
reconstructed_indices, [&trace_distance, &trace_coordinates](
|
||||
const std::pair<std::size_t, std::size_t> &prev,
|
||||
const std::pair<std::size_t, std::size_t> &curr)
|
||||
{
|
||||
trace_distance += util::coordinate_calculation::haversineDistance(
|
||||
trace_coordinates[prev.first], trace_coordinates[curr.first]);
|
||||
});
|
||||
|
||||
matching.confidence = confidence(trace_distance, matching_distance);
|
||||
|
||||
sub_matchings.push_back(matching);
|
||||
sub_matching_begin = sub_matching_end;
|
||||
}
|
||||
matching_debug.add_breakage(model.breakage);
|
||||
|
||||
return sub_matchings;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "engine/internal_route_result.hpp"
|
||||
#include "engine/search_engine_data.hpp"
|
||||
#include "extractor/turn_instructions.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
@@ -24,13 +24,6 @@ namespace osrm
|
||||
namespace engine
|
||||
{
|
||||
|
||||
SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_1;
|
||||
SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_1;
|
||||
SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_2;
|
||||
SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_2;
|
||||
SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_3;
|
||||
SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_3;
|
||||
|
||||
namespace routing_algorithms
|
||||
{
|
||||
|
||||
@@ -243,8 +236,8 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
|
||||
edge = recursion_stack.top();
|
||||
recursion_stack.pop();
|
||||
|
||||
// facade->FindEdge does not suffice here in case of shortcuts.
|
||||
// The above explanation unclear? Think!
|
||||
// Contraction might introduce double edges by inserting shortcuts
|
||||
// this searching for the smallest upwards edge found by the forward search
|
||||
EdgeID smaller_edge_id = SPECIAL_EDGEID;
|
||||
EdgeWeight edge_weight = std::numeric_limits<EdgeWeight>::max();
|
||||
for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.first))
|
||||
@@ -261,6 +254,8 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
|
||||
// edge.first edge.second
|
||||
// *<------------------*
|
||||
// edge_id
|
||||
// if we don't find a forward edge, this edge must have been an downwards edge
|
||||
// found by the reverse search.
|
||||
if (SPECIAL_EDGEID == smaller_edge_id)
|
||||
{
|
||||
for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.second))
|
||||
@@ -288,81 +283,96 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
|
||||
{
|
||||
BOOST_ASSERT_MSG(!ed.shortcut, "original edge flagged as shortcut");
|
||||
unsigned name_index = facade->GetNameIndexFromEdgeID(ed.id);
|
||||
const extractor::TurnInstruction turn_instruction =
|
||||
facade->GetTurnInstructionForEdgeID(ed.id);
|
||||
const auto turn_instruction = facade->GetTurnInstructionForEdgeID(ed.id);
|
||||
const extractor::TravelMode travel_mode =
|
||||
(unpacked_path.empty() && start_traversed_in_reverse)
|
||||
? phantom_node_pair.source_phantom.backward_travel_mode
|
||||
: facade->GetTravelModeForEdgeID(ed.id);
|
||||
|
||||
if (!facade->EdgeIsCompressed(ed.id))
|
||||
std::vector<NodeID> id_vector;
|
||||
facade->GetUncompressedGeometry(facade->GetGeometryIndexForEdgeID(ed.id),
|
||||
id_vector);
|
||||
BOOST_ASSERT(id_vector.size() > 0);
|
||||
|
||||
std::vector<EdgeWeight> weight_vector;
|
||||
facade->GetUncompressedWeights(facade->GetGeometryIndexForEdgeID(ed.id),
|
||||
weight_vector);
|
||||
BOOST_ASSERT(weight_vector.size() > 0);
|
||||
|
||||
auto total_weight = std::accumulate(weight_vector.begin(), weight_vector.end(), 0);
|
||||
|
||||
BOOST_ASSERT(weight_vector.size() == id_vector.size());
|
||||
// ed.distance should be total_weight + penalties (turn, stop, etc)
|
||||
BOOST_ASSERT(ed.distance >= total_weight);
|
||||
const bool is_first_segment = unpacked_path.empty();
|
||||
|
||||
const std::size_t start_index =
|
||||
(is_first_segment
|
||||
? ((start_traversed_in_reverse)
|
||||
? id_vector.size() -
|
||||
phantom_node_pair.source_phantom.fwd_segment_position - 1
|
||||
: phantom_node_pair.source_phantom.fwd_segment_position)
|
||||
: 0);
|
||||
const std::size_t end_index = id_vector.size();
|
||||
|
||||
BOOST_ASSERT(start_index >= 0);
|
||||
BOOST_ASSERT(start_index < end_index);
|
||||
for (std::size_t i = start_index; i < end_index; ++i)
|
||||
{
|
||||
BOOST_ASSERT(!facade->EdgeIsCompressed(ed.id));
|
||||
unpacked_path.emplace_back(facade->GetGeometryIndexForEdgeID(ed.id), name_index,
|
||||
turn_instruction, ed.distance, travel_mode);
|
||||
unpacked_path.push_back(
|
||||
PathData{id_vector[i], name_index, weight_vector[i],
|
||||
extractor::guidance::TurnInstruction::NO_TURN(), travel_mode});
|
||||
}
|
||||
else
|
||||
BOOST_ASSERT(unpacked_path.size() > 0);
|
||||
unpacked_path.back().turn_instruction = turn_instruction;
|
||||
unpacked_path.back().duration_until_turn += (ed.distance - total_weight);
|
||||
|
||||
if (is_first_segment)
|
||||
{
|
||||
std::vector<NodeID> id_vector;
|
||||
facade->GetUncompressedGeometry(facade->GetGeometryIndexForEdgeID(ed.id),
|
||||
id_vector);
|
||||
|
||||
std::vector<EdgeWeight> weight_vector;
|
||||
facade->GetUncompressedWeights(facade->GetGeometryIndexForEdgeID(ed.id),
|
||||
weight_vector);
|
||||
|
||||
int total_weight = std::accumulate(weight_vector.begin(), weight_vector.end(), 0);
|
||||
|
||||
BOOST_ASSERT(weight_vector.size() == id_vector.size());
|
||||
// ed.distance should be total_weight + penalties (turn, stop, etc)
|
||||
BOOST_ASSERT(ed.distance >= total_weight);
|
||||
|
||||
const std::size_t start_index =
|
||||
(unpacked_path.empty()
|
||||
? ((start_traversed_in_reverse)
|
||||
? id_vector.size() -
|
||||
phantom_node_pair.source_phantom.fwd_segment_position - 1
|
||||
: phantom_node_pair.source_phantom.fwd_segment_position)
|
||||
: 0);
|
||||
const std::size_t end_index = id_vector.size();
|
||||
|
||||
BOOST_ASSERT(start_index >= 0);
|
||||
BOOST_ASSERT(start_index <= end_index);
|
||||
for (std::size_t i = start_index; i < end_index; ++i)
|
||||
{
|
||||
unpacked_path.emplace_back(id_vector[i], name_index,
|
||||
extractor::TurnInstruction::NoTurn, weight_vector[i],
|
||||
travel_mode);
|
||||
}
|
||||
unpacked_path.back().turn_instruction = turn_instruction;
|
||||
unpacked_path.back().segment_duration += (ed.distance - total_weight);
|
||||
auto source_weight = start_traversed_in_reverse
|
||||
? phantom_node_pair.source_phantom.reverse_weight
|
||||
: phantom_node_pair.source_phantom.forward_weight;
|
||||
// Given this geometry:
|
||||
// U---v---w---x---Z
|
||||
// s
|
||||
// The above code will create segments for (v, w), (w,x) and (x, Z).
|
||||
// However the first segment duration needs to be adjusted to the fact that the
|
||||
// source phantom is in the middle of the segment.
|
||||
// We do this by subtracting v--s from the duration.
|
||||
BOOST_ASSERT(unpacked_path.front().duration_until_turn >= source_weight);
|
||||
unpacked_path.front().duration_until_turn -= source_weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<unsigned> id_vector;
|
||||
facade->GetUncompressedGeometry(phantom_node_pair.target_phantom.forward_packed_geometry_id,
|
||||
id_vector);
|
||||
std::vector<EdgeWeight> weight_vector;
|
||||
facade->GetUncompressedWeights(phantom_node_pair.target_phantom.forward_packed_geometry_id,
|
||||
weight_vector);
|
||||
const bool is_local_path = (phantom_node_pair.source_phantom.forward_packed_geometry_id ==
|
||||
phantom_node_pair.target_phantom.forward_packed_geometry_id) &&
|
||||
unpacked_path.empty();
|
||||
unpacked_path.empty();
|
||||
|
||||
std::size_t start_index = 0;
|
||||
if (is_local_path)
|
||||
{
|
||||
start_index = phantom_node_pair.source_phantom.fwd_segment_position;
|
||||
if (target_traversed_in_reverse)
|
||||
{
|
||||
start_index =
|
||||
id_vector.size() - phantom_node_pair.source_phantom.fwd_segment_position;
|
||||
}
|
||||
else
|
||||
{
|
||||
start_index = phantom_node_pair.source_phantom.fwd_segment_position;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t end_index = phantom_node_pair.target_phantom.fwd_segment_position;
|
||||
if (target_traversed_in_reverse)
|
||||
{
|
||||
std::reverse(id_vector.begin(), id_vector.end());
|
||||
end_index =
|
||||
id_vector.size() - phantom_node_pair.target_phantom.fwd_segment_position;
|
||||
end_index = id_vector.size() - phantom_node_pair.target_phantom.fwd_segment_position;
|
||||
}
|
||||
|
||||
if (start_index > end_index)
|
||||
@@ -370,16 +380,35 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
|
||||
start_index = std::min(start_index, id_vector.size() - 1);
|
||||
}
|
||||
|
||||
// Given the following compressed geometry:
|
||||
// U---v---w---x---y---Z
|
||||
// s t
|
||||
// s: fwd_segment 0
|
||||
// t: fwd_segment 3
|
||||
// -> (U, v), (v, w), (w, x)
|
||||
// note that (x, t) is _not_ included but needs to be added later.
|
||||
for (std::size_t i = start_index; i != end_index; (start_index < end_index ? ++i : --i))
|
||||
{
|
||||
BOOST_ASSERT(i < id_vector.size());
|
||||
BOOST_ASSERT(phantom_node_pair.target_phantom.forward_travel_mode > 0);
|
||||
unpacked_path.emplace_back(
|
||||
PathData{id_vector[i], phantom_node_pair.target_phantom.name_id,
|
||||
extractor::TurnInstruction::NoTurn, 0,
|
||||
target_traversed_in_reverse
|
||||
? phantom_node_pair.target_phantom.backward_travel_mode
|
||||
: phantom_node_pair.target_phantom.forward_travel_mode});
|
||||
unpacked_path.emplace_back(PathData{
|
||||
id_vector[i], phantom_node_pair.target_phantom.name_id, weight_vector[i],
|
||||
extractor::guidance::TurnInstruction::NO_TURN(),
|
||||
target_traversed_in_reverse ? phantom_node_pair.target_phantom.backward_travel_mode
|
||||
: phantom_node_pair.target_phantom.forward_travel_mode});
|
||||
}
|
||||
|
||||
if (is_local_path && unpacked_path.size() > 0)
|
||||
{
|
||||
auto source_weight = start_traversed_in_reverse
|
||||
? phantom_node_pair.source_phantom.reverse_weight
|
||||
: phantom_node_pair.source_phantom.forward_weight;
|
||||
// The above code will create segments for (v, w), (w,x), (x, y) and (y, Z).
|
||||
// However the first segment duration needs to be adjusted to the fact that the source
|
||||
// phantom is in the middle of the segment. We do this by subtracting v--s from the
|
||||
// duration.
|
||||
BOOST_ASSERT(unpacked_path.front().duration_until_turn >= source_weight);
|
||||
unpacked_path.front().duration_until_turn -= source_weight;
|
||||
}
|
||||
|
||||
// there is no equivalent to a node-based node in an edge-expanded graph.
|
||||
@@ -395,7 +424,8 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
|
||||
// looks like a trivially true check but tests for underflow
|
||||
BOOST_ASSERT(last_index > second_to_last_index);
|
||||
|
||||
if (unpacked_path[last_index].node == unpacked_path[second_to_last_index].node)
|
||||
if (unpacked_path[last_index].turn_via_node ==
|
||||
unpacked_path[second_to_last_index].turn_via_node)
|
||||
{
|
||||
unpacked_path.pop_back();
|
||||
}
|
||||
@@ -753,12 +783,12 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
|
||||
nodes.target_phantom = target_phantom;
|
||||
UnpackPath(packed_path.begin(), packed_path.end(), nodes, unpacked_path);
|
||||
|
||||
util::FixedPointCoordinate previous_coordinate = source_phantom.location;
|
||||
util::FixedPointCoordinate current_coordinate;
|
||||
util::Coordinate previous_coordinate = source_phantom.location;
|
||||
util::Coordinate current_coordinate;
|
||||
double distance = 0;
|
||||
for (const auto &p : unpacked_path)
|
||||
{
|
||||
current_coordinate = facade->GetCoordinateOfNode(p.node);
|
||||
current_coordinate = facade->GetCoordinateOfNode(p.turn_via_node);
|
||||
distance += util::coordinate_calculation::haversineDistance(previous_coordinate,
|
||||
current_coordinate);
|
||||
previous_coordinate = current_coordinate;
|
||||
@@ -777,7 +807,7 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
|
||||
SearchEngineData::QueryHeap &reverse_core_heap,
|
||||
const PhantomNode &source_phantom,
|
||||
const PhantomNode &target_phantom,
|
||||
int duration_upper_bound=INVALID_EDGE_WEIGHT) const
|
||||
int duration_upper_bound = INVALID_EDGE_WEIGHT) const
|
||||
{
|
||||
BOOST_ASSERT(forward_heap.Empty());
|
||||
BOOST_ASSERT(reverse_heap.Empty());
|
||||
@@ -831,7 +861,7 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
|
||||
SearchEngineData::QueryHeap &reverse_heap,
|
||||
const PhantomNode &source_phantom,
|
||||
const PhantomNode &target_phantom,
|
||||
int duration_upper_bound=INVALID_EDGE_WEIGHT) const
|
||||
int duration_upper_bound = INVALID_EDGE_WEIGHT) const
|
||||
{
|
||||
BOOST_ASSERT(forward_heap.Empty());
|
||||
BOOST_ASSERT(reverse_heap.Empty());
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "util/integer_range.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@@ -237,10 +238,9 @@ class ShortestPathRouting final
|
||||
}
|
||||
|
||||
void operator()(const std::vector<PhantomNodes> &phantom_nodes_vector,
|
||||
const std::vector<bool> &uturn_indicators,
|
||||
const boost::optional<bool> uturns,
|
||||
InternalRouteResult &raw_route_data) const
|
||||
{
|
||||
BOOST_ASSERT(uturn_indicators.size() == phantom_nodes_vector.size() + 1);
|
||||
engine_working_data.InitializeOrClearFirstThreadLocalStorage(
|
||||
super::facade->GetNumberOfNodes());
|
||||
engine_working_data.InitializeOrClearSecondThreadLocalStorage(
|
||||
@@ -266,6 +266,8 @@ class ShortestPathRouting final
|
||||
std::vector<NodeID> total_packed_path_to_reverse;
|
||||
std::vector<std::size_t> packed_leg_to_reverse_begin;
|
||||
|
||||
const bool allow_u_turn_at_via = uturns ? *uturns : super::facade->GetUTurnsDefault();
|
||||
|
||||
std::size_t current_leg = 0;
|
||||
// this implements a dynamic program that finds the shortest route through
|
||||
// a list of vias
|
||||
@@ -280,9 +282,6 @@ class ShortestPathRouting final
|
||||
const auto &source_phantom = phantom_node_pair.source_phantom;
|
||||
const auto &target_phantom = phantom_node_pair.target_phantom;
|
||||
|
||||
BOOST_ASSERT(current_leg + 1 < uturn_indicators.size());
|
||||
const bool allow_u_turn_at_via = uturn_indicators[current_leg + 1];
|
||||
|
||||
bool search_to_forward_node = target_phantom.forward_node_id != SPECIAL_NODEID;
|
||||
bool search_to_reverse_node = target_phantom.reverse_node_id != SPECIAL_NODEID;
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
#ifndef SEARCH_ENGINE_HPP
|
||||
#define SEARCH_ENGINE_HPP
|
||||
|
||||
#include "engine/search_engine_data.hpp"
|
||||
#include "engine/routing_algorithms/alternative_path.hpp"
|
||||
#include "engine/routing_algorithms/many_to_many.hpp"
|
||||
#include "engine/routing_algorithms/map_matching.hpp"
|
||||
#include "engine/routing_algorithms/shortest_path.hpp"
|
||||
#include "engine/routing_algorithms/direct_shortest_path.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
|
||||
template <class DataFacadeT> class SearchEngine
|
||||
{
|
||||
private:
|
||||
DataFacadeT *facade;
|
||||
SearchEngineData engine_working_data;
|
||||
|
||||
public:
|
||||
routing_algorithms::ShortestPathRouting<DataFacadeT> shortest_path;
|
||||
routing_algorithms::DirectShortestPathRouting<DataFacadeT> direct_shortest_path;
|
||||
routing_algorithms::AlternativeRouting<DataFacadeT> alternative_path;
|
||||
routing_algorithms::ManyToManyRouting<DataFacadeT> distance_table;
|
||||
routing_algorithms::MapMatching<DataFacadeT> map_matching;
|
||||
|
||||
explicit SearchEngine(DataFacadeT *facade)
|
||||
: facade(facade), shortest_path(facade, engine_working_data),
|
||||
direct_shortest_path(facade, engine_working_data),
|
||||
alternative_path(facade, engine_working_data),
|
||||
distance_table(facade, engine_working_data), map_matching(facade, engine_working_data)
|
||||
{
|
||||
static_assert(!std::is_pointer<DataFacadeT>::value, "don't instantiate with ptr type");
|
||||
static_assert(std::is_object<DataFacadeT>::value,
|
||||
"don't instantiate with void, function, or reference");
|
||||
}
|
||||
|
||||
~SearchEngine() {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SEARCH_ENGINE_HPP
|
||||
@@ -1,60 +0,0 @@
|
||||
#ifndef SEGMENT_INFORMATION_HPP
|
||||
#define SEGMENT_INFORMATION_HPP
|
||||
|
||||
#include "extractor/turn_instructions.hpp"
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
#include <utility>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
|
||||
// Struct fits everything in one cache line
|
||||
struct SegmentInformation
|
||||
{
|
||||
util::FixedPointCoordinate location;
|
||||
NodeID name_id;
|
||||
EdgeWeight duration;
|
||||
float length;
|
||||
short pre_turn_bearing; // more than enough [0..3600] fits into 12 bits
|
||||
short post_turn_bearing;
|
||||
extractor::TurnInstruction turn_instruction;
|
||||
extractor::TravelMode travel_mode;
|
||||
bool necessary;
|
||||
bool is_via_location;
|
||||
|
||||
explicit SegmentInformation(util::FixedPointCoordinate location,
|
||||
const NodeID name_id,
|
||||
const EdgeWeight duration,
|
||||
const float length,
|
||||
const extractor::TurnInstruction turn_instruction,
|
||||
const bool necessary,
|
||||
const bool is_via_location,
|
||||
const extractor::TravelMode travel_mode)
|
||||
: location(std::move(location)), name_id(name_id), duration(duration), length(length),
|
||||
pre_turn_bearing(0), post_turn_bearing(0), turn_instruction(turn_instruction),
|
||||
travel_mode(travel_mode), necessary(necessary), is_via_location(is_via_location)
|
||||
{
|
||||
}
|
||||
|
||||
explicit SegmentInformation(util::FixedPointCoordinate location,
|
||||
const NodeID name_id,
|
||||
const EdgeWeight duration,
|
||||
const float length,
|
||||
const extractor::TurnInstruction turn_instruction,
|
||||
const extractor::TravelMode travel_mode)
|
||||
: location(std::move(location)), name_id(name_id), duration(duration), length(length),
|
||||
pre_turn_bearing(0), post_turn_bearing(0), turn_instruction(turn_instruction),
|
||||
travel_mode(travel_mode),
|
||||
necessary(turn_instruction != extractor::TurnInstruction::NoTurn), is_via_location(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SEGMENT_INFORMATION_HPP */
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ENGINE_STATUS_HPP
|
||||
#define ENGINE_STATUS_HPP
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
|
||||
enum class Status
|
||||
{
|
||||
Ok,
|
||||
Error
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef TRIP_BRUTE_FORCE_HPP
|
||||
#define TRIP_BRUTE_FORCE_HPP
|
||||
|
||||
#include "engine/search_engine.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
#include "util/dist_table_wrapper.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef TRIP_FARTHEST_INSERTION_HPP
|
||||
#define TRIP_FARTHEST_INSERTION_HPP
|
||||
|
||||
#include "engine/search_engine.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
#include "util/dist_table_wrapper.hpp"
|
||||
|
||||
#include "osrm/json_container.hpp"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef TRIP_NEAREST_NEIGHBOUR_HPP
|
||||
#define TRIP_NEAREST_NEIGHBOUR_HPP
|
||||
|
||||
#include "engine/search_engine.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
#include "util/dist_table_wrapper.hpp"
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ class CompressedEdgeContainer
|
||||
public:
|
||||
struct CompressedEdge
|
||||
{
|
||||
public:
|
||||
NodeID node_id; // refers to an internal node-based-node
|
||||
EdgeWeight weight; // the weight of the edge leading to this node
|
||||
public:
|
||||
NodeID node_id; // refers to an internal node-based-node
|
||||
EdgeWeight weight; // the weight of the edge leading to this node
|
||||
};
|
||||
using EdgeBucket = std::vector<CompressedEdge>;
|
||||
|
||||
@@ -32,9 +32,8 @@ class CompressedEdgeContainer
|
||||
const EdgeWeight weight1,
|
||||
const EdgeWeight weight2);
|
||||
|
||||
void AddUncompressedEdge(const EdgeID edgei_id,
|
||||
const NodeID target_node,
|
||||
const EdgeWeight weight);
|
||||
void
|
||||
AddUncompressedEdge(const EdgeID edgei_id, const NodeID target_node, const EdgeWeight weight);
|
||||
|
||||
bool HasEntryForID(const EdgeID edge_id) const;
|
||||
void PrintStatistics() const;
|
||||
|
||||
@@ -4,16 +4,20 @@
|
||||
#define EDGE_BASED_GRAPH_FACTORY_HPP_
|
||||
|
||||
#include "extractor/edge_based_edge.hpp"
|
||||
#include "extractor/speed_profile.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
#include "extractor/profile_properties.hpp"
|
||||
#include "extractor/restriction_map.hpp"
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "util/deallocating_vector.hpp"
|
||||
#include "extractor/edge_based_node.hpp"
|
||||
#include "extractor/original_edge_data.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
#include "extractor/turn_instructions.hpp"
|
||||
#include "extractor/guidance/turn_analysis.hpp"
|
||||
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
|
||||
#include "util/node_based_graph.hpp"
|
||||
#include "extractor/restriction_map.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
#include "util/deallocating_vector.hpp"
|
||||
#include "util/name_table.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
@@ -48,7 +52,8 @@ class EdgeBasedGraphFactory
|
||||
const std::unordered_set<NodeID> &traffic_lights,
|
||||
std::shared_ptr<const RestrictionMap> restriction_map,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
SpeedProfileProperties speed_profile);
|
||||
ProfileProperties profile_properties,
|
||||
const util::NameTable &name_table);
|
||||
|
||||
void Run(const std::string &original_edge_data_filename,
|
||||
lua_State *lua_state,
|
||||
@@ -68,12 +73,12 @@ class EdgeBasedGraphFactory
|
||||
// with known angle.
|
||||
// Handles special cases like u-turns and roundabouts
|
||||
// For basic turns, the turn based on the angle-classification is returned
|
||||
TurnInstruction AnalyzeTurn(const NodeID u,
|
||||
const EdgeID e1,
|
||||
const NodeID v,
|
||||
const EdgeID e2,
|
||||
const NodeID w,
|
||||
const double angle) const;
|
||||
guidance::TurnInstruction AnalyzeTurn(const NodeID u,
|
||||
const EdgeID e1,
|
||||
const NodeID v,
|
||||
const EdgeID e2,
|
||||
const NodeID w,
|
||||
const double angle) const;
|
||||
|
||||
std::int32_t GetTurnPenalty(double angle, lua_State *lua_state) const;
|
||||
|
||||
@@ -101,73 +106,24 @@ class EdgeBasedGraphFactory
|
||||
const std::unordered_set<NodeID> &m_traffic_lights;
|
||||
const CompressedEdgeContainer &m_compressed_edge_container;
|
||||
|
||||
SpeedProfileProperties speed_profile;
|
||||
ProfileProperties profile_properties;
|
||||
|
||||
const util::NameTable &name_table;
|
||||
|
||||
void CompressGeometry();
|
||||
unsigned RenumberEdges();
|
||||
void GenerateEdgeExpandedNodes();
|
||||
#ifdef DEBUG_GEOMETRY
|
||||
void GenerateEdgeExpandedEdges(const std::string &original_edge_data_filename,
|
||||
lua_State *lua_state,
|
||||
const std::string &edge_segment_lookup_filename,
|
||||
const std::string &edge_fixed_penalties_filename,
|
||||
const bool generate_edge_lookup,
|
||||
const std::string &debug_turns_path);
|
||||
#else
|
||||
void GenerateEdgeExpandedEdges(const std::string &original_edge_data_filename,
|
||||
lua_State *lua_state,
|
||||
const std::string &edge_segment_lookup_filename,
|
||||
const std::string &edge_fixed_penalties_filename,
|
||||
const bool generate_edge_lookup);
|
||||
#endif
|
||||
|
||||
void InsertEdgeBasedNode(const NodeID u, const NodeID v);
|
||||
|
||||
void FlushVectorToStream(std::ofstream &edge_data_file,
|
||||
std::vector<OriginalEdgeData> &original_edge_data_vector) const;
|
||||
|
||||
struct TurnCandidate
|
||||
{
|
||||
EdgeID eid; // the id of the arc
|
||||
bool valid; // a turn may be relevant to good instructions, even if we cannot take the road
|
||||
double angle; // the approximated angle of the turn
|
||||
TurnInstruction instruction; // a proposed instruction
|
||||
double confidence; // how close to the border is the turn?
|
||||
|
||||
std::string toString() const
|
||||
{
|
||||
std::string result = "[turn] ";
|
||||
result += std::to_string(eid);
|
||||
result += " valid: ";
|
||||
result += std::to_string(valid);
|
||||
result += " angle: ";
|
||||
result += std::to_string(angle);
|
||||
result += " instruction: ";
|
||||
result += std::to_string(static_cast<std::int32_t>(instruction));
|
||||
result += " confidence: ";
|
||||
result += std::to_string(confidence);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Use In Order to generate base turns
|
||||
|
||||
// cannot be const due to the counters...
|
||||
std::vector<TurnCandidate> getTurnCandidates(NodeID from, EdgeID via_edge);
|
||||
std::vector<TurnCandidate> optimizeCandidates(NodeID via_edge,
|
||||
std::vector<TurnCandidate> turn_candidates) const;
|
||||
std::vector<TurnCandidate> suppressTurns(EdgeID via_edge,
|
||||
std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
QueryNode getRepresentativeCoordinate(const NodeID src,
|
||||
const NodeID tgt,
|
||||
const EdgeID via_eid,
|
||||
bool INVERTED) const;
|
||||
|
||||
bool isObviousChoice(EdgeID coming_from_eid,
|
||||
std::size_t turn_index,
|
||||
const std::vector<TurnCandidate> &turn_candidates) const;
|
||||
|
||||
std::size_t restricted_turns_counter;
|
||||
std::size_t skipped_uturns_counter;
|
||||
std::size_t skipped_barrier_turns_counter;
|
||||
|
||||
@@ -22,8 +22,7 @@ struct EdgeBasedNode
|
||||
EdgeBasedNode()
|
||||
: forward_edge_based_node_id(SPECIAL_NODEID), reverse_edge_based_node_id(SPECIAL_NODEID),
|
||||
u(SPECIAL_NODEID), v(SPECIAL_NODEID), name_id(0),
|
||||
forward_packed_geometry_id(SPECIAL_EDGEID),
|
||||
reverse_packed_geometry_id(SPECIAL_EDGEID),
|
||||
forward_packed_geometry_id(SPECIAL_EDGEID), reverse_packed_geometry_id(SPECIAL_EDGEID),
|
||||
component{INVALID_COMPONENTID, false},
|
||||
fwd_segment_position(std::numeric_limits<unsigned short>::max()),
|
||||
forward_travel_mode(TRAVEL_MODE_INACCESSIBLE),
|
||||
@@ -47,21 +46,19 @@ struct EdgeBasedNode
|
||||
reverse_edge_based_node_id(reverse_edge_based_node_id), u(u), v(v), name_id(name_id),
|
||||
forward_packed_geometry_id(forward_weight_or_packed_geometry_id_),
|
||||
reverse_packed_geometry_id(reverse_weight_or_packed_geometry_id_),
|
||||
component{component_id, is_tiny_component},
|
||||
fwd_segment_position(fwd_segment_position), forward_travel_mode(forward_travel_mode),
|
||||
backward_travel_mode(backward_travel_mode)
|
||||
component{component_id, is_tiny_component}, fwd_segment_position(fwd_segment_position),
|
||||
forward_travel_mode(forward_travel_mode), backward_travel_mode(backward_travel_mode)
|
||||
{
|
||||
BOOST_ASSERT((forward_edge_based_node_id != SPECIAL_NODEID) ||
|
||||
(reverse_edge_based_node_id != SPECIAL_NODEID));
|
||||
}
|
||||
|
||||
static inline util::FixedPointCoordinate Centroid(const util::FixedPointCoordinate a,
|
||||
const util::FixedPointCoordinate b)
|
||||
static inline util::Coordinate Centroid(const util::Coordinate a, const util::Coordinate b)
|
||||
{
|
||||
util::FixedPointCoordinate centroid;
|
||||
util::Coordinate centroid;
|
||||
// The coordinates of the midpoint are given by:
|
||||
centroid.lat = (a.lat + b.lat) / 2;
|
||||
centroid.lon = (a.lon + b.lon) / 2;
|
||||
centroid.lon = (a.lon + b.lon) / util::FixedLongitude(2);
|
||||
centroid.lat = (a.lat + b.lat) / util::FixedLatitude(2);
|
||||
return centroid;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,12 @@ namespace extractor
|
||||
|
||||
struct ExternalMemoryNode : QueryNode
|
||||
{
|
||||
ExternalMemoryNode(int lat, int lon, OSMNodeID node_id, bool barrier, bool traffic_lights)
|
||||
: QueryNode(lat, lon, node_id), barrier(barrier), traffic_lights(traffic_lights)
|
||||
ExternalMemoryNode(const util::FixedLongitude lon_,
|
||||
const util::FixedLatitude lat_,
|
||||
OSMNodeID node_id_,
|
||||
bool barrier_,
|
||||
bool traffic_lights_)
|
||||
: QueryNode(lon_, lat_, node_id_), barrier(barrier_), traffic_lights(traffic_lights_)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -21,12 +25,14 @@ struct ExternalMemoryNode : QueryNode
|
||||
|
||||
static ExternalMemoryNode min_value()
|
||||
{
|
||||
return ExternalMemoryNode(0, 0, MIN_OSM_NODEID, false, false);
|
||||
return ExternalMemoryNode(util::FixedLongitude(0), util::FixedLatitude(0), MIN_OSM_NODEID,
|
||||
false, false);
|
||||
}
|
||||
|
||||
static ExternalMemoryNode max_value()
|
||||
{
|
||||
return ExternalMemoryNode(std::numeric_limits<int>::max(), std::numeric_limits<int>::max(),
|
||||
return ExternalMemoryNode(util::FixedLongitude(std::numeric_limits<int>::max()),
|
||||
util::FixedLatitude(std::numeric_limits<int>::max()),
|
||||
MAX_OSM_NODEID, false, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,59 +31,8 @@ struct ExtractionWay
|
||||
is_startpoint = true;
|
||||
is_access_restricted = false;
|
||||
name.clear();
|
||||
forward_travel_mode = TRAVEL_MODE_DEFAULT;
|
||||
backward_travel_mode = TRAVEL_MODE_DEFAULT;
|
||||
}
|
||||
|
||||
enum Directions
|
||||
{
|
||||
notSure = 0,
|
||||
oneway,
|
||||
bidirectional,
|
||||
opposite
|
||||
};
|
||||
|
||||
// These accessor methods exists to support the depreciated "way.direction" access
|
||||
// in LUA. Since the direction attribute was removed from ExtractionWay, the
|
||||
// accessors translate to/from the mode attributes.
|
||||
void set_direction(const Directions m)
|
||||
{
|
||||
if (Directions::oneway == m)
|
||||
{
|
||||
forward_travel_mode = TRAVEL_MODE_DEFAULT;
|
||||
backward_travel_mode = TRAVEL_MODE_INACCESSIBLE;
|
||||
}
|
||||
else if (Directions::opposite == m)
|
||||
{
|
||||
forward_travel_mode = TRAVEL_MODE_INACCESSIBLE;
|
||||
backward_travel_mode = TRAVEL_MODE_DEFAULT;
|
||||
}
|
||||
else if (Directions::bidirectional == m)
|
||||
{
|
||||
forward_travel_mode = TRAVEL_MODE_DEFAULT;
|
||||
backward_travel_mode = TRAVEL_MODE_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
Directions get_direction() const
|
||||
{
|
||||
if (TRAVEL_MODE_INACCESSIBLE != forward_travel_mode &&
|
||||
TRAVEL_MODE_INACCESSIBLE != backward_travel_mode)
|
||||
{
|
||||
return Directions::bidirectional;
|
||||
}
|
||||
else if (TRAVEL_MODE_INACCESSIBLE != forward_travel_mode)
|
||||
{
|
||||
return Directions::oneway;
|
||||
}
|
||||
else if (TRAVEL_MODE_INACCESSIBLE != backward_travel_mode)
|
||||
{
|
||||
return Directions::opposite;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Directions::notSure;
|
||||
}
|
||||
forward_travel_mode = TRAVEL_MODE_INACCESSIBLE;
|
||||
backward_travel_mode = TRAVEL_MODE_INACCESSIBLE;
|
||||
}
|
||||
|
||||
// These accessors exists because it's not possible to take the address of a bitfield,
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef EXTRACTOR_HPP
|
||||
#define EXTRACTOR_HPP
|
||||
|
||||
@@ -13,6 +40,8 @@ namespace osrm
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
struct ProfileProperties;
|
||||
|
||||
class Extractor
|
||||
{
|
||||
public:
|
||||
@@ -22,13 +51,15 @@ class Extractor
|
||||
private:
|
||||
ExtractorConfig config;
|
||||
|
||||
void SetupScriptingEnvironment(lua_State *myLuaState, SpeedProfileProperties &speed_profile);
|
||||
std::pair<std::size_t, std::size_t>
|
||||
BuildEdgeExpandedGraph(std::vector<QueryNode> &internal_to_external_node_map,
|
||||
BuildEdgeExpandedGraph(lua_State* lua_state,
|
||||
const ProfileProperties& profile_properties,
|
||||
std::vector<QueryNode> &internal_to_external_node_map,
|
||||
std::vector<EdgeBasedNode> &node_based_edge_list,
|
||||
std::vector<bool> &node_is_startpoint,
|
||||
std::vector<EdgeWeight> &edge_based_node_weights,
|
||||
util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list);
|
||||
void WriteProfileProperties(const std::string& output_path, const ProfileProperties& properties) const;
|
||||
void WriteNodeMapping(const std::vector<QueryNode> &internal_to_external_node_map);
|
||||
void FindComponents(unsigned max_edge_id,
|
||||
const util::DeallocatingVector<EdgeBasedEdge> &edges,
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016, Project OSRM contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef EXTRACTOR_CONFIG_HPP
|
||||
#define EXTRACTOR_CONFIG_HPP
|
||||
|
||||
@@ -44,6 +71,7 @@ struct ExtractorConfig
|
||||
edge_segment_lookup_path = basepath + ".osrm.edge_segment_lookup";
|
||||
edge_penalty_path = basepath + ".osrm.edge_penalties";
|
||||
edge_based_node_weights_output_path = basepath + ".osrm.enw";
|
||||
profile_properties_output_path = basepath + ".osrm.properties";
|
||||
}
|
||||
|
||||
boost::filesystem::path config_file_path;
|
||||
@@ -61,6 +89,7 @@ struct ExtractorConfig
|
||||
std::string node_output_path;
|
||||
std::string rtree_nodes_output_path;
|
||||
std::string rtree_leafs_output_path;
|
||||
std::string profile_properties_output_path;
|
||||
|
||||
unsigned requested_num_threads;
|
||||
unsigned small_component_size;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "extractor/speed_profile.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
|
||||
#include <memory>
|
||||
@@ -22,8 +21,6 @@ class GraphCompressor
|
||||
using EdgeData = util::NodeBasedDynamicGraph::EdgeData;
|
||||
|
||||
public:
|
||||
GraphCompressor(SpeedProfileProperties speed_profile);
|
||||
|
||||
void Compress(const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const std::unordered_set<NodeID> &traffic_lights,
|
||||
RestrictionMap &restriction_map,
|
||||
@@ -34,8 +31,6 @@ class GraphCompressor
|
||||
void PrintStatistics(unsigned original_number_of_nodes,
|
||||
unsigned original_number_of_edges,
|
||||
const util::NodeBasedDynamicGraph &graph) const;
|
||||
|
||||
SpeedProfileProperties speed_profile;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef OSRM_EXTRACTOR_CLASSIFICATION_DATA_HPP_
|
||||
#define OSRM_EXTRACTOR_CLASSIFICATION_DATA_HPP_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <string>
|
||||
|
||||
// Forward Declaration to allow usage of external osmium::Way
|
||||
namespace osmium
|
||||
{
|
||||
class Way;
|
||||
}
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
enum class FunctionalRoadClass : std::uint8_t
|
||||
{
|
||||
UNKNOWN = 0,
|
||||
MOTORWAY,
|
||||
MOTORWAY_LINK,
|
||||
TRUNK,
|
||||
TRUNK_LINK,
|
||||
PRIMARY,
|
||||
PRIMARY_LINK,
|
||||
SECONDARY,
|
||||
SECONDARY_LINK,
|
||||
TERTIARY,
|
||||
TERTIARY_LINK,
|
||||
UNCLASSIFIED,
|
||||
RESIDENTIAL,
|
||||
SERVICE,
|
||||
LIVING_STREET,
|
||||
LOW_PRIORITY_ROAD // a road simply included for connectivity. Should be avoided at all cost
|
||||
};
|
||||
|
||||
FunctionalRoadClass functionalRoadClassFromTag(std::string const &tag);
|
||||
|
||||
inline bool isRampClass(const FunctionalRoadClass road_class)
|
||||
{
|
||||
// Primary Roads and down are usually too small to announce their links as ramps
|
||||
return road_class == FunctionalRoadClass::MOTORWAY_LINK ||
|
||||
road_class == FunctionalRoadClass::TRUNK_LINK;
|
||||
}
|
||||
|
||||
// TODO augment this with all data required for guidance generation
|
||||
struct RoadClassificationData
|
||||
{
|
||||
FunctionalRoadClass road_class = FunctionalRoadClass::UNKNOWN;
|
||||
|
||||
void augment(const osmium::Way &way);
|
||||
};
|
||||
|
||||
inline bool operator==(const RoadClassificationData lhs, const RoadClassificationData rhs)
|
||||
{
|
||||
return lhs.road_class == rhs.road_class;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_EXTRACTOR_CLASSIFICATION_DATA_HPP_
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef OSRM_EXTRACTOR_GUIDANCE_DISCRETE_ANGLE
|
||||
#define OSRM_EXTRACTOR_GUIDANCE_DISCRETE_ANGLE
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
using DiscreteAngle = std::uint8_t;
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif /* OSRM_EXTRACTOR_GUIDANCE_DISCRETE_ANGLE */
|
||||
@@ -0,0 +1,404 @@
|
||||
#ifndef OSRM_GUIDANCE_TOOLKIT_HPP_
|
||||
#define OSRM_GUIDANCE_TOOLKIT_HPP_
|
||||
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
|
||||
#include "extractor/guidance/discrete_angle.hpp"
|
||||
#include "extractor/guidance/classification_data.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
const constexpr double DESIRED_SEGMENT_LENGTH = 10.0;
|
||||
const constexpr bool shiftable_ccw[] = {false, true, true, false, false, true, true, false};
|
||||
const constexpr bool shiftable_cw[] = {false, false, true, true, false, false, true, true};
|
||||
const constexpr std::uint8_t modifier_bounds[detail::num_direction_modifiers] = {
|
||||
0, 36, 93, 121, 136, 163, 220, 255};
|
||||
const constexpr double discrete_angle_step_size = 360. / 256.;
|
||||
|
||||
template <typename IteratorType>
|
||||
util::Coordinate
|
||||
getCoordinateFromCompressedRange(util::Coordinate current_coordinate,
|
||||
const IteratorType compressed_geometry_begin,
|
||||
const IteratorType compressed_geometry_end,
|
||||
const util::Coordinate final_coordinate,
|
||||
const std::vector<extractor::QueryNode> &query_nodes)
|
||||
{
|
||||
const auto extractCoordinateFromNode = [](const extractor::QueryNode &node) -> util::Coordinate
|
||||
{
|
||||
return {node.lon, node.lat};
|
||||
};
|
||||
double distance_to_current_coordinate = 0;
|
||||
double distance_to_next_coordinate = 0;
|
||||
|
||||
// get the length that is missing from the current segment to reach DESIRED_SEGMENT_LENGTH
|
||||
const auto getFactor = [](const double first_distance, const double second_distance)
|
||||
{
|
||||
BOOST_ASSERT(first_distance < detail::DESIRED_SEGMENT_LENGTH);
|
||||
double segment_length = second_distance - first_distance;
|
||||
BOOST_ASSERT(segment_length > 0);
|
||||
BOOST_ASSERT(second_distance >= detail::DESIRED_SEGMENT_LENGTH);
|
||||
double missing_distance = detail::DESIRED_SEGMENT_LENGTH - first_distance;
|
||||
return missing_distance / segment_length;
|
||||
};
|
||||
|
||||
for (auto compressed_geometry_itr = compressed_geometry_begin;
|
||||
compressed_geometry_itr != compressed_geometry_end; ++compressed_geometry_itr)
|
||||
{
|
||||
const auto next_coordinate =
|
||||
extractCoordinateFromNode(query_nodes[compressed_geometry_itr->node_id]);
|
||||
distance_to_next_coordinate =
|
||||
distance_to_current_coordinate +
|
||||
util::coordinate_calculation::haversineDistance(current_coordinate, next_coordinate);
|
||||
|
||||
// reached point where coordinates switch between
|
||||
if (distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH)
|
||||
return util::coordinate_calculation::interpolateLinear(
|
||||
getFactor(distance_to_current_coordinate, distance_to_next_coordinate),
|
||||
current_coordinate, next_coordinate);
|
||||
|
||||
// prepare for next iteration
|
||||
current_coordinate = next_coordinate;
|
||||
distance_to_current_coordinate = distance_to_next_coordinate;
|
||||
}
|
||||
|
||||
distance_to_next_coordinate =
|
||||
distance_to_current_coordinate +
|
||||
util::coordinate_calculation::haversineDistance(current_coordinate, final_coordinate);
|
||||
|
||||
// reached point where coordinates switch between
|
||||
if (distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH)
|
||||
return util::coordinate_calculation::interpolateLinear(
|
||||
getFactor(distance_to_current_coordinate, distance_to_next_coordinate),
|
||||
current_coordinate, final_coordinate);
|
||||
else
|
||||
return final_coordinate;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
// Finds a (potentially inteprolated) coordinate that is DESIRED_SEGMENT_LENGTH away
|
||||
// from the start of an edge
|
||||
inline util::Coordinate
|
||||
getRepresentativeCoordinate(const NodeID from_node,
|
||||
const NodeID to_node,
|
||||
const EdgeID via_edge_id,
|
||||
const bool traverse_in_reverse,
|
||||
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||
const std::vector<extractor::QueryNode> &query_nodes)
|
||||
{
|
||||
const auto extractCoordinateFromNode = [](const extractor::QueryNode &node) -> util::Coordinate
|
||||
{
|
||||
return {node.lon, node.lat};
|
||||
};
|
||||
|
||||
// Uncompressed roads are simple, return the coordinate at the end
|
||||
if (!compressed_geometries.HasEntryForID(via_edge_id))
|
||||
{
|
||||
return extractCoordinateFromNode(traverse_in_reverse ? query_nodes[from_node]
|
||||
: query_nodes[to_node]);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto &geometry = compressed_geometries.GetBucketReference(via_edge_id);
|
||||
|
||||
const auto base_node_id = (traverse_in_reverse) ? to_node : from_node;
|
||||
const auto base_coordinate = extractCoordinateFromNode(query_nodes[base_node_id]);
|
||||
|
||||
const auto final_node = (traverse_in_reverse) ? from_node : to_node;
|
||||
const auto final_coordinate = extractCoordinateFromNode(query_nodes[final_node]);
|
||||
|
||||
if (traverse_in_reverse)
|
||||
return detail::getCoordinateFromCompressedRange(
|
||||
base_coordinate, geometry.rbegin(), geometry.rend(), final_coordinate, query_nodes);
|
||||
else
|
||||
return detail::getCoordinateFromCompressedRange(
|
||||
base_coordinate, geometry.begin(), geometry.end(), final_coordinate, query_nodes);
|
||||
}
|
||||
}
|
||||
|
||||
// shift an instruction around the degree circle in CCW order
|
||||
inline DirectionModifier forcedShiftCCW(const DirectionModifier modifier)
|
||||
{
|
||||
return static_cast<DirectionModifier>((static_cast<std::uint32_t>(modifier) + 1) %
|
||||
detail::num_direction_modifiers);
|
||||
}
|
||||
|
||||
inline DirectionModifier shiftCCW(const DirectionModifier modifier)
|
||||
{
|
||||
if (detail::shiftable_ccw[static_cast<int>(modifier)])
|
||||
return forcedShiftCCW(modifier);
|
||||
else
|
||||
return modifier;
|
||||
}
|
||||
|
||||
// shift an instruction around the degree circle in CW order
|
||||
inline DirectionModifier forcedShiftCW(const DirectionModifier modifier)
|
||||
{
|
||||
return static_cast<DirectionModifier>(
|
||||
(static_cast<std::uint32_t>(modifier) + detail::num_direction_modifiers - 1) %
|
||||
detail::num_direction_modifiers);
|
||||
}
|
||||
|
||||
inline DirectionModifier shiftCW(const DirectionModifier modifier)
|
||||
{
|
||||
if (detail::shiftable_cw[static_cast<int>(modifier)])
|
||||
return forcedShiftCW(modifier);
|
||||
else
|
||||
return modifier;
|
||||
}
|
||||
|
||||
inline bool isBasic(const TurnType type)
|
||||
{
|
||||
return type == TurnType::Turn || type == TurnType::EndOfRoad;
|
||||
}
|
||||
|
||||
inline bool isUturn(const TurnInstruction instruction)
|
||||
{
|
||||
return isBasic(instruction.type) && instruction.direction_modifier == DirectionModifier::UTurn;
|
||||
}
|
||||
|
||||
inline bool resolve(TurnInstruction &to_resolve, const TurnInstruction neighbor, bool resolve_cw)
|
||||
{
|
||||
const auto shifted_turn = resolve_cw ? shiftCW(to_resolve.direction_modifier)
|
||||
: shiftCCW(to_resolve.direction_modifier);
|
||||
if (shifted_turn == neighbor.direction_modifier ||
|
||||
shifted_turn == to_resolve.direction_modifier)
|
||||
return false;
|
||||
|
||||
to_resolve.direction_modifier = shifted_turn;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool resolveTransitive(TurnInstruction &first,
|
||||
TurnInstruction &second,
|
||||
const TurnInstruction third,
|
||||
bool resolve_cw)
|
||||
{
|
||||
if (resolve(second, third, resolve_cw))
|
||||
{
|
||||
first.direction_modifier =
|
||||
resolve_cw ? shiftCW(first.direction_modifier) : shiftCCW(first.direction_modifier);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool isSlightTurn(const TurnInstruction turn)
|
||||
{
|
||||
return (isBasic(turn.type) || turn.type == TurnType::NoTurn) &&
|
||||
(turn.direction_modifier == DirectionModifier::Straight ||
|
||||
turn.direction_modifier == DirectionModifier::SlightRight ||
|
||||
turn.direction_modifier == DirectionModifier::SlightLeft);
|
||||
}
|
||||
|
||||
inline bool isSlightModifier(const DirectionModifier direction_modifier)
|
||||
{
|
||||
return (direction_modifier == DirectionModifier::Straight ||
|
||||
direction_modifier == DirectionModifier::SlightRight ||
|
||||
direction_modifier == DirectionModifier::SlightLeft);
|
||||
}
|
||||
|
||||
inline bool isSharpTurn(const TurnInstruction turn)
|
||||
{
|
||||
return isBasic(turn.type) && (turn.direction_modifier == DirectionModifier::SharpLeft ||
|
||||
turn.direction_modifier == DirectionModifier::SharpRight);
|
||||
}
|
||||
|
||||
inline bool isStraight(const TurnInstruction turn)
|
||||
{
|
||||
return (isBasic(turn.type) || turn.type == TurnType::NoTurn) &&
|
||||
turn.direction_modifier == DirectionModifier::Straight;
|
||||
}
|
||||
|
||||
inline bool isConflict(const TurnInstruction first, const TurnInstruction second)
|
||||
{
|
||||
return (first.type == second.type && first.direction_modifier == second.direction_modifier) ||
|
||||
(isStraight(first) && isStraight(second));
|
||||
}
|
||||
|
||||
inline DiscreteAngle discretizeAngle(const double angle)
|
||||
{
|
||||
BOOST_ASSERT(angle >= 0. && angle <= 360.);
|
||||
return DiscreteAngle(static_cast<std::uint8_t>(angle / detail::discrete_angle_step_size));
|
||||
}
|
||||
|
||||
inline double angleFromDiscreteAngle(const DiscreteAngle angle)
|
||||
{
|
||||
return static_cast<double>(angle) * detail::discrete_angle_step_size;
|
||||
}
|
||||
|
||||
inline double angularDeviation(const double angle, const double from)
|
||||
{
|
||||
const double deviation = std::abs(angle - from);
|
||||
return std::min(360 - deviation, deviation);
|
||||
}
|
||||
|
||||
inline double getAngularPenalty(const double angle, DirectionModifier modifier)
|
||||
{
|
||||
// these are not aligned with getTurnDirection but represent an ideal center
|
||||
const double center[] = {0, 45, 90, 135, 180, 225, 270, 315};
|
||||
return angularDeviation(center[static_cast<int>(modifier)], angle);
|
||||
}
|
||||
|
||||
inline double getTurnConfidence(const double angle, TurnInstruction instruction)
|
||||
{
|
||||
|
||||
// special handling of U-Turns and Roundabout
|
||||
if (!isBasic(instruction.type) || instruction.direction_modifier == DirectionModifier::UTurn)
|
||||
return 1.0;
|
||||
|
||||
const double deviations[] = {0, 45, 50, 30, 20, 30, 50, 45};
|
||||
const double difference = getAngularPenalty(angle, instruction.direction_modifier);
|
||||
const double max_deviation = deviations[static_cast<int>(instruction.direction_modifier)];
|
||||
return 1.0 - (difference / max_deviation) * (difference / max_deviation);
|
||||
}
|
||||
|
||||
// Translates between angles and their human-friendly directional representation
|
||||
inline DirectionModifier getTurnDirection(const double angle)
|
||||
{
|
||||
// An angle of zero is a u-turn
|
||||
// 180 goes perfectly straight
|
||||
// 0-180 are right turns
|
||||
// 180-360 are left turns
|
||||
if (angle > 0 && angle < 60)
|
||||
return DirectionModifier::SharpRight;
|
||||
if (angle >= 60 && angle < 140)
|
||||
return DirectionModifier::Right;
|
||||
if (angle >= 140 && angle < 170)
|
||||
return DirectionModifier::SlightRight;
|
||||
if (angle >= 165 && angle <= 195)
|
||||
return DirectionModifier::Straight;
|
||||
if (angle > 190 && angle <= 220)
|
||||
return DirectionModifier::SlightLeft;
|
||||
if (angle > 220 && angle <= 300)
|
||||
return DirectionModifier::Left;
|
||||
if (angle > 300 && angle < 360)
|
||||
return DirectionModifier::SharpLeft;
|
||||
return DirectionModifier::UTurn;
|
||||
}
|
||||
|
||||
// swaps left <-> right modifier types
|
||||
inline DirectionModifier mirrorDirectionModifier(const DirectionModifier modifier)
|
||||
{
|
||||
const constexpr DirectionModifier results[] = {DirectionModifier::UTurn,
|
||||
DirectionModifier::SharpLeft,
|
||||
DirectionModifier::Left,
|
||||
DirectionModifier::SlightLeft,
|
||||
DirectionModifier::Straight,
|
||||
DirectionModifier::SlightRight,
|
||||
DirectionModifier::Right,
|
||||
DirectionModifier::SharpRight};
|
||||
return results[modifier];
|
||||
}
|
||||
|
||||
inline bool canBeSuppressed(const TurnType type)
|
||||
{
|
||||
if (type == TurnType::Turn)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool isLowPriorityRoadClass(const FunctionalRoadClass road_class)
|
||||
{
|
||||
return road_class == FunctionalRoadClass::LOW_PRIORITY_ROAD ||
|
||||
road_class == FunctionalRoadClass::SERVICE;
|
||||
}
|
||||
|
||||
inline bool isDistinct(const DirectionModifier first, const DirectionModifier second)
|
||||
{
|
||||
if ((first + 1) % detail::num_direction_modifiers == second)
|
||||
return false;
|
||||
|
||||
if ((second + 1) % detail::num_direction_modifiers == first)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool requiresNameAnnounced(const std::string &from, const std::string &to)
|
||||
{
|
||||
// FIXME, handle in profile to begin with?
|
||||
// this uses the encoding of references in the profile, which is very BAD
|
||||
// Input for this function should be a struct separating streetname, suffix (e.g. road,
|
||||
// boulevard, North, West ...), and a list of references
|
||||
std::string from_name;
|
||||
std::string from_ref;
|
||||
std::string to_name;
|
||||
std::string to_ref;
|
||||
|
||||
// Split from the format "{name} ({ref})" -> name, ref
|
||||
auto split = [](const std::string &name, std::string &out_name, std::string &out_ref)
|
||||
{
|
||||
const auto ref_begin = name.find_first_of('(');
|
||||
if (ref_begin != std::string::npos)
|
||||
{
|
||||
out_name = name.substr(0, ref_begin);
|
||||
out_ref = name.substr(ref_begin + 1, name.find_first_of(')') - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
out_name = name;
|
||||
}
|
||||
};
|
||||
|
||||
split(from, from_name, from_ref);
|
||||
split(to, to_name, to_ref);
|
||||
|
||||
// check similarity of names
|
||||
auto names_are_empty = from_name.empty() && to_name.empty();
|
||||
auto names_are_equal = from_name == to_name;
|
||||
auto name_is_removed = !from_name.empty() && to_name.empty();
|
||||
// references are contained in one another
|
||||
auto refs_are_empty = from_ref.empty() && to_ref.empty();
|
||||
auto ref_is_contained =
|
||||
!from_ref.empty() && !to_ref.empty() &&
|
||||
(from_ref.find(to_ref) != std::string::npos || to_ref.find(from_ref) != std::string::npos);
|
||||
auto ref_is_removed = !from_ref.empty() && to_ref.empty();
|
||||
|
||||
auto obvious_change = ref_is_contained || names_are_equal ||
|
||||
(names_are_empty && refs_are_empty) || name_is_removed || ref_is_removed;
|
||||
|
||||
return !obvious_change;
|
||||
}
|
||||
|
||||
inline int getPriority( const FunctionalRoadClass road_class )
|
||||
{
|
||||
//The road priorities indicate which roads can bee seen as more or less equal.
|
||||
//They are used in Fork-Discovery. Possibly should be moved to profiles post v5?
|
||||
//A fork can happen between road types that are at most 1 priority apart from each other
|
||||
const constexpr int road_priority[] = {10, 0, 10, 2, 10, 4, 10, 6, 10, 8, 10, 11, 10, 12, 10, 14};
|
||||
return road_priority[static_cast<int>(road_class)];
|
||||
}
|
||||
|
||||
inline bool canBeSeenAsFork(const FunctionalRoadClass first, const FunctionalRoadClass second)
|
||||
{
|
||||
// forks require similar road categories
|
||||
// Based on the priorities assigned above, we can set forks only if the road priorities match closely.
|
||||
// Potentially we could include features like number of lanes here and others?
|
||||
// Should also be moved to profiles
|
||||
return std::abs(getPriority(first) - getPriority(second)) <= 1;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_GUIDANCE_TOOLKIT_HPP_
|
||||
@@ -0,0 +1,202 @@
|
||||
#ifndef OSRM_EXTRACTOR_TURN_ANALYSIS
|
||||
#define OSRM_EXTRACTOR_TURN_ANALYSIS
|
||||
|
||||
#include "extractor/guidance/turn_classification.hpp"
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
#include "extractor/restriction_map.hpp"
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
|
||||
#include "util/name_table.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
// What is exposed to the outside
|
||||
struct TurnOperation final
|
||||
{
|
||||
EdgeID eid;
|
||||
double angle;
|
||||
TurnInstruction instruction;
|
||||
};
|
||||
|
||||
// For the turn analysis, we require a full list of all connected roads to determine the outcome.
|
||||
// Invalid turns can influence the perceived angles
|
||||
//
|
||||
// aaa(2)aa
|
||||
// a - bbbbb
|
||||
// aaa(1)aa
|
||||
//
|
||||
// will not be perceived as a turn from (1) -> b, and as a U-turn from (1) -> (2).
|
||||
// In addition, they can influence whether a turn is obvious or not.
|
||||
struct ConnectedRoad final
|
||||
{
|
||||
ConnectedRoad(const TurnOperation turn, const bool entry_allowed = false);
|
||||
|
||||
TurnOperation turn;
|
||||
bool entry_allowed; // a turn may be relevant to good instructions, even if we cannot take
|
||||
// the road
|
||||
|
||||
std::string toString() const
|
||||
{
|
||||
std::string result = "[connection] ";
|
||||
result += std::to_string(turn.eid);
|
||||
result += " allows entry: ";
|
||||
result += std::to_string(entry_allowed);
|
||||
result += " angle: ";
|
||||
result += std::to_string(turn.angle);
|
||||
result += " instruction: ";
|
||||
result += std::to_string(static_cast<std::int32_t>(turn.instruction.type)) + " " +
|
||||
std::to_string(static_cast<std::int32_t>(turn.instruction.direction_modifier));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
class TurnAnalysis
|
||||
{
|
||||
|
||||
public:
|
||||
TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const RestrictionMap &restriction_map,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const CompressedEdgeContainer &compressed_edge_container,
|
||||
const util::NameTable &name_table);
|
||||
|
||||
// the entry into the turn analysis
|
||||
std::vector<TurnOperation> getTurns(const NodeID from_node, const EdgeID via_eid) const;
|
||||
|
||||
private:
|
||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||
const std::vector<QueryNode> &node_info_list;
|
||||
const RestrictionMap &restriction_map;
|
||||
const std::unordered_set<NodeID> &barrier_nodes;
|
||||
const CompressedEdgeContainer &compressed_edge_container;
|
||||
const util::NameTable &name_table;
|
||||
|
||||
// Check for restrictions/barriers and generate a list of valid and invalid turns present at
|
||||
// the
|
||||
// node reached
|
||||
// from `from_node` via `via_eid`
|
||||
// The resulting candidates have to be analysed for their actual instructions later on.
|
||||
std::vector<ConnectedRoad> getConnectedRoads(const NodeID from_node,
|
||||
const EdgeID via_eid) const;
|
||||
|
||||
// Merge segregated roads to omit invalid turns in favor of treating segregated roads as
|
||||
// one.
|
||||
// This function combines roads the following way:
|
||||
//
|
||||
// * *
|
||||
// * is converted to *
|
||||
// v ^ +
|
||||
// v ^ +
|
||||
//
|
||||
// The treatment results in a straight turn angle of 180º rather than a turn angle of approx
|
||||
// 160
|
||||
std::vector<ConnectedRoad> mergeSegregatedRoads(std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// TODO distinguish roundabouts and rotaries
|
||||
// TODO handle bike/walk cases that allow crossing a roundabout!
|
||||
|
||||
// Processing of roundabouts
|
||||
// Produces instructions to enter/exit a roundabout or to stay on it.
|
||||
// Performs the distinction between roundabout and rotaries.
|
||||
std::vector<ConnectedRoad> handleRoundabouts(const EdgeID via_edge,
|
||||
const bool on_roundabout,
|
||||
const bool can_exit_roundabout,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Indicates a Junction containing a motoryway
|
||||
bool isMotorwayJunction(const EdgeID via_edge,
|
||||
const std::vector<ConnectedRoad> &intersection) const;
|
||||
|
||||
// Decide whether a turn is a turn or a ramp access
|
||||
TurnType findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const;
|
||||
|
||||
// Get the Instruction for an obvious turn
|
||||
// Instruction will be a silent instruction
|
||||
TurnInstruction getInstructionForObvious(const std::size_t number_of_candidates,
|
||||
const EdgeID via_edge,
|
||||
const ConnectedRoad &candidate) const;
|
||||
|
||||
// Helper Function that decides between NoTurn or NewName
|
||||
TurnInstruction
|
||||
noTurnOrNewName(const NodeID from, const EdgeID via_edge, const ConnectedRoad &candidate) const;
|
||||
|
||||
// Basic Turn Handling
|
||||
|
||||
// Dead end.
|
||||
std::vector<ConnectedRoad> handleOneWayTurn(std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Mode Changes, new names...
|
||||
std::vector<ConnectedRoad> handleTwoWayTurn(const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Forks, T intersections and similar
|
||||
std::vector<ConnectedRoad> handleThreeWayTurn(const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Handling of turns larger then degree three
|
||||
std::vector<ConnectedRoad> handleComplexTurn(const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Any Junction containing motorways
|
||||
std::vector<ConnectedRoad> handleMotorwayJunction(
|
||||
const EdgeID via_edge, std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
std::vector<ConnectedRoad> handleFromMotorway(const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
std::vector<ConnectedRoad> handleMotorwayRamp(const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Utility function, setting basic turn types. Prepares for normal turn handling.
|
||||
std::vector<ConnectedRoad> setTurnTypes(const NodeID from,
|
||||
const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Assignment of specific turn types
|
||||
void assignFork(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
|
||||
void assignFork(const EdgeID via_edge,
|
||||
ConnectedRoad &left,
|
||||
ConnectedRoad ¢er,
|
||||
ConnectedRoad &right) const;
|
||||
|
||||
void
|
||||
handleDistinctConflict(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
|
||||
|
||||
// Type specific fallbacks
|
||||
std::vector<ConnectedRoad>
|
||||
fallbackTurnAssignmentMotorway(std::vector<ConnectedRoad> intersection) const;
|
||||
|
||||
// Classification
|
||||
std::size_t findObviousTurn(const EdgeID via_edge,
|
||||
const std::vector<ConnectedRoad> &intersection) const;
|
||||
std::pair<std::size_t, std::size_t>
|
||||
findFork(const std::vector<ConnectedRoad> &intersection) const;
|
||||
|
||||
std::vector<ConnectedRoad> assignLeftTurns(const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection,
|
||||
const std::size_t starting_at) const;
|
||||
std::vector<ConnectedRoad> assignRightTurns(const EdgeID via_edge,
|
||||
std::vector<ConnectedRoad> intersection,
|
||||
const std::size_t up_to) const;
|
||||
|
||||
}; // class TurnAnalysis
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_EXTRACTOR_TURN_ANALYSIS
|
||||
@@ -0,0 +1,123 @@
|
||||
#ifndef OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_
|
||||
#define OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_
|
||||
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
|
||||
#include "util/typedefs.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/node_based_graph.hpp"
|
||||
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/query_node.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
struct TurnPossibility
|
||||
{
|
||||
TurnPossibility(DiscreteAngle angle, EdgeID edge_id)
|
||||
: angle(std::move(angle)), edge_id(std::move(edge_id))
|
||||
{
|
||||
}
|
||||
|
||||
TurnPossibility() : angle(0), edge_id(SPECIAL_EDGEID) {}
|
||||
|
||||
DiscreteAngle angle;
|
||||
EdgeID edge_id;
|
||||
};
|
||||
|
||||
struct CompareTurnPossibilities
|
||||
{
|
||||
bool operator()(const std::vector<TurnPossibility> &left,
|
||||
const std::vector<TurnPossibility> &right) const
|
||||
{
|
||||
if (left.size() < right.size())
|
||||
return true;
|
||||
if (left.size() > right.size())
|
||||
return false;
|
||||
for (std::size_t i = 0; i < left.size(); ++i)
|
||||
{
|
||||
if ((((int)left[i].angle + 16) % 256) / 32 < (((int)right[i].angle + 16) % 256) / 32)
|
||||
return true;
|
||||
if ((((int)left[i].angle + 16) % 256) / 32 > (((int)right[i].angle + 16) % 256) / 32)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
inline std::vector<TurnPossibility>
|
||||
classifyIntersection(NodeID nid,
|
||||
const util::NodeBasedDynamicGraph &graph,
|
||||
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||
const std::vector<extractor::QueryNode> &query_nodes)
|
||||
{
|
||||
|
||||
std::vector<TurnPossibility> turns;
|
||||
|
||||
if (graph.BeginEdges(nid) == graph.EndEdges(nid))
|
||||
return std::vector<TurnPossibility>();
|
||||
|
||||
const EdgeID base_id = graph.BeginEdges(nid);
|
||||
const auto base_coordinate = getRepresentativeCoordinate(nid, graph.GetTarget(base_id), base_id,
|
||||
graph.GetEdgeData(base_id).reversed,
|
||||
compressed_geometries, query_nodes);
|
||||
const auto node_coordinate = util::Coordinate(query_nodes[nid].lon, query_nodes[nid].lat);
|
||||
|
||||
// generate a list of all turn angles between a base edge, the node and a current edge
|
||||
for (const EdgeID eid : graph.GetAdjacentEdgeRange(nid))
|
||||
{
|
||||
const auto edge_coordinate = getRepresentativeCoordinate(
|
||||
nid, graph.GetTarget(eid), eid, false, compressed_geometries, query_nodes);
|
||||
|
||||
double angle = util::coordinate_calculation::computeAngle(base_coordinate, node_coordinate,
|
||||
edge_coordinate);
|
||||
turns.emplace_back(discretizeAngle(angle), eid);
|
||||
}
|
||||
|
||||
std::sort(turns.begin(), turns.end(),
|
||||
[](const TurnPossibility left, const TurnPossibility right)
|
||||
{
|
||||
return left.angle < right.angle;
|
||||
});
|
||||
|
||||
turns.push_back(turns.front()); // sentinel
|
||||
for (std::size_t turn_nr = 0; turn_nr + 1 < turns.size(); ++turn_nr)
|
||||
{
|
||||
turns[turn_nr].angle = (256 + static_cast<uint32_t>(turns[turn_nr + 1].angle) -
|
||||
static_cast<uint32_t>(turns[turn_nr].angle)) %
|
||||
256; // calculate the difference to the right
|
||||
}
|
||||
turns.pop_back(); // remove sentinel again
|
||||
|
||||
// find largest:
|
||||
std::size_t best_id = 0;
|
||||
DiscreteAngle largest_turn_angle = turns.front().angle;
|
||||
for (std::size_t current_turn_id = 1; current_turn_id < turns.size(); ++current_turn_id)
|
||||
{
|
||||
if (turns[current_turn_id].angle > largest_turn_angle)
|
||||
{
|
||||
largest_turn_angle = turns[current_turn_id].angle;
|
||||
best_id = current_turn_id;
|
||||
}
|
||||
}
|
||||
|
||||
// rotate all angles so the largest angle comes first
|
||||
std::rotate(turns.begin(), turns.begin() + best_id, turns.end());
|
||||
|
||||
return turns;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_
|
||||
@@ -0,0 +1,127 @@
|
||||
#ifndef OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_
|
||||
#define OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// inclusive bounds for turn modifiers
|
||||
const constexpr uint8_t num_direction_modifiers = 8;
|
||||
} // detail
|
||||
|
||||
// direction modifiers based on angle
|
||||
// Would be nice to have
|
||||
// enum class DirectionModifier : unsigned char
|
||||
enum DirectionModifier
|
||||
{
|
||||
UTurn,
|
||||
SharpRight,
|
||||
Right,
|
||||
SlightRight,
|
||||
Straight,
|
||||
SlightLeft,
|
||||
Left,
|
||||
SharpLeft
|
||||
};
|
||||
|
||||
// enum class TurnType : unsigned char
|
||||
enum TurnType // at the moment we can support 32 turn types, without increasing memory consumption
|
||||
{
|
||||
Invalid, // no valid turn instruction
|
||||
NoTurn, // end of segment without turn/middle of a segment
|
||||
Suppressed, // location that suppresses a turn
|
||||
NewName, // no turn, but name changes
|
||||
Continue, // remain on a street
|
||||
Turn, // basic turn
|
||||
FirstTurn, // First of x turns
|
||||
SecondTurn, // Second of x turns
|
||||
ThirdTurn, // Third of x turns
|
||||
FourthTurn, // Fourth of x turns
|
||||
Merge, // merge onto a street
|
||||
Ramp, // special turn (highway ramp exits)
|
||||
FirstRamp, // first turn onto a ramp
|
||||
SecondRamp, // second turn onto a ramp
|
||||
ThirdRamp, // third turn onto a ramp
|
||||
FourthRamp, // fourth turn onto a ramp
|
||||
Fork, // fork road splitting up
|
||||
EndOfRoad, // T intersection
|
||||
EnterRoundabout, // Entering a small Roundabout
|
||||
EnterRoundaboutAtExit, // Entering a small Roundabout at a countable exit
|
||||
EnterAndExitRoundabout, // Touching a roundabout
|
||||
ExitRoundabout, // Exiting a small Roundabout
|
||||
EnterRotary, // Enter a rotary
|
||||
EnterRotaryAtExit, // Enter A Rotary at a countable exit
|
||||
EnterAndExitRotary, // Touching a rotary
|
||||
ExitRotary, // Exit a rotary
|
||||
StayOnRoundabout, // Continue on Either a small or a large Roundabout
|
||||
Restriction, // Cross a Barrier, requires barrier penalties instead of full block
|
||||
Notification // Travel Mode Changes`
|
||||
};
|
||||
|
||||
// turn angle in 1.40625 degree -> 128 == 180 degree
|
||||
struct TurnInstruction
|
||||
{
|
||||
TurnInstruction(const TurnType type = TurnType::Invalid,
|
||||
const DirectionModifier direction_modifier = DirectionModifier::Straight)
|
||||
: type(type), direction_modifier(direction_modifier)
|
||||
{
|
||||
}
|
||||
|
||||
TurnType type : 5;
|
||||
DirectionModifier direction_modifier : 3;
|
||||
|
||||
static TurnInstruction INVALID()
|
||||
{
|
||||
return TurnInstruction(TurnType::Invalid, DirectionModifier::UTurn);
|
||||
}
|
||||
|
||||
static TurnInstruction NO_TURN()
|
||||
{
|
||||
return TurnInstruction(TurnType::NoTurn, DirectionModifier::UTurn);
|
||||
}
|
||||
|
||||
static TurnInstruction REMAIN_ROUNDABOUT(const DirectionModifier modifier)
|
||||
{
|
||||
return TurnInstruction(TurnType::StayOnRoundabout, modifier);
|
||||
}
|
||||
|
||||
static TurnInstruction ENTER_ROUNDABOUT(const DirectionModifier modifier)
|
||||
{
|
||||
return TurnInstruction(TurnType::EnterRoundabout, modifier);
|
||||
}
|
||||
|
||||
static TurnInstruction EXIT_ROUNDABOUT(const DirectionModifier modifier)
|
||||
{
|
||||
return TurnInstruction(TurnType::ExitRoundabout, modifier);
|
||||
}
|
||||
|
||||
static TurnInstruction SUPPRESSED(const DirectionModifier modifier)
|
||||
{
|
||||
return TurnInstruction{TurnType::Suppressed, modifier};
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator!=(const TurnInstruction lhs, const TurnInstruction rhs)
|
||||
{
|
||||
return lhs.type != rhs.type || lhs.direction_modifier != rhs.direction_modifier;
|
||||
}
|
||||
|
||||
inline bool operator==(const TurnInstruction lhs, const TurnInstruction rhs)
|
||||
{
|
||||
return lhs.type == rhs.type && lhs.direction_modifier == rhs.direction_modifier;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
#include <utility>
|
||||
#include "extractor/guidance/classification_data.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
@@ -50,7 +51,8 @@ struct InternalExtractorEdge
|
||||
false,
|
||||
true,
|
||||
TRAVEL_MODE_INACCESSIBLE,
|
||||
false)
|
||||
false,
|
||||
guidance::RoadClassificationData())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -64,7 +66,8 @@ struct InternalExtractorEdge
|
||||
bool access_restricted,
|
||||
bool startpoint,
|
||||
TravelMode travel_mode,
|
||||
bool is_split)
|
||||
bool is_split,
|
||||
guidance::RoadClassificationData road_classification)
|
||||
: result(OSMNodeID(source),
|
||||
OSMNodeID(target),
|
||||
name_id,
|
||||
@@ -75,7 +78,8 @@ struct InternalExtractorEdge
|
||||
access_restricted,
|
||||
startpoint,
|
||||
travel_mode,
|
||||
is_split),
|
||||
is_split,
|
||||
std::move(road_classification)),
|
||||
weight_data(std::move(weight_data))
|
||||
{
|
||||
}
|
||||
@@ -85,18 +89,20 @@ struct InternalExtractorEdge
|
||||
// intermediate edge weight
|
||||
WeightData weight_data;
|
||||
// coordinate of the source node
|
||||
util::FixedPointCoordinate source_coordinate;
|
||||
util::Coordinate source_coordinate;
|
||||
|
||||
// necessary static util functions for stxxl's sorting
|
||||
static InternalExtractorEdge min_osm_value()
|
||||
{
|
||||
return InternalExtractorEdge(MIN_OSM_NODEID, MIN_OSM_NODEID, 0, WeightData(), false, false,
|
||||
false, false, true, TRAVEL_MODE_INACCESSIBLE, false);
|
||||
false, false, true, TRAVEL_MODE_INACCESSIBLE, false,
|
||||
guidance::RoadClassificationData());
|
||||
}
|
||||
static InternalExtractorEdge max_osm_value()
|
||||
{
|
||||
return InternalExtractorEdge(MAX_OSM_NODEID, MAX_OSM_NODEID, 0, WeightData(), false, false,
|
||||
false, false, true, TRAVEL_MODE_INACCESSIBLE, false);
|
||||
false, false, true, TRAVEL_MODE_INACCESSIBLE, false,
|
||||
guidance::RoadClassificationData());
|
||||
}
|
||||
|
||||
static InternalExtractorEdge min_internal_value()
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "extractor/guidance/classification_data.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
@@ -23,7 +25,8 @@ struct NodeBasedEdge
|
||||
bool access_restricted,
|
||||
bool startpoint,
|
||||
TravelMode travel_mode,
|
||||
bool is_split);
|
||||
bool is_split,
|
||||
guidance::RoadClassificationData road_classification);
|
||||
|
||||
bool operator<(const NodeBasedEdge &other) const;
|
||||
|
||||
@@ -38,6 +41,7 @@ struct NodeBasedEdge
|
||||
bool startpoint : 1;
|
||||
bool is_split : 1;
|
||||
TravelMode travel_mode : 4;
|
||||
guidance::RoadClassificationData road_classification;
|
||||
};
|
||||
|
||||
struct NodeBasedEdgeWithOSM : NodeBasedEdge
|
||||
@@ -52,7 +56,8 @@ struct NodeBasedEdgeWithOSM : NodeBasedEdge
|
||||
bool access_restricted,
|
||||
bool startpoint,
|
||||
TravelMode travel_mode,
|
||||
bool is_split);
|
||||
bool is_split,
|
||||
guidance::RoadClassificationData road_classification);
|
||||
|
||||
OSMNodeID osm_source_id;
|
||||
OSMNodeID osm_target_id;
|
||||
@@ -77,10 +82,12 @@ inline NodeBasedEdge::NodeBasedEdge(NodeID source,
|
||||
bool access_restricted,
|
||||
bool startpoint,
|
||||
TravelMode travel_mode,
|
||||
bool is_split)
|
||||
bool is_split,
|
||||
guidance::RoadClassificationData road_classification)
|
||||
: source(source), target(target), name_id(name_id), weight(weight), forward(forward),
|
||||
backward(backward), roundabout(roundabout), access_restricted(access_restricted),
|
||||
startpoint(startpoint), is_split(is_split), travel_mode(travel_mode)
|
||||
startpoint(startpoint), is_split(is_split), travel_mode(travel_mode),
|
||||
road_classification(std::move(road_classification))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -101,17 +108,19 @@ inline bool NodeBasedEdge::operator<(const NodeBasedEdge &other) const
|
||||
return source < other.source;
|
||||
}
|
||||
|
||||
inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(OSMNodeID source,
|
||||
OSMNodeID target,
|
||||
NodeID name_id,
|
||||
EdgeWeight weight,
|
||||
bool forward,
|
||||
bool backward,
|
||||
bool roundabout,
|
||||
bool access_restricted,
|
||||
bool startpoint,
|
||||
TravelMode travel_mode,
|
||||
bool is_split)
|
||||
inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(
|
||||
OSMNodeID source,
|
||||
OSMNodeID target,
|
||||
NodeID name_id,
|
||||
EdgeWeight weight,
|
||||
bool forward,
|
||||
bool backward,
|
||||
bool roundabout,
|
||||
bool access_restricted,
|
||||
bool startpoint,
|
||||
TravelMode travel_mode,
|
||||
bool is_split,
|
||||
guidance::RoadClassificationData road_classification)
|
||||
: NodeBasedEdge(SPECIAL_NODEID,
|
||||
SPECIAL_NODEID,
|
||||
name_id,
|
||||
@@ -122,7 +131,8 @@ inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(OSMNodeID source,
|
||||
access_restricted,
|
||||
startpoint,
|
||||
travel_mode,
|
||||
is_split),
|
||||
is_split,
|
||||
std::move(road_classification)),
|
||||
osm_source_id(std::move(source)), osm_target_id(std::move(target))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define ORIGINAL_EDGE_DATA_HPP
|
||||
|
||||
#include "extractor/travel_mode.hpp"
|
||||
#include "extractor/turn_instructions.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <limits>
|
||||
@@ -16,25 +16,24 @@ struct OriginalEdgeData
|
||||
{
|
||||
explicit OriginalEdgeData(NodeID via_node,
|
||||
unsigned name_id,
|
||||
TurnInstruction turn_instruction,
|
||||
bool compressed_geometry,
|
||||
guidance::TurnInstruction turn_instruction,
|
||||
TravelMode travel_mode)
|
||||
: via_node(via_node), name_id(name_id), turn_instruction(turn_instruction),
|
||||
compressed_geometry(compressed_geometry), travel_mode(travel_mode)
|
||||
travel_mode(travel_mode)
|
||||
{
|
||||
}
|
||||
|
||||
OriginalEdgeData()
|
||||
: via_node(std::numeric_limits<unsigned>::max()),
|
||||
name_id(std::numeric_limits<unsigned>::max()), turn_instruction(TurnInstruction::NoTurn),
|
||||
compressed_geometry(false), travel_mode(TRAVEL_MODE_INACCESSIBLE)
|
||||
name_id(std::numeric_limits<unsigned>::max()),
|
||||
turn_instruction(guidance::TurnInstruction::INVALID()),
|
||||
travel_mode(TRAVEL_MODE_INACCESSIBLE)
|
||||
{
|
||||
}
|
||||
|
||||
NodeID via_node;
|
||||
unsigned name_id;
|
||||
TurnInstruction turn_instruction;
|
||||
bool compressed_geometry;
|
||||
guidance::TurnInstruction turn_instruction;
|
||||
TravelMode travel_mode;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef PROFILE_PROPERTIES_HPP
|
||||
#define PROFILE_PROPERTIES_HPP
|
||||
|
||||
#include <boost/numeric/conversion/cast.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
struct ProfileProperties
|
||||
{
|
||||
ProfileProperties()
|
||||
: traffic_signal_penalty(0), u_turn_penalty(0), allow_u_turn_at_via(false), use_turn_restrictions(false)
|
||||
{
|
||||
}
|
||||
|
||||
double GetUturnPenalty() const
|
||||
{
|
||||
return u_turn_penalty / 10.;
|
||||
}
|
||||
|
||||
void SetUturnPenalty(const double u_turn_penalty_)
|
||||
{
|
||||
u_turn_penalty = boost::numeric_cast<int>(u_turn_penalty_ * 10.);
|
||||
}
|
||||
|
||||
double GetTrafficSignalPenalty() const
|
||||
{
|
||||
return traffic_signal_penalty / 10.;
|
||||
}
|
||||
|
||||
void SetTrafficSignalPenalty(const double traffic_signal_penalty_)
|
||||
{
|
||||
traffic_signal_penalty = boost::numeric_cast<int>(traffic_signal_penalty_ * 10.);
|
||||
}
|
||||
|
||||
//! penalty to cross a traffic light in deci-seconds
|
||||
int traffic_signal_penalty;
|
||||
//! penalty to do a uturn in deci-seconds
|
||||
int u_turn_penalty;
|
||||
bool allow_u_turn_at_via;
|
||||
bool use_turn_restrictions;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "osrm/coordinate.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
@@ -17,30 +17,32 @@ struct QueryNode
|
||||
using key_type = OSMNodeID; // type of NodeID
|
||||
using value_type = int; // type of lat,lons
|
||||
|
||||
explicit QueryNode(int lat, int lon, key_type node_id)
|
||||
: lat(lat), lon(lon), node_id(std::move(node_id))
|
||||
explicit QueryNode(const util::FixedLongitude lon_,
|
||||
const util::FixedLatitude lat_,
|
||||
key_type node_id)
|
||||
: lon(lon_), lat(lat_), node_id(std::move(node_id))
|
||||
{
|
||||
}
|
||||
QueryNode()
|
||||
: lat(std::numeric_limits<int>::max()), lon(std::numeric_limits<int>::max()),
|
||||
: lon(std::numeric_limits<int>::max()), lat(std::numeric_limits<int>::max()),
|
||||
node_id(SPECIAL_OSM_NODEID)
|
||||
{
|
||||
}
|
||||
|
||||
int lat;
|
||||
int lon;
|
||||
util::FixedLongitude lon;
|
||||
util::FixedLatitude lat;
|
||||
key_type node_id;
|
||||
|
||||
static QueryNode min_value()
|
||||
{
|
||||
return QueryNode(static_cast<int>(-90 * COORDINATE_PRECISION),
|
||||
static_cast<int>(-180 * COORDINATE_PRECISION), MIN_OSM_NODEID);
|
||||
return QueryNode(util::FixedLongitude(-180 * COORDINATE_PRECISION),
|
||||
util::FixedLatitude(-90 * COORDINATE_PRECISION), MIN_OSM_NODEID);
|
||||
}
|
||||
|
||||
static QueryNode max_value()
|
||||
{
|
||||
return QueryNode(static_cast<int>(90 * COORDINATE_PRECISION),
|
||||
static_cast<int>(180 * COORDINATE_PRECISION), MAX_OSM_NODEID);
|
||||
return QueryNode(util::FixedLongitude(180 * COORDINATE_PRECISION),
|
||||
util::FixedLatitude(90 * COORDINATE_PRECISION), MAX_OSM_NODEID);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace osrm
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
class ProfileProperties;
|
||||
|
||||
/**
|
||||
* Parses the relations that represents turn restrictions.
|
||||
*
|
||||
@@ -40,11 +42,10 @@ namespace extractor
|
||||
class RestrictionParser
|
||||
{
|
||||
public:
|
||||
RestrictionParser(lua_State *lua_state);
|
||||
RestrictionParser(lua_State *lua_state, const ProfileProperties& properties);
|
||||
boost::optional<InputRestrictionContainer> TryParse(const osmium::Relation &relation) const;
|
||||
|
||||
private:
|
||||
void ReadUseRestrictionsSetting(lua_State *lua_state);
|
||||
void ReadRestrictionExceptions(lua_State *lua_state);
|
||||
bool ShouldIgnoreRestriction(const std::string &except_tag_string) const;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user