Compare commits

...

82 Commits

Author SHA1 Message Date
Patrick Niklaus fdbcefe358 Final OSRM release 2018-05-10 12:37:26 +00:00
Patrick Niklaus 853691aee2 Bump version 2018-05-08 15:56:18 +00:00
Patrick Niklaus 3b4e2e83ef Fix excluding nodes for one to all query 2018-05-08 15:50:25 +00:00
Patrick Niklaus c459530cb6 Address PR comments 2018-05-08 15:50:25 +00:00
Kajari Ghosh 2a15e6dec8 Add support for annotations=distances in MLD
This commit brings feature parity with CH for the `table` pluging.
2018-05-08 15:50:25 +00:00
Huyen Chau Nguyen 1a1293608d fix example requests for distance tables 2018-05-02 17:25:01 +02:00
Michael Krasnyk 318df9deaa Adjust ebn_weight assertion to new node weights 2018-05-01 18:53:04 +02:00
Michael Krasnyk cacb162520 Review and rebase updates 2018-04-28 08:59:18 +02:00
Michael Krasnyk 105709cb43 Don't handle response errors separately in osrm-runner 2018-04-28 08:59:18 +02:00
Michael Krasnyk 9695fa7941 Remove weight and duration from customizer::EdgeBasedGraphEdgeData 2018-04-28 08:59:18 +02:00
Michael Krasnyk fd9bebbfa7 Add EBG node durations 2018-04-28 08:59:18 +02:00
Michael Krasnyk db18e8669f Always read .osrm.enw file in updater 2018-04-28 08:59:18 +02:00
Michael Krasnyk 9b4a4fdd82 Add customizer::MultiLevelGraph 2018-04-28 08:59:18 +02:00
Michael Krasnyk cdc7e5f021 Fix for updating node weights in contraction hierarchy 2018-04-28 08:59:18 +02:00
Michael Krasnyk adc87149e2 List dataset blocks 2018-04-28 08:59:18 +02:00
Michael Krasnyk 8adbfe06ed Throw an exception if a shared region is not found 2018-04-28 08:59:18 +02:00
Patrick Niklaus ae805f9ec8 Make cheap rulers static 2018-04-27 05:21:08 +02:00
Patrick Niklaus be24689b0f Optimize distance calculation by getting rid of rounding 2018-04-27 05:21:08 +02:00
Michael Krasnyk 41c31a2388 Use mapbox::util::optional instead of std::experimental::optional 2018-04-27 05:21:08 +02:00
Huyen Chau Nguyen 8b866502d1 make eslint happy 2018-04-27 05:21:08 +02:00
Huyen Chau Nguyen 7837cd61df clang format 2018-04-27 05:21:08 +02:00
Huyen Chau Nguyen b573485c31 only use half of rulers because earth is symmetric 2018-04-27 05:21:08 +02:00
Huyen Chau Nguyen 4e68f3a7e1 change haversine computation to cheap ruler in distance tables 2018-04-27 05:21:08 +02:00
Huyen Chau Nguyen 5ba26d3d6d refactor cheap ruler cache 2018-04-27 05:21:08 +02:00
Daniel Patterson a6cf2eee7e Use faster method for calculating distances.
fix cmakelist
2018-04-27 05:21:08 +02:00
Patrick Niklaus 6843eb1479 Add changelog entries for profile changtes 2018-04-26 21:46:20 +02:00
Patrick Niklaus b331885d3c Add test fo excluding ferries on bikes 2018-04-26 21:46:20 +02:00
Patrick Niklaus 73716bd651 Add support for classes on bicycle profile 2018-04-26 21:46:20 +02:00
Michael Krasnyk 9edd161da3 Test for "No turn issued for right turn on un-named roads", #4414
The issue is fixed by d166fc6a75
2018-04-26 18:53:47 +02:00
Kajari Ghosh 1bc3ff6491 fix the ascii art to make sense 2018-04-25 23:51:27 +02:00
Kajari Ghosh b0f2ef287e fix the offset case when there is no packed path to unpack 2018-04-25 23:51:27 +02:00
Patrick Niklaus 16f53ff81a Sort unpacked paths by sharing 2018-04-25 16:07:31 +02:00
Patrick Niklaus 7a260dc2ba More parameter tuning 2018-04-25 16:07:31 +02:00
Patrick Niklaus 1c0d951f5e Adjust to PR comments 2018-04-25 16:07:31 +02:00
Patrick Niklaus 77a740c0fb Parameter tuning for different route lengths 2018-04-25 16:07:31 +02:00
Patrick Niklaus 61101db8cf Use duration and not weight for sharing 2018-04-25 16:07:31 +02:00
Patrick Niklaus b51632a2fb Chose alternatives parameters based on coordinate distance 2018-04-25 16:07:31 +02:00
Patrick Niklaus 9d10503a9c Replace step function with contious scaling 2018-04-25 16:07:31 +02:00
Patrick Niklaus 7d50e5afe0 Refactor parameters to be selected at runtime 2018-04-25 16:07:31 +02:00
Patrick Niklaus 549216c792 Compute sharing weighted by edge weight
This ensures that small insignificat turns don't affect the overall
sharing metric too much.
2018-04-25 16:07:31 +02:00
Patrick Niklaus 1990de7dcc Fix inserting edges of shortest path for sharing check
This caused very sub-optimal routes to be selected.
2018-04-25 16:07:31 +02:00
Kajari Ghosh 89f6e2d55b Parse table annotations param correctly (#5050)
* fix incorrect parameter parsing for node osrm and add tests

* fix boost spirit grammar parsing for annotations

* return NotImplemented when distance annotation is requested for MLD in table plugin

* update docs
2018-04-24 11:05:35 -04:00
Michael Krasnyk c628ecbf24 Update tile unit tests 2018-04-24 15:40:57 +02:00
Jochen Topf b6f19cd544 Use vtzero in tile plugin.
This uses the new vtzero library (https://github.com/mapbox/vtzero)
instead of using protozero directly.
2018-04-24 15:40:57 +02:00
Kajari Ghosh 14860b62e9 Unpack paths and return total distance in matrix plugin for CH (#4990) 2018-04-20 18:18:55 -04:00
Patrick Niklaus 9970b7d580 Merge pull request #5037 from Project-OSRM/subtrees-protozero-vtzero
Update libosmium and add protozero and vtzero libraries
2018-04-20 20:44:36 +02:00
Patrick Niklaus 7feb79ef91 Merge branch 'master' into subtrees-protozero-vtzero 2018-04-20 13:32:49 +00:00
Antoine Giret 6f84812903 Update profiles.md 2018-04-20 15:29:16 +02:00
Karen Shea 8085e86170 Change emoji 2018-04-20 13:19:48 +02:00
Karen Shea 35973576d9 Update releasing.md 2018-04-20 13:19:06 +02:00
Duane Gearhart 7740d5d7c0 Do not combine a segregated edge with a roundabout (#5040)
* Do not combine a segregated edge with a roundabout, add test
2018-04-20 11:41:53 +02:00
Patrick Niklaus c4eff6cd65 Add option to osrm-runner to limit the distance 2018-04-20 08:14:03 +03:00
Michael Krasnyk 5c4353b46e Removing submodules 2018-04-19 22:13:07 +03:00
Michael Krasnyk 05a5918909 Update CMakeLists.txt file 2018-04-19 22:13:06 +03:00
Michael Krasnyk 80ad38bbc6 Merge commit '6bee8866de99a602039feef463c22c972f0f86aa' as 'third_party/vtzero' 2018-04-19 22:03:52 +03:00
Michael Krasnyk 6bee8866de Squashed 'third_party/vtzero/' content from commit 2b43c8f5c
git-subtree-dir: third_party/vtzero
git-subtree-split: 2b43c8f5c7cc461a874a657673dfc15f8fb21d1d
2018-04-19 22:03:52 +03:00
Michael Krasnyk ba92674c6e Merge commit '68019a1fb20928beaa7b0cb2d8310af29ffe789e' as 'third_party/protozero' 2018-04-19 22:03:49 +03:00
Michael Krasnyk 68019a1fb2 Squashed 'third_party/protozero/' content from commit d5d8debf1
git-subtree-dir: third_party/protozero
git-subtree-split: d5d8debf1b17c6bb652395957b76cde7787e5377
2018-04-19 22:03:49 +03:00
Michael Krasnyk 3a9acde2c3 Merge commit 'a3a7a822e136d2357660d85124d8e7bb26ea6f7c' into subtrees-protozero-vtzero 2018-04-19 22:03:25 +03:00
Michael Krasnyk a3a7a822e1 Squashed 'third_party/libosmium/' changes from ce865381f..d2c1d872b
d2c1d872b Release v2.14.0
9c9aedd50 Update embedded catch.hpp to version 1.12.1.
f94e70d32 Change comment on namespace closing brace to make clang-tidy happy.
ad9f03afc Prefer prefix increment operator.
a141b9c0c Update change log.
363fc7a15 Add more XML tests.
303a3090f Refactor XML tests.
a1ee5dd56 Make util namespace inline.
6f1709a83 Cleanup progress bar code.
1b02b65e0 Use Location setters taking a string in osmium_tiles example.
7ffdc7618 Disable clang-tidy warning.
882631085 Better implementation of str_to_int.
4cbf53725 Fix another bitwise op on signed integer.
903bfac9d Disable clang-tidy warning.
01de3f518 Don't change location if set_lon() or set_lat() functions throw.
9a27e899d Fix bitwise operation on signed integer.
61650224f Disable some clang-tidy warnings.
9dfa8437e Provide a way to better mock the getenv function in tests.
ae2c172a2 Explicitly set integer type for integers in test code.
878850068 Simpler implementation. We don't need to look at errno at all.
8f9b886f5 Formatting
cf4760cb0 Use bit operations on unsigned where possible.
c7cb4cde8 No conversion needed later if we declare it this way.
01357a540 Add missing inline for free function.
3feef81ee Disable clang-tidy warning in place we can't do anything about.
bf083b8ee Avoid binary bitwise operations on signed integers.
f70b1c803 Move content of osmium::util::detail namespace into osmium::detail.
caf2f0033 Remove use of atoi and atol from code.
f564cef07 Formatting.
f0885fc73 Disable travis build with sanitizers.
858c58444 Use explicit asserts and then casts, not static_cast_with_assert.
a613b1b0a Disable clang-tidy warning.
50e327677 Always build and run data tests on travis.
20d7a09b0 Update submodule to add more XML tests.
6c8f652bf Merge pull request #250 from Nakaner/object-comparison-without-timestamp
38ea437c0 Make XML parser more strict.
7788f5108 Remove object_order_type_id_reverse_version_without_timestamp
fe2223d25 Ignore timestamps if only one object has a valid timestamp
a4515161a Code cleanup.
eb5ea1a0e Use explicit conversions.
105905c45 Only disable warning on non-MSVC compilers.
80fc1b35d Change the way IDs are parsed from strings.
78aedf91e More tests for StringMatcher and disable a clang-tidy warning.
644a00752 Move catch.hpp into its own directory.
19842756c Update to newest osm-testdata submodule.
0402f64d7 Fix the problem with "git submodule" in appveyor.
ec4095d32 Cleanup metadata_options code.
d7390741e Add static_cast to avoid conversion warning.
dd16be1c3 Remove else after throw.
ef4d618de Remove unused variable.
0fdb58d13 Check out submodule in appveyor builds.
6406282d3 Merge pull request #248 from Nakaner/metadata-options-more
eb138a902 make clang < 3.9 work
e703b0d1d make sections their own independent test cases
f68888006 detect_available_metadata must be inline
b53224588 move metadata_options into osmium namespace
edd363cfa make const what could be const
302a485e5 remove unnecessary include, add missing include
93a76e07e give osmium::io::metadata_from_object a better name
91dfbc427 update documentation
453cd6395 replace REQUIRE(!foo) by REQUIRE_FALSE(foo)
247c3b021 replace friend function by setter methods
0a62e589c performance improvement: check first character instead using std::strlen()
73568bbff move ctor of metadata_options which taks an OSMObject out of class
1879c9e47 restore metadata_options::to_string
71a303608 add more unit tests for osmium::io::metadata_options
843cdc89c replace osmium::io::metdata_options::to_string fully by operator<<
6402a8793 Fix bug in code which determines if an OSM object has a valid user.
a6d8143ae Move metadata_options out of "detail" subnamespace
61e32df10 add metadata_options::operator|=
d5f7cc0ab Use newest osm-testdata submodule.
31e34f10c add operator&=, operator<< and a to_string method to metadata_options
77d507172 Formatting fixes.
1c38b2a61 Update some URLs. Use https where possible.
10066cb3e Merge pull request #247 from Nakaner/issue-245
3c938ce82 Allow version=-1 and changeset=-1 for PBF input.
9a542c133 Add ReaderWithProgressBar class.
49839a1ce Replace strftime by our own implementation.
b32b90942 Update osm-testdata version used.
f88b6e548 Add more tests.
cb7e778f6 Handle NULL geometries in test cases.
1961a9c95 Clean up some test cases.
d7f45e071 Update osm-testdata submodule.
73a2ef18f Remove check for lost ways in multipolygon assembler.
1244e498a Update submodule also in msys2 build.
229fc2453 Revert "Make util namespace inline."
9f942d2e9 Revert "Add inline declaration to one util namespace we forgot."
40d4835ea Really fix appveyor build (I hope).
5ae071135 Add inline declaration to one util namespace we forgot.
e0916257c Clean up debug message.
2ddc474cc Make util namespace inline.
4b4e25f87 Travis config: Do not install clang-tidy, we are not using it.
5a608fba1 Fix travis build.
a075cc323 Fix appveyor build.
41ee9920b Add submodule with osm-testdata repository.
4d42245c0 Revert "Add clang-tidy build to travis."
9df355b64 Fix issue on some compilers.
c8a3c00d6 Add clang-tidy build to travis.
2fc80ad17 Fixed clang-tidy config file order.
2adf05267 Fix various clang-tidy warnings
1dca92459 Use the right syntax to override clang-tidy settings in the code.
25baecb46 Fix some issues found by clang-tidy.
54a8de6c0 Disable more clang-tidy tests.
de46d562e Also look for clang-tidy-6.0 in CMake config.
2172553d8 Add build with sanitizers on travis.
7e4e45562 Add object comparators ignoring the timestamp.
92fb231a1 Fix test code.
e541df0fc Cleanup test code.
5640269da Add comment in appveyor script about unusual parameter.
acb872376 Workaround for failing appveyor MSYS2 build.
0634bcf70 Set default value for m_file_size member.
3a2fe5781 Set default values on output options.
43d289fda Make metadata_options constructor explicit.
6e54992b4 Update change log.
e54b955cf Update included `catch.hpp` to version 1.12.0.
e50b40d53 Update copyright date.
0d8b401b6 Merge branch 'metadata'
c7da3a3b6 Remove superfluous has_visibles boolean.
7f74d33d3 Some cleanups of metadata code and fixes test.
5b3c34990 Output XML attributes uid and user independently of each other.
384167334 Merge pull request #243 from Nakaner/metadata_other
733f1916c Change path of nuget installs in appveyor config.
e48672999 Debug output from FindOsmium.cmake script is better formatted now.
79663be5d Change appveyor scripts to work without deprecated prebuilt libs.
443f7a9e4 Remove dependency on (win)getopt completely.
c133b8b14 Fix osmium_convert: Long options with = syntax.
b55daf00a Remove dependency on (win)getopt from area_test and convert examples.
92f7275a9 output visibility directly after the object ID in debug format
a771279f0 deprecate format option pbf_add_metadata
18e3f6005 The value "no" should be treated as "none" by metadata_options
c8b0b2e28 re-assign metadata_options instead of calling a public method of it
15bc4a200 Add more flexible metadata handling to PBF output
b085632b7 add more flexible metadata handling to OPL and debug output
a12c65974 Remove now wrong check.
767ad883a Allow PBF DenseNodes with only some attributes to be read.
fd5a7eb6c More flexible metadata encoding in OSM files.
d16c18581 Add instructions for bug reporting.
db52591ab Use __COUNTER__ instead of name for internal dummy name.
89e634dfc Cleanup.
931ec30ca Marks some false positives for clang-tidy google-runtime-int warning.
b59ef02a2 More rule of zero/rule of five fixes.
1b72b3386 Pass by value and use std::move.
b7d7017cc Handle some clang-tidy google-runtime-int warnings.
c043ca016 Fix special functions.
41d9f0795 Use fixed sized ints where possible.
0899a66a7 Revert some changes older compilers don't like.
41e05989a Fix several rule of zero/rule of five issues.
4e78a828f Various fixes based on clang-tidy reports.
636bda372 Default initialize arrays.
466e5aa6a Handle various clang-tidy warnings.
9f47aa07d Disable clang-tidy misc-forwarding-reference-overload warning.
5e97bf29f Mark Reader and Writer non-movable.
3a2221b27 Add minimal tests for thread-safe queue class.
affdeed6f Add some tests.
5ed055d50 Handle some warnings regarding "explicit".
b9d4c9bf5 Disable clang-tidy cert-err58-cpp warning.
254c25349 Make ADL work for begin()/end() of InputIterator<Reader>.
b64cb32ae Disable clang-tidy android-cloexec-* warnings.
6d6934ffc Fix various warnings reported by clang-tidy.
30dcabaed Another travis build fix.
88c59e669 Fix travis build.
2facb461f Fix travis and appveyor builds.
bba631a51 Remove protozero from repository.
aebe4a914 Use pass by value and std::move in some constructors.
c8c24c8fd Name unused parameters in comments.
9c2e321f9 Make deleted special functions public.
265c16284 Fix some problems with older compilers.
174e95211 Modernize class initialization code for some classes.
4e3e584f8 Disable more clang-tidy warnings.
6b53981bb Fix some class initializations.
fe1405ef8 Add/remove special functions for some classes.
c6dd930d9 Disable cppcoreguidelines-pro-type-static-cast-downcast warning.
0d09210ec Disable misc-unused-parameters warning in clang-tidy.
1dbc43112 Refactor to avoid misc-suspicious-string-compare clang-tidy warning.
91262cc86 Merge pull request #232 from worace/wkt-geojson-polygons
438076b09 Cleanup: Avoid else after return/break.
e0368e92c Disable modernize-raw-string-literal and hicpp-no-assembler check.
c822888fc Mark C style cast in macro from a library as NOLINT.
662d4a641 Moving a TypedMemoryMapping is noexcept.
aec1c4526 Use auto (clang-tidy check modernize-use-auto).
64857589d Consistent include order.
d1ce8de00 Disable hicpp-invalid-access-moved clang-tidy check.
0d3902420 Refactor test without SECTION.
32e9e9e1e Rewrite test avoiding nested sections.
eeb532902 Reorder includes according to best practice.
249e7b553 Revert "Add build with sanitizers to travis."
362cb419a Set CC env var in travis config.
9f520eca3 Add build with sanitizers to travis.
cfc18fef0 Travis config cleanup.
212c6cd42 Remove tests that triggers undefined behaviour in the std lib.
f30985884 Bugfix: Avoid undefined behaviour.
90191619a Make test work even if unused memory isn't returned to OS.
876867e68 Make older compilers happy.
925d1a797 Add names for unused parameters.
014b3a056 Avoid repeating the return type from the declaration
c4e705f92 Disable hicpp-invalid-access-moved clang-tidy warnings.
8c03b3827 Disable some warnings.
c49962ffe Refactor test to avoid spurious use-after-move warning.
04a0afbba Sort includes.
2aaab9a89 Use auto where possible.
e2d84109b More changes for old compilers.
5364ca2a8 Changes for old compilers.
098fc1fa2 Move initializations of class members to the declaration.
eea463026 Fix buffer overflow in o5m parser.
5dc74f2d5 Remove check from static cast (and document why).
7a2dfdcdc Add NOLINT for using namespace osmium::builder::attr.
01fa05f1e Clang-tidy config cleanup.
5e8f8df3f Order includes.
6b78d9b24 Various small fixes for problems detected by clang-tidy.
b815f71b7 Fix gdal check in CMake config.
d47487093 Fix integer type error.
dec9de9ec Bugfix: Throw if elemens in XML file are nested too deep.
85e37723b Various small cleanups.
5f40d726a Additional checks in o5m parser.
b59b4b6a6 Check before static cast and throw.
e57b15700 Disable compiling some header files if gdal lib is not available.
1d3056646 Better checking that PBF data is in range.
c586521d3 Add some consts to the code.
df17e8189 Trying to get the types right...
8e9b759e0 Check read and write system call for EINTR.
28c676054 Rename license file so that Github finds it.
be29e9a52 Remove duplicate space character.
36c14136f Update protozero to newest version (1.6.0).
a19181998 Make libosmium work with different protozero versions.
5c5d9fb36 Use tag and type from protozero to make PBF parser more robust.
03f894400 Add repology.org badge to README.md.
1840739dd Merge pull request #234 from AMDmi3/patch-1
d8e737196 Add support for DragonFly BSD
8de181c8d Revert "Return const object from postfix ++ operator."
0dfb1d649 Add polygon implementation for WKT and GeoJSON Geometry Factories
150e05aea Fix indentation.
6fdee974a Merge pull request #229 from alex85k/no-winsock
f302f08ce Merge pull request #228 from alex85k/fix-test-win
bfb60c888 remove ws2_32 from needed libraries
ef187851c no winsock2 on windows: replace htonl with protozero::detail::byteswap_inplace
9343f8453 Fix testdata-multipolygon test on Windows (executable path differs)
381cf9272 Do not return const from postfix increment operator.
46ec985e9 Add const version of OutputIterator::operator*.
c67de374f Disable clang-tidy test for const params.
f5eda165f Make functions noexcept and return const value from post increment op.
1e4e3160f Use {} when calling constructors. Use .empty() to check empty strings.
8893d16be Mark some tests using size() with NOLINT.
f8a74a8aa Make functions noexcept.
45c1e072c Use REQUIRE_FALSE(...) instead of REQUIRE(!...) for readability.
fc173dae7 Merge pull request #227 from alex85k/msys2-appveor
0a281c064 Add MSYS2 building to Appveyor
c93f73120 fix FindGem for Windows Ruby distributions
8bf6702b3 Merge pull request #226 from alex85k/master
e8dad731d gmtime_s should be used on both MSVC and GCC from MinGW64
d0d98e9c1 remove "osmium::util::MemoryMapping::" from class body
6830ee9bd ftruncate is not available only on MSVC (MinGW has it)
c0597584f Disable some clang-tidy checks.
578db8e4a Use empty() instead of size() or == "" to check for empty collections.
53c8f3a21 Return const object from postfix ++ operator.
bd5e4d109 Use emplace_back() instead of push_back().
0a0a4f24c Use correct C++ header.
35226ea86 Remove unused move.
df843a9cf Fix header guard.
759e76131 Avoid default argument on virtual function.
db4f1bdb9 Update included catch.hpp to version 1.10.0.
59e70e5c6 Pass constructor arguments by value and std::move() into members.
ee9b598ed Add tests for thread_handler.
ac661209a CMake config: Update list of clang-tidy versions we look for.
d7281215c Add comment to unusual code.
cbb4c09fb Split up some overlong test functions.
9870a6b90 Fix noexcept specification.
5e27c37d0 Use array instead of map to store input/output format creators.
987d12922 Mark some constructors noexcept.
17f4d8b7e Avoid static variable only needed as constant value.
fe705cef0 Avoid else after return in several cases.
d4a85878e Beautifying travis config.
f07d1e375 Make some special functions noexcept.
4bb0f7f22 Do not declare special functions on some classes.
bc6c5943d Disable some clang-tidy checks that produce false positives.
6b6598a5b Remove superfluous std::move() of POD type.
71c3f5e16 Make some special functions noexcept.
1cb32778c Remove explicit true/false in tests.
929ba89cf Always catch by const ref.
a9a255820 Code readability: No else clause after return.
f14364bd3 Code formatting fixes.
f41feb6a5 Simplify code.
727f6eff4 Mark dummy includes for IWYU.
72d20f835 Switch travis build to 'trusty'.
c480e6808 Do not run gcov from codecov script.
d16c6e90a Clean up lambda captures in xml_input_format.hpp.

git-subtree-dir: third_party/libosmium
git-subtree-split: d2c1d872bc915d9579350007383ce1ebfea3665f
2018-04-19 22:03:25 +03:00
Michael Krasnyk c7d22c2b92 Update dependencies script 2018-04-19 22:03:20 +03:00
Michael Krasnyk b2aeb47630 Remove dummy edges before inplace permutation 2018-04-19 12:18:48 +02:00
Patrick Niklaus 0a1d1901cc Fix help text and banner 2018-04-17 18:02:28 +02:00
Patrick Niklaus 29db0c80d9 Use CSV stringify to properly escape data in CSV 2018-04-17 18:02:28 +02:00
Huyen Chau Nguyen 4654751872 add dev dependencies for osrm runner 2018-04-17 18:02:28 +02:00
Marcel Radischat b5b18d8afc Update profiles.md
Add api-version 4 for lua profiles as latest api-version
2018-04-17 17:23:08 +02:00
Huyen Chau Nguyen 15139c934f add changelog entry 2018-04-16 11:39:44 +02:00
Huyen Chau Nguyen 7b8619e37c adjust tests to changes 2018-04-16 11:39:44 +02:00
Huyen Chau Nguyen f52b5c31b6 refactor bike profile 2018-04-16 11:39:44 +02:00
Huyen Chau Nguyen e98859e4c0 make primaries and secondaries more dangerous
only apply oneway safety considerations on primaries

re-adjust primary and secondary safeties
2018-04-16 11:39:44 +02:00
Patrick Niklaus c5cc4c5a74 Remove double log printing 2018-04-13 10:13:57 +00:00
karenzshea e3b831364f empty list of shmem regions if none found 2018-04-13 12:07:10 +02:00
karenzshea 730d2b5ef2 log err instead of throwing when no shmem regions found 2018-04-13 12:07:10 +02:00
Michael Krasnyk 16abee1022 Avoid using signed integers for edge IDs 2018-04-12 10:03:37 +02:00
Patrick Niklaus 69fa52a010 Use byte based tar size encoding above 8GB 2018-04-10 21:12:39 +02:00
Patrick Niklaus e1efa4c6ab Add test case for writing and reading huge tar file
This test case is disabled by default because it needs too much storage,
but serves as documentation for the future.
2018-04-10 21:12:39 +02:00
Michael Krasnyk 17cd1575f6 Use base-256 encoding for files larger 68G
Reference:
http://lists.busybox.net/pipermail/busybox/2011-May/075596.html
2018-04-10 21:12:39 +02:00
Michael Krasnyk 3cd8e0fef8 Use 12 octal digits in mtar_raw_header_t::size 2018-04-10 21:12:39 +02:00
Michael Krasnyk 397bb694fd Fix mtar file size truncation to 4G 2018-04-10 21:12:39 +02:00
Patrick Niklaus 473ebfcbf6 Fix debug printing for coordinates 2018-04-10 18:08:18 +02:00
Patrick Niklaus a06171438e Fix hard-coded hint strings in unit-tests 2018-04-10 18:08:18 +02:00
Patrick Niklaus ea0881553e [skip ci] Bump master to 5.18 2018-04-09 13:14:43 +00:00
871 changed files with 66994 additions and 6848 deletions
+1
View File
@@ -13,6 +13,7 @@ notifications:
branches:
only:
- master
- "5.18"
# enable building tags
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
+20 -1
View File
@@ -1,4 +1,21 @@
# UNRELEASED
# 5.18.0
- Changes from 5.17.0:
- Features:
- ADDED: `table` plugin now optionally returns `distance` matrix as part of response [#4990](https://github.com/Project-OSRM/osrm-backend/pull/4990)
- ADDED: New optional parameter `annotations` for `table` that accepts `distance`, `duration`, or both `distance,duration` as values [#4990](https://github.com/Project-OSRM/osrm-backend/pull/4990)
- Infrastructure:
- ADDED: Updated libosmium and added protozero and vtzero libraries [#5037](https://github.com/Project-OSRM/osrm-backend/pull/5037)
- CHANGED: Use vtzero library in tile plugin [#4686](https://github.com/Project-OSRM/osrm-backend/pull/4686)
- Profile:
- ADDED: Bicycle profile now returns classes for ferry and tunnel routes. [#5054](https://github.com/Project-OSRM/osrm-backend/pull/5054)
- ADDED: Bicycle profile allows to exclude ferry routes (default to not enabled) [#5054](https://github.com/Project-OSRM/osrm-backend/pull/5054)
# 5.17.1
- Changes from 5.17.0:
- Bugfixes:
- FIXED: Do not combine a segregated edge with a roundabout [#5039](https://github.com/Project-OSRM/osrm-backend/issues/5039)
# 5.17.0
- Changes from 5.16.0:
- Bugfixes:
- FIXED: deduplication of route steps when waypoints are used [#4909](https://github.com/Project-OSRM/osrm-backend/issues/4909)
@@ -6,9 +23,11 @@
- FIXED: Remove the last short annotation segment in `trimShortSegments` [#4946](https://github.com/Project-OSRM/osrm-backend/pull/4946)
- FIXED: Properly calculate annotations for speeds, durations and distances when waypoints are used with mapmatching [#4949](https://github.com/Project-OSRM/osrm-backend/pull/4949)
- FIXED: Don't apply unimplemented SH and PH conditions in OpeningHours and add inversed date ranges [#4992](https://github.com/Project-OSRM/osrm-backend/issues/4992)
- FIXED: integer overflow in `DynamicGraph::Renumber` [#5021](https://github.com/Project-OSRM/osrm-backend/pull/5021)
- Profile:
- CHANGED: Handle oneways in get_forward_backward_by_key [#4929](https://github.com/Project-OSRM/osrm-backend/pull/4929)
- FIXED: Do not route against oneway road if there is a cycleway in the wrong direction; also review bike profile [#4943](https://github.com/Project-OSRM/osrm-backend/issues/4943)
- CHANGED: Make cyclability weighting of the bike profile prefer safer routes more strongly [#5015](https://github.com/Project-OSRM/osrm-backend/issues/5015)
- Guidance:
- CHANGED: Don't use obviousness for links bifurcations [#4929](https://github.com/Project-OSRM/osrm-backend/pull/4929)
- FIXED: Adjust Straight direction modifiers of side roads in driveway handler [#4929](https://github.com/Project-OSRM/osrm-backend/pull/4929)
+24 -13
View File
@@ -167,7 +167,7 @@ add_executable(osrm-customize src/tools/customize.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:MICROTAR> $<TARGET_OBJECTS:UTIL>)
add_library(osrm src/osrm/osrm.cpp $<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:MICROTAR> $<TARGET_OBJECTS:UTIL> )
add_library(osrm src/osrm/osrm.cpp $<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:MICROTAR> $<TARGET_OBJECTS:UTIL>)
add_library(osrm_contract src/osrm/contractor.cpp $<TARGET_OBJECTS:CONTRACTOR> $<TARGET_OBJECTS:UTIL>)
add_library(osrm_extract src/osrm/extractor.cpp $<TARGET_OBJECTS:EXTRACTOR> $<TARGET_OBJECTS:MICROTAR> $<TARGET_OBJECTS:UTIL>)
add_library(osrm_guidance $<TARGET_OBJECTS:GUIDANCE> $<TARGET_OBJECTS:UTIL>)
@@ -416,11 +416,30 @@ if(UNIX AND NOT APPLE)
set(MAYBE_RT_LIBRARY -lrt)
endif()
# Disallow deprecated protozero APIs
add_definitions(-DPROTOZERO_STRICT_API)
find_package(Threads REQUIRED)
# Third-party libraries
set(RAPIDJSON_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rapidjson/include")
include_directories(SYSTEM ${RAPIDJSON_INCLUDE_DIR})
set(MICROTAR_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src")
include_directories(SYSTEM ${MICROTAR_INCLUDE_DIR})
set(MBXGEOM_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/geometry.hpp-0.9.2/include")
include_directories(SYSTEM ${MBXGEOM_INCLUDE_DIR})
set(CHEAPRULER_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/cheap-ruler-cpp-2.5.4/include")
include_directories(SYSTEM ${CHEAPRULER_INCLUDE_DIR})
add_library(MICROTAR OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src/microtar.c")
set_property(TARGET MICROTAR PROPERTY POSITION_INDEPENDENT_CODE ON)
set(PROTOZERO_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/protozero/include")
include_directories(SYSTEM ${PROTOZERO_INCLUDE_DIR})
set(VTZERO_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/vtzero/include")
include_directories(SYSTEM ${VTZERO_INCLUDE_DIR})
# if mason is enabled no find_package calls are made
# to ensure that we are only compiling and linking against
# fully portable mason packages
@@ -554,14 +573,6 @@ else()
include_directories(SYSTEM ${OSMIUM_INCLUDE_DIR})
endif()
set(RAPIDJSON_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rapidjson/include")
include_directories(SYSTEM ${RAPIDJSON_INCLUDE_DIR})
set(MICROTAR_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src")
include_directories(SYSTEM ${MICROTAR_INCLUDE_DIR})
add_library(MICROTAR OBJECT "${CMAKE_CURRENT_SOURCE_DIR}/third_party/microtar/src/microtar.c")
set_property(TARGET MICROTAR PROPERTY POSITION_INDEPENDENT_CODE ON)
# prefix compilation with ccache by default if available and on clang or gcc
if(ENABLE_CCACHE AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU"))
find_program(CCACHE_FOUND ccache)
@@ -862,4 +873,4 @@ if (ENABLE_NODE_BINDINGS)
endforeach()
add_library(check-headers STATIC EXCLUDE_FROM_ALL ${sources})
set_target_properties(check-headers PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${check_headers_dir})
endif()
endif()
+1 -1
View File
@@ -9,7 +9,7 @@ High performance routing engine written in C++14 designed to run on OpenStreetMa
The following services are available via HTTP API, C++ library interface and NodeJs wrapper:
- Nearest - Snaps coordinates to the street network and returns the nearest matches
- Route - Finds the fastest route between coordinates
- Table - Computes the duration of the fastest route between all pairs of supplied coordinates
- Table - Computes the duration or distances of the fastest route between all pairs of supplied coordinates
- Match - Snaps noisy GPS traces to the road network in the most plausible way
- Trip - Solves the Traveling Salesman Problem using a greedy heuristic
- Tile - Generates Mapbox Vector Tiles with internal routing metadata
+1 -1
View File
@@ -3,5 +3,5 @@ module.exports = {
verify: '--strict --tags ~@stress --tags ~@todo --tags ~@mld-only -f progress --require features/support --require features/step_definitions',
todo: '--strict --tags @todo --require features/support --require features/step_definitions',
all: '--strict --require features/support --require features/step_definitions',
mld: '--strict --tags ~@stress --tags ~@todo --require features/support --require features/step_definitions -f progress'
mld: '--strict --tags ~@stress --tags ~@todo --tags ~@ch --require features/support --require features/step_definitions -f progress'
};
+117 -8
View File
@@ -222,13 +222,13 @@ curl 'http://router.project-osrm.org/route/v1/driving/13.388860,52.517037;13.397
### Table service
Computes the duration of the fastest route between all pairs of supplied coordinates.
Computes the duration of the fastest route between all pairs of supplied coordinates. Returns the durations or distances or both between the coordinate pairs. Note that the distances are not the shortest distance between two coordinates, but rather the distances of the fastest routes. Duration is in seconds and distances is in meters.
```endpoint
GET /table/v1/{profile}/{coordinates}?{sources}=[{elem}...];&destinations=[{elem}...]
GET /table/v1/{profile}/{coordinates}?{sources}=[{elem}...];&{destinations}=[{elem}...]&annotations={duration|distance|duration,distance}
```
**Coordinates**
**Options**
In addition to the [general options](#general-options) the following options are supported for this service:
@@ -236,6 +236,8 @@ In addition to the [general options](#general-options) the following options are
|------------|--------------------------------------------------|---------------------------------------------|
|sources |`{index};{index}[;{index} ...]` or `all` (default)|Use location with given index as source. |
|destinations|`{index};{index}[;{index} ...]` or `all` (default)|Use location with given index as destination.|
|annotations |`duration` (default), `distance`, or `duration,distance`|Return the requested table or tables in response. Note that computing the `distances` table is currently only implemented for CH. If `annotations=distance` or `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned.
|
Unlike other array encoded options, the length of `sources` and `destinations` can be **smaller or equal**
to number of input locations;
@@ -253,14 +255,23 @@ sources=0;5;7&destinations=5;1;4;2;3;6
#### Example Request
```curl
# Returns a 3x3 matrix:
# Returns a 3x3 duration matrix:
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219'
# Returns a 1x3 matrix
# Returns a 1x3 duration matrix
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?sources=0'
# Returns a asymmetric 3x2 matrix with from the polyline encoded locations `qikdcB}~dpXkkHz`:
# Returns a asymmetric 3x2 duration matrix with from the polyline encoded locations `qikdcB}~dpXkkHz`:
curl 'http://router.project-osrm.org/table/v1/driving/polyline(egs_Iq_aqAppHzbHulFzeMe`EuvKpnCglA)?sources=0;1;3&destinations=2;4'
# Returns a 3x3 duration matrix:
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?annotations=duration'
# Returns a 3x3 distance matrix for CH:
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?annotations=distance'
# Returns a 3x3 duration matrix and a 3x3 distance matrix for CH:
curl 'http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52.529407;13.428555,52.523219?annotations=distance,duration'
```
**Response**
@@ -268,17 +279,115 @@ curl 'http://router.project-osrm.org/table/v1/driving/polyline(egs_Iq_aqAppHzbHu
- `code` if the request was successful `Ok` otherwise see the service dependent and general status codes.
- `durations` array of arrays that stores the matrix in row-major order. `durations[i][j]` gives the travel time from
the i-th waypoint to the j-th waypoint. Values are given in seconds. Can be `null` if no route between `i` and `j` can be found.
- `distances` array of arrays that stores the matrix in row-major order. `distances[i][j]` gives the travel distance from
the i-th waypoint to the j-th waypoint. Values are given in meters. Can be `null` if no route between `i` and `j` can be found. Note that computing the `distances` table is currently only implemented for CH. If `annotations=distance` or `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned.
- `sources` array of `Waypoint` objects describing all sources in order
- `destinations` array of `Waypoint` objects describing all destinations in order
In case of error the following `code`s are supported in addition to the general ones:
| Type | Description |
|-------------------|-----------------|
| Type | Description |
|------------------|-----------------|
| `NoTable` | No route found. |
| `NotImplemented` | This request is not supported |
All other properties might be undefined.
#### Example Response
```json
{
"sources": [
{
"location": [
13.3888,
52.517033
],
"hint": "PAMAgEVJAoAUAAAAIAAAAAcAAAAAAAAArss0Qa7LNEHiVIRA4lSEQAoAAAAQAAAABAAAAAAAAADMAAAAAEzMAKlYIQM8TMwArVghAwEA3wps52D3",
"name": "Friedrichstraße"
},
{
"location": [
13.397631,
52.529432
],
"hint": "WIQBgL6mAoAEAAAABgAAAAAAAAA7AAAAhU6PQHvHj0IAAAAAQbyYQgQAAAAGAAAAAAAAADsAAADMAAAAf27MABiJIQOCbswA_4ghAwAAXwVs52D3",
"name": "Torstraße"
},
{
"location": [
13.428554,
52.523239
],
"hint": "7UcAgP___38fAAAAUQAAACYAAABTAAAAhSQKQrXq5kKRbiZCWJo_Qx8AAABRAAAAJgAAAFMAAADMAAAASufMAOdwIQNL58wA03AhAwMAvxBs52D3",
"name": "Platz der Vereinten Nationen"
}
],
"durations": [
[
0,
192.6,
382.8
],
[
199,
0,
283.9
],
[
344.7,
222.3,
0
]
],
"destinations": [
{
"location": [
13.3888,
52.517033
],
"hint": "PAMAgEVJAoAUAAAAIAAAAAcAAAAAAAAArss0Qa7LNEHiVIRA4lSEQAoAAAAQAAAABAAAAAAAAADMAAAAAEzMAKlYIQM8TMwArVghAwEA3wps52D3",
"name": "Friedrichstraße"
},
{
"location": [
13.397631,
52.529432
],
"hint": "WIQBgL6mAoAEAAAABgAAAAAAAAA7AAAAhU6PQHvHj0IAAAAAQbyYQgQAAAAGAAAAAAAAADsAAADMAAAAf27MABiJIQOCbswA_4ghAwAAXwVs52D3",
"name": "Torstraße"
},
{
"location": [
13.428554,
52.523239
],
"hint": "7UcAgP___38fAAAAUQAAACYAAABTAAAAhSQKQrXq5kKRbiZCWJo_Qx8AAABRAAAAJgAAAFMAAADMAAAASufMAOdwIQNL58wA03AhAwMAvxBs52D3",
"name": "Platz der Vereinten Nationen"
}
],
"code": "Ok",
"distances": [
[
0,
1886.89,
3791.3
],
[
1824,
0,
2838.09
],
[
3275.36,
2361.73,
0
]
]
}
```
### Match service
Map matching matches/snaps given GPS points to the road network in the most plausible way.
+2 -2
View File
@@ -110,8 +110,8 @@ Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refer
### table
Computes duration tables for the given locations. Allows for both symmetric and asymmetric
tables.
Computes duration table for the given locations. Allows for both symmetric and asymmetric
tables. Optionally returns distance table.
**Parameters**
+3 -3
View File
@@ -68,7 +68,7 @@ If you want to prioritize certain streets, increase the rate on these.
## Elements
### api_version
A profile should set `api_version` at the top of your profile. This is done to ensure that older profiles are still supported when the api changes. If `api_version` is not defined, 0 will be assumed. The current api version is 2.
A profile should set `api_version` at the top of your profile. This is done to ensure that older profiles are still supported when the api changes. If `api_version` is not defined, 0 will be assumed. The current api version is 4.
### Library files
The folder [profiles/lib/](../profiles/lib/) contains LUA library files for handling many common processing tasks.
@@ -224,8 +224,8 @@ source_highway_turn_classification | Read | Integer |
source_access_turn_classification | Read | Integer | Classification based on access tag defined by user during setup. (default when not set: 0, allowed classification values are: 0-15))
source_speed | Read | Integer | Speed on this source road in km/h
source_priority_class | Read | Enum | The type of road priority class of the source. Defined in `include/extractor/guidance/road_classification.hpp`
target_restricted | Read | Boolean | Is it from a restricted access road? (See definition in `process_way`)
target_mode | Read | Enum | Travel mode before the turn. Defined in `include/extractor/travel_mode.hpp`
target_restricted | Read | Boolean | Is the target a restricted access road? (See definition in `process_way`)
target_mode | Read | Enum | Travel mode after the turn. Defined in `include/extractor/travel_mode.hpp`
target_is_motorway | Read | Boolean | Is the target road a motorway?
target_is_link | Read | Boolean | Is the target road a link?
target_number_of_lanes | Read | Integer | How many lanes does the target road have? (default when not tagged: 0)
+3 -3
View File
@@ -43,9 +43,9 @@ We may introduce forward-compatible changes: query parameters and response prope
1. Check out the appropriate release branch `x.y`
2. Make sure `CHANGELOG.md` is up to date.
3. Make sure the `package.json` is up to date.
4. Make sure all tests are passing (e.g. Travis CI gives you a :thumbs_up:)
5. Use an annotated tag to mark the release: `git tag vx.y.z -a` Body of the tag description should be the changelog entries.
3. Make sure the `package.json` on branch `x.y` has been committed.
4. Make sure all tests are passing (e.g. Travis CI gives you a :green_apple:)
5. Use an annotated tag to mark the release: `git tag vx.y.z -a` Body of the tag description should be the changelog entries. Commit should be one in which the `package.json` version matches the version you want to release.
6. Use `npm run docs` to generate the API documentation. Copy `build/docs/*` to `https://github.com/Project-OSRM/project-osrm.github.com` in the `docs/vN.N.N/api` directory
7. Push tags and commits: `git push; git push --tags`
8. On https://github.com/Project-OSRM/osrm-backend/releases press `Draft a new release`,
+92
View File
@@ -0,0 +1,92 @@
@routing @bicycle @mode
Feature: Bicycle - Mode flag
Background:
Given the profile "bicycle"
Scenario: Bicycle - We tag ferries with a class
Given the node map
"""
a b
c d
"""
And the ways
| nodes | highway | route |
| ab | primary | |
| bc | | ferry |
| cd | primary | |
When I route I should get
| from | to | route | turns | classes |
| a | d | ab,bc,cd,cd | depart,notification right,notification left,arrive | [()],[(ferry)],[()],[()] |
| d | a | cd,bc,ab,ab | depart,notification right,notification left,arrive | [()],[(ferry)],[()],[()] |
| c | a | bc,ab,ab | depart,notification left,arrive | [(ferry)],[()],[()] |
| d | b | cd,bc,bc | depart,notification right,arrive | [()],[(ferry)],[()] |
| a | c | ab,bc,bc | depart,notification right,arrive | [()],[(ferry)],[()] |
| b | d | bc,cd,cd | depart,notification left,arrive | [(ferry)],[()],[()] |
Scenario: Bicycle - We tag tunnel with a class
Background:
Given a grid size of 200 meters
Given the node map
"""
a b
c d
"""
And the ways
| nodes | tunnel |
| ab | no |
| bc | yes |
| cd | |
When I route I should get
| from | to | route | turns | classes |
| a | d | ab,bc,cd,cd | depart,new name right,new name left,arrive | [()],[(tunnel)],[()],[()] |
Scenario: Bicycle - We tag classes without intersections
Background:
Given a grid size of 200 meters
Given the node map
"""
a b c d
"""
And the ways
| nodes | name | tunnel |
| ab | road | |
| bc | road | yes |
| cd | road | |
When I route I should get
| from | to | route | turns | classes |
| a | d | road,road | depart,arrive | [(),(tunnel),()],[()] |
Scenario: Bicycle - From roundabout on ferry
Given the node map
"""
c
/ \
a---b d---f--h
\ /
e
|
g
"""
And the ways
| nodes | oneway | highway | junction | route |
| ab | yes | service | | |
| cb | yes | service | roundabout | |
| dc | yes | service | roundabout | |
| be | yes | service | roundabout | |
| ed | yes | service | roundabout | |
| eg | yes | service | | |
| df | | | | ferry |
| fh | yes | service | | |
When I route I should get
| from | to | route | turns | classes |
| a | h | ab,df,df,fh,fh | depart,roundabout-exit-2,exit roundabout slight right,notification straight,arrive | [()],[(),()],[(ferry)],[()],[()] |
+55
View File
@@ -0,0 +1,55 @@
@routing @bicycle @exclude
Feature: Bicycle - Exclude flags
Background:
Given the profile file "bicycle" initialized with
"""
profile.excludable = Sequence { Set { 'ferry' } }
"""
Given the node map
"""
a....b~~~~~c...f
: :
d.....e
"""
And the ways
| nodes | highway | route | duration | # |
| ab | service | | | always drivable |
| bc | | ferry | 00:00:01 | not drivable for exclude=ferry, but fast. |
| bd | service | | | always drivable |
| de | service | | | always drivable |
| ec | service | | | always drivable |
| cf | service | | | always drivable |
Scenario: Bicycle - exclude nothing
When I route I should get
| from | to | route |
| a | f | ab,bc,cf,cf |
When I match I should get
| trace | matchings | duration |
| abcf | abcf | 109 |
When I request a travel time matrix I should get
| | a | f |
| a | 0 | 109 |
| f | 109 | 0 |
Scenario: Bicycle - exclude ferry
Given the query options
| exclude | ferry |
When I route I should get
| from | to | route |
| a | f | ab,bd,de,ec,cf,cf |
When I match I should get
| trace | matchings | duration |
| abcf | abcf | 301.2 |
When I request a travel time matrix I should get
| | a | f |
| a | 0 | 301 +- 1 |
| f | 301.2 +- 1 | 0 |
+22 -22
View File
@@ -11,11 +11,11 @@ Feature: Bicycle - Adds penalties to unsafe roads
Then routability should be
| highway | cycleway | forw | backw | forw_rate | backw_rate |
| motorway | | | | | |
| primary | | 15 km/h | 15 km/h | 2.9 | 2.9 |
| secondary | | 15 km/h | 15 km/h | 3.1 | 3.1 |
| primary | | 15 km/h | 15 km/h | 2.1 | 2.1 |
| secondary | | 15 km/h | 15 km/h | 2.7 | 2.7 |
| tertiary | | 15 km/h | 15 km/h | 3.3 | 3.3 |
| primary_link | | 15 km/h | 15 km/h | 2.9 | 2.9 |
| secondary_link | | 15 km/h | 15 km/h | 3.1 | 3.1 |
| primary_link | | 15 km/h | 15 km/h | 2.1 | 2.1 |
| secondary_link | | 15 km/h | 15 km/h | 2.7 | 2.7 |
| tertiary_link | | 15 km/h | 15 km/h | 3.3 | 3.3 |
| residential | | 15 km/h | 15 km/h | 4.2 | 4.2 |
| cycleway | | 15 km/h | 15 km/h | 4.2 | 4.2 |
@@ -51,49 +51,49 @@ Feature: Bicycle - Adds penalties to unsafe roads
Then routability should be
| highway | cycleway:right | cycleway:left | forw | backw | forw_rate | backw_rate |
| motorway | track | | 15 km/h | | 4.2 | |
| primary | track | | 15 km/h | 15 km/h | 4.2 | 2.9 |
| secondary | track | | 15 km/h | 15 km/h | 4.2 | 3.1 |
| primary | track | | 15 km/h | 15 km/h | 4.2 | 2.1 |
| secondary | track | | 15 km/h | 15 km/h | 4.2 | 2.7 |
| tertiary | track | | 15 km/h | 15 km/h | 4.2 | 3.3 |
| primary_link | track | | 15 km/h | 15 km/h | 4.2 | 2.9 |
| secondary_link | track | | 15 km/h | 15 km/h | 4.2 | 3.1 |
| primary_link | track | | 15 km/h | 15 km/h | 4.2 | 2.1 |
| secondary_link | track | | 15 km/h | 15 km/h | 4.2 | 2.7 |
| tertiary_link | track | | 15 km/h | 15 km/h | 4.2 | 3.3 |
| residential | track | | 15 km/h | 15 km/h | 4.2 | 4.2 |
| cycleway | track | | 15 km/h | 15 km/h | 4.2 | 4.2 |
| footway | track | | 15 km/h | 4 km/h +-1 | 4.2 | 1.1 |
| motorway | | track | 15 km/h | | 4.2 | |
| primary | | track | 15 km/h | 15 km/h | 2.9 | 4.2 |
| secondary | | track | 15 km/h | 15 km/h | 3.1 | 4.2 |
| primary | | track | 15 km/h | 15 km/h | 2.1 | 4.2 |
| secondary | | track | 15 km/h | 15 km/h | 2.7 | 4.2 |
| tertiary | | track | 15 km/h | 15 km/h | 3.3 | 4.2 |
| primary_link | | track | 15 km/h | 15 km/h | 2.9 | 4.2 |
| secondary_link | | track | 15 km/h | 15 km/h | 3.1 | 4.2 |
| primary_link | | track | 15 km/h | 15 km/h | 2.1 | 4.2 |
| secondary_link | | track | 15 km/h | 15 km/h | 2.7 | 4.2 |
| tertiary_link | | track | 15 km/h | 15 km/h | 3.3 | 4.2 |
| residential | | track | 15 km/h | 15 km/h | 4.2 | 4.2 |
| cycleway | | track | 15 km/h | 15 km/h | 4.2 | 4.2 |
| footway | | track | 4 km/h +-1 | 15 km/h | 1.1 | 4.2 |
| motorway | lane | | 15 km/h | | 4.2 | |
| primary | lane | | 15 km/h | 15 km/h | 4.2 | 2.9 |
| secondary | lane | | 15 km/h | 15 km/h | 4.2 | 3.1 |
| primary | lane | | 15 km/h | 15 km/h | 4.2 | 2.1 |
| secondary | lane | | 15 km/h | 15 km/h | 4.2 | 2.7 |
| tertiary | lane | | 15 km/h | 15 km/h | 4.2 | 3.3 |
| primary_link | lane | | 15 km/h | 15 km/h | 4.2 | 2.9 |
| secondary_link | lane | | 15 km/h | 15 km/h | 4.2 | 3.1 |
| primary_link | lane | | 15 km/h | 15 km/h | 4.2 | 2.1 |
| secondary_link | lane | | 15 km/h | 15 km/h | 4.2 | 2.7 |
| tertiary_link | lane | | 15 km/h | 15 km/h | 4.2 | 3.3 |
| residential | lane | | 15 km/h +-1 | 15 km/h +-1 | 4.2 | 4.2 |
| cycleway | lane | | 15 km/h | 15 km/h | 4.2 | 4.2 |
| footway | lane | | 15 km/h | 4 km/h +-1 | 4.2 | 1.1 |
| motorway | | lane | 15 km/h | | 4.2 | |
| primary | | lane | 15 km/h | 15 km/h | 2.9 | 4.2 |
| secondary | | lane | 15 km/h +-1 | 15 km/h +-1 | 3.1 | 4.2 |
| primary | | lane | 15 km/h | 15 km/h | 2.1 | 4.2 |
| secondary | | lane | 15 km/h +-1 | 15 km/h +-1 | 2.7 | 4.2 |
| tertiary | | lane | 15 km/h | 15 km/h | 3.3 | 4.2 |
| primary_link | | lane | 15 km/h | 15 km/h | 2.9 | 4.2 |
| secondary_link | | lane | 15 km/h | 15 km/h | 3.1 | 4.2 |
| primary_link | | lane | 15 km/h | 15 km/h | 2.1 | 4.2 |
| secondary_link | | lane | 15 km/h | 15 km/h | 2.7 | 4.2 |
| tertiary_link | | lane | 15 km/h | 15 km/h | 3.3 | 4.2 |
| residential | | lane | 15 km/h | 15 km/h | 4.2 | 4.2 |
| cycleway | | lane | 15 km/h | 15 km/h | 4.2 | 4.2 |
| footway | | lane | 4 km/h +-1 | 15 km/h | 1.1 | 4.2 |
| motorway | shared_lane | | 15 km/h | | 4.2 | |
| primary | shared_lane | | 15 km/h | 15 km/h | 4.2 | 2.9 |
| primary | shared_lane | | 15 km/h | 15 km/h | 4.2 | 2.1 |
| motorway | | shared_lane | 15 km/h | | 4.2 | |
| primary | | shared_lane | 15 km/h | 15 km/h | 2.9 | 4.2 |
| primary | | shared_lane | 15 km/h | 15 km/h | 2.1 | 4.2 |
Scenario: Bike - Don't apply penalties for all kind of cycleways
@@ -1209,3 +1209,33 @@ Feature: Simple Turns
| a | c | knob,knob | depart,arrive |
| d | e | soph,soph | depart,arrive |
| d | a | soph,knob,knob | depart,turn left,arrive |
# https://www.openstreetmap.org/node/30797565
Scenario: No turn instruction when turning from unnamed onto unnamed
Given the node map
"""
a
|
|
|
|
b----------------c
|
|
|
|
|
|
d
"""
And the ways
| nodes | highway | name | ref |
| ab | trunk_link | | |
| db | secondary | | L 460 |
| bc | secondary | | |
When I route I should get
| from | to | route | turns |
| d | c | ,, | depart,turn right,arrive |
+74 -60
View File
@@ -1,74 +1,88 @@
var util = require('util');
module.exports = function () {
this.When(/^I request a travel time matrix I should get$/, (table, callback) => {
var NO_ROUTE = 2147483647; // MAX_INT
const durationsRegex = new RegExp(/^I request a travel time matrix I should get$/);
const distancesRegex = new RegExp(/^I request a travel distance matrix I should get$/);
var tableRows = table.raw();
const DURATIONS_NO_ROUTE = 2147483647; // MAX_INT
const DISTANCES_NO_ROUTE = 3.40282e+38; // MAX_FLOAT
if (tableRows[0][0] !== '') throw new Error('*** Top-left cell of matrix table must be empty');
this.When(durationsRegex, function(table, callback) {tableParse.call(this, table, DURATIONS_NO_ROUTE, 'durations', callback);}.bind(this));
this.When(distancesRegex, function(table, callback) {tableParse.call(this, table, DISTANCES_NO_ROUTE, 'distances', callback);}.bind(this));
};
var waypoints = [],
columnHeaders = tableRows[0].slice(1),
rowHeaders = tableRows.map((h) => h[0]).slice(1),
symmetric = columnHeaders.length == rowHeaders.length && columnHeaders.every((ele, i) => ele === rowHeaders[i]);
const durationsParse = function(v) { return isNaN(parseInt(v)); };
const distancesParse = function(v) { return isNaN(parseFloat(v)); };
if (symmetric) {
columnHeaders.forEach((nodeName) => {
var node = this.findNodeByName(nodeName);
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
waypoints.push({ coord: node, type: 'loc' });
function tableParse(table, noRoute, annotation, callback) {
const parse = annotation == 'distances' ? distancesParse : durationsParse;
const params = this.queryParams;
params.annotations = annotation == 'distances' ? 'distance' : 'duration';
var tableRows = table.raw();
if (tableRows[0][0] !== '') throw new Error('*** Top-left cell of matrix table must be empty');
var waypoints = [],
columnHeaders = tableRows[0].slice(1),
rowHeaders = tableRows.map((h) => h[0]).slice(1),
symmetric = columnHeaders.length == rowHeaders.length && columnHeaders.every((ele, i) => ele === rowHeaders[i]);
if (symmetric) {
columnHeaders.forEach((nodeName) => {
var node = this.findNodeByName(nodeName);
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
waypoints.push({ coord: node, type: 'loc' });
});
} else {
columnHeaders.forEach((nodeName) => {
var node = this.findNodeByName(nodeName);
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
waypoints.push({ coord: node, type: 'dst' });
});
rowHeaders.forEach((nodeName) => {
var node = this.findNodeByName(nodeName);
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
waypoints.push({ coord: node, type: 'src' });
});
}
var actual = [];
actual.push(table.headers);
this.reprocessAndLoadData((e) => {
if (e) return callback(e);
// compute matrix
this.requestTable(waypoints, params, (err, response) => {
if (err) return callback(err);
if (!response.body.length) return callback(new Error('Invalid response body'));
var json = JSON.parse(response.body);
var result = json[annotation].map(row => {
var hashes = {};
row.forEach((v, i) => { hashes[tableRows[0][i+1]] = parse(v) ? '' : v; });
return hashes;
});
} else {
columnHeaders.forEach((nodeName) => {
var node = this.findNodeByName(nodeName);
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
waypoints.push({ coord: node, type: 'dst' });
});
rowHeaders.forEach((nodeName) => {
var node = this.findNodeByName(nodeName);
if (!node) throw new Error(util.format('*** unknown node "%s"', nodeName));
waypoints.push({ coord: node, type: 'src' });
});
}
var actual = [];
actual.push(table.headers);
this.reprocessAndLoadData((e) => {
if (e) return callback(e);
// compute matrix
var params = this.queryParams;
this.requestTable(waypoints, params, (err, response) => {
if (err) return callback(err);
if (!response.body.length) return callback(new Error('Invalid response body'));
var json = JSON.parse(response.body);
var result = json['durations'].map(row => {
var hashes = {};
row.forEach((v, i) => { hashes[tableRows[0][i+1]] = isNaN(parseInt(v)) ? '' : v; });
return hashes;
});
var testRow = (row, ri, cb) => {
for (var k in result[ri]) {
if (this.FuzzyMatch.match(result[ri][k], row[k])) {
result[ri][k] = row[k];
} else if (row[k] === '' && result[ri][k] === NO_ROUTE) {
result[ri][k] = '';
} else {
result[ri][k] = result[ri][k].toString();
}
var testRow = (row, ri, cb) => {
for (var k in result[ri]) {
if (this.FuzzyMatch.match(result[ri][k], row[k])) {
result[ri][k] = row[k];
} else if (row[k] === '' && result[ri][k] === noRoute) {
result[ri][k] = '';
} else {
result[ri][k] = result[ri][k].toString();
}
}
result[ri][''] = row[''];
cb(null, result[ri]);
};
result[ri][''] = row[''];
cb(null, result[ri]);
};
this.processRowsAndDiff(table, testRow, callback);
});
this.processRowsAndDiff(table, testRow, callback);
});
});
};
}
+7 -1
View File
@@ -21,7 +21,8 @@ module.exports = {
matchRe = want.match(/^\/(.*)\/$/),
// we use this for matching before/after bearing
matchBearingListAbs = want.match(/^((\d+)->(\d+))(,(\d+)->(\d+))*\s+\+\-(.+)$/),
matchIntersectionListAbs = want.match(/^(((((true|false):\d+)\s{0,1})+,{0,1})+;{0,1})+\s+\+\-(.+)$/);
matchIntersectionListAbs = want.match(/^(((((true|false):\d+)\s{0,1})+,{0,1})+;{0,1})+\s+\+\-(.+)$/),
matchRangeNumbers = want.match(/\d+\+\-\d+/);
function inRange(margin, got, want) {
var fromR = parseFloat(want) - margin,
@@ -105,6 +106,11 @@ module.exports = {
return inRange(margin, got, matchAbs[1]);
} else if (matchRe) { // regex: /a,b,.*/
return got.match(matchRe[1]);
} else if (matchRangeNumbers) {
let real_want_and_margin = want.split('+-'),
margin = parseFloat(real_want_and_margin[1].trim()),
real_want = parseFloat(real_want_and_margin[0].trim());
return inRange(margin, got, real_want);
} else {
return false;
}
+1 -1
View File
@@ -51,7 +51,7 @@ module.exports = function () {
.defer(rimraf, this.scenarioLogFile)
.awaitAll(callback);
// uncomment to get path to logfile
// console.log(" Writing logging output to " + this.scenarioLogFile)
// console.log(' Writing logging output to ' + this.scenarioLogFile);
});
this.After((scenario, callback) => {
+19
View File
@@ -226,3 +226,22 @@ Feature: Distance calculation
| x | v | xv,xv | 424m +-1 |
| x | w | xw,xw | 360m +-1 |
| x | y | xy,xy | 316m +-1 |
# Check rounding errors
Scenario: Distances Long distances
Given a grid size of 1000 meters
Given the node map
"""
a b c d
"""
And the ways
| nodes |
| abcd |
When I route I should get
| from | to | distance |
| a | b | 1000m +-3 |
| a | c | 2000m +-3 |
| a | d | 3000m +-3 |
+335 -177
View File
@@ -1,14 +1,12 @@
@matrix @testbot
Feature: Basic Distance Matrix
# note that results are travel time, specified in 1/10th of seconds
# since testbot uses a default speed of 100m/10s, the result matches
# the number of meters as long as the way type is the default 'primary'
# note that results of travel distance are in metres
Background:
Given the profile "testbot"
And the partition extra arguments "--small-component-size 1 --max-cell-sizes 2,4,8,16"
Scenario: Testbot - Travel time matrix of minimal network
Scenario: Testbot - Travel distance matrix of minimal network
Given the node map
"""
a b
@@ -18,12 +16,99 @@ Feature: Basic Distance Matrix
| nodes |
| ab |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 10 |
| b | 10 | 0 |
When I request a travel distance matrix I should get
| | a | b |
| a | 0 | 100+-1 |
| b | 100+-1 | 0 |
Scenario: Testbot - Travel time matrix with different way speeds
Scenario: Testbot - Travel distance matrix of minimal network with toll exclude
Given the query options
| exclude | toll |
Given the node map
"""
a b
c d
"""
And the ways
| nodes | highway | toll | # |
| ab | motorway | | not drivable for exclude=motorway |
| cd | primary | | always drivable |
| ac | primary | yes | not drivable for exclude=toll and exclude=motorway,toll |
| bd | motorway | yes | not drivable for exclude=toll and exclude=motorway,toll |
When I request a travel distance matrix I should get
| | a | b | c | d |
| a | 0 | 100+-1 | | |
| b | 100+-1 | 0 | | |
| c | | | 0 | 100+-1 |
| d | | | 100+-1 | 0 |
Scenario: Testbot - Travel distance matrix of minimal network with motorway exclude
Given the query options
| exclude | motorway |
Given the node map
"""
a b
c d
"""
And the ways
| nodes | highway | # |
| ab | motorway | not drivable for exclude=motorway |
| cd | residential | |
| ac | residential | |
| bd | residential | |
When I request a travel distance matrix I should get
| | a | b | c | d |
| a | 0 | 300+-2 | 100+-2 | 200+-2 |
Scenario: Testbot - Travel distance matrix of minimal network disconnected motorway exclude
Given the query options
| exclude | motorway |
And the extract extra arguments "--small-component-size 4"
Given the node map
"""
ab efgh
cd
"""
And the ways
| nodes | highway | # |
| be | motorway | not drivable for exclude=motorway |
| abcd | residential | |
| efgh | residential | |
When I request a travel distance matrix I should get
| | a | b | e |
| a | 0 | 50+-1 | |
Scenario: Testbot - Travel distance matrix of minimal network with motorway and toll excludes
Given the query options
| exclude | motorway,toll |
Given the node map
"""
a b e f
c d g h
"""
And the ways
| nodes | highway | toll | # |
| be | motorway | | not drivable for exclude=motorway |
| dg | primary | yes | not drivable for exclude=toll |
| abcd | residential | | |
| efgh | residential | | |
When I request a travel distance matrix I should get
| | a | b | e | g |
| a | 0 | 100+-1 | | |
Scenario: Testbot - Travel distance matrix with different way speeds
Given the node map
"""
a b c d
@@ -35,40 +120,25 @@ Feature: Basic Distance Matrix
| bc | secondary |
| cd | tertiary |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 10 | 30 | 60 |
| b | 10 | 0 | 20 | 50 |
| c | 30 | 20 | 0 | 30 |
| d | 60 | 50 | 30 | 0 |
When I request a travel distance matrix I should get
| | a | b | c | d |
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
| c | 200+-1 | 100+-1 | 0 | 100+-1 |
| d | 300+-1 | 200+-1 | 100+-1 | 0 |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 10 | 30 | 60 |
When I request a travel distance matrix I should get
| | a | b | c | d |
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
When I request a travel time matrix I should get
| | a |
| a | 0 |
| b | 10 |
| c | 30 |
| d | 60 |
When I request a travel distance matrix I should get
| | a |
| a | 0 |
| b | 100+-1 |
| c | 200+-1 |
| d | 300+-1 |
Scenario: Testbot - Travel time matrix with fuzzy match
Given the node map
"""
a b
"""
And the ways
| nodes |
| ab |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 10 |
| b | 10 | 0 |
Scenario: Testbot - Travel time matrix of small grid
Scenario: Testbot - Travel distance matrix of small grid
Given the node map
"""
a b c
@@ -83,14 +153,14 @@ Feature: Basic Distance Matrix
| be |
| cf |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
| e | 20 | 10 | 0 | 10 |
| f | 30 | 20 | 10 | 0 |
When I request a travel distance matrix I should get
| | a | b | e | f |
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
| e | 200+-1 | 100+-1 | 0 | 100+-1 |
| f | 300+-1 | 200+-1 | 100+-1 | 0 |
Scenario: Testbot - Travel time matrix of network with unroutable parts
Scenario: Testbot - Travel distance matrix of network with unroutable parts
Given the node map
"""
a b
@@ -100,12 +170,12 @@ Feature: Basic Distance Matrix
| nodes | oneway |
| ab | yes |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 10 |
| b | | 0 |
When I request a travel distance matrix I should get
| | a | b |
| a | 0 | 100+-1 |
| b | | 0 |
Scenario: Testbot - Travel time matrix of network with oneways
Scenario: Testbot - Travel distance matrix of network with oneways
Given the node map
"""
x a b y
@@ -118,14 +188,14 @@ Feature: Basic Distance Matrix
| xa | |
| by | |
When I request a travel time matrix I should get
| | x | y | d | e |
| x | 0 | 30 | 40 | 30 |
| y | 50 | 0 | 30 | 20 |
| d | 20 | 30 | 0 | 30 |
| e | 30 | 40 | 10 | 0 |
When I request a travel distance matrix I should get
| | x | y | d | e |
| x | 0 | 300+-2 | 400+-2 | 300+-2 |
| y | 500+-2 | 0 | 300+-2 | 200+-2 |
| d | 200+-2 | 300+-2 | 0 | 300+-2 |
| e | 300+-2 | 400+-2 | 100+-2 | 0 |
Scenario: Testbot - Rectangular travel time matrix
Scenario: Testbot - Rectangular travel distance matrix
Given the node map
"""
a b c
@@ -140,51 +210,57 @@ Feature: Basic Distance Matrix
| be |
| cf |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
When I route I should get
| from | to | distance |
| e | a | 200m +- 1 |
| e | b | 100m +- 1 |
| f | a | 300m +- 1 |
| f | b | 200m +- 1 |
When I request a travel time matrix I should get
| | a |
| a | 0 |
| b | 10 |
| e | 20 |
| f | 30 |
When I request a travel distance matrix I should get
| | a | b | e | f |
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
When I request a travel distance matrix I should get
| | a |
| a | 0 |
| b | 100+-1 |
| e | 200+-1 |
| f | 300+-1 |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 10 |
| b | 10 | 0 |
| e | 20 | 10 |
| f | 30 | 20 |
When I request a travel distance matrix I should get
| | a | b | e | f |
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
| e | 20 | 10 | 0 | 10 |
When I request a travel distance matrix I should get
| | a | b |
| a | 0 | 100+-1 |
| b | 100+-1 | 0 |
| e | 200+-1 | 100+-1 |
| f | 300+-1 | 200+-1 |
When I request a travel time matrix I should get
| | a | b | e |
| a | 0 | 10 | 20 |
| b | 10 | 0 | 10 |
| e | 20 | 10 | 0 |
| f | 30 | 20 | 10 |
When I request a travel distance matrix I should get
| | a | b | e | f |
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
| e | 200+-1 | 100+-1 | 0 | 100+-1 |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
| e | 20 | 10 | 0 | 10 |
| f | 30 | 20 | 10 | 0 |
When I request a travel distance matrix I should get
| | a | b | e |
| a | 0 | 100+-1 | 200+-1 |
| b | 100+-1 | 0 | 100+-1 |
| e | 200+-1 | 100+-1 | 0 |
| f | 300+-1 | 200+-1 | 100+-1 |
When I request a travel distance matrix I should get
| | a | b | e | f |
| a | 0 | 100+-1 | 200+-1 | 300+-1 |
| b | 100+-1 | 0 | 100+-1 | 200+-1 |
| e | 200+-1 | 100+-1 | 0 | 100+-1 |
| f | 300+-1 | 200+-1 | 100+-1 | 0 |
Scenario: Testbot - Travel time 3x2 matrix
Scenario: Testbot - Travel distance 3x2 matrix
Given the node map
"""
a b c
@@ -199,10 +275,11 @@ Feature: Basic Distance Matrix
| be |
| cf |
When I request a travel time matrix I should get
| | b | e | f |
| a | 10 | 20 | 30 |
| b | 0 | 10 | 20 |
When I request a travel distance matrix I should get
| | b | e | f |
| a | 100+-1 | 200+-1 | 300+-1 |
| b | 0 | 100+-1 | 200+-1 |
Scenario: Testbot - All coordinates are from same small component
Given a grid size of 300 meters
@@ -221,10 +298,10 @@ Feature: Basic Distance Matrix
| da |
| fg |
When I request a travel time matrix I should get
| | f | g |
| f | 0 | 30 |
| g | 30 | 0 |
When I request a travel distance matrix I should get
| | f | g |
| f | 0 | 300+-2 |
| g | 300+-2 | 0 |
Scenario: Testbot - Coordinates are from different small component and snap to big CC
Given a grid size of 300 meters
@@ -244,14 +321,25 @@ Feature: Basic Distance Matrix
| fg |
| hi |
When I request a travel time matrix I should get
| | f | g | h | i |
| f | 0 | 30 | 0 | 30 |
| g | 30 | 0 | 30 | 0 |
| h | 0 | 30 | 0 | 30 |
| i | 30 | 0 | 30 | 0 |
When I route I should get
| from | to | distance |
| f | g | 300m |
| f | i | 300m |
| g | f | 300m |
| g | h | 300m |
| h | g | 300m |
| h | i | 300m |
| i | f | 300m |
| i | h | 300m |
Scenario: Testbot - Travel time matrix with loops
When I request a travel distance matrix I should get
| | f | g | h | i |
| f | 0 | 300+-2 | 0 | 300+-2 |
| g | 300+-2 | 0 | 300+-2 | 0 |
| h | 0 | 300+-2 | 0 | 300+-2 |
| i | 300+-2 | 0 | 300+-2 | 0 |
Scenario: Testbot - Travel distance matrix with loops
Given the node map
"""
a 1 2 b
@@ -265,14 +353,15 @@ Feature: Basic Distance Matrix
| cd | yes |
| da | yes |
When I request a travel time matrix I should get
| | 1 | 2 | 3 | 4 |
| 1 | 0 | 10 +-1 | 40 +-1 | 50 +-1 |
| 2 | 70 +-1 | 0 | 30 +-1 | 40 +-1 |
| 3 | 40 +-1 | 50 +-1 | 0 | 10 +-1 |
| 4 | 30 +-1 | 40 +-1 | 70 +-1 | 0 |
When I request a travel distance matrix I should get
| | 1 | 2 | 3 | 4 |
| 1 | 0 | 100+-1 | 400+-1 | 500+-1 |
| 2 | 700+-1 | 0 | 300+-1 | 400+-1 |
| 3 | 400+-1 | 500+-1 | 0 | 100+-1 |
| 4 | 300+-1 | 400+-1 | 700+-1 | 0 |
Scenario: Testbot - Travel time matrix based on segment durations
Scenario: Testbot - Travel distance matrix based on segment durations
Given the profile file
"""
local functions = require('testbot')
@@ -301,20 +390,19 @@ Feature: Basic Distance Matrix
"""
And the ways
| nodes |
| abcd |
| ce |
| nodes |
| abcd |
| ce |
When I request a travel time matrix I should get
| | a | b | c | d | e |
| a | 0 | 11 | 22 | 33 | 33 |
| b | 11 | 0 | 11 | 22 | 22 |
| c | 22 | 11 | 0 | 11 | 11 |
| d | 33 | 22 | 11 | 0 | 22 |
| e | 33 | 22 | 11 | 22 | 0 |
When I request a travel distance matrix I should get
| | a | b | c | d | e |
| a | 0 | 100+-2 | 200+-2 | 300+-2 | 400+-2 |
| b | 100+-2 | 0 | 100+-2 | 200+-2 | 300+-2 |
| c | 200+-2 | 100+-2 | 0 | 100+-2 | 200+-2 |
| d | 300+-2 | 200+-2 | 100+-2 | 0 | 300+-2 |
| e | 400+-2 | 300+-2 | 200+-2 | 300+-2 | 0 |
Scenario: Testbot - Travel time matrix for alternative loop paths
Scenario: Testbot - Travel distance matrix for alternative loop paths
Given the profile file
"""
local functions = require('testbot')
@@ -350,62 +438,132 @@ Feature: Basic Distance Matrix
| dc | yes |
| ca | yes |
When I request a travel time matrix I should get
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 1 | 0 | 11 | 3 | 2 | 6 | 5 | 8.9 | 7.9 |
| 2 | 1 | 0 | 4 | 3 | 7 | 6 | 9.9 | 8.9 |
| 3 | 9 | 8 | 0 | 11 | 3 | 2 | 5.9 | 4.9 |
| 4 | 10 | 9 | 1 | 0 | 4 | 3 | 6.9 | 5.9 |
| 5 | 6 | 5 | 9 | 8 | 0 | 11 | 2.9 | 1.9 |
| 6 | 7 | 6 | 10 | 9 | 1 | 0 | 3.9 | 2.9 |
| 7 | 3.1 | 2.1 | 6.1 | 5.1 | 9.1 | 8.1 | 0 | 11 |
| 8 | 4.1 | 3.1 | 7.1 | 6.1 | 10.1 | 9.1 | 1 | 0 |
When I request a travel distance matrix I should get
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 1 | 0 | 1100+-5 | 300+-5 | 200+-5 | 600+-5 | 500+-5 | 900+-5 | 800+-5 |
| 2 | 100+-5 | 0 | 400+-5 | 300+-5 | 700+-5 | 600+-5 | 1000+-5 | 900+-5 |
| 3 | 900+-5 | 800+-5 | 0 | 1100+-5 | 300+-5 | 200+-5 | 600+-5 | 500+-5 |
| 4 | 1000+-5 | 900+-5 | 100+-5 | 0 | 400+-5 | 300+-5 | 700+-5 | 600+-5 |
| 5 | 600+-5 | 500+-5 | 900+-5 | 800+-5 | 0 | 1100+-5 | 300+-5 | 200+-5 |
| 6 | 700+-5 | 600+-5 | 1000+-5 | 900+-5 | 100+-5 | 0 | 400+-5 | 300+-5 |
| 7 | 300+-5 | 200+-5 | 600+-5 | 500+-5 | 900+-5 | 800+-5 | 0 | 1100+-5 |
| 8 | 400+-5 | 300+-5 | 700+-5 | 600+-5 | 1000+-5 | 900+-5 | 100+-5 | 0 |
When I request a travel distance matrix I should get
| | 1 |
| 1 | 0 |
| 2 | 100+-5 |
| 3 | 900+-5 |
| 4 | 1000+-5 |
| 5 | 600+-5 |
| 6 | 700+-5 |
| 7 | 300+-5 |
| 8 | 400+-5 |
Scenario: Testbot - Travel time matrix with ties
Given the profile file
"""
local functions = require('testbot')
functions.process_segment = function(profile, segment)
segment.weight = 1
segment.duration = 1
end
functions.process_turn = function(profile, turn)
if turn.angle >= 0 then
turn.duration = 16
else
turn.duration = 4
end
turn.weight = 0
end
return functions
"""
And the node map
Scenario: Testbot - Travel distance matrix with ties
Given the node map
"""
a b
a b
c d
"""
And the ways
| nodes |
| ab |
| ac |
| bd |
| dc |
| nodes |
| ab |
| ac |
| bd |
| dc |
When I route I should get
| from | to | route | distance | time | weight |
| a | c | ac,ac | 200m | 5s | 5 |
| from | to | route | distance | time | weight |
| a | c | ac,ac | 200m | 20s | 20 |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 1 | 5 | 10 |
When I route I should get
| from | to | route | distance |
| a | b | ab,ab | 450m |
| a | c | ac,ac | 200m |
| a | d | ac,dc,dc | 500m +- 1 |
When I request a travel time matrix I should get
| | a |
| a | 0 |
| b | 1 |
| c | 15 |
| d | 10 |
When I request a travel distance matrix I should get
| | a | b | c | d |
| a | 0 | 450+-2 | 200+-2 | 500+-2 |
When I request a travel distance matrix I should get
| | a |
| a | 0 |
| b | 450+-2 |
| c | 200+-2 |
| d | 500+-2 |
When I request a travel distance matrix I should get
| | a | c |
| a | 0 | 200+-2 |
| c | 200+-2 | 0 |
# Check rounding errors
Scenario: Testbot - Long distances in tables
Given a grid size of 1000 meters
Given the node map
"""
a b c d
"""
And the ways
| nodes |
| abcd |
When I request a travel distance matrix I should get
| | a | b | c | d |
| a | 0 | 1000+-3 | 2000+-3 | 3000+-3 |
Scenario: Testbot - OneToMany vs ManyToOne
Given the node map
"""
a b
c
"""
And the ways
| nodes | oneway |
| ab | yes |
| ac | |
| bc | |
When I request a travel distance matrix I should get
| | a | b |
| b | 240.4 | 0 |
When I request a travel distance matrix I should get
| | a |
| a | 0 |
| b | 240.4 |
Scenario: Testbot - Varying distances between nodes
Given the node map
"""
a b c d
e
f
"""
And the ways
| nodes | oneway |
| feabcd | yes |
| ec | |
| fd | |
When I request a travel distance matrix I should get
| | a | b | c | d | e | f |
| a | 0 | 100+-1 | 300+-1 | 650+-1 | 1930+-1 | 1533+-1 |
| b | 760+-1 | 0 | 200+-1 | 550+-1 | 1830+-1 | 1433+-1 |
| c | 560+-2 | 660+-2 | 0 | 350+-1 | 1630+-1 | 1233+-1 |
| d | 1480+-2 | 1580+-1 | 1780+-1 | 0 | 1280+-1 | 883+-1 |
| e | 200+-2 | 300+-2 | 500+-1 | 710+-1 | 0 | 1593+-1 |
| f | 597+-1 | 696+-1 | 896+-1 | 1108+-1 | 400+-3 | 0 |
+512
View File
@@ -0,0 +1,512 @@
@matrix @testbot
Feature: Basic Duration Matrix
# note that results of travel time are in seconds
Background:
Given the profile "testbot"
And the partition extra arguments "--small-component-size 1 --max-cell-sizes 2,4,8,16"
Scenario: Testbot - Travel time matrix of minimal network
Given the node map
"""
a b
"""
And the ways
| nodes |
| ab |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 10 |
| b | 10 | 0 |
@ch
Scenario: Testbot - Travel time matrix of minimal network with toll exclude
Given the query options
| exclude | toll |
Given the node map
"""
a b
c d
"""
And the ways
| nodes | highway | toll | # |
| ab | motorway | | not drivable for exclude=motorway |
| cd | primary | | always drivable |
| ac | motorway | yes | not drivable for exclude=toll and exclude=motorway,toll |
| bd | motorway | yes | not drivable for exclude=toll and exclude=motorway,toll |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 15 | | |
| b | 15 | 0 | | |
| c | | | 0 | 10 |
| d | | | 10 | 0 |
@ch
Scenario: Testbot - Travel time matrix of minimal network with motorway exclude
Given the query options
| exclude | motorway |
Given the node map
"""
a b
c d
"""
And the ways
| nodes | highway | # |
| ab | motorway | not drivable for exclude=motorway |
| cd | residential | |
| ac | residential | |
| bd | residential | |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 45 | 15 | 30 |
@ch
Scenario: Testbot - Travel time matrix of minimal network disconnected motorway exclude
Given the query options
| exclude | motorway |
Given the node map
"""
ab efgh
cd
"""
And the ways
| nodes | highway | # |
| be | motorway | not drivable for exclude=motorway |
| abcd | residential | |
| efgh | residential | |
When I request a travel time matrix I should get
| | a | b | e |
| a | 0 | 7.5 | |
@ch
Scenario: Testbot - Travel time matrix of minimal network with motorway and toll excludes
Given the query options
| exclude | motorway,toll |
Given the node map
"""
a b e f
c d g h
"""
And the ways
| nodes | highway | toll | # |
| be | motorway | | not drivable for exclude=motorway |
| dg | primary | yes | not drivable for exclude=toll |
| abcd | residential | | |
| efgh | residential | | |
When I request a travel time matrix I should get
| | a | b | e | g |
| a | 0 | 15 | | |
Scenario: Testbot - Travel time matrix with different way speeds
Given the node map
"""
a b c d
"""
And the ways
| nodes | highway |
| ab | primary |
| bc | secondary |
| cd | tertiary |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 10 | 30 | 60 |
| b | 10 | 0 | 20 | 50 |
| c | 30 | 20 | 0 | 30 |
| d | 60 | 50 | 30 | 0 |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 10 | 30 | 60 |
When I request a travel time matrix I should get
| | a |
| a | 0 |
| b | 10 |
| c | 30 |
| d | 60 |
Scenario: Testbot - Travel time matrix of small grid
Given the node map
"""
a b c
d e f
"""
And the ways
| nodes |
| abc |
| def |
| ad |
| be |
| cf |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
| e | 20 | 10 | 0 | 10 |
| f | 30 | 20 | 10 | 0 |
Scenario: Testbot - Travel time matrix of network with unroutable parts
Given the node map
"""
a b
"""
And the ways
| nodes | oneway |
| ab | yes |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 10 |
| b | | 0 |
Scenario: Testbot - Travel time matrix of network with oneways
Given the node map
"""
x a b y
d e
"""
And the ways
| nodes | oneway |
| abeda | yes |
| xa | |
| by | |
When I request a travel time matrix I should get
| | x | y | d | e |
| x | 0 | 30 | 40 | 30 |
| y | 50 | 0 | 30 | 20 |
| d | 20 | 30 | 0 | 30 |
| e | 30 | 40 | 10 | 0 |
Scenario: Testbot - Rectangular travel time matrix
Given the node map
"""
a b c
d e f
"""
And the ways
| nodes |
| abc |
| def |
| ad |
| be |
| cf |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
When I request a travel time matrix I should get
| | a |
| a | 0 |
| b | 10 |
| e | 20 |
| f | 30 |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 10 |
| b | 10 | 0 |
| e | 20 | 10 |
| f | 30 | 20 |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
| e | 20 | 10 | 0 | 10 |
When I request a travel time matrix I should get
| | a | b | e |
| a | 0 | 10 | 20 |
| b | 10 | 0 | 10 |
| e | 20 | 10 | 0 |
| f | 30 | 20 | 10 |
When I request a travel time matrix I should get
| | a | b | e | f |
| a | 0 | 10 | 20 | 30 |
| b | 10 | 0 | 10 | 20 |
| e | 20 | 10 | 0 | 10 |
| f | 30 | 20 | 10 | 0 |
Scenario: Testbot - Travel time 3x2 matrix
Given the node map
"""
a b c
d e f
"""
And the ways
| nodes |
| abc |
| def |
| ad |
| be |
| cf |
When I request a travel time matrix I should get
| | b | e | f |
| a | 10 | 20 | 30 |
| b | 0 | 10 | 20 |
Scenario: Testbot - All coordinates are from same small component
Given a grid size of 300 meters
Given the extract extra arguments "--small-component-size 4"
Given the node map
"""
a b f
d e g
"""
And the ways
| nodes |
| ab |
| be |
| ed |
| da |
| fg |
When I request a travel time matrix I should get
| | f | g |
| f | 0 | 30 |
| g | 30 | 0 |
Scenario: Testbot - Coordinates are from different small component and snap to big CC
Given a grid size of 300 meters
Given the extract extra arguments "--small-component-size 4"
Given the node map
"""
a b f h
d e g i
"""
And the ways
| nodes |
| ab |
| be |
| ed |
| da |
| fg |
| hi |
When I request a travel time matrix I should get
| | f | g | h | i |
| f | 0 | 30 | 0 | 30 |
| g | 30 | 0 | 30 | 0 |
| h | 0 | 30 | 0 | 30 |
| i | 30 | 0 | 30 | 0 |
Scenario: Testbot - Travel time matrix with loops
Given the node map
"""
a 1 2 b
d 4 3 c
"""
And the ways
| nodes | oneway |
| ab | yes |
| bc | yes |
| cd | yes |
| da | yes |
When I request a travel time matrix I should get
| | 1 | 2 | 3 | 4 |
| 1 | 0 | 10 +-1 | 40 +-1 | 50 +-1 |
| 2 | 70 +-1 | 0 | 30 +-1 | 40 +-1 |
| 3 | 40 +-1 | 50 +-1 | 0 | 10 +-1 |
| 4 | 30 +-1 | 40 +-1 | 70 +-1 | 0 |
Scenario: Testbot - Travel time matrix based on segment durations
Given the profile file
"""
local functions = require('testbot')
functions.setup_testbot = functions.setup
functions.setup = function()
local profile = functions.setup_testbot()
profile.traffic_signal_penalty = 0
profile.u_turn_penalty = 0
return profile
end
functions.process_segment = function(profile, segment)
segment.weight = 2
segment.duration = 11
end
return functions
"""
And the node map
"""
a-b-c-d
.
e
"""
And the ways
| nodes |
| abcd |
| ce |
When I request a travel time matrix I should get
| | a | b | c | d | e |
| a | 0 | 11 | 22 | 33 | 33 |
| b | 11 | 0 | 11 | 22 | 22 |
| c | 22 | 11 | 0 | 11 | 11 |
| d | 33 | 22 | 11 | 0 | 22 |
| e | 33 | 22 | 11 | 22 | 0 |
Scenario: Testbot - Travel time matrix for alternative loop paths
Given the profile file
"""
local functions = require('testbot')
functions.setup_testbot = functions.setup
functions.setup = function()
local profile = functions.setup_testbot()
profile.traffic_signal_penalty = 0
profile.u_turn_penalty = 0
profile.weight_precision = 3
return profile
end
functions.process_segment = function(profile, segment)
segment.weight = 777
segment.duration = 3
end
return functions
"""
And the node map
"""
a 2 1 b
7 4
8 3
c 5 6 d
"""
And the ways
| nodes | oneway |
| ab | yes |
| bd | yes |
| dc | yes |
| ca | yes |
When I request a travel time matrix I should get
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 1 | 0 | 11 | 3 | 2 | 6 | 5 | 8.9 | 7.9 |
| 2 | 1 | 0 | 4 | 3 | 7 | 6 | 9.9 | 8.9 |
| 3 | 9 | 8 | 0 | 11 | 3 | 2 | 5.9 | 4.9 |
| 4 | 10 | 9 | 1 | 0 | 4 | 3 | 6.9 | 5.9 |
| 5 | 6 | 5 | 9 | 8 | 0 | 11 | 2.9 | 1.9 |
| 6 | 7 | 6 | 10 | 9 | 1 | 0 | 3.9 | 2.9 |
| 7 | 3.1 | 2.1 | 6.1 | 5.1 | 9.1 | 8.1 | 0 | 11 |
| 8 | 4.1 | 3.1 | 7.1 | 6.1 | 10.1 | 9.1 | 1 | 0 |
Scenario: Testbot - Travel time matrix with ties
Given the profile file
"""
local functions = require('testbot')
functions.process_segment = function(profile, segment)
segment.weight = 1
segment.duration = 1
end
functions.process_turn = function(profile, turn)
if turn.angle >= 0 then
turn.duration = 16
else
turn.duration = 4
end
turn.weight = 0
end
return functions
"""
And the node map
"""
a b
c d
"""
And the ways
| nodes |
| ab |
| ac |
| bd |
| dc |
When I route I should get
| from | to | route | distance | time | weight |
| a | c | ac,ac | 200m | 5s | 5 |
When I request a travel time matrix I should get
| | a | b | c | d |
| a | 0 | 1 | 5 | 10 |
When I request a travel time matrix I should get
| | a |
| a | 0 |
| b | 1 |
| c | 15 |
| d | 10 |
Scenario: Testbot - OneToMany vs ManyToOne
Given the node map
"""
a b
c
"""
And the ways
| nodes | oneway |
| ab | yes |
| ac | |
| bc | |
When I request a travel time matrix I should get
| | a | b |
| b | 24.1 | 0 |
When I request a travel time matrix I should get
| | a |
| a | 0 |
| b | 24.1 |
+61 -2
View File
@@ -39,5 +39,64 @@ Feature: Fixed bugs, kept to check for regressions
| de | yes |
When I route I should get
| from | to | route |
| 1 | 2 | bcd,bcd |
| from | to | route |
| 1 | 2 | bcd,bcd |
#############################
# This test models the OSM map at the location for
# https://github.com/Project-OSRM/osrm-backend/issues/5039
#############################
Scenario: Mixed Entry and Exit and segregated
Given the profile file "car" initialized with
"""
profile.properties.left_hand_driving = true
"""
Given the node locations
| node | lon | lat |
| a | 171.12889297029 | -42.58425289548 |
| b | 171.1299357 | -42.5849295 |
| c | 171.1295427 | -42.5849385 |
| d | 171.1297356 | -42.5852029 |
| e | 171.1296909 | -42.5851986 |
| f | 171.1295097 | -42.585007 |
| g | 171.1298225 | -42.5851928 |
| h | 171.1300262 | -42.5859122 |
| i | 171.1292651 | -42.584698 |
| j | 171.1297209 | -42.5848569 |
| k | 171.1297188 | -42.5854056 |
| l | 171.1298326 | -42.5857266 |
| m | 171.1298871 | -42.5848922 |
| n | 171.1296505 | -42.585189 |
| o | 171.1295206 | -42.5850862 |
| p | 171.1296106 | -42.5848862 |
| q | 171.1299784 | -42.5850191 |
| r | 171.1298867 | -42.5851671 |
| s | 171.1306955 | -42.5845812 |
| t | 171.129525 | -42.584807 |
| u | 171.1299705 | -42.584984 |
| v | 171.1299067 | -42.5849073 |
| w | 171.1302061 | -42.5848173 |
| x | 171.12975 | -42.5855753 |
| y | 171.129969 | -42.585079 |
| 1 | 171.131511926651| -42.584306746421966 |
| 2 | 171.128743886947| -42.58414875714669 |
And the ways
| nodes | highway | maxspeed | name | ref | surface | junction | oneway |
| ws | primary | 100 | Taramakau Highway | SH 6 | asphalt | | |
| kxlh | trunk | | Otira Highway | SH 73 | | | |
| ai | primary | 100 | Kumara Junction Highway | SH 6 | asphalt | | |
| qyrgdenof | primary | 100 | Kumara Junction | | | roundabout | yes |
| ke | trunk | | Otira Highway | SH 73 | | | yes |
| itj | primary | 100 | Kumara Junction Highway | SH 6 | | | yes |
| gk | trunk | | Otira Highway | SH 73 | | | yes |
| fi | primary | 100 | Kumara Junction Highway | SH 6 | | | yes |
| wq | primary | 100 | Taramakau Highway | SH 6 | | | yes |
| vw | primary | 100 | Taramakau Highway | SH 6 | | | yes |
| vbuq | primary | 100 | Kumara Junction | | | roundabout | yes |
| jmv | primary | 100 | Kumara Junction | | | roundabout | yes |
| fcpj | primary | 100 | Kumara Junction | | | roundabout | yes |
When I route I should get
| waypoints | route | turns |
| 1,2 | Taramakau Highway,Kumara Junction Highway,Kumara Junction Highway,Kumara Junction Highway | depart,Kumara Junction-exit-2,exit rotary slight left,arrive |
@@ -106,6 +106,40 @@ Feature: Multi level routing
| l | 144.7 | 60 |
| o | 124.7 | 0 |
When I request a travel distance matrix I should get
| | a | f | l | o |
| a | 0+-2 | 2287+-2 | 1443+-2 | 1243+-2 |
| f | 2284+-2 | 0+-2 | 1241+-2 | 1443+-2 |
| l | 1443+-2 | 1244+-2 | 0+-2 | 600+-2 |
| o | 1243+-2 | 1444+-2 | 600+-2 | 0+-2 |
When I request a travel distance matrix I should get
| | a | f | l | o |
| a | 0 | 2287.2+-2 | 1443+-2 | 1243+-2 |
When I request a travel distance matrix I should get
| | a |
| a | 0 |
| f | 2284.5+-2 |
| l | 1443.1 |
| o | 1243 |
When I request a travel distance matrix I should get
| | a | f | l | o |
| a | 0 | 2287+-2 | 1443+-2 | 1243+-2 |
| o | 1243 | 1444+-2 | 600+-2 | 0+-2 |
When I request a travel distance matrix I should get
| | a | o |
| a | 0+-2 | 1243+-2 |
| f | 2284+-2 | 1443+-2 |
| l | 1443+-2 | 600+-2 |
| o | 1243+-2 | 0+-2 |
Scenario: Testbot - Multi level routing: horizontal road
Given the node map
"""
+1 -1
View File
@@ -54,7 +54,7 @@ Feature: Traffic - speeds
| a | d | ad,ad | 27 km/h | 1275.7,0 | 1 |
| d | c | dc,dc | 36 km/h | 956.8,0 | 0 |
| g | b | fb,fb | 36 km/h | 164.7,0 | 0 |
| a | g | ad,df,fb,fb | 30 km/h | 1275.7,487.5,304.7,0 | 1:0:0 |
| a | g | ad,df,fb,fb | 30 km/h | 1295.7,487.5,304.7,0 | 1:0:0 |
Scenario: Weighting based on speed file weights, ETA based on file durations
+2 -1
View File
@@ -21,7 +21,8 @@ struct CustomizationConfig final : storage::IOConfig
".osrm.partition",
".osrm.cells",
".osrm.ebg_nodes",
".osrm.properties"},
".osrm.properties",
".osrm.enw"},
{},
{".osrm.cell_metrics", ".osrm.mldgr"}),
requested_num_threads(0)
+97 -16
View File
@@ -16,28 +16,109 @@ namespace osrm
namespace customizer
{
using EdgeBasedGraphEdgeData = partitioner::EdgeBasedGraphEdgeData;
struct MultiLevelEdgeBasedGraph
: public partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>
struct EdgeBasedGraphEdgeData
{
using Base =
partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>;
using Base::Base;
NodeID turn_id; // ID of the edge based node (node based edge)
};
struct MultiLevelEdgeBasedGraphView
: public partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::View>
template <typename EdgeDataT, storage::Ownership Ownership> class MultiLevelGraph;
namespace serialization
{
using Base = partitioner::MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::View>;
using Base::Base;
template <typename EdgeDataT, storage::Ownership Ownership>
void read(storage::tar::FileReader &reader,
const std::string &name,
MultiLevelGraph<EdgeDataT, Ownership> &graph);
template <typename EdgeDataT, storage::Ownership Ownership>
void write(storage::tar::FileWriter &writer,
const std::string &name,
const MultiLevelGraph<EdgeDataT, Ownership> &graph);
}
template <typename EdgeDataT, storage::Ownership Ownership>
class MultiLevelGraph : public partitioner::MultiLevelGraph<EdgeDataT, Ownership>
{
private:
using SuperT = partitioner::MultiLevelGraph<EdgeDataT, Ownership>;
using PartitionerGraphT = partitioner::MultiLevelGraph<partitioner::EdgeBasedGraphEdgeData,
storage::Ownership::Container>;
template <typename T> using Vector = util::ViewOrVector<T, Ownership>;
public:
using NodeArrayEntry = typename SuperT::NodeArrayEntry;
using EdgeArrayEntry = typename SuperT::EdgeArrayEntry;
using EdgeOffset = typename SuperT::EdgeOffset;
MultiLevelGraph() = default;
MultiLevelGraph(MultiLevelGraph &&) = default;
MultiLevelGraph(const MultiLevelGraph &) = default;
MultiLevelGraph &operator=(MultiLevelGraph &&) = default;
MultiLevelGraph &operator=(const MultiLevelGraph &) = default;
MultiLevelGraph(PartitionerGraphT &&graph,
Vector<EdgeWeight> node_weights_,
Vector<EdgeDuration> node_durations_)
: node_weights(std::move(node_weights_)), node_durations(std::move(node_durations_))
{
util::ViewOrVector<PartitionerGraphT::EdgeArrayEntry, storage::Ownership::Container>
original_edge_array;
std::tie(SuperT::node_array,
original_edge_array,
SuperT::node_to_edge_offset,
SuperT::connectivity_checksum) = std::move(graph).data();
SuperT::edge_array.reserve(original_edge_array.size());
for (const auto &edge : original_edge_array)
{
SuperT::edge_array.push_back({edge.target, {edge.data.turn_id}});
is_forward_edge.push_back(edge.data.forward);
is_backward_edge.push_back(edge.data.backward);
}
}
MultiLevelGraph(Vector<NodeArrayEntry> node_array_,
Vector<EdgeArrayEntry> edge_array_,
Vector<EdgeOffset> node_to_edge_offset_,
Vector<EdgeWeight> node_weights_,
Vector<EdgeDuration> node_durations_,
Vector<bool> is_forward_edge_,
Vector<bool> is_backward_edge_)
: SuperT(std::move(node_array_), std::move(edge_array_), std::move(node_to_edge_offset_)),
node_weights(std::move(node_weights_)), node_durations(std::move(node_durations_)),
is_forward_edge(is_forward_edge_), is_backward_edge(is_backward_edge_)
{
}
EdgeWeight GetNodeWeight(NodeID node) const { return node_weights[node]; }
EdgeWeight GetNodeDuration(NodeID node) const { return node_durations[node]; }
bool IsForwardEdge(EdgeID edge) const { return is_forward_edge[edge]; }
bool IsBackwardEdge(EdgeID edge) const { return is_backward_edge[edge]; }
friend void
serialization::read<EdgeDataT, Ownership>(storage::tar::FileReader &reader,
const std::string &name,
MultiLevelGraph<EdgeDataT, Ownership> &graph);
friend void
serialization::write<EdgeDataT, Ownership>(storage::tar::FileWriter &writer,
const std::string &name,
const MultiLevelGraph<EdgeDataT, Ownership> &graph);
protected:
Vector<EdgeWeight> node_weights;
Vector<EdgeDuration> node_durations;
Vector<bool> is_forward_edge;
Vector<bool> is_backward_edge;
};
struct StaticEdgeBasedGraphEdge : MultiLevelEdgeBasedGraph::InputEdge
{
using Base = MultiLevelEdgeBasedGraph::InputEdge;
using Base::Base;
};
using MultiLevelEdgeBasedGraph =
MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>;
using MultiLevelEdgeBasedGraphView =
MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::View>;
}
}
+33
View File
@@ -73,6 +73,39 @@ writeCellMetrics(const boost::filesystem::path &path,
}
}
}
// reads .osrm.mldgr file
template <typename MultiLevelGraphT>
inline void readGraph(const boost::filesystem::path &path,
MultiLevelGraphT &graph,
std::uint32_t &connectivity_checksum)
{
static_assert(std::is_same<customizer::MultiLevelEdgeBasedGraphView, MultiLevelGraphT>::value ||
std::is_same<customizer::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value,
"");
storage::tar::FileReader reader{path, storage::tar::FileReader::VerifyFingerprint};
reader.ReadInto("/mld/connectivity_checksum", connectivity_checksum);
serialization::read(reader, "/mld/multilevelgraph", graph);
}
// writes .osrm.mldgr file
template <typename MultiLevelGraphT>
inline void writeGraph(const boost::filesystem::path &path,
const MultiLevelGraphT &graph,
const std::uint32_t connectivity_checksum)
{
static_assert(std::is_same<customizer::MultiLevelEdgeBasedGraphView, MultiLevelGraphT>::value ||
std::is_same<customizer::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value,
"");
storage::tar::FileWriter writer{path, storage::tar::FileWriter::GenerateFingerprint};
writer.WriteElementCount64("/mld/connectivity_checksum", 1);
writer.WriteFrom("/mld/connectivity_checksum", connectivity_checksum);
serialization::write(writer, "/mld/multilevelgraph", graph);
}
}
}
}
+30
View File
@@ -1,6 +1,8 @@
#ifndef OSRM_CUSTOMIZER_SERIALIZATION_HPP
#define OSRM_CUSTOMIZER_SERIALIZATION_HPP
#include "customizer/edge_based_graph.hpp"
#include "partitioner/cell_storage.hpp"
#include "storage/serialization.hpp"
@@ -31,6 +33,34 @@ inline void write(storage::tar::FileWriter &writer,
storage::serialization::write(writer, name + "/weights", metric.weights);
storage::serialization::write(writer, name + "/durations", metric.durations);
}
template <typename EdgeDataT, storage::Ownership Ownership>
inline void read(storage::tar::FileReader &reader,
const std::string &name,
MultiLevelGraph<EdgeDataT, Ownership> &graph)
{
storage::serialization::read(reader, name + "/node_array", graph.node_array);
storage::serialization::read(reader, name + "/node_weights", graph.node_weights);
storage::serialization::read(reader, name + "/node_durations", graph.node_durations);
storage::serialization::read(reader, name + "/edge_array", graph.edge_array);
storage::serialization::read(reader, name + "/is_forward_edge", graph.is_forward_edge);
storage::serialization::read(reader, name + "/is_backward_edge", graph.is_backward_edge);
storage::serialization::read(reader, name + "/node_to_edge_offset", graph.node_to_edge_offset);
}
template <typename EdgeDataT, storage::Ownership Ownership>
inline void write(storage::tar::FileWriter &writer,
const std::string &name,
const MultiLevelGraph<EdgeDataT, Ownership> &graph)
{
storage::serialization::write(writer, name + "/node_array", graph.node_array);
storage::serialization::write(writer, name + "/node_weights", graph.node_weights);
storage::serialization::write(writer, name + "/node_durations", graph.node_durations);
storage::serialization::write(writer, name + "/edge_array", graph.edge_array);
storage::serialization::write(writer, name + "/is_forward_edge", graph.is_forward_edge);
storage::serialization::write(writer, name + "/is_backward_edge", graph.is_backward_edge);
storage::serialization::write(writer, name + "/node_to_edge_offset", graph.node_to_edge_offset);
}
}
}
}
+9
View File
@@ -50,6 +50,9 @@ template <typename AlgorithmT> struct HasMapMatching final : std::false_type
template <typename AlgorithmT> struct HasManyToManySearch final : std::false_type
{
};
template <typename AlgorithmT> struct SupportsDistanceAnnotationType final : std::false_type
{
};
template <typename AlgorithmT> struct HasGetTileTurns final : std::false_type
{
};
@@ -73,6 +76,9 @@ template <> struct HasMapMatching<ch::Algorithm> final : std::true_type
template <> struct HasManyToManySearch<ch::Algorithm> final : std::true_type
{
};
template <> struct SupportsDistanceAnnotationType<ch::Algorithm> final : std::true_type
{
};
template <> struct HasGetTileTurns<ch::Algorithm> final : std::true_type
{
};
@@ -96,6 +102,9 @@ template <> struct HasMapMatching<mld::Algorithm> final : std::true_type
template <> struct HasManyToManySearch<mld::Algorithm> final : std::true_type
{
};
template <> struct SupportsDistanceAnnotationType<mld::Algorithm> final : std::false_type
{
};
template <> struct HasGetTileTurns<mld::Algorithm> final : std::true_type
{
};
+48 -8
View File
@@ -36,9 +36,10 @@ class TableAPI final : public BaseAPI
{
}
virtual void MakeResponse(const std::vector<EdgeWeight> &durations,
const std::vector<PhantomNode> &phantoms,
util::json::Object &response) const
virtual void
MakeResponse(const std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>> &tables,
const std::vector<PhantomNode> &phantoms,
util::json::Object &response) const
{
auto number_of_sources = parameters.sources.size();
auto number_of_destinations = parameters.destinations.size();
@@ -64,8 +65,18 @@ class TableAPI final : public BaseAPI
response.values["destinations"] = MakeWaypoints(phantoms, parameters.destinations);
}
response.values["durations"] =
MakeTable(durations, number_of_sources, number_of_destinations);
if (parameters.annotations & TableParameters::AnnotationsType::Duration)
{
response.values["durations"] =
MakeDurationTable(tables.first, number_of_sources, number_of_destinations);
}
if (parameters.annotations & TableParameters::AnnotationsType::Distance)
{
response.values["distances"] =
MakeDistanceTable(tables.second, number_of_sources, number_of_destinations);
}
response.values["code"] = "Ok";
}
@@ -97,9 +108,9 @@ class TableAPI final : public BaseAPI
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
virtual util::json::Array MakeDurationTable(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>(0UL, number_of_rows))
@@ -116,6 +127,7 @@ class TableAPI final : public BaseAPI
{
return util::json::Value(util::json::Null());
}
// division by 10 because the duration is in deciseconds (10s)
return util::json::Value(util::json::Number(duration / 10.));
});
json_table.values.push_back(std::move(json_row));
@@ -123,6 +135,34 @@ class TableAPI final : public BaseAPI
return json_table;
}
virtual util::json::Array MakeDistanceTable(const std::vector<EdgeDistance> &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>(0UL, 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 EdgeDistance distance) {
if (distance == INVALID_EDGE_DISTANCE)
{
return util::json::Value(util::json::Null());
}
// round to single decimal place
return util::json::Value(
util::json::Number(std::round(distance * 10) / 10.));
});
json_table.values.push_back(std::move(json_row));
}
return json_table;
}
const TableParameters &parameters;
};
+41 -1
View File
@@ -60,6 +60,16 @@ struct TableParameters : public BaseParameters
std::vector<std::size_t> sources;
std::vector<std::size_t> destinations;
enum class AnnotationsType
{
None = 0,
Duration = 0x01,
Distance = 0x02,
All = Duration | Distance
};
AnnotationsType annotations = AnnotationsType::Duration;
TableParameters() = default;
template <typename... Args>
TableParameters(std::vector<std::size_t> sources_,
@@ -70,6 +80,16 @@ struct TableParameters : public BaseParameters
{
}
template <typename... Args>
TableParameters(std::vector<std::size_t> sources_,
std::vector<std::size_t> destinations_,
const AnnotationsType annotations_,
Args... args_)
: BaseParameters{std::forward<Args>(args_)...}, sources{std::move(sources_)},
destinations{std::move(destinations_)}, annotations{annotations_}
{
}
bool IsValid() const
{
if (!BaseParameters::IsValid())
@@ -79,7 +99,7 @@ struct TableParameters : public BaseParameters
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
// 1/ The user is able to specify duplicates in srcs and dsts, in that case it's their fault
// 2/ len(srcs) and len(dsts) smaller or equal to len(locations)
if (sources.size() > coordinates.size())
@@ -100,6 +120,26 @@ struct TableParameters : public BaseParameters
return true;
}
};
inline bool operator&(TableParameters::AnnotationsType lhs, TableParameters::AnnotationsType rhs)
{
return static_cast<bool>(
static_cast<std::underlying_type_t<TableParameters::AnnotationsType>>(lhs) &
static_cast<std::underlying_type_t<TableParameters::AnnotationsType>>(rhs));
}
inline TableParameters::AnnotationsType operator|(TableParameters::AnnotationsType lhs,
TableParameters::AnnotationsType rhs)
{
return (TableParameters::AnnotationsType)(
static_cast<std::underlying_type_t<TableParameters::AnnotationsType>>(lhs) |
static_cast<std::underlying_type_t<TableParameters::AnnotationsType>>(rhs));
}
inline TableParameters::AnnotationsType &operator|=(TableParameters::AnnotationsType &lhs,
TableParameters::AnnotationsType rhs)
{
return lhs = lhs | rhs;
}
}
}
}
@@ -2,6 +2,7 @@
#define OSRM_ENGINE_DATAFACADE_ALGORITHM_DATAFACADE_HPP
#include "contractor/query_edge.hpp"
#include "customizer/edge_based_graph.hpp"
#include "extractor/edge_based_edge.hpp"
#include "engine/algorithm.hpp"
@@ -59,7 +60,7 @@ template <> class AlgorithmDataFacade<CH>
template <> class AlgorithmDataFacade<MLD>
{
public:
using EdgeData = extractor::EdgeBasedEdge::EdgeData;
using EdgeData = customizer::EdgeBasedGraphEdgeData;
using EdgeRange = util::range<EdgeID>;
// search graph access
@@ -71,12 +72,20 @@ template <> class AlgorithmDataFacade<MLD>
virtual unsigned GetOutDegree(const NodeID n) const = 0;
virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0;
virtual EdgeWeight GetNodeWeight(const NodeID node) const = 0;
virtual EdgeWeight GetNodeDuration(const NodeID node) const = 0; // TODO: to be removed
virtual bool IsForwardEdge(EdgeID edge) const = 0;
virtual bool IsBackwardEdge(EdgeID edge) const = 0;
virtual NodeID GetTarget(const EdgeID e) const = 0;
virtual const EdgeData &GetEdgeData(const EdgeID e) const = 0;
virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0;
virtual const partitioner::MultiLevelPartitionView &GetMultiLevelPartition() const = 0;
virtual const partitioner::CellStorageView &GetCellStorage() const = 0;
@@ -133,7 +133,6 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
using RTreeNode = SharedRTree::TreeNode;
extractor::ClassData exclude_mask;
std::string m_timestamp;
extractor::ProfileProperties *m_profile_properties;
extractor::Datasources *m_datasources;
@@ -283,13 +282,13 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
return segment_data.GetReverseDatasources(id);
}
TurnPenalty GetWeightPenaltyForEdgeID(const unsigned id) const override final
TurnPenalty GetWeightPenaltyForEdgeID(const EdgeID id) const override final
{
BOOST_ASSERT(m_turn_weight_penalties.size() > id);
return m_turn_weight_penalties[id];
}
TurnPenalty GetDurationPenaltyForEdgeID(const unsigned id) const override final
TurnPenalty GetDurationPenaltyForEdgeID(const EdgeID id) const override final
{
BOOST_ASSERT(m_turn_duration_penalties.size() > id);
return m_turn_duration_penalties[id];
@@ -622,7 +621,6 @@ class ContiguousInternalMemoryDataFacade<CH>
const std::size_t exclude_index)
: ContiguousInternalMemoryDataFacadeBase(allocator, metric_name, exclude_index),
ContiguousInternalMemoryAlgorithmDataFacade<CH>(allocator, metric_name, exclude_index)
{
}
};
@@ -684,6 +682,31 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
return query_graph.GetOutDegree(n);
}
EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final
{
return query_graph.GetAdjacentEdgeRange(node);
}
EdgeWeight GetNodeWeight(const NodeID node) const override final
{
return query_graph.GetNodeWeight(node);
}
EdgeDuration GetNodeDuration(const NodeID node) const override final
{
return query_graph.GetNodeDuration(node);
}
bool IsForwardEdge(const NodeID node) const override final
{
return query_graph.IsForwardEdge(node);
}
bool IsBackwardEdge(const NodeID node) const override final
{
return query_graph.IsBackwardEdge(node);
}
NodeID GetTarget(const EdgeID e) const override final { return query_graph.GetTarget(e); }
const EdgeData &GetEdgeData(const EdgeID e) const override final
@@ -691,11 +714,6 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
return query_graph.GetEdgeData(e);
}
EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final
{
return query_graph.GetAdjacentEdgeRange(node);
}
EdgeRange GetBorderEdgeRange(const LevelID level, const NodeID node) const override final
{
return query_graph.GetBorderEdgeRange(level, node);
@@ -720,7 +738,6 @@ class ContiguousInternalMemoryDataFacade<MLD> final
const std::size_t exclude_index)
: ContiguousInternalMemoryDataFacadeBase(allocator, metric_name, exclude_index),
ContiguousInternalMemoryAlgorithmDataFacade<MLD>(allocator, metric_name, exclude_index)
{
}
};
@@ -33,7 +33,6 @@
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/any_range.hpp>
#include <cstddef>
#include <string>
@@ -87,9 +86,9 @@ class BaseDataFacade
virtual NodeForwardRange GetUncompressedForwardGeometry(const EdgeID id) const = 0;
virtual NodeReverseRange GetUncompressedReverseGeometry(const EdgeID id) const = 0;
virtual TurnPenalty GetWeightPenaltyForEdgeID(const unsigned id) const = 0;
virtual TurnPenalty GetWeightPenaltyForEdgeID(const EdgeID id) const = 0;
virtual TurnPenalty GetDurationPenaltyForEdgeID(const unsigned id) const = 0;
virtual TurnPenalty GetDurationPenaltyForEdgeID(const EdgeID id) const = 0;
// Gets the weight values for each segment in an uncompressed geometry.
// Should always be 1 shorter than GetUncompressedGeometry
+35 -2
View File
@@ -13,6 +13,7 @@
#include <algorithm>
#include <cmath>
#include <iterator>
#include <memory>
#include <vector>
@@ -447,6 +448,8 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
const auto forward_durations = datafacade.GetUncompressedForwardDurations(geometry_id);
const auto reverse_durations = datafacade.GetUncompressedReverseDurations(geometry_id);
const auto forward_geometry = datafacade.GetUncompressedForwardGeometry(geometry_id);
const auto forward_weight_offset =
std::accumulate(forward_weights.begin(),
forward_weights.begin() + data.fwd_segment_position,
@@ -457,12 +460,25 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
forward_durations.begin() + data.fwd_segment_position,
EdgeDuration{0});
EdgeWeight forward_weight = forward_weights[data.fwd_segment_position];
EdgeDuration forward_duration = forward_durations[data.fwd_segment_position];
EdgeDistance forward_distance_offset = 0;
for (auto current = forward_geometry.begin();
current < forward_geometry.begin() + data.fwd_segment_position;
++current)
{
forward_distance_offset += util::coordinate_calculation::fccApproximateDistance(
datafacade.GetCoordinateOfNode(*current),
datafacade.GetCoordinateOfNode(*std::next(current)));
}
BOOST_ASSERT(data.fwd_segment_position <
std::distance(forward_durations.begin(), forward_durations.end()));
EdgeWeight forward_weight = forward_weights[data.fwd_segment_position];
EdgeDuration forward_duration = forward_durations[data.fwd_segment_position];
EdgeDistance forward_distance = util::coordinate_calculation::fccApproximateDistance(
datafacade.GetCoordinateOfNode(forward_geometry(data.fwd_segment_position)),
point_on_segment);
const auto reverse_weight_offset =
std::accumulate(reverse_weights.begin(),
reverse_weights.end() - data.fwd_segment_position - 1,
@@ -473,10 +489,23 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
reverse_durations.end() - data.fwd_segment_position - 1,
EdgeDuration{0});
EdgeDistance reverse_distance_offset = 0;
for (auto current = forward_geometry.begin();
current < forward_geometry.end() - data.fwd_segment_position - 2;
++current)
{
reverse_distance_offset += util::coordinate_calculation::fccApproximateDistance(
datafacade.GetCoordinateOfNode(*current),
datafacade.GetCoordinateOfNode(*std::next(current)));
}
EdgeWeight reverse_weight =
reverse_weights[reverse_weights.size() - data.fwd_segment_position - 1];
EdgeDuration reverse_duration =
reverse_durations[reverse_durations.size() - data.fwd_segment_position - 1];
EdgeDistance reverse_distance = util::coordinate_calculation::fccApproximateDistance(
point_on_segment,
datafacade.GetCoordinateOfNode(forward_geometry(data.fwd_segment_position + 1)));
ratio = std::min(1.0, std::max(0.0, ratio));
if (data.forward_segment_id.id != SPECIAL_SEGMENTID)
@@ -510,6 +539,10 @@ template <typename RTreeT, typename DataFacadeT> class GeospatialQuery
reverse_weight,
forward_weight_offset,
reverse_weight_offset,
forward_distance,
reverse_distance,
forward_distance_offset,
reverse_distance_offset,
forward_duration,
reverse_duration,
forward_duration_offset,
+2 -2
View File
@@ -63,8 +63,8 @@ struct Hint
friend std::ostream &operator<<(std::ostream &, const Hint &);
};
static_assert(sizeof(Hint) == 64 + 4, "Hint is bigger than expected");
constexpr std::size_t ENCODED_HINT_SIZE = 92;
static_assert(sizeof(Hint) == 80 + 4, "Hint is bigger than expected");
constexpr std::size_t ENCODED_HINT_SIZE = 112;
static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint),
"ENCODED_HINT_SIZE does not match size of Hint");
}
+42 -3
View File
@@ -47,10 +47,13 @@ struct PhantomNode
: forward_segment_id{SPECIAL_SEGMENTID, false},
reverse_segment_id{SPECIAL_SEGMENTID, false}, forward_weight(INVALID_EDGE_WEIGHT),
reverse_weight(INVALID_EDGE_WEIGHT), forward_weight_offset(0), reverse_weight_offset(0),
forward_distance(INVALID_EDGE_DISTANCE), reverse_distance(INVALID_EDGE_DISTANCE),
forward_distance_offset(0), reverse_distance_offset(0),
forward_duration(MAXIMAL_EDGE_DURATION), reverse_duration(MAXIMAL_EDGE_DURATION),
forward_duration_offset(0), reverse_duration_offset(0), fwd_segment_position(0),
is_valid_forward_source{false}, is_valid_forward_target{false},
is_valid_reverse_source{false}, is_valid_reverse_target{false}, bearing(0)
{
}
@@ -78,6 +81,30 @@ struct PhantomNode
return reverse_duration + reverse_duration_offset;
}
// DO THIS FOR DISTANCE
EdgeDistance GetForwardDistance() const
{
// ..... <-- forward_distance
// .... <-- offset
// ......... <-- desired distance
// x <-- this is PhantomNode.location
// 0----1----2----3----4 <-- EdgeBasedGraph Node segments
BOOST_ASSERT(forward_segment_id.enabled);
return forward_distance + forward_distance_offset;
}
EdgeDistance GetReverseDistance() const
{
// .......... <-- reverse_distance
// ... <-- offset
// ............. <-- desired distance
// x <-- this is PhantomNode.location
// 0----1----2----3----4 <-- EdgeBasedGraph Node segments
BOOST_ASSERT(reverse_segment_id.enabled);
return reverse_distance + reverse_distance_offset;
}
bool IsBidirected() const { return forward_segment_id.enabled && reverse_segment_id.enabled; }
bool IsValid(const unsigned number_of_nodes) const
@@ -88,6 +115,8 @@ struct PhantomNode
(reverse_weight != INVALID_EDGE_WEIGHT)) &&
((forward_duration != MAXIMAL_EDGE_DURATION) ||
(reverse_duration != MAXIMAL_EDGE_DURATION)) &&
((forward_distance != INVALID_EDGE_DISTANCE) ||
(reverse_distance != INVALID_EDGE_DISTANCE)) &&
(component.id != INVALID_COMPONENTID);
}
@@ -130,6 +159,10 @@ struct PhantomNode
EdgeWeight reverse_weight,
EdgeWeight forward_weight_offset,
EdgeWeight reverse_weight_offset,
EdgeDistance forward_distance,
EdgeDistance reverse_distance,
EdgeDistance forward_distance_offset,
EdgeDistance reverse_distance_offset,
EdgeWeight forward_duration,
EdgeWeight reverse_duration,
EdgeWeight forward_duration_offset,
@@ -144,7 +177,9 @@ struct PhantomNode
: forward_segment_id{other.forward_segment_id},
reverse_segment_id{other.reverse_segment_id}, forward_weight{forward_weight},
reverse_weight{reverse_weight}, forward_weight_offset{forward_weight_offset},
reverse_weight_offset{reverse_weight_offset}, forward_duration{forward_duration},
reverse_weight_offset{reverse_weight_offset}, forward_distance{forward_distance},
reverse_distance{reverse_distance}, forward_distance_offset{forward_distance_offset},
reverse_distance_offset{reverse_distance_offset}, forward_duration{forward_duration},
reverse_duration{reverse_duration}, forward_duration_offset{forward_duration_offset},
reverse_duration_offset{reverse_duration_offset},
component{component.id, component.is_tiny}, location{location},
@@ -162,13 +197,17 @@ struct PhantomNode
EdgeWeight reverse_weight;
EdgeWeight forward_weight_offset; // TODO: try to remove -> requires path unpacking changes
EdgeWeight reverse_weight_offset; // TODO: try to remove -> requires path unpacking changes
EdgeDistance forward_distance;
EdgeDistance reverse_distance;
EdgeDistance forward_distance_offset; // TODO: try to remove -> requires path unpacking changes
EdgeDistance reverse_distance_offset; // TODO: try to remove -> requires path unpacking changes
EdgeWeight forward_duration;
EdgeWeight reverse_duration;
EdgeWeight forward_duration_offset; // TODO: try to remove -> requires path unpacking changes
EdgeWeight reverse_duration_offset; // TODO: try to remove -> requires path unpacking changes
ComponentID component;
util::Coordinate location;
util::Coordinate location; // this is the coordinate of x
util::Coordinate input_location;
unsigned short fwd_segment_position;
// is phantom node valid to be used as source or target
@@ -180,7 +219,7 @@ struct PhantomNode
unsigned short bearing : 12;
};
static_assert(sizeof(PhantomNode) == 64, "PhantomNode has more padding then expected");
static_assert(sizeof(PhantomNode) == 80, "PhantomNode has more padding then expected");
using PhantomNodePair = std::pair<PhantomNode, PhantomNode>;
+27 -10
View File
@@ -30,10 +30,12 @@ class RoutingAlgorithmsInterface
virtual InternalRouteResult
DirectShortestPathSearch(const PhantomNodes &phantom_node_pair) const = 0;
virtual std::vector<EdgeDuration>
virtual std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices) const = 0;
const std::vector<std::size_t> &target_indices,
const bool calculate_distance,
const bool calculate_duration) const = 0;
virtual routing_algorithms::SubMatchingList
MapMatching(const routing_algorithms::CandidateLists &candidates_list,
@@ -53,6 +55,7 @@ class RoutingAlgorithmsInterface
virtual bool HasDirectShortestPathSearch() const = 0;
virtual bool HasMapMatching() const = 0;
virtual bool HasManyToManySearch() const = 0;
virtual bool SupportsDistanceAnnotationType() const = 0;
virtual bool HasGetTileTurns() const = 0;
virtual bool HasExcludeFlags() const = 0;
virtual bool IsValid() const = 0;
@@ -81,10 +84,12 @@ template <typename Algorithm> class RoutingAlgorithms final : public RoutingAlgo
InternalRouteResult
DirectShortestPathSearch(const PhantomNodes &phantom_nodes) const final override;
std::vector<EdgeDuration>
virtual std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices) const final override;
const std::vector<std::size_t> &target_indices,
const bool calculate_distance,
const bool calculate_duration) const final override;
routing_algorithms::SubMatchingList
MapMatching(const routing_algorithms::CandidateLists &candidates_list,
@@ -124,6 +129,11 @@ template <typename Algorithm> class RoutingAlgorithms final : public RoutingAlgo
return routing_algorithms::HasManyToManySearch<Algorithm>::value;
}
bool SupportsDistanceAnnotationType() const final override
{
return routing_algorithms::SupportsDistanceAnnotationType<Algorithm>::value;
}
bool HasGetTileTurns() const final override
{
return routing_algorithms::HasGetTileTurns<Algorithm>::value;
@@ -184,10 +194,12 @@ inline routing_algorithms::SubMatchingList RoutingAlgorithms<Algorithm>::MapMatc
}
template <typename Algorithm>
std::vector<EdgeDuration> RoutingAlgorithms<Algorithm>::ManyToManySearch(
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &_source_indices,
const std::vector<std::size_t> &_target_indices) const
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
RoutingAlgorithms<Algorithm>::ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &_source_indices,
const std::vector<std::size_t> &_target_indices,
const bool calculate_distance,
const bool calculate_duration) const
{
BOOST_ASSERT(!phantom_nodes.empty());
@@ -205,8 +217,13 @@ std::vector<EdgeDuration> RoutingAlgorithms<Algorithm>::ManyToManySearch(
std::iota(target_indices.begin(), target_indices.end(), 0);
}
return routing_algorithms::manyToManySearch(
heaps, *facade, phantom_nodes, std::move(source_indices), std::move(target_indices));
return routing_algorithms::manyToManySearch(heaps,
*facade,
phantom_nodes,
std::move(source_indices),
std::move(target_indices),
calculate_distance,
calculate_duration);
}
template <typename Algorithm>
@@ -15,23 +15,43 @@ namespace engine
{
namespace routing_algorithms
{
namespace
{
struct NodeBucket
{
NodeID middle_node;
unsigned column_index; // a column in the weight/duration matrix
NodeID parent_node;
unsigned column_index : 31; // a column in the weight/duration matrix
unsigned from_clique_arc : 1;
EdgeWeight weight;
EdgeDuration duration;
NodeBucket(NodeID middle_node, unsigned column_index, EdgeWeight weight, EdgeDuration duration)
: middle_node(middle_node), column_index(column_index), weight(weight), duration(duration)
NodeBucket(NodeID middle_node,
NodeID parent_node,
bool from_clique_arc,
unsigned column_index,
EdgeWeight weight,
EdgeDuration duration)
: middle_node(middle_node), parent_node(parent_node), column_index(column_index),
from_clique_arc(from_clique_arc), weight(weight), duration(duration)
{
}
NodeBucket(NodeID middle_node,
NodeID parent_node,
unsigned column_index,
EdgeWeight weight,
EdgeDuration duration)
: middle_node(middle_node), parent_node(parent_node), column_index(column_index),
from_clique_arc(false), weight(weight), duration(duration)
{
}
// partial order comparison
bool operator<(const NodeBucket &rhs) const { return middle_node < rhs.middle_node; }
bool operator<(const NodeBucket &rhs) const
{
return std::tie(middle_node, column_index) < std::tie(rhs.middle_node, rhs.column_index);
}
// functor for equal_range
struct Compare
@@ -46,15 +66,36 @@ struct NodeBucket
return lhs < rhs.middle_node;
}
};
// functor for equal_range
struct ColumnCompare
{
unsigned column_idx;
ColumnCompare(unsigned column_idx) : column_idx(column_idx){};
bool operator()(const NodeBucket &lhs, const NodeID &rhs) const // lowerbound
{
return std::tie(lhs.middle_node, lhs.column_index) < std::tie(rhs, column_idx);
}
bool operator()(const NodeID &lhs, const NodeBucket &rhs) const // upperbound
{
return std::tie(lhs, column_idx) < std::tie(rhs.middle_node, rhs.column_index);
}
};
};
}
} // namespace
template <typename Algorithm>
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices);
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance,
const bool calculate_duration);
} // namespace routing_algorithms
} // namespace engine
@@ -181,6 +181,7 @@ void annotatePath(const FacadeT &facade,
BOOST_ASSERT(datasource_vector.size() > 0);
BOOST_ASSERT(weight_vector.size() + 1 == id_vector.size());
BOOST_ASSERT(duration_vector.size() + 1 == id_vector.size());
const bool is_first_segment = unpacked_path.empty();
const std::size_t start_index =
@@ -405,6 +406,22 @@ InternalRouteResult extractRoute(const DataFacade<AlgorithmT> &facade,
return raw_route_data;
}
template <typename FacadeT> EdgeDistance computeEdgeDistance(const FacadeT &facade, NodeID node_id)
{
const auto geometry_index = facade.GetGeometryIndex(node_id);
EdgeDistance total_distance = 0.0;
auto geometry_range = facade.GetUncompressedForwardGeometry(geometry_index.id);
for (auto current = geometry_range.begin(); current < geometry_range.end() - 1; ++current)
{
total_distance += util::coordinate_calculation::fccApproximateDistance(
facade.GetCoordinateOfNode(*current), facade.GetCoordinateOfNode(*std::next(current)));
}
return total_distance;
}
} // namespace routing_algorithms
} // namespace engine
} // namespace osrm
@@ -288,6 +288,106 @@ void unpackPath(const DataFacade<Algorithm> &facade,
}
}
template <typename BidirectionalIterator>
EdgeDistance calculateEBGNodeAnnotations(const DataFacade<Algorithm> &facade,
BidirectionalIterator packed_path_begin,
BidirectionalIterator packed_path_end)
{
// Make sure we have at least something to unpack
if (packed_path_begin == packed_path_end ||
std::distance(packed_path_begin, packed_path_end) <= 1)
return 0;
std::stack<std::tuple<NodeID, NodeID, bool>> recursion_stack;
std::stack<EdgeDistance> distance_stack;
// We have to push the path in reverse order onto the stack because it's LIFO.
for (auto current = std::prev(packed_path_end); current > packed_path_begin;
current = std::prev(current))
{
recursion_stack.emplace(*std::prev(current), *current, false);
}
std::tuple<NodeID, NodeID, bool> edge;
while (!recursion_stack.empty())
{
edge = recursion_stack.top();
recursion_stack.pop();
// Have we processed the edge before? tells us if we have values in the durations stack that
// we can add up
if (!std::get<2>(edge))
{ // haven't processed edge before, so process it in the body!
std::get<2>(edge) = true; // mark that this edge will now be processed
// Look for an edge on the forward CH graph (.forward)
EdgeID smaller_edge_id =
facade.FindSmallestEdge(std::get<0>(edge), std::get<1>(edge), [](const auto &data) {
return data.forward;
});
// If we didn't find one there, the we might be looking at a part of the path that
// was found using the backward search. Here, we flip the node order (.second,
// .first) and only consider edges with the `.backward` flag.
if (SPECIAL_EDGEID == smaller_edge_id)
{
smaller_edge_id =
facade.FindSmallestEdge(std::get<1>(edge),
std::get<0>(edge),
[](const auto &data) { return data.backward; });
}
// If we didn't find anything *still*, then something is broken and someone has
// called this function with bad values.
BOOST_ASSERT_MSG(smaller_edge_id != SPECIAL_EDGEID, "Invalid smaller edge ID");
const auto &data = facade.GetEdgeData(smaller_edge_id);
BOOST_ASSERT_MSG(data.weight != std::numeric_limits<EdgeWeight>::max(),
"edge weight invalid");
// If the edge is a shortcut, we need to add the two halfs to the stack.
if (data.shortcut)
{ // unpack
const NodeID middle_node_id = data.turn_id;
// Note the order here - we're adding these to a stack, so we
// want the first->middle to get visited before middle->second
recursion_stack.emplace(edge);
recursion_stack.emplace(middle_node_id, std::get<1>(edge), false);
recursion_stack.emplace(std::get<0>(edge), middle_node_id, false);
}
else
{
// compute the duration here and put it onto the duration stack using method
// similar to annotatePath but smaller
EdgeDistance distance = computeEdgeDistance(facade, std::get<0>(edge));
distance_stack.emplace(distance);
}
}
else
{ // the edge has already been processed. this means that there are enough values in the
// distances stack
BOOST_ASSERT_MSG(distance_stack.size() >= 2,
"There are not enough (at least 2) values on the distance stack");
EdgeDistance distance1 = distance_stack.top();
distance_stack.pop();
EdgeDistance distance2 = distance_stack.top();
distance_stack.pop();
EdgeDistance distance = distance1 + distance2;
distance_stack.emplace(distance);
}
}
EdgeDistance total_distance = 0;
while (!distance_stack.empty())
{
total_distance += distance_stack.top();
distance_stack.pop();
}
return total_distance;
}
template <typename RandomIter, typename FacadeT>
void unpackPath(const FacadeT &facade,
RandomIter packed_path_begin,
@@ -340,6 +440,11 @@ void retrievePackedPathFromSingleHeap(const SearchEngineData<Algorithm>::QueryHe
const NodeID middle_node_id,
std::vector<NodeID> &packed_path);
void retrievePackedPathFromSingleManyToManyHeap(
const SearchEngineData<Algorithm>::ManyToManyQueryHeap &search_heap,
const NodeID middle_node_id,
std::vector<NodeID> &packed_path);
// assumes that heaps are already setup correctly.
// ATTENTION: This only works if no additional offset is supplied next to the Phantom Node
// Offsets.
@@ -54,7 +54,7 @@ inline bool checkParentCellRestriction(CellID, const PhantomNodes &) { return tr
// Restricted search (Args is LevelID, CellID):
// * use the fixed level for queries
// * check if the node cell is the same as the specified parent onr
// * check if the node cell is the same as the specified parent
template <typename MultiLevelPartition>
inline LevelID getNodeQueryLevel(const MultiLevelPartition &, NodeID, LevelID level, CellID)
{
@@ -65,6 +65,61 @@ inline bool checkParentCellRestriction(CellID cell, LevelID, CellID parent)
{
return cell == parent;
}
// Unrestricted search with a single phantom node (Args is const PhantomNode &):
// * use partition.GetQueryLevel to find the node query level
// * allow to traverse all cells
template <typename MultiLevelPartition>
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
const NodeID node,
const PhantomNode &phantom_node)
{
auto highest_diffrent_level = [&partition, node](const SegmentID &phantom_node) {
if (phantom_node.enabled)
return partition.GetHighestDifferentLevel(phantom_node.id, node);
return INVALID_LEVEL_ID;
};
const auto node_level = std::min(highest_diffrent_level(phantom_node.forward_segment_id),
highest_diffrent_level(phantom_node.reverse_segment_id));
return node_level;
}
// Unrestricted search with a single phantom node and a vector of phantom nodes:
// * use partition.GetQueryLevel to find the node query level
// * allow to traverse all cells
template <typename MultiLevelPartition>
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
NodeID node,
const std::vector<PhantomNode> &phantom_nodes,
const std::size_t phantom_index,
const std::vector<std::size_t> &phantom_indices)
{
auto min_level = [&partition, node](const PhantomNode &phantom_node) {
const auto &forward_segment = phantom_node.forward_segment_id;
const auto forward_level =
forward_segment.enabled ? partition.GetHighestDifferentLevel(node, forward_segment.id)
: INVALID_LEVEL_ID;
const auto &reverse_segment = phantom_node.reverse_segment_id;
const auto reverse_level =
reverse_segment.enabled ? partition.GetHighestDifferentLevel(node, reverse_segment.id)
: INVALID_LEVEL_ID;
return std::min(forward_level, reverse_level);
};
// Get minimum level over all phantoms of the highest different level with respect to node
// This is equivalent to min_{∀ source, target} partition.GetQueryLevel(source, node, target)
auto result = min_level(phantom_nodes[phantom_index]);
for (const auto &index : phantom_indices)
{
result = std::min(result, min_level(phantom_nodes[index]));
}
return result;
}
}
// Heaps only record for each node its predecessor ("parent") on the shortest path.
@@ -74,6 +129,46 @@ inline bool checkParentCellRestriction(CellID cell, LevelID, CellID parent)
using PackedEdge = std::tuple</*from*/ NodeID, /*to*/ NodeID, /*from_clique_arc*/ bool>;
using PackedPath = std::vector<PackedEdge>;
template <bool DIRECTION, typename OutIter>
inline void retrievePackedPathFromSingleManyToManyHeap(
const SearchEngineData<Algorithm>::ManyToManyQueryHeap &heap, const NodeID middle, OutIter out)
{
NodeID current = middle;
NodeID parent = heap.GetData(current).parent;
while (current != parent)
{
const auto &data = heap.GetData(current);
if (DIRECTION == FORWARD_DIRECTION)
{
*out = std::make_tuple(parent, current, data.from_clique_arc);
++out;
}
else if (DIRECTION == REVERSE_DIRECTION)
{
*out = std::make_tuple(current, parent, data.from_clique_arc);
++out;
}
current = parent;
parent = heap.GetData(parent).parent;
}
}
template <bool DIRECTION>
inline PackedPath retrievePackedPathFromSingleManyToManyHeap(
const SearchEngineData<Algorithm>::ManyToManyQueryHeap &heap, const NodeID middle)
{
PackedPath packed_path;
retrievePackedPathFromSingleManyToManyHeap<DIRECTION>(
heap, middle, std::back_inserter(packed_path));
return packed_path;
}
template <bool DIRECTION, typename OutIter>
inline void retrievePackedPathFromSingleHeap(const SearchEngineData<Algorithm>::QueryHeap &heap,
const NodeID middle,
@@ -206,15 +301,22 @@ void relaxOutgoingEdges(const DataFacade<Algorithm> &facade,
for (const auto edge : facade.GetBorderEdgeRange(level, node))
{
const auto &edge_data = facade.GetEdgeData(edge);
if (DIRECTION == FORWARD_DIRECTION ? edge_data.forward : edge_data.backward)
if ((DIRECTION == FORWARD_DIRECTION) ? facade.IsForwardEdge(edge)
: facade.IsBackwardEdge(edge))
{
const NodeID to = facade.GetTarget(edge);
if (!facade.ExcludeNode(to) &&
checkParentCellRestriction(partition.GetCell(level + 1, to), args...))
{
BOOST_ASSERT_MSG(edge_data.weight > 0, "edge_weight invalid");
const EdgeWeight to_weight = weight + edge_data.weight;
const auto node_weight =
facade.GetNodeWeight(DIRECTION == FORWARD_DIRECTION ? node : to);
const auto turn_penalty = facade.GetWeightPenaltyForEdgeID(edge_data.turn_id);
// TODO: BOOST_ASSERT(edge_data.weight == node_weight + turn_penalty);
const EdgeWeight to_weight = weight + node_weight + turn_penalty;
if (!forward_heap.WasInserted(to))
{
@@ -407,6 +509,90 @@ UnpackedPath search(SearchEngineData<Algorithm> &engine_working_data,
return std::make_tuple(weight, std::move(unpacked_nodes), std::move(unpacked_edges));
}
// With (s, middle, t) we trace back the paths middle -> s and middle -> t.
// This gives us a packed path (node ids) from the base graph around s and t,
// and overlay node ids otherwise. We then have to unpack the overlay clique
// edges by recursively descending unpacking the path down to the base graph.
using UnpackedNodes = std::vector<NodeID>;
using UnpackedEdges = std::vector<EdgeID>;
using UnpackedPath = std::tuple<EdgeWeight, UnpackedNodes, UnpackedEdges>;
template <typename Algorithm, typename... Args>
UnpackedPath
unpackPathAndCalculateDistance(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
typename SearchEngineData<Algorithm>::QueryHeap &forward_heap,
typename SearchEngineData<Algorithm>::QueryHeap &reverse_heap,
const bool force_loop_forward,
const bool force_loop_reverse,
EdgeWeight weight_upper_bound,
PackedPath packed_path,
NodeID middle,
Args... args)
{
EdgeWeight weight = weight_upper_bound;
const auto &partition = facade.GetMultiLevelPartition();
const NodeID source_node = !packed_path.empty() ? std::get<0>(packed_path.front()) : middle;
// Unpack path
std::vector<NodeID> unpacked_nodes;
std::vector<EdgeID> unpacked_edges;
unpacked_nodes.reserve(packed_path.size());
unpacked_edges.reserve(packed_path.size());
unpacked_nodes.push_back(source_node);
for (auto const &packed_edge : packed_path)
{
NodeID source, target;
bool overlay_edge;
std::tie(source, target, overlay_edge) = packed_edge;
if (!overlay_edge)
{ // a base graph edge
unpacked_nodes.push_back(target);
unpacked_edges.push_back(facade.FindEdge(source, target));
}
else
{ // an overlay graph edge
LevelID level = getNodeQueryLevel(partition, source, args...);
CellID parent_cell_id = partition.GetCell(level, source);
BOOST_ASSERT(parent_cell_id == partition.GetCell(level, target));
LevelID sublevel = level - 1;
// Here heaps can be reused, let's go deeper!
forward_heap.Clear();
reverse_heap.Clear();
forward_heap.Insert(source, 0, {source});
reverse_heap.Insert(target, 0, {target});
// TODO: when structured bindings will be allowed change to
// auto [subpath_weight, subpath_source, subpath_target, subpath] = ...
EdgeWeight subpath_weight;
std::vector<NodeID> subpath_nodes;
std::vector<EdgeID> subpath_edges;
std::tie(subpath_weight, subpath_nodes, subpath_edges) = search(engine_working_data,
facade,
forward_heap,
reverse_heap,
force_loop_forward,
force_loop_reverse,
weight_upper_bound,
sublevel,
parent_cell_id);
BOOST_ASSERT(!subpath_edges.empty());
BOOST_ASSERT(subpath_nodes.size() > 1);
BOOST_ASSERT(subpath_nodes.front() == source);
BOOST_ASSERT(subpath_nodes.back() == target);
unpacked_nodes.insert(
unpacked_nodes.end(), std::next(subpath_nodes.begin()), subpath_nodes.end());
unpacked_edges.insert(unpacked_edges.end(), subpath_edges.begin(), subpath_edges.end());
}
}
return std::make_tuple(weight, std::move(unpacked_nodes), std::move(unpacked_edges));
}
// Alias to be compatible with the CH-based search
template <typename Algorithm>
inline void search(SearchEngineData<Algorithm> &engine_working_data,
@@ -91,6 +91,7 @@ class EdgeBasedGraphFactory
void GetEdgeBasedNodeSegments(std::vector<EdgeBasedNodeSegment> &nodes);
void GetStartPointMarkers(std::vector<bool> &node_is_startpoint);
void GetEdgeBasedNodeWeights(std::vector<EdgeWeight> &output_node_weights);
void GetEdgeBasedNodeDurations(std::vector<EdgeWeight> &output_node_durations);
std::uint32_t GetConnectivityChecksum() const;
std::uint64_t GetNumberOfEdgeBasedNodes() const;
@@ -117,6 +118,7 @@ class EdgeBasedGraphFactory
//! node weights that indicate the length of the segment (node based) represented by the
//! edge-based node
std::vector<EdgeWeight> m_edge_based_node_weights;
std::vector<EdgeDuration> m_edge_based_node_durations;
//! list of edge based nodes (compressed segments)
std::vector<EdgeBasedNodeSegment> m_edge_based_node_segments;
+1
View File
@@ -87,6 +87,7 @@ class Extractor
std::vector<EdgeBasedNodeSegment> &edge_based_node_segments,
std::vector<bool> &node_is_startpoint,
std::vector<EdgeWeight> &edge_based_node_weights,
std::vector<EdgeDuration> &edge_based_node_durations,
util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
std::uint32_t &connectivity_checksum);
+17 -3
View File
@@ -462,14 +462,28 @@ void readEdgeBasedNodeWeights(const boost::filesystem::path &path, NodeWeigtsVec
storage::serialization::read(reader, "/extractor/edge_based_node_weights", weights);
}
template <typename NodeWeigtsVectorT>
void writeEdgeBasedNodeWeights(const boost::filesystem::path &path,
const NodeWeigtsVectorT &weights)
template <typename NodeWeigtsVectorT, typename NodeDurationsVectorT>
void readEdgeBasedNodeWeightsDurations(const boost::filesystem::path &path,
NodeWeigtsVectorT &weights,
NodeDurationsVectorT &durations)
{
const auto fingerprint = storage::tar::FileReader::VerifyFingerprint;
storage::tar::FileReader reader{path, fingerprint};
storage::serialization::read(reader, "/extractor/edge_based_node_weights", weights);
storage::serialization::read(reader, "/extractor/edge_based_node_durations", durations);
}
template <typename NodeWeigtsVectorT, typename NodeDurationsVectorT>
void writeEdgeBasedNodeWeightsDurations(const boost::filesystem::path &path,
const NodeWeigtsVectorT &weights,
const NodeDurationsVectorT &durations)
{
const auto fingerprint = storage::tar::FileWriter::GenerateFingerprint;
storage::tar::FileWriter writer{path, fingerprint};
storage::serialization::write(writer, "/extractor/edge_based_node_weights", weights);
storage::serialization::write(writer, "/extractor/edge_based_node_durations", durations);
}
template <typename RTreeT>
+40
View File
@@ -1064,6 +1064,46 @@ argumentsToTableParameter(const Nan::FunctionCallbackInfo<v8::Value> &args,
}
}
if (obj->Has(Nan::New("annotations").ToLocalChecked()))
{
v8::Local<v8::Value> annotations = obj->Get(Nan::New("annotations").ToLocalChecked());
if (annotations.IsEmpty())
return table_parameters_ptr();
if (!annotations->IsArray())
{
Nan::ThrowError(
"Annotations must an array containing 'duration' or 'distance', or both");
return table_parameters_ptr();
}
params->annotations = osrm::TableParameters::AnnotationsType::None;
v8::Local<v8::Array> annotations_array = v8::Local<v8::Array>::Cast(annotations);
for (std::size_t i = 0; i < annotations_array->Length(); ++i)
{
const Nan::Utf8String annotations_utf8str(annotations_array->Get(i));
std::string annotations_str{*annotations_utf8str,
*annotations_utf8str + annotations_utf8str.length()};
if (annotations_str == "duration")
{
params->annotations =
params->annotations | osrm::TableParameters::AnnotationsType::Duration;
}
else if (annotations_str == "distance")
{
params->annotations =
params->annotations | osrm::TableParameters::AnnotationsType::Distance;
}
else
{
Nan::ThrowError("this 'annotations' param is not supported");
return table_parameters_ptr();
}
}
}
return params;
}
+29 -35
View File
@@ -1,8 +1,6 @@
#ifndef OSRM_PARTITIONER_SERILIZATION_HPP
#define OSRM_PARTITIONER_SERILIZATION_HPP
#include "customizer/edge_based_graph.hpp"
#include "partitioner/serialization.hpp"
#include "storage/io.hpp"
@@ -14,39 +12,6 @@ namespace partitioner
namespace files
{
// reads .osrm.mldgr file
template <typename MultiLevelGraphT>
inline void readGraph(const boost::filesystem::path &path,
MultiLevelGraphT &graph,
std::uint32_t &connectivity_checksum)
{
static_assert(std::is_same<customizer::MultiLevelEdgeBasedGraphView, MultiLevelGraphT>::value ||
std::is_same<customizer::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value,
"");
storage::tar::FileReader reader{path, storage::tar::FileReader::VerifyFingerprint};
reader.ReadInto("/mld/connectivity_checksum", connectivity_checksum);
serialization::read(reader, "/mld/multilevelgraph", graph);
}
// writes .osrm.mldgr file
template <typename MultiLevelGraphT>
inline void writeGraph(const boost::filesystem::path &path,
const MultiLevelGraphT &graph,
const std::uint32_t connectivity_checksum)
{
static_assert(std::is_same<customizer::MultiLevelEdgeBasedGraphView, MultiLevelGraphT>::value ||
std::is_same<customizer::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value,
"");
storage::tar::FileWriter writer{path, storage::tar::FileWriter::GenerateFingerprint};
writer.WriteElementCount64("/mld/connectivity_checksum", 1);
writer.WriteFrom("/mld/connectivity_checksum", connectivity_checksum);
serialization::write(writer, "/mld/multilevelgraph", graph);
}
// read .osrm.partition file
template <typename MultiLevelPartitionT>
inline void readPartition(const boost::filesystem::path &path, MultiLevelPartitionT &mlp)
@@ -102,6 +67,35 @@ inline void writeCells(const boost::filesystem::path &path, CellStorageT &storag
serialization::write(writer, "/mld/cellstorage", storage);
}
// reads .osrm.mldgr file
template <typename MultiLevelGraphT>
inline void readGraph(const boost::filesystem::path &path,
MultiLevelGraphT &graph,
std::uint32_t &connectivity_checksum)
{
static_assert(std::is_same<partitioner::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value, "");
storage::tar::FileReader reader{path, storage::tar::FileReader::VerifyFingerprint};
reader.ReadInto("/mld/connectivity_checksum", connectivity_checksum);
serialization::read(reader, "/mld/multilevelgraph", graph);
}
// writes .osrm.mldgr file
template <typename MultiLevelGraphT>
inline void writeGraph(const boost::filesystem::path &path,
const MultiLevelGraphT &graph,
const std::uint32_t connectivity_checksum)
{
static_assert(std::is_same<partitioner::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value, "");
storage::tar::FileWriter writer{path, storage::tar::FileWriter::GenerateFingerprint};
writer.WriteElementCount64("/mld/connectivity_checksum", 1);
writer.WriteFrom("/mld/connectivity_checksum", connectivity_checksum);
serialization::write(writer, "/mld/multilevelgraph", graph);
}
}
}
}
+15
View File
@@ -1,6 +1,7 @@
#ifndef OSRM_PARTITIONER_MULTI_LEVEL_GRAPH_HPP
#define OSRM_PARTITIONER_MULTI_LEVEL_GRAPH_HPP
#include "partitioner/edge_based_graph.hpp"
#include "partitioner/multi_level_partition.hpp"
#include "storage/shared_memory_ownership.hpp"
@@ -42,6 +43,8 @@ class MultiLevelGraph : public util::StaticGraph<EdgeDataT, Ownership>
template <typename T> using Vector = util::ViewOrVector<T, Ownership>;
public:
using SuperT::SuperT;
// We limit each node to have 255 edges
// this is very generous, we could probably pack this
using EdgeOffset = std::uint8_t;
@@ -146,6 +149,14 @@ class MultiLevelGraph : public util::StaticGraph<EdgeDataT, Ownership>
return max_border_node_id;
}
auto data() && // rvalue ref-qualifier is a safety-belt
{
return std::make_tuple(std::move(SuperT::node_array),
std::move(SuperT::edge_array),
std::move(node_to_edge_offset),
connectivity_checksum);
}
private:
template <typename ContainerT>
auto GetHighestBorderLevel(const MultiLevelPartition &mlp, const ContainerT &edges) const
@@ -218,9 +229,13 @@ class MultiLevelGraph : public util::StaticGraph<EdgeDataT, Ownership>
const std::string &name,
const MultiLevelGraph<EdgeDataT, Ownership> &graph);
protected:
Vector<EdgeOffset> node_to_edge_offset;
std::uint32_t connectivity_checksum;
};
using MultiLevelEdgeBasedGraph =
MultiLevelGraph<EdgeBasedGraphEdgeData, storage::Ownership::Container>;
}
}
+1 -1
View File
@@ -16,7 +16,7 @@ namespace partitioner
struct PartitionerConfig final : storage::IOConfig
{
PartitionerConfig()
: IOConfig({".osrm", ".osrm.fileIndex", ".osrm.ebg_nodes"},
: IOConfig({".osrm", ".osrm.fileIndex", ".osrm.ebg_nodes", ".osrm.enw"},
{".osrm.hsgr", ".osrm.cnbg"},
{".osrm.ebg",
".osrm.cnbg",
-20
View File
@@ -19,26 +19,6 @@ namespace partitioner
namespace serialization
{
template <typename EdgeDataT, storage::Ownership Ownership>
inline void read(storage::tar::FileReader &reader,
const std::string &name,
MultiLevelGraph<EdgeDataT, Ownership> &graph)
{
storage::serialization::read(reader, name + "/node_array", graph.node_array);
storage::serialization::read(reader, name + "/edge_array", graph.edge_array);
storage::serialization::read(reader, name + "/node_to_edge_offset", graph.node_to_edge_offset);
}
template <typename EdgeDataT, storage::Ownership Ownership>
inline void write(storage::tar::FileWriter &writer,
const std::string &name,
const MultiLevelGraph<EdgeDataT, Ownership> &graph)
{
storage::serialization::write(writer, name + "/node_array", graph.node_array);
storage::serialization::write(writer, name + "/edge_array", graph.edge_array);
storage::serialization::write(writer, name + "/node_to_edge_offset", graph.node_to_edge_offset);
}
template <storage::Ownership Ownership>
inline void read(storage::tar::FileReader &reader,
const std::string &name,
+23 -3
View File
@@ -22,11 +22,11 @@ namespace qi = boost::spirit::qi;
template <typename Iterator = std::string::iterator,
typename Signature = void(engine::api::TableParameters &)>
struct TableParametersGrammar final : public BaseParametersGrammar<Iterator, Signature>
struct TableParametersGrammar : public BaseParametersGrammar<Iterator, Signature>
{
using BaseGrammar = BaseParametersGrammar<Iterator, Signature>;
TableParametersGrammar() : BaseGrammar(root_rule)
TableParametersGrammar() : TableParametersGrammar(root_rule)
{
#ifdef BOOST_HAS_LONG_LONG
if (std::is_same<std::size_t, unsigned long long>::value)
@@ -51,15 +51,35 @@ struct TableParametersGrammar final : public BaseParametersGrammar<Iterator, Sig
table_rule = destinations_rule(qi::_r1) | sources_rule(qi::_r1);
root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") >
-('?' > (table_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1)) % '&');
-('?' > (table_rule(qi::_r1) | base_rule(qi::_r1)) % '&');
}
TableParametersGrammar(qi::rule<Iterator, Signature> &root_rule_) : BaseGrammar(root_rule_)
{
using AnnotationsType = engine::api::TableParameters::AnnotationsType;
annotations.add("duration", AnnotationsType::Duration)("distance",
AnnotationsType::Distance);
annotations_list = annotations[qi::_val |= qi::_1] % ',';
base_rule = BaseGrammar::base_rule(qi::_r1) |
(qi::lit("annotations=") >
annotations_list[ph::bind(&engine::api::TableParameters::annotations,
qi::_r1) = qi::_1]);
}
protected:
qi::rule<Iterator, Signature> base_rule;
private:
qi::rule<Iterator, Signature> root_rule;
qi::rule<Iterator, Signature> table_rule;
qi::rule<Iterator, Signature> sources_rule;
qi::rule<Iterator, Signature> destinations_rule;
qi::rule<Iterator, std::size_t()> size_t_;
qi::symbols<char, engine::api::TableParameters::AnnotationsType> annotations;
qi::rule<Iterator, engine::api::TableParameters::AnnotationsType()> annotations_list;
};
}
}
+14 -8
View File
@@ -46,33 +46,39 @@ class SharedDataIndex
template <typename T> auto GetBlockPtr(const std::string &name) const
{
const auto index_iter = block_to_region.find(name);
const auto &region = regions[index_iter->second];
const auto &region = GetBlockRegion(name);
return region.layout.GetBlockPtr<T>(region.memory_ptr, name);
}
template <typename T> auto GetBlockPtr(const std::string &name)
{
const auto index_iter = block_to_region.find(name);
const auto &region = regions[index_iter->second];
const auto &region = GetBlockRegion(name);
return region.layout.GetBlockPtr<T>(region.memory_ptr, name);
}
std::size_t GetBlockEntries(const std::string &name) const
{
const auto index_iter = block_to_region.find(name);
const auto &region = regions[index_iter->second];
const auto &region = GetBlockRegion(name);
return region.layout.GetBlockEntries(name);
}
std::size_t GetBlockSize(const std::string &name) const
{
const auto index_iter = block_to_region.find(name);
const auto &region = regions[index_iter->second];
const auto &region = GetBlockRegion(name);
return region.layout.GetBlockSize(name);
}
private:
const AllocatedRegion &GetBlockRegion(const std::string &name) const
{
const auto index_iter = block_to_region.find(name);
if (index_iter == block_to_region.end())
{
throw util::exception("data block " + name + " not found " + SOURCE_REF);
}
return regions[index_iter->second];
}
std::vector<AllocatedRegion> regions;
std::unordered_map<std::string, std::uint32_t> block_to_region;
};
+13
View File
@@ -117,6 +117,19 @@ template <typename Data> struct SharedMonitor
#endif
static void remove() { bi::shared_memory_object::remove(Data::name); }
static bool exists()
{
try
{
bi::shared_memory_object shmem_open =
bi::shared_memory_object(bi::open_only, Data::name, bi::read_only);
}
catch (const bi::interprocess_exception &exception)
{
return false;
}
return true;
}
private:
#if USE_BOOST_INTERPROCESS_CONDITION
+2 -4
View File
@@ -29,10 +29,8 @@ checkMTarError(int error_code, const boost::filesystem::path &filepath, const st
case MTAR_ESUCCESS:
return;
case MTAR_EFAILURE:
throw util::RuntimeError(filepath.string() + " : " + name,
ErrorCode::FileIOError,
SOURCE_REF,
std::strerror(errno));
throw util::RuntimeError(
filepath.string() + " : " + name, ErrorCode::FileIOError, SOURCE_REF);
case MTAR_EOPENFAIL:
throw util::RuntimeError(filepath.string() + " : " + name,
ErrorCode::FileOpenError,
+11 -2
View File
@@ -330,9 +330,18 @@ inline auto make_multi_level_graph_view(const SharedDataIndex &index, const std:
index, name + "/edge_array");
auto node_to_offset = make_vector_view<customizer::MultiLevelEdgeBasedGraphView::EdgeOffset>(
index, name + "/node_to_edge_offset");
auto node_weights = make_vector_view<EdgeWeight>(index, name + "/node_weights");
auto node_durations = make_vector_view<EdgeDuration>(index, name + "/node_durations");
auto is_forward_edge = make_vector_view<bool>(index, name + "/is_forward_edge");
auto is_backward_edge = make_vector_view<bool>(index, name + "/is_backward_edge");
return customizer::MultiLevelEdgeBasedGraphView(
std::move(node_list), std::move(edge_list), std::move(node_to_offset));
return customizer::MultiLevelEdgeBasedGraphView(std::move(node_list),
std::move(edge_list),
std::move(node_to_offset),
std::move(node_weights),
std::move(node_durations),
std::move(is_forward_edge),
std::move(is_backward_edge));
}
inline auto make_maneuver_overrides_views(const SharedDataIndex &index, const std::string &name)
+5 -3
View File
@@ -17,13 +17,15 @@ class Updater
public:
Updater(UpdaterConfig config_) : config(std::move(config_)) {}
using NumNodesAndEdges =
std::tuple<EdgeID, std::vector<extractor::EdgeBasedEdge>, std::uint32_t>;
NumNodesAndEdges LoadAndUpdateEdgeExpandedGraph() const;
EdgeID
LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
std::vector<EdgeWeight> &node_weights,
std::uint32_t &connectivity_checksum) const;
EdgeID
LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
std::vector<EdgeWeight> &node_weights,
std::vector<EdgeDuration> &node_durations, // TODO: to be deleted
std::uint32_t &connectivity_checksum) const;
private:
+2 -1
View File
@@ -53,7 +53,8 @@ struct UpdaterConfig final : storage::IOConfig
".osrm.geometry",
".osrm.fileIndex",
".osrm.properties",
".osrm.restrictions"},
".osrm.restrictions",
".osrm.enw"},
{},
{".osrm.datasource_names"}),
valid_now(0)
+3
View File
@@ -43,6 +43,9 @@ inline double radToDeg(const double radian)
//! Takes the squared euclidean distance of the input coordinates. Does not return meters!
std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rhs);
double fccApproximateDistance(const Coordinate first_coordinate,
const Coordinate second_coordinate);
double haversineDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
double greatCircleDistance(const Coordinate first_coordinate, const Coordinate second_coordinate);
+1 -1
View File
@@ -22,7 +22,7 @@ namespace util
inline std::ostream &operator<<(std::ostream &out, const Coordinate &coordinate)
{
out << std::setprecision(12) << "{" << toFloating(coordinate.lon) << ", "
<< toFloating(coordinate.lon) << "}";
<< toFloating(coordinate.lat) << "}";
return out;
}
}
+8 -1
View File
@@ -2,6 +2,8 @@
#define DYNAMICGRAPH_HPP
#include "util/deallocating_vector.hpp"
#include "util/exception.hpp"
#include "util/exception_utils.hpp"
#include "util/integer_range.hpp"
#include "util/permutation.hpp"
#include "util/typedefs.hpp"
@@ -411,7 +413,12 @@ template <typename EdgeDataT> class DynamicGraph
util::inplacePermutation(node_array.begin(), node_array.end(), old_to_new_node);
// Build up edge permutation
auto new_edge_index = 0;
if (edge_list.size() >= std::numeric_limits<EdgeID>::max())
{
throw util::exception("There are too many edges, OSRM only supports 2^32" + SOURCE_REF);
}
EdgeID new_edge_index = 0;
std::vector<EdgeID> old_to_new_edge(edge_list.size(), SPECIAL_EDGEID);
for (auto node : util::irange<NodeID>(0, number_of_nodes))
{
+2
View File
@@ -226,12 +226,14 @@ class QueryHeap
Data &GetData(NodeID node)
{
const auto index = node_index.peek_index(node);
BOOST_ASSERT((int)index >= 0 && (int)index < (int)inserted_nodes.size());
return inserted_nodes[index].data;
}
Data const &GetData(NodeID node) const
{
const auto index = node_index.peek_index(node);
BOOST_ASSERT((int)index >= 0 && (int)index < (int)inserted_nodes.size());
return inserted_nodes[index].data;
}
+1 -1
View File
@@ -312,7 +312,7 @@ class StaticGraph
});
}
// private:
protected:
NodeIterator number_of_nodes;
EdgeIterator number_of_edges;
+2
View File
@@ -75,6 +75,7 @@ using NameID = std::uint32_t;
using AnnotationID = std::uint32_t;
using EdgeWeight = std::int32_t;
using EdgeDuration = std::int32_t;
using EdgeDistance = float;
using SegmentWeight = std::uint32_t;
using SegmentDuration = std::uint32_t;
using TurnPenalty = std::int16_t; // turn penalty in 100ms units
@@ -113,6 +114,7 @@ static const SegmentDuration MAX_SEGMENT_DURATION = INVALID_SEGMENT_DURATION - 1
static const EdgeWeight INVALID_EDGE_WEIGHT = std::numeric_limits<EdgeWeight>::max();
static const EdgeDuration MAXIMAL_EDGE_DURATION = std::numeric_limits<EdgeDuration>::max();
static const TurnPenalty INVALID_TURN_PENALTY = std::numeric_limits<TurnPenalty>::max();
static const EdgeDistance INVALID_EDGE_DISTANCE = std::numeric_limits<EdgeDistance>::max();
// FIXME the bitfields we use require a reduced maximal duration, this should be kept consistent
// within the code base. For now we have to ensure that we don't case 30 bit to -1 and break any
-25
View File
@@ -9,31 +9,6 @@ namespace util
{
namespace vector_tile
{
const constexpr std::uint32_t ID_TAG = 1;
const constexpr std::uint32_t NAME_TAG = 1;
const constexpr std::uint32_t FEATURE_TAG = 2;
const constexpr std::uint32_t LAYER_TAG = 3;
const constexpr std::uint32_t GEOMETRY_TAG = 3;
const constexpr std::uint32_t KEY_TAG = 3;
const constexpr std::uint32_t VARIANT_TAG = 4;
const constexpr std::uint32_t EXTENT_TAG = 5;
const constexpr std::uint32_t VERSION_TAG = 15;
const constexpr std::uint32_t FEATURE_ATTRIBUTES_TAG = 2;
const constexpr std::uint32_t FEATURE_GEOMETRIES_TAG = 4;
const constexpr std::uint32_t GEOMETRY_TYPE_POINT = 1;
const constexpr std::uint32_t GEOMETRY_TYPE_LINE = 2;
const constexpr std::uint32_t VARIANT_TYPE_STRING = 1;
const constexpr std::uint32_t VARIANT_TYPE_FLOAT = 2;
const constexpr std::uint32_t VARIANT_TYPE_DOUBLE = 3;
const constexpr std::uint32_t VARIANT_TYPE_UINT64 = 5;
const constexpr std::uint32_t VARIANT_TYPE_SINT64 = 6;
const constexpr std::uint32_t VARIANT_TYPE_BOOL = 7;
// Vector tiles are 4096 virtual pixels on each side
const constexpr double EXTENT = 4096.0;
const constexpr double BUFFER = 128.0;
+7 -1
View File
@@ -1,6 +1,6 @@
{
"name": "osrm",
"version": "5.17.0-latest.1",
"version": "5.18.0",
"private": false,
"description": "The Open Source Routing Machine is a high performance routing engine written in C++14 designed to run on OpenStreetMap data.",
"dependencies": {
@@ -38,19 +38,25 @@
"node": ">=4.0.0"
},
"devDependencies": {
"ansi-escape-sequences": "^4.0.0",
"aws-sdk": "~2.0.31",
"babel-plugin-transform-class-properties": "^6.24.1",
"chalk": "^1.1.3",
"command-line-args": "^5.0.2",
"command-line-usage": "^5.0.4",
"csv-stringify": "^3.0.0",
"cucumber": "^1.2.1",
"d3-queue": "^2.0.3",
"docbox": "^1.0.6",
"documentation": "^4.0.0-rc.1",
"eslint": "^2.4.0",
"faucet": "^0.0.1",
"jsonpath": "^1.0.0",
"node-timeout": "0.0.4",
"polyline": "^0.2.0",
"request": "^2.69.0",
"tape": "^4.7.0",
"turf": "^3.0.14",
"xmlbuilder": "^4.2.1"
},
"bundleDependencies": [
+237 -146
View File
@@ -109,11 +109,11 @@ function setup()
-- reduce the driving speed by 30% for unsafe roads
-- only used for cyclability metric
unsafe_highway_list = {
primary = 0.7,
secondary = 0.75,
primary = 0.5,
secondary = 0.65,
tertiary = 0.8,
primary_link = 0.7,
secondary_link = 0.75,
primary_link = 0.5,
secondary_link = 0.65,
tertiary_link = 0.8,
},
@@ -193,6 +193,16 @@ function setup()
sett = 10
},
classes = Sequence {
'ferry', 'tunnel'
},
-- Which classes should be excludable
-- This increases memory usage so its disabled by default.
excludable = Sequence {
-- Set {'ferry'}
},
tracktype_speeds = {
},
@@ -250,204 +260,237 @@ end
function handle_bicycle_tags(profile,way,result,data)
-- initial routability check, filters out buildings, boundaries, etc
local route = way:get_value_by_key("route")
local man_made = way:get_value_by_key("man_made")
local railway = way:get_value_by_key("railway")
local amenity = way:get_value_by_key("amenity")
local public_transport = way:get_value_by_key("public_transport")
local bridge = way:get_value_by_key("bridge")
data.route = way:get_value_by_key("route")
data.man_made = way:get_value_by_key("man_made")
data.railway = way:get_value_by_key("railway")
data.amenity = way:get_value_by_key("amenity")
data.public_transport = way:get_value_by_key("public_transport")
data.bridge = way:get_value_by_key("bridge")
if (not data.highway or data.highway == '') and
(not route or route == '') and
(not profile.use_public_transport or not railway or railway=='') and
(not amenity or amenity=='') and
(not man_made or man_made=='') and
(not public_transport or public_transport=='') and
(not bridge or bridge=='')
(not data.route or data.route == '') and
(not profile.use_public_transport or not data.railway or data.railway=='') and
(not data.amenity or data.amenity=='') and
(not data.man_made or data.man_made=='') and
(not data.public_transport or data.public_transport=='') and
(not data.bridge or data.bridge=='')
then
return false
end
-- access
local access = find_access_tag(way, profile.access_tags_hierarchy)
if access and profile.access_tag_blacklist[access] then
data.access = find_access_tag(way, profile.access_tags_hierarchy)
if data.access and profile.access_tag_blacklist[data.access] then
return false
end
-- other tags
local junction = way:get_value_by_key("junction")
local maxspeed = parse_maxspeed(way:get_value_by_key ( "maxspeed") )
local maxspeed_forward = parse_maxspeed(way:get_value_by_key( "maxspeed:forward"))
local maxspeed_backward = parse_maxspeed(way:get_value_by_key( "maxspeed:backward"))
local barrier = way:get_value_by_key("barrier")
local oneway = way:get_value_by_key("oneway")
local oneway_bicycle = way:get_value_by_key("oneway:bicycle")
local cycleway = way:get_value_by_key("cycleway")
local cycleway_left = way:get_value_by_key("cycleway:left")
local cycleway_right = way:get_value_by_key("cycleway:right")
local duration = way:get_value_by_key("duration")
local service = way:get_value_by_key("service")
local foot = way:get_value_by_key("foot")
local foot_forward = way:get_value_by_key("foot:forward")
local foot_backward = way:get_value_by_key("foot:backward")
local bicycle = way:get_value_by_key("bicycle")
data.junction = way:get_value_by_key("junction")
data.maxspeed = parse_maxspeed(way:get_value_by_key ( "maxspeed") )
data.maxspeed_forward = parse_maxspeed(way:get_value_by_key( "maxspeed:forward"))
data.maxspeed_backward = parse_maxspeed(way:get_value_by_key( "maxspeed:backward"))
data.barrier = way:get_value_by_key("barrier")
data.oneway = way:get_value_by_key("oneway")
data.oneway_bicycle = way:get_value_by_key("oneway:bicycle")
data.cycleway = way:get_value_by_key("cycleway")
data.cycleway_left = way:get_value_by_key("cycleway:left")
data.cycleway_right = way:get_value_by_key("cycleway:right")
data.duration = way:get_value_by_key("duration")
data.service = way:get_value_by_key("service")
data.foot = way:get_value_by_key("foot")
data.foot_forward = way:get_value_by_key("foot:forward")
data.foot_backward = way:get_value_by_key("foot:backward")
data.bicycle = way:get_value_by_key("bicycle")
speed_handler(profile,way,result,data)
oneway_handler(profile,way,result,data)
cycleway_handler(profile,way,result,data)
bike_push_handler(profile,way,result,data)
local way_type_allows_pushing = false
-- maxspeed
limit( result, data.maxspeed, data.maxspeed_forward, data.maxspeed_backward )
-- not routable if no speed assigned
-- this avoid assertions in debug builds
if result.forward_speed <= 0 and result.duration <= 0 then
result.forward_mode = mode.inaccessible
end
if result.backward_speed <= 0 and result.duration <= 0 then
result.backward_mode = mode.inaccessible
end
safety_handler(profile,way,result,data)
end
function speed_handler(profile,way,result,data)
data.way_type_allows_pushing = false
-- speed
local bridge_speed = profile.bridge_speeds[bridge]
local bridge_speed = profile.bridge_speeds[data.bridge]
if (bridge_speed and bridge_speed > 0) then
data.highway = bridge
if duration and durationIsValid(duration) then
result.duration = math.max( parseDuration(duration), 1 )
data.highway = data.bridge
if data.duration and durationIsValid(data.duration) then
result.duration = math.max( parseDuration(data.duration), 1 )
end
result.forward_speed = bridge_speed
result.backward_speed = bridge_speed
way_type_allows_pushing = true
elseif profile.route_speeds[route] then
data.way_type_allows_pushing = true
elseif profile.route_speeds[data.route] then
-- ferries (doesn't cover routes tagged using relations)
result.forward_mode = mode.ferry
result.backward_mode = mode.ferry
if duration and durationIsValid(duration) then
result.duration = math.max( 1, parseDuration(duration) )
if data.duration and durationIsValid(data.duration) then
result.duration = math.max( 1, parseDuration(data.duration) )
else
result.forward_speed = profile.route_speeds[route]
result.backward_speed = profile.route_speeds[route]
result.forward_speed = profile.route_speeds[data.route]
result.backward_speed = profile.route_speeds[data.route]
end
-- railway platforms (old tagging scheme)
elseif railway and profile.platform_speeds[railway] then
result.forward_speed = profile.platform_speeds[railway]
result.backward_speed = profile.platform_speeds[railway]
way_type_allows_pushing = true
elseif data.railway and profile.platform_speeds[data.railway] then
result.forward_speed = profile.platform_speeds[data.railway]
result.backward_speed = profile.platform_speeds[data.railway]
data.way_type_allows_pushing = true
-- public_transport platforms (new tagging platform)
elseif public_transport and profile.platform_speeds[public_transport] then
result.forward_speed = profile.platform_speeds[public_transport]
result.backward_speed = profile.platform_speeds[public_transport]
way_type_allows_pushing = true
elseif data.public_transport and profile.platform_speeds[data.public_transport] then
result.forward_speed = profile.platform_speeds[data.public_transport]
result.backward_speed = profile.platform_speeds[data.public_transport]
data.way_type_allows_pushing = true
-- railways
elseif profile.use_public_transport and railway and profile.railway_speeds[railway] and profile.access_tag_whitelist[access] then
elseif profile.use_public_transport and data.railway and profile.railway_speeds[data.railway] and profile.access_tag_whitelist[data.access] then
result.forward_mode = mode.train
result.backward_mode = mode.train
result.forward_speed = profile.railway_speeds[railway]
result.backward_speed = profile.railway_speeds[railway]
elseif amenity and profile.amenity_speeds[amenity] then
result.forward_speed = profile.railway_speeds[data.railway]
result.backward_speed = profile.railway_speeds[data.railway]
elseif data.amenity and profile.amenity_speeds[data.amenity] then
-- parking areas
result.forward_speed = profile.amenity_speeds[amenity]
result.backward_speed = profile.amenity_speeds[amenity]
way_type_allows_pushing = true
result.forward_speed = profile.amenity_speeds[data.amenity]
result.backward_speed = profile.amenity_speeds[data.amenity]
data.way_type_allows_pushing = true
elseif profile.bicycle_speeds[data.highway] then
-- regular ways
result.forward_speed = profile.bicycle_speeds[data.highway]
result.backward_speed = profile.bicycle_speeds[data.highway]
way_type_allows_pushing = true
elseif access and profile.access_tag_whitelist[access] then
data.way_type_allows_pushing = true
elseif data.access and profile.access_tag_whitelist[data.access] then
-- unknown way, but valid access tag
result.forward_speed = profile.default_speed
result.backward_speed = profile.default_speed
way_type_allows_pushing = true
data.way_type_allows_pushing = true
end
end
function oneway_handler(profile,way,result,data)
-- oneway
local implied_oneway = junction == "roundabout" or junction == "circular" or data.highway == "motorway"
local reverse = false
data.implied_oneway = data.junction == "roundabout" or data.junction == "circular" or data.highway == "motorway"
data.reverse = false
if oneway_bicycle == "yes" or oneway_bicycle == "1" or oneway_bicycle == "true" then
if data.oneway_bicycle == "yes" or data.oneway_bicycle == "1" or data.oneway_bicycle == "true" then
result.backward_mode = mode.inaccessible
elseif oneway_bicycle == "no" or oneway_bicycle == "0" or oneway_bicycle == "false" then
elseif data.oneway_bicycle == "no" or data.oneway_bicycle == "0" or data.oneway_bicycle == "false" then
-- prevent other cases
elseif oneway_bicycle == "-1" then
elseif data.oneway_bicycle == "-1" then
result.forward_mode = mode.inaccessible
reverse = true
elseif oneway == "yes" or oneway == "1" or oneway == "true" then
data.reverse = true
elseif data.oneway == "yes" or data.oneway == "1" or data.oneway == "true" then
result.backward_mode = mode.inaccessible
elseif oneway == "no" or oneway == "0" or oneway == "false" then
elseif data.oneway == "no" or data.oneway == "0" or data.oneway == "false" then
-- prevent other cases
elseif oneway == "-1" then
elseif data.oneway == "-1" then
result.forward_mode = mode.inaccessible
reverse = true
elseif implied_oneway then
data.reverse = true
elseif data.implied_oneway then
result.backward_mode = mode.inaccessible
end
end
function cycleway_handler(profile,way,result,data)
-- cycleway
local has_cycleway_forward = false
local has_cycleway_backward = false
local is_oneway = result.forward_mode ~= mode.inaccessible and result.backward_mode ~= mode.inaccessible and not implied_oneway
data.has_cycleway_forward = false
data.has_cycleway_backward = false
data.is_twoway = result.forward_mode ~= mode.inaccessible and result.backward_mode ~= mode.inaccessible and not data.implied_oneway
-- cycleways on normal roads
if is_oneway then
if cycleway and profile.cycleway_tags[cycleway] then
has_cycleway_backward = true
has_cycleway_forward = true
if data.is_twoway then
if data.cycleway and profile.cycleway_tags[data.cycleway] then
data.has_cycleway_backward = true
data.has_cycleway_forward = true
end
if (cycleway_right and profile.cycleway_tags[cycleway_right]) or (cycleway_left and profile.opposite_cycleway_tags[cycleway_left]) then
has_cycleway_forward = true
if (data.cycleway_right and profile.cycleway_tags[data.cycleway_right]) or (data.cycleway_left and profile.opposite_cycleway_tags[data.cycleway_left]) then
data.has_cycleway_forward = true
end
if (cycleway_left and profile.cycleway_tags[cycleway_left]) or (cycleway_right and profile.opposite_cycleway_tags[cycleway_right]) then
has_cycleway_backward = true
if (data.cycleway_left and profile.cycleway_tags[data.cycleway_left]) or (data.cycleway_right and profile.opposite_cycleway_tags[data.cycleway_right]) then
data.has_cycleway_backward = true
end
else
local has_twoway_cycleway = (cycleway and profile.opposite_cycleway_tags[cycleway]) or (cycleway_right and profile.opposite_cycleway_tags[cycleway_right]) or (cycleway_left and profile.opposite_cycleway_tags[cycleway_left])
local has_opposite_cycleway = (cycleway_left and profile.opposite_cycleway_tags[cycleway_left]) or (cycleway_right and profile.opposite_cycleway_tags[cycleway_right])
local has_oneway_cycleway = (cycleway and profile.cycleway_tags[cycleway]) or (cycleway_right and profile.cycleway_tags[cycleway_right]) or (cycleway_left and profile.cycleway_tags[cycleway_left])
local has_twoway_cycleway = (data.cycleway and profile.opposite_cycleway_tags[data.cycleway]) or (data.cycleway_right and profile.opposite_cycleway_tags[data.cycleway_right]) or (data.cycleway_left and profile.opposite_cycleway_tags[data.cycleway_left])
local has_opposite_cycleway = (data.cycleway_left and profile.opposite_cycleway_tags[data.cycleway_left]) or (data.cycleway_right and profile.opposite_cycleway_tags[data.cycleway_right])
local has_oneway_cycleway = (data.cycleway and profile.cycleway_tags[data.cycleway]) or (data.cycleway_right and profile.cycleway_tags[data.cycleway_right]) or (data.cycleway_left and profile.cycleway_tags[data.cycleway_left])
-- set cycleway even though it is an one-way if opposite is tagged
if has_twoway_cycleway then
has_cycleway_backward = true
has_cycleway_forward = true
data.has_cycleway_backward = true
data.has_cycleway_forward = true
elseif has_opposite_cycleway then
if not reverse then
has_cycleway_backward = true
if not data.reverse then
data.has_cycleway_backward = true
else
has_cycleway_forward = true
data.has_cycleway_forward = true
end
elseif has_oneway_cycleway then
if not reverse then
has_cycleway_forward = true
if not data.reverse then
data.has_cycleway_forward = true
else
has_cycleway_backward = true
data.has_cycleway_backward = true
end
end
end
if has_cycleway_backward then
if data.has_cycleway_backward then
result.backward_mode = mode.cycling
result.backward_speed = profile.bicycle_speeds["cycleway"]
end
if has_cycleway_forward then
if data.has_cycleway_forward then
result.forward_mode = mode.cycling
result.forward_speed = profile.bicycle_speeds["cycleway"]
end
end
function bike_push_handler(profile,way,result,data)
-- pushing bikes - if no other mode found
if result.forward_mode == mode.inaccessible or result.backward_mode == mode.inaccessible or
result.forward_speed == -1 or result.backward_speed == -1 then
if foot ~= 'no' then
if data.foot ~= 'no' then
local push_forward_speed = nil
local push_backward_speed = nil
if profile.pedestrian_speeds[data.highway] then
push_forward_speed = profile.pedestrian_speeds[data.highway]
push_backward_speed = profile.pedestrian_speeds[data.highway]
elseif man_made and profile.man_made_speeds[man_made] then
push_forward_speed = profile.man_made_speeds[man_made]
push_backward_speed = profile.man_made_speeds[man_made]
elseif data.man_made and profile.man_made_speeds[data.man_made] then
push_forward_speed = profile.man_made_speeds[data.man_made]
push_backward_speed = profile.man_made_speeds[data.man_made]
else
if foot == 'yes' then
if data.foot == 'yes' then
push_forward_speed = profile.walking_speed
if not implied_oneway then
if not data.implied_oneway then
push_backward_speed = profile.walking_speed
end
elseif foot_forward == 'yes' then
elseif data.foot_forward == 'yes' then
push_forward_speed = profile.walking_speed
elseif foot_backward == 'yes' then
elseif data.foot_backward == 'yes' then
push_backward_speed = profile.walking_speed
elseif way_type_allows_pushing then
elseif data.way_type_allows_pushing then
push_forward_speed = profile.walking_speed
if not implied_oneway then
if not data.implied_oneway then
push_backward_speed = profile.walking_speed
end
end
@@ -466,65 +509,77 @@ function handle_bicycle_tags(profile,way,result,data)
end
-- dismount
if bicycle == "dismount" then
if data.bicycle == "dismount" then
result.forward_mode = mode.pushing_bike
result.backward_mode = mode.pushing_bike
result.forward_speed = profile.walking_speed
result.backward_speed = profile.walking_speed
end
end
-- maxspeed
limit( result, maxspeed, maxspeed_forward, maxspeed_backward )
-- not routable if no speed assigned
-- this avoid assertions in debug builds
if result.forward_speed <= 0 and result.duration <= 0 then
result.forward_mode = mode.inaccessible
end
if result.backward_speed <= 0 and result.duration <= 0 then
result.backward_mode = mode.inaccessible
end
function safety_handler(profile,way,result,data)
-- convert duration into cyclability
if profile.properties.weight_name == 'cyclability' then
local safety_penalty = profile.unsafe_highway_list[data.highway] or 1.
local is_unsafe = safety_penalty < 1
local forward_is_unsafe = is_unsafe and not has_cycleway_forward
local backward_is_unsafe = is_unsafe and not has_cycleway_backward
local is_undesireable = data.highway == "service" and profile.service_penalties[service]
local forward_penalty = 1.
local backward_penalty = 1.
if forward_is_unsafe then
forward_penalty = math.min(forward_penalty, safety_penalty)
end
if backward_is_unsafe then
backward_penalty = math.min(backward_penalty, safety_penalty)
end
local safety_penalty = profile.unsafe_highway_list[data.highway] or 1.
local is_unsafe = safety_penalty < 1
if is_undesireable then
forward_penalty = math.min(forward_penalty, profile.service_penalties[service])
backward_penalty = math.min(backward_penalty, profile.service_penalties[service])
end
-- primaries that are one ways are probably huge primaries where the lanes need to be separated
if is_unsafe and data.highway == 'primary' and not data.is_twoway then
safety_penalty = safety_penalty * 0.5
end
if is_unsafe and data.highway == 'secondary' and not data.is_twoway then
safety_penalty = safety_penalty * 0.6
end
local forward_is_unsafe = is_unsafe and not data.has_cycleway_forward
local backward_is_unsafe = is_unsafe and not data.has_cycleway_backward
local is_undesireable = data.highway == "service" and profile.service_penalties[data.service]
local forward_penalty = 1.
local backward_penalty = 1.
if forward_is_unsafe then
forward_penalty = math.min(forward_penalty, safety_penalty)
end
if backward_is_unsafe then
backward_penalty = math.min(backward_penalty, safety_penalty)
end
if is_undesireable then
forward_penalty = math.min(forward_penalty, profile.service_penalties[data.service])
backward_penalty = math.min(backward_penalty, profile.service_penalties[data.service])
end
if result.forward_speed > 0 then
-- convert from km/h to m/s
result.forward_rate = result.forward_speed / 3.6 * forward_penalty
end
if result.backward_speed > 0 then
-- convert from km/h to m/s
result.backward_rate = result.backward_speed / 3.6 * backward_penalty
end
if result.duration > 0 then
result.weight = result.duration / forward_penalty
end
if data.highway == "bicycle" then
safety_bonus = safety_bonus + 0.2
if result.forward_speed > 0 then
-- convert from km/h to m/s
result.forward_rate = result.forward_speed / 3.6 * forward_penalty
result.forward_rate = result.forward_speed / 3.6 * safety_bonus
end
if result.backward_speed > 0 then
-- convert from km/h to m/s
result.backward_rate = result.backward_speed / 3.6 * backward_penalty
result.backward_rate = result.backward_speed / 3.6 * safety_bonus
end
if result.duration > 0 then
result.weight = result.duration / forward_penalty
result.weight = result.duration / safety_bonus
end
end
end
end
function process_way(profile, way, result)
-- the initial filtering of ways based on presence of tags
-- affects processing times significantly, because all ways
@@ -542,6 +597,39 @@ function process_way(profile, way, result)
local data = {
-- prefetch tags
highway = way:get_value_by_key('highway'),
route = nil,
man_made = nil,
railway = nil,
amenity = nil,
public_transport = nil,
bridge = nil,
access = nil,
junction = nil,
maxspeed = nil,
maxspeed_forward = nil,
maxspeed_backward = nil,
barrier = nil,
oneway = nil,
oneway_bicycle = nil,
cycleway = nil,
cycleway_left = nil,
cycleway_right = nil,
duration = nil,
service = nil,
foot = nil,
foot_forward = nil,
foot_backward = nil,
bicycle = nil,
way_type_allows_pushing = false,
has_cycleway_forward = false,
has_cycleway_backward = false,
is_twoway = true,
reverse = false,
implied_oneway = false
}
local handlers = Sequence {
@@ -572,6 +660,9 @@ function process_way(profile, way, result)
-- set name, ref and pronunciation
WayHandlers.names,
-- set classes
WayHandlers.classes,
-- set weight properties of the way
WayHandlers.weights
}
+17 -8
View File
@@ -306,37 +306,46 @@ end
-- add class information
function WayHandlers.classes(profile,way,result,data)
if not profile.classes then
return
end
local allowed_classes = Set {}
for k, v in pairs(profile.classes) do
allowed_classes[v] = true
end
local forward_toll, backward_toll = Tags.get_forward_backward_by_key(way, data, "toll")
local forward_route, backward_route = Tags.get_forward_backward_by_key(way, data, "route")
local tunnel = way:get_value_by_key("tunnel")
if tunnel and tunnel ~= "no" then
if allowed_classes["tunnel"] and tunnel and tunnel ~= "no" then
result.forward_classes["tunnel"] = true
result.backward_classes["tunnel"] = true
end
if forward_toll == "yes" then
if allowed_classes["toll"] and forward_toll == "yes" then
result.forward_classes["toll"] = true
end
if backward_toll == "yes" then
if allowed_classes["toll"] and backward_toll == "yes" then
result.backward_classes["toll"] = true
end
if forward_route == "ferry" then
if allowed_classes["ferry"] and forward_route == "ferry" then
result.forward_classes["ferry"] = true
end
if backward_route == "ferry" then
if allowed_classes["ferry"] and backward_route == "ferry" then
result.backward_classes["ferry"] = true
end
if result.forward_restricted then
if allowed_classes["restricted"] and result.forward_restricted then
result.forward_classes["restricted"] = true
end
if result.backward_restricted then
if allowed_classes["restricted"] and result.backward_restricted then
result.backward_classes["restricted"] = true
end
if data.highway == "motorway" or data.highway == "motorway_link" then
if allowed_classes["motorway"] and (data.highway == "motorway" or data.highway == "motorway_link") then
result.forward_classes["motorway"] = true
result.backward_classes["motorway"] = true
end
+6 -6
View File
@@ -5,7 +5,7 @@
-- Secondary road: 18km/h = 18000m/3600s = 100m/20s
-- Tertiary road: 12km/h = 12000m/3600s = 100m/30s
api_version = 3
api_version = 4
function setup()
return {
@@ -14,7 +14,7 @@ function setup()
max_speed_for_map_matching = 30/3.6, --km -> m/s
weight_name = 'duration',
process_call_tagless_node = false,
uturn_penalty = 20,
u_turn_penalty = 20,
traffic_light_penalty = 7, -- seconds
use_turn_restrictions = true
},
@@ -32,7 +32,7 @@ function setup()
primary = 36,
secondary = 18,
tertiary = 12,
steps = 6,
steps = 6
}
}
end
@@ -128,9 +128,9 @@ function process_way (profile, way, result)
end
function process_turn (profile, turn)
if turn.direction_modifier == direction_modifier.uturn then
turn.duration = profile.properties.uturn_penalty
turn.weight = profile.properties.uturn_penalty
if turn.is_u_turn then
turn.duration = turn.duration + profile.properties.u_turn_penalty
turn.weight = turn.weight + profile.properties.u_turn_penalty
end
if turn.has_traffic_light then
turn.duration = turn.duration + profile.properties.traffic_light_penalty
+3 -1
View File
@@ -9,6 +9,8 @@ lonlat = lambda x: (coord2float(x['lon']['__value']), coord2float(x['lat']['__va
def call(this, method, *args):
"""Call this.method(args)"""
if (str(this) == '<optimized out>'):
raise BaseException('"this" is optimized out')
command = '(*({})({})).{}({})'.format(this.type.target().pointer(), this.address, method, ','.join((str(x) for x in args)))
return gdb.parse_and_eval(command)
@@ -234,7 +236,6 @@ class SVGPrinter (gdb.Command):
mld_facade = facade.cast(gdb.lookup_type('osrm::engine::datafacade::ContiguousInternalMemoryAlgorithmDataFacade<osrm::engine::routing_algorithms::mld::Algorithm>'))
mld_partition = mld_facade['mld_partition']
mld_levels = call(mld_partition, 'GetNumberOfLevels')
print (mld_level, mld_levels)
if mld_level < mld_levels:
sentinel_node = call(mld_partition['partition'], 'size') - 1 # GetSentinelNode
number_of_cells = call(mld_partition, 'GetCell', mld_level, sentinel_node) # GetNumberOfCells
@@ -272,6 +273,7 @@ class SVGPrinter (gdb.Command):
for node in nodes:
geometry_id = call(facade, 'GetGeometryIndex', node)
direction = 'forward' if geometry_id['forward'] else 'reverse'
print (geometry_id, direction)
geometry = SVGPrinter.getByGeometryId(facade, geometry_id, 'Geometry')
weights = SVGPrinter.getByGeometryId(facade, geometry_id, 'Weights')
+43
View File
@@ -0,0 +1,43 @@
/*********************************
* Takes an XML `.osm` file and converts it into a cucumber scenario definition like
* Given the node locations
* | node | lon | lat |
* .....
* Given the ways
* | nodes | tag1 | tag2 | tag3 |
* .....
*
* Note that cucumber tests are limited to 26 nodes (labelled a-z), so
* you'll need to use pretty small OSM extracts to get this to work.
*****************************************/
var fs = require('fs');
var parseString = require('xml2js').parseString;
var data = fs.readFileSync('filename.osm', 'utf8');
const items = parseString(data, (err, result) => {
var idmap = {};
console.log('Given the node locations');
console.log(' | node | lon | lat |');
result.osm.node.filter(n => !n.$.action || n.$.action !== 'delete').forEach(i => {
var code = String.fromCharCode(97 + Object.keys(idmap).length)
idmap[i.$.id] = code;
console.log(` | ${code} | ${i.$.lon} | ${i.$.lat} |`);
});
var allkeys = {};
var waytags = {};
result.osm.way.filter(n => !n.$.action || n.$.action !== 'delete').forEach(w => {
if (!waytags[w.$.id]) waytags[w.$.id] = {};
w.tag.forEach(t => { allkeys[t.$.k] = t.$.v; waytags[w.$.id][t.$.k] = t.$.v; });
});
console.log('And the ways');
console.log(` | nodes | ${Object.keys(allkeys).join(' | ')} |`);
result.osm.way.filter(n => !n.$.action || n.$.action !== 'delete').forEach(w => {
console.log(` | ${w.nd.map(n => idmap[n.$.ref]).join('')} | ${Object.keys(allkeys).map(k => waytags[w.$.id][k] || '').join(' | ')} |`);
});
});
+60 -45
View File
@@ -10,13 +10,12 @@ const clu = require('command-line-usage');
const ansi = require('ansi-escape-sequences');
const turf = require('turf');
const jp = require('jsonpath');
const csv_stringify = require('csv-stringify/lib/sync');
const run_query = (query_options, filters, callback) => {
let tic = () => 0.;
http.request(query_options, function (res) {
let body = '', ttfb = tic();
if (res.statusCode != 200)
return callback(query_options.path, res.statusCode, ttfb);
res.setEncoding('utf8');
res.on('data', function (chunk) {
@@ -35,27 +34,51 @@ const run_query = (query_options, filters, callback) => {
}).end();
};
function generate_points(polygon, number) {
function generate_points(polygon, number, coordinates_number, max_distance) {
let query_points = [];
while (query_points.length < number) {
var chunk = turf
.random('points', number, { bbox: turf.bbox(polygon)})
.features
.map(x => x.geometry.coordinates)
.filter(pt => turf.inside(pt, polygon));
query_points = query_points.concat(chunk);
let points = [];
while(points.length < coordinates_number) {
let chunk = turf
.random('points', coordinates_number, { bbox: turf.bbox(polygon)})
.features
.map(x => x.geometry.coordinates)
.filter(pt => turf.inside(pt, polygon));
if (max_distance > 0)
{
chunk.forEach(pt => {
if (points.length == 0)
{
points.push(pt);
}
else
{
let distance = turf.distance(pt, points[points.length-1], 'meters');
if (distance < max_distance)
{
points.push(pt);
}
}
});
}
else
{
points = points.concat(chunk);
}
}
query_points.push(points);
}
return query_points.slice(0, number);
return query_points;
}
function generate_queries(options, query_points, coordinates_number) {
let queries = [];
for (let chunk = 0; chunk < query_points.length; chunk += coordinates_number)
{
let points = query_points.slice(chunk, chunk + coordinates_number);
let query = options.path.replace(/{}/g, x => points.pop().join(','));
queries.push(query);
}
function generate_queries(options, query_points) {
let queries = query_points.map(points => {
return options.path.replace(/{}/g, x => points.pop().join(','));
});
return queries;
}
@@ -73,27 +96,22 @@ function BoundingBox(x) {
}
const optionsList = [
{name: 'help', alias: 'h', type: Boolean, description: 'Display this usage guide.', defaultValue: false},
{name: 'server', alias: 's', type: ServerDetails, defaultValue: ServerDetails('localhost:5000'),
description: 'OSRM routing server', typeLabel: '[underline]{hostname[:port]}'},
{name: 'path', alias: 'p', type: String, defaultValue: '/route/v1/driving/{};{}',
description: 'OSRM query path with {} coordinate placeholders, default /route/v1/driving/{};{}', typeLabel: '[underline]{path}'},
{name: 'filter', alias: 'f', type: String, defaultValue: ['$.routes[0].weight'], multiple: true,
description: 'JSONPath filters, default "$.routes[0].weight"', typeLabel: '[underline]{filter}'},
{name: 'bounding-box', alias: 'b', type: BoundingBox, defaultValue: BoundingBox('5.86442,47.2654,15.0508,55.1478'), multiple: true,
description: 'queries bounding box, default "5.86442,47.2654,15.0508,55.1478"', typeLabel: '[underline]{west,south,east,north}'},
{name: 'max-sockets', alias: 'm', type: Number, defaultValue: 1,
description: 'how many concurrent sockets the agent can have open per origin, default 1', typeLabel: '[underline]{number}'},
{name: 'number', alias: 'n', type: Number, defaultValue: 10,
description: 'number of query points, default 10', typeLabel: '[underline]{number}'},
{name: 'queries-files', alias: 'q', type: String,
description: 'CSV file with queries in the first row', typeLabel: '[underline]{file}'}];
{name: 'server', alias: 's', type: ServerDetails, defaultValue: ServerDetails('localhost:5000'), description: 'OSRM routing server', typeLabel: '{underline hostname[:port]}'},
{name: 'path', alias: 'p', type: String, defaultValue: '/route/v1/driving/{};{}', description: 'OSRM query path with \\{\\} coordinate placeholders, default /route/v2/driving/\\{\\};\\{\\}', typeLabel: '{underline path}'},
{name: 'filter', alias: 'f', type: String, defaultValue: ['$.routes[0].weight'], multiple: true, description: 'JSONPath filters, default "$.routes[0].weight"', typeLabel: '{underline filter}'},
{name: 'bounding-box', alias: 'b', type: BoundingBox, defaultValue: BoundingBox('5.86442,47.2654,15.0508,55.1478'), multiple: true, description: 'queries bounding box, default "5.86442,47.2654,15.0508,55.1478"', typeLabel: '{underline west,south,east,north}'},
{name: 'max-sockets', alias: 'm', type: Number, defaultValue: 1, description: 'how many concurrent sockets the agent can have open per origin, default 1', typeLabel: '{underline number}'},
{name: 'number', alias: 'n', type: Number, defaultValue: 10, description: 'number of query points, default 10', typeLabel: '{underline number}'},
{name: 'max-distance', alias: 'd', type: Number, defaultValue: -1, description: 'maximal distance between coordinates', typeLabel: '{underline number}'},
{name: 'queries-files', alias: 'q', type: String, description: 'CSV file with queries in the first row', typeLabel: '{underline file}'},
];
const options = cla(optionsList);
if (options.help) {
const banner =
String.raw` ____ _______ __ ___ ___ __ ___ ___ _________ ` + '\n' +
String.raw` / __ \/ __/ _ \/ |/ / / _ \/ / / / |/ / |/ / __/ _ \ ` + '\n' +
String.raw`/ /_/ /\ \/ , _/ /|_/ / / , _/ /_/ / / / _// , _/ ` + '\n' +
String.raw`\____/___/_/|_/_/ /_/ /_/|_|\____/_/|_/_/|_/___/_/|_| `;
const banner =`\
____ _______ __ ______ __ ___ ___ _________
/ __ \\\\/ __/ _ \\\\/ |/ / _ \\\\/ / / / |/ / |/ / __/ _ \\\\
/ /_/ /\\\\ \\\\/ , _/ /|_/ / , _/ /_/ / / / _// , _/
\\\\____/___/_/|_/_/ /_/_/|_|\\\\____/_/|_/_/|_/___/_/|_|`;
const usage = clu([
{ content: ansi.format(banner, 'green'), raw: true },
{ header: 'Run OSRM queries and collect results'/*, content: 'Generates something [italic]{very} important.'*/ },
@@ -114,8 +132,8 @@ if (options.hasOwnProperty('queries-files')) {
} else {
const polygon = options['bounding-box'].map(x => x.poly).reduce((x,y) => turf.union(x, y));
const coordinates_number = (options.path.match(/{}/g) || []).length;
const query_points = generate_points(polygon, coordinates_number * options.number);
queries = generate_queries(options, query_points, coordinates_number);
const query_points = generate_points(polygon, options.number, coordinates_number, options['max-distance']);
queries = generate_queries(options, query_points);
}
queries = queries.map(q => { return {hostname: options.server.hostname, port: options.server.port, path: q}; });
@@ -123,11 +141,8 @@ queries = queries.map(q => { return {hostname: options.server.hostname, port: op
http.globalAgent.maxSockets = options['max-sockets'];
queries.map(query => {
run_query(query, options.filter, (query, code, ttfb, total, results) => {
let str = `"${query}",${code}`;
if (ttfb !== undefined) str += `,${ttfb}`;
if (total !== undefined) str += `,${total}`;
if (typeof results === 'object' && results.length > 0)
str += ',' + results.map(x => isNaN(x) ? '"' + JSON.stringify(x).replace(/\n/g, ';').replace(/"/g, "'") + '"' : Number(x)).join(',');
console.log(str);
let data = results ? JSON.stringify(results[0]) : '';
let record = [[query, code, ttfb, total, data]];
process.stdout.write(csv_stringify(record));
});
});
+32 -61
View File
@@ -9,79 +9,50 @@ set -o nounset
# structure will be lost.
# http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html
OSMIUM_REPO="https://github.com/osmcode/libosmium.git"
OSMIUM_TAG=v2.13.1
OSMIUM_PATH="osmcode/libosmium"
OSMIUM_TAG=v2.14.0
VARIANT_REPO="https://github.com/mapbox/variant.git"
VARIANT_PATH="mapbox/variant"
VARIANT_TAG=v1.1.3
SOL_REPO="https://github.com/ThePhD/sol2.git"
SOL_PATH="ThePhD/sol2"
SOL_TAG=v2.17.5
RAPIDJSON_REPO="https://github.com/miloyip/rapidjson.git"
RAPIDJSON_PATH="Tencent/rapidjson"
RAPIDJSON_TAG=v1.1.0
MICROTAR_REPO="https://github.com/rxi/microtar"
MICROTAR_PATH="rxi/microtar"
MICROTAR_TAG=v0.1.0
VARIANT_LATEST=$(curl "https://api.github.com/repos/mapbox/variant/releases/latest" | jq ".tag_name")
OSMIUM_LATEST=$(curl "https://api.github.com/repos/osmcode/libosmium/releases/latest" | jq ".tag_name")
SOL_LATEST=$(curl "https://api.github.com/repos/ThePhD/sol2/releases/latest" | jq ".tag_name")
RAPIDJSON_LATEST=$(curl "https://api.github.com/repos/miloyip/rapidjson/releases/latest" | jq ".tag_name")
MICROTAR_LATEST=$(curl "https://api.github.com/repos/rxi/microtar/releases/latest" | jq ".tag_name")
PROTOZERO_PATH="mapbox/protozero"
PROTOZERO_TAG=v1.6.2
echo "Latest osmium release is $OSMIUM_LATEST, pulling in \"$OSMIUM_TAG\""
echo "Latest variant release is $VARIANT_LATEST, pulling in \"$VARIANT_TAG\""
echo "Latest sol2 release is $SOL_LATEST, pulling in \"$SOL_TAG\""
echo "Latest rapidjson release is $RAPIDJSON_LATEST, pulling in \"$RAPIDJSON_TAG\""
echo "Latest microtar release is $MICROTAR_LATEST, pulling in \"$MICROTAR_TAG\""
VTZERO_PATH="mapbox/vtzero"
VTZERO_TAG=v1.0.1
read -p "Update osmium (y/n) " ok
if [[ $ok =~ [yY] ]]
then
if [ -d "third_party/libosmium" ]; then
git subtree pull -P third_party/libosmium/ $OSMIUM_REPO $OSMIUM_TAG --squash
else
git subtree add -P third_party/libosmium/ $OSMIUM_REPO $OSMIUM_TAG --squash
fi
fi
function update_subtree () {
name=${1^^}
path=$(tmpvar=${name}_PATH && echo ${!tmpvar})
tag=$(tmpvar=${name}_TAG && echo ${!tmpvar})
dir=$(basename $path)
repo="https://github.com/${path}.git"
latest=$(curl -s "https://api.github.com/repos/${path}/releases/latest" | jq ".tag_name")
read -p "Update variant (y/n) " ok
if [[ $ok =~ [yY] ]]
then
if [ -d "third_party/variant" ]; then
git subtree pull -P third_party/variant/ $VARIANT_REPO $VARIANT_TAG --squash
else
git subtree add -P third_party/variant/ $VARIANT_REPO $VARIANT_TAG --squash
fi
fi
echo "Latest $1 release is ${latest}, pulling in \"${tag}\""
read -p "Update sol2 (y/n) " ok
if [[ $ok =~ [yY] ]]
then
if [ -d "third_party/sol2" ]; then
git subtree pull -P third_party/sol2/sol2/ $SOL_REPO $SOL_TAG --squash
else
git subtree add -P third_party/sol2/sol2/ $SOL_REPO $SOL_TAG --squash
fi
fi
read -p "Update ${1} (y/n) " ok
read -p "Update rapidjson (y/n) " ok
if [[ $ok =~ [yY] ]]
then
if [ -d "third_party/rapidjson" ]; then
git subtree pull -P third_party/rapidjson/ $RAPIDJSON_REPO $RAPIDJSON_TAG --squash
else
git subtree add -P third_party/rapidjson/ $RAPIDJSON_REPO $RAPIDJSON_TAG --squash
fi
fi
if [[ $ok =~ [yY] ]]
then
if [ -d "third_party/$dir" ]; then
git subtree pull -P third_party/$dir ${repo} ${tag} --squash
else
git subtree add -P third_party/$dir ${repo} ${tag} --squash
fi
fi
}
read -p "Update microtar (y/n) " ok
if [[ $ok =~ [yY] ]]
then
if [ -d "third_party/microtar" ]; then
git subtree pull -P third_party/microtar/ $MICROTAR_REPO $MICROTAR_TAG --squash
else
git subtree add -P third_party/microtar/ $MICROTAR_REPO $MICROTAR_TAG --squash
fi
fi
## Update dependencies
for dep in osmium variant sol rapidjson microtar protozero vtzero ; do
update_subtree $dep
done
+6
View File
@@ -75,6 +75,12 @@ int Contractor::Run()
EdgeID number_of_edge_based_nodes = updater.LoadAndUpdateEdgeExpandedGraph(
edge_based_edge_list, node_weights, connectivity_checksum);
// Convert node weights for oneway streets to INVALID_EDGE_WEIGHT
for (auto &weight : node_weights)
{
weight = (weight & 0x80000000) ? INVALID_EDGE_WEIGHT : weight;
}
// Contracting the edge-expanded graph
TIMER_START(contraction);
+21 -9
View File
@@ -74,24 +74,28 @@ void printUnreachableStatistics(const Partition &partition,
auto LoadAndUpdateEdgeExpandedGraph(const CustomizationConfig &config,
const partitioner::MultiLevelPartition &mlp,
std::vector<EdgeWeight> &node_weights,
std::vector<EdgeDuration> &node_durations,
std::uint32_t &connectivity_checksum)
{
updater::Updater updater(config.updater_config);
EdgeID num_nodes;
std::vector<extractor::EdgeBasedEdge> edge_based_edge_list;
std::tie(num_nodes, edge_based_edge_list, connectivity_checksum) =
updater.LoadAndUpdateEdgeExpandedGraph();
EdgeID num_nodes = updater.LoadAndUpdateEdgeExpandedGraph(
edge_based_edge_list, node_weights, node_durations, connectivity_checksum);
auto directed = partitioner::splitBidirectionalEdges(edge_based_edge_list);
auto tidied =
partitioner::prepareEdgesForUsageInGraph<StaticEdgeBasedGraphEdge>(std::move(directed));
auto edge_based_graph = customizer::MultiLevelEdgeBasedGraph(mlp, num_nodes, std::move(tidied));
auto tidied = partitioner::prepareEdgesForUsageInGraph<
typename partitioner::MultiLevelEdgeBasedGraph::InputEdge>(std::move(directed));
auto edge_based_graph =
partitioner::MultiLevelEdgeBasedGraph(mlp, num_nodes, std::move(tidied));
return edge_based_graph;
}
std::vector<CellMetric> customizeFilteredMetrics(const MultiLevelEdgeBasedGraph &graph,
std::vector<CellMetric> customizeFilteredMetrics(const partitioner::MultiLevelEdgeBasedGraph &graph,
const partitioner::CellStorage &storage,
const CellCustomizer &customizer,
const std::vector<std::vector<bool>> &node_filters)
@@ -119,8 +123,13 @@ int Customizer::Run(const CustomizationConfig &config)
partitioner::MultiLevelPartition mlp;
partitioner::files::readPartition(config.GetPath(".osrm.partition"), mlp);
std::vector<EdgeWeight> node_weights;
std::vector<EdgeDuration> node_durations; // TODO: to be removed later
std::uint32_t connectivity_checksum = 0;
auto graph = LoadAndUpdateEdgeExpandedGraph(config, mlp, connectivity_checksum);
auto graph = LoadAndUpdateEdgeExpandedGraph(
config, mlp, node_weights, node_durations, connectivity_checksum);
BOOST_ASSERT(graph.GetNumberOfNodes() == node_weights.size());
std::for_each(node_weights.begin(), node_weights.end(), [](auto &w) { w &= 0x7fffffff; });
util::Log() << "Loaded edge based graph: " << graph.GetNumberOfEdges() << " edges, "
<< graph.GetNumberOfNodes() << " nodes";
@@ -157,7 +166,10 @@ int Customizer::Run(const CustomizationConfig &config)
util::Log() << "MLD customization writing took " << TIMER_SEC(writing_mld_data) << " seconds";
TIMER_START(writing_graph);
partitioner::files::writeGraph(config.GetPath(".osrm.mldgr"), graph, connectivity_checksum);
MultiLevelEdgeBasedGraph shaved_graph{
std::move(graph), std::move(node_weights), std::move(node_durations)};
customizer::files::writeGraph(
config.GetPath(".osrm.mldgr"), shaved_graph, connectivity_checksum);
TIMER_STOP(writing_graph);
util::Log() << "Graph writing took " << TIMER_SEC(writing_graph) << " seconds";
+4 -3
View File
@@ -618,9 +618,10 @@ RouteSteps collapseSegregatedTurnInstructions(RouteSteps steps)
TransferLanesStrategy());
++next_step;
}
// else if the current step is segregated and the next step is not then combine with turn
// adjustment
else if (curr_step->is_segregated && !next_step->is_segregated)
// else if the current step is segregated and the next step is not segregated
// and the next step is not a roundabout then combine with turn adjustment
else if (curr_step->is_segregated && !next_step->is_segregated &&
!hasRoundaboutType(next_step->maneuver.instruction))
{
// Determine if u-turn
if (bearingsAreReversed(
+9 -4
View File
@@ -81,16 +81,21 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
}
auto snapped_phantoms = SnapPhantomNodes(phantom_nodes);
auto result_table =
algorithms.ManyToManySearch(snapped_phantoms, params.sources, params.destinations);
if (result_table.empty())
bool request_distance = params.annotations & api::TableParameters::AnnotationsType::Distance;
bool request_duration = params.annotations & api::TableParameters::AnnotationsType::Duration;
auto result_tables_pair = algorithms.ManyToManySearch(
snapped_phantoms, params.sources, params.destinations, request_distance, request_duration);
if ((request_duration && result_tables_pair.first.empty()) ||
(request_distance && result_tables_pair.second.empty()))
{
return Error("NoTable", "No table found", result);
}
api::TableAPI table_api{facade, params};
table_api.MakeResponse(result_table, snapped_phantoms, result);
table_api.MakeResponse(result_tables_pair, snapped_phantoms, result);
return Status::Ok;
}
+212 -540
View File
@@ -1,5 +1,6 @@
#include "guidance/turn_instruction.hpp"
#include "engine/plugins/plugin_base.hpp"
#include "engine/plugins/plugin_base.hpp"
#include "engine/plugins/tile.hpp"
@@ -8,21 +9,19 @@
#include "util/vector_tile.hpp"
#include "util/web_mercator.hpp"
#include "engine/api/json_factory.hpp"
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/multi/geometries/multi_linestring.hpp>
#include <protozero/pbf_writer.hpp>
#include <protozero/varint.hpp>
#include <vtzero/builder.hpp>
#include <vtzero/geometry.hpp>
#include <vtzero/index.hpp>
#include <algorithm>
#include <numeric>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
@@ -41,45 +40,7 @@ constexpr const static int MIN_ZOOM_FOR_TURNS = 15;
namespace
{
// Creates an indexed lookup table for values - used to encoded the vector tile
// which uses a lookup table and index pointers for encoding
template <typename T> struct ValueIndexer
{
private:
std::vector<T> used_values;
std::unordered_map<T, std::size_t> value_offsets;
public:
std::size_t add(const T &value)
{
const auto found = value_offsets.find(value);
std::size_t offset;
if (found == value_offsets.end())
{
used_values.push_back(value);
offset = used_values.size() - 1;
value_offsets[value] = offset;
}
else
{
offset = found->second;
}
return offset;
}
std::size_t indexOf(const T &value) { return value_offsets[value]; }
const std::vector<T> &values() { return used_values; }
std::size_t size() const { return used_values.size(); }
};
using RTreeLeaf = datafacade::BaseDataFacade::RTreeLeaf;
// TODO: Port all this encoding logic to https://github.com/mapbox/vector-tile, which wasn't
// available when this code was originally written.
// Simple container class for WGS84 coordinates
template <typename T> struct Point final
{
@@ -132,58 +93,15 @@ const static box_t clip_box(point_t(-util::vector_tile::BUFFER, -util::vector_ti
point_t(util::vector_tile::EXTENT + util::vector_tile::BUFFER,
util::vector_tile::EXTENT + util::vector_tile::BUFFER));
// from mapnik-vector-tile
// Encodes a linestring using protobuf zigzag encoding
inline bool encodeLinestring(const FixedLine &line,
protozero::packed_field_uint32 &geometry,
std::int32_t &start_x,
std::int32_t &start_y)
{
const std::size_t line_size = line.size();
if (line_size < 2)
{
return false;
}
const unsigned lineto_count = static_cast<const unsigned>(line_size) - 1;
auto pt = line.begin();
const constexpr int MOVETO_COMMAND = 9;
geometry.add_element(MOVETO_COMMAND); // 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;
// This means LINETO repeated N times
// See: https://github.com/mapbox/vector-tile-spec/tree/master/2.1#example-command-integers
geometry.add_element((lineto_count << 3u) | 2u);
// Now that we've issued the LINETO REPEAT N command, we append
// N coordinate pairs immediately after the command.
for (++pt; pt != line.end(); ++pt)
{
const std::int32_t dx = pt->x - start_x;
const std::int32_t dy = pt->y - start_y;
geometry.add_element(protozero::encode_zigzag32(dx));
geometry.add_element(protozero::encode_zigzag32(dy));
start_x = pt->x;
start_y = pt->y;
}
return true;
}
// from mapnik-vctor-tile
// Encodes a point
inline void encodePoint(const FixedPoint &pt, protozero::packed_field_uint32 &geometry)
{
const constexpr int MOVETO_COMMAND = 9;
geometry.add_element(MOVETO_COMMAND);
const std::int32_t dx = pt.x;
const std::int32_t dy = pt.y;
// Manual zigzag encoding.
geometry.add_element(protozero::encode_zigzag32(dx));
geometry.add_element(protozero::encode_zigzag32(dy));
}
/**
* Return the x1,y1,x2,y2 pixel coordinates of a line in a given
* tile.
*
* @param start the first coordinate of the line
* @param target the last coordinate of the line
* @param tile_bbox the boundaries of the tile, in mercator coordinates
* @return a FixedLine with coordinates relative to the tile_bbox.
*/
linestring_t floatLineToTileLine(const FloatLine &geo_line, const BBox &tile_bbox)
{
linestring_t unclipped_line;
@@ -361,6 +279,144 @@ std::vector<NodeID> getSegregatedNodes(const DataFacadeBase &facade,
return result;
}
struct SpeedLayer : public vtzero::layer_builder
{
vtzero::value_index_small_uint uint_index;
vtzero::value_index<vtzero::double_value_type, float, std::unordered_map> double_index;
vtzero::value_index_internal<std::unordered_map> string_index;
vtzero::value_index_bool bool_index;
vtzero::index_value key_speed;
vtzero::index_value key_is_small;
vtzero::index_value key_datasource;
vtzero::index_value key_weight;
vtzero::index_value key_duration;
vtzero::index_value key_name;
vtzero::index_value key_rate;
SpeedLayer(vtzero::tile_builder &tile)
: layer_builder(tile, "speeds"), uint_index(*this), double_index(*this),
string_index(*this), bool_index(*this), key_speed(add_key_without_dup_check("speed")),
key_is_small(add_key_without_dup_check("is_small")),
key_datasource(add_key_without_dup_check("datasource")),
key_weight(add_key_without_dup_check("weight")),
key_duration(add_key_without_dup_check("duration")),
key_name(add_key_without_dup_check("name")), key_rate(add_key_without_dup_check("rate"))
{
}
}; // struct SpeedLayer
class SpeedLayerFeatureBuilder : public vtzero::linestring_feature_builder
{
SpeedLayer &m_layer;
public:
SpeedLayerFeatureBuilder(SpeedLayer &layer, uint64_t id)
: vtzero::linestring_feature_builder(layer), m_layer(layer)
{
set_id(id);
}
void set_speed(unsigned int value)
{
add_property(m_layer.key_speed, m_layer.uint_index(std::min(value, 127u)));
}
void set_is_small(bool value) { add_property(m_layer.key_is_small, m_layer.bool_index(value)); }
void set_datasource(const std::string &value)
{
add_property(m_layer.key_datasource,
m_layer.string_index(vtzero::encoded_property_value{value}));
}
void set_weight(double value) { add_property(m_layer.key_weight, m_layer.double_index(value)); }
void set_duration(double value)
{
add_property(m_layer.key_duration, m_layer.double_index(value));
}
void set_name(const boost::string_ref &value)
{
add_property(
m_layer.key_name,
m_layer.string_index(vtzero::encoded_property_value{value.data(), value.size()}));
}
void set_rate(double value) { add_property(m_layer.key_rate, m_layer.double_index(value)); }
}; // class SpeedLayerFeatureBuilder
struct TurnsLayer : public vtzero::layer_builder
{
vtzero::value_index<vtzero::sint_value_type, int, std::unordered_map> int_index;
vtzero::value_index<vtzero::float_value_type, float, std::unordered_map> float_index;
vtzero::value_index_internal<std::unordered_map> string_index;
vtzero::index_value key_bearing_in;
vtzero::index_value key_turn_angle;
vtzero::index_value key_cost;
vtzero::index_value key_weight;
vtzero::index_value key_turn_type;
vtzero::index_value key_turn_modifier;
TurnsLayer(vtzero::tile_builder &tile)
: layer_builder(tile, "turns"), int_index(*this), float_index(*this), string_index(*this),
key_bearing_in(add_key_without_dup_check("bearing_in")),
key_turn_angle(add_key_without_dup_check("turn_angle")),
key_cost(add_key_without_dup_check("cost")),
key_weight(add_key_without_dup_check("weight")),
key_turn_type(add_key_without_dup_check("type")),
key_turn_modifier(add_key_without_dup_check("modifier"))
{
}
}; // struct TurnsLayer
class TurnsLayerFeatureBuilder : public vtzero::point_feature_builder
{
TurnsLayer &m_layer;
public:
TurnsLayerFeatureBuilder(TurnsLayer &layer, uint64_t id)
: vtzero::point_feature_builder(layer), m_layer(layer)
{
set_id(id);
}
void set_bearing_in(int value)
{
add_property(m_layer.key_bearing_in, m_layer.int_index(value));
}
void set_turn_angle(int value)
{
add_property(m_layer.key_turn_angle, m_layer.int_index(value));
}
void set_cost(float value) { add_property(m_layer.key_cost, m_layer.float_index(value)); }
void set_weight(float value) { add_property(m_layer.key_weight, m_layer.float_index(value)); }
void set_turn(osrm::guidance::TurnInstruction value)
{
const auto type = osrm::guidance::internalInstructionTypeToString(value.type);
const auto modifier = osrm::guidance::instructionModifierToString(value.direction_modifier);
add_property(
m_layer.key_turn_type,
m_layer.string_index(vtzero::encoded_property_value{type.data(), type.size()}));
add_property(
m_layer.key_turn_modifier,
m_layer.string_index(vtzero::encoded_property_value{modifier.data(), modifier.size()}));
}
}; // class TurnsLayerFeatureBuilder
void encodeVectorTile(const DataFacadeBase &facade,
unsigned x,
unsigned y,
@@ -371,117 +427,25 @@ void encodeVectorTile(const DataFacadeBase &facade,
const std::vector<NodeID> &segregated_nodes,
std::string &pbf_buffer)
{
std::uint8_t max_datasource_id = 0;
// Vector tiles encode properties on features as indexes into a layer-specific
// lookup table. These ValueIndexer's act as memoizers for values as we discover
// them during edge explioration, and are then used to generate the lookup
// tables for each tile layer.
ValueIndexer<int> line_int_index;
ValueIndexer<util::StringView> line_string_index;
ValueIndexer<int> point_int_index;
ValueIndexer<float> point_float_index;
ValueIndexer<std::string> point_string_index;
vtzero::tile_builder tile;
const auto get_geometry_id = [&facade](auto edge) {
return facade.GetGeometryIndex(edge.forward_segment_id.id).id;
};
// Vector tiles encode feature properties as indexes into a lookup table. So, we need
// to "pre-loop" over all the edges to create the lookup tables. Once we have those, we
// can then encode the features, and we'll know the indexes that feature properties
// need to refer to.
for (const auto &edge_index : sorted_edge_indexes)
{
const auto &edge = edges[edge_index];
const auto geometry_id = get_geometry_id(edge);
const auto forward_datasource_range = facade.GetUncompressedForwardDatasources(geometry_id);
const auto reverse_datasource_range = facade.GetUncompressedReverseDatasources(geometry_id);
BOOST_ASSERT(edge.fwd_segment_position < forward_datasource_range.size());
const auto forward_datasource = forward_datasource_range(edge.fwd_segment_position);
BOOST_ASSERT(edge.fwd_segment_position < reverse_datasource_range.size());
const auto reverse_datasource = reverse_datasource_range(reverse_datasource_range.size() -
edge.fwd_segment_position - 1);
// Keep track of the highest datasource seen so that we don't write unnecessary
// data to the layer attribute values
max_datasource_id = std::max(max_datasource_id, forward_datasource);
max_datasource_id = std::max(max_datasource_id, reverse_datasource);
}
// Convert tile coordinates into mercator coordinates
double min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat;
util::web_mercator::xyzToMercator(
x, y, z, min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat);
const BBox tile_bbox{min_mercator_lon, min_mercator_lat, max_mercator_lon, max_mercator_lat};
// Protobuf serializes blocks when objects go out of scope, hence
// the extra scoping below.
protozero::pbf_writer tile_writer{pbf_buffer};
// XXX leaving in some superfluous scopes to make diff easier to read.
{
{
// Add a layer object to the PBF stream. 3=='layer' from the vector tile spec
// (2.1)
protozero::pbf_writer line_layer_writer(tile_writer, util::vector_tile::LAYER_TAG);
// TODO: don't write a layer if there are no features
line_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version
// Field 1 is the "layer name" field, it's a string
line_layer_writer.add_string(util::vector_tile::NAME_TAG, "speeds"); // name
// Field 5 is the tile extent. It's a uint32 and should be set to 4096
// for normal vector tiles.
line_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG,
util::vector_tile::EXTENT); // extent
// Because we need to know the indexes into the vector tile lookup table,
// we need to do an initial pass over the data and create the complete
// index of used values.
for (const auto &edge_index : sorted_edge_indexes)
{
const auto &edge = edges[edge_index];
const auto geometry_id = get_geometry_id(edge);
// Get coordinates for start/end nodes of segment (NodeIDs u and v)
const auto a = facade.GetCoordinateOfNode(edge.u);
const auto b = facade.GetCoordinateOfNode(edge.v);
// Calculate the length in meters
const double length = osrm::util::coordinate_calculation::haversineDistance(a, b);
// Weight values
const auto forward_weight_range = facade.GetUncompressedForwardWeights(geometry_id);
const auto reverse_weight_range = facade.GetUncompressedReverseWeights(geometry_id);
const auto forward_weight = forward_weight_range[edge.fwd_segment_position];
const auto reverse_weight = reverse_weight_range[reverse_weight_range.size() -
edge.fwd_segment_position - 1];
line_int_index.add(forward_weight);
line_int_index.add(reverse_weight);
std::uint32_t forward_rate =
static_cast<std::uint32_t>(round(length / forward_weight * 10.));
std::uint32_t reverse_rate =
static_cast<std::uint32_t>(round(length / reverse_weight * 10.));
line_int_index.add(forward_rate);
line_int_index.add(reverse_rate);
// Duration values
const auto forward_duration_range =
facade.GetUncompressedForwardDurations(geometry_id);
const auto reverse_duration_range =
facade.GetUncompressedReverseDurations(geometry_id);
const auto forward_duration = forward_duration_range[edge.fwd_segment_position];
const auto reverse_duration = reverse_duration_range[reverse_duration_range.size() -
edge.fwd_segment_position - 1];
line_int_index.add(forward_duration);
line_int_index.add(reverse_duration);
}
// Begin the layer features block
{
SpeedLayer speeds_layer{tile};
// Each feature gets a unique id, starting at 1
unsigned id = 1;
for (const auto &edge_index : sorted_edge_indexes)
@@ -516,7 +480,6 @@ void encodeVectorTile(const DataFacadeBase &facade,
const auto reverse_duration =
reverse_duration_range[reverse_duration_range.size() -
edge.fwd_segment_position - 1];
const auto forward_datasource_idx =
forward_datasource_range(edge.fwd_segment_position);
const auto reverse_datasource_idx = reverse_datasource_range(
@@ -526,84 +489,10 @@ void encodeVectorTile(const DataFacadeBase &facade,
const auto name_id = facade.GetNameIndex(edge.forward_segment_id.id);
auto name = facade.GetNameForID(name_id);
line_string_index.add(name);
const auto encode_tile_line = [&line_layer_writer,
&component_id,
&id,
&max_datasource_id,
&line_int_index](
const FixedLine &tile_line,
const std::uint32_t speed_kmh_idx,
const std::uint32_t rate_idx,
const std::size_t weight_idx,
const std::size_t duration_idx,
const DatasourceID datasource_idx,
const std::size_t name_idx,
std::int32_t &start_x,
std::int32_t &start_y) {
// Here, we save the two attributes for our feature: the speed and
// the is_small boolean. We only serve up speeds from 0-139, so all we
// do is save the first
protozero::pbf_writer feature_writer(line_layer_writer,
util::vector_tile::FEATURE_TAG);
// Field 3 is the "geometry type" field. Value 2 is "line"
feature_writer.add_enum(
util::vector_tile::GEOMETRY_TAG,
util::vector_tile::GEOMETRY_TYPE_LINE); // geometry type
// Field 1 for the feature is the "id" field.
feature_writer.add_uint64(util::vector_tile::ID_TAG, 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, util::vector_tile::FEATURE_ATTRIBUTES_TAG);
field.add_element(0); // "speed" tag key offset
field.add_element(std::min(
speed_kmh_idx, 127u)); // save the speed value, capped at 127
field.add_element(1); // "is_small" tag key offset
field.add_element(
128 + (component_id.is_tiny ? 0 : 1)); // is_small feature offset
field.add_element(2); // "datasource" tag key offset
field.add_element(130 + datasource_idx); // datasource value offset
field.add_element(3); // "weight" tag key offset
field.add_element(130 + max_datasource_id + 1 +
weight_idx); // weight value offset
field.add_element(4); // "duration" tag key offset
field.add_element(130 + max_datasource_id + 1 +
duration_idx); // duration value offset
field.add_element(5); // "name" tag key offset
field.add_element(130 + max_datasource_id + 1 +
line_int_index.values().size() + name_idx);
field.add_element(6); // rate tag key offset
field.add_element(130 + max_datasource_id + 1 + rate_idx);
}
{
// Encode the geometry for the feature
protozero::packed_field_uint32 geometry(
feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG);
encodeLinestring(tile_line, geometry, start_x, start_y);
}
};
// If this is a valid forward edge, go ahead and add it to the tile
if (forward_duration != 0 && edge.forward_segment_id.enabled)
{
std::int32_t start_x = 0;
std::int32_t start_y = 0;
// Calculate the speed for this line
// Speeds are looked up in a simple 1:1 table, so the speed value == lookup
// table index
std::uint32_t speed_kmh_idx =
static_cast<std::uint32_t>(round(length / forward_duration * 10 * 3.6));
@@ -616,15 +505,19 @@ void encodeVectorTile(const DataFacadeBase &facade,
auto tile_line = coordinatesToTileLine(a, b, tile_bbox);
if (!tile_line.empty())
{
encode_tile_line(tile_line,
speed_kmh_idx,
line_int_index.indexOf(forward_rate),
line_int_index.indexOf(forward_weight),
line_int_index.indexOf(forward_duration),
forward_datasource_idx,
line_string_index.indexOf(name),
start_x,
start_y);
SpeedLayerFeatureBuilder fbuilder{speeds_layer, id};
fbuilder.add_linestring_from_container(tile_line);
fbuilder.set_speed(speed_kmh_idx);
fbuilder.set_is_small(component_id.is_tiny);
fbuilder.set_datasource(
facade.GetDatasourceName(forward_datasource_idx).to_string());
fbuilder.set_weight(forward_weight / 10.0);
fbuilder.set_duration(forward_duration / 10.0);
fbuilder.set_name(name);
fbuilder.set_rate(forward_rate / 10.0);
fbuilder.commit();
}
}
@@ -632,12 +525,7 @@ void encodeVectorTile(const DataFacadeBase &facade,
// properties
if (reverse_duration != 0 && edge.reverse_segment_id.enabled)
{
std::int32_t start_x = 0;
std::int32_t start_y = 0;
// Calculate the speed for this line
// Speeds are looked up in a simple 1:1 table, so the speed value == lookup
// table index
std::uint32_t speed_kmh_idx =
static_cast<std::uint32_t>(round(length / reverse_duration * 10 * 3.6));
@@ -650,232 +538,52 @@ void encodeVectorTile(const DataFacadeBase &facade,
auto tile_line = coordinatesToTileLine(b, a, tile_bbox);
if (!tile_line.empty())
{
encode_tile_line(tile_line,
speed_kmh_idx,
line_int_index.indexOf(reverse_rate),
line_int_index.indexOf(reverse_weight),
line_int_index.indexOf(reverse_duration),
reverse_datasource_idx,
line_string_index.indexOf(name),
start_x,
start_y);
SpeedLayerFeatureBuilder fbuilder{speeds_layer, id};
fbuilder.add_linestring_from_container(tile_line);
fbuilder.set_speed(speed_kmh_idx);
fbuilder.set_is_small(component_id.is_tiny);
fbuilder.set_datasource(
facade.GetDatasourceName(reverse_datasource_idx).to_string());
fbuilder.set_weight(reverse_weight / 10.0);
fbuilder.set_duration(reverse_duration / 10.0);
fbuilder.set_name(name);
fbuilder.set_rate(reverse_rate / 10.0);
fbuilder.commit();
}
}
}
}
// Field id 3 is the "keys" attribute
// We need two "key" fields, these are referred to with 0 and 1 (their array
// indexes) earlier
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "speed");
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "is_small");
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "datasource");
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "weight");
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "duration");
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "name");
line_layer_writer.add_string(util::vector_tile::KEY_TAG, "rate");
// 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(line_layer_writer,
util::vector_tile::VARIANT_TAG);
// Attribute value 5 == uint64 type
values_writer.add_uint64(util::vector_tile::VARIANT_TYPE_UINT64, i);
}
{
protozero::pbf_writer values_writer(line_layer_writer,
util::vector_tile::VARIANT_TAG);
// Attribute value 7 == bool type
values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, true);
}
{
protozero::pbf_writer values_writer(line_layer_writer,
util::vector_tile::VARIANT_TAG);
// Attribute value 7 == bool type
values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, false);
}
for (std::size_t i = 0; i <= max_datasource_id; i++)
{
// Writing field type 4 == variant type
protozero::pbf_writer values_writer(line_layer_writer,
util::vector_tile::VARIANT_TAG);
// Attribute value 1 == string type
values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING,
facade.GetDatasourceName(i).to_string());
}
for (auto value : line_int_index.values())
{
// Writing field type 4 == variant type
protozero::pbf_writer values_writer(line_layer_writer,
util::vector_tile::VARIANT_TAG);
// Attribute value 2 == float type
// Durations come out of OSRM in integer deciseconds, so we convert them
// to seconds with a simple /10 for display
values_writer.add_double(util::vector_tile::VARIANT_TYPE_DOUBLE, value / 10.);
}
for (const auto &name : line_string_index.values())
{
// Writing field type 4 == variant type
protozero::pbf_writer values_writer(line_layer_writer,
util::vector_tile::VARIANT_TAG);
// Attribute value 1 == string type
values_writer.add_string(
util::vector_tile::VARIANT_TYPE_STRING, name.data(), name.size());
}
}
// Only add the turn layer to the tile if it has some features (we sometimes won't
// for tiles of z<16, and tiles that don't show any intersections)
if (!all_turn_data.empty())
{
struct EncodedTurnData
TurnsLayer turns_layer{tile};
uint64_t id = 0;
for (const auto &turn_data : all_turn_data)
{
util::Coordinate coordinate;
std::size_t angle_index;
std::size_t turn_index;
std::size_t duration_index;
std::size_t weight_index;
std::size_t turntype_index;
std::size_t turnmodifier_index;
};
// we need to pre-encode all values here because we need the full offsets later
// for encoding the actual features.
std::vector<EncodedTurnData> encoded_turn_data(all_turn_data.size());
std::transform(
all_turn_data.begin(),
all_turn_data.end(),
encoded_turn_data.begin(),
[&](const routing_algorithms::TurnData &t) {
auto angle_idx = point_int_index.add(t.in_angle);
auto turn_idx = point_int_index.add(t.turn_angle);
auto duration_idx =
point_float_index.add(t.duration / 10.0); // Note conversion to float here
auto weight_idx =
point_float_index.add(t.weight / 10.0); // Note conversion to float here
auto turntype_idx = point_string_index.add(
osrm::guidance::internalInstructionTypeToString(t.turn_instruction.type));
auto turnmodifier_idx =
point_string_index.add(osrm::guidance::instructionModifierToString(
t.turn_instruction.direction_modifier));
return EncodedTurnData{t.coordinate,
angle_idx,
turn_idx,
duration_idx,
weight_idx,
turntype_idx,
turnmodifier_idx};
});
// Now write the points layer for turn penalty data:
// Add a layer object to the PBF stream. 3=='layer' from the vector tile spec
// (2.1)
protozero::pbf_writer point_layer_writer(tile_writer, util::vector_tile::LAYER_TAG);
point_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version
point_layer_writer.add_string(util::vector_tile::NAME_TAG, "turns"); // name
point_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG,
util::vector_tile::EXTENT); // extent
// Begin writing the set of point features
{
// Start each features with an ID starting at 1
int id = 1;
// Helper function to encode a new point feature on a vector tile.
const auto encode_tile_point = [&](const FixedPoint &tile_point,
const auto &point_turn_data) {
protozero::pbf_writer feature_writer(point_layer_writer,
util::vector_tile::FEATURE_TAG);
// Field 3 is the "geometry type" field. Value 1 is "point"
feature_writer.add_enum(
util::vector_tile::GEOMETRY_TAG,
util::vector_tile::GEOMETRY_TYPE_POINT); // geometry type
feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id
{
// Write out the 4 properties we want on the feature. These
// refer to indexes in the properties lookup table, which we
// add to the tile after we add all features.
protozero::packed_field_uint32 field(
feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG);
field.add_element(0); // "bearing_in" tag key offset
field.add_element(point_turn_data.angle_index);
field.add_element(1); // "turn_angle" tag key offset
field.add_element(point_turn_data.turn_index);
field.add_element(2); // "cost" tag key offset
field.add_element(point_int_index.size() + point_turn_data.duration_index);
field.add_element(3); // "weight" tag key offset
field.add_element(point_int_index.size() + point_turn_data.weight_index);
field.add_element(4); // "type" tag key offset
field.add_element(point_int_index.size() + point_float_index.size() +
point_turn_data.turntype_index);
field.add_element(5); // "modifier" tag key offset
field.add_element(point_int_index.size() + point_float_index.size() +
point_turn_data.turnmodifier_index);
}
{
// Add the geometry as the last field in this feature
protozero::packed_field_uint32 geometry(
feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG);
encodePoint(tile_point, geometry);
}
};
// Loop over all the turns we found and add them as features to the layer
for (const auto &turndata : encoded_turn_data)
const auto tile_point = coordinatesToTilePoint(turn_data.coordinate, tile_bbox);
if (boost::geometry::within(point_t(tile_point.x, tile_point.y), clip_box))
{
const auto tile_point = coordinatesToTilePoint(turndata.coordinate, tile_bbox);
if (!boost::geometry::within(point_t(tile_point.x, tile_point.y), clip_box))
{
continue;
}
encode_tile_point(tile_point, turndata);
TurnsLayerFeatureBuilder fbuilder{turns_layer, ++id};
fbuilder.add_point(tile_point);
fbuilder.set_bearing_in(turn_data.in_angle);
fbuilder.set_turn_angle(turn_data.turn_angle);
fbuilder.set_cost(turn_data.duration / 10.0);
fbuilder.set_weight(turn_data.weight / 10.0);
fbuilder.set_turn(turn_data.turn_instruction);
fbuilder.commit();
}
}
// Add the names of the three attributes we added to all the turn penalty
// features previously. The indexes used there refer to these keys.
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "bearing_in");
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "turn_angle");
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "cost");
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "weight");
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "type");
point_layer_writer.add_string(util::vector_tile::KEY_TAG, "modifier");
// Now, save the lists of integers and floats that our features refer to.
for (const auto &value : point_int_index.values())
{
protozero::pbf_writer values_writer(point_layer_writer,
util::vector_tile::VARIANT_TAG);
values_writer.add_sint64(util::vector_tile::VARIANT_TYPE_SINT64, value);
}
for (const auto &value : point_float_index.values())
{
protozero::pbf_writer values_writer(point_layer_writer,
util::vector_tile::VARIANT_TAG);
values_writer.add_float(util::vector_tile::VARIANT_TYPE_FLOAT, value);
}
for (const auto &value : point_string_index.values())
{
protozero::pbf_writer values_writer(point_layer_writer,
util::vector_tile::VARIANT_TAG);
values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING, value);
}
}
// OSM Node tile layer
{
protozero::pbf_writer point_layer_writer(tile_writer, util::vector_tile::LAYER_TAG);
point_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version
point_layer_writer.add_string(util::vector_tile::NAME_TAG, "osmnodes"); // name
point_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG,
util::vector_tile::EXTENT); // extent
std::vector<NodeID> internal_nodes;
internal_nodes.reserve(edges.size() * 2);
for (const auto &edge : edges)
@@ -887,6 +595,8 @@ void encodeVectorTile(const DataFacadeBase &facade,
auto new_end = std::unique(internal_nodes.begin(), internal_nodes.end());
internal_nodes.resize(new_end - internal_nodes.begin());
vtzero::layer_builder osmnodes_layer{tile, "osmnodes"};
for (const auto &internal_node : internal_nodes)
{
const auto coord = facade.GetCoordinateOfNode(internal_node);
@@ -895,32 +605,19 @@ void encodeVectorTile(const DataFacadeBase &facade,
{
continue;
}
protozero::pbf_writer feature_writer(point_layer_writer,
util::vector_tile::FEATURE_TAG);
// Field 3 is the "geometry type" field. Value 1 is "point"
feature_writer.add_enum(util::vector_tile::GEOMETRY_TAG,
util::vector_tile::GEOMETRY_TYPE_POINT); // geometry type
const auto osmid =
static_cast<OSMNodeID::value_type>(facade.GetOSMNodeIDOfNode(internal_node));
feature_writer.add_uint64(util::vector_tile::ID_TAG, osmid); // id
// There are no additional properties, just the ID and the geometry
{
// Add the geometry as the last field in this feature
protozero::packed_field_uint32 geometry(
feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG);
encodePoint(tile_point, geometry);
}
vtzero::point_feature_builder fbuilder{osmnodes_layer};
fbuilder.set_id(
static_cast<OSMNodeID::value_type>(facade.GetOSMNodeIDOfNode(internal_node)));
fbuilder.add_point(tile_point);
fbuilder.commit();
}
}
// Internal nodes tile layer
{
protozero::pbf_writer line_layer_writer(tile_writer, util::vector_tile::LAYER_TAG);
line_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version
line_layer_writer.add_string(util::vector_tile::NAME_TAG, "internal-nodes"); // name
line_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG,
util::vector_tile::EXTENT); // extent
vtzero::layer_builder internal_nodes_layer{tile, "internal-nodes"};
unsigned id = 0;
for (auto edgeNodeID : segregated_nodes)
{
auto const geomIndex = facade.GetGeometryIndex(edgeNodeID);
@@ -937,46 +634,21 @@ void encodeVectorTile(const DataFacadeBase &facade,
points.push_back(facade.GetCoordinateOfNode(nodeID));
}
const auto encode_tile_line = [&line_layer_writer, &id](
const FixedLine &tile_line, std::int32_t &start_x, std::int32_t &start_y) {
protozero::pbf_writer feature_writer(line_layer_writer,
util::vector_tile::FEATURE_TAG);
feature_writer.add_enum(util::vector_tile::GEOMETRY_TAG,
util::vector_tile::GEOMETRY_TYPE_LINE); // geometry type
feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id
{
protozero::packed_field_uint32 field(
feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG);
}
{
// Encode the geometry for the feature
protozero::packed_field_uint32 geometry(
feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG);
encodeLinestring(tile_line, geometry, start_x, start_y);
}
};
std::int32_t start_x = 0;
std::int32_t start_y = 0;
auto tile_lines = coordinatesToTileLine(points, tile_bbox);
if (!tile_lines.empty())
{
for (auto const &tl : tile_lines)
vtzero::linestring_feature_builder fbuilder{internal_nodes_layer};
for (auto const &tile_line : tile_lines)
{
encode_tile_line(tl, start_x, start_y);
fbuilder.add_linestring_from_container(tile_line);
}
fbuilder.commit();
}
}
}
}
// protozero serializes data during object destructors, so once the scope closes,
// our result buffer will have all the tile data encoded into it.
tile.serialize(pbf_buffer);
}
}
+24 -19
View File
@@ -131,7 +131,7 @@ void ManipulateTableForFSE(const std::size_t source_id,
result_table.SetValue(destination_id, i, INVALID_EDGE_WEIGHT);
}
// set destination->source to zero so rountrip treats source and
// set destination->source to zero so roundtrip treats source and
// destination as one location
result_table.SetValue(destination_id, source_id, 0);
@@ -216,61 +216,66 @@ Status TripPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
BOOST_ASSERT(snapped_phantoms.size() == number_of_locations);
// compute the duration table of all phantom nodes
auto result_table = util::DistTableWrapper<EdgeWeight>(
algorithms.ManyToManySearch(snapped_phantoms, {}, {}), number_of_locations);
auto result_duration_table = util::DistTableWrapper<EdgeWeight>(
algorithms
.ManyToManySearch(
snapped_phantoms, {}, {}, /*requestDistance*/ false, /*requestDuration*/ true)
.first,
number_of_locations);
if (result_table.size() == 0)
if (result_duration_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,
BOOST_ASSERT_MSG(result_duration_table.size() == number_of_locations * number_of_locations,
"Distance Table has wrong size");
if (!IsStronglyConnectedComponent(result_table))
if (!IsStronglyConnectedComponent(result_duration_table))
{
return Error("NoTrips", "No trip visiting all destinations possible.", json_result);
}
if (fixed_start && fixed_end)
{
ManipulateTableForFSE(source_id, destination_id, result_table);
ManipulateTableForFSE(source_id, destination_id, result_duration_table);
}
std::vector<NodeID> trip;
trip.reserve(number_of_locations);
std::vector<NodeID> duration_trip;
duration_trip.reserve(number_of_locations);
// get an optimized order in which the destinations should be visited
if (number_of_locations < BF_MAX_FEASABLE)
{
trip = trip::BruteForceTrip(number_of_locations, result_table);
duration_trip = trip::BruteForceTrip(number_of_locations, result_duration_table);
}
else
{
trip = trip::FarthestInsertionTrip(number_of_locations, result_table);
duration_trip = trip::FarthestInsertionTrip(number_of_locations, result_duration_table);
}
// rotate result such that roundtrip starts at node with index 0
// thist first if covers scenarios: !fixed_end || fixed_start || (fixed_start && fixed_end)
if (!fixed_end || fixed_start)
{
auto desired_start_index = std::find(std::begin(trip), std::end(trip), 0);
BOOST_ASSERT(desired_start_index != std::end(trip));
std::rotate(std::begin(trip), desired_start_index, std::end(trip));
auto desired_start_index = std::find(std::begin(duration_trip), std::end(duration_trip), 0);
BOOST_ASSERT(desired_start_index != std::end(duration_trip));
std::rotate(std::begin(duration_trip), desired_start_index, std::end(duration_trip));
}
else if (fixed_end && !fixed_start && parameters.roundtrip)
{
auto desired_start_index = std::find(std::begin(trip), std::end(trip), destination_id);
BOOST_ASSERT(desired_start_index != std::end(trip));
std::rotate(std::begin(trip), desired_start_index, std::end(trip));
auto desired_start_index =
std::find(std::begin(duration_trip), std::end(duration_trip), destination_id);
BOOST_ASSERT(desired_start_index != std::end(duration_trip));
std::rotate(std::begin(duration_trip), desired_start_index, std::end(duration_trip));
}
// get the route when visiting all destinations in optimized order
InternalRouteResult route =
ComputeRoute(algorithms, snapped_phantoms, trip, parameters.roundtrip);
ComputeRoute(algorithms, snapped_phantoms, duration_trip, parameters.roundtrip);
// get api response
const std::vector<std::vector<NodeID>> trips = {trip};
const std::vector<std::vector<NodeID>> trips = {duration_trip};
const std::vector<InternalRouteResult> routes = {route};
api::TripAPI trip_api{facade, parameters};
trip_api.MakeResponse(trips, routes, snapped_phantoms, json_result);
@@ -34,22 +34,27 @@ using Facade = DataFacade<Algorithm>;
namespace
{
// Alternative paths candidate via nodes are taken from overlapping search spaces.
// Overlapping by a third guarantees us taking candidate nodes "from the middle".
const constexpr auto kSearchSpaceOverlapFactor = 1.33;
// Unpack n-times more candidate paths to run high-quality checks on.
// Unpacking paths yields higher chance to find good alternatives but is also expensive.
const constexpr auto kAlternativesToUnpackFactor = 2.0;
// Alternative paths length requirement (stretch).
// At most 25% longer then the shortest path.
const constexpr auto kAtMostLongerBy = 0.25;
// Alternative paths similarity requirement (sharing).
// At least 15% different than the shortest path.
const constexpr auto kAtLeastDifferentBy = 0.85;
// Alternative paths are still reasonable around the via node candidate (local optimality).
// At least optimal around 10% sub-paths around the via node candidate.
const /*constexpr*/ auto kAtLeastOptimalAroundViaBy = 0.10;
// gcc 7.1 ICE ^
struct Parameters
{
// Alternative paths candidate via nodes are taken from overlapping search spaces.
// Overlapping by a third guarantees us taking candidate nodes "from the middle".
double kSearchSpaceOverlapFactor = 1.33;
// Unpack n-times more candidate paths to run high-quality checks on.
// Unpacking paths yields higher chance to find good alternatives but is also expensive.
unsigned kAlternativesToUnpackFactor = 2;
// Alternative paths length requirement (stretch).
// At most 25% longer then the shortest path.
double kAtMostLongerBy = 0.25;
// Alternative paths similarity requirement (sharing).
// At least 25% different than the shortest path.
double kAtMostSameBy = 0.75;
// Alternative paths are still reasonable around the via node candidate (local optimality).
// At least optimal around 10% sub-paths around the via node candidate.
double kAtLeastOptimalAroundViaBy = 0.1;
// Alternative paths similarity requirement (sharing) based on calles.
// At least 15% different than the shortest path.
double kCellsAtMostSameBy = 0.95;
};
// Represents a via middle node where forward (from s) and backward (from t)
// search spaces overlap and the weight a path (made up of s,via and via,t) has.
@@ -71,11 +76,104 @@ struct WeightedViaNodePackedPath
// its total weight and the via node used to construct the path.
struct WeightedViaNodeUnpackedPath
{
double sharing;
WeightedViaNode via;
UnpackedNodes nodes;
UnpackedEdges edges;
};
// Scale the maximum allowed weight increase based on its magnitude:
// - Shortest path 10 minutes, alternative 13 minutes => Factor of 0.30 ok
// - Shortest path 10 hours, alternative 13 hours => Factor of 0.30 unreasonable
double getLongerByFactorBasedOnDuration(const EdgeWeight duration)
{
BOOST_ASSERT(duration != INVALID_EDGE_WEIGHT);
// We only have generic weights here and no durations without unpacking.
// We also have restricted way penalties which are huge and will screw scaling here.
//
// Users can pass us generic weights not based on durations; we can't do anything about
// it here other than either generating too many or no alternatives in these cases.
//
// We scale the weights with a step function based on some rough guestimates, so that
// they match tens of minutes, in the low hours, tens of hours, etc.
// Computed using a least-squares curve fitiing on the following values:
//
// def func(xs, a, b, c, d):
// return a + b/(xs-d) + c/(xs-d)**3
//
// xs = np.array([5 * 60, 10 * 60, 30 * 60, 60 * 60, 3 * 60 * 60, 10 * 60 * 60])
// ys = np.array([1.0, 0.75, 0.5, 0.4, 0.3, 0.2])
//
// xs_interp = np.arange(5*60, 10*60*60, 5*60)
// ys_interp = np.interp(xs_interp, xs, ys)
//
// params, _ = scipy.optimize.curve_fit(func, xs_interp, ys_interp)
//
// The hyperbolic shape was chosen because it interpolated well between
// the given datapoints and drops off for large durations.
const constexpr auto a = 1.91578463e-01;
const constexpr auto b = 1.35118442e+03;
const constexpr auto c = 2.45437877e+09;
const constexpr auto d = -2.07944571e+03;
if (duration < EdgeWeight(5 * 60))
{
return 1.0;
}
else if (duration > EdgeWeight(10 * 60 * 60))
{
return 0.20;
}
// Bigger than 10 minutes but smaller than 10 hours
BOOST_ASSERT(duration >= 5 * 60 && duration <= 10 * 60 * 60);
return a + b / (duration - d) + c / std::pow(duration - d, 3);
}
Parameters parametersFromRequest(const PhantomNodes &phantom_node_pair)
{
Parameters parameters;
const auto distance = util::coordinate_calculation::haversineDistance(
phantom_node_pair.source_phantom.location, phantom_node_pair.target_phantom.location);
// 10km
if (distance < 10000.)
{
parameters.kAlternativesToUnpackFactor = 10.0;
parameters.kCellsAtMostSameBy = 1.0;
parameters.kAtLeastOptimalAroundViaBy = 0.2;
parameters.kAtMostSameBy = 0.50;
}
// 20km
else if (distance < 20000.)
{
parameters.kAlternativesToUnpackFactor = 8.0;
parameters.kCellsAtMostSameBy = 1.0;
parameters.kAtLeastOptimalAroundViaBy = 0.2;
parameters.kAtMostSameBy = 0.60;
}
// 50km
else if (distance < 50000.)
{
parameters.kAlternativesToUnpackFactor = 6.0;
parameters.kCellsAtMostSameBy = 0.95;
parameters.kAtMostSameBy = 0.65;
}
// 100km
else if (distance < 100000.)
{
parameters.kAlternativesToUnpackFactor = 4.0;
parameters.kCellsAtMostSameBy = 0.95;
parameters.kAtMostSameBy = 0.70;
}
return parameters;
}
// Filters candidates which are on not unique.
// Returns an iterator to the uniquified range's new end.
// Note: mutates the range in-place invalidating iterators.
@@ -110,49 +208,13 @@ RandIt filterViaCandidatesByRoadImportance(RandIt first, RandIt last, const Faca
return last;
}
// Scale the maximum allowed weight increase based on its magnitude:
// - Shortest path 10 minutes, alternative 13 minutes => Factor of 0.30 ok
// - Shortest path 10 hours, alternative 13 hours => Factor of 0.30 unreasonable
double scaledAtMostLongerByFactorBasedOnDuration(EdgeWeight duration)
{
BOOST_ASSERT(duration != INVALID_EDGE_WEIGHT);
// We only have generic weights here and no durations without unpacking.
// We also have restricted way penalties which are huge and will screw scaling here.
//
// Users can pass us generic weights not based on durations; we can't do anything about
// it here other than either generating too many or no alternatives in these cases.
//
// We scale the weights with a step function based on some rough guestimates, so that
// they match tens of minutes, in the low hours, tens of hours, etc.
// Todo: instead of a piecewise constant function should this be a continuous function?
// At the moment there are "hard" jump edge cases when crossing the thresholds.
auto scaledAtMostLongerBy = kAtMostLongerBy;
const constexpr auto minutes = 60.;
const constexpr auto hours = 60. * minutes;
if (duration < EdgeWeight(10 * minutes))
scaledAtMostLongerBy *= 1.20;
else if (duration < EdgeWeight(30 * minutes))
scaledAtMostLongerBy *= 1.00;
else if (duration < EdgeWeight(1 * hours))
scaledAtMostLongerBy *= 0.90;
else if (duration < EdgeWeight(3 * hours))
scaledAtMostLongerBy *= 0.70;
else if (duration > EdgeWeight(10 * hours))
scaledAtMostLongerBy *= 0.50;
return scaledAtMostLongerBy;
}
// Filters candidates with much higher weight than the primary route. Mutates range in-place.
// Returns an iterator to the filtered range's new end.
template <typename RandIt>
RandIt
filterViaCandidatesByStretch(RandIt first, RandIt last, EdgeWeight weight, double weight_multiplier)
RandIt filterViaCandidatesByStretch(RandIt first,
RandIt last,
const EdgeWeight weight,
const Parameters &parameters)
{
util::static_assert_iter_category<RandIt, std::random_access_iterator_tag>();
util::static_assert_iter_value<RandIt, WeightedViaNode>();
@@ -160,9 +222,7 @@ filterViaCandidatesByStretch(RandIt first, RandIt last, EdgeWeight weight, doubl
// Assumes weight roughly corresponds to duration-ish. If this is not the case e.g.
// because users are setting weight to be distance in the profiles, then we might
// either generate more candidates than we have to or not enough. But is okay.
const auto scaled_at_most_longer_by =
scaledAtMostLongerByFactorBasedOnDuration(weight / weight_multiplier);
const auto stretch_weight_limit = (1. + scaled_at_most_longer_by) * weight;
const auto stretch_weight_limit = (1. + parameters.kAtMostLongerBy) * weight;
const auto over_weight_limit = [=](const auto via) {
return via.weight > stretch_weight_limit;
@@ -198,8 +258,15 @@ filterViaCandidatesByViaNotOnPath(const WeightedViaNodePackedPath &path, RandIt
// Filters packed paths with similar cells between each other. Mutates range in-place.
// Returns an iterator to the filtered range's new end.
template <typename RandIt>
RandIt filterPackedPathsByCellSharing(RandIt first, RandIt last, const Partition &partition)
RandIt filterPackedPathsByCellSharing(RandIt first,
RandIt last,
const Partition &partition,
const Parameters &parameters)
{
// In this case we don't need to calculate sharing, because it would not filter anything
if (parameters.kCellsAtMostSameBy >= 1.0)
return last;
util::static_assert_iter_category<RandIt, std::random_access_iterator_tag>();
util::static_assert_iter_value<RandIt, WeightedViaNodePackedPath>();
@@ -226,14 +293,13 @@ RandIt filterPackedPathsByCellSharing(RandIt first, RandIt last, const Partition
return last;
std::unordered_set<CellID> cells;
cells.reserve(size * (shortest_path.path.size() + 1) * (1. + kAtMostLongerBy));
cells.reserve(size * (shortest_path.path.size() + 1) * (1 + parameters.kAtMostLongerBy));
cells.insert(get_cell(std::get<0>(shortest_path.path.front())));
for (const auto &edge : shortest_path.path)
cells.insert(get_cell(std::get<1>(edge)));
const auto over_sharing_limit = [&](const auto &packed) {
if (packed.path.empty())
{ // don't remove routes with single-node (empty) path
return false;
@@ -253,7 +319,7 @@ RandIt filterPackedPathsByCellSharing(RandIt first, RandIt last, const Partition
const auto sharing = 1. - difference;
if (sharing > kAtLeastDifferentBy)
if (sharing > parameters.kCellsAtMostSameBy)
{
return true;
}
@@ -277,7 +343,8 @@ RandIt filterPackedPathsByLocalOptimality(const WeightedViaNodePackedPath &path,
const Heap &forward_heap,
const Heap &reverse_heap,
RandIt first,
RandIt last)
RandIt last,
const Parameters &parameters)
{
util::static_assert_iter_category<RandIt, std::random_access_iterator_tag>();
util::static_assert_iter_value<RandIt, WeightedViaNodePackedPath>();
@@ -376,7 +443,7 @@ RandIt filterPackedPathsByLocalOptimality(const WeightedViaNodePackedPath &path,
const auto detour_length = forward_heap.GetKey(via) - forward_heap.GetKey(a) +
reverse_heap.GetKey(via) - reverse_heap.GetKey(b);
return plateaux_length < kAtLeastOptimalAroundViaBy * detour_length;
return plateaux_length < parameters.kAtLeastOptimalAroundViaBy * detour_length;
};
return std::remove_if(first, last, is_not_locally_optimal);
@@ -384,7 +451,11 @@ RandIt filterPackedPathsByLocalOptimality(const WeightedViaNodePackedPath &path,
// Filters unpacked paths compared to all other paths. Mutates range in-place.
// Returns an iterator to the filtered range's new end.
template <typename RandIt> RandIt filterUnpackedPathsBySharing(RandIt first, RandIt last)
template <typename RandIt>
RandIt filterUnpackedPathsBySharing(RandIt first,
RandIt last,
const Facade &facade,
const Parameters &parameters)
{
util::static_assert_iter_category<RandIt, std::random_access_iterator_tag>();
util::static_assert_iter_value<RandIt, WeightedViaNodeUnpackedPath>();
@@ -399,46 +470,59 @@ template <typename RandIt> RandIt filterUnpackedPathsBySharing(RandIt first, Ran
if (shortest_path.edges.empty())
return last;
std::unordered_set<EdgeID> edges;
edges.reserve(size * shortest_path.edges.size() * (1. + kAtMostLongerBy));
std::unordered_set<NodeID> nodes;
nodes.reserve(size * shortest_path.nodes.size() * (1.25));
edges.insert(begin(shortest_path.edges), begin(shortest_path.edges));
const auto over_sharing_limit = [&](const auto &unpacked) {
nodes.insert(begin(shortest_path.nodes), end(shortest_path.nodes));
const auto over_sharing_limit = [&](auto &unpacked) {
if (unpacked.edges.empty())
{ // don't remove routes with single-node (empty) path
return false;
}
const auto not_seen = [&](const EdgeID edge) { return edges.count(edge) < 1; };
const auto different = std::count_if(begin(unpacked.edges), end(unpacked.edges), not_seen);
EdgeWeight total_duration = 0;
const auto add_if_seen = [&](const EdgeWeight duration, const NodeID node) {
auto node_duration = facade.GetNodeDuration(node);
total_duration += node_duration;
if (nodes.count(node) > 0)
{
return duration + node_duration;
}
return duration;
};
const auto difference = different / static_cast<double>(unpacked.edges.size());
BOOST_ASSERT(difference >= 0.);
BOOST_ASSERT(difference <= 1.);
const auto shared_duration = std::accumulate(
begin(unpacked.nodes), end(unpacked.nodes), EdgeDuration{0}, add_if_seen);
const auto sharing = 1. - difference;
unpacked.sharing = shared_duration / static_cast<double>(total_duration);
BOOST_ASSERT(unpacked.sharing >= 0.);
BOOST_ASSERT(unpacked.sharing <= 1.);
if (sharing > kAtLeastDifferentBy)
if (unpacked.sharing > parameters.kAtMostSameBy)
{
return true;
}
else
{
edges.insert(begin(unpacked.edges), end(unpacked.edges));
nodes.insert(begin(unpacked.nodes), end(unpacked.nodes));
return false;
}
};
return std::remove_if(first + 1, last, over_sharing_limit);
auto end = std::remove_if(first + 1, last, over_sharing_limit);
std::sort(
first + 1, end, [](const auto &lhs, const auto &rhs) { return lhs.sharing < rhs.sharing; });
return end;
}
// Filters annotated routes by stretch based on duration. Mutates range in-place.
// Returns an iterator to the filtered range's new end.
template <typename RandIt>
RandIt
filterAnnotatedRoutesByStretch(RandIt first, RandIt last, const InternalRouteResult &shortest_route)
RandIt filterAnnotatedRoutesByStretch(RandIt first,
RandIt last,
const InternalRouteResult &shortest_route,
const Parameters &parameters)
{
util::static_assert_iter_category<RandIt, std::random_access_iterator_tag>();
util::static_assert_iter_value<RandIt, InternalRouteResult>();
@@ -446,9 +530,7 @@ filterAnnotatedRoutesByStretch(RandIt first, RandIt last, const InternalRouteRes
BOOST_ASSERT(shortest_route.is_valid());
const auto shortest_route_duration = shortest_route.duration();
const auto scaled_at_most_longer_by =
scaledAtMostLongerByFactorBasedOnDuration(shortest_route_duration);
const auto stretch_duration_limit = (1. + scaled_at_most_longer_by) * shortest_route_duration;
const auto stretch_duration_limit = (1. + parameters.kAtMostLongerBy) * shortest_route_duration;
const auto over_duration_limit = [=](const auto &route) {
return route.duration() > stretch_duration_limit;
@@ -559,6 +641,7 @@ void unpackPackedPaths(InputIt first,
}
WeightedViaNodeUnpackedPath unpacked_path{
0.0,
WeightedViaNode{packed_path_via, packed_path_weight},
std::move(unpacked_nodes),
std::move(unpacked_edges)};
@@ -573,7 +656,8 @@ void unpackPackedPaths(InputIt first,
inline std::vector<WeightedViaNode>
makeCandidateVias(SearchEngineData<Algorithm> &search_engine_data,
const Facade &facade,
const PhantomNodes &phantom_node_pair)
const PhantomNodes &phantom_node_pair,
const Parameters &parameters)
{
Heap &forward_heap = *search_engine_data.forward_heap_1;
Heap &reverse_heap = *search_engine_data.reverse_heap_1;
@@ -605,7 +689,7 @@ makeCandidateVias(SearchEngineData<Algorithm> &search_engine_data,
while (forward_heap.Size() + reverse_heap.Size() > 0)
{
if (shortest_path_weight != INVALID_EDGE_WEIGHT)
overlap_weight = shortest_path_weight * kSearchSpaceOverlapFactor;
overlap_weight = shortest_path_weight * parameters.kSearchSpaceOverlapFactor;
// Termination criteria - when we have a shortest path this will guarantee for our overlap.
const bool keep_going = forward_heap_min + reverse_heap_min < overlap_weight;
@@ -670,7 +754,7 @@ makeCandidateVias(SearchEngineData<Algorithm> &search_engine_data,
return candidate_vias;
}
} // anon. ns
} // namespace
// Alternative Routes for MLD.
//
@@ -691,9 +775,11 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
const PhantomNodes &phantom_node_pair,
unsigned number_of_alternatives)
{
Parameters parameters = parametersFromRequest(phantom_node_pair);
const auto max_number_of_alternatives = number_of_alternatives;
const auto max_number_of_alternatives_to_unpack =
kAlternativesToUnpackFactor * max_number_of_alternatives;
parameters.kAlternativesToUnpackFactor * max_number_of_alternatives;
BOOST_ASSERT(max_number_of_alternatives > 0);
BOOST_ASSERT(max_number_of_alternatives_to_unpack >= max_number_of_alternatives);
@@ -707,7 +793,8 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
Heap &reverse_heap = *search_engine_data.reverse_heap_1;
// Do forward and backward search, save search space overlap as via candidates.
auto candidate_vias = makeCandidateVias(search_engine_data, facade, phantom_node_pair);
auto candidate_vias =
makeCandidateVias(search_engine_data, facade, phantom_node_pair, parameters);
const auto by_weight = [](const auto &lhs, const auto &rhs) { return lhs.weight < rhs.weight; };
auto shortest_path_via_it =
@@ -730,6 +817,9 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
NodeID shortest_path_via = shortest_path_via_it->node;
EdgeWeight shortest_path_weight = shortest_path_via_it->weight;
const double duration_estimation = shortest_path_weight / facade.GetWeightMultiplier();
parameters.kAtMostLongerBy = getLongerByFactorBasedOnDuration(duration_estimation);
// Filters via candidate nodes with heuristics
// Note: filter pipeline below only makes range smaller; no need to erase items
@@ -738,8 +828,7 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
it = filterViaCandidatesByUniqueNodeIds(begin(candidate_vias), it);
it = filterViaCandidatesByRoadImportance(begin(candidate_vias), it, facade);
it = filterViaCandidatesByStretch(
begin(candidate_vias), it, shortest_path_weight, facade.GetWeightMultiplier());
it = filterViaCandidatesByStretch(begin(candidate_vias), it, shortest_path_weight, parameters);
// Pre-rank by weight; sharing filtering below then discards by similarity.
std::sort(begin(candidate_vias), it, [](const auto lhs, const auto rhs) {
@@ -783,10 +872,10 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
forward_heap, // paths for s, via
reverse_heap, // paths for via, t
begin(weighted_packed_paths) + 1,
alternative_paths_last);
alternative_paths_last,
parameters);
alternative_paths_last = filterPackedPathsByCellSharing(
begin(weighted_packed_paths), alternative_paths_last, partition);
begin(weighted_packed_paths), alternative_paths_last, partition, parameters);
BOOST_ASSERT(weighted_packed_paths.size() >= 1);
@@ -816,7 +905,8 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
auto unpacked_paths_last = end(unpacked_paths);
unpacked_paths_last = filterUnpackedPathsBySharing(begin(unpacked_paths), end(unpacked_paths));
unpacked_paths_last = filterUnpackedPathsBySharing(
begin(unpacked_paths), end(unpacked_paths), facade, parameters);
const auto unpacked_paths_first = begin(unpacked_paths);
const auto number_of_unpacked_paths =
@@ -850,7 +940,9 @@ InternalManyRoutesResult alternativePathSearch(SearchEngineData<Algorithm> &sear
if (routes.size() > 1)
{
routes_last = filterAnnotatedRoutesByStretch(routes_first + 1, routes_last, *routes_first);
parameters.kAtMostLongerBy = getLongerByFactorBasedOnDuration(routes_first->duration());
routes_last = filterAnnotatedRoutesByStretch(
routes_first + 1, routes_last, *routes_first, parameters);
routes.erase(routes_last, end(routes));
}
@@ -1,5 +1,4 @@
#include "engine/routing_algorithms/direct_shortest_path.hpp"
#include "engine/routing_algorithms/routing_base.hpp"
#include "engine/routing_algorithms/routing_base_ch.hpp"
#include "engine/routing_algorithms/routing_base_mld.hpp"
+208 -24
View File
@@ -60,8 +60,8 @@ void relaxOutgoingEdges(const DataFacade<Algorithm> &facade,
if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward)
{
const NodeID to = facade.GetTarget(edge);
const auto edge_weight = data.weight;
const auto edge_duration = data.duration;
BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid");
@@ -85,12 +85,13 @@ void relaxOutgoingEdges(const DataFacade<Algorithm> &facade,
}
void forwardRoutingStep(const DataFacade<Algorithm> &facade,
const unsigned row_idx,
const unsigned number_of_targets,
const std::size_t row_index,
const std::size_t number_of_targets,
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
const std::vector<NodeBucket> &search_space_with_buckets,
std::vector<EdgeWeight> &weights_table,
std::vector<EdgeDuration> &durations_table,
std::vector<NodeID> &middle_nodes_table,
const PhantomNode &phantom_node)
{
const auto node = query_heap.DeleteMin();
@@ -105,12 +106,12 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
for (const auto &current_bucket : boost::make_iterator_range(bucket_list))
{
// Get target id from bucket entry
const auto column_idx = current_bucket.column_index;
const auto column_index = current_bucket.column_index;
const auto target_weight = current_bucket.weight;
const auto target_duration = current_bucket.duration;
auto &current_weight = weights_table[row_idx * number_of_targets + column_idx];
auto &current_duration = durations_table[row_idx * number_of_targets + column_idx];
auto &current_weight = weights_table[row_index * number_of_targets + column_index];
auto &current_duration = durations_table[row_index * number_of_targets + column_index];
// Check if new weight is better
auto new_weight = source_weight + target_weight;
@@ -122,12 +123,14 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
{
current_weight = std::min(current_weight, new_weight);
current_duration = std::min(current_duration, new_duration);
middle_nodes_table[row_index * number_of_targets + column_index] = node;
}
}
else if (std::tie(new_weight, new_duration) < std::tie(current_weight, current_duration))
{
current_weight = new_weight;
current_duration = new_duration;
middle_nodes_table[row_index * number_of_targets + column_index] = node;
}
}
@@ -136,7 +139,7 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
}
void backwardRoutingStep(const DataFacade<Algorithm> &facade,
const unsigned column_idx,
const unsigned column_index,
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
std::vector<NodeBucket> &search_space_with_buckets,
const PhantomNode &phantom_node)
@@ -144,9 +147,11 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
const auto node = query_heap.DeleteMin();
const auto target_weight = query_heap.GetKey(node);
const auto target_duration = query_heap.GetData(node).duration;
const auto parent = query_heap.GetData(node).parent;
// Store settled nodes in search space bucket
search_space_with_buckets.emplace_back(node, column_idx, target_weight, target_duration);
search_space_with_buckets.emplace_back(
node, parent, column_index, target_weight, target_duration);
relaxOutgoingEdges<REVERSE_DIRECTION>(
facade, node, target_weight, target_duration, query_heap, phantom_node);
@@ -154,26 +159,187 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
} // namespace ch
template <>
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<ch::Algorithm> &engine_working_data,
const DataFacade<ch::Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices)
void retrievePackedPathFromSearchSpace(const NodeID middle_node_id,
const unsigned column_index,
const std::vector<NodeBucket> &search_space_with_buckets,
std::vector<NodeID> &packed_leg)
{
auto bucket_list = std::equal_range(search_space_with_buckets.begin(),
search_space_with_buckets.end(),
middle_node_id,
NodeBucket::ColumnCompare(column_index));
NodeID current_node_id = middle_node_id;
BOOST_ASSERT_MSG(std::distance(bucket_list.first, bucket_list.second) == 1,
"The pointers are not pointing to the same element.");
while (bucket_list.first->parent_node != current_node_id &&
bucket_list.first != search_space_with_buckets.end())
{
current_node_id = bucket_list.first->parent_node;
packed_leg.emplace_back(current_node_id);
bucket_list = std::equal_range(search_space_with_buckets.begin(),
search_space_with_buckets.end(),
current_node_id,
NodeBucket::ColumnCompare(column_index));
}
}
void calculateDistances(typename SearchEngineData<ch::Algorithm>::ManyToManyQueryHeap &query_heap,
const DataFacade<ch::Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &target_indices,
const std::size_t row_index,
const std::size_t source_index,
const PhantomNode &source_phantom,
const std::size_t number_of_targets,
const std::vector<NodeBucket> &search_space_with_buckets,
std::vector<EdgeDistance> &distances_table,
const std::vector<NodeID> &middle_nodes_table)
{
std::vector<NodeID> packed_leg;
for (auto column_index : util::irange<std::size_t>(0, number_of_targets))
{
const auto target_index = target_indices[column_index];
const auto &target_phantom = phantom_nodes[target_index];
if (source_index == target_index)
{
distances_table[row_index * number_of_targets + column_index] = 0.0;
continue;
}
NodeID middle_node_id = middle_nodes_table[row_index * number_of_targets + column_index];
if (middle_node_id == SPECIAL_NODEID) // takes care of one-ways
{
distances_table[row_index * number_of_targets + column_index] = INVALID_EDGE_DISTANCE;
continue;
}
// Step 1: Find path from source to middle node
ch::retrievePackedPathFromSingleManyToManyHeap(query_heap, middle_node_id, packed_leg);
std::reverse(packed_leg.begin(), packed_leg.end());
packed_leg.push_back(middle_node_id);
// Step 2: Find path from middle to target node
retrievePackedPathFromSearchSpace(
middle_node_id, column_index, search_space_with_buckets, packed_leg);
if (packed_leg.size() == 1 && (needsLoopForward(source_phantom, target_phantom) ||
needsLoopBackwards(source_phantom, target_phantom)))
{
auto weight = ch::getLoopWeight<false>(facade, packed_leg.front());
if (weight != INVALID_EDGE_WEIGHT)
packed_leg.push_back(packed_leg.front());
}
if (!packed_leg.empty())
{
auto annotation =
ch::calculateEBGNodeAnnotations(facade, packed_leg.begin(), packed_leg.end());
distances_table[row_index * number_of_targets + column_index] = annotation;
// check the direction of travel to figure out how to calculate the offset to/from
// the source/target
if (source_phantom.forward_segment_id.id == packed_leg.front())
{
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
// -->s <-- subtract offset to start at source
// ......... <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
EdgeDistance offset = source_phantom.GetForwardDistance();
distances_table[row_index * number_of_targets + column_index] -= offset;
}
else if (source_phantom.reverse_segment_id.id == packed_leg.front())
{
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
// s<------- <-- subtract offset to start at source
// ... <-- want this distance
// entry 0---1---2---3 <-- 3 is exit node
EdgeDistance offset = source_phantom.GetReverseDistance();
distances_table[row_index * number_of_targets + column_index] -= offset;
}
if (target_phantom.forward_segment_id.id == packed_leg.back())
{
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
// ++>t <-- add offset to get to target
// ................ <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
EdgeDistance offset = target_phantom.GetForwardDistance();
distances_table[row_index * number_of_targets + column_index] += offset;
}
else if (target_phantom.reverse_segment_id.id == packed_leg.back())
{
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
// <++t <-- add offset to get from target
// ................ <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
EdgeDistance offset = target_phantom.GetReverseDistance();
distances_table[row_index * number_of_targets + column_index] += offset;
}
}
else
{
// there is no shortcut to unpack. source and target are on the same EBG Node.
// if the offset of the target is greater than the offset of the source, subtract it
if (target_phantom.GetForwardDistance() > source_phantom.GetForwardDistance())
{
// --------->t <-- offsets
// ->s <-- subtract source offset from target offset
// ......... <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
EdgeDistance offset =
target_phantom.GetForwardDistance() - source_phantom.GetForwardDistance();
distances_table[row_index * number_of_targets + column_index] = offset;
}
else
{
// s<--- <-- offsets
// t<--------- <-- subtract source offset from target offset
// ...... <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
EdgeDistance offset =
target_phantom.GetReverseDistance() - source_phantom.GetReverseDistance();
distances_table[row_index * number_of_targets + column_index] = offset;
}
}
packed_leg.clear();
}
}
template <>
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
manyToManySearch(SearchEngineData<ch::Algorithm> &engine_working_data,
const DataFacade<ch::Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance,
const bool calculate_duration)
{
(void)calculate_duration; // TODO: stub to use when computing durations become optional
const auto number_of_sources = source_indices.size();
const auto number_of_targets = target_indices.size();
const auto number_of_entries = number_of_sources * number_of_targets;
std::vector<EdgeWeight> weights_table(number_of_entries, INVALID_EDGE_WEIGHT);
std::vector<EdgeDuration> durations_table(number_of_entries, MAXIMAL_EDGE_DURATION);
std::vector<EdgeDistance> distances_table;
std::vector<NodeID> middle_nodes_table(number_of_entries, SPECIAL_NODEID);
std::vector<NodeBucket> search_space_with_buckets;
// Populate buckets with paths from all accessible nodes to destinations via backward searches
for (std::uint32_t column_idx = 0; column_idx < target_indices.size(); ++column_idx)
for (std::uint32_t column_index = 0; column_index < target_indices.size(); ++column_index)
{
const auto index = target_indices[column_idx];
const auto index = target_indices[column_index];
const auto &phantom = phantom_nodes[index];
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
@@ -184,7 +350,8 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<ch::Algorithm> &engi
// Explore search space
while (!query_heap.Empty())
{
backwardRoutingStep(facade, column_idx, query_heap, search_space_with_buckets, phantom);
backwardRoutingStep(
facade, column_index, query_heap, search_space_with_buckets, phantom);
}
}
@@ -192,32 +359,49 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<ch::Algorithm> &engi
std::sort(search_space_with_buckets.begin(), search_space_with_buckets.end());
// Find shortest paths from sources to all accessible nodes
for (std::uint32_t row_idx = 0; row_idx < source_indices.size(); ++row_idx)
for (std::uint32_t row_index = 0; row_index < source_indices.size(); ++row_index)
{
const auto index = source_indices[row_idx];
const auto &phantom = phantom_nodes[index];
const auto source_index = source_indices[row_index];
const auto &source_phantom = phantom_nodes[source_index];
// Clear heap and insert source nodes
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
facade.GetNumberOfNodes());
auto &query_heap = *(engine_working_data.many_to_many_heap);
insertSourceInHeap(query_heap, phantom);
insertSourceInHeap(query_heap, source_phantom);
// Explore search space
while (!query_heap.Empty())
{
forwardRoutingStep(facade,
row_idx,
row_index,
number_of_targets,
query_heap,
search_space_with_buckets,
weights_table,
durations_table,
phantom);
middle_nodes_table,
source_phantom);
}
if (calculate_distance)
{
distances_table.resize(number_of_entries, INVALID_EDGE_DISTANCE);
calculateDistances(query_heap,
facade,
phantom_nodes,
target_indices,
row_index,
source_index,
source_phantom,
number_of_targets,
search_space_with_buckets,
distances_table,
middle_nodes_table);
}
}
return durations_table;
return std::make_pair(durations_table, distances_table);
}
} // namespace routing_algorithms
+476 -102
View File
@@ -1,5 +1,5 @@
#include "engine/routing_algorithms/many_to_many.hpp"
#include "engine/routing_algorithms/routing_base.hpp"
#include "engine/routing_algorithms/routing_base_mld.hpp"
#include <boost/assert.hpp>
#include <boost/range/iterator_range_core.hpp>
@@ -19,22 +19,8 @@ namespace routing_algorithms
namespace mld
{
template <typename MultiLevelPartition>
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
const NodeID node,
const PhantomNode &phantom_node)
{
auto highest_diffrent_level = [&partition, node](const SegmentID &phantom_node) {
if (phantom_node.enabled)
return partition.GetHighestDifferentLevel(phantom_node.id, node);
return INVALID_LEVEL_ID;
};
const auto node_level = std::min(highest_diffrent_level(phantom_node.forward_segment_id),
highest_diffrent_level(phantom_node.reverse_segment_id));
return node_level;
}
using PackedEdge = std::tuple</*from*/ NodeID, /*to*/ NodeID, /*from_clique_arc*/ bool>;
using PackedPath = std::vector<PackedEdge>;
template <typename MultiLevelPartition>
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
@@ -50,38 +36,6 @@ inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
return node_level;
}
template <typename MultiLevelPartition>
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
NodeID node,
const std::vector<PhantomNode> &phantom_nodes,
const std::size_t phantom_index,
const std::vector<std::size_t> &phantom_indices)
{
auto min_level = [&partition, node](const PhantomNode &phantom_node) {
const auto &forward_segment = phantom_node.forward_segment_id;
const auto forward_level =
forward_segment.enabled ? partition.GetHighestDifferentLevel(node, forward_segment.id)
: INVALID_LEVEL_ID;
const auto &reverse_segment = phantom_node.reverse_segment_id;
const auto reverse_level =
reverse_segment.enabled ? partition.GetHighestDifferentLevel(node, reverse_segment.id)
: INVALID_LEVEL_ID;
return std::min(forward_level, reverse_level);
};
// Get minimum level over all phantoms of the highest different level with respect to node
// This is equivalent to min_{∀ source, target} partition.GetQueryLevel(source, node, target)
auto result = min_level(phantom_nodes[phantom_index]);
for (const auto &index : phantom_indices)
{
result = std::min(result, min_level(phantom_nodes[index]));
}
return result;
}
template <bool DIRECTION, typename... Args>
void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
const NodeID node,
@@ -125,8 +79,10 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
{
query_heap.Insert(to, to_weight, {node, true, to_duration});
}
else if (std::tie(to_weight, to_duration) <
std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration))
else if (std::tie(to_weight, to_duration, node) <
std::tie(query_heap.GetKey(to),
query_heap.GetData(to).duration,
query_heap.GetData(to).parent))
{
query_heap.GetData(to) = {node, true, to_duration};
query_heap.DecreaseKey(to, to_weight);
@@ -155,8 +111,10 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
{
query_heap.Insert(to, to_weight, {node, true, to_duration});
}
else if (std::tie(to_weight, to_duration) <
std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration))
else if (std::tie(to_weight, to_duration, node) <
std::tie(query_heap.GetKey(to),
query_heap.GetData(to).duration,
query_heap.GetData(to).parent))
{
query_heap.GetData(to) = {node, true, to_duration};
query_heap.DecreaseKey(to, to_weight);
@@ -172,7 +130,8 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
for (const auto edge : facade.GetBorderEdgeRange(level, node))
{
const auto &data = facade.GetEdgeData(edge);
if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward)
if ((DIRECTION == FORWARD_DIRECTION) ? facade.IsForwardEdge(edge)
: facade.IsBackwardEdge(edge))
{
const NodeID to = facade.GetTarget(edge);
if (facade.ExcludeNode(to))
@@ -180,12 +139,16 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
continue;
}
const auto edge_weight = data.weight;
const auto edge_duration = data.duration;
const auto turn_id = data.turn_id;
const auto node_id = DIRECTION == FORWARD_DIRECTION ? node : facade.GetTarget(edge);
const auto node_weight = facade.GetNodeWeight(node_id);
const auto node_duration = facade.GetNodeDuration(node_id);
const auto turn_weight = node_weight + facade.GetWeightPenaltyForEdgeID(turn_id);
const auto turn_duration = node_duration + facade.GetDurationPenaltyForEdgeID(turn_id);
BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid");
const auto to_weight = weight + edge_weight;
const auto to_duration = duration + edge_duration;
BOOST_ASSERT_MSG(node_weight + turn_weight > 0, "edge weight is invalid");
const auto to_weight = weight + turn_weight;
const auto to_duration = duration + turn_duration;
// New Node discovered -> Add to Heap + Node Info Storage
if (!query_heap.WasInserted(to))
@@ -193,8 +156,10 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
query_heap.Insert(to, to_weight, {node, false, to_duration});
}
// Found a shorter Path -> Update weight and set new parent
else if (std::tie(to_weight, to_duration) <
std::tie(query_heap.GetKey(to), query_heap.GetData(to).duration))
else if (std::tie(to_weight, to_duration, node) <
std::tie(query_heap.GetKey(to),
query_heap.GetData(to).duration,
query_heap.GetData(to).parent))
{
query_heap.GetData(to) = {node, false, to_duration};
query_heap.DecreaseKey(to, to_weight);
@@ -207,14 +172,18 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
// Unidirectional multi-layer Dijkstra search for 1-to-N and N-to-1 matrices
//
template <bool DIRECTION>
std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
std::size_t phantom_index,
const std::vector<std::size_t> &phantom_indices)
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
std::size_t phantom_index,
const std::vector<std::size_t> &phantom_indices,
const bool calculate_distance)
{
std::vector<EdgeWeight> weights(phantom_indices.size(), INVALID_EDGE_WEIGHT);
std::vector<EdgeDuration> durations(phantom_indices.size(), MAXIMAL_EDGE_DURATION);
std::vector<EdgeDistance> distances_table;
std::vector<NodeID> middle_nodes_table(phantom_indices.size(), SPECIAL_NODEID);
// Collect destination (source) nodes into a map
std::unordered_multimap<NodeID, std::tuple<std::size_t, EdgeWeight, EdgeDuration>>
@@ -282,6 +251,7 @@ std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_wo
{
weights[index] = path_weight;
durations[index] = path_duration;
middle_nodes_table[index] = node;
}
// Remove node from destinations list
@@ -294,21 +264,36 @@ std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_wo
}
};
// Check a single path result and insert adjacent nodes into heap
auto insert_node = [&](NodeID node, EdgeWeight initial_weight, EdgeDuration initial_duration) {
// Update single node paths
update_values(node, initial_weight, initial_duration);
query_heap.Insert(node, initial_weight, {node, initial_duration});
// Place adjacent nodes into heap
for (auto edge : facade.GetAdjacentEdgeRange(node))
{
const auto &data = facade.GetEdgeData(edge);
if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward)
const auto to = facade.GetTarget(edge);
if (facade.ExcludeNode(to))
{
query_heap.Insert(facade.GetTarget(edge),
data.weight + initial_weight,
{node, data.duration + initial_duration});
continue;
}
if ((DIRECTION == FORWARD_DIRECTION ? facade.IsForwardEdge(edge)
: facade.IsBackwardEdge(edge)) &&
!query_heap.WasInserted(to))
{
const auto turn_id = data.turn_id;
const auto node_id = DIRECTION == FORWARD_DIRECTION ? node : to;
const auto edge_weight = initial_weight + facade.GetNodeWeight(node_id) +
facade.GetWeightPenaltyForEdgeID(turn_id);
const auto edge_duration = initial_duration + facade.GetNodeDuration(node_id) +
facade.GetDurationPenaltyForEdgeID(turn_id);
query_heap.Insert(to, edge_weight, {node, edge_duration});
}
}
};
@@ -318,28 +303,35 @@ std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_wo
if (DIRECTION == FORWARD_DIRECTION)
{
if (phantom_node.IsValidForwardSource())
{
insert_node(phantom_node.forward_segment_id.id,
-phantom_node.GetForwardWeightPlusOffset(),
-phantom_node.GetForwardDuration());
}
if (phantom_node.IsValidReverseSource())
{
insert_node(phantom_node.reverse_segment_id.id,
-phantom_node.GetReverseWeightPlusOffset(),
-phantom_node.GetReverseDuration());
}
}
else if (DIRECTION == REVERSE_DIRECTION)
{
if (phantom_node.IsValidForwardTarget())
{
insert_node(phantom_node.forward_segment_id.id,
phantom_node.GetForwardWeightPlusOffset(),
phantom_node.GetForwardDuration());
}
if (phantom_node.IsValidReverseTarget())
{
insert_node(phantom_node.reverse_segment_id.id,
phantom_node.GetReverseWeightPlusOffset(),
phantom_node.GetReverseDuration());
}
}
}
@@ -364,7 +356,127 @@ std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_wo
phantom_indices);
}
return durations;
if (calculate_distance)
{
// Initialize unpacking heaps
engine_working_data.InitializeOrClearFirstThreadLocalStorage(
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
distances_table.resize(phantom_indices.size(), INVALID_EDGE_DISTANCE);
for (unsigned location = 0; location < phantom_indices.size(); ++location)
{
// Get the "middle" node that is the last node of a path
const NodeID middle_node_id = middle_nodes_table[location];
if (middle_node_id == SPECIAL_NODEID) // takes care of one-ways
{
continue;
}
// Retrieve the packed path from the heap
PackedPath packed_path = mld::retrievePackedPathFromSingleManyToManyHeap<DIRECTION>(
query_heap, middle_node_id);
// ... and reverse it to have packed edges in the correct order,
if (DIRECTION == FORWARD_DIRECTION)
{
std::reverse(packed_path.begin(), packed_path.end());
}
// ... unpack path
auto &forward_heap = *engine_working_data.forward_heap_1;
auto &reverse_heap = *engine_working_data.reverse_heap_1;
EdgeWeight weight = INVALID_EDGE_WEIGHT;
std::vector<NodeID> unpacked_nodes;
std::vector<EdgeID> unpacked_edges;
std::tie(weight, unpacked_nodes, unpacked_edges) =
unpackPathAndCalculateDistance(engine_working_data,
facade,
forward_heap,
reverse_heap,
DO_NOT_FORCE_LOOPS,
DO_NOT_FORCE_LOOPS,
INVALID_EDGE_WEIGHT,
packed_path,
middle_node_id,
phantom_nodes,
phantom_index,
phantom_indices);
// Accumulate the path length without the last node
auto annotation = 0.0;
BOOST_ASSERT(!unpacked_nodes.empty());
for (auto node = unpacked_nodes.begin(), last_node = std::prev(unpacked_nodes.end());
node != last_node;
++node)
{
annotation += computeEdgeDistance(facade, *node);
}
// ... and add negative source and positive target offsets
// ⚠ for REVERSE_DIRECTION original source and target phantom nodes are swapped
// Get source and target phantom nodes
// * 1-to-N: source is a single index, target is the corresponding from the indices list
// * N-to-1: source is the corresponding from the indices list, target is a single index
auto source_phantom_index = phantom_index;
auto target_phantom_index = phantom_indices[location];
if (DIRECTION == REVERSE_DIRECTION)
{
std::swap(source_phantom_index, target_phantom_index);
}
const auto &source_phantom = phantom_nodes[source_phantom_index];
const auto &target_phantom = phantom_nodes[target_phantom_index];
const NodeID source_node = unpacked_nodes.front();
const NodeID target_node = unpacked_nodes.back();
EdgeDistance source_offset = 0., target_offset = 0.;
if (source_phantom.IsValidForwardSource() &&
source_phantom.forward_segment_id.id == source_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0
// to 3
// -->s <-- subtract offset to start at source
// ......... <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
source_offset = source_phantom.GetForwardDistance();
}
else if (source_phantom.IsValidReverseSource() &&
source_phantom.reverse_segment_id.id == source_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
// s<------- <-- subtract offset to start at source
// ... <-- want this distance
// entry 0---1---2---3 <-- 3 is exit node
source_offset = source_phantom.GetReverseDistance();
}
if (target_phantom.IsValidForwardTarget() &&
target_phantom.forward_segment_id.id == target_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0
// to 3
// ++>t <-- add offset to get to target
// ................ <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
target_offset = target_phantom.GetForwardDistance();
}
else if (target_phantom.IsValidReverseTarget() &&
target_phantom.reverse_segment_id.id == target_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0
// to 3
// <++t <-- add offset to get from target
// ................ <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
target_offset = target_phantom.GetReverseDistance();
}
distances_table[location] = -source_offset + annotation + target_offset;
}
}
return std::make_pair(durations, distances_table);
}
//
@@ -379,6 +491,7 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
const std::vector<NodeBucket> &search_space_with_buckets,
std::vector<EdgeWeight> &weights_table,
std::vector<EdgeDuration> &durations_table,
std::vector<NodeID> &middle_nodes_table,
const PhantomNode &phantom_node)
{
const auto node = query_heap.DeleteMin();
@@ -415,6 +528,7 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
{
current_weight = new_weight;
current_duration = new_duration;
middle_nodes_table[location] = node;
}
}
@@ -432,9 +546,12 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
const auto node = query_heap.DeleteMin();
const auto target_weight = query_heap.GetKey(node);
const auto target_duration = query_heap.GetData(node).duration;
const auto parent = query_heap.GetData(node).parent;
const auto from_clique_arc = query_heap.GetData(node).from_clique_arc;
// Store settled nodes in search space bucket
search_space_with_buckets.emplace_back(node, column_idx, target_weight, target_duration);
search_space_with_buckets.emplace_back(
node, parent, from_clique_arc, column_idx, target_weight, target_duration);
const auto &partition = facade.GetMultiLevelPartition();
const auto maximal_level = partition.GetNumberOfLevels() - 1;
@@ -444,11 +561,225 @@ void backwardRoutingStep(const DataFacade<Algorithm> &facade,
}
template <bool DIRECTION>
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices)
void retrievePackedPathFromSearchSpace(NodeID middle_node_id,
const unsigned column_idx,
const std::vector<NodeBucket> &search_space_with_buckets,
PackedPath &path)
{
auto bucket_list = std::equal_range(search_space_with_buckets.begin(),
search_space_with_buckets.end(),
middle_node_id,
NodeBucket::ColumnCompare(column_idx));
BOOST_ASSERT_MSG(std::distance(bucket_list.first, bucket_list.second) == 1,
"The pointers are not pointing to the same element.");
NodeID current_node_id = middle_node_id;
while (bucket_list.first->parent_node != current_node_id &&
bucket_list.first != search_space_with_buckets.end())
{
const auto parent_node_id = bucket_list.first->parent_node;
const auto from = DIRECTION == FORWARD_DIRECTION ? current_node_id : parent_node_id;
const auto to = DIRECTION == FORWARD_DIRECTION ? parent_node_id : current_node_id;
path.emplace_back(std::make_tuple(from, to, bucket_list.first->from_clique_arc));
current_node_id = parent_node_id;
bucket_list = std::equal_range(search_space_with_buckets.begin(),
search_space_with_buckets.end(),
current_node_id,
NodeBucket::ColumnCompare(column_idx));
BOOST_ASSERT_MSG(std::distance(bucket_list.first, bucket_list.second) == 1,
"The pointers are not pointing to the same element.");
}
}
template <bool DIRECTION>
void calculateDistances(typename SearchEngineData<mld::Algorithm>::ManyToManyQueryHeap &query_heap,
const DataFacade<mld::Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &target_indices,
const unsigned row_idx,
const std::size_t source_index,
const unsigned number_of_sources,
const unsigned number_of_targets,
const std::vector<NodeBucket> &search_space_with_buckets,
std::vector<EdgeDistance> &distances_table,
const std::vector<NodeID> &middle_nodes_table,
SearchEngineData<mld::Algorithm> &engine_working_data)
{
engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes(),
facade.GetMaxBorderNodeID() + 1);
for (unsigned column_idx = 0; column_idx < number_of_targets; ++column_idx)
{
// Step 1: Get source and target phantom nodes that were used in the bucketed search
auto source_phantom_index = source_index;
auto target_phantom_index = target_indices[column_idx];
const auto &source_phantom = phantom_nodes[source_phantom_index];
const auto &target_phantom = phantom_nodes[target_phantom_index];
const auto location = DIRECTION == FORWARD_DIRECTION
? row_idx * number_of_targets + column_idx
: row_idx + column_idx * number_of_sources;
if (source_phantom_index == target_phantom_index)
{
distances_table[location] = 0.0;
continue;
}
NodeID middle_node_id = middle_nodes_table[location];
if (middle_node_id == SPECIAL_NODEID) // takes care of one-ways
{
distances_table[location] = INVALID_EDGE_DISTANCE;
continue;
}
// Step 2: Find path from source to middle node
PackedPath packed_path =
mld::retrievePackedPathFromSingleManyToManyHeap<DIRECTION>(query_heap, middle_node_id);
if (DIRECTION == FORWARD_DIRECTION)
{
std::reverse(packed_path.begin(), packed_path.end());
}
auto &forward_heap = *engine_working_data.forward_heap_1;
auto &reverse_heap = *engine_working_data.reverse_heap_1;
EdgeWeight weight = INVALID_EDGE_WEIGHT;
std::vector<NodeID> unpacked_nodes_from_source;
std::vector<EdgeID> unpacked_edges;
std::tie(weight, unpacked_nodes_from_source, unpacked_edges) =
unpackPathAndCalculateDistance(engine_working_data,
facade,
forward_heap,
reverse_heap,
DO_NOT_FORCE_LOOPS,
DO_NOT_FORCE_LOOPS,
INVALID_EDGE_WEIGHT,
packed_path,
middle_node_id,
source_phantom);
// Step 3: Find path from middle to target node
packed_path.clear();
retrievePackedPathFromSearchSpace<DIRECTION>(
middle_node_id, column_idx, search_space_with_buckets, packed_path);
if (DIRECTION == REVERSE_DIRECTION)
{
std::reverse(packed_path.begin(), packed_path.end());
}
std::vector<NodeID> unpacked_nodes_to_target;
std::tie(weight, unpacked_nodes_to_target, unpacked_edges) =
unpackPathAndCalculateDistance(engine_working_data,
facade,
forward_heap,
reverse_heap,
DO_NOT_FORCE_LOOPS,
DO_NOT_FORCE_LOOPS,
INVALID_EDGE_WEIGHT,
packed_path,
middle_node_id,
target_phantom);
if (DIRECTION == REVERSE_DIRECTION)
{
std::swap(unpacked_nodes_to_target, unpacked_nodes_from_source);
}
// Step 4: Compute annotation value along the path nodes without the target node
auto annotation = 0.0;
for (auto node = unpacked_nodes_from_source.begin(),
last_node = std::prev(unpacked_nodes_from_source.end());
node != last_node;
++node)
{
annotation += computeEdgeDistance(facade, *node);
}
for (auto node = unpacked_nodes_to_target.begin(),
last_node = std::prev(unpacked_nodes_to_target.end());
node != last_node;
++node)
{
annotation += computeEdgeDistance(facade, *node);
}
// Step 5: Get phantom node offsets and compute the annotation value
EdgeDistance source_offset = 0., target_offset = 0.;
{
// ⚠ for REVERSE_DIRECTION original source and target phantom nodes are swapped
if (DIRECTION == REVERSE_DIRECTION)
{
std::swap(source_phantom_index, target_phantom_index);
}
const auto &source_phantom = phantom_nodes[source_phantom_index];
const auto &target_phantom = phantom_nodes[target_phantom_index];
NodeID source_node = unpacked_nodes_from_source.front();
NodeID target_node = unpacked_nodes_to_target.back();
if (source_phantom.IsValidForwardSource() &&
source_phantom.forward_segment_id.id == source_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0
// to 3
// -->s <-- subtract offset to start at source
// ......... <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
source_offset = source_phantom.GetForwardDistance();
}
else if (source_phantom.IsValidReverseSource() &&
source_phantom.reverse_segment_id.id == source_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0 to 3
// s<------- <-- subtract offset to start at source
// ... <-- want this distance
// entry 0---1---2---3 <-- 3 is exit node
source_offset = source_phantom.GetReverseDistance();
}
if (target_phantom.IsValidForwardTarget() &&
target_phantom.forward_segment_id.id == target_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0
// to 3
// ++>t <-- add offset to get to target
// ................ <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
target_offset = target_phantom.GetForwardDistance();
}
else if (target_phantom.IsValidReverseTarget() &&
target_phantom.reverse_segment_id.id == target_node)
{
// ............ <-- calculateEGBAnnotation returns distance from 0
// to 3
// <++t <-- add offset to get from target
// ................ <-- want this distance as result
// entry 0---1---2---3--- <-- 3 is exit node
target_offset = target_phantom.GetReverseDistance();
}
}
distances_table[location] = -source_offset + annotation + target_offset;
}
}
template <bool DIRECTION>
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
const DataFacade<Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance)
{
const auto number_of_sources = source_indices.size();
const auto number_of_targets = target_indices.size();
@@ -456,6 +787,8 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_w
std::vector<EdgeWeight> weights_table(number_of_entries, INVALID_EDGE_WEIGHT);
std::vector<EdgeDuration> durations_table(number_of_entries, MAXIMAL_EDGE_DURATION);
std::vector<EdgeDistance> distances_table;
std::vector<NodeID> middle_nodes_table(number_of_entries, SPECIAL_NODEID);
std::vector<NodeBucket> search_space_with_buckets;
@@ -463,22 +796,22 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_w
for (std::uint32_t column_idx = 0; column_idx < target_indices.size(); ++column_idx)
{
const auto index = target_indices[column_idx];
const auto &phantom = phantom_nodes[index];
const auto &target_phantom = phantom_nodes[index];
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
auto &query_heap = *(engine_working_data.many_to_many_heap);
if (DIRECTION == FORWARD_DIRECTION)
insertTargetInHeap(query_heap, phantom);
insertTargetInHeap(query_heap, target_phantom);
else
insertSourceInHeap(query_heap, phantom);
insertSourceInHeap(query_heap, target_phantom);
// explore search space
while (!query_heap.Empty())
{
backwardRoutingStep<DIRECTION>(
facade, column_idx, query_heap, search_space_with_buckets, phantom);
facade, column_idx, query_heap, search_space_with_buckets, target_phantom);
}
}
@@ -488,18 +821,19 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_w
// Find shortest paths from sources to all accessible nodes
for (std::uint32_t row_idx = 0; row_idx < source_indices.size(); ++row_idx)
{
const auto index = source_indices[row_idx];
const auto &phantom = phantom_nodes[index];
const auto source_index = source_indices[row_idx];
const auto &source_phantom = phantom_nodes[source_index];
// Clear heap and insert source nodes
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
facade.GetNumberOfNodes(), facade.GetMaxBorderNodeID() + 1);
auto &query_heap = *(engine_working_data.many_to_many_heap);
if (DIRECTION == FORWARD_DIRECTION)
insertSourceInHeap(query_heap, phantom);
insertSourceInHeap(query_heap, source_phantom);
else
insertTargetInHeap(query_heap, phantom);
insertTargetInHeap(query_heap, source_phantom);
// Explore search space
while (!query_heap.Empty())
@@ -512,11 +846,29 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_w
search_space_with_buckets,
weights_table,
durations_table,
phantom);
middle_nodes_table,
source_phantom);
}
if (calculate_distance)
{
distances_table.resize(number_of_entries, INVALID_EDGE_DISTANCE);
calculateDistances<DIRECTION>(query_heap,
facade,
phantom_nodes,
target_indices, // source_indices
row_idx,
source_index,
number_of_sources,
number_of_targets,
search_space_with_buckets,
distances_table,
middle_nodes_table,
engine_working_data);
}
}
return durations_table;
return std::make_pair(durations_table, distances_table);
}
} // namespace mld
@@ -534,32 +886,54 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_w
// then search is performed on a reversed graph with phantom nodes with flipped roles and
// returning a transposed matrix.
template <>
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
const DataFacade<mld::Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices)
std::pair<std::vector<EdgeDuration>, std::vector<EdgeDistance>>
manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
const DataFacade<mld::Algorithm> &facade,
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices,
const bool calculate_distance,
const bool calculate_duration)
{
(void)calculate_duration; // flag stub to use for calculating distances in matrix in mld in the
// future
if (source_indices.size() == 1)
{ // TODO: check if target_indices.size() == 1 and do a bi-directional search
return mld::oneToManySearch<FORWARD_DIRECTION>(
engine_working_data, facade, phantom_nodes, source_indices.front(), target_indices);
return mld::oneToManySearch<FORWARD_DIRECTION>(engine_working_data,
facade,
phantom_nodes,
source_indices.front(),
target_indices,
calculate_distance);
}
if (target_indices.size() == 1)
{
return mld::oneToManySearch<REVERSE_DIRECTION>(
engine_working_data, facade, phantom_nodes, target_indices.front(), source_indices);
return mld::oneToManySearch<REVERSE_DIRECTION>(engine_working_data,
facade,
phantom_nodes,
target_indices.front(),
source_indices,
calculate_distance);
}
if (target_indices.size() < source_indices.size())
{
return mld::manyToManySearch<REVERSE_DIRECTION>(
engine_working_data, facade, phantom_nodes, target_indices, source_indices);
return mld::manyToManySearch<REVERSE_DIRECTION>(engine_working_data,
facade,
phantom_nodes,
target_indices,
source_indices,
calculate_distance);
}
return mld::manyToManySearch<FORWARD_DIRECTION>(
engine_working_data, facade, phantom_nodes, source_indices, target_indices);
return mld::manyToManySearch<FORWARD_DIRECTION>(engine_working_data,
facade,
phantom_nodes,
source_indices,
target_indices,
calculate_distance);
}
} // namespace routing_algorithms
@@ -59,6 +59,24 @@ void retrievePackedPathFromSingleHeap(const SearchEngineData<Algorithm>::QueryHe
}
}
void retrievePackedPathFromSingleManyToManyHeap(
const SearchEngineData<Algorithm>::ManyToManyQueryHeap &search_heap,
const NodeID middle_node_id,
std::vector<NodeID> &packed_path)
{
NodeID current_node_id = middle_node_id;
// all initial nodes will have itself as parent, or a node not in the heap
// in case of a core search heap. We need a distinction between core entry nodes
// and start nodes since otherwise start node specific code that assumes
// node == node.parent (e.g. the loop code) might get actived.
while (current_node_id != search_heap.GetData(current_node_id).parent &&
search_heap.WasInserted(search_heap.GetData(current_node_id).parent))
{
current_node_id = search_heap.GetData(current_node_id).parent;
packed_path.emplace_back(current_node_id);
}
}
// assumes that heaps are already setup correctly.
// ATTENTION: This only works if no additional offset is supplied next to the Phantom Node
// Offsets.
+2 -37
View File
@@ -101,7 +101,6 @@ std::vector<TurnData> generateTurns(const datafacade &facade,
// w
// uv is the "approach"
// vw is the "exit"
// Look at every node in the directed graph we created
for (const auto &startnode : sorted_startnodes)
{
@@ -146,42 +145,8 @@ std::vector<TurnData> generateTurns(const datafacade &facade,
const auto &data = facade.GetEdgeData(edge_based_edge_id);
// Now, calculate the sum of the weight of all the segments.
const auto &geometry =
edge_based_node_info.find(approachedge.edge_based_node_id)->second;
EdgeWeight sum_node_weight = 0;
EdgeDuration sum_node_duration = 0;
if (geometry.is_geometry_forward)
{
const auto approach_weight =
facade.GetUncompressedForwardWeights(geometry.packed_geometry_id);
const auto approach_duration =
facade.GetUncompressedForwardDurations(geometry.packed_geometry_id);
sum_node_weight = std::accumulate(
approach_weight.begin(), approach_weight.end(), EdgeWeight{0});
sum_node_duration = std::accumulate(
approach_duration.begin(), approach_duration.end(), EdgeDuration{0});
}
else
{
const auto approach_weight =
facade.GetUncompressedReverseWeights(geometry.packed_geometry_id);
const auto approach_duration =
facade.GetUncompressedReverseDurations(geometry.packed_geometry_id);
sum_node_weight = std::accumulate(
approach_weight.begin(), approach_weight.end(), EdgeWeight{0});
sum_node_duration = std::accumulate(
approach_duration.begin(), approach_duration.end(), EdgeDuration{0});
}
// The edge.weight is the whole edge weight, which includes the turn
// cost.
// The turn cost is the edge.weight minus the sum of the individual road
// segment weights. This might not be 100% accurate, because some
// intersections include stop signs, traffic signals and other
// penalties, but at this stage, we can't divide those out, so we just
// treat the whole lot as the "turn cost" that we'll stick on the map.
const auto turn_weight = data.weight - sum_node_weight;
const auto turn_duration = data.duration - sum_node_duration;
const auto turn_weight = facade.GetWeightPenaltyForEdgeID(data.turn_id);
const auto turn_duration = facade.GetDurationPenaltyForEdgeID(data.turn_id);
const auto turn_instruction = facade.GetTurnInstructionForEdgeID(data.turn_id);
// Find the three nodes that make up the turn movement)
+21 -2
View File
@@ -107,6 +107,13 @@ void EdgeBasedGraphFactory::GetEdgeBasedNodeWeights(std::vector<EdgeWeight> &out
swap(m_edge_based_node_weights, output_node_weights);
}
void EdgeBasedGraphFactory::GetEdgeBasedNodeDurations(
std::vector<EdgeWeight> &output_node_durations)
{
using std::swap; // Koenig swap
swap(m_edge_based_node_durations, output_node_durations);
}
std::uint32_t EdgeBasedGraphFactory::GetConnectivityChecksum() const
{
return m_connectivity_checksum;
@@ -138,9 +145,16 @@ NBGToEBG EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const N
BOOST_ASSERT(nbe_to_ebn_mapping[edge_id_1] != SPECIAL_NODEID ||
nbe_to_ebn_mapping[edge_id_2] != SPECIAL_NODEID);
// ⚠ Use the sign bit of node weights to distinguish oneway streets:
// * MSB is set - a node corresponds to a one-way street
// * MSB is clear - a node corresponds to a bidirectional street
// Before using node weights data values must be adjusted:
// * in contraction if MSB is set the node weight is INVALID_EDGE_WEIGHT.
// This adjustment is needed to enforce loop creation for oneways.
// * in other cases node weights must be masked with 0x7fffffff to clear MSB
if (nbe_to_ebn_mapping[edge_id_1] != SPECIAL_NODEID &&
nbe_to_ebn_mapping[edge_id_2] == SPECIAL_NODEID)
m_edge_based_node_weights[nbe_to_ebn_mapping[edge_id_1]] = INVALID_EDGE_WEIGHT;
m_edge_based_node_weights[nbe_to_ebn_mapping[edge_id_1]] |= 0x80000000;
BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_id_1) ==
m_compressed_edge_container.HasEntryForID(edge_id_2));
@@ -278,6 +292,7 @@ unsigned EdgeBasedGraphFactory::LabelEdgeBasedNodes()
// heuristic: node-based graph node is a simple intersection with four edges
// (edge-based nodes)
m_edge_based_node_weights.reserve(4 * m_node_based_graph.GetNumberOfNodes());
m_edge_based_node_durations.reserve(4 * m_node_based_graph.GetNumberOfNodes());
nbe_to_ebn_mapping.resize(m_node_based_graph.GetEdgeCapacity(), SPECIAL_NODEID);
// renumber edge based node of outgoing edges
@@ -294,6 +309,7 @@ unsigned EdgeBasedGraphFactory::LabelEdgeBasedNodes()
}
m_edge_based_node_weights.push_back(edge_data.weight);
m_edge_based_node_durations.push_back(edge_data.duration);
BOOST_ASSERT(numbered_edges_count < m_node_based_graph.GetNumberOfEdges());
nbe_to_ebn_mapping[current_edge] = numbered_edges_count;
@@ -387,8 +403,10 @@ EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_re
segregated_edges.count(eid) > 0;
const auto ebn_weight = m_edge_based_node_weights[nbe_to_ebn_mapping[eid]];
BOOST_ASSERT(ebn_weight == INVALID_EDGE_WEIGHT || ebn_weight == edge_data.weight);
BOOST_ASSERT((ebn_weight & 0x7fffffff) == edge_data.weight);
m_edge_based_node_weights.push_back(ebn_weight);
m_edge_based_node_durations.push_back(
m_edge_based_node_durations[nbe_to_ebn_mapping[eid]]);
edge_based_node_id++;
progress.PrintStatus(progress_counter++);
@@ -397,6 +415,7 @@ EdgeBasedGraphFactory::GenerateEdgeExpandedNodes(const WayRestrictionMap &way_re
BOOST_ASSERT(m_edge_based_node_segments.size() == m_edge_based_node_is_startpoint.size());
BOOST_ASSERT(m_number_of_edge_based_nodes == m_edge_based_node_weights.size());
BOOST_ASSERT(m_number_of_edge_based_nodes == m_edge_based_node_durations.size());
util::Log() << "Generated " << m_number_of_edge_based_nodes << " nodes ("
<< way_restriction_map.NumberOfDuplicatedNodes()
+6 -2
View File
@@ -241,6 +241,7 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
util::DeallocatingVector<EdgeBasedEdge> edge_based_edge_list;
std::vector<bool> node_is_startpoint;
std::vector<EdgeWeight> edge_based_node_weights;
std::vector<EdgeDuration> edge_based_node_durations;
std::uint32_t ebg_connectivity_checksum = 0;
// Create a node-based graph from the OSRM file
@@ -320,6 +321,7 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
edge_based_node_segments,
node_is_startpoint,
edge_based_node_weights,
edge_based_node_durations,
edge_based_edge_list,
ebg_connectivity_checksum);
@@ -343,8 +345,8 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
util::Log() << "Saving edge-based node weights to file.";
TIMER_START(timer_write_node_weights);
extractor::files::writeEdgeBasedNodeWeights(config.GetPath(".osrm.enw"),
edge_based_node_weights);
extractor::files::writeEdgeBasedNodeWeightsDurations(
config.GetPath(".osrm.enw"), edge_based_node_weights, edge_based_node_durations);
TIMER_STOP(timer_write_node_weights);
util::Log() << "Done writing. (" << TIMER_SEC(timer_write_node_weights) << ")";
@@ -733,6 +735,7 @@ EdgeID Extractor::BuildEdgeExpandedGraph(
std::vector<EdgeBasedNodeSegment> &edge_based_node_segments,
std::vector<bool> &node_is_startpoint,
std::vector<EdgeWeight> &edge_based_node_weights,
std::vector<EdgeDuration> &edge_based_node_durations,
util::DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
std::uint32_t &connectivity_checksum)
{
@@ -782,6 +785,7 @@ EdgeID Extractor::BuildEdgeExpandedGraph(
edge_based_graph_factory.GetEdgeBasedNodeSegments(edge_based_node_segments);
edge_based_graph_factory.GetStartPointMarkers(node_is_startpoint);
edge_based_graph_factory.GetEdgeBasedNodeWeights(edge_based_node_weights);
edge_based_graph_factory.GetEdgeBasedNodeDurations(edge_based_node_durations);
connectivity_checksum = edge_based_graph_factory.GetConnectivityChecksum();
return number_of_edge_based_nodes;
+10 -6
View File
@@ -263,7 +263,7 @@ NAN_METHOD(Engine::nearest) //
// clang-format off
/**
* Computes duration tables for the given locations. Allows for both symmetric and asymmetric
* Computes duration and distance tables for the given locations. Allows for both symmetric and asymmetric
* tables.
*
* @name table
@@ -274,17 +274,20 @@ NAN_METHOD(Engine::nearest) //
* Can be `null` or an array of `[{value},{range}]` with `integer 0 .. 360,integer 0 .. 180`.
* @param {Array} [options.radiuses] Limits the coordinate snapping to streets in the given radius in meters. Can be `null` (unlimited, default) or `double >= 0`.
* @param {Array} [options.hints] Hints for the coordinate snapping. Array of base64 encoded strings.
* @param {Array} [options.sources] An array of `index` elements (`0 <= integer < #coordinates`) to
* use
* location with given index as source. Default is to use all.
* @param {Array} [options.destinations] An array of `index` elements (`0 <= integer <
* #coordinates`) to use location with given index as destination. Default is to use all.
* @param {Array} [options.sources] An array of `index` elements (`0 <= integer < #coordinates`) to use
* location with given index as source. Default is to use all.
* @param {Array} [options.destinations] An array of `index` elements (`0 <= integer < #coordinates`) to use location with given index as destination. Default is to use all.
* @param {Array} [options.approaches] Keep waypoints on curb side. Can be `null` (unrestricted, default) or `curb`.
* @param {Array} [options.annotations] An array of the table types to return. Values can be `duration` or `distance` or both. If no annotations parameter is added, the default is to return the `durations` table. If `annotations=distance` or `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned.
* @param {Function} callback
*
* @returns {Object} containing `durations`, `sources`, and `destinations`.
* **`durations`**: array of arrays that stores the matrix in row-major order. `durations[i][j]` gives the travel time from the i-th waypoint to the j-th waypoint.
* Values are given in seconds.
* **`distances`**: array of arrays that stores the matrix in row-major order. `distances[i][j]` gives the travel time from the i-th waypoint to the j-th waypoint.
* Values are given in meters. Note that computing the `distances` table is currently only implemented for CH. If `annotations=distance` or
* `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned.
* **`sources`**: array of [`aypoint`](#waypoint) objects describing all sources in order.
* **`destinations`**: array of [`aypoint`](#waypoint) objects describing all destinations in order.
*
@@ -299,6 +302,7 @@ NAN_METHOD(Engine::nearest) //
* };
* osrm.table(options, function(err, response) {
* console.log(response.durations); // array of arrays, matrix in row-major order
* console.log(response.distances); // array of arrays, matrix in row-major order (currently only implemented for CH router)
* console.log(response.sources); // array of Waypoint objects
* console.log(response.destinations); // array of Waypoint objects
* });
+10
View File
@@ -144,6 +144,16 @@ int Partitioner::Run(const PartitionerConfig &config)
renumber(node_data, permutation);
extractor::files::writeNodeData(config.GetPath(".osrm.ebg_nodes"), node_data);
}
{
std::vector<EdgeWeight> node_weights;
std::vector<EdgeDuration> node_durations;
extractor::files::readEdgeBasedNodeWeightsDurations(
config.GetPath(".osrm.enw"), node_weights, node_durations);
util::inplacePermutation(node_weights.begin(), node_weights.end(), permutation);
util::inplacePermutation(node_durations.begin(), node_durations.end(), permutation);
extractor::files::writeEdgeBasedNodeWeightsDurations(
config.GetPath(".osrm.enw"), node_weights, node_durations);
}
{
const auto &filename = config.GetPath(".osrm.maneuver_overrides");
std::vector<extractor::StorageManeuverOverride> maneuver_overrides;
+1 -1
View File
@@ -554,7 +554,7 @@ void Storage::PopulateUpdatableData(const SharedDataIndex &index)
{
auto graph_view = make_multi_level_graph_view(index, "/mld/multilevelgraph");
std::uint32_t graph_connectivity_checksum = 0;
partitioner::files::readGraph(
customizer::files::readGraph(
config.GetPath(".osrm.mldgr"), graph_view, graph_connectivity_checksum);
auto turns_connectivity_checksum =
+49 -13
View File
@@ -1,6 +1,8 @@
#include "storage/serialization.hpp"
#include "storage/shared_memory.hpp"
#include "storage/shared_monitor.hpp"
#include "storage/storage.hpp"
#include "osrm/exception.hpp"
#include "util/log.hpp"
#include "util/meminfo.hpp"
@@ -25,14 +27,17 @@ void deleteRegion(const storage::SharedRegionRegister::ShmKey key)
}
}
void listRegions()
void listRegions(bool show_blocks)
{
osrm::util::Log() << "name\tshm key\ttimestamp\tsize";
if (!storage::SharedMonitor<storage::SharedRegionRegister>::exists())
{
return;
}
storage::SharedMonitor<storage::SharedRegionRegister> monitor;
std::vector<std::string> names;
const auto &shared_register = monitor.data();
shared_register.List(std::back_inserter(names));
osrm::util::Log() << "name\tshm key\ttimestamp\tsize";
for (const auto &name : names)
{
auto id = shared_register.Find(name);
@@ -40,6 +45,23 @@ void listRegions()
auto shm = osrm::storage::makeSharedMemory(region.shm_key);
osrm::util::Log() << name << "\t" << static_cast<int>(region.shm_key) << "\t"
<< region.timestamp << "\t" << shm->Size();
if (show_blocks)
{
using namespace storage;
auto memory = makeSharedMemory(region.shm_key);
io::BufferReader reader(reinterpret_cast<char *>(memory->Ptr()), memory->Size());
DataLayout layout;
serialization::read(reader, layout);
std::vector<std::string> block_names;
layout.List("", std::back_inserter(block_names));
for (auto &name : block_names)
{
osrm::util::Log() << " " << name << " " << layout.GetBlockSize(name);
}
}
}
}
@@ -76,6 +98,7 @@ bool generateDataStoreOptions(const int argc,
int &max_wait,
std::string &dataset_name,
bool &list_datasets,
bool &list_blocks,
bool &only_metric)
{
// declare a group of options that will be allowed only on command line
@@ -105,14 +128,19 @@ bool generateDataStoreOptions(const int argc,
boost::program_options::value<bool>(&list_datasets)
->default_value(false)
->implicit_value(true),
"Name of the dataset to load into memory. This allows having multiple datasets in memory "
"at the same time.") //
("only-metric",
boost::program_options::value<bool>(&only_metric)
"List all OSRM datasets currently in memory") //
("list-blocks",
boost::program_options::value<bool>(&list_blocks)
->default_value(false)
->implicit_value(true),
"Only reload the metric data without updating the full dataset. This is an optimization "
"for traffic updates.");
"List all OSRM datasets currently in memory")(
"only-metric",
boost::program_options::value<bool>(&only_metric)
->default_value(false)
->implicit_value(true),
"Only reload the metric data without updating the full dataset. This is an "
"optimization "
"for traffic updates.");
// hidden options, will be allowed on command line but will not be shown to the user
boost::program_options::options_description hidden_options("Hidden options");
@@ -207,18 +235,26 @@ int main(const int argc, const char *argv[]) try
int max_wait = -1;
std::string dataset_name;
bool list_datasets = false;
bool list_blocks = false;
bool only_metric = false;
if (!generateDataStoreOptions(
argc, argv, verbosity, base_path, max_wait, dataset_name, list_datasets, only_metric))
if (!generateDataStoreOptions(argc,
argv,
verbosity,
base_path,
max_wait,
dataset_name,
list_datasets,
list_blocks,
only_metric))
{
return EXIT_SUCCESS;
}
util::LogPolicy::GetInstance().SetLevel(verbosity);
if (list_datasets)
if (list_datasets || list_blocks)
{
listRegions();
listRegions(list_blocks);
return EXIT_SUCCESS;
}
+17 -15
View File
@@ -517,7 +517,6 @@ updateConditionalTurns(std::vector<TurnPenalty> &turn_weight_penalties,
{
if (IsRestrictionValid(time_zone_handler, penalty))
{
std::cout << "Disabling: " << penalty.turn_offset << std::endl;
turn_weight_penalties[penalty.turn_offset] = INVALID_TURN_PENALTY;
updated_turns.push_back(penalty.turn_offset);
}
@@ -526,20 +525,20 @@ updateConditionalTurns(std::vector<TurnPenalty> &turn_weight_penalties,
}
}
Updater::NumNodesAndEdges Updater::LoadAndUpdateEdgeExpandedGraph() const
EdgeID
Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
std::vector<EdgeWeight> &node_weights,
std::uint32_t &connectivity_checksum) const
{
std::vector<EdgeWeight> node_weights;
std::vector<extractor::EdgeBasedEdge> edge_based_edge_list;
std::uint32_t connectivity_checksum;
auto number_of_edge_based_nodes = Updater::LoadAndUpdateEdgeExpandedGraph(
edge_based_edge_list, node_weights, connectivity_checksum);
return std::make_tuple(
number_of_edge_based_nodes, std::move(edge_based_edge_list), connectivity_checksum);
std::vector<EdgeDuration> node_durations(node_weights.size());
return LoadAndUpdateEdgeExpandedGraph(
edge_based_edge_list, node_weights, node_durations, connectivity_checksum);
}
EdgeID
Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &edge_based_edge_list,
std::vector<EdgeWeight> &node_weights,
std::vector<EdgeDuration> &node_durations,
std::uint32_t &connectivity_checksum) const
{
TIMER_START(load_edges);
@@ -548,6 +547,9 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &e
std::vector<util::Coordinate> coordinates;
extractor::PackedOSMIDs osm_node_ids;
extractor::files::readEdgeBasedNodeWeightsDurations(
config.GetPath(".osrm.enw"), node_weights, node_durations);
extractor::files::readEdgeBasedGraph(config.GetPath(".osrm.ebg"),
number_of_edge_based_nodes,
edge_based_edge_list,
@@ -743,12 +745,12 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector<extractor::EdgeBasedEdge> &e
accumulated_segment_data[updated_iter - updated_segments.begin()];
// Update the node-weight cache. This is the weight of the edge-based-node
// only,
// it doesn't include the turn. We may visit the same node multiple times,
// but
// we should always assign the same value here.
if (node_weights.size() > 0)
node_weights[edge.source] = new_weight;
// only, it doesn't include the turn. We may visit the same node multiple times,
// but we should always assign the same value here.
BOOST_ASSERT(edge.source < node_weights.size());
node_weights[edge.source] =
node_weights[edge.source] & 0x80000000 ? new_weight | 0x80000000 : new_weight;
node_durations[edge.source] = new_duration;
// We found a zero-speed edge, so we'll skip this whole edge-based-edge
// which
+57 -3
View File
@@ -5,6 +5,8 @@
#include <boost/assert.hpp>
#include <mapbox/cheap_ruler.hpp>
#include <algorithm>
#include <iterator>
#include <limits>
@@ -18,6 +20,44 @@ namespace util
namespace coordinate_calculation
{
namespace
{
class CheapRulerContainer
{
public:
CheapRulerContainer(const int number_of_rulers)
: cheap_ruler_cache(number_of_rulers, mapbox::cheap_ruler::CheapRuler(0)),
step(90.0 * COORDINATE_PRECISION / number_of_rulers)
{
for (int n = 0; n < number_of_rulers; n++)
{
cheap_ruler_cache[n] = mapbox::cheap_ruler::CheapRuler(
step * (n + 0.5) / COORDINATE_PRECISION, mapbox::cheap_ruler::CheapRuler::Meters);
}
};
mapbox::cheap_ruler::CheapRuler &getRuler(const FixedLatitude lat_1, const FixedLatitude lat_2)
{
auto lat = (lat_1 + lat_2) / util::FixedLatitude{2};
return getRuler(lat);
}
mapbox::cheap_ruler::CheapRuler &getRuler(const FixedLatitude lat)
{
BOOST_ASSERT(step > 2);
// the |lat| > 0 -> |lat|-1 > -1 -> (|lat|-1)/step > -1/step > -1/2 >= -1 -> bin >= 0
std::size_t bin = (std::abs(static_cast<int>(lat)) - 1) / step;
BOOST_ASSERT(bin < cheap_ruler_cache.size());
return cheap_ruler_cache[bin];
};
private:
std::vector<mapbox::cheap_ruler::CheapRuler> cheap_ruler_cache;
const int step;
};
static CheapRulerContainer cheap_ruler_container(1800);
} // namespace
// Does not project the coordinates!
std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rhs)
{
@@ -32,6 +72,20 @@ std::uint64_t squaredEuclideanDistance(const Coordinate lhs, const Coordinate rh
return result;
}
// Uses method described here:
// https://www.gpo.gov/fdsys/pkg/CFR-2005-title47-vol4/pdf/CFR-2005-title47-vol4-sec73-208.pdf
// should be within 0.1% or so of Vincenty method (assuming 19 buckets are enough)
// Should be more faster and more precise than Haversine
double fccApproximateDistance(const Coordinate coordinate_1, const Coordinate coordinate_2)
{
const auto lon1 = static_cast<double>(util::toFloating(coordinate_1.lon));
const auto lat1 = static_cast<double>(util::toFloating(coordinate_1.lat));
const auto lon2 = static_cast<double>(util::toFloating(coordinate_2.lon));
const auto lat2 = static_cast<double>(util::toFloating(coordinate_2.lat));
return cheap_ruler_container.getRuler(coordinate_1.lat, coordinate_2.lat)
.distance({lon1, lat1}, {lon2, lat2});
}
double haversineDistance(const Coordinate coordinate_1, const Coordinate coordinate_2)
{
auto lon1 = static_cast<int>(coordinate_1.lon);
@@ -423,6 +477,6 @@ double computeArea(const std::vector<Coordinate> &polygon)
return area / 2.;
}
} // ns coordinate_calculation
} // ns util
} // ns osrm
} // namespace coordinate_calculation
} // namespace util
} // namespace osrm
+1 -1
View File
@@ -10,7 +10,7 @@ exports.three_test_coordinates = [[7.41337, 43.72956],
exports.two_test_coordinates = exports.three_test_coordinates.slice(0, 2)
exports.test_tile = {'at': [17059, 11948, 15], 'size': 168571};
exports.test_tile = {'at': [17059, 11948, 15], 'size': 148750};
// Test files generated by the routing engine; check test/data
if (process.env.OSRM_DATA_PATH !== undefined) {
+210 -161
View File
@@ -5,179 +5,228 @@ var mld_data_path = require('./constants').mld_data_path;
var three_test_coordinates = require('./constants').three_test_coordinates;
var two_test_coordinates = require('./constants').two_test_coordinates;
test('table: distance table in Monaco', function(assert) {
assert.plan(11);
test('table: test annotations paramater combination', function(assert) {
assert.plan(12);
var osrm = new OSRM(data_path);
var options = {
coordinates: [three_test_coordinates[0], three_test_coordinates[1]],
annotations: ['distance']
};
osrm.table(options, function(err, table) {
assert.ifError(err);
assert.ok(table['distances'], 'distances table result should exist');
assert.notOk(table['durations'], 'durations table result should not exist');
});
options = {
coordinates: [three_test_coordinates[0], three_test_coordinates[1]],
annotations: ['duration']
};
osrm.table(options, function(err, table) {
assert.ifError(err);
assert.ok(table['durations'], 'durations table result should exist');
assert.notOk(table['distances'], 'distances table result should not exist');
});
options = {
coordinates: [three_test_coordinates[0], three_test_coordinates[1]],
annotations: ['duration', 'distance']
};
osrm.table(options, function(err, table) {
assert.ifError(err);
assert.ok(table['durations'], 'durations table result should exist');
assert.ok(table['distances'], 'distances table result should exist');
});
options = {
coordinates: [three_test_coordinates[0], three_test_coordinates[1]]
};
osrm.table(options, function(err, table) {
assert.ifError(err);
assert.ok(Array.isArray(table.durations), 'result must be an array');
var row_count = table.durations.length;
for (var i = 0; i < row_count; ++i) {
var column = table.durations[i];
var column_count = column.length;
assert.equal(row_count, column_count);
for (var j = 0; j < column_count; ++j) {
if (i == j) {
// check that diagonal is zero
assert.equal(0, column[j], 'diagonal must be zero');
} else {
// everything else is non-zero
assert.notEqual(0, column[j], 'other entries must be non-zero');
// and finite (not nan, inf etc.)
assert.ok(Number.isFinite(column[j]), 'distance is finite number');
assert.ok(table['durations'], 'durations table result should exist');
assert.notOk(table['distances'], 'distances table result should not exist');
});
});
var tables = ['distances', 'durations'];
tables.forEach(function(annotation) {
test('table: ' + annotation + ' table in Monaco', function(assert) {
assert.plan(11);
var osrm = new OSRM(data_path);
var options = {
coordinates: [three_test_coordinates[0], three_test_coordinates[1]],
annotations: [annotation.slice(0,-1)]
};
osrm.table(options, function(err, table) {
assert.ifError(err);
assert.ok(Array.isArray(table[annotation]), 'result must be an array');
var row_count = table[annotation].length;
for (var i = 0; i < row_count; ++i) {
var column = table[annotation][i];
var column_count = column.length;
assert.equal(row_count, column_count);
for (var j = 0; j < column_count; ++j) {
if (i == j) {
// check that diagonal is zero
assert.equal(0, column[j], 'diagonal must be zero');
} else {
// everything else is non-zero
assert.notEqual(0, column[j], 'other entries must be non-zero');
// and finite (not nan, inf etc.)
assert.ok(Number.isFinite(column[j]), 'distance is finite number');
}
}
}
}
assert.equal(options.coordinates.length, row_count);
assert.equal(options.coordinates.length, row_count);
});
});
});
test('table: distance table in Monaco with sources/destinations', function(assert) {
assert.plan(7);
var osrm = new OSRM(data_path);
var options = {
coordinates: [three_test_coordinates[0], three_test_coordinates[1]],
sources: [0],
destinations: [0,1]
};
osrm.table(options, function(err, table) {
assert.ifError(err);
assert.ok(Array.isArray(table.durations), 'result must be an array');
var row_count = table.durations.length;
for (var i = 0; i < row_count; ++i) {
var column = table.durations[i];
var column_count = column.length;
assert.equal(options.destinations.length, column_count);
for (var j = 0; j < column_count; ++j) {
if (i == j) {
// check that diagonal is zero
assert.equal(0, column[j], 'diagonal must be zero');
} else {
// everything else is non-zero
assert.notEqual(0, column[j], 'other entries must be non-zero');
// and finite (not nan, inf etc.)
assert.ok(Number.isFinite(column[j]), 'distance is finite number');
test('table: ' + annotation + ' table in Monaco with sources/destinations', function(assert) {
assert.plan(7);
var osrm = new OSRM(data_path);
var options = {
coordinates: [three_test_coordinates[0], three_test_coordinates[1]],
sources: [0],
destinations: [0,1],
annotations: [annotation.slice(0,-1)]
};
osrm.table(options, function(err, table) {
assert.ifError(err);
assert.ok(Array.isArray(table[annotation]), 'result must be an array');
var row_count = table[annotation].length;
for (var i = 0; i < row_count; ++i) {
var column = table[annotation][i];
var column_count = column.length;
assert.equal(options.destinations.length, column_count);
for (var j = 0; j < column_count; ++j) {
if (i == j) {
// check that diagonal is zero
assert.equal(0, column[j], 'diagonal must be zero');
} else {
// everything else is non-zero
assert.notEqual(0, column[j], 'other entries must be non-zero');
// and finite (not nan, inf etc.)
assert.ok(Number.isFinite(column[j]), 'distance is finite number');
}
}
}
}
assert.equal(options.sources.length, row_count);
});
});
test('table: throws on invalid arguments', function(assert) {
assert.plan(14);
var osrm = new OSRM(data_path);
var options = {};
assert.throws(function() { osrm.table(options); },
/Two arguments required/);
options.coordinates = null;
assert.throws(function() { osrm.table(options, function() {}); },
/Coordinates must be an array of \(lon\/lat\) pairs/);
options.coordinates = [three_test_coordinates[0]];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/At least two coordinates must be provided/);
options.coordinates = three_test_coordinates[0];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Coordinates must be an array of \(lon\/lat\) pairs/);
options.coordinates = [three_test_coordinates[0][0], three_test_coordinates[0][1]];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Coordinates must be an array of \(lon\/lat\) pairs/);
options.coordinates = two_test_coordinates;
options.sources = true;
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Sources must be an array of indices \(or undefined\)/);
options.sources = [0, 4];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Source indices must be less than or equal to the number of coordinates/);
options.sources = [0.3, 1.1];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Source must be an integer/);
options.destinations = true;
delete options.sources;
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Destinations must be an array of indices \(or undefined\)/);
options.destinations = [0, 4];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Destination indices must be less than or equal to the number of coordinates/);
options.destinations = [0.3, 1.1];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Destination must be an integer/);
// does not throw: the following two have been changed in OSRM v5
options.sources = [0, 1];
delete options.destinations;
assert.doesNotThrow(function() { osrm.table(options, function(err, response) {}) },
/Both sources and destinations need to be specified/);
options.destinations = [0, 1];
assert.doesNotThrow(function() { osrm.table(options, function(err, response) {}) },
/You can either specify sources and destinations, or coordinates/);
assert.throws(function() { osrm.route({coordinates: two_test_coordinates, generate_hints: null}, function(err, route) {}) },
/generate_hints must be of type Boolean/);
});
test('table: throws on invalid arguments', function(assert) {
assert.plan(1);
var osrm = new OSRM(data_path);
assert.throws(function() { osrm.table(null, function() {}); },
/First arg must be an object/);
});
test('table: distance table in Monaco with hints', function(assert) {
assert.plan(5);
var osrm = new OSRM(data_path);
var options = {
coordinates: two_test_coordinates,
generate_hints: true // true is default but be explicit here
};
osrm.table(options, function(err, table) {
console.log(table);
assert.ifError(err);
function assertHasHints(waypoint) {
assert.notStrictEqual(waypoint.hint, undefined);
}
table.sources.map(assertHasHints);
table.destinations.map(assertHasHints);
});
});
test('table: distance table in Monaco without hints', function(assert) {
assert.plan(5);
var osrm = new OSRM(data_path);
var options = {
coordinates: two_test_coordinates,
generate_hints: false // true is default
};
osrm.table(options, function(err, table) {
assert.ifError(err);
function assertHasNoHints(waypoint) {
assert.strictEqual(waypoint.hint, undefined);
}
table.sources.map(assertHasNoHints);
table.destinations.map(assertHasNoHints);
});
});
test('table: table in Monaco without motorways', function(assert) {
assert.plan(2);
var osrm = new OSRM({path: mld_data_path, algorithm: 'MLD'});
var options = {
coordinates: two_test_coordinates,
exclude: ['motorway']
};
osrm.table(options, function(err, response) {
assert.ifError(err);
assert.equal(response.durations.length, 2);
assert.equal(options.sources.length, row_count);
});
});
test('table: ' + annotation + ' throws on invalid arguments', function(assert) {
assert.plan(14);
var osrm = new OSRM(data_path);
var options = {annotations: [annotation.slice(0,-1)]};
assert.throws(function() { osrm.table(options); },
/Two arguments required/);
options.coordinates = null;
assert.throws(function() { osrm.table(options, function() {}); },
/Coordinates must be an array of \(lon\/lat\) pairs/);
options.coordinates = [three_test_coordinates[0]];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/At least two coordinates must be provided/);
options.coordinates = three_test_coordinates[0];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Coordinates must be an array of \(lon\/lat\) pairs/);
options.coordinates = [three_test_coordinates[0][0], three_test_coordinates[0][1]];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Coordinates must be an array of \(lon\/lat\) pairs/);
options.coordinates = two_test_coordinates;
options.sources = true;
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Sources must be an array of indices \(or undefined\)/);
options.sources = [0, 4];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Source indices must be less than or equal to the number of coordinates/);
options.sources = [0.3, 1.1];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Source must be an integer/);
options.destinations = true;
delete options.sources;
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Destinations must be an array of indices \(or undefined\)/);
options.destinations = [0, 4];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Destination indices must be less than or equal to the number of coordinates/);
options.destinations = [0.3, 1.1];
assert.throws(function() { osrm.table(options, function(err, response) {}) },
/Destination must be an integer/);
// does not throw: the following two have been changed in OSRM v5
options.sources = [0, 1];
delete options.destinations;
assert.doesNotThrow(function() { osrm.table(options, function(err, response) {}) },
/Both sources and destinations need to be specified/);
options.destinations = [0, 1];
assert.doesNotThrow(function() { osrm.table(options, function(err, response) {}) },
/You can either specify sources and destinations, or coordinates/);
assert.throws(function() { osrm.route({coordinates: two_test_coordinates, generate_hints: null}, function(err, route) {}) },
/generate_hints must be of type Boolean/);
});
test('table: throws on invalid arguments', function(assert) {
assert.plan(1);
var osrm = new OSRM(data_path);
assert.throws(function() { osrm.table(null, function() {}); },
/First arg must be an object/);
});
test('table: ' + annotation + ' table in Monaco with hints', function(assert) {
assert.plan(5);
var osrm = new OSRM(data_path);
var options = {
coordinates: two_test_coordinates,
generate_hints: true, // true is default but be explicit here
annotations: [annotation.slice(0,-1)]
};
osrm.table(options, function(err, table) {
assert.ifError(err);
function assertHasHints(waypoint) {
assert.notStrictEqual(waypoint.hint, undefined);
}
table.sources.map(assertHasHints);
table.destinations.map(assertHasHints);
});
});
test('table: ' + annotation + ' table in Monaco without hints', function(assert) {
assert.plan(5);
var osrm = new OSRM(data_path);
var options = {
coordinates: two_test_coordinates,
generate_hints: false, // true is default
annotations: [annotation.slice(0,-1)]
};
osrm.table(options, function(err, table) {
assert.ifError(err);
function assertHasNoHints(waypoint) {
assert.strictEqual(waypoint.hint, undefined);
}
table.sources.map(assertHasNoHints);
table.destinations.map(assertHasNoHints);
});
});
test('table: ' + annotation + ' table in Monaco without motorways', function(assert) {
assert.plan(1);
var osrm = new OSRM({path: mld_data_path, algorithm: 'MLD'});
var options = {
coordinates: two_test_coordinates,
exclude: ['motorway'],
annotations: [annotation.slice(0,-1)]
};
osrm.table(options, function(err, response) {
assert.equal(response[annotation].length, 2);
});
});
});
+18
View File
@@ -0,0 +1,18 @@
Standard: Cpp11
IndentWidth: 4
AccessModifierOffset: -4
UseTab: Never
BinPackParameters: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
AlwaysBreakTemplateDeclarations: true
NamespaceIndentation: None
PointerBindsToType: true
SpacesInParentheses: false
BreakBeforeBraces: Attach
ColumnLimit: 100
Cpp11BracedListStyle: false
SpacesBeforeTrailingComments: 1
@@ -0,0 +1,2 @@
build/
mason_packages/
+25
View File
@@ -0,0 +1,25 @@
language: generic
matrix:
include:
- os: linux
env: CXX=g++-4.9
sudo: required
dist: trusty
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'g++-4.9', 'cmake', 'cmake-data' ]
- os: linux
env: CXX=g++-5
sudo: required
dist: trusty
addons:
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
packages: [ 'g++-5', 'cmake', 'cmake-data' ]
cache: apt
script:
- cmake . && make && ./cheap_ruler
+27
View File
@@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
project(cheap_ruler LANGUAGES CXX C)
include(cmake/build.cmake)
include(cmake/mason.cmake)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.10)
set(CMAKE_CONFIGURATION_TYPES Debug Release)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Wextra -Wpedantic -Wshadow")
mason_use(geometry VERSION 0.9.2 HEADER_ONLY)
mason_use(gtest VERSION 1.8.0)
mason_use(variant VERSION 1.1.4 HEADER_ONLY)
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
add_executable(cheap_ruler
${PROJECT_SOURCE_DIR}/test/cheap_ruler.cpp
)
target_include_directories(cheap_ruler
PUBLIC ${PROJECT_SOURCE_DIR}/include
)
target_add_mason_package(cheap_ruler PRIVATE geometry)
target_add_mason_package(cheap_ruler PRIVATE gtest)
target_add_mason_package(cheap_ruler PRIVATE variant)
+15
View File
@@ -0,0 +1,15 @@
ISC License
Copyright (c) 2017, Mapbox
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
+199
View File
@@ -0,0 +1,199 @@
# cheap-ruler-cpp
Port to C++ of [Cheap Ruler](https://github.com/mapbox/cheap-ruler), a collection of very fast approximations to common geodesic measurements.
[![Build Status](https://travis-ci.org/mapbox/cheap-ruler-cpp.svg?branch=master)](https://travis-ci.org/mapbox/cheap-ruler-cpp)
# Usage
```cpp
#include <mapbox/cheap_ruler.hpp>
namespace cr = mapbox::cheap_ruler;
```
All `point`, `line_string`, `polygon`, and `box` references are [mapbox::geometry](https://github.com/mapbox/geometry.hpp) data structures.
## Create a ruler object
#### `CheapRuler(double latitude, Unit unit)`
Creates a ruler object that will approximate measurements around the given latitude with an optional distance unit. Once created, the ruler object has access to the [methods](#methods) below.
```cpp
auto ruler = cr::CheapRuler(32.8351);
auto milesRuler = cr::CheapRuler(32.8351, cr::CheapRuler::Miles);
```
Possible units:
* `cheap_ruler::CheapRuler::Unit`
* `cheap_ruler::CheapRuler::Kilometers`
* `cheap_ruler::CheapRuler::Miles`
* `cheap_ruler::CheapRuler::NauticalMiles`
* `cheap_ruler::CheapRuler::Meters`
* `cheap_ruler::CheapRuler::Yards`
* `cheap_ruler::CheapRuler::Feet`
* `cheap_ruler::CheapRuler::Inches`
#### `CheapRuler::fromTile(uint32_t y, uint32_t z)`
Creates a ruler object from tile coordinates (`y` and `z` integers). Convenient in tile-reduce scripts.
```cpp
auto ruler = cr::CheapRuler::fromTile(11041, 15);
```
## Methods
#### `distance(point a, point b)`
Given two points of the form [x = longitude, y = latitude], returns the distance (`double`).
```cpp
cr::point point_a{-96.9148, 32.8351};
cr::point point_b{-96.9146, 32.8386};
auto distance = ruler.distance(point_a, point_b);
std::clog << distance; // 0.388595
```
#### `bearing(point a, point b)`
Returns the bearing (`double`) between two points in angles.
```cpp
cr::point point_a{-96.9148, 32.8351};
cr::point point_b{-96.9146, 32.8386};
auto bearing = ruler.bearing(point_a, point_b);
std::clog << bearing; // 2.76206
```
#### `destination(point origin, double distance, double bearing)`
Returns a new point (`point`) given distance and bearing from the starting point.
```cpp
cr::point point_a{-96.9148, 32.8351};
auto dest = ruler.destination(point_a, 1.0, -175);
std::clog << dest.x << ", " << dest.y; // -96.9148, 32.8261
```
#### `offset(point origin, double dx, double dy)`
Returns a new point (`point`) given easting and northing offsets from the starting point.
```cpp
cr::point point_a{-96.9148, 32.8351};
auto os = ruler.offset(point_a, 10.0, -5.0);
std::clog << os.x << ", " << os.y; // -96.808, 32.79
```
#### `lineDistance(const line_string& points)`
Given a line (an array of points), returns the total line distance (`double`).
```cpp
cr::line_string line_a{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }};
auto line_distance = ruler.lineDistance(line_a);
std::clog << line_distance; // 88.2962
```
#### `area(polygon poly)`
Given a polygon (an array of rings, where each ring is an array of points), returns the area (`double`).
```cpp
cr::linear_ring ring{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }, { -96.9, 32.8 }};
auto area = ruler.area(cr::polygon{ ring });
std::clog << area; //
```
#### `along(const line_string& line, double distance)`
Returns the point (`point`) at a specified distance along the line.
```cpp
cr::linear_ring ring{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }, { -96.9, 32.8 }};
auto area = ruler.area(cr::polygon{ ring });
std::clog << area; // 259.581
```
#### `pointOnLine(const line_string& line, point p)`
Returns a tuple of the form `std::pair<point, unsigned>` where point is closest point on the line from the given point, index is the start index of the segment with the closest point, and t is a parameter from 0 to 1 that indicates where the closest point is on that segment.
```cpp
cr::line_string line{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }};
cr::point point{-96.9, 32.79};
auto pol = ruler.pointOnLine(line, point);
auto point = std::get<0>(pol);
std::clog << point.x << ", " << point.y; // -96.9, 32.8 (point)
std::clog << std::get<1>(pol); // 0 (index)
std::clog << std::get<2>(pol); // 0. (t)
```
#### `lineSlice(point start, point stop, const line_string& line)`
Returns a part of the given line (`line_string`) between the start and the stop points (or their closest points on the line).
```cpp
cr::line_string line{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }};
cr::point start_point{-96.9, 32.8};
cr::point stop_point{-96.8, 32.8};
auto slice = ruler.lineSlice(start_point, stop_point, line);
std::clog << slice[0].x << ", " << slice[0].y; // -96.9, 32.8
std::clog << slice[1].x << ", " << slice[1].y; // -96.8, 32.8
```
#### `lineSliceAlong(double start, double stop, const line_string& line)`
Returns a part of the given line (`line_string`) between the start and the stop points indicated by distance along the line.
```cpp
cr::line_string line{{ -96.9, 32.8 }, { -96.8, 32.8 }, { -96.2, 32.3 }};
auto slice = ruler.lineSliceAlong(0.1, 1.2, line);
```
#### `bufferPoint(point p, double buffer)`
Given a point, returns a bounding box object ([w, s, e, n]) created from the given point buffered by a given distance.
```cpp
cr::point point{-96.9, 32.8};
auto box = ruler.bufferPoint(point, 0.1);
```
#### `bufferBBox(box bbox, double buffer)`
Given a bounding box, returns the box buffered by a given distance.
```cpp
cr::box bbox({ 30, 38 }, { 40, 39 });
auto bbox2 = ruler.bufferBBox(bbox, 1);
```
#### `insideBBox(point p, box bbox)`
Returns true (`bool`) if the given point is inside in the given bounding box, otherwise false.
```cpp
cr::box bbox({ 30, 38 }, { 40, 39 });
auto inside = ruler.insideBBox({ 35, 38.5 }, bbox);
std::clog << inside; // true
```
# Develop
```shell
# create targets
cmake .
# build
make
# test
./cheap_ruler
# or just do it all in one!
cmake . && make && ./cheap_ruler
```
+11
View File
@@ -0,0 +1,11 @@
# Generate source groups so the files are properly sorted in IDEs like Xcode.
function(create_source_groups target)
get_target_property(sources ${target} SOURCES)
foreach(file ${sources})
get_filename_component(file "${file}" ABSOLUTE)
string(REGEX REPLACE "^${CMAKE_SOURCE_DIR}/" "" group "${file}")
get_filename_component(group "${group}" DIRECTORY)
string(REPLACE "/" "\\" group "${group}")
source_group("${group}" FILES "${file}")
endforeach()
endfunction()
+235
View File
@@ -0,0 +1,235 @@
# Mason CMake
include(CMakeParseArguments)
function(mason_detect_platform)
# Determine platform
if(NOT MASON_PLATFORM)
# we call uname -s manually here since
# CMAKE_HOST_SYSTEM_NAME will not be defined before the project() call
execute_process(
COMMAND uname -s
OUTPUT_VARIABLE UNAME
OUTPUT_STRIP_TRAILING_WHITESPACE)
if (UNAME STREQUAL "Darwin")
set(MASON_PLATFORM "osx" PARENT_SCOPE)
else()
set(MASON_PLATFORM "linux" PARENT_SCOPE)
endif()
endif()
# Determine platform version string
if(NOT MASON_PLATFORM_VERSION)
# Android Studio only passes ANDROID_ABI, but we need to adjust that to the Mason
if(MASON_PLATFORM STREQUAL "android" AND NOT MASON_PLATFORM_VERSION)
if (ANDROID_ABI STREQUAL "armeabi")
set(MASON_PLATFORM_VERSION "arm-v5-9" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "armeabi-v7a")
set(MASON_PLATFORM_VERSION "arm-v7-9" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "arm64-v8a")
set(MASON_PLATFORM_VERSION "arm-v8-21" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "x86")
set(MASON_PLATFORM_VERSION "x86-9" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "x86_64")
set(MASON_PLATFORM_VERSION "x86-64-21" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "mips")
set(MASON_PLATFORM_VERSION "mips-9" PARENT_SCOPE)
elseif (ANDROID_ABI STREQUAL "mips64")
set(MASON_PLATFORM_VERSION "mips-64-9" PARENT_SCOPE)
else()
message(FATAL_ERROR "Unknown ANDROID_ABI '${ANDROID_ABI}'.")
endif()
elseif(MASON_PLATFORM STREQUAL "ios")
set(MASON_PLATFORM_VERSION "8.0" PARENT_SCOPE)
else()
execute_process(
COMMAND uname -m
OUTPUT_VARIABLE MASON_PLATFORM_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(MASON_PLATFORM_VERSION "${MASON_PLATFORM_VERSION}" PARENT_SCOPE)
endif()
endif()
endfunction()
function(mason_use _PACKAGE)
if(NOT _PACKAGE)
message(FATAL_ERROR "[Mason] No package name given")
endif()
cmake_parse_arguments("" "HEADER_ONLY" "VERSION" "" ${ARGN})
if(_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "[Mason] mason_use() called with unrecognized arguments: ${_UNPARSED_ARGUMENTS}")
endif()
if(NOT _VERSION)
message(FATAL_ERROR "[Mason] Specifying a version is required")
endif()
if(MASON_PACKAGE_${_PACKAGE}_INVOCATION STREQUAL "${MASON_INVOCATION}")
# Check that the previous invocation of mason_use didn't select another version of this package
if(NOT MASON_PACKAGE_${_PACKAGE}_VERSION STREQUAL ${_VERSION})
message(FATAL_ERROR "[Mason] Already using ${_PACKAGE} ${MASON_PACKAGE_${_PACKAGE}_VERSION}. Cannot select version ${_VERSION}.")
endif()
else()
if(_HEADER_ONLY)
set(_PLATFORM_ID "headers")
else()
set(_PLATFORM_ID "${MASON_PLATFORM}-${MASON_PLATFORM_VERSION}")
endif()
set(_SLUG "${_PLATFORM_ID}/${_PACKAGE}/${_VERSION}")
set(_INSTALL_PATH "${MASON_PACKAGE_DIR}/${_SLUG}")
file(RELATIVE_PATH _INSTALL_PATH_RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${_INSTALL_PATH}")
if(NOT EXISTS "${_INSTALL_PATH}")
set(_CACHE_PATH "${MASON_PACKAGE_DIR}/.binaries/${_SLUG}.tar.gz")
if (NOT EXISTS "${_CACHE_PATH}")
# Download the package
set(_URL "${MASON_REPOSITORY}/${_SLUG}.tar.gz")
message("[Mason] Downloading package ${_URL}...")
set(_FAILED)
set(_ERROR)
# Note: some CMake versions are compiled without SSL support
get_filename_component(_CACHE_DIR "${_CACHE_PATH}" DIRECTORY)
file(MAKE_DIRECTORY "${_CACHE_DIR}")
execute_process(
COMMAND curl --retry 3 -s -f -S -L "${_URL}" -o "${_CACHE_PATH}.tmp"
RESULT_VARIABLE _FAILED
ERROR_VARIABLE _ERROR)
if(_FAILED)
message(FATAL_ERROR "[Mason] Failed to download ${_URL}: ${_ERROR}")
else()
# We downloaded to a temporary file to prevent half-finished downloads
file(RENAME "${_CACHE_PATH}.tmp" "${_CACHE_PATH}")
endif()
endif()
# Unpack the package
message("[Mason] Unpacking package to ${_INSTALL_PATH_RELATIVE}...")
file(MAKE_DIRECTORY "${_INSTALL_PATH}")
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xzf "${_CACHE_PATH}"
WORKING_DIRECTORY "${_INSTALL_PATH}")
endif()
# Error out if there is no config file.
if(NOT EXISTS "${_INSTALL_PATH}/mason.ini")
message(FATAL_ERROR "[Mason] Could not find mason.ini for package ${_PACKAGE} ${_VERSION}")
endif()
set(MASON_PACKAGE_${_PACKAGE}_PREFIX "${_INSTALL_PATH}" CACHE STRING "${_PACKAGE} ${_INSTALL_PATH}" FORCE)
mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_PREFIX)
# Load the configuration from the ini file
file(STRINGS "${_INSTALL_PATH}/mason.ini" _CONFIG_FILE)
foreach(_LINE IN LISTS _CONFIG_FILE)
string(REGEX MATCH "^([a-z_]+) *= *" _KEY "${_LINE}")
if (_KEY)
string(LENGTH "${_KEY}" _KEY_LENGTH)
string(SUBSTRING "${_LINE}" ${_KEY_LENGTH} -1 _VALUE)
string(REGEX REPLACE ";.*$" "" _VALUE "${_VALUE}") # Trim trailing commas
string(REPLACE "{prefix}" "${_INSTALL_PATH}" _VALUE "${_VALUE}")
string(STRIP "${_VALUE}" _VALUE)
string(REPLACE "=" "" _KEY "${_KEY}")
string(STRIP "${_KEY}" _KEY)
string(TOUPPER "${_KEY}" _KEY)
if(_KEY STREQUAL "INCLUDE_DIRS" OR _KEY STREQUAL "STATIC_LIBS" )
separate_arguments(_VALUE)
endif()
set(MASON_PACKAGE_${_PACKAGE}_${_KEY} "${_VALUE}" CACHE STRING "${_PACKAGE} ${_KEY}" FORCE)
mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_${_KEY})
endif()
endforeach()
# Compare version in the package to catch errors early on
if(NOT _VERSION STREQUAL MASON_PACKAGE_${_PACKAGE}_VERSION)
message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has version '${MASON_PACKAGE_${_PACKAGE}_VERSION}', but required '${_VERSION}'")
endif()
if(NOT _PACKAGE STREQUAL MASON_PACKAGE_${_PACKAGE}_NAME)
message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has name '${MASON_PACKAGE_${_PACKAGE}_NAME}', but required '${_NAME}'")
endif()
if(NOT _HEADER_ONLY)
if(NOT MASON_PLATFORM STREQUAL MASON_PACKAGE_${_PACKAGE}_PLATFORM)
message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has platform '${MASON_PACKAGE_${_PACKAGE}_PLATFORM}', but required '${MASON_PLATFORM}'")
endif()
if(NOT MASON_PLATFORM_VERSION STREQUAL MASON_PACKAGE_${_PACKAGE}_PLATFORM_VERSION)
message(FATAL_ERROR "[Mason] Package at ${_INSTALL_PATH_RELATIVE} has platform version '${MASON_PACKAGE_${_PACKAGE}_PLATFORM_VERSION}', but required '${MASON_PLATFORM_VERSION}'")
endif()
endif()
# Concatenate the static libs and libraries
set(_LIBRARIES)
list(APPEND _LIBRARIES ${MASON_PACKAGE_${_PACKAGE}_STATIC_LIBS} ${MASON_PACKAGE_${_PACKAGE}_LDFLAGS})
set(MASON_PACKAGE_${_PACKAGE}_LIBRARIES "${_LIBRARIES}" CACHE STRING "${_PACKAGE} _LIBRARIES" FORCE)
mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_LIBRARIES)
if(NOT _HEADER_ONLY)
string(REGEX MATCHALL "(^| +)-L *([^ ]+)" MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS "${MASON_PACKAGE_${_PACKAGE}_LDFLAGS}")
string(REGEX REPLACE "(^| +)-L *" "\\1" MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS "${MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS}")
set(MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS "${MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS}" CACHE STRING "${_PACKAGE} ${MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS}" FORCE)
mark_as_advanced(MASON_PACKAGE_${_PACKAGE}_LIBRARY_DIRS)
endif()
# Store invocation ID to prevent different versions of the same package in one invocation
set(MASON_PACKAGE_${_PACKAGE}_INVOCATION "${MASON_INVOCATION}" CACHE INTERNAL "${_PACKAGE} invocation ID" FORCE)
endif()
endfunction()
macro(target_add_mason_package _TARGET _VISIBILITY _PACKAGE)
if (NOT MASON_PACKAGE_${_PACKAGE}_INVOCATION)
message(FATAL_ERROR "[Mason] Package ${_PACKAGE} has not been initialized yet")
endif()
target_include_directories(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_INCLUDE_DIRS}")
target_compile_definitions(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_DEFINITIONS}")
target_compile_options(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_OPTIONS}")
target_link_libraries(${_TARGET} ${_VISIBILITY} "${MASON_PACKAGE_${_PACKAGE}_LIBRARIES}")
endmacro()
# Setup
string(RANDOM LENGTH 16 MASON_INVOCATION)
# Read environment variables if CMake is run in command mode
if (CMAKE_ARGC)
set(MASON_PLATFORM "$ENV{MASON_PLATFORM}")
set(MASON_PLATFORM_VERSION "$ENV{MASON_PLATFORM_VERSION}")
set(MASON_PACKAGE_DIR "$ENV{MASON_PACKAGE_DIR}")
set(MASON_REPOSITORY "$ENV{MASON_REPOSITORY}")
endif()
# Directory where Mason packages are located; typically ends with mason_packages
if (NOT MASON_PACKAGE_DIR)
set(MASON_PACKAGE_DIR "${CMAKE_SOURCE_DIR}/mason_packages")
endif()
# URL prefix of where packages are located.
if (NOT MASON_REPOSITORY)
set(MASON_REPOSITORY "https://mason-binaries.s3.amazonaws.com")
endif()
mason_detect_platform()
# Execute commands if CMake is run in command mode
if (CMAKE_ARGC)
# Collect remaining arguments for passing to mason_use
set(_MASON_ARGS)
foreach(I RANGE 4 ${CMAKE_ARGC})
list(APPEND _MASON_ARGS "${CMAKE_ARGV${I}}")
endforeach()
# Install the package
mason_use(${_MASON_ARGS})
# Optionally print variables
if(DEFINED MASON_PACKAGE_${CMAKE_ARGV4}_${CMAKE_ARGV3})
# CMake can't write to stdout with message()
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${MASON_PACKAGE_${CMAKE_ARGV4}_${CMAKE_ARGV3}}")
endif()
endif()

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