Compare commits

...

179 Commits

Author SHA1 Message Date
Daniel J. Hofmann
e1e76d04d1 Adds round trip tests for RFC 4648 Test Vectors and equality checks 2016-03-23 22:47:22 +01:00
Daniel J. Hofmann
451bbe3021 Makes hint Equatable and Printable for tests 2016-03-23 22:47:22 +01:00
Daniel J. Hofmann
88a76aaecf Make Hint encoding safe for passing them as GET parameter in URLs
Thanks @TheMarex for flagging this!
2016-03-23 22:42:28 +01:00
Daniel J. Hofmann
87428f8fc9 Completely re-write base64 logic, make API suck less in doing so 2016-03-23 22:42:12 +01:00
Daniel J. Hofmann
6c9ddde682 RFC 4648 Test Vectors 2016-03-23 22:41:10 +01:00
Daniel J. Hofmann
b4b148e3dc Object Encoder -> Base64, kill false dependencies while doing so 2016-03-23 22:40:59 +01:00
Daniel J. Hofmann
4a84ca3197 Adapts Hint encoding and decoding to new fixed data facade 2016-03-23 22:40:45 +01:00
Daniel J. Hofmann
df182ebc38 Formats parameter parser unit tests 2016-03-23 22:40:32 +01:00
Patrick Niklaus
c86864976c Fix hint size 2016-03-23 22:36:20 +01:00
Patrick Niklaus
c3dd77d32b Always safe the absolute path to .fileIndex 2016-03-23 22:18:11 +01:00
Patrick Niklaus
2357d1c095 Merge pull request #2137 from noblige/rewrite/new-api
fixed compilation error on debian jessie with boost 1.54
2016-03-23 14:35:36 -04:00
Patrick Niklaus
7cd70679f1 Install storage_config.hpp 2016-03-23 19:19:00 +01:00
Aleksei Potov
08c1ac0daf compilation error on debian jessie with boost 1.54 2016-03-23 17:55:56 +00:00
Daniel Patterson
48237b30ea Fix boost geometry constructor.
Versions older than 1.58 don't support the initializer-list form.
2016-03-23 10:47:06 -07:00
Moritz Kobitzsch
cac1298864 syncronize geometry and steps after post-processing 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
83e6679d61 added list of intersections to the step-maneuver, not in api so far 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
f3ea86b611 fix initial maneuvers 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
f3de53c363 post processing moved onto route-steps, looses sync with geometry segments 2016-03-23 17:52:51 +01:00
Patrick Niklaus
19434d42b4 Simplify the timestamp handling 2016-03-23 17:52:51 +01:00
Patrick Niklaus
ea24ea64f2 Check all streams 2016-03-23 17:52:51 +01:00
Patrick Niklaus
39f5cf1c22 std::string -> boost::filesystem::path 2016-03-23 17:52:51 +01:00
Patrick Niklaus
91384ecd8c Only allow to specify the common base path 2016-03-23 17:52:51 +01:00
Patrick Niklaus
5c28eabbe0 print -> io.write 2016-03-23 17:52:51 +01:00
Patrick Niklaus
42dd45b29b Address PR comments
Renamed lua_function_exists and removes unused print function
2016-03-23 17:52:51 +01:00
Patrick Niklaus
6f4095f23f Use uturn default from .properties file 2016-03-23 17:52:51 +01:00
Patrick Niklaus
9c6c02d85b Adds .properties file to osrm-extract ouput
This file contains global properties set by the lua
profile, such as enabling uturns at vias and penalties.
This file will be consumed by the server.
2016-03-23 17:52:51 +01:00
Patrick Niklaus
47afe60e5f Use global uturns parameter.
Instead of previously per-via settings like uturns=true;false;true;; it
now only supports a global setting uturns=true.
2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
0471ace55d improves consistency of fork handling 2016-03-23 17:52:51 +01:00
Patrick Niklaus
49e2a4ec07 Make gcc 4.8 happy and disable protected because of lambdas 2016-03-23 17:52:51 +01:00
Daniel Patterson
991c5f5d5d Properly clip lines so that we don't get crazy coords with long linestrings (relative to tile coords) 2016-03-23 17:52:51 +01:00
Daniel Patterson
87c77e7d77 Include edge duration information. 2016-03-23 17:52:51 +01:00
Patrick Niklaus
2cf120fc49 Implement viewport code to fix simplification
This fixes #2083
2016-03-23 17:52:51 +01:00
Patrick Niklaus
d4eaea1e7d Add tests for coordinate transformation 2016-03-23 17:52:51 +01:00
Patrick Niklaus
51a13675d4 Consolidate math functions 2016-03-23 17:52:51 +01:00
Patrick Niklaus
af4cb96aa5 get_name_for_id -> GetNameForID 2016-03-23 17:52:51 +01:00
Patrick Niklaus
439a7ba661 Simplfy name change announcement 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
a0ff0b1258 fix division by zero 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
1cc9a2f4a1 restructured to only return valid turns to the outside + cleanup 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
a3cf7f09e8 less new names, forks consider road classes, api clean-up 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
831a536224 implement basic turn handling 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
7df00683e1 implements relative position feature based on coordinates 2016-03-23 17:52:51 +01:00
Patrick Niklaus
0c0803c77b Don't sum up durations of merged steps since we do that in a different place now 2016-03-23 17:52:51 +01:00
Patrick Niklaus
7c5d56afcb Fix foward/backwad swap 2016-03-23 17:52:51 +01:00
Patrick Niklaus
892b9dff4a Limit zoomlevel to 18 2016-03-23 17:52:51 +01:00
Patrick Niklaus
04a8c1833c Fix shared memory 2016-03-23 17:52:51 +01:00
Patrick Niklaus
c217ff815c Fixup for last commit 2016-03-23 17:52:51 +01:00
Daniel Patterson
893c9f7326 Enables the use of multiple segment-speed-files on the osrm-contract
command line, and exposes the file name used for each edge in the debug
tiles.
2016-03-23 17:52:51 +01:00
Patrick Niklaus
84fa7ae353 Fix ingestion fixed duration values from UnpackPath 2016-03-23 17:52:51 +01:00
Patrick Niklaus
bb3256573b Fix durations in UnpackPath 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
96d9ef3951 fixes a broken assertion 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
11572702e9 fix merging of turn instructions 2016-03-23 17:52:51 +01:00
Patrick Niklaus
6d16bb5b25 Include reverse edges again 2016-03-23 17:52:51 +01:00
Patrick Niklaus
89eb2a3778 Formating and logging changes for turn classification 2016-03-23 17:52:51 +01:00
Patrick Niklaus
87408cfd73 Handle case of dead-end edges by inserting an invalid turn 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
dd4066c46e report depart/arrive in addition to waypoint 2016-03-23 17:52:51 +01:00
Patrick Niklaus
3f559211a4 Only install necessary headers 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
5892c6ea09 Adds the license preamble for all publicly installed eaders, closes #2036 2016-03-23 17:52:51 +01:00
Patrick Niklaus
dea0063a56 Remove the encoder/decoder dependecy from Hint 2016-03-23 17:52:51 +01:00
Patrick Niklaus
eec270968e Rename alternative -> alternatives 2016-03-23 17:52:51 +01:00
Patrick Niklaus
056a7422e0 Remove obsolete debug information 2016-03-23 17:52:51 +01:00
Patrick Niklaus
3d3fea768c Just return NoSegment in map matching if all candidates are empty 2016-03-23 17:52:51 +01:00
Patrick Niklaus
1a6c16fea1 Add failing test for map matching of outlier 2016-03-23 17:52:51 +01:00
Patrick Niklaus
3c56385ba8 Fix missing capitalization of error codes 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
1947671ae3 encapsulated into class 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
3189f24a17 improving fork handling on three-way turns 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
dd5c730a01 fix comparison for ramps on three-way turns 2016-03-23 17:52:51 +01:00
Patrick Niklaus
da1dbcfb34 Minor auto iterator cleanup 2016-03-23 17:52:51 +01:00
Patrick Niklaus
8f5091f711 Don't pass down unsnapped coordinates. All information is already there 2016-03-23 17:52:51 +01:00
Patrick Niklaus
0da7302d62 Fix camMergeTrivially 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
f0b40c1948 fixes roundabout counting 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
ecefb4a20e fixes assignment for basic turn types / invalid ramp assignment 2016-03-23 17:52:51 +01:00
Patrick Niklaus
9b1861e017 Add edge id assertions 2016-03-23 17:52:51 +01:00
Patrick Niklaus
4fb041391f Don't pass shared_ptr down to functions
"Don’t pass a smart pointer as a function parameter unless you want to
use or manipulate the smart pointer itself, such as to share or transfer
ownership."

Source:
http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/
2016-03-23 17:52:51 +01:00
Patrick Niklaus
64cc7d8aac Move bearing to public namespace 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
556223d43b Properly includes needed headers in turn analysis interface 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
0b1e82105e Const-correctnes for compressed geometry iterator 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
c9ad8736b0 Use stdint and using type-alias for discrete angle 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
cded2501a5 Hide functional road classification based on tags in implementation file 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
4d94ec2792 Inline initialize functional road classification hash table 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
7970bc4ad6 256 functional road classes should be enough 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
f84ce976ea Moves route assembly into implementation file 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
ce9a4666f3 Fixes remaining engine/guidance includes 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
ff55b4e001 Fixes accumulate living in <numeric> and not <algorithm> 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
8040af329b Puts step maneuver handling into implementation file 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
b8c059b5c2 Removes penalizing move 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
4e2e2059b1 Uses static_casts for underlying type in post processing 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
922599a8ca Fix asymmetry in min/max from using -max 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
5bc0595591 Adapts MakeResponse to not pass vector by pointer 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
0f600c9262 Fixes multi-line comment 2016-03-23 17:52:51 +01:00
Daniel J. Hofmann
9338d418c8 Runs scripts/format.sh 2016-03-23 17:52:51 +01:00
Patrick Niklaus
4d56b8ff0d Fix coodinate include and unused warnings 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
92b4f14db6 start of four way turns 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
a1f1da4c16 improved fork handling 2016-03-23 17:52:51 +01:00
Patrick Niklaus
c337d55d45 Fix crash on extracting Berlin in guidance 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
be5f231641 bugfixing/classification 2016-03-23 17:52:51 +01:00
Patrick Niklaus
c19fae4a43 Big Restructuring / Cleanup 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
0831e71438 starting on conflict resolution 2016-03-23 17:52:51 +01:00
Lauren Budorick
c44a370dd3 Fixes for gcc compiling, temporary hacks to remove later 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
9f5e79501e handle segregated roads (merge for turn analysis) 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
cf5e722578 structural changes, motorway handling 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
f12bb2fa2f enter and exit roundabout feature - currently not showing turn 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
28d878ed23 migrated out of edge based graph factory 2016-03-23 17:52:51 +01:00
Moritz Kobitzsch
6db16e950a relative waypoint locations 2016-03-23 17:52:14 +01:00
Moritz Kobitzsch
862f8d6ff3 handling of roundabouts (simple version) 2016-03-23 17:52:14 +01:00
Moritz Kobitzsch
8c67dd4504 advanced guidance on 5.0 2016-03-23 17:52:14 +01:00
Patrick Niklaus
23d79bde54 Fix numerical problems with polyline 2016-03-23 17:50:13 +01:00
Patrick Niklaus
410d561b4c Fix table response format to return null + double in seconds 2016-03-23 17:50:13 +01:00
Patrick Niklaus
8ab38b126c Return NoMatch 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
c13f1aa89d Provides ctor from base path for EngineConfig, fixes #2030 2016-03-23 17:50:13 +01:00
Patrick Niklaus
b4bf0ec5fe Add support for tile plugin 2016-03-23 17:50:13 +01:00
Patrick Niklaus
f7b7dbf51a Preliminary integration of the tile plugin 2016-03-23 17:50:13 +01:00
Patrick Niklaus
41c9f94e5f sources and destinations can be empty actually 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
77adbca138 Fixes coordinate, source and destination validation by means of backporting #2041 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
f3e06b41dd Fixes ownership semantics and forwarding references misplacements in the JSON factory 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
ee4b8618f8 Unwrap function call from identity lambda 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
58973a4fd1 Uses JSON's String constructor for polyline encoding 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
4b8017d412 Passes coordinates by value 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
64d4d58a0d Asserts on unknown TurnInstruction 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
5d83fa1e4c Fixes header includes in the JSON factory 2016-03-23 17:50:13 +01:00
Dane Springmeyer
7a6aa6f1a7 fix compile of osrm-components 2016-03-23 17:50:13 +01:00
Patrick Niklaus
e5d964492a Fix if the last coordinate is not found 2016-03-23 17:50:13 +01:00
Patrick Niklaus
bb2baa82aa Allocate correct table size 2016-03-23 17:50:13 +01:00
Patrick Niklaus
0b5875a412 Fix travel mode passing from profiles up to the API 2016-03-23 17:50:13 +01:00
Patrick Niklaus
9a2b8cb16d Fix geometries type in steps 2016-03-23 17:50:13 +01:00
Patrick Niklaus
5fb6db7a9a Fix table parameter parsing 2016-03-23 17:50:13 +01:00
Patrick Niklaus
c1647c99c8 Fix behaviour of table if sources/destinations arrays are empty 2016-03-23 17:50:13 +01:00
Patrick Niklaus
39d6c33b32 Fuck. this. shit. 2016-03-23 17:50:13 +01:00
Patrick Niklaus
9b75f618e8 Change stream operator of strong typedef 2016-03-23 17:50:13 +01:00
Patrick Niklaus
225049ffa7 Fix stream operator for coordinate 2016-03-23 17:50:13 +01:00
Patrick Niklaus
2df832a95e Add stream operator to Rectangle 2016-03-23 17:50:13 +01:00
Patrick Niklaus
75912b3662 Add euclideanDistance to coordinate_calculation 2016-03-23 17:50:13 +01:00
Patrick Niklaus
c9a19c954e Simplify static_rtree tests 2016-03-23 17:50:13 +01:00
Patrick Niklaus
9a19086926 First round of lat,lng -> lng,lat switcheroo 2016-03-23 17:50:13 +01:00
Patrick Niklaus
90e8a3e1dc Add rectangle unit test 2016-03-23 17:50:13 +01:00
Patrick Niklaus
0270ee59b4 Fix match and trip API response 2016-03-23 17:50:13 +01:00
Patrick Niklaus
5728af4a2a Fix out-of-bounds write in map_matching 2016-03-23 17:50:13 +01:00
Dane Springmeyer
6cf19ac4cf Fix compile on OS X 2016-03-23 17:50:13 +01:00
Patrick Niklaus
3f81f6b441 Finish the nearest plugin 2016-03-23 17:50:13 +01:00
Patrick Niklaus
669aa9e672 Initialize NearestParameters correctly 2016-03-23 17:50:13 +01:00
Patrick Niklaus
1d3afd5c38 Adapt to feedback in #519 2016-03-23 17:50:13 +01:00
Patrick Niklaus
c53a448589 Add trip plugin 2016-03-23 17:50:13 +01:00
Patrick Niklaus
cb82376083 Hook up map matching 2016-03-23 17:50:13 +01:00
Patrick Niklaus
9d10490613 First compiling version of map_match plugin 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
1cc7d3ddfb Adapts example/example.cpp to new osrm api 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
aad3d8aa1e Install _all_ transitively from public headers included header 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
9258791811 Fix missing headers in hint.hpp 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
19d5912a93 Adds $prefix/include/osrm to include dirs so that transitive header includes without osrm prefix can be found 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
a4770feac4 Adapt the example to include all osrm public headers 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
c8c6f8c2e7 Fixes missing public header installations 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
d7f9c31d0b Fix forward declarations in publicly facing osrm header 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
44b802c053 Enable all plugins with aStatus::Error return code fallback for not implemented ones 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
99f18051f6 Adds publicly facing alias headers for parameters 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
99f94969aa Temporarily comment out match.cpp as to not break the build process 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
bc6d5bbde2 We don't need templates at all, this is not CRTP? 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
6aa5ab5904 Fix classes for service member function definitions 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
0c64503218 Service skeletons for nearest, trip, match 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
e15ba88c0b Fix grammar constraint and enable all plugin links 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
2f9d1d3db2 Plugin grammar skeletons 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
42e6e974ac Enforce parameter and grammar type to catch subtle bugs 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
5a97e7a7ce Link parameters to grammars 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
8c54794d5a Require a BaseParameters type at compile time via enable_if 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
e377a19c10 Adapts Nearest plugin to new API 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
0fb6e9bf3f Fix deleting incomplete type and make Engine moveable only 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
d6a8690425 Adapts publicly facing new API 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
cc65446785 Adapts NearestParameters to new API 2016-03-23 17:50:13 +01:00
Patrick Niklaus
487df70eb3 Initial non-building match plugin 2016-03-23 17:50:13 +01:00
Lauren Budorick
aa79c41804 Include numeric in assemble_overview.cpp (needed on OSX for std::accumulate) 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
7faadb1233 Semantic action handler requires passing optional by value and fusion::vector2 2016-03-23 17:50:13 +01:00
Patrick Niklaus
5c8a895471 Add tests for bearing parsing 2016-03-23 17:50:13 +01:00
Patrick Niklaus
e932a8253a Add table service 2016-03-23 17:50:13 +01:00
Patrick Niklaus
84097964b7 Add table API 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
c2a35b35ad Optional<T> semantic action handler takes T argument 2016-03-23 17:50:13 +01:00
Patrick Niklaus
f6612e2afa Fix parameter parsing tests 2016-03-23 17:50:13 +01:00
Patrick Niklaus
09378f28fd Fix table plugin 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
01ddfbcba3 First take at distance table API re-write 2016-03-23 17:50:13 +01:00
Daniel J. Hofmann
0468d7a7c5 Adapts TableParameters and its validation to new API 2016-03-23 17:50:13 +01:00
Patrick Niklaus
39bc0fd330 Add viaroute suport for new API 2016-03-23 17:50:13 +01:00
Patrick Niklaus
53dbb1eac2 Also exclude the compressed flag from the data format 2016-03-23 17:50:13 +01:00
Patrick Niklaus
f5bc843fe6 Remove geometry indicator 2016-03-23 17:50:13 +01:00
237 changed files with 14727 additions and 6745 deletions

1
.gitignore vendored
View File

@ -40,6 +40,7 @@ Thumbs.db
# build related files #
#######################
/build/
/example/build/
/cmake/postinst
# Eclipse related files #

View File

@ -45,7 +45,7 @@ add_custom_target(FingerPrintConfigure ALL ${CMAKE_COMMAND}
COMMENT "Configuring revision fingerprint"
VERBATIM)
add_custom_target(tests DEPENDS engine-tests extractor-tests util-tests)
add_custom_target(tests DEPENDS engine-tests extractor-tests util-tests server-tests)
add_custom_target(benchmarks DEPENDS rtree-bench)
set(BOOST_COMPONENTS date_time filesystem iostreams program_options regex system thread unit_test_framework)
@ -55,7 +55,7 @@ configure_file(
${CMAKE_CURRENT_BINARY_DIR}/include/util/version.hpp
)
file(GLOB UtilGlob src/util/*.cpp)
file(GLOB ExtractorGlob src/extractor/*.cpp)
file(GLOB ExtractorGlob src/extractor/*.cpp src/extractor/*/*.cpp)
file(GLOB ContractorGlob src/contractor/*.cpp)
file(GLOB StorageGlob src/storage/*.cpp)
file(GLOB ServerGlob src/server/*.cpp src/server/**/*.cpp)
@ -63,6 +63,7 @@ file(GLOB EngineGlob src/engine/*.cpp src/engine/**/*.cpp)
file(GLOB ExtractorTestsGlob unit_tests/extractor/*.cpp)
file(GLOB EngineTestsGlob unit_tests/engine/*.cpp)
file(GLOB UtilTestsGlob unit_tests/util/*.cpp)
file(GLOB ServerTestsGlob unit_tests/server/*.cpp)
file(GLOB IOTestsGlob unit_tests/io/*.cpp)
add_library(UTIL OBJECT ${UtilGlob})
@ -79,7 +80,7 @@ add_executable(osrm-extract src/tools/extract.cpp)
add_executable(osrm-contract src/tools/contract.cpp)
add_executable(osrm-routed src/tools/routed.cpp $<TARGET_OBJECTS:SERVER> $<TARGET_OBJECTS:UTIL>)
add_executable(osrm-datastore src/tools/store.cpp $<TARGET_OBJECTS:UTIL>)
add_library(osrm src/osrm/osrm.cpp $<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:UTIL>)
add_library(osrm src/osrm/osrm.cpp $<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:UTIL> $<TARGET_OBJECTS:STORAGE>)
add_library(osrm_extract $<TARGET_OBJECTS:EXTRACTOR> $<TARGET_OBJECTS:UTIL>)
add_library(osrm_contract $<TARGET_OBJECTS:CONTRACTOR> $<TARGET_OBJECTS:UTIL>)
add_library(osrm_store $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:UTIL>)
@ -88,10 +89,12 @@ add_library(osrm_store $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:UTIL>)
add_executable(engine-tests EXCLUDE_FROM_ALL unit_tests/engine_tests.cpp ${EngineTestsGlob} $<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:UTIL>)
add_executable(extractor-tests EXCLUDE_FROM_ALL unit_tests/extractor_tests.cpp ${ExtractorTestsGlob} $<TARGET_OBJECTS:EXTRACTOR> $<TARGET_OBJECTS:UTIL>)
add_executable(util-tests EXCLUDE_FROM_ALL unit_tests/util_tests.cpp ${UtilTestsGlob} $<TARGET_OBJECTS:UTIL>)
add_executable(server-tests EXCLUDE_FROM_ALL unit_tests/server_tests.cpp ${ServerTestsGlob} $<TARGET_OBJECTS:UTIL> $<TARGET_OBJECTS:SERVER>)
# Benchmarks
add_executable(rtree-bench EXCLUDE_FROM_ALL src/benchmarks/static_rtree.cpp $<TARGET_OBJECTS:UTIL>)
target_include_directories(engine-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/unit_tests)
target_include_directories(util-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/unit_tests)
target_include_directories(rtree-bench PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/unit_tests)
@ -327,6 +330,7 @@ target_link_libraries(osrm_extract ${EXTRACTOR_LIBRARIES})
target_link_libraries(osrm_store ${STORAGE_LIBRARIES})
# Tests
target_link_libraries(engine-tests ${ENGINE_LIBRARIES})
target_link_libraries(server-tests osrm ${Boost_LIBRARIES})
target_link_libraries(extractor-tests ${EXTRACTOR_LIBRARIES})
target_link_libraries(rtree-bench ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${TBB_LIBRARIES})
target_link_libraries(util-tests ${UTIL_LIBRARIES})
@ -373,16 +377,19 @@ set_property(TARGET osrm-routed PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE)
file(GLOB VariantGlob third_party/variant/*.hpp)
file(GLOB LibraryGlob include/osrm/*.hpp)
set(EngineHeader include/engine/engine.hpp include/engine/engine_config.hpp include/engine/route_parameters.hpp)
set(UtilHeader include/util/coordinate.hpp include/util/json_container.hpp)
set(ExtractorHeader include/extractor/extractor.hpp include/extractor/extractor_config.hpp)
file(GLOB ParametersGlob include/engine/api/*_parameters.hpp)
set(EngineHeader include/engine/status.hpp include/engine/engine_config.hpp include/engine/hint.hpp include/engine/bearing.hpp include/engine/phantom_node.hpp)
set(UtilHeader include/util/coordinate.hpp include/util/json_container.hpp include/util/typedefs.hpp include/util/strong_typedef.hpp)
set(ExtractorHeader include/extractor/extractor.hpp include/extractor/extractor_config.hpp include/extractor/travel_mode.hpp)
set(ContractorHeader include/contractor/contractor.hpp include/contractor/contractor_config.hpp)
#set(StorageHeader include/storage/storage.hpp include/storage/storage_config.hpp)
set(StorageHeader include/storage/storage.hpp include/storage/storage_config.hpp)
install(FILES ${EngineHeader} DESTINATION include/osrm/engine)
install(FILES ${UtilHeader} DESTINATION include/osrm/util)
install(FILES ${StorageHeader} DESTINATION include/osrm/storage)
install(FILES ${ExtractorHeader} DESTINATION include/osrm/extractor)
install(FILES ${ContractorHeader} DESTINATION include/osrm/contractor)
install(FILES ${LibraryGlob} DESTINATION include/osrm)
install(FILES ${ParametersGlob} DESTINATION include/osrm/engine/api)
install(FILES ${VariantGlob} DESTINATION include/variant)
install(TARGETS osrm-extract DESTINATION bin)
install(TARGETS osrm-contract DESTINATION bin)

View File

@ -1,5 +1,5 @@
prefix=@CMAKE_INSTALL_PREFIX@
includedir=${prefix}/include
includedir=${prefix}/include ${prefix}/include/osrm
libdir=${prefix}/lib
Name: libOSRM

View File

@ -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

View File

@ -1,61 +1,85 @@
#include "osrm/json_container.hpp"
#include "osrm/engine_config.hpp"
#include "osrm/route_parameters.hpp"
#include "osrm/table_parameters.hpp"
#include "osrm/nearest_parameters.hpp"
#include "osrm/trip_parameters.hpp"
#include "osrm/match_parameters.hpp"
#include "osrm/coordinate.hpp"
#include "osrm/engine_config.hpp"
#include "osrm/json_container.hpp"
#include "osrm/status.hpp"
#include "osrm/osrm.hpp"
#include <string>
#include <utility>
#include <iostream>
#include <exception>
#include <cstdlib>
int main(int argc, const char *argv[]) try
{
if (argc < 2)
{
std::cerr << "Error: Not enough arguments." << std::endl
<< "Run " << argv[0] << " data.osrm" << std::endl;
std::cerr << "Usage: " << argv[0] << " data.osrm\n";
return EXIT_FAILURE;
}
osrm::EngineConfig engine_config;
std::string base_path(argv[1]);
engine_config.server_paths["ramindex"] = base_path + ".ramIndex";
engine_config.server_paths["fileindex"] = base_path + ".fileIndex";
engine_config.server_paths["hsgrdata"] = base_path + ".hsgr";
engine_config.server_paths["nodesdata"] = base_path + ".nodes";
engine_config.server_paths["edgesdata"] = base_path + ".edges";
engine_config.server_paths["coredata"] = base_path + ".core";
engine_config.server_paths["geometries"] = base_path + ".geometry";
engine_config.server_paths["timestamp"] = base_path + ".timestamp";
engine_config.server_paths["namesdata"] = base_path + ".names";
engine_config.use_shared_memory = false;
using namespace osrm;
osrm::OSRM routing_machine(engine_config);
// Configure based on a .osrm base path, and no datasets in shared mem from osrm-datastore
EngineConfig config;
config.storage_config = {argv[1]};
config.use_shared_memory = false;
osrm::RouteParameters route_parameters;
// route is in Monaco
route_parameters.service = "viaroute";
route_parameters.AddCoordinate(43.731142, 7.419758);
route_parameters.AddCoordinate(43.736825, 7.419505);
// Routing machine with several services (such as Route, Table, Nearest, Trip, Match)
OSRM osrm{config};
osrm::json::Object json_result;
const int result_code = routing_machine.RunQuery(route_parameters, json_result);
std::cout << "result code: " << result_code << std::endl;
// 2xx code
if (result_code / 100 == 2)
// The following shows how to use the Route service; configure this service
RouteParameters params;
// Route in monaco
params.coordinates.push_back({util::FloatLongitude(7.419758), util::FloatLatitude(43.731142)});
params.coordinates.push_back({util::FloatLongitude(7.419505), util::FloatLatitude(43.736825)});
// Response is in JSON format
json::Object result;
// Execute routing request, this does the heavy lifting
const auto status = osrm.Route(params, result);
if (status == Status::Ok)
{
// Extract data out of JSON structure
auto& summary = json_result.values["route_summary"].get<osrm::json::Object>();
auto duration = summary.values["total_time"].get<osrm::json::Number>().value;
auto distance = summary.values["total_distance"].get<osrm::json::Number>().value;
std::cout << "duration: " << duration << std::endl;
std::cout << "distance: " << distance << std::endl;
auto &routes = result.values["routes"].get<json::Array>();
// Let's just use the first route
auto &route = routes.values.at(0).get<json::Object>();
const auto distance = route.values["distance"].get<json::Number>().value;
const auto duration = route.values["duration"].get<json::Number>().value;
// Warn users if extract does not contain the default Berlin coordinates from above
if (distance == 0 or duration == 0)
{
std::cout << "Note: distance or duration is zero. ";
std::cout << "You are probably doing a query outside of the OSM extract.\n\n";
}
std::cout << "Distance: " << distance << " meter\n";
std::cout << "Duration: " << duration << " seconds\n";
}
else if (status == Status::Error)
{
const auto code = result.values["code"].get<json::String>().value;
const auto message = result.values["message"].get<json::String>().value;
std::cout << "Code: " << code << "\n";
std::cout << "Message: " << code << "\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
catch (const std::exception &current_exception)
catch (const std::exception &e)
{
std::cout << "exception: " << current_exception.what();
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}

View File

@ -0,0 +1,202 @@
@routing @guidance
Feature: Basic Roundabout
Background:
Given the profile "testbot"
Given a grid size of 10 meters
Scenario: Ramp Exit Right
Given the node map
| a | b | c | d | e |
| | | | f | g |
And the ways
| nodes | highway |
| abcde | motorway |
| bfg | motorway_link |
When I route I should get
| waypoints | route | turns |
| a,e | abcde, abcde | depart, arrive |
| a,g | abcde, bfg, bfg | depart, ramp-slight-right, arrive |
Scenario: Ramp Exit Right Curved Right
Given the node map
| a | b | c | | |
| | | f | d | |
| | | | g | e |
And the ways
| nodes | highway |
| abcde | motorway |
| bfg | motorway_link |
When I route I should get
| waypoints | route | turns |
| a,e | abcde, abcde | depart, arrive |
| a,g | abcde, bfg, bfg | depart, ramp-slight-right, arrive |
Scenario: Ramp Exit Right Curved Left
Given the node map
| | | | | e |
| | | | d | g |
| a | b | c | f | |
And the ways
| nodes | highway |
| abcde | motorway |
| cfg | motorway_link |
When I route I should get
| waypoints | route | turns |
| a,e | abcde, abcde | depart, arrive |
| a,g | abcde, cfg, cfg | depart, ramp-slight-right, arrive |
Scenario: Ramp Exit Left
Given the node map
| | | | f | g |
| a | b | c | d | e |
And the ways
| nodes | highway |
| abcde | motorway |
| bfg | motorway_link |
When I route I should get
| waypoints | route | turns |
| a,e | abcde, abcde | depart, arrive |
| a,g | abcde, bfg, bfg | depart, ramp-slight-left, arrive |
Scenario: Ramp Exit Left Curved Left
Given the node map
| | | | g | e |
| | | f | d | |
| a | b | c | | |
And the ways
| nodes | highway |
| abcde | motorway |
| bfg | motorway_link |
When I route I should get
| waypoints | route | turns |
| a,e | abcde, abcde | depart, arrive |
| a,g | abcde, bfg, bfg | depart, ramp-slight-left, arrive |
Scenario: Ramp Exit Left Curved Right
Given the node map
| a | b | c | f | |
| | | | d | g |
| | | | | e |
And the ways
| nodes | highway |
| abcde | motorway |
| cfg | motorway_link |
When I route I should get
| waypoints | route | turns |
| a,e | abcde, abcde | depart, arrive |
| a,g | abcde, cfg, cfg | depart, ramp-slight-left, arrive |
Scenario: On Ramp Right
Given the node map
| a | b | c | d | e |
| f | g | | | |
And the ways
| nodes | highway |
| abcde | motorway |
| fgd | motorway_link |
When I route I should get
| waypoints | route | turns |
| a,e | abcde, abcde | depart, arrive |
| f,e | abcde, fgd, fgd | depart, merge-slight-left, arrive |
Scenario: On Ramp Left
Given the node map
| f | g | | | |
| a | b | c | d | e |
And the ways
| nodes | highway |
| abcde | motorway |
| fgd | motorway_link |
When I route I should get
| waypoints | route | turns |
| a,e | abcde, abcde | depart, arrive |
| f,e | abcde, fgd, fgd | depart, merge-slight-right, arrive |
Scenario: Highway Fork
Given the node map
| | | | | d | e |
| a | b | c | | | |
| | | | | f | g |
And the ways
| nodes | highway |
| abcde | motorway |
| cfg | motorway |
When I route I should get
| waypoints | route | turns |
| a,e | abcde, abcde, abcde | depart, fork-left, arrive |
| a,g | abcde, cfg, cfg | depart, fork-right, arrive |
Scenario: Fork After Ramp
Given the node map
| | | | | d | e |
| a | b | c | | | |
| | | | | f | g |
And the ways
| nodes | highway |
| abc | motorway_link |
| cde | motorway |
| cfg | motorway |
When I route I should get
| waypoints | route | turns |
| a,e | abc, cde, cde | depart, fork-left, arrive |
| a,g | abc, cfg, cfg | depart, fork-right, arrive |
Scenario: On And Off Ramp Right
Given the node map
| a | b | | c | | d | e |
| f | g | | | | h | i |
And the ways
| nodes | highway |
| abcde | motorway |
| fgc | motorway_link |
| chi | motorway_link |
When I route I should get
| waypoints | route | turns |
| a,e | abcde, abcde | depart, arrive |
| f,e | fgc, abcde, abcde | depart, merge-slight-left, arrive |
| a,i | abcde, chi, chi | depart, ramp-slight-right, arrive |
| f,i | fgc, chi, chi | depart, turn-slight-right, arrive |
Scenario: On And Off Ramp Left
Given the node map
| f | g | | | | h | i |
| a | b | | c | | d | e |
And the ways
| nodes | highway |
| abcde | motorway |
| fgc | motorway_link |
| chi | motorway_link |
When I route I should get
| waypoints | route | turns |
| a,e | abcde, abcde | depart, arrive |
| f,e | fgc, abcde, abcde | depart, merge-slight-right, arrive |
| a,i | abcde, chi, chi | depart, ramp-slight-left, arrive |
| f,i | fgc, chi, chi | depart, turn-slight-left, arrive |

View File

@ -0,0 +1,173 @@
@routing @guidance
Feature: Basic Roundabout
Background:
Given the profile "testbot"
Given a grid size of 10 meters
Scenario: Enter and Exit
Given the node map
| | | a | | |
| | | b | | |
| h | g | | c | d |
| | | e | | |
| | | f | | |
And the ways
| nodes | roundabout |
| ab | no |
| cd | no |
| ef | no |
| gh | no |
| bcegb | yes |
When I route I should get
| waypoints | route | turns |
| a,d | ab,cd,cd | depart, roundabout-exit-1, arrive |
| a,f | ab,ef,ef | depart, roundabout-exit-2, arrive |
| a,h | ab,gh,gh | depart, roundabout-exit-3, arrive |
| d,f | cd,ef,ef | depart, roundabout-exit-1, arrive |
| d,h | cd,gh,gh | depart, roundabout-exit-2, arrive |
| d,a | cd,ab,ab | depart, roundabout-exit-3, arrive |
| f,h | ef,gh,gh | depart, roundabout-exit-1, arrive |
| f,a | ef,ab,ab | depart, roundabout-exit-2, arrive |
| f,d | ef,cd,cd | depart, roundabout-exit-3, arrive |
| h,a | gh,ab,ab | depart, roundabout-exit-1, arrive |
| h,d | gh,cd,cd | depart, roundabout-exit-2, arrive |
| h,f | gh,ef,ef | depart, roundabout-exit-3, arrive |
Scenario: Only Enter
Given the node map
| | | a | | |
| | | b | | |
| h | g | | c | d |
| | | e | | |
| | | f | | |
And the ways
| nodes | roundabout |
| ab | no |
| cd | no |
| ef | no |
| gh | no |
| bcegb | yes |
When I route I should get
| waypoints | route | turns |
| a,b | ab,ab | depart, arrive |
| a,c | ab,bcegb | depart, roundabout-enter, arrive |
| a,e | ab,bcegb | depart, roundabout-enter, arrive |
| a,g | ab,bcegb | depart, roundabout-enter, arrive |
| d,c | cd,cd | depart, arrive |
| d,e | cd,bcegb | depart, roundabout-enter, arrive |
| d,g | cd,bcegb | depart, roundabout-enter, arrive |
| d,b | cd,bcegb | depart, roundabout-enter, arrive |
| f,e | ef,ef | depart, arrive |
| f,g | ef,bcegb | depart, roundabout-enter, arrive |
| f,b | ef,bcegb | depart, roundabout-enter, arrive |
| f,c | ef,bcegb | depart, roundabout-enter, arrive |
| h,g | gh,gh | depart, arrive |
| h,b | gh,bcegb | depart, roundabout-enter, arrive |
| h,c | gh,bcegb | depart, roundabout-enter, arrive |
| h,e | gh,bcegb | depart, roundabout-enter, arrive |
Scenario: Only Exit
Given the node map
| | | a | | |
| | | b | | |
| h | g | | c | d |
| | | e | | |
| | | f | | |
And the ways
| nodes | roundabout |
| ab | no |
| cd | no |
| ef | no |
| gh | no |
| bcegb | yes |
When I route I should get
| waypoints | route | turns |
| b,a | ab,ab | depart, arrive |
| b,d | bcegb,cd,cd | depart, roundabout-exit-1, arrive |
| b,f | bcegb,ef,ef | depart, roundabout-exit-2, arrive |
| b,h | bcegb,gh,gh | depart, roundabout-exit-3, arrive |
| c,d | cd,cd | depart, arrive |
| c,f | bcegb,ef,ef | depart, roundabout-exit-1, arrive |
| c,h | bcegb,gh,gh | depart, roundabout-exit-2, arrive |
| c,a | bcegb,ab,ab | depart, roundabout-exit-3, arrive |
| e,f | ef,ef | depart, arrive |
| e,h | bcegb,gh,gh | depart, roundabout-exit-1, arrive |
| e,a | bcegb,ab,ab | depart, roundabout-exit-2, arrive |
| e,d | bcegb,cd,cd | depart, roundabout-exit-3, arrive |
| g,h | gh,gh | depart, arrive |
| g,a | bcegb,ab,ab | depart, roundabout-exit-1, arrive |
| g,d | bcegb,cd,cd | depart, roundabout-exit-2, arrive |
| g,f | bcegb,ef,ef | depart, roundabout-exit-3, arrive |
Scenario: Drive Around
Given the node map
| | | a | | |
| | | b | | |
| h | g | | c | d |
| | | e | | |
| | | f | | |
And the ways
| nodes | roundabout |
| ab | no |
| cd | no |
| ef | no |
| gh | no |
| bcegb | yes |
When I route I should get
| waypoints | route | turns |
| b,c | bcegb,bcegb | depart, arrive |
| b,e | bcegb,bcegb | depart, arrive |
| b,g | bcegb,bcegb | depart, arrive |
| c,e | bcegb,bcegb | depart, arrive |
| c,g | bcegb,bcegb | depart, arrive |
| c,b | bcegb,bcegb | depart, arrive |
| e,g | bcegb,bcegb | depart, arrive |
| e,b | bcegb,bcegb | depart, arrive |
| e,c | bcegb,bcegb | depart, arrive |
| g,b | bcegb,bcegb | depart, arrive |
| g,c | bcegb,bcegb | depart, arrive |
| g,e | bcegb,bcegb | depart, arrive |
Scenario: Mixed Entry and Exit
Given the node map
| | a | | c | |
| l | | b | | d |
| | k | | e | |
| j | | h | | f |
| | i | | g | |
And the ways
| nodes | roundabout | oneway |
| abc | no | yes |
| def | no | yes |
| ghi | no | yes |
| jkl | no | yes |
| behkb | yes | yes |
When I route I should get
| waypoints | route | turns |
| a,c | abc,abc,abc | depart, roundabout-exit-1, arrive |
| a,f | abc,def,def | depart, roundabout-exit-2, arrive |
| a,i | abc,ghi,ghi | depart, roundabout-exit-3, arrive |
| a,l | abc,jkl,jkl | depart, roundabout-exit-4, arrive |
| d,f | def,def,def | depart, roundabout-exit-1, arrive |
| d,i | def,ghi,ghi | depart, roundabout-exit-2, arrive |
| d,l | def,jkl,jkl | depart, roundabout-exit-3, arrive |
| d,c | def,abc,abc | depart, roundabout-exit-4, arrive |
| g,i | ghi,ghi,ghi | depart, roundabout-exit-1, arrive |
| g,l | ghi,jkl,jkl | depart, roundabout-exit-2, arrive |
| g,c | ghi,abc,abc | depart, roundabout-exit-3, arrive |
| g,f | ghi,edf,edf | depart, roundabout-exit-4, arrive |
| j,l | jkl,jkl,jkl | depart, roundabout-exit-1, arrive |
| j,c | jkl,abc,abc | depart, roundabout-exit-2, arrive |
| j,f | jkl,def,def | depart, roundabout-exit-3, arrive |
| j,i | jkl,ghi,ghi | depart, roundabout-exit-4, arrive |

View File

@ -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 |

View File

@ -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);
};
}

View File

@ -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;
};
}
}

View File

@ -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;
}
}

View 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 <boost/range/algorithm/transform.hpp>
#include <vector>
namespace osrm
{
namespace engine
{
namespace api
{
class BaseAPI
{
public:
BaseAPI(const datafacade::BaseDataFacade &facade_, const BaseParameters &parameters_)
: facade(facade_), parameters(parameters_)
{
}
util::json::Array MakeWaypoints(const std::vector<PhantomNodes> &segment_end_coordinates) const
{
BOOST_ASSERT(parameters.coordinates.size() > 0);
BOOST_ASSERT(parameters.coordinates.size() == segment_end_coordinates.size() + 1);
util::json::Array waypoints;
waypoints.values.resize(parameters.coordinates.size());
waypoints.values[0] = MakeWaypoint(segment_end_coordinates.front().source_phantom);
auto out_iter = std::next(waypoints.values.begin());
boost::range::transform(segment_end_coordinates, out_iter,
[this](const PhantomNodes &phantom_pair)
{
return MakeWaypoint(phantom_pair.target_phantom);
});
return waypoints;
}
// FIXME gcc 4.8 doesn't support for lambdas to call protected member functions
// protected:
util::json::Object MakeWaypoint(const PhantomNode &phantom) const
{
return json::makeWaypoint(phantom.location, facade.GetNameForID(phantom.name_id),
Hint{phantom, facade.GetCheckSum()});
}
const datafacade::BaseDataFacade &facade;
const BaseParameters &parameters;
};
} // ns api
} // ns engine
} // ns osrm
#endif

View 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

View 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_

View File

@ -0,0 +1,119 @@
#ifndef ENGINE_API_MATCH_HPP
#define ENGINE_API_MATCH_HPP
#include "engine/api/route_api.hpp"
#include "engine/api/match_parameters.hpp"
#include "engine/datafacade/datafacade_base.hpp"
#include "engine/internal_route_result.hpp"
#include "engine/map_matching/sub_matching.hpp"
#include "util/integer_range.hpp"
namespace osrm
{
namespace engine
{
namespace api
{
class MatchAPI final : public RouteAPI
{
public:
MatchAPI(const datafacade::BaseDataFacade &facade_, const MatchParameters &parameters_)
: RouteAPI(facade_, parameters_), parameters(parameters_)
{
}
void MakeResponse(const std::vector<map_matching::SubMatching> &sub_matchings,
const std::vector<InternalRouteResult> &sub_routes,
util::json::Object &response) const
{
auto number_of_routes = sub_matchings.size();
util::json::Array routes;
routes.values.reserve(number_of_routes);
BOOST_ASSERT(sub_matchings.size() == sub_routes.size());
for (auto index : util::irange<std::size_t>(0UL, sub_matchings.size()))
{
auto route = MakeRoute(sub_routes[index].segment_end_coordinates,
sub_routes[index].unpacked_path_segments,
sub_routes[index].source_traversed_in_reverse,
sub_routes[index].target_traversed_in_reverse);
route.values["confidence"] = sub_matchings[index].confidence;
routes.values.push_back(std::move(route));
}
response.values["tracepoints"] = MakeTracepoints(sub_matchings);
response.values["matchings"] = std::move(routes);
response.values["code"] = "ok";
}
// FIXME gcc 4.8 doesn't support for lambdas to call protected member functions
// protected:
// FIXME this logic is a little backwards. We should change the output format of the
// map_matching
// routing algorithm to be easier to consume here.
util::json::Array
MakeTracepoints(const std::vector<map_matching::SubMatching> &sub_matchings) const
{
util::json::Array waypoints;
waypoints.values.reserve(parameters.coordinates.size());
struct MatchingIndex
{
MatchingIndex() = default;
MatchingIndex(unsigned sub_matching_index_, unsigned point_index_)
: sub_matching_index(sub_matching_index_), point_index(point_index_)
{
}
unsigned sub_matching_index = std::numeric_limits<unsigned>::max();
unsigned point_index = std::numeric_limits<unsigned>::max();
bool NotMatched()
{
return sub_matching_index == std::numeric_limits<unsigned>::max() &&
point_index == std::numeric_limits<unsigned>::max();
}
};
std::vector<MatchingIndex> trace_idx_to_matching_idx(parameters.coordinates.size());
for (auto sub_matching_index :
util::irange(0u, static_cast<unsigned>(sub_matchings.size())))
{
for (auto point_index : util::irange(
0u, static_cast<unsigned>(sub_matchings[sub_matching_index].indices.size())))
{
trace_idx_to_matching_idx[sub_matchings[sub_matching_index].indices[point_index]] =
MatchingIndex{sub_matching_index, point_index};
}
}
for (auto trace_index : util::irange(0UL, parameters.coordinates.size()))
{
auto matching_index = trace_idx_to_matching_idx[trace_index];
if (matching_index.NotMatched())
{
waypoints.values.push_back(util::json::Null());
continue;
}
const auto &phantom =
sub_matchings[matching_index.sub_matching_index].nodes[matching_index.point_index];
auto waypoint = BaseAPI::MakeWaypoint(phantom);
waypoint.values["matchings_index"] = matching_index.sub_matching_index;
waypoint.values["waypoint_index"] = matching_index.point_index;
waypoints.values.push_back(std::move(waypoint));
}
return waypoints;
}
const MatchParameters &parameters;
};
} // ns api
} // ns engine
} // ns osrm
#endif

View 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

View File

@ -0,0 +1,57 @@
#ifndef ENGINE_API_NEAREST_API_HPP
#define ENGINE_API_NEAREST_API_HPP
#include "engine/api/base_api.hpp"
#include "engine/api/nearest_parameters.hpp"
#include "engine/api/json_factory.hpp"
#include "engine/phantom_node.hpp"
#include <boost/assert.hpp>
#include <vector>
namespace osrm
{
namespace engine
{
namespace api
{
class NearestAPI final : public BaseAPI
{
public:
NearestAPI(const datafacade::BaseDataFacade &facade_, const NearestParameters &parameters_)
: BaseAPI(facade_, parameters_), parameters(parameters_)
{
}
void MakeResponse(const std::vector<std::vector<PhantomNodeWithDistance>> &phantom_nodes,
util::json::Object &response) const
{
BOOST_ASSERT(phantom_nodes.size() == 1);
BOOST_ASSERT(parameters.coordinates.size() == 1);
util::json::Array waypoints;
waypoints.values.resize(phantom_nodes.front().size());
std::transform(phantom_nodes.front().begin(), phantom_nodes.front().end(),
waypoints.values.begin(),
[this](const PhantomNodeWithDistance &phantom_with_distance)
{
auto waypoint = MakeWaypoint(phantom_with_distance.phantom_node);
waypoint.values["distance"] = phantom_with_distance.distance;
return waypoint;
});
response.values["code"] = "ok";
response.values["waypoints"] = std::move(waypoints);
}
const NearestParameters &parameters;
};
} // ns api
} // ns engine
} // ns osrm
#endif

View 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

View File

@ -0,0 +1,183 @@
#ifndef ENGINE_API_ROUTE_HPP
#define ENGINE_API_ROUTE_HPP
#include "engine/api/base_api.hpp"
#include "engine/api/route_parameters.hpp"
#include "engine/api/json_factory.hpp"
#include "engine/datafacade/datafacade_base.hpp"
#include "engine/guidance/assemble_leg.hpp"
#include "engine/guidance/assemble_route.hpp"
#include "engine/guidance/assemble_geometry.hpp"
#include "engine/guidance/assemble_overview.hpp"
#include "engine/guidance/assemble_steps.hpp"
#include "engine/guidance/post_processing.hpp"
#include "engine/internal_route_result.hpp"
#include "util/coordinate.hpp"
#include "util/integer_range.hpp"
#include <vector>
namespace osrm
{
namespace engine
{
namespace api
{
class RouteAPI : public BaseAPI
{
public:
RouteAPI(const datafacade::BaseDataFacade &facade_, const RouteParameters &parameters_)
: BaseAPI(facade_, parameters_), parameters(parameters_)
{
}
void MakeResponse(const InternalRouteResult &raw_route, util::json::Object &response) const
{
auto number_of_routes = raw_route.has_alternative() ? 2UL : 1UL;
util::json::Array routes;
routes.values.resize(number_of_routes);
routes.values[0] =
MakeRoute(raw_route.segment_end_coordinates, raw_route.unpacked_path_segments,
raw_route.source_traversed_in_reverse, raw_route.target_traversed_in_reverse);
if (raw_route.has_alternative())
{
std::vector<std::vector<PathData>> wrapped_leg(1);
wrapped_leg.front() = std::move(raw_route.unpacked_alternative);
routes.values[1] = MakeRoute(raw_route.segment_end_coordinates, wrapped_leg,
raw_route.alt_source_traversed_in_reverse,
raw_route.alt_target_traversed_in_reverse);
}
response.values["waypoints"] = BaseAPI::MakeWaypoints(raw_route.segment_end_coordinates);
response.values["routes"] = std::move(routes);
response.values["code"] = "ok";
}
// FIXME gcc 4.8 doesn't support for lambdas to call protected member functions
// protected:
template <typename ForwardIter>
util::json::Value MakeGeometry(ForwardIter begin, ForwardIter end) const
{
if (parameters.geometries == RouteParameters::GeometriesType::Polyline)
{
return json::makePolyline(begin, end);
}
BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON);
return json::makeGeoJSONLineString(begin, end);
}
util::json::Object MakeRoute(const std::vector<PhantomNodes> &segment_end_coordinates,
const std::vector<std::vector<PathData>> &unpacked_path_segments,
const std::vector<bool> &source_traversed_in_reverse,
const std::vector<bool> &target_traversed_in_reverse) const
{
std::vector<guidance::RouteLeg> legs;
std::vector<guidance::LegGeometry> leg_geometries;
auto number_of_legs = segment_end_coordinates.size();
legs.reserve(number_of_legs);
leg_geometries.reserve(number_of_legs);
for (auto idx : util::irange(0UL, number_of_legs))
{
const auto &phantoms = segment_end_coordinates[idx];
const auto &path_data = unpacked_path_segments[idx];
const bool reversed_source = source_traversed_in_reverse[idx];
const bool reversed_target = target_traversed_in_reverse[idx];
auto leg_geometry = guidance::assembleGeometry(
BaseAPI::facade, path_data, phantoms.source_phantom, phantoms.target_phantom);
auto leg = guidance::assembleLeg(BaseAPI::facade, path_data, leg_geometry,
phantoms.target_phantom, reversed_target);
if (parameters.steps)
{
auto steps = guidance::assembleSteps(
BaseAPI::facade, path_data, leg_geometry, phantoms.source_phantom,
phantoms.target_phantom, reversed_source, reversed_target);
/* Perform step-based post-processing.
*
* Using post-processing on basis of route-steps for a single leg at a time
* comes at the cost that we cannot count the correct exit for roundabouts.
* We can only emit the exit nr/intersections up to/starting at a part of the leg.
* If a roundabout is not terminated in a leg, we will end up with a enter-roundabout
* and exit-roundabout-nr where the exit nr is out of sync with the previous enter.
*
* | S |
* * *
* ----* * ----
* T
* ----* * ----
* V * *
* | |
* | |
*
* Coming from S via V to T, we end up with the legs S->V and V->T. V-T will say to take
* the second exit, even though counting from S it would be the third.
* For S, we only emit `roundabout` without an exit number, showing that we enter a roundabout
* to find a via point.
* The same exit will be emitted, though, if we should start routing at S, making
* the overall response consistent.
*/
leg.steps = guidance::postProcess(std::move(steps));
leg_geometry = guidance::resyncGeometry(std::move(leg_geometry),leg.steps);
}
leg_geometries.push_back(std::move(leg_geometry));
legs.push_back(std::move(leg));
}
auto route = guidance::assembleRoute(legs);
boost::optional<util::json::Value> json_overview;
if (parameters.overview != RouteParameters::OverviewType::False)
{
const auto use_simplification =
parameters.overview == RouteParameters::OverviewType::Simplified;
BOOST_ASSERT(use_simplification ||
parameters.overview == RouteParameters::OverviewType::Full);
auto overview = guidance::assembleOverview(leg_geometries, use_simplification);
json_overview = MakeGeometry(overview.begin(), overview.end());
}
std::vector<util::json::Value> step_geometries;
for (const auto idx : util::irange(0UL, legs.size()))
{
auto &leg_geometry = leg_geometries[idx];
std::transform(
legs[idx].steps.begin(), legs[idx].steps.end(), std::back_inserter(step_geometries),
[this, &leg_geometry](const guidance::RouteStep &step)
{
if (parameters.geometries == RouteParameters::GeometriesType::Polyline)
{
return static_cast<util::json::Value>(
json::makePolyline(leg_geometry.locations.begin() + step.geometry_begin,
leg_geometry.locations.begin() + step.geometry_end));
}
BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON);
return static_cast<util::json::Value>(json::makeGeoJSONLineString(
leg_geometry.locations.begin() + step.geometry_begin,
leg_geometry.locations.begin() + step.geometry_end));
});
}
return json::makeRoute(route,
json::makeRouteLegs(std::move(legs), std::move(step_geometries)),
std::move(json_overview));
}
const RouteParameters &parameters;
};
} // ns api
} // ns engine
} // ns osrm
#endif

View File

@ -0,0 +1,85 @@
/*
Copyright (c) 2016, Project OSRM contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ENGINE_API_ROUTE_PARAMETERS_HPP
#define ENGINE_API_ROUTE_PARAMETERS_HPP
#include "engine/api/base_parameters.hpp"
#include <vector>
namespace osrm
{
namespace engine
{
namespace api
{
struct RouteParameters : public BaseParameters
{
enum class GeometriesType
{
Polyline,
GeoJSON
};
enum class OverviewType
{
Simplified,
Full,
False
};
RouteParameters() = default;
template <typename... Args>
RouteParameters(const bool steps_,
const bool alternatives_,
const GeometriesType geometries_,
const OverviewType overview_,
const boost::optional<bool> uturns_,
Args... args_)
: BaseParameters{std::forward<Args>(args_)...}, steps{steps_}, alternatives{alternatives_},
geometries{geometries_}, overview{overview_}, uturns{uturns_}
{
}
bool steps = true;
bool alternatives = true;
GeometriesType geometries = GeometriesType::Polyline;
OverviewType overview = OverviewType::Simplified;
boost::optional<bool> uturns;
bool IsValid() const
{
return coordinates.size() >= 2 && BaseParameters::IsValid();
}
};
}
}
}
#endif

View File

@ -0,0 +1,133 @@
#ifndef ENGINE_API_TABLE_HPP
#define ENGINE_API_TABLE_HPP
#include "engine/api/base_api.hpp"
#include "engine/api/table_parameters.hpp"
#include "engine/api/json_factory.hpp"
#include "engine/datafacade/datafacade_base.hpp"
#include "engine/guidance/assemble_leg.hpp"
#include "engine/guidance/assemble_route.hpp"
#include "engine/guidance/assemble_geometry.hpp"
#include "engine/guidance/assemble_overview.hpp"
#include "engine/guidance/assemble_steps.hpp"
#include "engine/internal_route_result.hpp"
#include "util/integer_range.hpp"
#include <boost/range/algorithm/transform.hpp>
namespace osrm
{
namespace engine
{
namespace api
{
class TableAPI final : public BaseAPI
{
public:
TableAPI(const datafacade::BaseDataFacade &facade_, const TableParameters &parameters_)
: BaseAPI(facade_, parameters_), parameters(parameters_)
{
}
virtual void MakeResponse(const std::vector<EdgeWeight> &durations,
const std::vector<PhantomNode> &phantoms,
util::json::Object &response) const
{
auto number_of_sources = parameters.sources.size();
auto number_of_destinations = parameters.destinations.size();
;
// symmetric case
if (parameters.sources.empty())
{
response.values["sources"] = MakeWaypoints(phantoms);
number_of_sources = phantoms.size();
}
else
{
response.values["sources"] = MakeWaypoints(phantoms, parameters.sources);
}
if (parameters.destinations.empty())
{
response.values["destinations"] = MakeWaypoints(phantoms);
number_of_destinations = phantoms.size();
}
else
{
response.values["destinations"] = MakeWaypoints(phantoms, parameters.destinations);
}
response.values["durations"] =
MakeTable(durations, number_of_sources, number_of_destinations);
response.values["code"] = "ok";
}
// FIXME gcc 4.8 doesn't support for lambdas to call protected member functions
// protected:
virtual util::json::Array MakeWaypoints(const std::vector<PhantomNode> &phantoms) const
{
util::json::Array json_waypoints;
json_waypoints.values.reserve(phantoms.size());
BOOST_ASSERT(phantoms.size() == parameters.coordinates.size());
boost::range::transform(phantoms, std::back_inserter(json_waypoints.values),
[this](const PhantomNode &phantom)
{
return BaseAPI::MakeWaypoint(phantom);
});
return json_waypoints;
}
virtual util::json::Array MakeWaypoints(const std::vector<PhantomNode> &phantoms,
const std::vector<std::size_t> &indices) const
{
util::json::Array json_waypoints;
json_waypoints.values.reserve(indices.size());
boost::range::transform(indices, std::back_inserter(json_waypoints.values),
[this, phantoms](const std::size_t idx)
{
BOOST_ASSERT(idx < phantoms.size());
return BaseAPI::MakeWaypoint(phantoms[idx]);
});
return json_waypoints;
}
virtual util::json::Array MakeTable(const std::vector<EdgeWeight> &values,
std::size_t number_of_rows,
std::size_t number_of_columns) const
{
util::json::Array json_table;
for (const auto row : util::irange<std::size_t>(0, number_of_rows))
{
util::json::Array json_row;
auto row_begin_iterator = values.begin() + (row * number_of_columns);
auto row_end_iterator = values.begin() + ((row + 1) * number_of_columns);
json_row.values.resize(number_of_columns);
std::transform(row_begin_iterator, row_end_iterator, json_row.values.begin(),
[](const EdgeWeight duration)
{
if (duration == INVALID_EDGE_WEIGHT)
{
return util::json::Value(util::json::Null());
}
return util::json::Value(util::json::Number(duration / 10.));
});
json_table.values.push_back(std::move(json_row));
}
return json_table;
}
const TableParameters &parameters;
};
} // ns api
} // ns engine
} // ns osrm
#endif

View 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

View 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

View File

@ -0,0 +1,111 @@
#ifndef ENGINE_API_TRIP_HPP
#define ENGINE_API_TRIP_HPP
#include "engine/api/route_api.hpp"
#include "engine/api/trip_parameters.hpp"
#include "engine/datafacade/datafacade_base.hpp"
#include "engine/internal_route_result.hpp"
#include "util/integer_range.hpp"
namespace osrm
{
namespace engine
{
namespace api
{
class TripAPI final : public RouteAPI
{
public:
TripAPI(const datafacade::BaseDataFacade &facade_, const TripParameters &parameters_)
: RouteAPI(facade_, parameters_), parameters(parameters_)
{
}
void MakeResponse(const std::vector<std::vector<NodeID>> &sub_trips,
const std::vector<InternalRouteResult> &sub_routes,
const std::vector<PhantomNode> &phantoms,
util::json::Object &response) const
{
auto number_of_routes = sub_trips.size();
util::json::Array routes;
routes.values.reserve(number_of_routes);
BOOST_ASSERT(sub_trips.size() == sub_routes.size());
for (auto index : util::irange<std::size_t>(0UL, sub_trips.size()))
{
auto route = MakeRoute(sub_routes[index].segment_end_coordinates,
sub_routes[index].unpacked_path_segments,
sub_routes[index].source_traversed_in_reverse,
sub_routes[index].target_traversed_in_reverse);
routes.values.push_back(std::move(route));
}
response.values["waypoints"] = MakeWaypoints(sub_trips, phantoms);
response.values["trips"] = std::move(routes);
response.values["code"] = "ok";
}
// FIXME gcc 4.8 doesn't support for lambdas to call protected member functions
// protected:
// FIXME this logic is a little backwards. We should change the output format of the
// trip plugin routing algorithm to be easier to consume here.
util::json::Array MakeWaypoints(const std::vector<std::vector<NodeID>> &sub_trips,
const std::vector<PhantomNode> &phantoms) const
{
util::json::Array waypoints;
waypoints.values.reserve(parameters.coordinates.size());
struct TripIndex
{
TripIndex() = default;
TripIndex(unsigned sub_trip_index_, unsigned point_index_)
: sub_trip_index(sub_trip_index_), point_index(point_index_)
{
}
unsigned sub_trip_index = std::numeric_limits<unsigned>::max();
unsigned point_index = std::numeric_limits<unsigned>::max();
bool NotUsed()
{
return sub_trip_index == std::numeric_limits<unsigned>::max() &&
point_index == std::numeric_limits<unsigned>::max();
}
};
std::vector<TripIndex> input_idx_to_trip_idx(parameters.coordinates.size());
for (auto sub_trip_index : util::irange(0u, static_cast<unsigned>(sub_trips.size())))
{
for (auto point_index :
util::irange(0u, static_cast<unsigned>(sub_trips[sub_trip_index].size())))
{
input_idx_to_trip_idx[sub_trips[sub_trip_index][point_index]] =
TripIndex{sub_trip_index, point_index};
}
}
for (auto input_index : util::irange(0UL, parameters.coordinates.size()))
{
auto trip_index = input_idx_to_trip_idx[input_index];
BOOST_ASSERT(!trip_index.NotUsed());
auto waypoint = BaseAPI::MakeWaypoint(phantoms[input_index]);
waypoint.values["trips_index"] = trip_index.sub_trip_index;
waypoint.values["waypoint_index"] = trip_index.point_index;
waypoints.values.push_back(std::move(waypoint));
}
return waypoints;
}
const TripParameters &parameters;
};
} // ns api
} // ns engine
} // ns osrm
#endif

View 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

View File

@ -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_

126
include/engine/base64.hpp Normal file
View File

@ -0,0 +1,126 @@
#ifndef OSRM_BASE64_HPP
#define OSRM_BASE64_HPP
#include <string>
#include <iterator>
#include <type_traits>
#include <cstddef>
#include <climits>
#include <boost/archive/iterators/binary_from_base64.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/range/algorithm/copy.hpp>
// RFC 4648 "The Base16, Base32, and Base64 Data Encodings"
// See: https://tools.ietf.org/html/rfc4648
// Implementation adapted from: http://stackoverflow.com/a/28471421
// The C++ standard guarantees none of this by default, but we need it in the following.
static_assert(CHAR_BIT == 8u, "we assume a byte holds 8 bits");
static_assert(sizeof(char) == 1u, "we assume a char is one byte large");
namespace osrm
{
namespace engine
{
// Encoding Implementation
// Encodes a chunk of memory to Base64.
inline std::string encodeBase64(const unsigned char *first, std::size_t size)
{
using namespace boost::archive::iterators;
const std::string bytes{first, first + size};
using Iter = base64_from_binary<transform_width<std::string::const_iterator, 6, 8>>;
Iter view_first{begin(bytes)};
Iter view_last{end(bytes)};
std::string encoded{view_first, view_last};
return encoded.append((3 - size % 3) % 3, '=');
}
// C++11 standard 3.9.1/1: Plain char, signed char, and unsigned char are three distinct types
// Overload for signed char catches (not only but also) C-string literals.
inline std::string encodeBase64(const signed char *first, std::size_t size)
{
return encodeBase64(reinterpret_cast<const unsigned char *>(first), size);
}
// Overload for char catches (not only but also) C-string literals.
inline std::string encodeBase64(const char *first, std::size_t size)
{
return encodeBase64(reinterpret_cast<const unsigned char *>(first), size);
}
// Convenience specialization, encoding from string instead of byte-dumping it.
inline std::string encodeBase64(const std::string &x) { return encodeBase64(x.data(), x.size()); }
// Encode any sufficiently trivial object to Base64.
template <typename T> std::string encodeBase64Bytewise(const T &x)
{
#if not defined __GNUC__ or __GNUC__ > 4
static_assert(std::is_trivially_copyable<T>::value, "requires a trivially copyable type");
#endif
return encodeBase64(reinterpret_cast<const unsigned char *>(&x), sizeof(T));
}
// Decoding Implementation
// Decodes into a chunk of memory that is at least as large as the input.
template <typename OutputIter> void decodeBase64(const std::string &encoded, OutputIter out)
{
using namespace boost::archive::iterators;
using namespace boost::algorithm;
using Iter = transform_width<binary_from_base64<std::string::const_iterator>, 8, 6>;
Iter view_first{begin(encoded)};
Iter view_last{end(encoded)};
const auto null = [](const unsigned char c)
{
return c == '\0';
};
const auto bytes = trim_right_copy_if(std::string{view_first, view_last}, null);
boost::copy(bytes, out);
}
// Convenience specialization, filling string instead of byte-dumping into it.
inline std::string decodeBase64(const std::string &encoded)
{
std::string rv;
decodeBase64(encoded, std::back_inserter(rv));
return rv;
}
// Decodes from Base 64 to any sufficiently trivial object.
template <typename T> T decodeBase64Bytewise(const std::string &encoded)
{
#if not defined __GNUC__ or __GNUC__ > 4
static_assert(std::is_trivially_copyable<T>::value, "requires a trivially copyable type");
#endif
T x;
decodeBase64(encoded, reinterpret_cast<unsigned char *>(&x));
return x;
}
} // ns engine
} // ns osrm
#endif /* OSRM_BASE64_HPP */

View 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

View File

@ -5,8 +5,9 @@
#include "extractor/edge_based_node.hpp"
#include "extractor/external_memory_node.hpp"
#include "contractor/query_edge.hpp"
#include "engine/phantom_node.hpp"
#include "extractor/turn_instructions.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "util/integer_range.hpp"
#include "util/exception.hpp"
#include "util/string_util.hpp"
@ -29,11 +30,11 @@ namespace datafacade
using EdgeRange = util::range<EdgeID>;
template <class EdgeDataT> class BaseDataFacade
class BaseDataFacade
{
public:
using EdgeData = contractor::QueryEdge::EdgeData;
using RTreeLeaf = extractor::EdgeBasedNode;
using EdgeData = EdgeDataT;
BaseDataFacade() {}
virtual ~BaseDataFacade() {}
@ -46,7 +47,7 @@ template <class EdgeDataT> class BaseDataFacade
virtual NodeID GetTarget(const EdgeID e) const = 0;
virtual const EdgeDataT &GetEdgeData(const EdgeID e) const = 0;
virtual const EdgeData &GetEdgeData(const EdgeID e) const = 0;
virtual EdgeID BeginEdges(const NodeID n) const = 0;
@ -63,9 +64,7 @@ template <class EdgeDataT> class BaseDataFacade
FindEdgeIndicateIfReverse(const NodeID from, const NodeID to, bool &result) const = 0;
// node and edge information access
virtual util::FixedPointCoordinate GetCoordinateOfNode(const unsigned id) const = 0;
virtual bool EdgeIsCompressed(const unsigned id) const = 0;
virtual util::Coordinate GetCoordinateOfNode(const unsigned id) const = 0;
virtual unsigned GetGeometryIndexForEdgeID(const unsigned id) const = 0;
@ -75,31 +74,63 @@ template <class EdgeDataT> class BaseDataFacade
// Gets the weight values for each segment in an uncompressed geometry.
// Should always be 1 shorter than GetUncompressedGeometry
virtual void GetUncompressedWeights(const EdgeID id,
std::vector<EdgeWeight> &result_weights) const = 0;
std::vector<EdgeWeight> &result_weights) const = 0;
virtual extractor::TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const = 0;
// Returns the data source ids that were used to supply the edge
// weights. Will return an empty array when only the base profile is used.
virtual void GetUncompressedDatasources(const EdgeID id,
std::vector<uint8_t> &data_sources) const = 0;
// Gets the name of a datasource
virtual std::string GetDatasourceName(const uint8_t datasource_name_id) const = 0;
virtual extractor::guidance::TurnInstruction
GetTurnInstructionForEdgeID(const unsigned id) const = 0;
virtual extractor::TravelMode GetTravelModeForEdgeID(const unsigned id) const = 0;
virtual std::vector<RTreeLeaf> GetEdgesInBox(const util::FixedPointCoordinate &south_west,
const util::FixedPointCoordinate &north_east) = 0;
virtual std::vector<RTreeLeaf> GetEdgesInBox(const util::Coordinate south_west,
const util::Coordinate north_east) = 0;
virtual std::vector<PhantomNodeWithDistance>
NearestPhantomNodesInRange(const util::FixedPointCoordinate input_coordinate,
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
const float max_distance,
const int bearing = 0,
const int bearing_range = 180) = 0;
const int bearing,
const int bearing_range) = 0;
virtual std::vector<PhantomNodeWithDistance>
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
const float max_distance) = 0;
virtual std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate,
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const int bearing = 0,
const int bearing_range = 180) = 0;
const double max_distance,
const int bearing,
const int bearing_range) = 0;
virtual std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const int bearing,
const int bearing_range) = 0;
virtual std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate, const unsigned max_results) = 0;
virtual std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const double max_distance) = 0;
virtual std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate) = 0;
virtual std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
const double max_distance) = 0;
virtual std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
const double max_distance,
const int bearing,
const int bearing_range) = 0;
virtual std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate,
const int bearing = 0,
const int bearing_range = 180) = 0;
const util::Coordinate input_coordinate, const int bearing, const int bearing_range) = 0;
virtual unsigned GetCheckSum() const = 0;
@ -107,11 +138,13 @@ template <class EdgeDataT> class BaseDataFacade
virtual unsigned GetNameIndexFromEdgeID(const unsigned id) const = 0;
virtual std::string get_name_for_id(const unsigned name_id) const = 0;
virtual std::string GetNameForID(const unsigned name_id) const = 0;
virtual std::size_t GetCoreSize() const = 0;
virtual std::string GetTimestamp() const = 0;
virtual bool GetUTurnsDefault() const = 0;
};
}
}

View File

@ -5,8 +5,11 @@
#include "engine/datafacade/datafacade_base.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "engine/geospatial_query.hpp"
#include "extractor/original_edge_data.hpp"
#include "extractor/profile_properties.hpp"
#include "extractor/query_node.hpp"
#include "contractor/query_edge.hpp"
#include "util/shared_memory_vector_wrapper.hpp"
@ -46,17 +49,17 @@ namespace engine
namespace datafacade
{
template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacade<EdgeDataT>
class InternalDataFacade final : public BaseDataFacade
{
private:
using super = BaseDataFacade<EdgeDataT>;
using super = BaseDataFacade;
using QueryGraph = util::StaticGraph<typename super::EdgeData>;
using InputEdge = typename QueryGraph::InputEdge;
using RTreeLeaf = typename super::RTreeLeaf;
using InternalRTree =
util::StaticRTree<RTreeLeaf, util::ShM<util::FixedPointCoordinate, false>::vector, false>;
using InternalGeospatialQuery = GeospatialQuery<InternalRTree, BaseDataFacade<EdgeDataT>>;
util::StaticRTree<RTreeLeaf, util::ShM<util::Coordinate, false>::vector, false>;
using InternalGeospatialQuery = GeospatialQuery<InternalRTree, BaseDataFacade>;
InternalDataFacade() {}
@ -65,17 +68,19 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
std::unique_ptr<QueryGraph> m_query_graph;
std::string m_timestamp;
std::shared_ptr<util::ShM<util::FixedPointCoordinate, false>::vector> m_coordinate_list;
std::shared_ptr<util::ShM<util::Coordinate, false>::vector> m_coordinate_list;
util::ShM<NodeID, false>::vector m_via_node_list;
util::ShM<unsigned, false>::vector m_name_ID_list;
util::ShM<extractor::TurnInstruction, false>::vector m_turn_instruction_list;
util::ShM<extractor::guidance::TurnInstruction, false>::vector m_turn_instruction_list;
util::ShM<extractor::TravelMode, false>::vector m_travel_mode_list;
util::ShM<char, false>::vector m_names_char_list;
util::ShM<bool, false>::vector m_edge_is_compressed;
util::ShM<unsigned, false>::vector m_geometry_indices;
util::ShM<extractor::CompressedEdgeContainer::CompressedEdge, false>::vector m_geometry_list;
util::ShM<bool, false>::vector m_is_core_node;
util::ShM<unsigned, false>::vector m_segment_weights;
util::ShM<uint8_t, false>::vector m_datasource_list;
util::ShM<std::string, false>::vector m_datasource_names;
extractor::ProfileProperties m_profile_properties;
boost::thread_specific_ptr<InternalRTree> m_static_rtree;
boost::thread_specific_ptr<InternalGeospatialQuery> m_geospatial_query;
@ -83,26 +88,26 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
boost::filesystem::path file_index_path;
util::RangeTable<16, false> m_name_table;
void LoadProfileProperties(const boost::filesystem::path &properties_path)
{
boost::filesystem::ifstream in_stream(properties_path);
if (!in_stream)
{
throw util::exception("Could not open " + properties_path.string() + " for reading.");
}
in_stream.read(reinterpret_cast<char*>(&m_profile_properties), sizeof(m_profile_properties));
}
void LoadTimestamp(const boost::filesystem::path &timestamp_path)
{
if (boost::filesystem::exists(timestamp_path))
util::SimpleLogger().Write() << "Loading Timestamp";
boost::filesystem::ifstream timestamp_stream(timestamp_path);
if (!timestamp_stream)
{
util::SimpleLogger().Write() << "Loading Timestamp";
boost::filesystem::ifstream timestamp_stream(timestamp_path);
if (!timestamp_stream)
{
util::SimpleLogger().Write(logWARNING) << timestamp_path << " not found";
}
getline(timestamp_stream, m_timestamp);
}
if (m_timestamp.empty())
{
m_timestamp = "n/a";
}
if (25 < m_timestamp.length())
{
m_timestamp.resize(25);
throw util::exception("Could not open " + timestamp_path.string() + " for reading.");
}
getline(timestamp_stream, m_timestamp);
}
void LoadGraph(const boost::filesystem::path &hsgr_path)
@ -133,15 +138,12 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
extractor::QueryNode current_node;
unsigned number_of_coordinates = 0;
nodes_input_stream.read((char *)&number_of_coordinates, sizeof(unsigned));
m_coordinate_list =
std::make_shared<std::vector<util::FixedPointCoordinate>>(number_of_coordinates);
m_coordinate_list = std::make_shared<std::vector<util::Coordinate>>(number_of_coordinates);
for (unsigned i = 0; i < number_of_coordinates; ++i)
{
nodes_input_stream.read((char *)&current_node, sizeof(extractor::QueryNode));
m_coordinate_list->at(i) =
util::FixedPointCoordinate(current_node.lat, current_node.lon);
BOOST_ASSERT((std::abs(m_coordinate_list->at(i).lat) >> 30) == 0);
BOOST_ASSERT((std::abs(m_coordinate_list->at(i).lon) >> 30) == 0);
m_coordinate_list->at(i) = util::Coordinate(current_node.lon, current_node.lat);
BOOST_ASSERT(m_coordinate_list->at(i).IsValid());
}
boost::filesystem::ifstream edges_input_stream(edges_file, std::ios::binary);
@ -151,9 +153,6 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
m_name_ID_list.resize(number_of_edges);
m_turn_instruction_list.resize(number_of_edges);
m_travel_mode_list.resize(number_of_edges);
m_edge_is_compressed.resize(number_of_edges);
unsigned compressed = 0;
extractor::OriginalEdgeData current_edge_data;
for (unsigned i = 0; i < number_of_edges; ++i)
@ -164,11 +163,6 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
m_name_ID_list[i] = current_edge_data.name_id;
m_turn_instruction_list[i] = current_edge_data.turn_instruction;
m_travel_mode_list[i] = current_edge_data.travel_mode;
m_edge_is_compressed[i] = current_edge_data.compressed_geometry;
if (m_edge_is_compressed[i])
{
++compressed;
}
}
}
@ -218,7 +212,41 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
if (number_of_compressed_geometries > 0)
{
geometry_stream.read((char *)&(m_geometry_list[0]),
number_of_compressed_geometries * sizeof(extractor::CompressedEdgeContainer::CompressedEdge));
number_of_compressed_geometries *
sizeof(extractor::CompressedEdgeContainer::CompressedEdge));
}
}
void LoadDatasourceInfo(const boost::filesystem::path &datasource_names_file,
const boost::filesystem::path &datasource_indexes_file)
{
boost::filesystem::ifstream datasources_stream(datasource_indexes_file, std::ios::binary);
if (!datasources_stream)
{
throw util::exception("Could not open " + datasource_indexes_file.string() + " for reading!");
}
BOOST_ASSERT(datasources_stream);
std::size_t number_of_datasources = 0;
datasources_stream.read(reinterpret_cast<char *>(&number_of_datasources),
sizeof(std::size_t));
if (number_of_datasources > 0)
{
m_datasource_list.resize(number_of_datasources);
datasources_stream.read(reinterpret_cast<char *>(&(m_datasource_list[0])),
number_of_datasources * sizeof(uint8_t));
}
boost::filesystem::ifstream datasourcenames_stream(datasource_names_file, std::ios::binary);
if (!datasourcenames_stream)
{
throw util::exception("Could not open " + datasource_names_file.string() + " for reading!");
}
BOOST_ASSERT(datasourcenames_stream);
std::string name;
while (std::getline(datasourcenames_stream, name))
{
m_datasource_names.push_back(std::move(name));
}
}
@ -227,7 +255,8 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
BOOST_ASSERT_MSG(!m_coordinate_list->empty(), "coordinates must be loaded before r-tree");
m_static_rtree.reset(new InternalRTree(ram_index_path, file_index_path, m_coordinate_list));
m_geospatial_query.reset(new InternalGeospatialQuery(*m_static_rtree, m_coordinate_list, *this));
m_geospatial_query.reset(
new InternalGeospatialQuery(*m_static_rtree, m_coordinate_list, *this));
}
void LoadStreetNames(const boost::filesystem::path &names_file)
@ -254,40 +283,35 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
m_geospatial_query.reset();
}
explicit InternalDataFacade(
const std::unordered_map<std::string, boost::filesystem::path> &server_paths)
explicit InternalDataFacade(const storage::StorageConfig& config)
{
// cache end iterator to quickly check .find against
const auto end_it = end(server_paths);
const auto file_for = [&server_paths, &end_it](const std::string &path)
{
const auto it = server_paths.find(path);
if (it == end_it || !boost::filesystem::is_regular_file(it->second))
throw util::exception("no valid " + path + " file given in ini file");
return it->second;
};
ram_index_path = file_for("ramindex");
file_index_path = file_for("fileindex");
ram_index_path = config.ram_index_path;
file_index_path = config.file_index_path;
util::SimpleLogger().Write() << "loading graph data";
LoadGraph(file_for("hsgrdata"));
LoadGraph(config.hsgr_data_path);
util::SimpleLogger().Write() << "loading edge information";
LoadNodeAndEdgeInformation(file_for("nodesdata"), file_for("edgesdata"));
LoadNodeAndEdgeInformation(config.nodes_data_path, config.edges_data_path);
util::SimpleLogger().Write() << "loading core information";
LoadCoreInformation(file_for("coredata"));
LoadCoreInformation(config.core_data_path);
util::SimpleLogger().Write() << "loading geometries";
LoadGeometries(file_for("geometries"));
LoadGeometries(config.geometries_path);
util::SimpleLogger().Write() << "loading datasource info";
LoadDatasourceInfo(config.datasource_names_path,
config.datasource_indexes_path);
util::SimpleLogger().Write() << "loading timestamp";
LoadTimestamp(file_for("timestamp"));
LoadTimestamp(config.timestamp_path);
util::SimpleLogger().Write() << "loading profile properties";
LoadProfileProperties(config.properties_path);
util::SimpleLogger().Write() << "loading street names";
LoadStreetNames(file_for("namesdata"));
LoadStreetNames(config.names_data_path);
}
// search graph access
@ -302,7 +326,7 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
NodeID GetTarget(const EdgeID e) const override final { return m_query_graph->GetTarget(e); }
EdgeDataT &GetEdgeData(const EdgeID e) const override final
EdgeData &GetEdgeData(const EdgeID e) const override final
{
return m_query_graph->GetEdgeData(e);
}
@ -334,17 +358,13 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
}
// node and edge information access
util::FixedPointCoordinate GetCoordinateOfNode(const unsigned id) const override final
util::Coordinate GetCoordinateOfNode(const unsigned id) const override final
{
return m_coordinate_list->at(id);
}
bool EdgeIsCompressed(const unsigned id) const override final
{
return m_edge_is_compressed.at(id);
}
extractor::TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const override final
extractor::guidance::TurnInstruction
GetTurnInstructionForEdgeID(const unsigned id) const override final
{
return m_turn_instruction_list.at(id);
}
@ -354,25 +374,37 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
return m_travel_mode_list.at(id);
}
std::vector<RTreeLeaf>
GetEdgesInBox(const util::FixedPointCoordinate &south_west,
const util::FixedPointCoordinate &north_east) override final
std::vector<RTreeLeaf> GetEdgesInBox(const util::Coordinate south_west,
const util::Coordinate north_east) override final
{
if (!m_static_rtree.get())
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
const util::RectangleInt2D bbox{
south_west.lon, north_east.lon, south_west.lat, north_east.lat};
const util::RectangleInt2D bbox{south_west.lon, north_east.lon, south_west.lat,
north_east.lat};
return m_geospatial_query->Search(bbox);
}
std::vector<PhantomNodeWithDistance>
NearestPhantomNodesInRange(const util::FixedPointCoordinate input_coordinate,
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
const float max_distance) override final
{
if (!m_static_rtree.get())
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance);
}
std::vector<PhantomNodeWithDistance>
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
const float max_distance,
const int bearing = 0,
const int bearing_range = 180) override final
const int bearing,
const int bearing_range) override final
{
if (!m_static_rtree.get())
{
@ -385,10 +417,37 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
}
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate,
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results) override final
{
if (!m_static_rtree.get())
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results);
}
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const int bearing = 0,
const int bearing_range = 180) override final
const double max_distance) override final
{
if (!m_static_rtree.get())
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance);
}
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const int bearing,
const int bearing_range) override final
{
if (!m_static_rtree.get())
{
@ -400,10 +459,70 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
bearing_range);
}
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const double max_distance,
const int bearing,
const int bearing_range) override final
{
if (!m_static_rtree.get())
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance,
bearing, bearing_range);
}
std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
const double max_distance) override final
{
if (!m_static_rtree.get())
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
input_coordinate, max_distance);
}
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate,
const int bearing = 0,
const int bearing_range = 180) override final
const util::Coordinate input_coordinate) override final
{
if (!m_static_rtree.get())
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
input_coordinate);
}
std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
const double max_distance,
const int bearing,
const int bearing_range) override final
{
if (!m_static_rtree.get())
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
input_coordinate, max_distance, bearing, bearing_range);
}
std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
const int bearing,
const int bearing_range) override final
{
if (!m_static_rtree.get())
{
@ -422,7 +541,7 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
return m_name_ID_list.at(id);
}
std::string get_name_for_id(const unsigned name_id) const override final
std::string GetNameForID(const unsigned name_id) const override final
{
if (std::numeric_limits<unsigned>::max() == name_id)
{
@ -468,22 +587,69 @@ template <class EdgeDataT> class InternalDataFacade final : public BaseDataFacad
result_nodes.clear();
result_nodes.reserve(end - begin);
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge){ result_nodes.emplace_back(edge.node_id); });
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
{
result_nodes.emplace_back(edge.node_id);
});
}
virtual void GetUncompressedWeights(const EdgeID id,
std::vector<EdgeWeight> &result_weights) const override final
virtual void
GetUncompressedWeights(const EdgeID id,
std::vector<EdgeWeight> &result_weights) const override final
{
const unsigned begin = m_geometry_indices.at(id);
const unsigned end = m_geometry_indices.at(id + 1);
result_weights.clear();
result_weights.reserve(end - begin);
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge){ result_weights.emplace_back(edge.weight); });
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
{
result_weights.emplace_back(edge.weight);
});
}
// Returns the data source ids that were used to supply the edge
// weights.
virtual void
GetUncompressedDatasources(const EdgeID id,
std::vector<uint8_t> &result_datasources) const override final
{
const unsigned begin = m_geometry_indices.at(id);
const unsigned end = m_geometry_indices.at(id + 1);
result_datasources.clear();
result_datasources.reserve(end - begin);
// If there was no datasource info, return an array of 0's.
if (m_datasource_list.empty())
{
for (unsigned i = 0; i < end - begin; ++i)
{
result_datasources.push_back(0);
}
}
else
{
std::for_each(m_datasource_list.begin() + begin, m_datasource_list.begin() + end,
[&](const uint8_t &datasource_id)
{
result_datasources.push_back(datasource_id);
});
}
}
virtual std::string GetDatasourceName(const uint8_t datasource_name_id) const override final
{
BOOST_ASSERT(m_datasource_names.size() >= 1);
BOOST_ASSERT(m_datasource_names.size() > datasource_name_id);
return m_datasource_names[datasource_name_id];
}
std::string GetTimestamp() const override final { return m_timestamp; }
bool GetUTurnsDefault() const override final { return m_profile_properties.allow_u_turn_at_via; }
};
}
}

View File

@ -7,6 +7,9 @@
#include "storage/shared_datatype.hpp"
#include "storage/shared_memory.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "extractor/profile_properties.hpp"
#include "engine/geospatial_query.hpp"
#include "util/range_table.hpp"
#include "util/static_graph.hpp"
@ -36,12 +39,11 @@ namespace engine
namespace datafacade
{
template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<EdgeDataT>
class SharedDataFacade final : public BaseDataFacade
{
private:
using EdgeData = EdgeDataT;
using super = BaseDataFacade<EdgeData>;
using super = BaseDataFacade;
using QueryGraph = util::StaticGraph<EdgeData, true>;
using GraphNode = typename QueryGraph::NodeArrayEntry;
using GraphEdge = typename QueryGraph::EdgeArrayEntry;
@ -49,8 +51,8 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
using InputEdge = typename QueryGraph::InputEdge;
using RTreeLeaf = typename super::RTreeLeaf;
using SharedRTree =
util::StaticRTree<RTreeLeaf, util::ShM<util::FixedPointCoordinate, true>::vector, true>;
using SharedGeospatialQuery = GeospatialQuery<SharedRTree, BaseDataFacade<EdgeDataT>>;
util::StaticRTree<RTreeLeaf, util::ShM<util::Coordinate, true>::vector, true>;
using SharedGeospatialQuery = GeospatialQuery<SharedRTree, BaseDataFacade>;
using TimeStampedRTreePair = std::pair<unsigned, std::shared_ptr<SharedRTree>>;
using RTreeNode = typename SharedRTree::TreeNode;
@ -67,18 +69,23 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
std::unique_ptr<storage::SharedMemory> m_layout_memory;
std::unique_ptr<storage::SharedMemory> m_large_memory;
std::string m_timestamp;
extractor::ProfileProperties* m_profile_properties;
std::shared_ptr<util::ShM<util::FixedPointCoordinate, true>::vector> m_coordinate_list;
std::shared_ptr<util::ShM<util::Coordinate, true>::vector> m_coordinate_list;
util::ShM<NodeID, true>::vector m_via_node_list;
util::ShM<unsigned, true>::vector m_name_ID_list;
util::ShM<extractor::TurnInstruction, true>::vector m_turn_instruction_list;
util::ShM<extractor::guidance::TurnInstruction, true>::vector m_turn_instruction_list;
util::ShM<extractor::TravelMode, true>::vector m_travel_mode_list;
util::ShM<char, true>::vector m_names_char_list;
util::ShM<unsigned, true>::vector m_name_begin_indices;
util::ShM<bool, true>::vector m_edge_is_compressed;
util::ShM<unsigned, true>::vector m_geometry_indices;
util::ShM<extractor::CompressedEdgeContainer::CompressedEdge, true>::vector m_geometry_list;
util::ShM<bool, true>::vector m_is_core_node;
util::ShM<uint8_t, true>::vector m_datasource_list;
util::ShM<char, true>::vector m_datasource_name_data;
util::ShM<std::size_t, true>::vector m_datasource_name_offsets;
util::ShM<std::size_t, true>::vector m_datasource_name_lengths;
boost::thread_specific_ptr<std::pair<unsigned, std::shared_ptr<SharedRTree>>> m_static_rtree;
boost::thread_specific_ptr<SharedGeospatialQuery> m_geospatial_query;
@ -93,6 +100,12 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
util::SimpleLogger().Write() << "set checksum: " << m_check_sum;
}
void LoadProfileProperties()
{
m_profile_properties =
data_layout->GetBlockPtr<extractor::ProfileProperties>(shared_memory, storage::SharedDataLayout::PROPERTIES);
}
void LoadTimestamp()
{
auto timestamp_ptr =
@ -135,9 +148,9 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
void LoadNodeAndEdgeInformation()
{
auto coordinate_list_ptr = data_layout->GetBlockPtr<util::FixedPointCoordinate>(
auto coordinate_list_ptr = data_layout->GetBlockPtr<util::Coordinate>(
shared_memory, storage::SharedDataLayout::COORDINATE_LIST);
m_coordinate_list = util::make_unique<util::ShM<util::FixedPointCoordinate, true>::vector>(
m_coordinate_list = util::make_unique<util::ShM<util::Coordinate, true>::vector>(
coordinate_list_ptr,
data_layout->num_entries[storage::SharedDataLayout::COORDINATE_LIST]);
@ -147,11 +160,13 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
travel_mode_list_ptr, data_layout->num_entries[storage::SharedDataLayout::TRAVEL_MODE]);
m_travel_mode_list = std::move(travel_mode_list);
auto turn_instruction_list_ptr = data_layout->GetBlockPtr<extractor::TurnInstruction>(
shared_memory, storage::SharedDataLayout::TURN_INSTRUCTION);
typename util::ShM<extractor::TurnInstruction, true>::vector turn_instruction_list(
turn_instruction_list_ptr,
data_layout->num_entries[storage::SharedDataLayout::TURN_INSTRUCTION]);
auto turn_instruction_list_ptr =
data_layout->GetBlockPtr<extractor::guidance::TurnInstruction>(
shared_memory, storage::SharedDataLayout::TURN_INSTRUCTION);
typename util::ShM<extractor::guidance::TurnInstruction, true>::vector
turn_instruction_list(
turn_instruction_list_ptr,
data_layout->num_entries[storage::SharedDataLayout::TURN_INSTRUCTION]);
m_turn_instruction_list = std::move(turn_instruction_list);
auto name_id_list_ptr = data_layout->GetBlockPtr<unsigned>(
@ -207,13 +222,6 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
void LoadGeometries()
{
auto geometries_compressed_ptr = data_layout->GetBlockPtr<unsigned>(
shared_memory, storage::SharedDataLayout::GEOMETRIES_INDICATORS);
typename util::ShM<bool, true>::vector edge_is_compressed(
geometries_compressed_ptr,
data_layout->num_entries[storage::SharedDataLayout::GEOMETRIES_INDICATORS]);
m_edge_is_compressed = std::move(edge_is_compressed);
auto geometries_index_ptr = data_layout->GetBlockPtr<unsigned>(
shared_memory, storage::SharedDataLayout::GEOMETRIES_INDEX);
typename util::ShM<unsigned, true>::vector geometry_begin_indices(
@ -223,11 +231,39 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
auto geometries_list_ptr =
data_layout->GetBlockPtr<extractor::CompressedEdgeContainer::CompressedEdge>(
shared_memory, storage::SharedDataLayout::GEOMETRIES_LIST);
typename util::ShM<extractor::CompressedEdgeContainer::CompressedEdge, true>::vector geometry_list(
geometries_list_ptr,
data_layout->num_entries[storage::SharedDataLayout::GEOMETRIES_LIST]);
shared_memory, storage::SharedDataLayout::GEOMETRIES_LIST);
typename util::ShM<extractor::CompressedEdgeContainer::CompressedEdge, true>::vector
geometry_list(geometries_list_ptr,
data_layout->num_entries[storage::SharedDataLayout::GEOMETRIES_LIST]);
m_geometry_list = std::move(geometry_list);
auto datasources_list_ptr = data_layout->GetBlockPtr<uint8_t>(
shared_memory, storage::SharedDataLayout::DATASOURCES_LIST);
typename util::ShM<uint8_t, true>::vector datasources_list(
datasources_list_ptr,
data_layout->num_entries[storage::SharedDataLayout::DATASOURCES_LIST]);
m_datasource_list = std::move(datasources_list);
auto datasource_name_data_ptr = data_layout->GetBlockPtr<char>(
shared_memory, storage::SharedDataLayout::DATASOURCE_NAME_DATA);
typename util::ShM<char, true>::vector datasource_name_data(
datasource_name_data_ptr,
data_layout->num_entries[storage::SharedDataLayout::DATASOURCE_NAME_DATA]);
m_datasource_name_data = std::move(datasource_name_data);
auto datasource_name_offsets_ptr = data_layout->GetBlockPtr<std::size_t>(
shared_memory, storage::SharedDataLayout::DATASOURCE_NAME_OFFSETS);
typename util::ShM<std::size_t, true>::vector datasource_name_offsets(
datasource_name_offsets_ptr,
data_layout->num_entries[storage::SharedDataLayout::DATASOURCE_NAME_OFFSETS]);
m_datasource_name_offsets = std::move(datasource_name_offsets);
auto datasource_name_lengths_ptr = data_layout->GetBlockPtr<std::size_t>(
shared_memory, storage::SharedDataLayout::DATASOURCE_NAME_LENGTHS);
typename util::ShM<std::size_t, true>::vector datasource_name_lengths(
datasource_name_lengths_ptr,
data_layout->num_entries[storage::SharedDataLayout::DATASOURCE_NAME_LENGTHS]);
m_datasource_name_lengths = std::move(datasource_name_lengths);
}
public:
@ -244,7 +280,8 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
}
data_timestamp_ptr = static_cast<storage::SharedDataTimestamp *>(
storage::makeSharedMemory(storage::CURRENT_REGIONS,
sizeof(storage::SharedDataTimestamp), false, false)->Ptr());
sizeof(storage::SharedDataTimestamp), false, false)
->Ptr());
CURRENT_LAYOUT = storage::LAYOUT_NONE;
CURRENT_DATA = storage::DATA_NONE;
CURRENT_TIMESTAMP = 0;
@ -314,9 +351,10 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
LoadViaNodeList();
LoadNames();
LoadCoreInformation();
LoadProfileProperties();
util::SimpleLogger().Write()
<< "number of geometries: " << m_coordinate_list->size();
util::SimpleLogger().Write() << "number of geometries: "
<< m_coordinate_list->size();
for (unsigned i = 0; i < m_coordinate_list->size(); ++i)
{
if (!GetCoordinateOfNode(i).IsValid())
@ -341,7 +379,7 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
NodeID GetTarget(const EdgeID e) const override final { return m_query_graph->GetTarget(e); }
EdgeDataT &GetEdgeData(const EdgeID e) const override final
EdgeData &GetEdgeData(const EdgeID e) const override final
{
return m_query_graph->GetEdgeData(e);
}
@ -373,16 +411,11 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
}
// node and edge information access
util::FixedPointCoordinate GetCoordinateOfNode(const NodeID id) const override final
util::Coordinate GetCoordinateOfNode(const NodeID id) const override final
{
return m_coordinate_list->at(id);
}
virtual bool EdgeIsCompressed(const unsigned id) const override final
{
return m_edge_is_compressed.at(id);
}
virtual void GetUncompressedGeometry(const EdgeID id,
std::vector<NodeID> &result_nodes) const override final
{
@ -391,19 +424,27 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
result_nodes.clear();
result_nodes.reserve(end - begin);
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge){ result_nodes.emplace_back(edge.node_id); });
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
{
result_nodes.emplace_back(edge.node_id);
});
}
virtual void GetUncompressedWeights(const EdgeID id,
std::vector<EdgeWeight> &result_weights) const override final
virtual void
GetUncompressedWeights(const EdgeID id,
std::vector<EdgeWeight> &result_weights) const override final
{
const unsigned begin = m_geometry_indices.at(id);
const unsigned end = m_geometry_indices.at(id + 1);
result_weights.clear();
result_weights.reserve(end - begin);
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge){ result_weights.emplace_back(edge.weight); });
std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end,
[&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge)
{
result_weights.emplace_back(edge.weight);
});
}
virtual unsigned GetGeometryIndexForEdgeID(const unsigned id) const override final
@ -411,7 +452,8 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
return m_via_node_list.at(id);
}
extractor::TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const override final
extractor::guidance::TurnInstruction
GetTurnInstructionForEdgeID(const unsigned id) const override final
{
return m_turn_instruction_list.at(id);
}
@ -421,25 +463,37 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
return m_travel_mode_list.at(id);
}
std::vector<RTreeLeaf>
GetEdgesInBox(const util::FixedPointCoordinate &south_west,
const util::FixedPointCoordinate &north_east) override final
std::vector<RTreeLeaf> GetEdgesInBox(const util::Coordinate south_west,
const util::Coordinate north_east) override final
{
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
const util::RectangleInt2D bbox{
south_west.lon, north_east.lon, south_west.lat, north_east.lat};
const util::RectangleInt2D bbox{south_west.lon, north_east.lon, south_west.lat,
north_east.lat};
return m_geospatial_query->Search(bbox);
}
std::vector<PhantomNodeWithDistance>
NearestPhantomNodesInRange(const util::FixedPointCoordinate input_coordinate,
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
const float max_distance) override final
{
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodesInRange(input_coordinate, max_distance);
}
std::vector<PhantomNodeWithDistance>
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
const float max_distance,
const int bearing = 0,
const int bearing_range = 180) override final
const int bearing,
const int bearing_range) override final
{
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
{
@ -452,10 +506,37 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
}
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate,
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results) override final
{
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results);
}
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const int bearing = 0,
const int bearing_range = 180) override final
const double max_distance) override final
{
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance);
}
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const int bearing,
const int bearing_range) override final
{
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
{
@ -467,10 +548,70 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
bearing_range);
}
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const double max_distance,
const int bearing,
const int bearing_range) override final
{
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodes(input_coordinate, max_results, max_distance,
bearing, bearing_range);
}
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate,
const int bearing = 0,
const int bearing_range = 180) override final
const util::Coordinate input_coordinate) override final
{
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
input_coordinate);
}
std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
const double max_distance) override final
{
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
input_coordinate, max_distance);
}
std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
const double max_distance,
const int bearing,
const int bearing_range) override final
{
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
{
LoadRTree();
BOOST_ASSERT(m_geospatial_query.get());
}
return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent(
input_coordinate, max_distance, bearing, bearing_range);
}
std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
const int bearing,
const int bearing_range) override final
{
if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first)
{
@ -489,7 +630,7 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
return m_name_ID_list.at(id);
}
std::string get_name_for_id(const unsigned name_id) const override final
std::string GetNameForID(const unsigned name_id) const override final
{
if (std::numeric_limits<unsigned>::max() == name_id)
{
@ -520,7 +661,54 @@ template <class EdgeDataT> class SharedDataFacade final : public BaseDataFacade<
virtual std::size_t GetCoreSize() const override final { return m_is_core_node.size(); }
// Returns the data source ids that were used to supply the edge
// weights.
virtual void
GetUncompressedDatasources(const EdgeID id,
std::vector<uint8_t> &result_datasources) const override final
{
const unsigned begin = m_geometry_indices.at(id);
const unsigned end = m_geometry_indices.at(id + 1);
result_datasources.clear();
result_datasources.reserve(end - begin);
// If there was no datasource info, return an array of 0's.
if (m_datasource_list.empty())
{
for (unsigned i = 0; i < end - begin; ++i)
{
result_datasources.push_back(0);
}
}
else
{
std::for_each(m_datasource_list.begin() + begin, m_datasource_list.begin() + end,
[&](const uint8_t &datasource_id)
{
result_datasources.push_back(datasource_id);
});
}
}
virtual std::string GetDatasourceName(const uint8_t datasource_name_id) const override final
{
BOOST_ASSERT(m_datasource_name_offsets.size() >= 1);
BOOST_ASSERT(m_datasource_name_offsets.size() > datasource_name_id);
std::string result;
result.reserve(m_datasource_name_lengths[datasource_name_id]);
std::copy(m_datasource_name_data.begin() + m_datasource_name_offsets[datasource_name_id],
m_datasource_name_data.begin() + m_datasource_name_offsets[datasource_name_id] +
m_datasource_name_lengths[datasource_name_id],
std::back_inserter(result));
return result;
}
std::string GetTimestamp() const override final { return m_timestamp; }
bool GetUTurnsDefault() const override final { return m_profile_properties->allow_u_turn_at_via; }
};
}
}

View File

@ -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);
}
}
}

View File

@ -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 &parameters, util::json::Object &result);
Status Table(const api::TableParameters &parameters, util::json::Object &result);
Status Nearest(const api::NearestParameters &parameters, util::json::Object &result);
Status Trip(const api::TripParameters &parameters, util::json::Object &result);
Status Match(const api::MatchParameters &parameters, util::json::Object &result);
Status Tile(const api::TileParameters &parameters, 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;
};
}
}

View File

@ -28,9 +28,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ENGINE_CONFIG_HPP
#define ENGINE_CONFIG_HPP
#include "storage/storage_config.hpp"
#include <boost/filesystem/path.hpp>
#include <unordered_map>
#include <string>
namespace osrm
@ -41,14 +42,15 @@ namespace engine
struct EngineConfig
{
std::unordered_map<std::string, boost::filesystem::path> server_paths;
bool IsValid() const;
storage::StorageConfig storage_config;
int max_locations_trip = -1;
int max_locations_viaroute = -1;
int max_locations_distance_table = -1;
int max_locations_map_matching = -1;
bool use_shared_memory = true;
};
}
}

View File

@ -28,7 +28,9 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
using CoordinateList = typename RTreeT::CoordinateList;
public:
GeospatialQuery(RTreeT &rtree_, std::shared_ptr<CoordinateList> coordinates_, DataFacadeT &datafacade_)
GeospatialQuery(RTreeT &rtree_,
std::shared_ptr<CoordinateList> coordinates_,
DataFacadeT &datafacade_)
: rtree(rtree_), coordinates(std::move(coordinates_)), datafacade(datafacade_)
{
}
@ -41,10 +43,28 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
// Returns nearest PhantomNodes in the given bearing range within max_distance.
// Does not filter by small/big component!
std::vector<PhantomNodeWithDistance>
NearestPhantomNodesInRange(const util::FixedPointCoordinate input_coordinate,
NearestPhantomNodesInRange(const util::Coordinate input_coordinate, const double max_distance)
{
auto results = rtree.Nearest(input_coordinate,
[](const EdgeData &)
{
return std::make_pair(true, true);
},
[max_distance](const std::size_t, const double min_dist)
{
return min_dist > max_distance;
});
return MakePhantomNodes(input_coordinate, results);
}
// Returns nearest PhantomNodes in the given bearing range within max_distance.
// Does not filter by small/big component!
std::vector<PhantomNodeWithDistance>
NearestPhantomNodesInRange(const util::Coordinate input_coordinate,
const double max_distance,
const int bearing = 0,
const int bearing_range = 180)
const int bearing,
const int bearing_range)
{
auto results =
rtree.Nearest(input_coordinate,
@ -63,10 +83,10 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
// Returns max_results nearest PhantomNodes in the given bearing range.
// Does not filter by small/big component!
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::FixedPointCoordinate input_coordinate,
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const int bearing = 0,
const int bearing_range = 180)
const int bearing,
const int bearing_range)
{
auto results = rtree.Nearest(input_coordinate,
[this, bearing, bearing_range](const EdgeData &data)
@ -81,12 +101,144 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
return MakePhantomNodes(input_coordinate, results);
}
// Returns max_results nearest PhantomNodes in the given bearing range within the maximum
// distance.
// Does not filter by small/big component!
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const double max_distance,
const int bearing,
const int bearing_range)
{
auto results = rtree.Nearest(
input_coordinate,
[this, bearing, bearing_range](const EdgeData &data)
{
return checkSegmentBearing(data, bearing, bearing_range);
},
[max_results, max_distance](const std::size_t num_results, const double min_dist)
{
return num_results >= max_results || min_dist > max_distance;
});
return MakePhantomNodes(input_coordinate, results);
}
// Returns max_results nearest PhantomNodes.
// Does not filter by small/big component!
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate, const unsigned max_results)
{
auto results = rtree.Nearest(input_coordinate,
[](const EdgeData &)
{
return std::make_pair(true, true);
},
[max_results](const std::size_t num_results, const double)
{
return num_results >= max_results;
});
return MakePhantomNodes(input_coordinate, results);
}
// Returns max_results nearest PhantomNodes in the given max distance.
// Does not filter by small/big component!
std::vector<PhantomNodeWithDistance>
NearestPhantomNodes(const util::Coordinate input_coordinate,
const unsigned max_results,
const double max_distance)
{
auto results = rtree.Nearest(
input_coordinate,
[](const EdgeData &)
{
return std::make_pair(true, true);
},
[max_results, max_distance](const std::size_t num_results, const double min_dist)
{
return num_results >= max_results || min_dist > max_distance;
});
return MakePhantomNodes(input_coordinate, results);
}
// Returns the nearest phantom node. If this phantom node is not from a big component
// a second phantom node is return that is the nearest coordinate in a big component.
std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
const double max_distance)
{
bool has_small_component = false;
bool has_big_component = false;
auto results = rtree.Nearest(
input_coordinate,
[&has_big_component, &has_small_component](const EdgeData &data)
{
auto use_segment =
(!has_small_component || (!has_big_component && !data.component.is_tiny));
auto use_directions = std::make_pair(use_segment, use_segment);
has_big_component = has_big_component || !data.component.is_tiny;
has_small_component = has_small_component || data.component.is_tiny;
return use_directions;
},
[&has_big_component, max_distance](const std::size_t num_results, const double min_dist)
{
return (num_results > 0 && has_big_component) || min_dist > max_distance;
});
if (results.size() == 0)
{
return std::make_pair(PhantomNode{}, PhantomNode{});
}
BOOST_ASSERT(results.size() == 1 || results.size() == 2);
return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node,
MakePhantomNode(input_coordinate, results.back()).phantom_node);
}
// Returns the nearest phantom node. If this phantom node is not from a big component
// a second phantom node is return that is the nearest coordinate in a big component.
std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate)
{
bool has_small_component = false;
bool has_big_component = false;
auto results =
rtree.Nearest(input_coordinate,
[&has_big_component, &has_small_component](const EdgeData &data)
{
auto use_segment = (!has_small_component ||
(!has_big_component && !data.component.is_tiny));
auto use_directions = std::make_pair(use_segment, use_segment);
has_big_component = has_big_component || !data.component.is_tiny;
has_small_component = has_small_component || data.component.is_tiny;
return use_directions;
},
[&has_big_component](const std::size_t num_results, const double)
{
return num_results > 0 && has_big_component;
});
if (results.size() == 0)
{
return std::make_pair(PhantomNode{}, PhantomNode{});
}
BOOST_ASSERT(results.size() == 1 || results.size() == 2);
return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node,
MakePhantomNode(input_coordinate, results.back()).phantom_node);
}
// Returns the nearest phantom node. If this phantom node is not from a big component
// a second phantom node is return that is the nearest coordinate in a big component.
std::pair<PhantomNode, PhantomNode> NearestPhantomNodeWithAlternativeFromBigComponent(
const util::FixedPointCoordinate input_coordinate,
const int bearing = 0,
const int bearing_range = 180)
const util::Coordinate input_coordinate, const int bearing, const int bearing_range)
{
bool has_small_component = false;
bool has_big_component = false;
@ -126,9 +278,55 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
MakePhantomNode(input_coordinate, results.back()).phantom_node);
}
// Returns the nearest phantom node. If this phantom node is not from a big component
// a second phantom node is return that is the nearest coordinate in a big component.
std::pair<PhantomNode, PhantomNode>
NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate,
const double max_distance,
const int bearing,
const int bearing_range)
{
bool has_small_component = false;
bool has_big_component = false;
auto results = rtree.Nearest(
input_coordinate,
[this, bearing, bearing_range, &has_big_component, &has_small_component](
const EdgeData &data)
{
auto use_segment =
(!has_small_component || (!has_big_component && !data.component.is_tiny));
auto use_directions = std::make_pair(use_segment, use_segment);
if (use_segment)
{
use_directions = checkSegmentBearing(data, bearing, bearing_range);
if (use_directions.first || use_directions.second)
{
has_big_component = has_big_component || !data.component.is_tiny;
has_small_component = has_small_component || data.component.is_tiny;
}
}
return use_directions;
},
[&has_big_component, max_distance](const std::size_t num_results, const double min_dist)
{
return (num_results > 0 && has_big_component) || min_dist > max_distance;
});
if (results.size() == 0)
{
return std::make_pair(PhantomNode{}, PhantomNode{});
}
BOOST_ASSERT(results.size() > 0);
return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node,
MakePhantomNode(input_coordinate, results.back()).phantom_node);
}
private:
std::vector<PhantomNodeWithDistance>
MakePhantomNodes(const util::FixedPointCoordinate input_coordinate,
MakePhantomNodes(const util::Coordinate input_coordinate,
const std::vector<EdgeData> &results) const
{
std::vector<PhantomNodeWithDistance> distance_and_phantoms(results.size());
@ -140,10 +338,10 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
return distance_and_phantoms;
}
PhantomNodeWithDistance MakePhantomNode(const util::FixedPointCoordinate input_coordinate,
PhantomNodeWithDistance MakePhantomNode(const util::Coordinate input_coordinate,
const EdgeData &data) const
{
util::FixedPointCoordinate point_on_segment;
util::Coordinate point_on_segment;
double ratio;
const auto current_perpendicular_distance =
util::coordinate_calculation::perpendicularDistance(
@ -156,10 +354,11 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
int forward_offset = 0, forward_weight = 0;
int reverse_offset = 0, reverse_weight = 0;
if (data.forward_packed_geometry_id != SPECIAL_EDGEID) {
if (data.forward_packed_geometry_id != SPECIAL_EDGEID)
{
std::vector<EdgeWeight> forward_weight_vector;
datafacade.GetUncompressedWeights(data.forward_packed_geometry_id,
forward_weight_vector);
forward_weight_vector);
for (std::size_t i = 0; i < data.fwd_segment_position; i++)
{
forward_offset += forward_weight_vector[i];
@ -167,31 +366,40 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
forward_weight = forward_weight_vector[data.fwd_segment_position];
}
if (data.reverse_packed_geometry_id != SPECIAL_EDGEID) {
if (data.reverse_packed_geometry_id != SPECIAL_EDGEID)
{
std::vector<EdgeWeight> reverse_weight_vector;
datafacade.GetUncompressedWeights(data.reverse_packed_geometry_id,
reverse_weight_vector);
BOOST_ASSERT(data.fwd_segment_position < reverse_weight_vector.size());
for (std::size_t i = 0; i < reverse_weight_vector.size() - data.fwd_segment_position - 1; i++)
for (std::size_t i = 0;
i < reverse_weight_vector.size() - data.fwd_segment_position - 1; i++)
{
reverse_offset += reverse_weight_vector[i];
}
reverse_weight = reverse_weight_vector[reverse_weight_vector.size() -
data.fwd_segment_position - 1];
reverse_weight =
reverse_weight_vector[reverse_weight_vector.size() - data.fwd_segment_position - 1];
}
ratio = std::min(1.0, std::max(0.0, ratio));
if (SPECIAL_NODEID != data.forward_edge_based_node_id) {
if (SPECIAL_NODEID != data.forward_edge_based_node_id)
{
forward_weight *= ratio;
}
if (SPECIAL_NODEID != data.reverse_edge_based_node_id) {
if (SPECIAL_NODEID != data.reverse_edge_based_node_id)
{
reverse_weight *= 1.0 - ratio;
}
auto transformed = PhantomNodeWithDistance{PhantomNode{data, forward_weight, forward_offset,
reverse_weight, reverse_offset, point_on_segment},
auto transformed = PhantomNodeWithDistance{PhantomNode{data,
forward_weight,
forward_offset,
reverse_weight,
reverse_offset,
point_on_segment,
input_coordinate},
current_perpendicular_distance};
return transformed;

View File

@ -0,0 +1,81 @@
#ifndef ENGINE_GUIDANCE_ASSEMBLE_GEOMETRY_HPP
#define ENGINE_GUIDANCE_ASSEMBLE_GEOMETRY_HPP
#include "engine/internal_route_result.hpp"
#include "engine/phantom_node.hpp"
#include "engine/guidance/route_step.hpp"
#include "engine/guidance/leg_geometry.hpp"
#include "engine/guidance/toolkit.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/coordinate.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "extractor/travel_mode.hpp"
#include <vector>
#include <utility>
namespace osrm
{
namespace engine
{
namespace guidance
{
// Extracts the geometry for each segment and calculates the traveled distance
// Combines the geometry form the phantom node with the PathData
// to the full route geometry.
//
// turn 0 1 2 3 4
// s...x...y...z...t
// |---|segment 0
// |---| segment 1
// |---| segment 2
// |---| segment 3
template <typename DataFacadeT>
LegGeometry assembleGeometry(const DataFacadeT &facade,
const std::vector<PathData> &leg_data,
const PhantomNode &source_node,
const PhantomNode &target_node)
{
LegGeometry geometry;
// segment 0 first and last
geometry.segment_offsets.push_back(0);
geometry.locations.push_back(source_node.location);
auto current_distance = 0.;
auto prev_coordinate = geometry.locations.front();
for (const auto &path_point : leg_data)
{
auto coordinate = facade.GetCoordinateOfNode(path_point.turn_via_node);
current_distance +=
util::coordinate_calculation::haversineDistance(prev_coordinate, coordinate);
// all changes to this check have to be matched with assemble_steps
if (path_point.turn_instruction.type != extractor::guidance::TurnType::NoTurn)
{
geometry.segment_distances.push_back(current_distance);
geometry.segment_offsets.push_back(geometry.locations.size());
current_distance = 0.;
}
prev_coordinate = coordinate;
geometry.locations.push_back(std::move(coordinate));
}
current_distance +=
util::coordinate_calculation::haversineDistance(prev_coordinate, target_node.location);
// segment leading to the target node
geometry.segment_distances.push_back(current_distance);
geometry.segment_offsets.push_back(geometry.locations.size());
geometry.locations.push_back(target_node.location);
BOOST_ASSERT(geometry.segment_distances.size() == geometry.segment_offsets.size() - 1);
BOOST_ASSERT(geometry.locations.size() > geometry.segment_distances.size());
return geometry;
}
}
}
}
#endif

View File

@ -0,0 +1,159 @@
#ifndef ENGINE_GUIDANCE_ASSEMBLE_LEG_HPP_
#define ENGINE_GUIDANCE_ASSEMBLE_LEG_HPP_
#include "engine/guidance/route_leg.hpp"
#include "engine/guidance/route_step.hpp"
#include "engine/guidance/leg_geometry.hpp"
#include "engine/internal_route_result.hpp"
#include <cstddef>
#include <cstdint>
#include <vector>
#include <array>
#include <algorithm>
namespace osrm
{
namespace engine
{
namespace guidance
{
namespace detail
{
const constexpr std::size_t MAX_USED_SEGMENTS = 2;
struct NamedSegment
{
double duration;
std::uint32_t position;
std::uint32_t name_id;
};
template <std::size_t SegmentNumber>
std::array<std::uint32_t, SegmentNumber> summarizeRoute(const std::vector<PathData> &route_data)
{
// merges segments with same name id
const auto collapse_segments = [](std::vector<NamedSegment> &segments)
{
auto out = segments.begin();
auto end = segments.end();
for (auto in = segments.begin(); in != end; ++in)
{
if (in->name_id == out->name_id)
{
out->duration += in->duration;
}
else
{
++out;
BOOST_ASSERT(out != end);
*out = *in;
}
}
return out;
};
std::vector<NamedSegment> segments(route_data.size());
std::uint32_t index = 0;
std::transform(
route_data.begin(), route_data.end(), segments.begin(), [&index](const PathData &point)
{
return NamedSegment{point.duration_until_turn / 10.0, index++, point.name_id};
});
// this makes sure that the segment with the lowest position comes first
std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs)
{
return lhs.name_id < rhs.name_id ||
(lhs.name_id == rhs.name_id && lhs.position < rhs.position);
});
auto new_end = collapse_segments(segments);
segments.resize(new_end - segments.begin());
// sort descending
std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs)
{
return lhs.duration > rhs.duration;
});
// make sure the segments are sorted by position
segments.resize(std::min(segments.size(), SegmentNumber));
std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs)
{
return lhs.position < rhs.position;
});
std::array<std::uint32_t, SegmentNumber> summary;
std::fill(summary.begin(), summary.end(), 0);
std::transform(segments.begin(), segments.end(), summary.begin(),
[](const NamedSegment &segment)
{
return segment.name_id;
});
return summary;
}
}
template <typename DataFacadeT>
RouteLeg assembleLeg(const DataFacadeT &facade,
const std::vector<PathData> &route_data,
const LegGeometry &leg_geometry,
const PhantomNode &target_node,
const bool target_traversed_in_reverse)
{
const auto target_duration =
(target_traversed_in_reverse ? target_node.reverse_weight
: target_node.forward_weight) /
10.;
auto distance = std::accumulate(leg_geometry.segment_distances.begin(),
leg_geometry.segment_distances.end(), 0.);
auto duration = std::accumulate(route_data.begin(), route_data.end(), 0.,
[](const double sum, const PathData &data)
{
return sum + data.duration_until_turn;
}) /
10.;
// s
// |
// Given a route a---b---c where there is a right turn at b.
// |
// d
// |--t
// e
// (a, b, c) gets compressed to (a,c)
// (c, d, e) gets compressed to (c,e)
// The duration of the turn (a,c) -> (c,e) will be the duration of (a,c) (e.g. the duration
// of (a,b,c)).
// The phantom node of s will contain:
// `forward_weight`: duration of (a,s)
// `forward_offset`: 0 (its the first segment)
// The phantom node of t will contain:
// `forward_weight`: duration of (d,t)
// `forward_offset`: duration of (c, d)
// path_data will have entries for (s,b), (b, c), (c, d) but (d, t) is only
// caputed by the phantom node. So we need to add the target duration here.
duration = duration + target_duration;
auto summary_array = detail::summarizeRoute<detail::MAX_USED_SEGMENTS>(route_data);
BOOST_ASSERT(detail::MAX_USED_SEGMENTS > 0);
BOOST_ASSERT(summary_array.begin() != summary_array.end());
std::string summary =
std::accumulate(std::next(summary_array.begin()), summary_array.end(),
facade.GetNameForID(summary_array.front()),
[&facade](std::string previous, const std::uint32_t name_id)
{
if (name_id != 0)
{
previous += ", " + facade.GetNameForID(name_id);
}
return previous;
});
return RouteLeg{duration, distance, summary, {}};
}
} // namespace guidance
} // namespace engine
} // namespace osrm
#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_

View 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

View 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

View File

@ -0,0 +1,184 @@
#ifndef ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_
#define ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_
#include "engine/guidance/route_step.hpp"
#include "engine/guidance/step_maneuver.hpp"
#include "engine/guidance/leg_geometry.hpp"
#include "engine/guidance/toolkit.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "engine/internal_route_result.hpp"
#include "engine/phantom_node.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/coordinate.hpp"
#include "util/bearing.hpp"
#include "extractor/travel_mode.hpp"
#include <vector>
#include <boost/optional.hpp>
namespace osrm
{
namespace engine
{
namespace guidance
{
namespace detail
{
StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction,
const LegGeometry &leg_geometry,
const std::size_t segment_index);
StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instruction,
const WaypointType waypoint_type,
const LegGeometry &leg_geometry);
} // ns detail
template <typename DataFacadeT>
std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
const std::vector<PathData> &leg_data,
const LegGeometry &leg_geometry,
const PhantomNode &source_node,
const PhantomNode &target_node,
const bool source_traversed_in_reverse,
const bool target_traversed_in_reverse)
{
const double constexpr ZERO_DURATION = 0., ZERO_DISTANCE = 0.;
const EdgeWeight source_duration =
source_traversed_in_reverse ? source_node.reverse_weight : source_node.forward_weight;
const auto source_mode = source_traversed_in_reverse ? source_node.backward_travel_mode
: source_node.forward_travel_mode;
const EdgeWeight target_duration =
target_traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight;
const auto target_mode = target_traversed_in_reverse ? target_node.backward_travel_mode
: target_node.forward_travel_mode;
const auto number_of_segments = leg_geometry.GetNumberOfSegments();
std::vector<RouteStep> steps;
steps.reserve(number_of_segments);
std::size_t segment_index = 0;
BOOST_ASSERT(leg_geometry.locations.size() >= 2);
// We report the relative position of source/target to the road only within a range that is
// sufficiently different but not full of the path
const constexpr double MINIMAL_RELATIVE_DISTANCE = 5., MAXIMAL_RELATIVE_DISTANCE = 300.;
const auto distance_to_start = util::coordinate_calculation::haversineDistance(
source_node.input_location, leg_geometry.locations[0]);
const auto initial_modifier =
distance_to_start >= MINIMAL_RELATIVE_DISTANCE &&
distance_to_start <= MAXIMAL_RELATIVE_DISTANCE
? angleToDirectionModifier(util::coordinate_calculation::computeAngle(
source_node.input_location, leg_geometry.locations[0], leg_geometry.locations[1]))
: extractor::guidance::DirectionModifier::UTurn;
if (leg_data.size() > 0)
{
StepManeuver maneuver = detail::stepManeuverFromGeometry(
extractor::guidance::TurnInstruction{extractor::guidance::TurnType::NoTurn,
initial_modifier},
WaypointType::Depart, leg_geometry);
maneuver.location = source_node.location;
// PathData saves the information we need of the segment _before_ the turn,
// but a RouteStep is with regard to the segment after the turn.
// We need to skip the first segment because it is already covered by the
// initial start of a route
int segment_duration = 0;
for (const auto &path_point : leg_data)
{
segment_duration += path_point.duration_until_turn;
// all changes to this check have to be matched with assemble_geometry
if (path_point.turn_instruction.type != extractor::guidance::TurnType::NoTurn)
{
BOOST_ASSERT(segment_duration >= 0);
const auto name = facade.GetNameForID(path_point.name_id);
const auto distance = leg_geometry.segment_distances[segment_index];
steps.push_back(RouteStep{path_point.name_id,
name,
segment_duration / 10.0,
distance,
path_point.travel_mode,
maneuver,
leg_geometry.FrontIndex(segment_index),
leg_geometry.BackIndex(segment_index) + 1});
maneuver = detail::stepManeuverFromGeometry(path_point.turn_instruction,
leg_geometry, segment_index);
segment_index++;
segment_duration = 0;
}
}
const auto distance = leg_geometry.segment_distances[segment_index];
const int duration = segment_duration + target_duration;
BOOST_ASSERT(duration >= 0);
steps.push_back(RouteStep{target_node.name_id,
facade.GetNameForID(target_node.name_id),
duration / 10.,
distance,
target_mode,
maneuver,
leg_geometry.FrontIndex(segment_index),
leg_geometry.BackIndex(segment_index) + 1});
}
// In this case the source + target are on the same edge segment
else
{
BOOST_ASSERT(source_node.fwd_segment_position == target_node.fwd_segment_position);
// s t
// u-------------v
// |---| source_duration
// |---------| target_duration
StepManeuver maneuver = detail::stepManeuverFromGeometry(
extractor::guidance::TurnInstruction{extractor::guidance::TurnType::NoTurn,
initial_modifier},
WaypointType::Depart, leg_geometry);
int duration = target_duration - source_duration;
BOOST_ASSERT(duration >= 0);
steps.push_back(RouteStep{source_node.name_id,
facade.GetNameForID(source_node.name_id),
duration / 10.,
leg_geometry.segment_distances[segment_index],
source_mode,
std::move(maneuver),
leg_geometry.FrontIndex(segment_index),
leg_geometry.BackIndex(segment_index) + 1});
}
BOOST_ASSERT(segment_index == number_of_segments - 1);
const auto distance_from_end = util::coordinate_calculation::haversineDistance(
target_node.input_location, leg_geometry.locations.back());
const auto final_modifier =
distance_from_end >= MINIMAL_RELATIVE_DISTANCE &&
distance_from_end <= MAXIMAL_RELATIVE_DISTANCE
? angleToDirectionModifier(util::coordinate_calculation::computeAngle(
leg_geometry.locations[leg_geometry.locations.size() - 2],
leg_geometry.locations[leg_geometry.locations.size() - 1],
target_node.input_location))
: extractor::guidance::DirectionModifier::UTurn;
// This step has length zero, the only reason we need it is the target location
auto final_maneuver = detail::stepManeuverFromGeometry(
extractor::guidance::TurnInstruction{extractor::guidance::TurnType::NoTurn, final_modifier},
WaypointType::Arrive, leg_geometry);
steps.push_back(RouteStep{target_node.name_id,
facade.GetNameForID(target_node.name_id),
ZERO_DURATION,
ZERO_DISTANCE,
target_mode,
final_maneuver,
leg_geometry.locations.size(),
leg_geometry.locations.size()});
return steps;
}
} // namespace guidance
} // namespace engine
} // namespace osrm
#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_

View 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

View File

@ -0,0 +1,30 @@
#ifndef ENGINE_GUIDANCE_POST_PROCESSING_HPP
#define ENGINE_GUIDANCE_POST_PROCESSING_HPP
#include "engine/guidance/route_step.hpp"
#include "engine/guidance/leg_geometry.hpp"
#include <vector>
namespace osrm
{
namespace engine
{
namespace guidance
{
// passed as none-reference to modify in-place and move out again
std::vector<RouteStep> postProcess(std::vector<RouteStep> steps);
// postProcess will break the connection between the leg geometry
// for which a segment is supposed to represent exactly the coordinates
// between routing maneuvers and the route steps itself.
// If required, we can get both in sync again using this function.
// Move in LegGeometry for modification in place.
LegGeometry resyncGeometry(LegGeometry leg_geometry, const std::vector<RouteStep> &steps);
} // namespace guidance
} // namespace engine
} // namespace osrm
#endif // ENGINE_GUIDANCE_POST_PROCESSING_HPP

View File

@ -0,0 +1,20 @@
#ifndef ROUTE_HPP
#define ROUTE_HPP
namespace osrm
{
namespace engine
{
namespace guidance
{
struct Route
{
double duration;
double distance;
};
}
}
}
#endif

View 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

View 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

View File

@ -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_

View File

@ -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_

View File

@ -0,0 +1,45 @@
#ifndef ENGINE_GUIDANCE_STEP_MANEUVER_HPP
#define ENGINE_GUIDANCE_STEP_MANEUVER_HPP
#include "util/coordinate.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include <cstdint>
#include <vector>
namespace osrm
{
namespace engine
{
namespace guidance
{
enum class WaypointType : std::uint8_t
{
None,
Arrive,
Depart,
};
//A represenetation of intermediate intersections
struct IntermediateIntersection
{
double duration;
double distance;
util::Coordinate location;
};
struct StepManeuver
{
util::Coordinate location;
double bearing_before;
double bearing_after;
extractor::guidance::TurnInstruction instruction;
WaypointType waypoint_type;
unsigned exit;
std::vector<IntermediateIntersection> intersections;
};
} // namespace guidance
} // namespace engine
} // namespace osrmn
#endif

View File

@ -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

View 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_ */

81
include/engine/hint.hpp Normal file
View File

@ -0,0 +1,81 @@
/*
Copyright (c) 2016, Project OSRM contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ENGINE_HINT_HPP
#define ENGINE_HINT_HPP
#include "engine/phantom_node.hpp"
#include "util/coordinate.hpp"
#include <string>
#include <cstdint>
#include <iosfwd>
namespace osrm
{
namespace engine
{
// Fwd. decls.
namespace datafacade
{
struct BaseDataFacade;
}
// Is returned as a temporary identifier for snapped coodinates
struct Hint
{
PhantomNode phantom;
std::uint32_t data_checksum;
bool IsValid(const util::Coordinate new_input_coordinates,
const datafacade::BaseDataFacade &facade) const;
std::string ToBase64() const;
static Hint FromBase64(const std::string &base64Hint);
friend bool operator==(const Hint &, const Hint &);
friend std::ostream &operator<<(std::ostream &, const Hint &);
};
#ifndef _MSC_VER
static_assert(sizeof(Hint) == 60 + 4, "Hint is bigger than expected");
constexpr std::size_t ENCODED_HINT_SIZE = 88;
static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint),
"ENCODED_HINT_SIZE does not match size of Hint");
#else
// PhantomNode is bigger under windows because MSVC does not support bit packing
static_assert(sizeof(Hint) == 64 + 4, "Hint is bigger than expected");
constexpr std::size_t ENCODED_HINT_SIZE = 92;
static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint),
"ENCODED_HINT_SIZE does not match size of Hint");
#endif
}
}
#endif

View File

@ -3,7 +3,7 @@
#include "engine/phantom_node.hpp"
#include "extractor/travel_mode.hpp"
#include "extractor/turn_instructions.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "util/typedefs.hpp"
#include "osrm/coordinate.hpp"
@ -15,28 +15,19 @@ namespace osrm
namespace engine
{
const constexpr unsigned INVALID_EXIT_NR = 0;
struct PathData
{
PathData()
: node(SPECIAL_NODEID), name_id(INVALID_EDGE_WEIGHT), segment_duration(INVALID_EDGE_WEIGHT),
turn_instruction(extractor::TurnInstruction::NoTurn),
travel_mode(TRAVEL_MODE_INACCESSIBLE)
{
}
PathData(NodeID node,
unsigned name_id,
extractor::TurnInstruction turn_instruction,
EdgeWeight segment_duration,
extractor::TravelMode travel_mode)
: node(node), name_id(name_id), segment_duration(segment_duration),
turn_instruction(turn_instruction), travel_mode(travel_mode)
{
}
NodeID node;
// id of via node of the turn
NodeID turn_via_node;
// name of the street that leads to the turn
unsigned name_id;
EdgeWeight segment_duration;
extractor::TurnInstruction turn_instruction;
// duration that is traveled on the segment until the turn is reached
EdgeWeight duration_until_turn;
// instruction to execute at the turn
extractor::guidance::TurnInstruction turn_instruction;
// travel mode of the street that leads to the turn
extractor::TravelMode travel_mode : 4;
};

View File

@ -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];

View 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

View 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

View File

@ -1,91 +0,0 @@
#ifndef OBJECT_ENCODER_HPP
#define OBJECT_ENCODER_HPP
#include <boost/assert.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/binary_from_base64.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>
#include <cstdint>
#include <climits>
namespace osrm
{
namespace engine
{
namespace detail
{
static_assert(CHAR_BIT == 8u, "we assume a byte holds 8 bits");
static_assert(sizeof(char) == 1u, "we assume a char is one byte large");
using Base64FromBinary = boost::archive::iterators::base64_from_binary<
boost::archive::iterators::transform_width<const char *, // sequence of chars
6, // get view of 6 bit
8 // from sequence of 8 bit
>>;
using BinaryFromBase64 = boost::archive::iterators::transform_width<
boost::archive::iterators::binary_from_base64<std::string::const_iterator>,
8, // get a view of 8 bit
6 // from a sequence of 6 bit
>;
} // ns detail
template <typename T> std::string encodeBase64(const T &x)
{
#if not defined __GNUC__ or __GNUC__ > 4
static_assert(std::is_trivially_copyable<T>::value, "requires a trivially copyable type");
#endif
std::vector<unsigned char> bytes{reinterpret_cast<const char *>(&x),
reinterpret_cast<const char *>(&x) + sizeof(T)};
BOOST_ASSERT(!bytes.empty());
std::size_t bytes_to_pad{0};
while (bytes.size() % 3 != 0)
{
bytes_to_pad += 1;
bytes.push_back(0);
}
BOOST_ASSERT(bytes_to_pad == 0 || bytes_to_pad == 1 || bytes_to_pad == 2);
BOOST_ASSERT_MSG(0 == bytes.size() % 3, "base64 input data size is not a multiple of 3");
std::string encoded{detail::Base64FromBinary{bytes.data()},
detail::Base64FromBinary{bytes.data() + (bytes.size() - bytes_to_pad)}};
std::replace(begin(encoded), end(encoded), '+', '-');
std::replace(begin(encoded), end(encoded), '/', '_');
return encoded;
}
template <typename T> T decodeBase64(std::string encoded)
{
#if not defined __GNUC__ or __GNUC__ > 4
static_assert(std::is_trivially_copyable<T>::value, "requires a trivially copyable type");
#endif
std::replace(begin(encoded), end(encoded), '-', '+');
std::replace(begin(encoded), end(encoded), '_', '/');
T rv;
std::copy(detail::BinaryFromBase64{begin(encoded)},
detail::BinaryFromBase64{begin(encoded) + encoded.length()},
reinterpret_cast<char *>(&rv));
return rv;
}
} // ns engine
} // ns osrm
#endif /* OBJECT_ENCODER_HPP */

View File

@ -1,3 +1,30 @@
/*
Copyright (c) 2016, Project OSRM contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PHANTOM_NODES_H
#define PHANTOM_NODES_H
@ -28,7 +55,8 @@ struct PhantomNode
unsigned reverse_packed_geometry_id_,
bool is_tiny_component,
unsigned component_id,
util::FixedPointCoordinate location,
util::Coordinate location,
util::Coordinate input_location,
unsigned short fwd_segment_position,
extractor::TravelMode forward_travel_mode,
extractor::TravelMode backward_travel_mode)
@ -37,8 +65,8 @@ struct PhantomNode
forward_offset(forward_offset), reverse_offset(reverse_offset),
forward_packed_geometry_id(forward_packed_geometry_id_),
reverse_packed_geometry_id(reverse_packed_geometry_id_),
component{component_id, is_tiny_component},
location(std::move(location)), fwd_segment_position(fwd_segment_position),
component{component_id, is_tiny_component}, location(std::move(location)),
input_location(std::move(input_location)), fwd_segment_position(fwd_segment_position),
forward_travel_mode(forward_travel_mode), backward_travel_mode(backward_travel_mode)
{
}
@ -47,10 +75,9 @@ struct PhantomNode
: forward_node_id(SPECIAL_NODEID), reverse_node_id(SPECIAL_NODEID),
name_id(std::numeric_limits<unsigned>::max()), forward_weight(INVALID_EDGE_WEIGHT),
reverse_weight(INVALID_EDGE_WEIGHT), forward_offset(0), reverse_offset(0),
forward_packed_geometry_id(SPECIAL_EDGEID),
reverse_packed_geometry_id(SPECIAL_EDGEID),
component{INVALID_COMPONENTID, false},
fwd_segment_position(0), forward_travel_mode(TRAVEL_MODE_INACCESSIBLE),
forward_packed_geometry_id(SPECIAL_EDGEID), reverse_packed_geometry_id(SPECIAL_EDGEID),
component{INVALID_COMPONENTID, false}, fwd_segment_position(0),
forward_travel_mode(TRAVEL_MODE_INACCESSIBLE),
backward_travel_mode(TRAVEL_MODE_INACCESSIBLE)
{
}
@ -89,12 +116,23 @@ struct PhantomNode
(component.id != INVALID_COMPONENTID) && (name_id != INVALID_NAMEID);
}
bool IsValid(const unsigned number_of_nodes, const util::Coordinate queried_coordinate) const
{
return queried_coordinate == input_location && IsValid(number_of_nodes);
}
bool IsValid() const { return location.IsValid() && (name_id != INVALID_NAMEID); }
bool operator==(const PhantomNode &other) const { return location == other.location; }
template <class OtherT>
PhantomNode(const OtherT &other, int forward_weight_, int forward_offset_, int reverse_weight_, int reverse_offset_, const util::FixedPointCoordinate foot_point)
explicit PhantomNode(const OtherT &other,
int forward_weight_,
int forward_offset_,
int reverse_weight_,
int reverse_offset_,
const util::Coordinate location_,
const util::Coordinate input_location_)
{
forward_node_id = other.forward_edge_based_node_id;
reverse_node_id = other.reverse_edge_based_node_id;
@ -112,7 +150,8 @@ struct PhantomNode
component.id = other.component.id;
component.is_tiny = other.component.is_tiny;
location = foot_point;
location = location_;
input_location = input_location_;
fwd_segment_position = other.fwd_segment_position;
forward_travel_mode = other.forward_travel_mode;
@ -137,7 +176,8 @@ struct PhantomNode
#ifndef _MSC_VER
static_assert(sizeof(ComponentType) == 4, "ComponentType needs to 4 bytes big");
#endif
util::FixedPointCoordinate location;
util::Coordinate location;
util::Coordinate input_location;
unsigned short fwd_segment_position;
// note 4 bits would suffice for each,
// but the saved byte would be padding anyway
@ -146,7 +186,9 @@ struct PhantomNode
};
#ifndef _MSC_VER
static_assert(sizeof(PhantomNode) == 52, "PhantomNode has more padding then expected");
static_assert(sizeof(PhantomNode) == 60, "PhantomNode has more padding then expected");
#else
static_assert(sizeof(PhantomNode) == 64, "PhantomNode has more padding then expected");
#endif
using PhantomNodePair = std::pair<PhantomNode, PhantomNode>;

View File

@ -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

View File

@ -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 &parameters, 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;
};
}
}

View File

@ -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 &params, util::json::Object &result);
};
}
}

View File

@ -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 &parameters,
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 &parameters, 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 &parameters)
{
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;
}
};
}
}

View 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 &params, util::json::Object &result);
private:
SearchEngineData heaps;
routing_algorithms::ManyToManyRouting<datafacade::BaseDataFacade> distance_table;
int max_locations_distance_table;
};
}
}
}
#endif // TABLE_HPP

View File

@ -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 &parameters, std::string &pbf_buffer);
};
}
}

View File

@ -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 &parameters,
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 &parameters, util::json::Object &json_result);
};
}
}

View File

@ -1,17 +1,15 @@
#ifndef VIA_ROUTE_HPP
#define VIA_ROUTE_HPP
#include "engine/datafacade/datafacade_base.hpp"
#include "engine/plugins/plugin_base.hpp"
#include "engine/api/route_api.hpp"
#include "engine/api_response_generator.hpp"
#include "engine/object_encoder.hpp"
#include "engine/search_engine.hpp"
#include "util/for_each_pair.hpp"
#include "util/integer_range.hpp"
#include "util/make_unique.hpp"
#include "util/simple_logger.hpp"
#include "util/timing_util.hpp"
#include "osrm/json_container.hpp"
#include "engine/search_engine_data.hpp"
#include "engine/routing_algorithms/shortest_path.hpp"
#include "engine/routing_algorithms/alternative_path.hpp"
#include "engine/routing_algorithms/direct_shortest_path.hpp"
#include "util/json_container.hpp"
#include <cstdlib>
@ -27,146 +25,20 @@ namespace engine
namespace plugins
{
template <class DataFacadeT> class ViaRoutePlugin final : public BasePlugin
class ViaRoutePlugin final : public BasePlugin
{
private:
std::string descriptor_string;
std::unique_ptr<SearchEngine<DataFacadeT>> search_engine_ptr;
DataFacadeT *facade;
SearchEngineData heaps;
routing_algorithms::ShortestPathRouting<datafacade::BaseDataFacade> shortest_path;
routing_algorithms::AlternativeRouting<datafacade::BaseDataFacade> alternative_path;
routing_algorithms::DirectShortestPathRouting<datafacade::BaseDataFacade> direct_shortest_path;
int max_locations_viaroute;
public:
explicit ViaRoutePlugin(DataFacadeT *facade, int max_locations_viaroute)
: descriptor_string("viaroute"), facade(facade),
max_locations_viaroute(max_locations_viaroute)
{
search_engine_ptr = util::make_unique<SearchEngine<DataFacadeT>>(facade);
}
explicit ViaRoutePlugin(datafacade::BaseDataFacade &facade, int max_locations_viaroute);
virtual ~ViaRoutePlugin() {}
const std::string GetDescriptor() const override final { return descriptor_string; }
Status HandleRequest(const RouteParameters &route_parameters,
util::json::Object &json_result) override final
{
if (max_locations_viaroute > 0 &&
(static_cast<int>(route_parameters.coordinates.size()) > max_locations_viaroute))
{
json_result.values["status_message"] =
"Number of entries " + std::to_string(route_parameters.coordinates.size()) +
" is higher than current maximum (" + std::to_string(max_locations_viaroute) + ")";
return Status::Error;
}
if (!check_all_coordinates(route_parameters.coordinates))
{
json_result.values["status_message"] = "Invalid coordinates";
return Status::Error;
}
const auto &input_bearings = route_parameters.bearings;
if (input_bearings.size() > 0 &&
route_parameters.coordinates.size() != input_bearings.size())
{
json_result.values["status_message"] =
"Number of bearings does not match number of coordinate";
return Status::Error;
}
std::vector<PhantomNodePair> phantom_node_pair_list(route_parameters.coordinates.size());
const bool checksum_OK = (route_parameters.check_sum == facade->GetCheckSum());
for (const auto i : util::irange<std::size_t>(0, route_parameters.coordinates.size()))
{
if (checksum_OK && i < route_parameters.hints.size() &&
!route_parameters.hints[i].empty())
{
phantom_node_pair_list[i].first =
decodeBase64<PhantomNode>(route_parameters.hints[i]);
if (phantom_node_pair_list[i].first.IsValid(facade->GetNumberOfNodes()))
{
continue;
}
}
const int bearing = input_bearings.size() > 0 ? input_bearings[i].first : 0;
const int range = input_bearings.size() > 0
? (input_bearings[i].second ? *input_bearings[i].second : 10)
: 180;
phantom_node_pair_list[i] = facade->NearestPhantomNodeWithAlternativeFromBigComponent(
route_parameters.coordinates[i], bearing, range);
// we didn't found a fitting node, return error
if (!phantom_node_pair_list[i].first.IsValid(facade->GetNumberOfNodes()))
{
json_result.values["status_message"] =
std::string("Could not find a matching segment for coordinate ") +
std::to_string(i);
return Status::NoSegment;
}
BOOST_ASSERT(phantom_node_pair_list[i].first.IsValid(facade->GetNumberOfNodes()));
BOOST_ASSERT(phantom_node_pair_list[i].second.IsValid(facade->GetNumberOfNodes()));
}
auto snapped_phantoms = snapPhantomNodes(phantom_node_pair_list);
InternalRouteResult raw_route;
auto build_phantom_pairs =
[&raw_route](const PhantomNode &first_node, const PhantomNode &second_node)
{
raw_route.segment_end_coordinates.push_back(PhantomNodes{first_node, second_node});
};
util::for_each_pair(snapped_phantoms, build_phantom_pairs);
if (1 == raw_route.segment_end_coordinates.size())
{
if (route_parameters.alternate_route && facade->GetCoreSize() == 0)
{
search_engine_ptr->alternative_path(raw_route.segment_end_coordinates.front(),
raw_route);
}
else
{
search_engine_ptr->direct_shortest_path(raw_route.segment_end_coordinates,
route_parameters.uturns, raw_route);
}
}
else
{
search_engine_ptr->shortest_path(raw_route.segment_end_coordinates,
route_parameters.uturns, raw_route);
}
// we can only know this after the fact, different SCC ids still
// allow for connection in one direction.
if (raw_route.is_valid())
{
auto generator = MakeApiResponseGenerator(facade);
generator.DescribeRoute(route_parameters, raw_route, json_result);
json_result.values["status_message"] = "Found route between points";
}
else
{
auto first_component_id = snapped_phantoms.front().component.id;
auto not_in_same_component =
std::any_of(snapped_phantoms.begin(), snapped_phantoms.end(),
[first_component_id](const PhantomNode &node)
{
return node.component.id != first_component_id;
});
if (not_in_same_component)
{
json_result.values["status_message"] = "Impossible route between points";
return Status::EmptyResult;
}
else
{
json_result.values["status_message"] = "No route found between points";
return Status::Error;
}
}
return Status::Ok;
}
Status HandleRequest(const api::RouteParameters &route_parameters,
util::json::Object &json_result);
};
}
}

View File

@ -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);
}
}

View File

@ -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 */

View File

@ -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

View File

@ -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 &parameter);
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

View File

@ -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. "

View File

@ -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 &current_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 &current_distance = (*result_table)[source_id * number_of_targets + target_id];
auto &current_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))
{

View File

@ -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> &timestamps) 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 &current_viterbi = model.viterbi[t];
auto &current_pruned = model.pruned[t];
auto &current_suspicious = model.suspicious[t];
auto &current_parents = model.parents[t];
auto &current_lengths = model.path_lengths[t];
auto &current_lengths = model.path_distances[t];
const auto &current_timestamps_list = candidates_list[t];
const auto &current_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;
}
};
}

View File

@ -4,7 +4,7 @@
#include "util/coordinate_calculation.hpp"
#include "engine/internal_route_result.hpp"
#include "engine/search_engine_data.hpp"
#include "extractor/turn_instructions.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "util/typedefs.hpp"
#include <boost/assert.hpp>
@ -24,13 +24,6 @@ namespace osrm
namespace engine
{
SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_1;
SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_1;
SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_2;
SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_2;
SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_3;
SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_3;
namespace routing_algorithms
{
@ -243,8 +236,8 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
edge = recursion_stack.top();
recursion_stack.pop();
// facade->FindEdge does not suffice here in case of shortcuts.
// The above explanation unclear? Think!
// Contraction might introduce double edges by inserting shortcuts
// this searching for the smallest upwards edge found by the forward search
EdgeID smaller_edge_id = SPECIAL_EDGEID;
EdgeWeight edge_weight = std::numeric_limits<EdgeWeight>::max();
for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.first))
@ -261,6 +254,8 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
// edge.first edge.second
// *<------------------*
// edge_id
// if we don't find a forward edge, this edge must have been an downwards edge
// found by the reverse search.
if (SPECIAL_EDGEID == smaller_edge_id)
{
for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.second))
@ -288,81 +283,96 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
{
BOOST_ASSERT_MSG(!ed.shortcut, "original edge flagged as shortcut");
unsigned name_index = facade->GetNameIndexFromEdgeID(ed.id);
const extractor::TurnInstruction turn_instruction =
facade->GetTurnInstructionForEdgeID(ed.id);
const auto turn_instruction = facade->GetTurnInstructionForEdgeID(ed.id);
const extractor::TravelMode travel_mode =
(unpacked_path.empty() && start_traversed_in_reverse)
? phantom_node_pair.source_phantom.backward_travel_mode
: facade->GetTravelModeForEdgeID(ed.id);
if (!facade->EdgeIsCompressed(ed.id))
std::vector<NodeID> id_vector;
facade->GetUncompressedGeometry(facade->GetGeometryIndexForEdgeID(ed.id),
id_vector);
BOOST_ASSERT(id_vector.size() > 0);
std::vector<EdgeWeight> weight_vector;
facade->GetUncompressedWeights(facade->GetGeometryIndexForEdgeID(ed.id),
weight_vector);
BOOST_ASSERT(weight_vector.size() > 0);
auto total_weight = std::accumulate(weight_vector.begin(), weight_vector.end(), 0);
BOOST_ASSERT(weight_vector.size() == id_vector.size());
// ed.distance should be total_weight + penalties (turn, stop, etc)
BOOST_ASSERT(ed.distance >= total_weight);
const bool is_first_segment = unpacked_path.empty();
const std::size_t start_index =
(is_first_segment
? ((start_traversed_in_reverse)
? id_vector.size() -
phantom_node_pair.source_phantom.fwd_segment_position - 1
: phantom_node_pair.source_phantom.fwd_segment_position)
: 0);
const std::size_t end_index = id_vector.size();
BOOST_ASSERT(start_index >= 0);
BOOST_ASSERT(start_index < end_index);
for (std::size_t i = start_index; i < end_index; ++i)
{
BOOST_ASSERT(!facade->EdgeIsCompressed(ed.id));
unpacked_path.emplace_back(facade->GetGeometryIndexForEdgeID(ed.id), name_index,
turn_instruction, ed.distance, travel_mode);
unpacked_path.push_back(
PathData{id_vector[i], name_index, weight_vector[i],
extractor::guidance::TurnInstruction::NO_TURN(), travel_mode});
}
else
BOOST_ASSERT(unpacked_path.size() > 0);
unpacked_path.back().turn_instruction = turn_instruction;
unpacked_path.back().duration_until_turn += (ed.distance - total_weight);
if (is_first_segment)
{
std::vector<NodeID> id_vector;
facade->GetUncompressedGeometry(facade->GetGeometryIndexForEdgeID(ed.id),
id_vector);
std::vector<EdgeWeight> weight_vector;
facade->GetUncompressedWeights(facade->GetGeometryIndexForEdgeID(ed.id),
weight_vector);
int total_weight = std::accumulate(weight_vector.begin(), weight_vector.end(), 0);
BOOST_ASSERT(weight_vector.size() == id_vector.size());
// ed.distance should be total_weight + penalties (turn, stop, etc)
BOOST_ASSERT(ed.distance >= total_weight);
const std::size_t start_index =
(unpacked_path.empty()
? ((start_traversed_in_reverse)
? id_vector.size() -
phantom_node_pair.source_phantom.fwd_segment_position - 1
: phantom_node_pair.source_phantom.fwd_segment_position)
: 0);
const std::size_t end_index = id_vector.size();
BOOST_ASSERT(start_index >= 0);
BOOST_ASSERT(start_index <= end_index);
for (std::size_t i = start_index; i < end_index; ++i)
{
unpacked_path.emplace_back(id_vector[i], name_index,
extractor::TurnInstruction::NoTurn, weight_vector[i],
travel_mode);
}
unpacked_path.back().turn_instruction = turn_instruction;
unpacked_path.back().segment_duration += (ed.distance - total_weight);
auto source_weight = start_traversed_in_reverse
? phantom_node_pair.source_phantom.reverse_weight
: phantom_node_pair.source_phantom.forward_weight;
// Given this geometry:
// U---v---w---x---Z
// s
// The above code will create segments for (v, w), (w,x) and (x, Z).
// However the first segment duration needs to be adjusted to the fact that the
// source phantom is in the middle of the segment.
// We do this by subtracting v--s from the duration.
BOOST_ASSERT(unpacked_path.front().duration_until_turn >= source_weight);
unpacked_path.front().duration_until_turn -= source_weight;
}
}
}
std::vector<unsigned> id_vector;
facade->GetUncompressedGeometry(phantom_node_pair.target_phantom.forward_packed_geometry_id,
id_vector);
std::vector<EdgeWeight> weight_vector;
facade->GetUncompressedWeights(phantom_node_pair.target_phantom.forward_packed_geometry_id,
weight_vector);
const bool is_local_path = (phantom_node_pair.source_phantom.forward_packed_geometry_id ==
phantom_node_pair.target_phantom.forward_packed_geometry_id) &&
unpacked_path.empty();
unpacked_path.empty();
std::size_t start_index = 0;
if (is_local_path)
{
start_index = phantom_node_pair.source_phantom.fwd_segment_position;
if (target_traversed_in_reverse)
{
start_index =
id_vector.size() - phantom_node_pair.source_phantom.fwd_segment_position;
}
else
{
start_index = phantom_node_pair.source_phantom.fwd_segment_position;
}
}
std::size_t end_index = phantom_node_pair.target_phantom.fwd_segment_position;
if (target_traversed_in_reverse)
{
std::reverse(id_vector.begin(), id_vector.end());
end_index =
id_vector.size() - phantom_node_pair.target_phantom.fwd_segment_position;
end_index = id_vector.size() - phantom_node_pair.target_phantom.fwd_segment_position;
}
if (start_index > end_index)
@ -370,16 +380,35 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
start_index = std::min(start_index, id_vector.size() - 1);
}
// Given the following compressed geometry:
// U---v---w---x---y---Z
// s t
// s: fwd_segment 0
// t: fwd_segment 3
// -> (U, v), (v, w), (w, x)
// note that (x, t) is _not_ included but needs to be added later.
for (std::size_t i = start_index; i != end_index; (start_index < end_index ? ++i : --i))
{
BOOST_ASSERT(i < id_vector.size());
BOOST_ASSERT(phantom_node_pair.target_phantom.forward_travel_mode > 0);
unpacked_path.emplace_back(
PathData{id_vector[i], phantom_node_pair.target_phantom.name_id,
extractor::TurnInstruction::NoTurn, 0,
target_traversed_in_reverse
? phantom_node_pair.target_phantom.backward_travel_mode
: phantom_node_pair.target_phantom.forward_travel_mode});
unpacked_path.emplace_back(PathData{
id_vector[i], phantom_node_pair.target_phantom.name_id, weight_vector[i],
extractor::guidance::TurnInstruction::NO_TURN(),
target_traversed_in_reverse ? phantom_node_pair.target_phantom.backward_travel_mode
: phantom_node_pair.target_phantom.forward_travel_mode});
}
if (is_local_path && unpacked_path.size() > 0)
{
auto source_weight = start_traversed_in_reverse
? phantom_node_pair.source_phantom.reverse_weight
: phantom_node_pair.source_phantom.forward_weight;
// The above code will create segments for (v, w), (w,x), (x, y) and (y, Z).
// However the first segment duration needs to be adjusted to the fact that the source
// phantom is in the middle of the segment. We do this by subtracting v--s from the
// duration.
BOOST_ASSERT(unpacked_path.front().duration_until_turn >= source_weight);
unpacked_path.front().duration_until_turn -= source_weight;
}
// there is no equivalent to a node-based node in an edge-expanded graph.
@ -395,7 +424,8 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
// looks like a trivially true check but tests for underflow
BOOST_ASSERT(last_index > second_to_last_index);
if (unpacked_path[last_index].node == unpacked_path[second_to_last_index].node)
if (unpacked_path[last_index].turn_via_node ==
unpacked_path[second_to_last_index].turn_via_node)
{
unpacked_path.pop_back();
}
@ -753,12 +783,12 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
nodes.target_phantom = target_phantom;
UnpackPath(packed_path.begin(), packed_path.end(), nodes, unpacked_path);
util::FixedPointCoordinate previous_coordinate = source_phantom.location;
util::FixedPointCoordinate current_coordinate;
util::Coordinate previous_coordinate = source_phantom.location;
util::Coordinate current_coordinate;
double distance = 0;
for (const auto &p : unpacked_path)
{
current_coordinate = facade->GetCoordinateOfNode(p.node);
current_coordinate = facade->GetCoordinateOfNode(p.turn_via_node);
distance += util::coordinate_calculation::haversineDistance(previous_coordinate,
current_coordinate);
previous_coordinate = current_coordinate;
@ -777,7 +807,7 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
SearchEngineData::QueryHeap &reverse_core_heap,
const PhantomNode &source_phantom,
const PhantomNode &target_phantom,
int duration_upper_bound=INVALID_EDGE_WEIGHT) const
int duration_upper_bound = INVALID_EDGE_WEIGHT) const
{
BOOST_ASSERT(forward_heap.Empty());
BOOST_ASSERT(reverse_heap.Empty());
@ -831,7 +861,7 @@ template <class DataFacadeT, class Derived> class BasicRoutingInterface
SearchEngineData::QueryHeap &reverse_heap,
const PhantomNode &source_phantom,
const PhantomNode &target_phantom,
int duration_upper_bound=INVALID_EDGE_WEIGHT) const
int duration_upper_bound = INVALID_EDGE_WEIGHT) const
{
BOOST_ASSERT(forward_heap.Empty());
BOOST_ASSERT(reverse_heap.Empty());

View File

@ -9,6 +9,7 @@
#include "util/integer_range.hpp"
#include <boost/assert.hpp>
#include <boost/optional.hpp>
namespace osrm
{
@ -237,10 +238,9 @@ class ShortestPathRouting final
}
void operator()(const std::vector<PhantomNodes> &phantom_nodes_vector,
const std::vector<bool> &uturn_indicators,
const boost::optional<bool> uturns,
InternalRouteResult &raw_route_data) const
{
BOOST_ASSERT(uturn_indicators.size() == phantom_nodes_vector.size() + 1);
engine_working_data.InitializeOrClearFirstThreadLocalStorage(
super::facade->GetNumberOfNodes());
engine_working_data.InitializeOrClearSecondThreadLocalStorage(
@ -266,6 +266,8 @@ class ShortestPathRouting final
std::vector<NodeID> total_packed_path_to_reverse;
std::vector<std::size_t> packed_leg_to_reverse_begin;
const bool allow_u_turn_at_via = uturns ? *uturns : super::facade->GetUTurnsDefault();
std::size_t current_leg = 0;
// this implements a dynamic program that finds the shortest route through
// a list of vias
@ -280,9 +282,6 @@ class ShortestPathRouting final
const auto &source_phantom = phantom_node_pair.source_phantom;
const auto &target_phantom = phantom_node_pair.target_phantom;
BOOST_ASSERT(current_leg + 1 < uturn_indicators.size());
const bool allow_u_turn_at_via = uturn_indicators[current_leg + 1];
bool search_to_forward_node = target_phantom.forward_node_id != SPECIAL_NODEID;
bool search_to_reverse_node = target_phantom.reverse_node_id != SPECIAL_NODEID;

View File

@ -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

View File

@ -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
View 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

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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;

View File

@ -4,16 +4,20 @@
#define EDGE_BASED_GRAPH_FACTORY_HPP_
#include "extractor/edge_based_edge.hpp"
#include "extractor/speed_profile.hpp"
#include "util/typedefs.hpp"
#include "extractor/profile_properties.hpp"
#include "extractor/restriction_map.hpp"
#include "extractor/compressed_edge_container.hpp"
#include "util/deallocating_vector.hpp"
#include "extractor/edge_based_node.hpp"
#include "extractor/original_edge_data.hpp"
#include "extractor/query_node.hpp"
#include "extractor/turn_instructions.hpp"
#include "extractor/guidance/turn_analysis.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "util/node_based_graph.hpp"
#include "extractor/restriction_map.hpp"
#include "util/typedefs.hpp"
#include "util/deallocating_vector.hpp"
#include "util/name_table.hpp"
#include <algorithm>
#include <cstdint>
@ -48,7 +52,8 @@ class EdgeBasedGraphFactory
const std::unordered_set<NodeID> &traffic_lights,
std::shared_ptr<const RestrictionMap> restriction_map,
const std::vector<QueryNode> &node_info_list,
SpeedProfileProperties speed_profile);
ProfileProperties profile_properties,
const util::NameTable &name_table);
void Run(const std::string &original_edge_data_filename,
lua_State *lua_state,
@ -68,12 +73,12 @@ class EdgeBasedGraphFactory
// with known angle.
// Handles special cases like u-turns and roundabouts
// For basic turns, the turn based on the angle-classification is returned
TurnInstruction AnalyzeTurn(const NodeID u,
const EdgeID e1,
const NodeID v,
const EdgeID e2,
const NodeID w,
const double angle) const;
guidance::TurnInstruction AnalyzeTurn(const NodeID u,
const EdgeID e1,
const NodeID v,
const EdgeID e2,
const NodeID w,
const double angle) const;
std::int32_t GetTurnPenalty(double angle, lua_State *lua_state) const;
@ -101,73 +106,24 @@ class EdgeBasedGraphFactory
const std::unordered_set<NodeID> &m_traffic_lights;
const CompressedEdgeContainer &m_compressed_edge_container;
SpeedProfileProperties speed_profile;
ProfileProperties profile_properties;
const util::NameTable &name_table;
void CompressGeometry();
unsigned RenumberEdges();
void GenerateEdgeExpandedNodes();
#ifdef DEBUG_GEOMETRY
void GenerateEdgeExpandedEdges(const std::string &original_edge_data_filename,
lua_State *lua_state,
const std::string &edge_segment_lookup_filename,
const std::string &edge_fixed_penalties_filename,
const bool generate_edge_lookup,
const std::string &debug_turns_path);
#else
void GenerateEdgeExpandedEdges(const std::string &original_edge_data_filename,
lua_State *lua_state,
const std::string &edge_segment_lookup_filename,
const std::string &edge_fixed_penalties_filename,
const bool generate_edge_lookup);
#endif
void InsertEdgeBasedNode(const NodeID u, const NodeID v);
void FlushVectorToStream(std::ofstream &edge_data_file,
std::vector<OriginalEdgeData> &original_edge_data_vector) const;
struct TurnCandidate
{
EdgeID eid; // the id of the arc
bool valid; // a turn may be relevant to good instructions, even if we cannot take the road
double angle; // the approximated angle of the turn
TurnInstruction instruction; // a proposed instruction
double confidence; // how close to the border is the turn?
std::string toString() const
{
std::string result = "[turn] ";
result += std::to_string(eid);
result += " valid: ";
result += std::to_string(valid);
result += " angle: ";
result += std::to_string(angle);
result += " instruction: ";
result += std::to_string(static_cast<std::int32_t>(instruction));
result += " confidence: ";
result += std::to_string(confidence);
return result;
}
};
// Use In Order to generate base turns
// cannot be const due to the counters...
std::vector<TurnCandidate> getTurnCandidates(NodeID from, EdgeID via_edge);
std::vector<TurnCandidate> optimizeCandidates(NodeID via_edge,
std::vector<TurnCandidate> turn_candidates) const;
std::vector<TurnCandidate> suppressTurns(EdgeID via_edge,
std::vector<TurnCandidate> turn_candidates) const;
QueryNode getRepresentativeCoordinate(const NodeID src,
const NodeID tgt,
const EdgeID via_eid,
bool INVERTED) const;
bool isObviousChoice(EdgeID coming_from_eid,
std::size_t turn_index,
const std::vector<TurnCandidate> &turn_candidates) const;
std::size_t restricted_turns_counter;
std::size_t skipped_uturns_counter;
std::size_t skipped_barrier_turns_counter;

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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,

View File

@ -1,3 +1,30 @@
/*
Copyright (c) 2016, Project OSRM contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef EXTRACTOR_HPP
#define EXTRACTOR_HPP
@ -13,6 +40,8 @@ namespace osrm
namespace extractor
{
struct ProfileProperties;
class Extractor
{
public:
@ -22,13 +51,15 @@ class Extractor
private:
ExtractorConfig config;
void SetupScriptingEnvironment(lua_State *myLuaState, SpeedProfileProperties &speed_profile);
std::pair<std::size_t, std::size_t>
BuildEdgeExpandedGraph(std::vector<QueryNode> &internal_to_external_node_map,
BuildEdgeExpandedGraph(lua_State* lua_state,
const ProfileProperties& profile_properties,
std::vector<QueryNode> &internal_to_external_node_map,
std::vector<EdgeBasedNode> &node_based_edge_list,
std::vector<bool> &node_is_startpoint,
std::vector<EdgeWeight> &edge_based_node_weights,
util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list);
void WriteProfileProperties(const std::string& output_path, const ProfileProperties& properties) const;
void WriteNodeMapping(const std::vector<QueryNode> &internal_to_external_node_map);
void FindComponents(unsigned max_edge_id,
const util::DeallocatingVector<EdgeBasedEdge> &edges,

View File

@ -1,3 +1,30 @@
/*
Copyright (c) 2016, Project OSRM contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef EXTRACTOR_CONFIG_HPP
#define EXTRACTOR_CONFIG_HPP
@ -44,6 +71,7 @@ struct ExtractorConfig
edge_segment_lookup_path = basepath + ".osrm.edge_segment_lookup";
edge_penalty_path = basepath + ".osrm.edge_penalties";
edge_based_node_weights_output_path = basepath + ".osrm.enw";
profile_properties_output_path = basepath + ".osrm.properties";
}
boost::filesystem::path config_file_path;
@ -61,6 +89,7 @@ struct ExtractorConfig
std::string node_output_path;
std::string rtree_nodes_output_path;
std::string rtree_leafs_output_path;
std::string profile_properties_output_path;
unsigned requested_num_threads;
unsigned small_component_size;

View File

@ -3,7 +3,6 @@
#include "util/typedefs.hpp"
#include "extractor/speed_profile.hpp"
#include "util/node_based_graph.hpp"
#include <memory>
@ -22,8 +21,6 @@ class GraphCompressor
using EdgeData = util::NodeBasedDynamicGraph::EdgeData;
public:
GraphCompressor(SpeedProfileProperties speed_profile);
void Compress(const std::unordered_set<NodeID> &barrier_nodes,
const std::unordered_set<NodeID> &traffic_lights,
RestrictionMap &restriction_map,
@ -34,8 +31,6 @@ class GraphCompressor
void PrintStatistics(unsigned original_number_of_nodes,
unsigned original_number_of_edges,
const util::NodeBasedDynamicGraph &graph) const;
SpeedProfileProperties speed_profile;
};
}
}

View 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_

View 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 */

View File

@ -0,0 +1,404 @@
#ifndef OSRM_GUIDANCE_TOOLKIT_HPP_
#define OSRM_GUIDANCE_TOOLKIT_HPP_
#include "util/bearing.hpp"
#include "util/coordinate.hpp"
#include "util/coordinate_calculation.hpp"
#include "extractor/compressed_edge_container.hpp"
#include "extractor/query_node.hpp"
#include "extractor/guidance/discrete_angle.hpp"
#include "extractor/guidance/classification_data.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include <algorithm>
#include <map>
#include <cmath>
#include <cstdint>
#include <string>
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace detail
{
const constexpr double DESIRED_SEGMENT_LENGTH = 10.0;
const constexpr bool shiftable_ccw[] = {false, true, true, false, false, true, true, false};
const constexpr bool shiftable_cw[] = {false, false, true, true, false, false, true, true};
const constexpr std::uint8_t modifier_bounds[detail::num_direction_modifiers] = {
0, 36, 93, 121, 136, 163, 220, 255};
const constexpr double discrete_angle_step_size = 360. / 256.;
template <typename IteratorType>
util::Coordinate
getCoordinateFromCompressedRange(util::Coordinate current_coordinate,
const IteratorType compressed_geometry_begin,
const IteratorType compressed_geometry_end,
const util::Coordinate final_coordinate,
const std::vector<extractor::QueryNode> &query_nodes)
{
const auto extractCoordinateFromNode = [](const extractor::QueryNode &node) -> util::Coordinate
{
return {node.lon, node.lat};
};
double distance_to_current_coordinate = 0;
double distance_to_next_coordinate = 0;
// get the length that is missing from the current segment to reach DESIRED_SEGMENT_LENGTH
const auto getFactor = [](const double first_distance, const double second_distance)
{
BOOST_ASSERT(first_distance < detail::DESIRED_SEGMENT_LENGTH);
double segment_length = second_distance - first_distance;
BOOST_ASSERT(segment_length > 0);
BOOST_ASSERT(second_distance >= detail::DESIRED_SEGMENT_LENGTH);
double missing_distance = detail::DESIRED_SEGMENT_LENGTH - first_distance;
return missing_distance / segment_length;
};
for (auto compressed_geometry_itr = compressed_geometry_begin;
compressed_geometry_itr != compressed_geometry_end; ++compressed_geometry_itr)
{
const auto next_coordinate =
extractCoordinateFromNode(query_nodes[compressed_geometry_itr->node_id]);
distance_to_next_coordinate =
distance_to_current_coordinate +
util::coordinate_calculation::haversineDistance(current_coordinate, next_coordinate);
// reached point where coordinates switch between
if (distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH)
return util::coordinate_calculation::interpolateLinear(
getFactor(distance_to_current_coordinate, distance_to_next_coordinate),
current_coordinate, next_coordinate);
// prepare for next iteration
current_coordinate = next_coordinate;
distance_to_current_coordinate = distance_to_next_coordinate;
}
distance_to_next_coordinate =
distance_to_current_coordinate +
util::coordinate_calculation::haversineDistance(current_coordinate, final_coordinate);
// reached point where coordinates switch between
if (distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH)
return util::coordinate_calculation::interpolateLinear(
getFactor(distance_to_current_coordinate, distance_to_next_coordinate),
current_coordinate, final_coordinate);
else
return final_coordinate;
}
} // namespace detail
// Finds a (potentially inteprolated) coordinate that is DESIRED_SEGMENT_LENGTH away
// from the start of an edge
inline util::Coordinate
getRepresentativeCoordinate(const NodeID from_node,
const NodeID to_node,
const EdgeID via_edge_id,
const bool traverse_in_reverse,
const extractor::CompressedEdgeContainer &compressed_geometries,
const std::vector<extractor::QueryNode> &query_nodes)
{
const auto extractCoordinateFromNode = [](const extractor::QueryNode &node) -> util::Coordinate
{
return {node.lon, node.lat};
};
// Uncompressed roads are simple, return the coordinate at the end
if (!compressed_geometries.HasEntryForID(via_edge_id))
{
return extractCoordinateFromNode(traverse_in_reverse ? query_nodes[from_node]
: query_nodes[to_node]);
}
else
{
const auto &geometry = compressed_geometries.GetBucketReference(via_edge_id);
const auto base_node_id = (traverse_in_reverse) ? to_node : from_node;
const auto base_coordinate = extractCoordinateFromNode(query_nodes[base_node_id]);
const auto final_node = (traverse_in_reverse) ? from_node : to_node;
const auto final_coordinate = extractCoordinateFromNode(query_nodes[final_node]);
if (traverse_in_reverse)
return detail::getCoordinateFromCompressedRange(
base_coordinate, geometry.rbegin(), geometry.rend(), final_coordinate, query_nodes);
else
return detail::getCoordinateFromCompressedRange(
base_coordinate, geometry.begin(), geometry.end(), final_coordinate, query_nodes);
}
}
// shift an instruction around the degree circle in CCW order
inline DirectionModifier forcedShiftCCW(const DirectionModifier modifier)
{
return static_cast<DirectionModifier>((static_cast<std::uint32_t>(modifier) + 1) %
detail::num_direction_modifiers);
}
inline DirectionModifier shiftCCW(const DirectionModifier modifier)
{
if (detail::shiftable_ccw[static_cast<int>(modifier)])
return forcedShiftCCW(modifier);
else
return modifier;
}
// shift an instruction around the degree circle in CW order
inline DirectionModifier forcedShiftCW(const DirectionModifier modifier)
{
return static_cast<DirectionModifier>(
(static_cast<std::uint32_t>(modifier) + detail::num_direction_modifiers - 1) %
detail::num_direction_modifiers);
}
inline DirectionModifier shiftCW(const DirectionModifier modifier)
{
if (detail::shiftable_cw[static_cast<int>(modifier)])
return forcedShiftCW(modifier);
else
return modifier;
}
inline bool isBasic(const TurnType type)
{
return type == TurnType::Turn || type == TurnType::EndOfRoad;
}
inline bool isUturn(const TurnInstruction instruction)
{
return isBasic(instruction.type) && instruction.direction_modifier == DirectionModifier::UTurn;
}
inline bool resolve(TurnInstruction &to_resolve, const TurnInstruction neighbor, bool resolve_cw)
{
const auto shifted_turn = resolve_cw ? shiftCW(to_resolve.direction_modifier)
: shiftCCW(to_resolve.direction_modifier);
if (shifted_turn == neighbor.direction_modifier ||
shifted_turn == to_resolve.direction_modifier)
return false;
to_resolve.direction_modifier = shifted_turn;
return true;
}
inline bool resolveTransitive(TurnInstruction &first,
TurnInstruction &second,
const TurnInstruction third,
bool resolve_cw)
{
if (resolve(second, third, resolve_cw))
{
first.direction_modifier =
resolve_cw ? shiftCW(first.direction_modifier) : shiftCCW(first.direction_modifier);
return true;
}
return false;
}
inline bool isSlightTurn(const TurnInstruction turn)
{
return (isBasic(turn.type) || turn.type == TurnType::NoTurn) &&
(turn.direction_modifier == DirectionModifier::Straight ||
turn.direction_modifier == DirectionModifier::SlightRight ||
turn.direction_modifier == DirectionModifier::SlightLeft);
}
inline bool isSlightModifier(const DirectionModifier direction_modifier)
{
return (direction_modifier == DirectionModifier::Straight ||
direction_modifier == DirectionModifier::SlightRight ||
direction_modifier == DirectionModifier::SlightLeft);
}
inline bool isSharpTurn(const TurnInstruction turn)
{
return isBasic(turn.type) && (turn.direction_modifier == DirectionModifier::SharpLeft ||
turn.direction_modifier == DirectionModifier::SharpRight);
}
inline bool isStraight(const TurnInstruction turn)
{
return (isBasic(turn.type) || turn.type == TurnType::NoTurn) &&
turn.direction_modifier == DirectionModifier::Straight;
}
inline bool isConflict(const TurnInstruction first, const TurnInstruction second)
{
return (first.type == second.type && first.direction_modifier == second.direction_modifier) ||
(isStraight(first) && isStraight(second));
}
inline DiscreteAngle discretizeAngle(const double angle)
{
BOOST_ASSERT(angle >= 0. && angle <= 360.);
return DiscreteAngle(static_cast<std::uint8_t>(angle / detail::discrete_angle_step_size));
}
inline double angleFromDiscreteAngle(const DiscreteAngle angle)
{
return static_cast<double>(angle) * detail::discrete_angle_step_size;
}
inline double angularDeviation(const double angle, const double from)
{
const double deviation = std::abs(angle - from);
return std::min(360 - deviation, deviation);
}
inline double getAngularPenalty(const double angle, DirectionModifier modifier)
{
// these are not aligned with getTurnDirection but represent an ideal center
const double center[] = {0, 45, 90, 135, 180, 225, 270, 315};
return angularDeviation(center[static_cast<int>(modifier)], angle);
}
inline double getTurnConfidence(const double angle, TurnInstruction instruction)
{
// special handling of U-Turns and Roundabout
if (!isBasic(instruction.type) || instruction.direction_modifier == DirectionModifier::UTurn)
return 1.0;
const double deviations[] = {0, 45, 50, 30, 20, 30, 50, 45};
const double difference = getAngularPenalty(angle, instruction.direction_modifier);
const double max_deviation = deviations[static_cast<int>(instruction.direction_modifier)];
return 1.0 - (difference / max_deviation) * (difference / max_deviation);
}
// Translates between angles and their human-friendly directional representation
inline DirectionModifier getTurnDirection(const double angle)
{
// An angle of zero is a u-turn
// 180 goes perfectly straight
// 0-180 are right turns
// 180-360 are left turns
if (angle > 0 && angle < 60)
return DirectionModifier::SharpRight;
if (angle >= 60 && angle < 140)
return DirectionModifier::Right;
if (angle >= 140 && angle < 170)
return DirectionModifier::SlightRight;
if (angle >= 165 && angle <= 195)
return DirectionModifier::Straight;
if (angle > 190 && angle <= 220)
return DirectionModifier::SlightLeft;
if (angle > 220 && angle <= 300)
return DirectionModifier::Left;
if (angle > 300 && angle < 360)
return DirectionModifier::SharpLeft;
return DirectionModifier::UTurn;
}
// swaps left <-> right modifier types
inline DirectionModifier mirrorDirectionModifier(const DirectionModifier modifier)
{
const constexpr DirectionModifier results[] = {DirectionModifier::UTurn,
DirectionModifier::SharpLeft,
DirectionModifier::Left,
DirectionModifier::SlightLeft,
DirectionModifier::Straight,
DirectionModifier::SlightRight,
DirectionModifier::Right,
DirectionModifier::SharpRight};
return results[modifier];
}
inline bool canBeSuppressed(const TurnType type)
{
if (type == TurnType::Turn)
return true;
return false;
}
inline bool isLowPriorityRoadClass(const FunctionalRoadClass road_class)
{
return road_class == FunctionalRoadClass::LOW_PRIORITY_ROAD ||
road_class == FunctionalRoadClass::SERVICE;
}
inline bool isDistinct(const DirectionModifier first, const DirectionModifier second)
{
if ((first + 1) % detail::num_direction_modifiers == second)
return false;
if ((second + 1) % detail::num_direction_modifiers == first)
return false;
return true;
}
inline bool requiresNameAnnounced(const std::string &from, const std::string &to)
{
// FIXME, handle in profile to begin with?
// this uses the encoding of references in the profile, which is very BAD
// Input for this function should be a struct separating streetname, suffix (e.g. road,
// boulevard, North, West ...), and a list of references
std::string from_name;
std::string from_ref;
std::string to_name;
std::string to_ref;
// Split from the format "{name} ({ref})" -> name, ref
auto split = [](const std::string &name, std::string &out_name, std::string &out_ref)
{
const auto ref_begin = name.find_first_of('(');
if (ref_begin != std::string::npos)
{
out_name = name.substr(0, ref_begin);
out_ref = name.substr(ref_begin + 1, name.find_first_of(')') - 1);
}
else
{
out_name = name;
}
};
split(from, from_name, from_ref);
split(to, to_name, to_ref);
// check similarity of names
auto names_are_empty = from_name.empty() && to_name.empty();
auto names_are_equal = from_name == to_name;
auto name_is_removed = !from_name.empty() && to_name.empty();
// references are contained in one another
auto refs_are_empty = from_ref.empty() && to_ref.empty();
auto ref_is_contained =
!from_ref.empty() && !to_ref.empty() &&
(from_ref.find(to_ref) != std::string::npos || to_ref.find(from_ref) != std::string::npos);
auto ref_is_removed = !from_ref.empty() && to_ref.empty();
auto obvious_change = ref_is_contained || names_are_equal ||
(names_are_empty && refs_are_empty) || name_is_removed || ref_is_removed;
return !obvious_change;
}
inline int getPriority( const FunctionalRoadClass road_class )
{
//The road priorities indicate which roads can bee seen as more or less equal.
//They are used in Fork-Discovery. Possibly should be moved to profiles post v5?
//A fork can happen between road types that are at most 1 priority apart from each other
const constexpr int road_priority[] = {10, 0, 10, 2, 10, 4, 10, 6, 10, 8, 10, 11, 10, 12, 10, 14};
return road_priority[static_cast<int>(road_class)];
}
inline bool canBeSeenAsFork(const FunctionalRoadClass first, const FunctionalRoadClass second)
{
// forks require similar road categories
// Based on the priorities assigned above, we can set forks only if the road priorities match closely.
// Potentially we could include features like number of lanes here and others?
// Should also be moved to profiles
return std::abs(getPriority(first) - getPriority(second)) <= 1;
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif // OSRM_GUIDANCE_TOOLKIT_HPP_

View File

@ -0,0 +1,202 @@
#ifndef OSRM_EXTRACTOR_TURN_ANALYSIS
#define OSRM_EXTRACTOR_TURN_ANALYSIS
#include "extractor/guidance/turn_classification.hpp"
#include "extractor/guidance/toolkit.hpp"
#include "extractor/restriction_map.hpp"
#include "extractor/compressed_edge_container.hpp"
#include "util/name_table.hpp"
#include <cstdint>
#include <string>
#include <vector>
#include <memory>
#include <utility>
#include <unordered_set>
namespace osrm
{
namespace extractor
{
namespace guidance
{
// What is exposed to the outside
struct TurnOperation final
{
EdgeID eid;
double angle;
TurnInstruction instruction;
};
// For the turn analysis, we require a full list of all connected roads to determine the outcome.
// Invalid turns can influence the perceived angles
//
// aaa(2)aa
// a - bbbbb
// aaa(1)aa
//
// will not be perceived as a turn from (1) -> b, and as a U-turn from (1) -> (2).
// In addition, they can influence whether a turn is obvious or not.
struct ConnectedRoad final
{
ConnectedRoad(const TurnOperation turn, const bool entry_allowed = false);
TurnOperation turn;
bool entry_allowed; // a turn may be relevant to good instructions, even if we cannot take
// the road
std::string toString() const
{
std::string result = "[connection] ";
result += std::to_string(turn.eid);
result += " allows entry: ";
result += std::to_string(entry_allowed);
result += " angle: ";
result += std::to_string(turn.angle);
result += " instruction: ";
result += std::to_string(static_cast<std::int32_t>(turn.instruction.type)) + " " +
std::to_string(static_cast<std::int32_t>(turn.instruction.direction_modifier));
return result;
}
};
class TurnAnalysis
{
public:
TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
const std::vector<QueryNode> &node_info_list,
const RestrictionMap &restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const CompressedEdgeContainer &compressed_edge_container,
const util::NameTable &name_table);
// the entry into the turn analysis
std::vector<TurnOperation> getTurns(const NodeID from_node, const EdgeID via_eid) const;
private:
const util::NodeBasedDynamicGraph &node_based_graph;
const std::vector<QueryNode> &node_info_list;
const RestrictionMap &restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const CompressedEdgeContainer &compressed_edge_container;
const util::NameTable &name_table;
// Check for restrictions/barriers and generate a list of valid and invalid turns present at
// the
// node reached
// from `from_node` via `via_eid`
// The resulting candidates have to be analysed for their actual instructions later on.
std::vector<ConnectedRoad> getConnectedRoads(const NodeID from_node,
const EdgeID via_eid) const;
// Merge segregated roads to omit invalid turns in favor of treating segregated roads as
// one.
// This function combines roads the following way:
//
// * *
// * is converted to *
// v ^ +
// v ^ +
//
// The treatment results in a straight turn angle of 180º rather than a turn angle of approx
// 160
std::vector<ConnectedRoad> mergeSegregatedRoads(std::vector<ConnectedRoad> intersection) const;
// TODO distinguish roundabouts and rotaries
// TODO handle bike/walk cases that allow crossing a roundabout!
// Processing of roundabouts
// Produces instructions to enter/exit a roundabout or to stay on it.
// Performs the distinction between roundabout and rotaries.
std::vector<ConnectedRoad> handleRoundabouts(const EdgeID via_edge,
const bool on_roundabout,
const bool can_exit_roundabout,
std::vector<ConnectedRoad> intersection) const;
// Indicates a Junction containing a motoryway
bool isMotorwayJunction(const EdgeID via_edge,
const std::vector<ConnectedRoad> &intersection) const;
// Decide whether a turn is a turn or a ramp access
TurnType findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const;
// Get the Instruction for an obvious turn
// Instruction will be a silent instruction
TurnInstruction getInstructionForObvious(const std::size_t number_of_candidates,
const EdgeID via_edge,
const ConnectedRoad &candidate) const;
// Helper Function that decides between NoTurn or NewName
TurnInstruction
noTurnOrNewName(const NodeID from, const EdgeID via_edge, const ConnectedRoad &candidate) const;
// Basic Turn Handling
// Dead end.
std::vector<ConnectedRoad> handleOneWayTurn(std::vector<ConnectedRoad> intersection) const;
// Mode Changes, new names...
std::vector<ConnectedRoad> handleTwoWayTurn(const EdgeID via_edge,
std::vector<ConnectedRoad> intersection) const;
// Forks, T intersections and similar
std::vector<ConnectedRoad> handleThreeWayTurn(const EdgeID via_edge,
std::vector<ConnectedRoad> intersection) const;
// Handling of turns larger then degree three
std::vector<ConnectedRoad> handleComplexTurn(const EdgeID via_edge,
std::vector<ConnectedRoad> intersection) const;
// Any Junction containing motorways
std::vector<ConnectedRoad> handleMotorwayJunction(
const EdgeID via_edge, std::vector<ConnectedRoad> intersection) const;
std::vector<ConnectedRoad> handleFromMotorway(const EdgeID via_edge,
std::vector<ConnectedRoad> intersection) const;
std::vector<ConnectedRoad> handleMotorwayRamp(const EdgeID via_edge,
std::vector<ConnectedRoad> intersection) const;
// Utility function, setting basic turn types. Prepares for normal turn handling.
std::vector<ConnectedRoad> setTurnTypes(const NodeID from,
const EdgeID via_edge,
std::vector<ConnectedRoad> intersection) const;
// Assignment of specific turn types
void assignFork(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
void assignFork(const EdgeID via_edge,
ConnectedRoad &left,
ConnectedRoad &center,
ConnectedRoad &right) const;
void
handleDistinctConflict(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
// Type specific fallbacks
std::vector<ConnectedRoad>
fallbackTurnAssignmentMotorway(std::vector<ConnectedRoad> intersection) const;
// Classification
std::size_t findObviousTurn(const EdgeID via_edge,
const std::vector<ConnectedRoad> &intersection) const;
std::pair<std::size_t, std::size_t>
findFork(const std::vector<ConnectedRoad> &intersection) const;
std::vector<ConnectedRoad> assignLeftTurns(const EdgeID via_edge,
std::vector<ConnectedRoad> intersection,
const std::size_t starting_at) const;
std::vector<ConnectedRoad> assignRightTurns(const EdgeID via_edge,
std::vector<ConnectedRoad> intersection,
const std::size_t up_to) const;
}; // class TurnAnalysis
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif // OSRM_EXTRACTOR_TURN_ANALYSIS

View 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_

View File

@ -0,0 +1,127 @@
#ifndef OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_
#define OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_
#include <cstdint>
#include <boost/assert.hpp>
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace detail
{
// inclusive bounds for turn modifiers
const constexpr uint8_t num_direction_modifiers = 8;
} // detail
// direction modifiers based on angle
// Would be nice to have
// enum class DirectionModifier : unsigned char
enum DirectionModifier
{
UTurn,
SharpRight,
Right,
SlightRight,
Straight,
SlightLeft,
Left,
SharpLeft
};
// enum class TurnType : unsigned char
enum TurnType // at the moment we can support 32 turn types, without increasing memory consumption
{
Invalid, // no valid turn instruction
NoTurn, // end of segment without turn/middle of a segment
Suppressed, // location that suppresses a turn
NewName, // no turn, but name changes
Continue, // remain on a street
Turn, // basic turn
FirstTurn, // First of x turns
SecondTurn, // Second of x turns
ThirdTurn, // Third of x turns
FourthTurn, // Fourth of x turns
Merge, // merge onto a street
Ramp, // special turn (highway ramp exits)
FirstRamp, // first turn onto a ramp
SecondRamp, // second turn onto a ramp
ThirdRamp, // third turn onto a ramp
FourthRamp, // fourth turn onto a ramp
Fork, // fork road splitting up
EndOfRoad, // T intersection
EnterRoundabout, // Entering a small Roundabout
EnterRoundaboutAtExit, // Entering a small Roundabout at a countable exit
EnterAndExitRoundabout, // Touching a roundabout
ExitRoundabout, // Exiting a small Roundabout
EnterRotary, // Enter a rotary
EnterRotaryAtExit, // Enter A Rotary at a countable exit
EnterAndExitRotary, // Touching a rotary
ExitRotary, // Exit a rotary
StayOnRoundabout, // Continue on Either a small or a large Roundabout
Restriction, // Cross a Barrier, requires barrier penalties instead of full block
Notification // Travel Mode Changes`
};
// turn angle in 1.40625 degree -> 128 == 180 degree
struct TurnInstruction
{
TurnInstruction(const TurnType type = TurnType::Invalid,
const DirectionModifier direction_modifier = DirectionModifier::Straight)
: type(type), direction_modifier(direction_modifier)
{
}
TurnType type : 5;
DirectionModifier direction_modifier : 3;
static TurnInstruction INVALID()
{
return TurnInstruction(TurnType::Invalid, DirectionModifier::UTurn);
}
static TurnInstruction NO_TURN()
{
return TurnInstruction(TurnType::NoTurn, DirectionModifier::UTurn);
}
static TurnInstruction REMAIN_ROUNDABOUT(const DirectionModifier modifier)
{
return TurnInstruction(TurnType::StayOnRoundabout, modifier);
}
static TurnInstruction ENTER_ROUNDABOUT(const DirectionModifier modifier)
{
return TurnInstruction(TurnType::EnterRoundabout, modifier);
}
static TurnInstruction EXIT_ROUNDABOUT(const DirectionModifier modifier)
{
return TurnInstruction(TurnType::ExitRoundabout, modifier);
}
static TurnInstruction SUPPRESSED(const DirectionModifier modifier)
{
return TurnInstruction{TurnType::Suppressed, modifier};
}
};
inline bool operator!=(const TurnInstruction lhs, const TurnInstruction rhs)
{
return lhs.type != rhs.type || lhs.direction_modifier != rhs.direction_modifier;
}
inline bool operator==(const TurnInstruction lhs, const TurnInstruction rhs)
{
return lhs.type == rhs.type && lhs.direction_modifier == rhs.direction_modifier;
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif // OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_

View File

@ -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()

View File

@ -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))
{
}

View File

@ -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;
};
}

View File

@ -0,0 +1,48 @@
#ifndef PROFILE_PROPERTIES_HPP
#define PROFILE_PROPERTIES_HPP
#include <boost/numeric/conversion/cast.hpp>
namespace osrm
{
namespace extractor
{
struct ProfileProperties
{
ProfileProperties()
: traffic_signal_penalty(0), u_turn_penalty(0), allow_u_turn_at_via(false), use_turn_restrictions(false)
{
}
double GetUturnPenalty() const
{
return u_turn_penalty / 10.;
}
void SetUturnPenalty(const double u_turn_penalty_)
{
u_turn_penalty = boost::numeric_cast<int>(u_turn_penalty_ * 10.);
}
double GetTrafficSignalPenalty() const
{
return traffic_signal_penalty / 10.;
}
void SetTrafficSignalPenalty(const double traffic_signal_penalty_)
{
traffic_signal_penalty = boost::numeric_cast<int>(traffic_signal_penalty_ * 10.);
}
//! penalty to cross a traffic light in deci-seconds
int traffic_signal_penalty;
//! penalty to do a uturn in deci-seconds
int u_turn_penalty;
bool allow_u_turn_at_via;
bool use_turn_restrictions;
};
}
}
#endif

View File

@ -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);
}
};
}

View File

@ -19,6 +19,8 @@ namespace osrm
namespace extractor
{
class ProfileProperties;
/**
* Parses the relations that represents turn restrictions.
*
@ -40,11 +42,10 @@ namespace extractor
class RestrictionParser
{
public:
RestrictionParser(lua_State *lua_state);
RestrictionParser(lua_State *lua_state, const ProfileProperties& properties);
boost::optional<InputRestrictionContainer> TryParse(const osmium::Relation &relation) const;
private:
void ReadUseRestrictionsSetting(lua_State *lua_state);
void ReadRestrictionExceptions(lua_State *lua_state);
bool ShouldIgnoreRestriction(const std::string &except_tag_string) const;

Some files were not shown because too many files have changed in this diff Show More