Compare commits
135 Commits
master
...
v5.0.0-new
Author | SHA1 | Date | |
---|---|---|---|
|
a558e5de4f | ||
|
d62f8bc994 | ||
|
f11dc9b978 | ||
|
93a05b7f70 | ||
|
9b75ff98c9 | ||
|
b28d1a00d6 | ||
|
8ddbbb0889 | ||
|
8345648c02 | ||
|
15fa82405b | ||
|
e53e1cbe0f | ||
|
a48e5e4804 | ||
|
9519ff2e30 | ||
|
7543de4480 | ||
|
93e8c42589 | ||
|
9e140d1e0f | ||
|
bf73834a72 | ||
|
b637fffc01 | ||
|
f31617dd9c | ||
|
28ed237f9a | ||
|
973785d1eb | ||
|
b04919db51 | ||
|
0ccce27fdf | ||
|
5e82ee60e7 | ||
|
05f1c85de3 | ||
|
aca943723f | ||
|
d82d7cf66e | ||
|
c608b77a43 | ||
|
07dba1c325 | ||
|
f06a636e7d | ||
|
3060f4414d | ||
|
bf01038e63 | ||
|
c6004d4401 | ||
|
d8759b7a2e | ||
|
304f361077 | ||
|
aeb435a2f7 | ||
|
fca25b45cd | ||
|
2402a1341a | ||
|
c9a0c0da1c | ||
|
9c5d0f1e91 | ||
|
f47f008e7d | ||
|
1fe9012c29 | ||
|
9db0b029aa | ||
|
c48e5f5ee2 | ||
|
5eb8990739 | ||
|
533d6c0a6b | ||
|
2a454c4272 | ||
|
908fd98824 | ||
|
4f0a84e8de | ||
|
6883fdca5c | ||
|
396e14d46e | ||
|
951ffe8484 | ||
|
6aa12b1dd6 | ||
|
74fe0beef6 | ||
|
383c9619ec | ||
|
b85270e540 | ||
|
b06dddaf5e | ||
|
3fa9672d9a | ||
|
6547978906 | ||
|
9087a01aac | ||
|
ad56ed8832 | ||
|
4b46dec169 | ||
|
6f5c3067f1 | ||
|
41600eeadc | ||
|
6dd23b2984 | ||
|
91b97ae7f7 | ||
|
28de928a5d | ||
|
209d1ada6a | ||
|
399a2233f3 | ||
|
d4009e11d5 | ||
|
8c9100cd9e | ||
|
3a7d527a7e | ||
|
c96a485108 | ||
|
c2de49ccab | ||
|
ae8dde6afe | ||
|
6bc3f5d6da | ||
|
dbab7b421c | ||
|
4dd6dfbfe2 | ||
|
f039da95ec | ||
|
5626182c60 | ||
|
721cc32acb | ||
|
0ec5f06a2f | ||
|
32982c7609 | ||
|
828767ba18 | ||
|
83deae637b | ||
|
88f9cf5aea | ||
|
f5e79f5c7e | ||
|
3f7056ee30 | ||
|
0aa98454f5 | ||
|
eb179af1ce | ||
|
03a230a768 | ||
|
0bd658c304 | ||
|
0c8113943b | ||
|
ef790e8e82 | ||
|
cd4dbfac42 | ||
|
6b72c007f9 | ||
|
007e4a69c8 | ||
|
fab343d0d3 | ||
|
8d4ee327cd | ||
|
ba2feca497 | ||
|
a9872a4892 | ||
|
0369634886 | ||
|
b10bd7c45e | ||
|
2134162fc6 | ||
|
00b3147c9b | ||
|
3b6d6bbbf2 | ||
|
52653c8ffa | ||
|
891ac48d1c | ||
|
26dd3c8cd7 | ||
|
820d0444fb | ||
|
b1a11aa567 | ||
|
0c970e4035 | ||
|
c21e0855e9 | ||
|
1b2bbd086e | ||
|
8eee4c23cc | ||
|
7e1c164937 | ||
|
6fecce23fc | ||
|
7abb7ed0e1 | ||
|
097771879b | ||
|
2d558a0b83 | ||
|
23a5edb29b | ||
|
942c71814d | ||
|
482aa63001 | ||
|
989bbfb250 | ||
|
decf976489 | ||
|
88a501f77c | ||
|
98beea7649 | ||
|
2bd8efd140 | ||
|
73e71765ab | ||
|
a57680323f | ||
|
75f356fcc6 | ||
|
d35c862a8b | ||
|
df236b72fb | ||
|
8b2b153465 | ||
|
26ffdf2dcb | ||
|
3377bf8faf |
1
.gitignore
vendored
1
.gitignore
vendored
@ -40,6 +40,7 @@ Thumbs.db
|
||||
# build related files #
|
||||
#######################
|
||||
/build/
|
||||
/example/build/
|
||||
/cmake/postinst
|
||||
|
||||
# Eclipse related files #
|
||||
|
@ -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})
|
||||
@ -88,6 +89,7 @@ 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>)
|
||||
@ -327,6 +329,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,9 +376,10 @@ 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)
|
||||
install(FILES ${EngineHeader} DESTINATION include/osrm/engine)
|
||||
@ -383,6 +387,7 @@ install(FILES ${UtilHeader} DESTINATION include/osrm/util)
|
||||
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,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
|
||||
|
@ -1,61 +1,84 @@
|
||||
#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{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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
64
include/engine/api/base_api.hpp
Normal file
64
include/engine/api/base_api.hpp
Normal file
@ -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 <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(parameters.coordinates.front(),
|
||||
segment_end_coordinates.front().source_phantom);
|
||||
|
||||
auto coord_iter = std::next(parameters.coordinates.begin());
|
||||
auto out_iter = std::next(waypoints.values.begin());
|
||||
for (const auto &phantoms : segment_end_coordinates)
|
||||
{
|
||||
*out_iter++ = MakeWaypoint(*coord_iter++, phantoms.target_phantom);
|
||||
}
|
||||
return waypoints;
|
||||
}
|
||||
|
||||
protected:
|
||||
util::json::Object MakeWaypoint(const util::Coordinate input_coordinate,
|
||||
const PhantomNode &phantom) const
|
||||
{
|
||||
return json::makeWaypoint(phantom.location, facade.get_name_for_id(phantom.name_id),
|
||||
Hint{input_coordinate, phantom, facade.GetCheckSum()});
|
||||
}
|
||||
|
||||
const datafacade::BaseDataFacade &facade;
|
||||
const BaseParameters ¶meters;
|
||||
};
|
||||
|
||||
} // ns api
|
||||
} // ns engine
|
||||
} // ns osrm
|
||||
|
||||
#endif
|
74
include/engine/api/base_parameters.hpp
Normal file
74
include/engine/api/base_parameters.hpp
Normal file
@ -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
|
81
include/engine/api/json_factory.hpp
Normal file
81
include/engine/api/json_factory.hpp
Normal file
@ -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_
|
117
include/engine/api/match_api.hpp
Normal file
117
include/engine/api/match_api.hpp
Normal file
@ -0,0 +1,117 @@
|
||||
#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";
|
||||
}
|
||||
|
||||
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(parameters.coordinates[trace_index], 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
|
70
include/engine/api/match_parameters.hpp
Normal file
70
include/engine/api/match_parameters.hpp
Normal file
@ -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
|
58
include/engine/api/nearest_api.hpp
Normal file
58
include/engine/api/nearest_api.hpp
Normal file
@ -0,0 +1,58 @@
|
||||
#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(parameters.coordinates.front(),
|
||||
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
|
50
include/engine/api/nearest_parameters.hpp
Normal file
50
include/engine/api/nearest_parameters.hpp
Normal file
@ -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
|
155
include/engine/api/route_api.hpp
Normal file
155
include/engine/api/route_api.hpp
Normal file
@ -0,0 +1,155 @@
|
||||
#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";
|
||||
}
|
||||
|
||||
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,
|
||||
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);
|
||||
|
||||
unpacked_path_segments = guidance::postProcess(std::move(unpacked_path_segments));
|
||||
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.source_phantom, phantoms.target_phantom,
|
||||
reversed_source, reversed_target);
|
||||
|
||||
if (parameters.steps)
|
||||
{
|
||||
leg.steps = guidance::assembleSteps(
|
||||
BaseAPI::facade, path_data, leg_geometry, phantoms.source_phantom,
|
||||
phantoms.target_phantom, reversed_source, reversed_target);
|
||||
}
|
||||
|
||||
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
|
86
include/engine/api/route_parameters.hpp
Normal file
86
include/engine/api/route_parameters.hpp
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
|
||||
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_,
|
||||
std::vector<boost::optional<bool>> uturns_,
|
||||
Args... args_)
|
||||
: BaseParameters{std::forward<Args>(args_)...}, steps{steps_}, alternatives{alternatives_},
|
||||
geometries{geometries_}, overview{overview_}, uturns{std::move(uturns_)}
|
||||
{
|
||||
}
|
||||
|
||||
bool steps = true;
|
||||
bool alternatives = true;
|
||||
GeometriesType geometries = GeometriesType::Polyline;
|
||||
OverviewType overview = OverviewType::Simplified;
|
||||
std::vector<boost::optional<bool>> uturns;
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return coordinates.size() >= 2 && BaseParameters::IsValid() &&
|
||||
(uturns.empty() || uturns.size() == coordinates.size());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
131
include/engine/api/table_api.hpp
Normal file
131
include/engine/api/table_api.hpp
Normal file
@ -0,0 +1,131 @@
|
||||
#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"
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
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());
|
||||
auto phantom_iter = phantoms.begin();
|
||||
auto coordinate_iter = parameters.coordinates.begin();
|
||||
for (; phantom_iter != phantoms.end() && coordinate_iter != parameters.coordinates.end();
|
||||
++phantom_iter, ++coordinate_iter)
|
||||
{
|
||||
json_waypoints.values.push_back(BaseAPI::MakeWaypoint(*coordinate_iter, *phantom_iter));
|
||||
}
|
||||
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());
|
||||
for (auto idx : indices)
|
||||
{
|
||||
BOOST_ASSERT(idx < phantoms.size() && idx < parameters.coordinates.size());
|
||||
json_waypoints.values.push_back(
|
||||
BaseAPI::MakeWaypoint(parameters.coordinates[idx], 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
|
98
include/engine/api/table_parameters.hpp
Normal file
98
include/engine/api/table_parameters.hpp
Normal file
@ -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
|
51
include/engine/api/tile_parameters.hpp
Normal file
51
include/engine/api/tile_parameters.hpp
Normal file
@ -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
|
110
include/engine/api/trip_api.hpp
Normal file
110
include/engine/api/trip_api.hpp
Normal file
@ -0,0 +1,110 @@
|
||||
#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";
|
||||
}
|
||||
|
||||
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(parameters.coordinates[input_index], 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
|
50
include/engine/api/trip_parameters.hpp
Normal file
50
include/engine/api/trip_parameters.hpp
Normal file
@ -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_
|
52
include/engine/bearing.hpp
Normal file
52
include/engine/bearing.hpp
Normal file
@ -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;
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
#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/query_node.hpp"
|
||||
@ -46,17 +48,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 +67,18 @@ 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;
|
||||
|
||||
boost::thread_specific_ptr<InternalRTree> m_static_rtree;
|
||||
boost::thread_specific_ptr<InternalGeospatialQuery> m_geospatial_query;
|
||||
@ -133,15 +136,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 +151,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 +161,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 +210,36 @@ 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)
|
||||
{
|
||||
std::ifstream datasources_stream(datasource_indexes_file.c_str(), std::ios::binary);
|
||||
if (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));
|
||||
}
|
||||
}
|
||||
|
||||
std::ifstream datasourcenames_stream(datasource_names_file.c_str(), std::ios::binary);
|
||||
if (datasourcenames_stream)
|
||||
{
|
||||
std::string name;
|
||||
while (std::getline(datasourcenames_stream, name))
|
||||
{
|
||||
m_datasource_names.push_back(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,7 +248,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)
|
||||
@ -268,6 +290,14 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
return it->second;
|
||||
};
|
||||
|
||||
const auto optional_file_for = [&server_paths, &end_it](const std::string &path)
|
||||
{
|
||||
const auto it = server_paths.find(path);
|
||||
if (it == end_it)
|
||||
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");
|
||||
|
||||
@ -283,6 +313,10 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
|
||||
util::SimpleLogger().Write() << "loading geometries";
|
||||
LoadGeometries(file_for("geometries"));
|
||||
|
||||
util::SimpleLogger().Write() << "loading datasource info";
|
||||
LoadDatasourceInfo(optional_file_for("datasource_names"),
|
||||
optional_file_for("datasource_indexes"));
|
||||
|
||||
util::SimpleLogger().Write() << "loading timestamp";
|
||||
LoadTimestamp(file_for("timestamp"));
|
||||
|
||||
@ -302,7 +336,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 +368,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 +384,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 +427,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 +469,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())
|
||||
{
|
||||
@ -468,19 +597,68 @@ 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
|
||||
{
|
||||
if (m_datasource_names.empty() || datasource_name_id > m_datasource_names.size())
|
||||
{
|
||||
if (datasource_name_id == 0)
|
||||
return "lua profile";
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return m_datasource_names[datasource_name_id];
|
||||
}
|
||||
|
||||
std::string GetTimestamp() const override final { return m_timestamp; }
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include "storage/shared_datatype.hpp"
|
||||
#include "storage/shared_memory.hpp"
|
||||
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
|
||||
#include "engine/geospatial_query.hpp"
|
||||
#include "util/range_table.hpp"
|
||||
#include "util/static_graph.hpp"
|
||||
@ -36,12 +38,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 +50,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;
|
||||
|
||||
@ -68,17 +69,21 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
std::unique_ptr<storage::SharedMemory> m_large_memory;
|
||||
std::string m_timestamp;
|
||||
|
||||
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;
|
||||
@ -135,9 +140,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 +152,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 +214,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 +223,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 +272,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;
|
||||
@ -315,8 +344,8 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
|
||||
LoadNames();
|
||||
LoadCoreInformation();
|
||||
|
||||
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 +370,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 +402,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 +415,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 +443,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 +454,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 +497,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 +539,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)
|
||||
{
|
||||
@ -520,6 +652,56 @@ 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
|
||||
{
|
||||
std::string result;
|
||||
|
||||
if (m_datasource_name_offsets.empty() ||
|
||||
datasource_name_id > m_datasource_name_offsets.size())
|
||||
{
|
||||
if (datasource_name_id == 0)
|
||||
return "lua profile";
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
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; }
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,23 @@ namespace engine
|
||||
|
||||
struct EngineConfig
|
||||
{
|
||||
EngineConfig() = default;
|
||||
|
||||
EngineConfig(const boost::filesystem::path &base)
|
||||
: server_paths{{"ramindex", base.string() + ".ramIndex"},
|
||||
{"fileindex", base.string() + ".fileIndex"},
|
||||
{"hsgrdata", base.string() + ".hsgr"},
|
||||
{"nodesdata", base.string() + ".nodes"},
|
||||
{"edgesdata", base.string() + ".edges"},
|
||||
{"coredata", base.string() + ".core"},
|
||||
{"geometries", base.string() + ".geometry"},
|
||||
{"timestamp", base.string() + ".timestamp"},
|
||||
{"datasource_names", base.string() + ".datasource_names"},
|
||||
{"datasource_indexes", base.string() + ".datasource_indexes"},
|
||||
{"namesdata", base.string() + ".names"}}
|
||||
{
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, boost::filesystem::path> server_paths;
|
||||
int max_locations_trip = -1;
|
||||
int max_locations_viaroute = -1;
|
||||
@ -48,7 +65,6 @@ struct EngineConfig
|
||||
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,19 +101,151 @@ 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::FixedPointCoordinate input_coordinate,
|
||||
const int bearing = 0,
|
||||
const int bearing_range = 180)
|
||||
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,
|
||||
[this, bearing, bearing_range, &has_big_component, &has_small_component](
|
||||
const EdgeData &data)
|
||||
[&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::Coordinate input_coordinate, 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));
|
||||
@ -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,32 +366,37 @@ 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},
|
||||
current_perpendicular_distance};
|
||||
auto transformed =
|
||||
PhantomNodeWithDistance{PhantomNode{data, forward_weight, forward_offset,
|
||||
reverse_weight, reverse_offset, point_on_segment},
|
||||
current_perpendicular_distance};
|
||||
|
||||
return transformed;
|
||||
}
|
||||
|
80
include/engine/guidance/assemble_geometry.hpp
Normal file
80
include/engine/guidance/assemble_geometry.hpp
Normal file
@ -0,0 +1,80 @@
|
||||
#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);
|
||||
|
||||
if (!isSilent(path_point.turn_instruction))
|
||||
{
|
||||
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
|
169
include/engine/guidance/assemble_leg.hpp
Normal file
169
include/engine/guidance/assemble_leg.hpp
Normal file
@ -0,0 +1,169 @@
|
||||
#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 &source_node,
|
||||
const PhantomNode &target_node,
|
||||
const bool source_traversed_in_reverse,
|
||||
const bool target_traversed_in_reverse)
|
||||
{
|
||||
const auto source_duration =
|
||||
(source_traversed_in_reverse ? source_node.GetReverseWeightPlusOffset()
|
||||
: source_node.GetForwardWeightPlusOffset()) /
|
||||
10.;
|
||||
const auto target_duration =
|
||||
(target_traversed_in_reverse ? target_node.GetReverseWeightPlusOffset()
|
||||
: target_node.GetForwardWeightPlusOffset()) /
|
||||
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)
|
||||
//
|
||||
// TODO discuss, this should not be the case after danpats fixes
|
||||
// The PathData will contain entries of b, c and d. But only c will contain
|
||||
// a duration value since its the only point associated with a turn.
|
||||
// As such we want to slice of the duration for (a,s) and add the duration for
|
||||
// (c,d,t)
|
||||
duration = duration - source_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.get_name_for_id(summary_array.front()),
|
||||
[&facade](std::string previous, const std::uint32_t name_id)
|
||||
{
|
||||
if (name_id != 0)
|
||||
{
|
||||
previous += ", " + facade.get_name_for_id(name_id);
|
||||
}
|
||||
return previous;
|
||||
});
|
||||
|
||||
return RouteLeg{duration, distance, summary, {}};
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_
|
24
include/engine/guidance/assemble_overview.hpp
Normal file
24
include/engine/guidance/assemble_overview.hpp
Normal file
@ -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
|
22
include/engine/guidance/assemble_route.hpp
Normal file
22
include/engine/guidance/assemble_route.hpp
Normal file
@ -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
|
152
include/engine/guidance/assemble_steps.hpp
Normal file
152
include/engine/guidance/assemble_steps.hpp
Normal file
@ -0,0 +1,152 @@
|
||||
#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 WaypointType waypoint_type,
|
||||
const LegGeometry &leg_geometry,
|
||||
const std::size_t segment_index,
|
||||
const unsigned exit);
|
||||
} // 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 EdgeWeight source_duration =
|
||||
source_traversed_in_reverse ? source_node.forward_weight : source_node.reverse_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.forward_weight : target_node.reverse_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;
|
||||
const auto initial_modifier =
|
||||
leg_geometry.locations.size() >= 3
|
||||
? angleToDirectionModifier(util::coordinate_calculation::computeAngle(
|
||||
leg_geometry.locations[0], leg_geometry.locations[1], leg_geometry.locations[2]))
|
||||
: 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, segment_index, INVALID_EXIT_NR);
|
||||
|
||||
// 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;
|
||||
|
||||
if (path_point.turn_instruction != extractor::guidance::TurnInstruction::NO_TURN())
|
||||
{
|
||||
BOOST_ASSERT(segment_duration >= 0);
|
||||
const auto name = facade.get_name_for_id(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,
|
||||
WaypointType::None, leg_geometry,
|
||||
segment_index, path_point.exit);
|
||||
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.get_name_for_id(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 = {source_node.location, 0., 0.,
|
||||
extractor::guidance::TurnInstruction{
|
||||
extractor::guidance::TurnType::NoTurn, initial_modifier},
|
||||
WaypointType::Depart, INVALID_EXIT_NR};
|
||||
int duration = target_duration - source_duration;
|
||||
BOOST_ASSERT(duration >= 0);
|
||||
|
||||
steps.push_back(RouteStep{source_node.name_id, facade.get_name_for_id(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 final_modifier =
|
||||
leg_geometry.locations.size() >= 3
|
||||
? angleToDirectionModifier(util::coordinate_calculation::computeAngle(
|
||||
leg_geometry.locations[leg_geometry.locations.size() - 3],
|
||||
leg_geometry.locations[leg_geometry.locations.size() - 2],
|
||||
leg_geometry.locations[leg_geometry.locations.size() - 1]))
|
||||
: extractor::guidance::DirectionModifier::UTurn;
|
||||
// This step has length zero, the only reason we need it is the target location
|
||||
steps.push_back(RouteStep{
|
||||
target_node.name_id, facade.get_name_for_id(target_node.name_id), 0., 0., target_mode,
|
||||
StepManeuver{target_node.location, 0., 0.,
|
||||
extractor::guidance::TurnInstruction{extractor::guidance::TurnType::NoTurn,
|
||||
final_modifier},
|
||||
WaypointType::Arrive, INVALID_EXIT_NR},
|
||||
leg_geometry.locations.size(), leg_geometry.locations.size()});
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_
|
54
include/engine/guidance/leg_geometry.hpp
Normal file
54
include/engine/guidance/leg_geometry.hpp
Normal file
@ -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
|
21
include/engine/guidance/post_processing.hpp
Normal file
21
include/engine/guidance/post_processing.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef ENGINE_GUIDANCE_POST_PROCESSING_HPP
|
||||
#define ENGINE_GUIDANCE_POST_PROCESSING_HPP
|
||||
|
||||
#include "engine/internal_route_result.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
std::vector<std::vector<PathData>> postProcess(std::vector<std::vector<PathData>> path_data);
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
||||
#endif // ENGINE_GUIDANCE_POST_PROCESSING_HPP
|
20
include/engine/guidance/route.hpp
Normal file
20
include/engine/guidance/route.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef ROUTE_HPP
|
||||
#define ROUTE_HPP
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
struct Route
|
||||
{
|
||||
double duration;
|
||||
double distance;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
29
include/engine/guidance/route_leg.hpp
Normal file
29
include/engine/guidance/route_leg.hpp
Normal file
@ -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
|
40
include/engine/guidance/route_step.hpp
Normal file
40
include/engine/guidance/route_step.hpp
Normal file
@ -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_
|
35
include/engine/guidance/step_maneuver.hpp
Normal file
35
include/engine/guidance/step_maneuver.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef ENGINE_GUIDANCE_STEP_MANEUVER_HPP
|
||||
#define ENGINE_GUIDANCE_STEP_MANEUVER_HPP
|
||||
|
||||
#include "util/coordinate.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
enum class WaypointType : std::uint8_t
|
||||
{
|
||||
None,
|
||||
Arrive,
|
||||
Depart,
|
||||
};
|
||||
|
||||
struct StepManeuver
|
||||
{
|
||||
util::Coordinate location;
|
||||
double bearing_before;
|
||||
double bearing_after;
|
||||
extractor::guidance::TurnInstruction instruction;
|
||||
WaypointType waypoint_type;
|
||||
unsigned exit;
|
||||
};
|
||||
} // 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
|
63
include/engine/guidance/toolkit.hpp
Normal file
63
include/engine/guidance/toolkit.hpp
Normal file
@ -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_ */
|
76
include/engine/hint.hpp
Normal file
76
include/engine/hint.hpp
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
|
||||
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>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace engine
|
||||
{
|
||||
|
||||
// Is returned as a temporary identifier for snapped coodinates
|
||||
struct Hint
|
||||
{
|
||||
util::Coordinate input_coordinate;
|
||||
PhantomNode phantom;
|
||||
std::uint32_t data_checksum;
|
||||
|
||||
template <typename DataFacadeT>
|
||||
bool IsValid(const util::Coordinate new_input_coordinates, DataFacadeT &facade) const
|
||||
{
|
||||
auto is_same_input_coordinate = new_input_coordinates.lon == input_coordinate.lon &&
|
||||
new_input_coordinates.lat == input_coordinate.lat;
|
||||
return is_same_input_coordinate && phantom.IsValid(facade.GetNumberOfNodes()) &&
|
||||
facade.GetCheckSum() == data_checksum;
|
||||
}
|
||||
|
||||
std::string ToBase64() const;
|
||||
static Hint FromBase64(const std::string &base64Hint);
|
||||
};
|
||||
|
||||
#ifndef _MSC_VER
|
||||
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
|
||||
constexpr std::size_t ENCODED_HINT_SIZE = 84;
|
||||
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,29 +15,22 @@ 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;
|
||||
// exit ID of highway exit, roundabout exit, intersection nr
|
||||
unsigned exit;
|
||||
};
|
||||
|
||||
struct InternalRouteResult
|
||||
|
@ -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];
|
||||
|
58
include/engine/map_matching/matching_confidence.hpp
Normal file
58
include/engine/map_matching/matching_confidence.hpp
Normal file
@ -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
|
25
include/engine/map_matching/sub_matching.hpp
Normal file
25
include/engine/map_matching/sub_matching.hpp
Normal file
@ -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,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,7 @@ struct PhantomNode
|
||||
unsigned reverse_packed_geometry_id_,
|
||||
bool is_tiny_component,
|
||||
unsigned component_id,
|
||||
util::FixedPointCoordinate location,
|
||||
util::Coordinate location,
|
||||
unsigned short fwd_segment_position,
|
||||
extractor::TravelMode forward_travel_mode,
|
||||
extractor::TravelMode backward_travel_mode)
|
||||
@ -37,9 +64,9 @@ 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),
|
||||
forward_travel_mode(forward_travel_mode), backward_travel_mode(backward_travel_mode)
|
||||
component{component_id, is_tiny_component}, location(std::move(location)),
|
||||
fwd_segment_position(fwd_segment_position), forward_travel_mode(forward_travel_mode),
|
||||
backward_travel_mode(backward_travel_mode)
|
||||
{
|
||||
}
|
||||
|
||||
@ -47,10 +74,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)
|
||||
{
|
||||
}
|
||||
@ -94,7 +120,12 @@ struct PhantomNode
|
||||
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 foot_point)
|
||||
{
|
||||
forward_node_id = other.forward_edge_based_node_id;
|
||||
reverse_node_id = other.reverse_edge_based_node_id;
|
||||
@ -137,7 +168,7 @@ struct PhantomNode
|
||||
#ifndef _MSC_VER
|
||||
static_assert(sizeof(ComponentType) == 4, "ComponentType needs to 4 bytes big");
|
||||
#endif
|
||||
util::FixedPointCoordinate location;
|
||||
util::Coordinate location;
|
||||
unsigned short fwd_segment_position;
|
||||
// note 4 bits would suffice for each,
|
||||
// but the saved byte would be padding anyway
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
35
include/engine/plugins/table.hpp
Normal file
35
include/engine/plugins/table.hpp
Normal file
@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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,16 @@
|
||||
#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 +26,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,98 @@ 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,
|
||||
INVALID_EXIT_NR});
|
||||
}
|
||||
else
|
||||
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);
|
||||
// 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 >=
|
||||
phantom_node_pair.source_phantom.forward_weight);
|
||||
unpacked_path.front().duration_until_turn -=
|
||||
start_traversed_in_reverse
|
||||
? phantom_node_pair.source_phantom.forward_weight
|
||||
: phantom_node_pair.source_phantom.reverse_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 +382,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
|
||||
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,
|
||||
INVALID_EXIT_NR});
|
||||
}
|
||||
|
||||
if (is_local_path && unpacked_path.size() > 0)
|
||||
{
|
||||
// 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 >=
|
||||
phantom_node_pair.source_phantom.forward_weight);
|
||||
unpacked_path.front().duration_until_turn -=
|
||||
phantom_node_pair.source_phantom.forward_weight;
|
||||
}
|
||||
|
||||
// there is no equivalent to a node-based node in an edge-expanded graph.
|
||||
@ -395,7 +426,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 +785,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 +809,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 +863,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
|
||||
{
|
||||
@ -236,11 +237,14 @@ class ShortestPathRouting final
|
||||
}
|
||||
}
|
||||
|
||||
static const constexpr bool UTURN_DEFAULT = false;
|
||||
|
||||
void operator()(const std::vector<PhantomNodes> &phantom_nodes_vector,
|
||||
const std::vector<bool> &uturn_indicators,
|
||||
const std::vector<boost::optional<bool>> &uturn_indicators,
|
||||
InternalRouteResult &raw_route_data) const
|
||||
{
|
||||
BOOST_ASSERT(uturn_indicators.size() == phantom_nodes_vector.size() + 1);
|
||||
BOOST_ASSERT(uturn_indicators.empty() ||
|
||||
uturn_indicators.size() == phantom_nodes_vector.size() + 1);
|
||||
engine_working_data.InitializeOrClearFirstThreadLocalStorage(
|
||||
super::facade->GetNumberOfNodes());
|
||||
engine_working_data.InitializeOrClearSecondThreadLocalStorage(
|
||||
@ -266,6 +270,8 @@ class ShortestPathRouting final
|
||||
std::vector<NodeID> total_packed_path_to_reverse;
|
||||
std::vector<std::size_t> packed_leg_to_reverse_begin;
|
||||
|
||||
const bool use_uturn_indicators = !uturn_indicators.empty();
|
||||
|
||||
std::size_t current_leg = 0;
|
||||
// this implements a dynamic program that finds the shortest route through
|
||||
// a list of vias
|
||||
@ -280,8 +286,11 @@ 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];
|
||||
const bool use_uturn_default =
|
||||
!use_uturn_indicators || !uturn_indicators[current_leg + 1];
|
||||
const bool allow_u_turn_at_via =
|
||||
(use_uturn_default && UTURN_DEFAULT) ||
|
||||
(!use_uturn_default && *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 */
|
44
include/engine/status.hpp
Normal file
44
include/engine/status.hpp
Normal file
@ -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;
|
||||
|
@ -5,15 +5,18 @@
|
||||
|
||||
#include "extractor/edge_based_edge.hpp"
|
||||
#include "extractor/speed_profile.hpp"
|
||||
#include "util/typedefs.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 <algorithm>
|
||||
#include <cstdint>
|
||||
@ -68,12 +71,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;
|
||||
|
||||
@ -106,68 +109,17 @@ class EdgeBasedGraphFactory
|
||||
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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
67
include/extractor/guidance/classification_data.hpp
Normal file
67
include/extractor/guidance/classification_data.hpp
Normal file
@ -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_
|
19
include/extractor/guidance/discrete_angle.hpp
Normal file
19
include/extractor/guidance/discrete_angle.hpp
Normal file
@ -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 */
|
322
include/extractor/guidance/toolkit.hpp
Normal file
322
include/extractor/guidance/toolkit.hpp
Normal file
@ -0,0 +1,322 @@
|
||||
#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 <map>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
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, TurnInstruction instruction)
|
||||
{
|
||||
const double center[] = {0, 45, 90, 135, 180, 225, 270, 315};
|
||||
return angularDeviation(center[static_cast<int>(instruction.direction_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, 35, 10, 35, 50, 45};
|
||||
const double difference = getAngularPenalty(angle, instruction);
|
||||
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 >= 170 && angle <= 190)
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_GUIDANCE_TOOLKIT_HPP_
|
207
include/extractor/guidance/turn_analysis.hpp
Normal file
207
include/extractor/guidance/turn_analysis.hpp
Normal file
@ -0,0 +1,207 @@
|
||||
#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 <cstdint>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
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.type)) + " " +
|
||||
std::to_string(static_cast<std::int32_t>(instruction.direction_modifier));
|
||||
result += " confidence: ";
|
||||
result += std::to_string(confidence);
|
||||
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);
|
||||
|
||||
// the entry into the turn analysis
|
||||
std::vector<TurnCandidate> 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;
|
||||
|
||||
// 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<TurnCandidate> getTurnCandidates(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<TurnCandidate>
|
||||
mergeSegregatedRoads(const NodeID from_node,
|
||||
const EdgeID via_eid,
|
||||
std::vector<TurnCandidate> turn_candidates) 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<TurnCandidate> handleRoundabouts(const NodeID from,
|
||||
const EdgeID via_edge,
|
||||
const bool on_roundabout,
|
||||
const bool can_enter_roundabout,
|
||||
const bool can_exit_roundabout,
|
||||
std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
// Indicates a Junction containing a motoryway
|
||||
bool isMotorwayJunction(const NodeID from,
|
||||
const EdgeID via_edge,
|
||||
const std::vector<TurnCandidate> &turn_candidates) const;
|
||||
|
||||
// Decide whether a turn is a turn or a ramp access
|
||||
TurnType findBasicTurnType(const EdgeID via_edge, const TurnCandidate &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 TurnCandidate &candidate) const;
|
||||
|
||||
// Helper Function that decides between NoTurn or NewName
|
||||
TurnInstruction
|
||||
noTurnOrNewName(const NodeID from, const EdgeID via_edge, const TurnCandidate &candidate) const;
|
||||
|
||||
// Basic Turn Handling
|
||||
|
||||
// Dead end.
|
||||
std::vector<TurnCandidate> handleOneWayTurn(const NodeID from,
|
||||
const EdgeID via_edge,
|
||||
std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
// Mode Changes, new names...
|
||||
std::vector<TurnCandidate> handleTwoWayTurn(const NodeID from,
|
||||
const EdgeID via_edge,
|
||||
std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
// Forks, T intersections and similar
|
||||
std::vector<TurnCandidate> handleThreeWayTurn(const NodeID from,
|
||||
const EdgeID via_edge,
|
||||
std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
// Normal Intersection. Can still contain forks...
|
||||
std::vector<TurnCandidate> handleFourWayTurn(const NodeID from,
|
||||
const EdgeID via_edge,
|
||||
std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
// Fallback for turns of high complexion
|
||||
std::vector<TurnCandidate> handleComplexTurn(const NodeID from,
|
||||
const EdgeID via_edge,
|
||||
std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
// Any Junction containing motorways
|
||||
std::vector<TurnCandidate> handleMotorwayJunction(
|
||||
const NodeID from, const EdgeID via_edge, std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
std::vector<TurnCandidate> handleFromMotorway(
|
||||
const NodeID from, const EdgeID via_edge, std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
std::vector<TurnCandidate> handleMotorwayRamp(
|
||||
const NodeID from, const EdgeID via_edge, std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
// Utility function, setting basic turn types. Prepares for normal turn handling.
|
||||
std::vector<TurnCandidate> setTurnTypes(const NodeID from,
|
||||
const EdgeID via_edge,
|
||||
std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
// Utility function to handle direction modifier conflicts if reasonably possible
|
||||
std::vector<TurnCandidate> handleConflicts(const NodeID from,
|
||||
const EdgeID via_edge,
|
||||
std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
// Old fallbacks, to be removed
|
||||
std::vector<TurnCandidate> optimizeRamps(const EdgeID via_edge,
|
||||
std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
std::vector<TurnCandidate> optimizeCandidates(const EdgeID via_eid,
|
||||
std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
bool isObviousChoice(const EdgeID via_eid,
|
||||
const std::size_t turn_index,
|
||||
const std::vector<TurnCandidate> &turn_candidates) const;
|
||||
|
||||
std::vector<TurnCandidate> suppressTurns(const EdgeID via_eid,
|
||||
std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
// node_u -- (edge_1) --> node_v -- (edge_2) --> node_w
|
||||
TurnInstruction AnalyzeTurn(const NodeID node_u,
|
||||
const EdgeID edge1,
|
||||
const NodeID node_v,
|
||||
const EdgeID edge2,
|
||||
const NodeID node_w,
|
||||
const double angle) const;
|
||||
|
||||
// Assignment of specific turn types
|
||||
void assignFork(const EdgeID via_edge, TurnCandidate &left, TurnCandidate &right) const;
|
||||
void assignFork(const EdgeID via_edge,
|
||||
TurnCandidate &left,
|
||||
TurnCandidate ¢er,
|
||||
TurnCandidate &right) const;
|
||||
|
||||
//Type specific fallbacks
|
||||
std::vector<TurnCandidate>
|
||||
fallbackTurnAssignmentMotorway(std::vector<TurnCandidate> turn_candidates) const;
|
||||
|
||||
}; // class TurnAnalysis
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_EXTRACTOR_TURN_ANALYSIS
|
123
include/extractor/guidance/turn_classification.hpp
Normal file
123
include/extractor/guidance/turn_classification.hpp
Normal file
@ -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_
|
119
include/extractor/guidance/turn_instruction.hpp
Normal file
119
include/extractor/guidance/turn_instruction.hpp
Normal file
@ -0,0 +1,119 @@
|
||||
#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
|
||||
Merge, // merge onto a street
|
||||
Ramp, // special turn (highway ramp exits)
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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 TRAVEL_MODE_HPP
|
||||
#define TRAVEL_MODE_HPP
|
||||
|
||||
@ -13,5 +40,18 @@ using TravelMode = unsigned char;
|
||||
}
|
||||
|
||||
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_INACCESSIBLE = 0;
|
||||
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_DEFAULT = 1;
|
||||
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_DRIVING = 1;
|
||||
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_CYCLING = 2;
|
||||
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_WALKING = 3;
|
||||
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_FERRY = 4;
|
||||
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_TRAIN = 5;
|
||||
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_PUSHING_BIKE = 6;
|
||||
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_MOVABLE_BRIDGE = 7;
|
||||
// FIXME only for testbot.lua
|
||||
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_STEPS_UP = 8;
|
||||
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_STEPS_DOWN = 9;
|
||||
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_RIVER_UP = 10;
|
||||
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_RIVER_DOWN = 11;
|
||||
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_ROUTE = 12;
|
||||
|
||||
#endif /* TRAVEL_MODE_HPP */
|
||||
|
@ -1,182 +0,0 @@
|
||||
#ifndef TURN_INSTRUCTIONS_HPP
|
||||
#define TURN_INSTRUCTIONS_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
|
||||
enum class TurnInstruction : unsigned char
|
||||
{
|
||||
NoTurn = 0,
|
||||
GoStraight,
|
||||
TurnSlightRight,
|
||||
TurnRight,
|
||||
TurnSharpRight,
|
||||
UTurn,
|
||||
TurnSharpLeft,
|
||||
TurnLeft,
|
||||
TurnSlightLeft,
|
||||
ReachViaLocation,
|
||||
HeadOn,
|
||||
EnterRoundAbout,
|
||||
LeaveRoundAbout,
|
||||
StayOnRoundAbout,
|
||||
StartAtEndOfStreet,
|
||||
ReachedYourDestination,
|
||||
NameChanges,
|
||||
EnterAgainstAllowedDirection,
|
||||
LeaveAgainstAllowedDirection,
|
||||
InverseAccessRestrictionFlag = 127,
|
||||
AccessRestrictionFlag = 128,
|
||||
AccessRestrictionPenalty = 129
|
||||
};
|
||||
|
||||
// shiftable turns to left and right
|
||||
const constexpr bool shiftable_left[] = {false, false, true, true, true, false, false, true, true};
|
||||
const constexpr bool shiftable_right[] = {false, false, true, true, false, false, true, true, true};
|
||||
|
||||
inline TurnInstruction shiftTurnToLeft(TurnInstruction turn)
|
||||
{
|
||||
BOOST_ASSERT_MSG(static_cast<int>(turn) < 9,
|
||||
"Shift turn only supports basic turn instructions");
|
||||
if (turn > TurnInstruction::TurnSlightLeft)
|
||||
return turn;
|
||||
else
|
||||
return shiftable_left[static_cast<int>(turn)]
|
||||
? (static_cast<TurnInstruction>(static_cast<int>(turn) - 1))
|
||||
: turn;
|
||||
}
|
||||
|
||||
inline TurnInstruction shiftTurnToRight(TurnInstruction turn)
|
||||
{
|
||||
BOOST_ASSERT_MSG(static_cast<int>(turn) < 9,
|
||||
"Shift turn only supports basic turn instructions");
|
||||
if (turn > TurnInstruction::TurnSlightLeft)
|
||||
return turn;
|
||||
else
|
||||
return shiftable_right[static_cast<int>(turn)]
|
||||
? (static_cast<TurnInstruction>(static_cast<int>(turn) + 1))
|
||||
: turn;
|
||||
}
|
||||
|
||||
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, TurnInstruction instruction)
|
||||
{
|
||||
BOOST_ASSERT_MSG(static_cast<int>(instruction) < 9,
|
||||
"Angular penalty only supports basic turn instructions");
|
||||
const double center[] = {180, 180, 135, 90, 45,
|
||||
0, 315, 270, 225}; // centers of turns from getTurnDirection
|
||||
return angularDeviation(center[static_cast<int>(instruction)], angle);
|
||||
}
|
||||
|
||||
inline double getTurnConfidence(const double angle, TurnInstruction instruction)
|
||||
{
|
||||
|
||||
// special handling of U-Turns and Roundabout
|
||||
if (instruction >= TurnInstruction::HeadOn || instruction == TurnInstruction::UTurn ||
|
||||
instruction == TurnInstruction::NoTurn || instruction == TurnInstruction::EnterRoundAbout ||
|
||||
instruction == TurnInstruction::StayOnRoundAbout || instruction == TurnInstruction::LeaveRoundAbout )
|
||||
return 1.0;
|
||||
|
||||
BOOST_ASSERT_MSG(static_cast<int>(instruction) < 9,
|
||||
"Turn confidence only supports basic turn instructions");
|
||||
const double deviations[] = {10, 10, 35, 50, 45, 0, 45, 50, 35};
|
||||
const double difference = getAngularPenalty(angle, instruction);
|
||||
const double max_deviation = deviations[static_cast<int>(instruction)];
|
||||
return 1.0 - (difference / max_deviation) * (difference / max_deviation);
|
||||
}
|
||||
|
||||
// Translates between angles and their human-friendly directional representation
|
||||
inline TurnInstruction 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 TurnInstruction::TurnSharpRight;
|
||||
if (angle >= 60 && angle < 140)
|
||||
return TurnInstruction::TurnRight;
|
||||
if (angle >= 140 && angle < 170)
|
||||
return TurnInstruction::TurnSlightRight;
|
||||
if (angle >= 170 && angle <= 190)
|
||||
return TurnInstruction::GoStraight;
|
||||
if (angle > 190 && angle <= 220)
|
||||
return TurnInstruction::TurnSlightLeft;
|
||||
if (angle > 220 && angle <= 300)
|
||||
return TurnInstruction::TurnLeft;
|
||||
if (angle > 300 && angle < 360)
|
||||
return TurnInstruction::TurnSharpLeft;
|
||||
return TurnInstruction::UTurn;
|
||||
}
|
||||
|
||||
// Decides if a turn is needed to be done for the current instruction
|
||||
inline bool isTurnNecessary(const TurnInstruction turn_instruction)
|
||||
{
|
||||
if (TurnInstruction::NoTurn == turn_instruction ||
|
||||
TurnInstruction::StayOnRoundAbout == turn_instruction)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool resolve(TurnInstruction &to_resolve, const TurnInstruction neighbor, bool resolve_right)
|
||||
{
|
||||
const auto shifted_turn =
|
||||
resolve_right ? shiftTurnToRight(to_resolve) : shiftTurnToLeft(to_resolve);
|
||||
if (shifted_turn == neighbor || shifted_turn == to_resolve)
|
||||
return false;
|
||||
|
||||
to_resolve = shifted_turn;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool resolveTransitive(TurnInstruction &first,
|
||||
TurnInstruction &second,
|
||||
const TurnInstruction third,
|
||||
bool resolve_right)
|
||||
{
|
||||
if (resolve(second, third, resolve_right))
|
||||
{
|
||||
first = resolve_right ? shiftTurnToRight(first) : shiftTurnToLeft(first);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool isSlightTurn(const TurnInstruction turn)
|
||||
{
|
||||
return turn == TurnInstruction::GoStraight || turn == TurnInstruction::TurnSlightRight ||
|
||||
turn == TurnInstruction::TurnSlightLeft || turn == TurnInstruction::NoTurn;
|
||||
}
|
||||
|
||||
inline bool isSharpTurn(const TurnInstruction turn)
|
||||
{
|
||||
return turn == TurnInstruction::TurnSharpLeft || turn == TurnInstruction::TurnSharpRight;
|
||||
}
|
||||
|
||||
inline bool isStraight(const TurnInstruction turn)
|
||||
{
|
||||
return turn == TurnInstruction::GoStraight || turn == TurnInstruction::NoTurn;
|
||||
}
|
||||
|
||||
inline bool isConflict(const TurnInstruction first, const TurnInstruction second)
|
||||
{
|
||||
return first == second || (isStraight(first) && isStraight(second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* TURN_INSTRUCTIONS_HPP */
|
38
include/osrm/bearing.hpp
Normal file
38
include/osrm/bearing.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
|
||||
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_BEARING_HPP
|
||||
#define OSRM_BEARING_HPP
|
||||
|
||||
#include "engine/bearing.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
using engine::Bearing;
|
||||
}
|
||||
|
||||
#endif
|
@ -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 GLOBAL_COORDINATE_HPP
|
||||
#define GLOBAL_COORDINATE_HPP
|
||||
|
||||
@ -5,7 +32,7 @@
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
using util::FixedPointCoordinate;
|
||||
using util::Coordinate;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -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 GLOBAL_ENGINE_CONFIG_HPP
|
||||
#define GLOBAL_ENGINE_CONFIG_HPP
|
||||
|
||||
@ -5,8 +32,7 @@
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
using engine::EngineConfig;
|
||||
using engine::EngineConfig;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,8 +1,35 @@
|
||||
/*
|
||||
|
||||
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 GLOBAL_JSON_CONTAINER_HPP
|
||||
#define GLOBAL_JSON_CONTAINER_HPP
|
||||
#include "util/json_container.hpp"
|
||||
namespace osrm
|
||||
{
|
||||
namespace json = osrm::util::json;
|
||||
namespace json = osrm::util::json;
|
||||
}
|
||||
#endif
|
||||
|
38
include/osrm/match_parameters.hpp
Normal file
38
include/osrm/match_parameters.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
|
||||
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 GLOBAL_MATCH_PARAMETERS_HPP
|
||||
#define GLOBAL_MATCH_PARAMETERS_HPP
|
||||
|
||||
#include "engine/api/match_parameters.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
using engine::api::MatchParameters;
|
||||
}
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user