diff --git a/.gitignore b/.gitignore index 6ed5d2a4e..6264b4294 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# pre compiled dependencies # +############################# +osrm-deps + # Compiled source # ################### *.com diff --git a/.travis.yml b/.travis.yml index ed023e4e9..55e719035 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,7 @@ script: - make - make tests - make benchmarks + - ./algorithm-tests - ./datastructure-tests - cd .. - cucumber -p verify @@ -58,7 +59,7 @@ notifications: skip_join: false recipients: - - dennis@mapbox.com + - patrick@mapbox.com email: on_success: change on_failure: always diff --git a/CMakeLists.txt b/CMakeLists.txt index 503171a59..80eb3d3fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,7 +90,7 @@ set( add_library(COORDINATE OBJECT ${CoordinateGlob}) add_library(GITDESCRIPTION OBJECT util/git_sha.cpp) -add_library(OSRM ${OSRMSources} $ $ $ $ $ $ $ $ $) +add_library(OSRM ${OSRMSources} $ $ $ $ $ $ $ $ $ $) add_library(FINGERPRINT OBJECT util/fingerprint.cpp) add_dependencies(FINGERPRINT FingerPrintConfigure) @@ -328,12 +328,10 @@ if(WITH_TOOLS OR BUILD_TOOLS) if(UNIX AND NOT APPLE) target_link_libraries(osrm-unlock-all rt) endif() - add_executable(osrm-check-hsgr tools/check-hsgr.cpp $ $ $) - target_link_libraries(osrm-check-hsgr ${Boost_LIBRARIES}) + add_executable(osrm-check-hsgr tools/check-hsgr.cpp $ $ $ $) + target_link_libraries(osrm-check-hsgr ${Boost_LIBRARIES} ${TBB_LIBRARIES}) add_executable(osrm-springclean tools/springclean.cpp $ $ $ $) target_link_libraries(osrm-springclean ${Boost_LIBRARIES}) - add_executable(osrm-graph-compare tools/graph_compare.cpp $ $ $ $ $ $ $) - target_link_libraries(osrm-graph-compare ${Boost_LIBRARIES} ${TBB_LIBRARIES}) install(TARGETS osrm-cli DESTINATION bin) install(TARGETS osrm-io-benchmark DESTINATION bin) diff --git a/Gemfile.lock b/Gemfile.lock index ac056a0a2..3363e9218 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,22 +2,27 @@ GEM remote: http://rubygems.org/ specs: builder (3.2.2) - cucumber (1.3.8) + cucumber (2.0.0) builder (>= 2.1.2) + cucumber-core (~> 1.1.3) diff-lcs (>= 1.1.3) - gherkin (~> 2.12.1) + gherkin (~> 2.12) multi_json (>= 1.7.5, < 2.0) - multi_test (>= 0.0.2) - diff-lcs (1.2.4) - gherkin (2.12.1) + multi_test (>= 0.1.2) + cucumber-core (1.1.3) + gherkin (~> 2.12.0) + diff-lcs (1.2.5) + gherkin (2.12.2) multi_json (~> 1.3) - multi_json (1.8.0) - multi_test (0.0.2) + multi_json (1.11.0) + multi_test (0.1.2) osmlib-base (0.1.4) - rake (10.1.0) - rspec-expectations (2.14.3) - diff-lcs (>= 1.1.3, < 2.0) - sys-proctable (0.9.3) + rake (10.4.2) + rspec-expectations (3.2.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.2.0) + rspec-support (3.2.2) + sys-proctable (0.9.8) PLATFORMS ruby diff --git a/README.md b/README.md index 624cc1da7..d319d26bc 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,25 @@ -# Readme +## About -For instructions on how to compile and run OSRM, please consult the Wiki at +The Open Source Routing Machine is a high performance routing engine written in C++11 designed to run on OpenStreetMap data. -https://github.com/Project-OSRM/osrm-backend/wiki +## Current build status -or use our free and daily updated online service at +| build config | branch | status | +|:-------------|:--------|:------------| +| Linux | master | [![Build Status](https://travis-ci.org/Project-OSRM/osrm-backend.png?branch=master)](https://travis-ci.org/Project-OSRM/osrm-backend) | +| Linux | develop | [![Build Status](https://travis-ci.org/Project-OSRM/osrm-backend.png?branch=develop)](https://travis-ci.org/Project-OSRM/osrm-backend) | +| Windows | master/develop | [![Build status](https://ci.appveyor.com/api/projects/status/4iuo3s9gxprmcjjh)](https://ci.appveyor.com/project/DennisOSRM/osrm-backend) | +| LUAbind fork | master | [![Build Status](https://travis-ci.org/DennisOSRM/luabind.png?branch=master)](https://travis-ci.org/DennisOSRM/luabind) | -http://map.project-osrm.org +## Building + +For instructions on how to [build](https://github.com/Project-OSRM/osrm-backend/wiki/Building-OSRM) and [run OSRM](https://github.com/Project-OSRM/osrm-backend/wiki/Running-OSRM), please consult [the Wiki](https://github.com/Project-OSRM/osrm-backend/wiki). + +To quickly try OSRM use our [free and daily updated online service](http://map.project-osrm.org) + +## Documentation + +See the Wiki's [server API documentation](https://github.com/Project-OSRM/osrm-backend/wiki/Server-api) as well as the [library API documentation](https://github.com/Project-OSRM/osrm-backend/wiki/Library-api) ## References in publications @@ -31,11 +44,3 @@ When using the code in a (scientific) publication, please cite } ``` -## Current build status - -| build config | branch | status | -|:-------------|:--------|:------------| -| Linux | master | [![Build Status](https://travis-ci.org/Project-OSRM/osrm-backend.png?branch=master)](https://travis-ci.org/DennisOSRM/Project-OSRM) | -| Linux | develop | [![Build Status](https://travis-ci.org/Project-OSRM/osrm-backend.png?branch=develop)](https://travis-ci.org/DennisOSRM/Project-OSRM) | -| Windows | master/develop | [![Build status](https://ci.appveyor.com/api/projects/status/4iuo3s9gxprmcjjh)](https://ci.appveyor.com/project/DennisOSRM/osrm-backend) | -| LUAbind fork | master | [![Build Status](https://travis-ci.org/DennisOSRM/luabind.png?branch=master)](https://travis-ci.org/DennisOSRM/luabind) | diff --git a/algorithms/polyline_compressor.cpp b/algorithms/polyline_compressor.cpp index d5fd5828b..0db75dcc6 100644 --- a/algorithms/polyline_compressor.cpp +++ b/algorithms/polyline_compressor.cpp @@ -88,3 +88,41 @@ PolylineCompressor::get_encoded_string(const std::vector &po } return encode_vector(delta_numbers); } + +std::vector PolylineCompressor::decode_string(const std::string &geometry_string) const +{ + std::vector new_coordinates; + int index = 0, len = geometry_string.size(); + int lat = 0, lng = 0; + + while (index < len) + { + int b, shift = 0, result = 0; + do + { + b = geometry_string.at(index++) - 63; + result |= (b & 0x1f) << shift; + shift += 5; + } while (b >= 0x20); + int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); + lat += dlat; + + shift = 0; + result = 0; + do + { + b = geometry_string.at(index++) - 63; + result |= (b & 0x1f) << shift; + shift += 5; + } while (b >= 0x20); + int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); + lng += dlng; + + FixedPointCoordinate p; + p.lat = COORDINATE_PRECISION * (((double) lat / 1E6)); + p.lon = COORDINATE_PRECISION * (((double) lng / 1E6)); + new_coordinates.push_back(p); + } + + return new_coordinates; +} diff --git a/algorithms/polyline_compressor.hpp b/algorithms/polyline_compressor.hpp index 933ac7afa..a148200ca 100644 --- a/algorithms/polyline_compressor.hpp +++ b/algorithms/polyline_compressor.hpp @@ -30,6 +30,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. struct SegmentInformation; +#include + #include #include @@ -42,6 +44,8 @@ class PolylineCompressor public: std::string get_encoded_string(const std::vector &polyline) const; + + std::vector decode_string(const std::string &geometry_string) const; }; #endif /* POLYLINECOMPRESSOR_H_ */ diff --git a/algorithms/tiny_components.hpp b/algorithms/tiny_components.hpp index 8eb61a912..8abc1cbc6 100644 --- a/algorithms/tiny_components.hpp +++ b/algorithms/tiny_components.hpp @@ -150,7 +150,7 @@ template class TarjanSCC // Traverse outgoing edges if (barrier_node_set.find(v) != barrier_node_set.end() && u != vprime) { - // continue; + continue; } if (to_node_of_only_restriction != std::numeric_limits::max() && diff --git a/appveyor-build.bat b/appveyor-build.bat new file mode 100644 index 000000000..eef29b1ba --- /dev/null +++ b/appveyor-build.bat @@ -0,0 +1,113 @@ +@ECHO OFF +SETLOCAL +SET EL=0 + +ECHO platform^: %platform% +:: HARDCODE "x64" as it is uppercase on AppVeyor and download from S3 is case sensitive +SET DEPSPKG=osrm-deps-win-x64-14.0.7z + +:: local development +IF "%computername%"=="MB" GOTO SKIPDL + +IF EXIST %DEPSPKG% DEL %DEPSPKG% +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +ECHO downloading %DEPSPKG% +powershell Invoke-WebRequest https://mapbox.s3.amazonaws.com/windows-builds/windows-deps/$env:DEPSPKG -OutFile C:\projects\osrm\$env:DEPSPKG +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +7z -y x %DEPSPKG% | %windir%\system32\FIND "ing archive" +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +:SKIPDL + +IF EXIST build rd /s /q build +IF %ERRORLEVEL% NEQ 0 GOTO ERROR +mkdir build +IF %ERRORLEVEL% NEQ 0 GOTO ERROR +cd build +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +SET OSRMDEPSDIR=c:\projects\osrm\osrm-deps +set PREFIX=%OSRMDEPSDIR%/libs +set BOOST_ROOT=%OSRMDEPSDIR%/boost +set TBB_INSTALL_DIR=%OSRMDEPSDIR%/tbb +set TBB_ARCH_PLATFORM=intel64/vc14 + +ECHO calling cmake .... +cmake .. ^ +-G "Visual Studio 14 Win64" ^ +-DBOOST_ROOT=%BOOST_ROOT% ^ +-DBoost_ADDITIONAL_VERSIONS=1.57 ^ +-DBoost_USE_MULTITHREADED=ON ^ +-DBoost_USE_STATIC_LIBS=ON ^ +-DCMAKE_BUILD_TYPE=%CONFIGURATION% ^ +-DCMAKE_INSTALL_PREFIX=%PREFIX% +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +ECHO building ... +msbuild OSRM.sln ^ +/p:Configuration=%Configuration% ^ +/p:Platform=x64 ^ +/t:rebuild ^ +/p:BuildInParallel=true ^ +/m:%NUMBER_OF_PROCESSORS% ^ +/toolsversion:14.0 ^ +/p:PlatformToolset=v140 ^ +/clp:Verbosity=normal ^ +/nologo ^ +/flp1:logfile=build_errors.txt;errorsonly ^ +/flp2:logfile=build_warnings.txt;warningsonly +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +ECHO ========= TODO^: CREATE PACKAGES ========== + +CD c:\projects\osrm\build\%Configuration% +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +SET PATH=c:\projects\osrm\osrm-deps\libs\bin;%PATH% + +ECHO running datastructure-tests.exe ... +datastructure-tests.exe +IF %ERRORLEVEL% NEQ 0 GOTO ERROR +ECHO running algorithm-tests.exe ... +algorithm-tests.exe +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +GOTO DONE + +:ERROR +SET EL=%ERRORLEVEL% +ECHO ============== ERROR =============== + +:DONE +ECHO ============= DONE =============== +CD C:\projects\osrm +EXIT /b %EL% + + + + + - cd c:/projects/osrm + - mkdir build + - cd build + - echo Running cmake... + - call "%VS120COMNTOOLS%\..\..\VC\vcvarsall.bat" x86_amd64 + - SET PATH=C:\Program Files (x86)\MSBuild\12.0\bin\;%PATH% + - SET P=c:/projects/osrm + - set TBB_INSTALL_DIR=%P%/tbb + - set TBB_ARCH_PLATFORM=intel64/vc12 + - cmake .. -G "Visual Studio 14 Win64" -DCMAKE_BUILD_TYPE=%Configuration% -DCMAKE_INSTALL_PREFIX=%P%/libs -DBOOST_ROOT=%P%/boost_min -DBoost_ADDITIONAL_VERSIONS=1.57 -DBoost_USE_STATIC_LIBS=ON + - SET PLATFORM_TOOLSET=v140 + - SET TOOLS_VERSION=14.0 + - msbuild /p:Platform=x64 /clp:Verbosity=minimal /toolsversion:%TOOLS_VERSION% /p:PlatformToolset=%PLATFORM_TOOLSET% /nologo OSRM.sln + - msbuild /p:Platform=x64 /clp:Verbosity=minimal /toolsversion:%TOOLS_VERSION% /p:PlatformToolset=%PLATFORM_TOOLSET% /nologo tests.vcxproj + - cd %Configuration% + - if "%APPVEYOR_REPO_BRANCH%"=="develop" (7z a %P%/osrm_%Configuration%.zip *.exe *.pdb %P%/libs/bin/*.dll -tzip) + - cd ..\..\profiles + - echo disk=c:\temp\stxxl,10000,wincall > .stxxl.txt + - if "%APPVEYOR_REPO_BRANCH%"=="develop" (7z a %P%/osrm_%Configuration%.zip * -tzip) + - set PATH=%PATH%;c:/projects/osrm/libs/bin + - cd c:/projects/osrm/build/%Configuration% + - datastructure-tests.exe + - algorithm-tests.exe diff --git a/appveyor.yml b/appveyor.yml index afb8dd406..cf45a3224 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,60 +1,33 @@ environment: matrix: - - configuration: Debug - configuration: Release - -# branches to build -branches: - # whitelist - only: - - develop +# - configuration: Debug # scripts that are called at very beginning, before repo cloning init: - git config --global core.autocrlf input +os: Visual Studio 2015 RC + # clone directory clone_folder: c:\projects\osrm platform: x64 install: - # by default, all script lines are interpreted as batch - - nuget install protobuf - - cd c:\projects\osrm - - curl -O http://build.project-osrm.org/libs_osrm_%Configuration%.7z - - 7z x libs_osrm_%Configuration%.7z | find ":" + - set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH% + - CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 build_script: - - cd c:/projects/osrm - - mkdir build - - cd build - - echo Running cmake... - - call "%VS120COMNTOOLS%\..\..\VC\vcvarsall.bat" x86_amd64 - - SET PATH=C:\Program Files (x86)\MSBuild\12.0\bin\;%PATH% - - SET P=c:/projects/osrm - - set TBB_INSTALL_DIR=%P%/tbb - - set TBB_ARCH_PLATFORM=intel64/vc12 - - cmake .. -G "Visual Studio 12 Win64" -DCMAKE_BUILD_TYPE=%Configuration% -DCMAKE_INSTALL_PREFIX=%P%/libs -DBOOST_ROOT=%P%/boost_min -DBoost_ADDITIONAL_VERSIONS=1.57 -DBoost_USE_STATIC_LIBS=ON -T CTP_Nov2013 - - msbuild /clp:Verbosity=minimal /nologo OSRM.sln - - msbuild /clp:Verbosity=minimal /nologo tests.vcxproj - - cd %Configuration% - - if "%APPVEYOR_REPO_BRANCH%"=="develop" (7z a %P%/osrm_%Configuration%.zip *.exe *.pdb %P%/libs/bin/*.dll -tzip) - - cd ..\..\profiles - - echo disk=c:\temp\stxxl,10000,wincall > .stxxl.txt - - if "%APPVEYOR_REPO_BRANCH%"=="develop" (7z a %P%/osrm_%Configuration%.zip * -tzip) - - set PATH=%PATH%;c:/projects/osrm/libs/bin - - cd c:/projects/osrm/build/%Configuration% - - datastructure-tests.exe - - algorithm-tests.exe + - CALL appveyor-build.bat test: off artifacts: - - path: osrm_Debug.zip - name: osrm_Debug.zip - path: osrm_Release.zip name: osrm_Release.zip +# - path: osrm_Debug.zip +# name: osrm_Debug.zip deploy: provider: FTP diff --git a/build-local.bat b/build-local.bat new file mode 100644 index 000000000..47a1fb3a6 --- /dev/null +++ b/build-local.bat @@ -0,0 +1,19 @@ +@ECHO OFF + +SET PLATFORM=x64 +SET CONFIGURATION=Release + +WHERE msbuild +IF %ERRORLEVEL% EQU 0 GOTO RUNBUILD + +SET PATH=C:\mb\windows-builds-64\tmp-bin\cmake-3.1.0-win32-x86\bin;%PATH% +SET PATH=C:\Program Files\7-Zip;%PATH% +ECHO activating VS command prompt ... +SET PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH% +CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 + +:RUNBUILD + +powershell Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted -Force +CALL appveyor-build.bat +EXIT /b %ERRORLEVEL% diff --git a/contractor/contractor.hpp b/contractor/contractor.hpp index ccfabbc8f..4468f5e49 100644 --- a/contractor/contractor.hpp +++ b/contractor/contractor.hpp @@ -171,7 +171,8 @@ class Contractor { SimpleLogger().Write(logWARNING) << "Edge weight large -> " - << static_cast(std::max(diter->weight, 1)); + << static_cast(std::max(diter->weight, 1)) << " : " + << static_cast(diter->source) << " -> " << static_cast(diter->target); } #endif edges.emplace_back(diter->source, diter->target, @@ -186,6 +187,7 @@ class Contractor } // clear input vector input_edge_list.clear(); + // FIXME not sure if we need this edges.shrink_to_fit(); tbb::parallel_sort(edges.begin(), edges.end()); @@ -953,7 +955,6 @@ class Contractor } std::shared_ptr contractor_graph; - std::vector contracted_edge_list; stxxl::vector external_edge_list; std::vector orig_node_id_to_new_id_map; XORFastHash fast_hash; diff --git a/contractor/contractor_options.cpp b/contractor/contractor_options.cpp new file mode 100644 index 000000000..eaa1ba997 --- /dev/null +++ b/contractor/contractor_options.cpp @@ -0,0 +1,135 @@ +/* + +Copyright (c) 2015, Project OSRM contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "contractor_options.hpp" + +#include "../util/git_sha.hpp" +#include "../util/simple_logger.hpp" + +#include +#include + +#include + +return_code +ContractorOptions::ParseArguments(int argc, char *argv[], ContractorConfig &contractor_config) +{ + // declare a group of options that will be allowed only on command line + boost::program_options::options_description generic_options("Options"); + generic_options.add_options()("version,v", "Show version")("help,h", "Show this help message")( + "config,c", boost::program_options::value(&contractor_config.config_file_path) + ->default_value("contractor.ini"), + "Path to a configuration file."); + + // declare a group of options that will be allowed both on command line and in config file + boost::program_options::options_description config_options("Configuration"); + config_options.add_options()( + "restrictions,r", + boost::program_options::value(&contractor_config.restrictions_path), + "Restrictions file in .osrm.restrictions format")( + "profile,p", boost::program_options::value(&contractor_config.profile_path) + ->default_value("profile.lua"), + "Path to LUA routing profile")( + "threads,t", boost::program_options::value(&contractor_config.requested_num_threads) + ->default_value(tbb::task_scheduler_init::default_num_threads()), + "Number of threads to use"); + + // hidden options, will be allowed both on command line and in config file, but will not be + // shown to the user + boost::program_options::options_description hidden_options("Hidden options"); + hidden_options.add_options()( + "input,i", boost::program_options::value(&contractor_config.osrm_input_path), + "Input file in .osm, .osm.bz2 or .osm.pbf format"); + + // positional option + boost::program_options::positional_options_description positional_options; + positional_options.add("input", 1); + + // combine above options for parsing + boost::program_options::options_description cmdline_options; + cmdline_options.add(generic_options).add(config_options).add(hidden_options); + + boost::program_options::options_description config_file_options; + config_file_options.add(config_options).add(hidden_options); + + boost::program_options::options_description visible_options( + "Usage: " + boost::filesystem::basename(argv[0]) + " [options]"); + visible_options.add(generic_options).add(config_options); + + // parse command line options + boost::program_options::variables_map option_variables; + boost::program_options::store(boost::program_options::command_line_parser(argc, argv) + .options(cmdline_options) + .positional(positional_options) + .run(), + option_variables); + + const auto &temp_config_path = option_variables["config"].as(); + if (boost::filesystem::is_regular_file(temp_config_path)) + { + boost::program_options::store(boost::program_options::parse_config_file( + temp_config_path.string().c_str(), cmdline_options, true), + option_variables); + } + + if (option_variables.count("version")) + { + SimpleLogger().Write() << g_GIT_DESCRIPTION; + return return_code::exit; + } + + if (option_variables.count("help")) + { + SimpleLogger().Write() << "\n" << visible_options; + return return_code::exit; + } + + boost::program_options::notify(option_variables); + + if (!option_variables.count("restrictions")) + { + contractor_config.restrictions_path = contractor_config.osrm_input_path.string() + ".restrictions"; + } + + if (!option_variables.count("input")) + { + SimpleLogger().Write() << "\n" << visible_options; + return return_code::fail; + } + + return return_code::ok; +} + +void ContractorOptions::GenerateOutputFilesNames(ContractorConfig &contractor_config) +{ + contractor_config.node_output_path = contractor_config.osrm_input_path.string() + ".nodes"; + contractor_config.edge_output_path = contractor_config.osrm_input_path.string() + ".edges"; + contractor_config.geometry_output_path = contractor_config.osrm_input_path.string() + ".geometry"; + contractor_config.graph_output_path = contractor_config.osrm_input_path.string() + ".hsgr"; + contractor_config.rtree_nodes_output_path = contractor_config.osrm_input_path.string() + ".ramIndex"; + contractor_config.rtree_leafs_output_path = contractor_config.osrm_input_path.string() + ".fileIndex"; +} diff --git a/contractor/contractor_options.hpp b/contractor/contractor_options.hpp new file mode 100644 index 000000000..87541af9d --- /dev/null +++ b/contractor/contractor_options.hpp @@ -0,0 +1,68 @@ +/* + +Copyright (c) 2015, Project OSRM contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef CONTRACTOR_OPTIONS_HPP +#define CONTRACTOR_OPTIONS_HPP + +#include + +#include + +enum class return_code : unsigned +{ + ok, + fail, + exit +}; + +struct ContractorConfig +{ + ContractorConfig() noexcept : requested_num_threads(0) {} + + boost::filesystem::path config_file_path; + boost::filesystem::path osrm_input_path; + boost::filesystem::path restrictions_path; + boost::filesystem::path profile_path; + + std::string node_output_path; + std::string edge_output_path; + std::string geometry_output_path; + std::string graph_output_path; + std::string rtree_nodes_output_path; + std::string rtree_leafs_output_path; + + unsigned requested_num_threads; +}; + +struct ContractorOptions +{ + static return_code ParseArguments(int argc, char *argv[], ContractorConfig &extractor_config); + + static void GenerateOutputFilesNames(ContractorConfig &extractor_config); +}; + +#endif // EXTRACTOR_OPTIONS_HPP diff --git a/contractor/edge_based_graph_factory.cpp b/contractor/edge_based_graph_factory.cpp index 268bb9f8d..dbd73a73c 100644 --- a/contractor/edge_based_graph_factory.cpp +++ b/contractor/edge_based_graph_factory.cpp @@ -40,21 +40,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -EdgeBasedGraphFactory::EdgeBasedGraphFactory( - const std::shared_ptr &node_based_graph, - std::unique_ptr restriction_map, - std::vector &barrier_node_list, - std::vector &traffic_light_node_list, - std::vector &node_info_list, - SpeedProfileProperties &speed_profile) +EdgeBasedGraphFactory::EdgeBasedGraphFactory(std::shared_ptr node_based_graph, + std::shared_ptr restriction_map, + std::unique_ptr> barrier_node_list, + std::unique_ptr> traffic_light_node_list, + const std::vector &node_info_list, + const SpeedProfileProperties &speed_profile) : speed_profile(speed_profile), m_number_of_edge_based_nodes(std::numeric_limits::max()), - m_node_info_list(node_info_list), m_node_based_graph(node_based_graph), + m_node_info_list(node_info_list), + m_node_based_graph(std::move(node_based_graph)), m_restriction_map(std::move(restriction_map)), max_id(0), removed_node_count(0) { // insert into unordered sets for fast lookup - m_barrier_nodes.insert(barrier_node_list.begin(), barrier_node_list.end()); - m_traffic_lights.insert(traffic_light_node_list.begin(), traffic_light_node_list.end()); + m_barrier_nodes.insert(barrier_node_list->begin(), barrier_node_list->end()); + m_traffic_lights.insert(traffic_light_node_list->begin(), traffic_light_node_list->end()); } void EdgeBasedGraphFactory::GetEdgeBasedEdges(DeallocatingVector &output_edge_list) @@ -193,15 +193,16 @@ void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, { BOOST_ASSERT(forward_data.forward); } + else + { + BOOST_ASSERT(!forward_data.forward); + } + if (reverse_data.edgeBasedNodeID != SPECIAL_NODEID) { BOOST_ASSERT(reverse_data.forward); } - if (forward_data.edgeBasedNodeID == SPECIAL_NODEID) - { - BOOST_ASSERT(!forward_data.forward); - } - if (reverse_data.edgeBasedNodeID == SPECIAL_NODEID) + else { BOOST_ASSERT(!reverse_data.forward); } @@ -233,7 +234,6 @@ void EdgeBasedGraphFactory::Run(const std::string &original_edge_data_filename, const std::string &geometry_filename, lua_State *lua_state) { - TIMER_START(geometry); CompressGeometry(); TIMER_STOP(geometry); @@ -290,12 +290,33 @@ void EdgeBasedGraphFactory::CompressGeometry() continue; } + /* + * reverse_e2 forward_e2 + * u <---------- v -----------> w + * ----------> <----------- + * forward_e1 reverse_e1 + * + * Will be compressed to: + * + * reverse_e1 + * u <---------- w + * ----------> + * forward_e1 + * + * If the edges are compatible. + * + */ + const bool reverse_edge_order = !(m_node_based_graph->GetEdgeData(m_node_based_graph->BeginEdges(node_v)).forward); const EdgeID forward_e2 = m_node_based_graph->BeginEdges(node_v) + reverse_edge_order; BOOST_ASSERT(SPECIAL_EDGEID != forward_e2); + BOOST_ASSERT(forward_e2 >= m_node_based_graph->BeginEdges(node_v) && + forward_e2 < m_node_based_graph->EndEdges(node_v)); const EdgeID reverse_e2 = m_node_based_graph->BeginEdges(node_v) + 1 - reverse_edge_order; BOOST_ASSERT(SPECIAL_EDGEID != reverse_e2); + BOOST_ASSERT(reverse_e2 >= m_node_based_graph->BeginEdges(node_v) && + reverse_e2 < m_node_based_graph->EndEdges(node_v)); const EdgeData &fwd_edge_data2 = m_node_based_graph->GetEdgeData(forward_e2); const EdgeData &rev_edge_data2 = m_node_based_graph->GetEdgeData(reverse_e2); @@ -322,9 +343,20 @@ void EdgeBasedGraphFactory::CompressGeometry() continue; } - if ( // TODO: rename to IsCompatibleTo - fwd_edge_data1.IsEqualTo(fwd_edge_data2) && rev_edge_data1.IsEqualTo(rev_edge_data2)) + // this case can happen if two ways with different names overlap + if (fwd_edge_data1.nameID != rev_edge_data1.nameID || + fwd_edge_data2.nameID != rev_edge_data2.nameID) { + continue; + } + + if (fwd_edge_data1.IsCompatibleTo(fwd_edge_data2) && rev_edge_data1.IsCompatibleTo(rev_edge_data2)) + { + BOOST_ASSERT(m_node_based_graph->GetEdgeData(forward_e1).nameID == + m_node_based_graph->GetEdgeData(reverse_e1).nameID); + BOOST_ASSERT(m_node_based_graph->GetEdgeData(forward_e2).nameID == + m_node_based_graph->GetEdgeData(reverse_e2).nameID); + // Get distances before graph is modified const int forward_weight1 = m_node_based_graph->GetEdgeData(forward_e1).distance; const int forward_weight2 = m_node_based_graph->GetEdgeData(forward_e2).distance; @@ -336,7 +368,7 @@ void EdgeBasedGraphFactory::CompressGeometry() const int reverse_weight2 = m_node_based_graph->GetEdgeData(reverse_e2).distance; BOOST_ASSERT(0 != reverse_weight1); - BOOST_ASSERT(0 != forward_weight2); + BOOST_ASSERT(0 != reverse_weight2); const bool has_node_penalty = m_traffic_lights.find(node_v) != m_traffic_lights.end(); @@ -362,11 +394,11 @@ void EdgeBasedGraphFactory::CompressGeometry() // update any involved turn restrictions m_restriction_map->FixupStartingTurnRestriction(node_u, node_v, node_w); m_restriction_map->FixupArrivingTurnRestriction(node_u, node_v, node_w, - m_node_based_graph); + *m_node_based_graph); m_restriction_map->FixupStartingTurnRestriction(node_w, node_v, node_u); m_restriction_map->FixupArrivingTurnRestriction(node_w, node_v, node_u, - m_node_based_graph); + *m_node_based_graph); // store compressed geometry in container m_geometry_compressor.CompressEdge( @@ -378,8 +410,7 @@ void EdgeBasedGraphFactory::CompressGeometry() reverse_weight2 + (has_node_penalty ? speed_profile.traffic_signal_penalty : 0)); ++removed_node_count; - BOOST_ASSERT(m_node_based_graph->GetEdgeData(forward_e1).nameID == - m_node_based_graph->GetEdgeData(reverse_e1).nameID); + } } SimpleLogger().Write() << "removed " << removed_node_count << " nodes"; @@ -403,18 +434,19 @@ void EdgeBasedGraphFactory::CompressGeometry() << new_edge_count / (double)original_number_of_edges; } -/** - * Writes the id of the edge in the edge expanded graph (into the edge in the node based graph) - */ +/// Renumbers all _forward_ edges and sets the edgeBasedNodeID. +/// A specific numbering is not important. Any unique ID will do. void EdgeBasedGraphFactory::RenumberEdges() { - // renumber edge based node IDs + // renumber edge based node of outgoing edges unsigned numbered_edges_count = 0; for (const auto current_node : osrm::irange(0u, m_node_based_graph->GetNumberOfNodes())) { for (const auto current_edge : m_node_based_graph->GetAdjacentEdgeRange(current_node)) { EdgeData &edge_data = m_node_based_graph->GetEdgeData(current_edge); + + // this edge is an incoming edge if (!edge_data.forward) { continue; @@ -466,7 +498,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedNodes() const NodeID node_v = m_node_based_graph->GetTarget(e1); BOOST_ASSERT(SPECIAL_NODEID != node_v); - // pick only every other edge + // pick only every other edge, since we have every edge as an outgoing + // and incoming egde if (node_u > node_v) { continue; @@ -492,6 +525,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedNodes() const bool component_is_tiny = size_of_component < 1000; + // we only set edgeBasedNodeID for forward edges if (edge_data.edgeBasedNodeID == SPECIAL_NODEID) { InsertEdgeBasedNode(node_v, node_u, diff --git a/contractor/edge_based_graph_factory.hpp b/contractor/edge_based_graph_factory.hpp index 95b65babc..a5274396f 100644 --- a/contractor/edge_based_graph_factory.hpp +++ b/contractor/edge_based_graph_factory.hpp @@ -59,12 +59,12 @@ class EdgeBasedGraphFactory struct SpeedProfileProperties; - explicit EdgeBasedGraphFactory(const std::shared_ptr &node_based_graph, - std::unique_ptr restricion_map, - std::vector &barrier_node_list, - std::vector &traffic_light_node_list, - std::vector &node_info_list, - SpeedProfileProperties &speed_profile); + explicit EdgeBasedGraphFactory(std::shared_ptr node_based_graph, + std::shared_ptr restricion_map, + std::unique_ptr> barrier_node_list, + std::unique_ptr> traffic_light_node_list, + const std::vector &node_info_list, + const SpeedProfileProperties &speed_profile); void Run(const std::string &original_edge_data_filename, const std::string &geometry_filename, @@ -97,15 +97,16 @@ class EdgeBasedGraphFactory unsigned m_number_of_edge_based_nodes; - std::vector m_node_info_list; std::vector m_edge_based_node_list; DeallocatingVector m_edge_based_edge_list; + const std::vector& m_node_info_list; std::shared_ptr m_node_based_graph; + std::shared_ptr m_restriction_map; + std::unordered_set m_barrier_nodes; std::unordered_set m_traffic_lights; - std::unique_ptr m_restriction_map; GeometryCompressor m_geometry_compressor; diff --git a/contractor/processing_chain.cpp b/contractor/processing_chain.cpp index ccff7fd09..a76bc657f 100644 --- a/contractor/processing_chain.cpp +++ b/contractor/processing_chain.cpp @@ -48,7 +48,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include #include #include @@ -57,166 +56,98 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -Prepare::Prepare() : requested_num_threads(1) {} - Prepare::~Prepare() {} -int Prepare::Process(int argc, char *argv[]) +int Prepare::Run() { - LogPolicy::GetInstance().Unmute(); - TIMER_START(preparing); - TIMER_START(expansion); - - if (!ParseArguments(argc, argv)) - { - return 0; - } - if (!boost::filesystem::is_regular_file(input_path)) - { - SimpleLogger().Write(logWARNING) << "Input file " << input_path.string() << " not found!"; - return 1; - } - - if (!boost::filesystem::is_regular_file(profile_path)) - { - SimpleLogger().Write(logWARNING) << "Profile " << profile_path.string() << " not found!"; - return 1; - } - - if (1 > requested_num_threads) - { - SimpleLogger().Write(logWARNING) << "Number of threads must be 1 or larger"; - return 1; - } - - const unsigned recommended_num_threads = tbb::task_scheduler_init::default_num_threads(); - - SimpleLogger().Write() << "Input file: " << input_path.filename().string(); - SimpleLogger().Write() << "Restrictions file: " << restrictions_path.filename().string(); - SimpleLogger().Write() << "Profile: " << profile_path.filename().string(); - SimpleLogger().Write() << "Threads: " << requested_num_threads; - if (recommended_num_threads != requested_num_threads) - { - SimpleLogger().Write(logWARNING) << "The recommended number of threads is " - << recommended_num_threads - << "! This setting may have performance side-effects."; - } - - tbb::task_scheduler_init init(requested_num_threads); - - LogPolicy::GetInstance().Unmute(); - - FingerPrint fingerprint_orig; - CheckRestrictionsFile(fingerprint_orig); - - boost::filesystem::ifstream input_stream(input_path, std::ios::in | std::ios::binary); - - node_filename = input_path.string() + ".nodes"; - edge_out = input_path.string() + ".edges"; - geometry_filename = input_path.string() + ".geometry"; - graph_out = input_path.string() + ".hsgr"; - rtree_nodes_path = input_path.string() + ".ramIndex"; - rtree_leafs_path = input_path.string() + ".fileIndex"; - - /*** Setup Scripting Environment ***/ - // Create a new lua state - lua_State *lua_state = luaL_newstate(); - - // Connect LuaBind to this lua state - luabind::open(lua_state); - - EdgeBasedGraphFactory::SpeedProfileProperties speed_profile; - - if (!SetupScriptingEnvironment(lua_state, speed_profile)) - { - return 1; - } - #ifdef WIN32 #pragma message("Memory consumption on Windows can be higher due to different bit packing") #else - static_assert(sizeof(ImportEdge) == 20, - "changing ImportEdge type has influence on memory consumption!"); + static_assert(sizeof(NodeBasedEdge) == 20, + "changing NodeBasedEdge type has influence on memory consumption!"); + static_assert(sizeof(EdgeBasedEdge) == 16, + "changing EdgeBasedEdge type has influence on memory consumption!"); #endif - NodeID number_of_node_based_nodes = readBinaryOSRMGraphFromStream( - input_stream, edge_list, barrier_node_list, traffic_light_list, - &internal_to_external_node_map, restriction_list); - input_stream.close(); - if (edge_list.empty()) - { - SimpleLogger().Write(logWARNING) << "The input data is empty, exiting."; - return 1; - } + TIMER_START(preparing); - SimpleLogger().Write() << restriction_list.size() << " restrictions, " - << barrier_node_list.size() << " bollard nodes, " - << traffic_light_list.size() << " traffic lights"; + // Create a new lua state - std::vector node_based_edge_list; - unsigned number_of_edge_based_nodes = 0; + SimpleLogger().Write() << "Generating edge-expanded graph representation"; + + TIMER_START(expansion); + + auto node_based_edge_list = osrm::make_unique>();; DeallocatingVector edge_based_edge_list; + auto internal_to_external_node_map = osrm::make_unique>(); + auto graph_size = + BuildEdgeExpandedGraph(*internal_to_external_node_map, + *node_based_edge_list, edge_based_edge_list); - // init node_based_edge_list, edge_based_edge_list by edgeList - number_of_edge_based_nodes = - BuildEdgeExpandedGraph(lua_state, number_of_node_based_nodes, node_based_edge_list, - edge_based_edge_list, speed_profile); - lua_close(lua_state); + auto number_of_node_based_nodes = graph_size.first; + auto number_of_edge_based_nodes = graph_size.second; TIMER_STOP(expansion); - BuildRTree(node_based_edge_list); + SimpleLogger().Write() << "building r-tree ..."; + TIMER_START(rtree); - RangebasedCRC32 crc32; - if (crc32.using_hardware()) - { - SimpleLogger().Write() << "using hardware based CRC32 computation"; - } - else - { - SimpleLogger().Write() << "using software based CRC32 computation"; - } + BuildRTree(*node_based_edge_list, *internal_to_external_node_map); - const unsigned crc32_value = crc32(node_based_edge_list); - node_based_edge_list.clear(); - node_based_edge_list.shrink_to_fit(); - SimpleLogger().Write() << "CRC32: " << crc32_value; + TIMER_STOP(rtree); - WriteNodeMapping(); + SimpleLogger().Write() << "writing node map ..."; + WriteNodeMapping(std::move(internal_to_external_node_map)); - /*** - * Contracting the edge-expanded graph - */ - - SimpleLogger().Write() << "initializing contractor"; - auto contractor = - osrm::make_unique(number_of_edge_based_nodes, edge_based_edge_list); + // Contracting the edge-expanded graph TIMER_START(contraction); - contractor->Run(); + auto contracted_edge_list = osrm::make_unique>(); + ContractGraph(number_of_edge_based_nodes, edge_based_edge_list, *contracted_edge_list); TIMER_STOP(contraction); SimpleLogger().Write() << "Contraction took " << TIMER_SEC(contraction) << " sec"; - DeallocatingVector contracted_edge_list; - contractor->GetEdges(contracted_edge_list); - contractor.reset(); + std::size_t number_of_used_edges = WriteContractedGraph(number_of_edge_based_nodes, + std::move(node_based_edge_list), + std::move(contracted_edge_list)); - /*** - * Sorting contracted edges in a way that the static query graph can read some in in-place. - */ + TIMER_STOP(preparing); - tbb::parallel_sort(contracted_edge_list.begin(), contracted_edge_list.end()); - const unsigned contracted_edge_count = contracted_edge_list.size(); + SimpleLogger().Write() << "Preprocessing : " << TIMER_SEC(preparing) << " seconds"; + SimpleLogger().Write() << "Expansion : " << (number_of_node_based_nodes / TIMER_SEC(expansion)) + << " nodes/sec and " + << (number_of_edge_based_nodes / TIMER_SEC(expansion)) << " edges/sec"; + + SimpleLogger().Write() << "Contraction: " + << (number_of_edge_based_nodes / TIMER_SEC(contraction)) + << " nodes/sec and " << number_of_used_edges / TIMER_SEC(contraction) + << " edges/sec"; + + SimpleLogger().Write() << "finished preprocessing"; + + return 0; +} + +std::size_t Prepare::WriteContractedGraph(unsigned number_of_edge_based_nodes, + std::unique_ptr> node_based_edge_list, + std::unique_ptr> contracted_edge_list) +{ + const unsigned crc32_value = CalculateEdgeChecksum(std::move(node_based_edge_list)); + + // Sorting contracted edges in a way that the static query graph can read some in in-place. + tbb::parallel_sort(contracted_edge_list->begin(), contracted_edge_list->end()); + const unsigned contracted_edge_count = contracted_edge_list->size(); SimpleLogger().Write() << "Serializing compacted graph of " << contracted_edge_count << " edges"; - boost::filesystem::ofstream hsgr_output_stream(graph_out, std::ios::binary); - hsgr_output_stream.write((char *)&fingerprint_orig, sizeof(FingerPrint)); + const FingerPrint fingerprint = FingerPrint::GetValid(); + boost::filesystem::ofstream hsgr_output_stream(config.graph_output_path, std::ios::binary); + hsgr_output_stream.write((char *)&fingerprint, sizeof(FingerPrint)); const unsigned max_used_node_id = 1 + [&contracted_edge_list] { unsigned tmp_max = 0; - for (const QueryEdge &edge : contracted_edge_list) + for (const QueryEdge &edge : *contracted_edge_list) { BOOST_ASSERT(SPECIAL_NODEID != edge.source); BOOST_ASSERT(SPECIAL_NODEID != edge.target); @@ -241,7 +172,7 @@ int Prepare::Process(int argc, char *argv[]) for (const auto node : osrm::irange(0u, max_used_node_id)) { last_edge = edge; - while ((edge < contracted_edge_count) && (contracted_edge_list[edge].source == node)) + while ((edge < contracted_edge_count) && ((*contracted_edge_list)[edge].source == node)) { ++edge; } @@ -270,19 +201,19 @@ int Prepare::Process(int argc, char *argv[]) hsgr_output_stream.write((char *)&node_array[0], sizeof(StaticGraph::NodeArrayEntry) * node_array_size); } - // serialize all edges + // serialize all edges SimpleLogger().Write() << "Building edge array"; edge = 0; int number_of_used_edges = 0; StaticGraph::EdgeArrayEntry current_edge; - for (const auto edge : osrm::irange(0, contracted_edge_list.size())) + for (const auto edge : osrm::irange(0, contracted_edge_list->size())) { // no eigen loops - BOOST_ASSERT(contracted_edge_list[edge].source != contracted_edge_list[edge].target); - current_edge.target = contracted_edge_list[edge].target; - current_edge.data = contracted_edge_list[edge].data; + BOOST_ASSERT((*contracted_edge_list)[edge].source != (*contracted_edge_list)[edge].target); + current_edge.target = (*contracted_edge_list)[edge].target; + current_edge.data = (*contracted_edge_list)[edge].data; // every target needs to be valid BOOST_ASSERT(current_edge.target < max_used_node_id); @@ -290,12 +221,12 @@ int Prepare::Process(int argc, char *argv[]) if (current_edge.data.distance <= 0) { SimpleLogger().Write(logWARNING) << "Edge: " << edge - << ",source: " << contracted_edge_list[edge].source - << ", target: " << contracted_edge_list[edge].target + << ",source: " << (*contracted_edge_list)[edge].source + << ", target: " << (*contracted_edge_list)[edge].target << ", dist: " << current_edge.data.distance; SimpleLogger().Write(logWARNING) << "Failed at adjacency list of node " - << contracted_edge_list[edge].source << "/" + << (*contracted_edge_list)[edge].source << "/" << node_array.size() - 1; return 1; } @@ -305,170 +236,54 @@ int Prepare::Process(int argc, char *argv[]) ++number_of_used_edges; } - hsgr_output_stream.close(); - TIMER_STOP(preparing); - - SimpleLogger().Write() << "Preprocessing : " << TIMER_SEC(preparing) << " seconds"; - SimpleLogger().Write() << "Expansion : " << (number_of_node_based_nodes / TIMER_SEC(expansion)) - << " nodes/sec and " - << (number_of_edge_based_nodes / TIMER_SEC(expansion)) << " edges/sec"; - - SimpleLogger().Write() << "Contraction: " - << (number_of_edge_based_nodes / TIMER_SEC(contraction)) - << " nodes/sec and " << number_of_used_edges / TIMER_SEC(contraction) - << " edges/sec"; - - node_array.clear(); - SimpleLogger().Write() << "finished preprocessing"; - - return 0; + return number_of_used_edges; } -/** - \brief Parses command line arguments - \param argc count of arguments - \param argv array of arguments - \param result [out] value for exit return value - \return true if everything is ok, false if need to terminate execution -*/ -bool Prepare::ParseArguments(int argc, char *argv[]) +unsigned Prepare::CalculateEdgeChecksum(std::unique_ptr> node_based_edge_list) { - // declare a group of options that will be allowed only on command line - boost::program_options::options_description generic_options("Options"); - generic_options.add_options()("version,v", "Show version")("help,h", "Show this help message")( - "config,c", boost::program_options::value(&config_file_path) - ->default_value("contractor.ini"), - "Path to a configuration file."); - - // declare a group of options that will be allowed both on command line and in config file - boost::program_options::options_description config_options("Configuration"); - config_options.add_options()( - "restrictions,r", - boost::program_options::value(&restrictions_path), - "Restrictions file in .osrm.restrictions format")( - "profile,p", boost::program_options::value(&profile_path) - ->default_value("profile.lua"), - "Path to LUA routing profile")( - "threads,t", boost::program_options::value(&requested_num_threads) - ->default_value(tbb::task_scheduler_init::default_num_threads()), - "Number of threads to use"); - - // hidden options, will be allowed both on command line and in config file, but will not be - // shown to the user - boost::program_options::options_description hidden_options("Hidden options"); - hidden_options.add_options()( - "input,i", boost::program_options::value(&input_path), - "Input file in .osm, .osm.bz2 or .osm.pbf format"); - - // positional option - boost::program_options::positional_options_description positional_options; - positional_options.add("input", 1); - - // combine above options for parsing - boost::program_options::options_description cmdline_options; - cmdline_options.add(generic_options).add(config_options).add(hidden_options); - - boost::program_options::options_description config_file_options; - config_file_options.add(config_options).add(hidden_options); - - boost::program_options::options_description visible_options( - "Usage: " + boost::filesystem::basename(argv[0]) + " [options]"); - visible_options.add(generic_options).add(config_options); - - // parse command line options - boost::program_options::variables_map option_variables; - boost::program_options::store(boost::program_options::command_line_parser(argc, argv) - .options(cmdline_options) - .positional(positional_options) - .run(), - option_variables); - - const auto &temp_config_path = option_variables["config"].as(); - if (boost::filesystem::is_regular_file(temp_config_path)) + RangebasedCRC32 crc32; + if (crc32.using_hardware()) { - boost::program_options::store(boost::program_options::parse_config_file( - temp_config_path.string().c_str(), cmdline_options, true), - option_variables); + SimpleLogger().Write() << "using hardware based CRC32 computation"; + } + else + { + SimpleLogger().Write() << "using software based CRC32 computation"; } - if (option_variables.count("version")) - { - SimpleLogger().Write() << g_GIT_DESCRIPTION; - return false; - } + const unsigned crc32_value = crc32(*node_based_edge_list); + SimpleLogger().Write() << "CRC32: " << crc32_value; - if (option_variables.count("help")) - { - SimpleLogger().Write() << "\n" << visible_options; - return false; - } - - boost::program_options::notify(option_variables); - - if (!option_variables.count("restrictions")) - { - restrictions_path = std::string(input_path.string() + ".restrictions"); - } - - if (!option_variables.count("input")) - { - SimpleLogger().Write() << "\n" << visible_options; - return false; - } - - return true; -} - -/** - \brief Loads and checks file UUIDs -*/ -void Prepare::CheckRestrictionsFile(FingerPrint &fingerprint_orig) -{ - boost::filesystem::ifstream restriction_stream(restrictions_path, std::ios::binary); - FingerPrint fingerprint_loaded; - unsigned number_of_usable_restrictions = 0; - restriction_stream.read((char *)&fingerprint_loaded, sizeof(FingerPrint)); - if (!fingerprint_loaded.TestPrepare(fingerprint_orig)) - { - SimpleLogger().Write(logWARNING) << ".restrictions was prepared with different build.\n" - "Reprocess to get rid of this warning."; - } - - restriction_stream.read((char *)&number_of_usable_restrictions, sizeof(unsigned)); - restriction_list.resize(number_of_usable_restrictions); - if (number_of_usable_restrictions > 0) - { - restriction_stream.read((char *)&(restriction_list[0]), - number_of_usable_restrictions * sizeof(TurnRestriction)); - } - restriction_stream.close(); + return crc32_value; } /** \brief Setups scripting environment (lua-scripting) Also initializes speed profile. */ -bool Prepare::SetupScriptingEnvironment( +void Prepare::SetupScriptingEnvironment( lua_State *lua_state, EdgeBasedGraphFactory::SpeedProfileProperties &speed_profile) { // open utility libraries string library; luaL_openlibs(lua_state); // adjust lua load path - luaAddScriptFolderToLoadPath(lua_state, profile_path.string().c_str()); + luaAddScriptFolderToLoadPath(lua_state, config.profile_path.string().c_str()); // Now call our function in a lua script - if (0 != luaL_dofile(lua_state, profile_path.string().c_str())) + if (0 != luaL_dofile(lua_state, config.profile_path.string().c_str())) { - std::cerr << lua_tostring(lua_state, -1) << " occured in scripting block" << std::endl; - return false; + std::stringstream msg; + msg << lua_tostring(lua_state, -1) << " occured in scripting block"; + throw osrm::exception(msg.str()); } if (0 != luaL_dostring(lua_state, "return traffic_signal_penalty\n")) { - std::cerr << lua_tostring(lua_state, -1) << " occured in scripting block" << std::endl; - return false; + std::stringstream msg; + msg << lua_tostring(lua_state, -1) << " occured in scripting block"; + throw osrm::exception(msg.str()); } speed_profile.traffic_signal_penalty = 10 * lua_tointeger(lua_state, -1); SimpleLogger().Write(logDEBUG) @@ -476,92 +291,140 @@ bool Prepare::SetupScriptingEnvironment( if (0 != luaL_dostring(lua_state, "return u_turn_penalty\n")) { - std::cerr << lua_tostring(lua_state, -1) << " occured in scripting block" << std::endl; - return false; + std::stringstream msg; + msg << lua_tostring(lua_state, -1) << " occured in scripting block"; + throw osrm::exception(msg.str()); } speed_profile.u_turn_penalty = 10 * lua_tointeger(lua_state, -1); speed_profile.has_turn_penalty_function = lua_function_exists(lua_state, "turn_function"); - - return true; } +/** + \brief Build load restrictions from .restriction file + */ +std::shared_ptr Prepare::LoadRestrictionMap() +{ + boost::filesystem::ifstream input_stream(config.restrictions_path, std::ios::in | std::ios::binary); + + std::vector restriction_list; + loadRestrictionsFromFile(input_stream, restriction_list); + + SimpleLogger().Write() << " - " << restriction_list.size() << " restrictions."; + + return std::make_shared(restriction_list); +} + +/** + \brief Load node based graph from .osrm file + */ +std::shared_ptr +Prepare::LoadNodeBasedGraph(std::vector &barrier_node_list, + std::vector &traffic_light_list, + std::vector& internal_to_external_node_map) +{ + std::vector edge_list; + + boost::filesystem::ifstream input_stream(config.osrm_input_path, std::ios::in | std::ios::binary); + + NodeID number_of_node_based_nodes = loadNodesFromFile(input_stream, + barrier_node_list, traffic_light_list, + internal_to_external_node_map); + + SimpleLogger().Write() << " - " << barrier_node_list.size() << " bollard nodes, " + << traffic_light_list.size() << " traffic lights"; + + loadEdgesFromFile(input_stream, edge_list); + + if (edge_list.empty()) + { + SimpleLogger().Write(logWARNING) << "The input data is empty, exiting."; + return std::shared_ptr(); + } + + return NodeBasedDynamicGraphFromImportEdges(number_of_node_based_nodes, edge_list); +} + + /** \brief Building an edge-expanded graph from node-based input and turn restrictions */ -std::size_t -Prepare::BuildEdgeExpandedGraph(lua_State *lua_state, - NodeID number_of_node_based_nodes, +std::pair +Prepare::BuildEdgeExpandedGraph(std::vector &internal_to_external_node_map, std::vector &node_based_edge_list, - DeallocatingVector &edge_based_edge_list, - EdgeBasedGraphFactory::SpeedProfileProperties &speed_profile) + DeallocatingVector &edge_based_edge_list) { - SimpleLogger().Write() << "Generating edge-expanded graph representation"; - std::shared_ptr node_based_graph = - NodeBasedDynamicGraphFromImportEdges(number_of_node_based_nodes, edge_list); - std::unique_ptr restriction_map = - osrm::make_unique(restriction_list); - std::shared_ptr edge_based_graph_factory = - std::make_shared(node_based_graph, std::move(restriction_map), - barrier_node_list, traffic_light_list, - internal_to_external_node_map, speed_profile); - edge_list.clear(); - edge_list.shrink_to_fit(); + lua_State *lua_state = luaL_newstate(); + luabind::open(lua_state); - edge_based_graph_factory->Run(edge_out, geometry_filename, lua_state); + EdgeBasedGraphFactory::SpeedProfileProperties speed_profile; - restriction_list.clear(); - restriction_list.shrink_to_fit(); - barrier_node_list.clear(); - barrier_node_list.shrink_to_fit(); - traffic_light_list.clear(); - traffic_light_list.shrink_to_fit(); + SetupScriptingEnvironment(lua_state, speed_profile); + + auto barrier_node_list = osrm::make_unique>(); + auto traffic_light_list = osrm::make_unique>(); + + auto restriction_map = LoadRestrictionMap(); + auto node_based_graph = LoadNodeBasedGraph(*barrier_node_list, *traffic_light_list, internal_to_external_node_map); + + const std::size_t number_of_node_based_nodes = node_based_graph->GetNumberOfNodes(); + + EdgeBasedGraphFactory edge_based_graph_factory(node_based_graph, + restriction_map, + std::move(barrier_node_list), + std::move(traffic_light_list), + internal_to_external_node_map, + speed_profile); + + edge_based_graph_factory.Run(config.edge_output_path, config.geometry_output_path, lua_state); + lua_close(lua_state); const std::size_t number_of_edge_based_nodes = - edge_based_graph_factory->GetNumberOfEdgeBasedNodes(); + edge_based_graph_factory.GetNumberOfEdgeBasedNodes(); BOOST_ASSERT(number_of_edge_based_nodes != std::numeric_limits::max()); -#ifndef WIN32 - static_assert(sizeof(EdgeBasedEdge) == 16, - "changing ImportEdge type has influence on memory consumption!"); -#endif - edge_based_graph_factory->GetEdgeBasedEdges(edge_based_edge_list); - edge_based_graph_factory->GetEdgeBasedNodes(node_based_edge_list); + edge_based_graph_factory.GetEdgeBasedEdges(edge_based_edge_list); + edge_based_graph_factory.GetEdgeBasedNodes(node_based_edge_list); - edge_based_graph_factory.reset(); - node_based_graph.reset(); + return std::make_pair(number_of_node_based_nodes, number_of_edge_based_nodes); +} - return number_of_edge_based_nodes; +/** + \brief Build contracted graph. + */ +void Prepare::ContractGraph(const std::size_t number_of_edge_based_nodes, + DeallocatingVector& edge_based_edge_list, + DeallocatingVector& contracted_edge_list) +{ + Contractor contractor(number_of_edge_based_nodes, edge_based_edge_list); + contractor.Run(); + contractor.GetEdges(contracted_edge_list); } /** \brief Writing info on original (node-based) nodes */ -void Prepare::WriteNodeMapping() +void Prepare::WriteNodeMapping(std::unique_ptr> internal_to_external_node_map) { - SimpleLogger().Write() << "writing node map ..."; - boost::filesystem::ofstream node_stream(node_filename, std::ios::binary); - const unsigned size_of_mapping = internal_to_external_node_map.size(); + boost::filesystem::ofstream node_stream(config.node_output_path, std::ios::binary); + const unsigned size_of_mapping = internal_to_external_node_map->size(); node_stream.write((char *)&size_of_mapping, sizeof(unsigned)); if (size_of_mapping > 0) { - node_stream.write((char *)&(internal_to_external_node_map[0]), + node_stream.write((char *) internal_to_external_node_map->data(), size_of_mapping * sizeof(QueryNode)); } node_stream.close(); - internal_to_external_node_map.clear(); - internal_to_external_node_map.shrink_to_fit(); } /** \brief Building rtree-based nearest-neighbor data structure - Saves info to files: '.ramIndex' and '.fileIndex'. + Saves tree into '.ramIndex' and leaves into '.fileIndex'. */ -void Prepare::BuildRTree(std::vector &node_based_edge_list) +void Prepare::BuildRTree(const std::vector &node_based_edge_list, const std::vector& internal_to_external_node_map) { - SimpleLogger().Write() << "building r-tree ..."; - StaticRTree(node_based_edge_list, rtree_nodes_path.c_str(), - rtree_leafs_path.c_str(), internal_to_external_node_map); + StaticRTree(node_based_edge_list, config.rtree_nodes_output_path.c_str(), + config.rtree_leafs_output_path.c_str(), internal_to_external_node_map); } diff --git a/contractor/processing_chain.hpp b/contractor/processing_chain.hpp index 933213a4c..281768166 100644 --- a/contractor/processing_chain.hpp +++ b/contractor/processing_chain.hpp @@ -28,11 +28,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef PROCESSING_CHAIN_HPP #define PROCESSING_CHAIN_HPP +#include "contractor_options.hpp" #include "edge_based_graph_factory.hpp" #include "../data_structures/query_edge.hpp" #include "../data_structures/static_graph.hpp" -class FingerPrint; struct EdgeBasedNode; struct lua_State; @@ -50,46 +50,36 @@ class Prepare using InputEdge = DynamicGraph::InputEdge; using StaticEdge = StaticGraph::InputEdge; - explicit Prepare(); + explicit Prepare(const ContractorConfig& contractor_config) + : config(contractor_config) {} Prepare(const Prepare &) = delete; ~Prepare(); - int Process(int argc, char *argv[]); + int Run(); protected: - bool ParseArguments(int argc, char *argv[]); - void CheckRestrictionsFile(FingerPrint &fingerprint_orig); - bool SetupScriptingEnvironment(lua_State *myLuaState, + void SetupScriptingEnvironment(lua_State *myLuaState, EdgeBasedGraphFactory::SpeedProfileProperties &speed_profile); - std::size_t BuildEdgeExpandedGraph(lua_State *myLuaState, - NodeID nodeBasedNodeNumber, - std::vector &nodeBasedEdgeList, - DeallocatingVector &edgeBasedEdgeList, - EdgeBasedGraphFactory::SpeedProfileProperties &speed_profile); - void WriteNodeMapping(); - void BuildRTree(std::vector &node_based_edge_list); - + std::shared_ptr LoadRestrictionMap(); + unsigned CalculateEdgeChecksum(std::unique_ptr> node_based_edge_list); + void ContractGraph(const std::size_t number_of_edge_based_nodes, + DeallocatingVector& edge_based_edge_list, + DeallocatingVector& contracted_edge_list); + std::size_t WriteContractedGraph(unsigned number_of_edge_based_nodes, + std::unique_ptr> node_based_edge_list, + std::unique_ptr> contracted_edge_list); + std::shared_ptr LoadNodeBasedGraph(std::vector &barrier_node_list, + std::vector &traffic_light_list, + std::vector& internal_to_external_node_map); + std::pair + BuildEdgeExpandedGraph(std::vector &internal_to_external_node_map, + std::vector &node_based_edge_list, + DeallocatingVector &edge_based_edge_list); + void WriteNodeMapping(std::unique_ptr> internal_to_external_node_map); + void BuildRTree(const std::vector &node_based_edge_list, + const std::vector &internal_to_external_node_map); private: - std::vector internal_to_external_node_map; - std::vector restriction_list; - std::vector barrier_node_list; - std::vector traffic_light_list; - std::vector edge_list; - - unsigned requested_num_threads; - boost::filesystem::path config_file_path; - boost::filesystem::path input_path; - boost::filesystem::path restrictions_path; - boost::filesystem::path preinfo_path; - boost::filesystem::path profile_path; - - std::string node_filename; - std::string edge_out; - std::string info_out; - std::string geometry_filename; - std::string graph_out; - std::string rtree_nodes_path; - std::string rtree_leafs_path; + ContractorConfig config; }; #endif // PROCESSING_CHAIN_HPP diff --git a/data_structures/dynamic_graph.hpp b/data_structures/dynamic_graph.hpp index 43e6c3aca..b18204c61 100644 --- a/data_structures/dynamic_graph.hpp +++ b/data_structures/dynamic_graph.hpp @@ -85,6 +85,9 @@ template class DynamicGraph edge_list.resize(number_of_nodes); } + /** + * Constructs a DynamicGraph from a list of edges sorted by source node id. + */ template DynamicGraph(const NodeIterator nodes, const ContainerT &graph) { number_of_nodes = nodes; diff --git a/data_structures/import_edge.cpp b/data_structures/import_edge.cpp index f41b066b1..3f2146ca3 100644 --- a/data_structures/import_edge.cpp +++ b/data_structures/import_edge.cpp @@ -47,6 +47,13 @@ bool NodeBasedEdge::operator<(const NodeBasedEdge &other) const return source < other.source; } +NodeBasedEdge::NodeBasedEdge() + : source(SPECIAL_NODEID), target(SPECIAL_NODEID), name_id(0), weight(0), forward(false), + backward(false), roundabout(false), in_tiny_cc(false), + access_restricted(false), is_split(false), travel_mode(false) +{ +} + NodeBasedEdge::NodeBasedEdge(NodeID source, NodeID target, NodeID name_id, diff --git a/data_structures/import_edge.hpp b/data_structures/import_edge.hpp index f422de116..e48985073 100644 --- a/data_structures/import_edge.hpp +++ b/data_structures/import_edge.hpp @@ -35,6 +35,7 @@ struct NodeBasedEdge { bool operator<(const NodeBasedEdge &e) const; + NodeBasedEdge(); explicit NodeBasedEdge(NodeID source, NodeID target, NodeID name_id, @@ -58,8 +59,6 @@ struct NodeBasedEdge bool access_restricted : 1; bool is_split : 1; TravelMode travel_mode : 4; - - NodeBasedEdge() = delete; }; struct EdgeBasedEdge @@ -86,6 +85,4 @@ struct EdgeBasedEdge bool backward : 1; }; -using ImportEdge = NodeBasedEdge; - #endif /* IMPORT_EDGE_HPP */ diff --git a/data_structures/node_based_graph.hpp b/data_structures/node_based_graph.hpp index 54e07a7ec..b69053a97 100644 --- a/data_structures/node_based_graph.hpp +++ b/data_structures/node_based_graph.hpp @@ -64,7 +64,7 @@ struct NodeBasedEdgeData backward = temp_flag; } - bool IsEqualTo(const NodeBasedEdgeData &other) const + bool IsCompatibleTo(const NodeBasedEdgeData &other) const { return (forward == other.forward) && (backward == other.backward) && (nameID == other.nameID) && (ignore_in_grid == other.ignore_in_grid) && @@ -72,26 +72,123 @@ struct NodeBasedEdgeData } }; -struct SimpleEdgeData -{ - SimpleEdgeData() : capacity(0) {} - EdgeWeight capacity; -}; - using NodeBasedDynamicGraph = DynamicGraph; -using SimpleNodeBasedDynamicGraph = DynamicGraph; -// Factory method to create NodeBasedDynamicGraph from ImportEdges +inline bool validateNeighborHood(const NodeBasedDynamicGraph& graph, const NodeID source) +{ + for (auto edge = graph.BeginEdges(source); edge < graph.EndEdges(source); ++edge) + { + const auto& data = graph.GetEdgeData(edge); + if (!data.forward && !data.backward) + { + SimpleLogger().Write(logWARNING) << "Invalid edge directions"; + return false; + } + + auto target = graph.GetTarget(edge); + if (target == SPECIAL_NODEID) + { + SimpleLogger().Write(logWARNING) << "Invalid edge target"; + return false; + } + + bool found_reverse = false; + for (auto rev_edge = graph.BeginEdges(target); rev_edge < graph.EndEdges(target); ++rev_edge) + { + auto rev_target = graph.GetTarget(rev_edge); + if (rev_target == SPECIAL_NODEID) + { + SimpleLogger().Write(logWARNING) << "Invalid reverse edge target"; + return false; + } + + if (rev_target != source) + { + continue; + } + + if (found_reverse) + { + SimpleLogger().Write(logWARNING) << "Found more than one reverse edge"; + return false; + } + + const auto& rev_data = graph.GetEdgeData(rev_edge); + + // edge is incoming, this must be an outgoing edge + if (data.backward && !rev_data.forward) + { + SimpleLogger().Write(logWARNING) << "Found no outgoing edge to an incoming edge!"; + return false; + } + + // edge is bi-directional, reverse must be as well + if (data.forward && data.backward && (!rev_data.forward || !rev_data.backward)) + { + SimpleLogger().Write(logWARNING) << "Found bi-directional edge that is not bi-directional to both ends"; + return false; + } + + found_reverse = true; + + } + + if (!found_reverse) + { + SimpleLogger().Write(logWARNING) << "Could not find reverse edge"; + return false; + } + } + + return true; +} + +// This function checks if the overal graph is undirected (has an edge in each direction). +inline bool validateNodeBasedGraph(const NodeBasedDynamicGraph& graph) +{ + for (auto source = 0u; source < graph.GetNumberOfNodes(); ++source) + { + if (!validateNeighborHood(graph, source)) + { + return false; + } + } + + return true; +} + +// Factory method to create NodeBasedDynamicGraph from NodeBasedEdges +// The since DynamicGraph expects directed edges, we need to insert +// two edges for undirected edges. inline std::shared_ptr -NodeBasedDynamicGraphFromImportEdges(int number_of_nodes, std::vector &input_edge_list) +NodeBasedDynamicGraphFromImportEdges(int number_of_nodes, std::vector &input_edge_list) { static_assert(sizeof(NodeBasedEdgeData) == 16, "changing node based edge data size changes memory consumption"); DeallocatingVector edges_list; NodeBasedDynamicGraph::InputEdge edge; - for (const ImportEdge &import_edge : input_edge_list) + + // Since DynamicGraph assumes directed edges we have to make sure we transformed + // the compressed edge format into single directed edges. We do this to make sure + // every node also knows its incoming edges, not only its outgoing edges and use the backward=true + // flag to indicate which is which. + // + // We do the transformation in the following way: + // + // if the edge (a, b) is split: + // 1. this edge must be in only one direction, so its a --> b + // 2. there must be another directed edge b --> a somewhere in the data + // if the edge (a, b) is not split: + // 1. this edge be on of a --> b od a <-> b + // (a <-- b gets reducted to b --> a) + // 2. a --> b will be transformed to a --> b and b <-- a + // 3. a <-> b will be transformed to a <-> b and b <-> a (I think a --> b and b <-- a would work as well though) + for (const NodeBasedEdge &import_edge : input_edge_list) { + // edges that are not forward get converted by flipping the end points + BOOST_ASSERT(import_edge.forward); + if (import_edge.forward) { edge.source = import_edge.source; @@ -99,20 +196,10 @@ NodeBasedDynamicGraphFromImportEdges(int number_of_nodes, std::vector(import_edge.weight), 1); + edge.data.distance = static_cast(import_edge.weight); BOOST_ASSERT(edge.data.distance > 0); edge.data.shortcut = false; edge.data.roundabout = import_edge.roundabout; @@ -132,147 +219,16 @@ NodeBasedDynamicGraphFromImportEdges(int number_of_nodes, std::vector::max(); - // remove parallel edges - while (i < edges_list.size() && edges_list[i].source == source && - edges_list[i].target == target) - { - if (edges_list[i].data.forward) - { - forward_edge.data.distance = - std::min(edges_list[i].data.distance, forward_edge.data.distance); - } - if (edges_list[i].data.backward) - { - reverse_edge.data.distance = - std::min(edges_list[i].data.distance, reverse_edge.data.distance); - } - ++i; - } - // merge edges (s,t) and (t,s) into bidirectional edge - if (forward_edge.data.distance == reverse_edge.data.distance) - { - if (static_cast(forward_edge.data.distance) != std::numeric_limits::max()) - { - forward_edge.data.backward = true; - edges_list[edge_count++] = forward_edge; - } - } - else - { // insert seperate edges - if (static_cast(forward_edge.data.distance) != std::numeric_limits::max()) - { - edges_list[edge_count++] = forward_edge; - } - if (static_cast(reverse_edge.data.distance) != std::numeric_limits::max()) - { - edges_list[edge_count++] = reverse_edge; - } - } - } - edges_list.resize(edge_count); - SimpleLogger().Write() << "merged " << edges_list.size() - edge_count << " edges out of " - << edges_list.size(); auto graph = std::make_shared( static_cast(number_of_nodes), edges_list); - return graph; -} -template -inline std::shared_ptr -SimpleNodeBasedDynamicGraphFromEdges(int number_of_nodes, std::vector &input_edge_list) -{ - static_assert(sizeof(NodeBasedEdgeData) == 16, - "changing node based edge data size changes memory consumption"); - tbb::parallel_sort(input_edge_list.begin(), input_edge_list.end()); - DeallocatingVector edges_list; - SimpleNodeBasedDynamicGraph::InputEdge edge; - edge.data.capacity = 1; - for (const SimpleEdgeT &import_edge : input_edge_list) - { - if (import_edge.source == import_edge.target) - { - continue; - } - edge.source = import_edge.source; - edge.target = import_edge.target; - edges_list.push_back(edge); - std::swap(edge.source, edge.target); - edges_list.push_back(edge); - } +#ifndef NDEBUG + BOOST_ASSERT(validateNodeBasedGraph(*graph)); +#endif - // remove duplicate edges - tbb::parallel_sort(edges_list.begin(), edges_list.end()); - NodeID edge_count = 0; - for (NodeID i = 0; i < edges_list.size();) - { - const NodeID source = edges_list[i].source; - const NodeID target = edges_list[i].target; - // remove eigenloops - if (source == target) - { - i++; - continue; - } - SimpleNodeBasedDynamicGraph::InputEdge forward_edge; - SimpleNodeBasedDynamicGraph::InputEdge reverse_edge; - forward_edge = reverse_edge = edges_list[i]; - forward_edge.data.capacity = reverse_edge.data.capacity = INVALID_EDGE_WEIGHT; - // remove parallel edges - while (i < edges_list.size() && edges_list[i].source == source && - edges_list[i].target == target) - { - forward_edge.data.capacity = - std::min(edges_list[i].data.capacity, forward_edge.data.capacity); - reverse_edge.data.capacity = - std::min(edges_list[i].data.capacity, reverse_edge.data.capacity); - ++i; - } - // merge edges (s,t) and (t,s) into bidirectional edge - if (forward_edge.data.capacity == reverse_edge.data.capacity) - { - if (static_cast(forward_edge.data.capacity) != INVALID_EDGE_WEIGHT) - { - edges_list[edge_count++] = forward_edge; - } - } - else - { // insert seperate edges - if (static_cast(forward_edge.data.capacity) != INVALID_EDGE_WEIGHT) - { - edges_list[edge_count++] = forward_edge; - } - if (static_cast(reverse_edge.data.capacity) != INVALID_EDGE_WEIGHT) - { - edges_list[edge_count++] = reverse_edge; - } - } - } - SimpleLogger().Write() << "merged " << edges_list.size() - edge_count << " edges out of " - << edges_list.size(); - - auto graph = std::make_shared(number_of_nodes, edges_list); return graph; } diff --git a/data_structures/restriction_map.hpp b/data_structures/restriction_map.hpp index 27a6698db..98b360c25 100644 --- a/data_structures/restriction_map.hpp +++ b/data_structures/restriction_map.hpp @@ -91,6 +91,7 @@ template <> struct hash class RestrictionMap { public: + RestrictionMap() : m_count(0) {}; RestrictionMap(const std::vector &restriction_list); // Replace end v with w in each turn restriction containing u as via node @@ -98,7 +99,7 @@ class RestrictionMap void FixupArrivingTurnRestriction(const NodeID node_u, const NodeID node_v, const NodeID node_w, - const std::shared_ptr &graph) + const GraphT &graph) { BOOST_ASSERT(node_u != SPECIAL_NODEID); BOOST_ASSERT(node_v != SPECIAL_NODEID); @@ -112,9 +113,9 @@ class RestrictionMap // find all potential start edges. It is more efficent to get a (small) list // of potential start edges than iterating over all buckets std::vector predecessors; - for (const EdgeID current_edge_id : graph->GetAdjacentEdgeRange(node_u)) + for (const EdgeID current_edge_id : graph.GetAdjacentEdgeRange(node_u)) { - const NodeID target = graph->GetTarget(current_edge_id); + const NodeID target = graph.GetTarget(current_edge_id); if (node_v != target) { predecessors.push_back(target); diff --git a/data_structures/route_parameters.cpp b/data_structures/route_parameters.cpp index 3b615e2c4..86b955358 100644 --- a/data_structures/route_parameters.cpp +++ b/data_structures/route_parameters.cpp @@ -31,6 +31,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include "../algorithms/polyline_compressor.hpp" + RouteParameters::RouteParameters() : zoom_level(18), print_instructions(false), alternate_route(true), geometry(true), compression(true), deprecatedAPI(false), uturn_default(false), classify(false), @@ -131,3 +133,9 @@ void RouteParameters::addCoordinate( static_cast(COORDINATE_PRECISION * boost::fusion::at_c<0>(received_coordinates)), static_cast(COORDINATE_PRECISION * boost::fusion::at_c<1>(received_coordinates))); } + +void RouteParameters::getCoordinatesFromGeometry(const std::string geometry_string) +{ + PolylineCompressor pc; + coordinates = pc.decode_string(geometry_string); +} diff --git a/data_structures/static_rtree.hpp b/data_structures/static_rtree.hpp index f219a6475..fa5607198 100644 --- a/data_structures/static_rtree.hpp +++ b/data_structures/static_rtree.hpp @@ -344,7 +344,7 @@ class StaticRTree StaticRTree(const StaticRTree &) = delete; // Construct a packed Hilbert-R-Tree with Kamel-Faloutsos algorithm [1] - explicit StaticRTree(std::vector &input_data_vector, + explicit StaticRTree(const std::vector &input_data_vector, const std::string tree_node_filename, const std::string leaf_node_filename, const std::vector &coordinate_list) @@ -646,22 +646,16 @@ class StaticRTree const FixedPointCoordinate &input_coordinate, std::vector &result_phantom_node_vector, const unsigned max_number_of_phantom_nodes, - const unsigned max_checked_elements = 4 * LEAF_NODE_SIZE) + const float max_distance = 1100) { unsigned inspected_elements = 0; unsigned number_of_elements_from_big_cc = 0; unsigned number_of_elements_from_tiny_cc = 0; -#ifdef NDEBUG - unsigned pruned_elements = 0; -#endif std::pair projected_coordinate = { mercator::lat2y(input_coordinate.lat / COORDINATE_PRECISION), input_coordinate.lon / COORDINATE_PRECISION}; - // upper bound pruning technique - upper_bound pruning_bound(max_number_of_phantom_nodes); - // initialize queue with root element std::priority_queue traversal_queue; traversal_queue.emplace(0.f, m_search_tree[0]); @@ -669,6 +663,10 @@ class StaticRTree while (!traversal_queue.empty()) { const IncrementalQueryCandidate current_query_node = traversal_queue.top(); + if (current_query_node.min_dist > max_distance) + { + break; + } traversal_queue.pop(); if (current_query_node.node.template is()) @@ -692,18 +690,7 @@ class StaticRTree // distance must be non-negative BOOST_ASSERT(0.f <= current_perpendicular_distance); - if (pruning_bound.get() >= current_perpendicular_distance || - current_edge.is_in_tiny_cc()) - { - pruning_bound.insert(current_perpendicular_distance); - traversal_queue.emplace(current_perpendicular_distance, current_edge); - } -#ifdef NDEBUG - else - { - ++pruned_elements; - } -#endif + traversal_queue.emplace(current_perpendicular_distance, current_edge); } } else @@ -771,9 +758,8 @@ class StaticRTree } // stop the search by flushing the queue - if ((result_phantom_node_vector.size() >= max_number_of_phantom_nodes && - number_of_elements_from_big_cc > 0) || - inspected_elements >= max_checked_elements) + if (result_phantom_node_vector.size() >= max_number_of_phantom_nodes && + number_of_elements_from_big_cc > 0) { traversal_queue = std::priority_queue{}; } @@ -809,6 +795,10 @@ class StaticRTree unsigned number_of_elements_from_big_cc = 0; unsigned number_of_elements_from_tiny_cc = 0; + // is true if a big cc was added to the queue to we also have a lower bound + // for them. it actives pruning for big components + bool has_big_cc = false; + unsigned pruned_elements = 0; std::pair projected_coordinate = { @@ -849,10 +839,11 @@ class StaticRTree BOOST_ASSERT(0.f <= current_perpendicular_distance); if (pruning_bound.get() >= current_perpendicular_distance || - current_edge.is_in_tiny_cc()) + (!has_big_cc && !current_edge.is_in_tiny_cc())) { pruning_bound.insert(current_perpendicular_distance); traversal_queue.emplace(current_perpendicular_distance, current_edge); + has_big_cc = has_big_cc || !current_edge.is_in_tiny_cc(); } else { diff --git a/datastore.cpp b/datastore.cpp index 3c032a2a9..89efefcb2 100644 --- a/datastore.cpp +++ b/datastore.cpp @@ -264,9 +264,10 @@ int main(const int argc, const char *argv[]) boost::filesystem::ifstream hsgr_input_stream(hsgr_path, std::ios::binary); - FingerPrint fingerprint_loaded, fingerprint_orig; + FingerPrint fingerprint_valid = FingerPrint::GetValid(); + FingerPrint fingerprint_loaded; hsgr_input_stream.read((char *)&fingerprint_loaded, sizeof(FingerPrint)); - if (fingerprint_loaded.TestGraphUtil(fingerprint_orig)) + if (fingerprint_loaded.TestGraphUtil(fingerprint_valid)) { SimpleLogger().Write(logDEBUG) << "Fingerprint checked out ok"; } diff --git a/descriptors/description_factory.cpp b/descriptors/description_factory.cpp index e4665622f..71dd79513 100644 --- a/descriptors/description_factory.cpp +++ b/descriptors/description_factory.cpp @@ -173,6 +173,7 @@ void DescriptionFactory::Run(const unsigned zoom_level) // } // string0 = string1; // } + // float segment_length = 0.; EdgeWeight segment_duration = 0; @@ -197,7 +198,8 @@ void DescriptionFactory::Run(const unsigned zoom_level) // Post-processing to remove empty or nearly empty path segments if (path_description.size() > 2 && - std::numeric_limits::epsilon() > path_description.back().length) + std::numeric_limits::epsilon() > path_description.back().length && + !(path_description.end() - 2)->is_via_location) { path_description.pop_back(); path_description.back().necessary = true; @@ -206,7 +208,8 @@ void DescriptionFactory::Run(const unsigned zoom_level) } if (path_description.size() > 2 && - std::numeric_limits::epsilon() > path_description.front().length) + std::numeric_limits::epsilon() > path_description.front().length && + !(path_description.begin() + 1)->is_via_location) { path_description.erase(path_description.begin()); path_description.front().turn_instruction = TurnInstruction::HeadOn; diff --git a/extract.cpp b/extract.cpp index a14762983..5f88cc301 100644 --- a/extract.cpp +++ b/extract.cpp @@ -73,7 +73,7 @@ int main(int argc, char *argv[]) << " not found!"; return 1; } - return extractor().run(extractor_config); + return extractor(extractor_config).run(); } catch (const std::exception &e) { diff --git a/extractor/extraction_containers.cpp b/extractor/extraction_containers.cpp index 7c912f33f..caca05ccb 100644 --- a/extractor/extraction_containers.cpp +++ b/extractor/extraction_containers.cpp @@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../util/osrm_exception.hpp" #include "../util/simple_logger.hpp" #include "../util/timing_util.hpp" +#include "../util/fingerprint.hpp" #include #include @@ -54,6 +55,7 @@ ExtractionContainers::ExtractionContainers() ExtractionContainers::~ExtractionContainers() { + // FIXME isn't this done implicitly of the stxxl::vectors go out of scope? used_node_id_list.clear(); all_nodes_list.clear(); all_edges_list.clear(); @@ -71,394 +73,561 @@ ExtractionContainers::~ExtractionContainers() * - filter nodes list to nodes that are referenced by ways * - merge edges with nodes to include location of start/end points and serialize * - * FIXME: Each of this step should be an own function for readability. */ void ExtractionContainers::PrepareData(const std::string &output_file_name, - const std::string &restrictions_file_name) + const std::string &restrictions_file_name, + const std::string &name_file_name) { try { - unsigned number_of_used_nodes = 0; - unsigned number_of_used_edges = 0; - - std::cout << "[extractor] Sorting used nodes ... " << std::flush; - TIMER_START(sorting_used_nodes); - stxxl::sort(used_node_id_list.begin(), used_node_id_list.end(), Cmp(), stxxl_memory); - TIMER_STOP(sorting_used_nodes); - std::cout << "ok, after " << TIMER_SEC(sorting_used_nodes) << "s" << std::endl; - - std::cout << "[extractor] Erasing duplicate nodes ... " << std::flush; - TIMER_START(erasing_dups); - auto new_end = std::unique(used_node_id_list.begin(), used_node_id_list.end()); - used_node_id_list.resize(new_end - used_node_id_list.begin()); - TIMER_STOP(erasing_dups); - std::cout << "ok, after " << TIMER_SEC(erasing_dups) << "s" << std::endl; - - std::cout << "[extractor] Sorting all nodes ... " << std::flush; - TIMER_START(sorting_nodes); - stxxl::sort(all_nodes_list.begin(), all_nodes_list.end(), ExternalMemoryNodeSTXXLCompare(), - stxxl_memory); - TIMER_STOP(sorting_nodes); - std::cout << "ok, after " << TIMER_SEC(sorting_nodes) << "s" << std::endl; - - std::cout << "[extractor] Sorting used ways ... " << std::flush; - TIMER_START(sort_ways); - stxxl::sort(way_start_end_id_list.begin(), way_start_end_id_list.end(), - FirstAndLastSegmentOfWayStxxlCompare(), stxxl_memory); - TIMER_STOP(sort_ways); - std::cout << "ok, after " << TIMER_SEC(sort_ways) << "s" << std::endl; - - std::cout << "[extractor] Sorting " << restrictions_list.size() - << " restrictions. by from... " << std::flush; - TIMER_START(sort_restrictions); - stxxl::sort(restrictions_list.begin(), restrictions_list.end(), - CmpRestrictionContainerByFrom(), stxxl_memory); - TIMER_STOP(sort_restrictions); - std::cout << "ok, after " << TIMER_SEC(sort_restrictions) << "s" << std::endl; - - std::cout << "[extractor] Fixing restriction starts ... " << std::flush; - TIMER_START(fix_restriction_starts); - auto restrictions_iterator = restrictions_list.begin(); - auto way_start_and_end_iterator = way_start_end_id_list.cbegin(); - - while (way_start_and_end_iterator != way_start_end_id_list.cend() && - restrictions_iterator != restrictions_list.end()) - { - if (way_start_and_end_iterator->way_id < restrictions_iterator->restriction.from.way) - { - ++way_start_and_end_iterator; - continue; - } - - if (way_start_and_end_iterator->way_id > restrictions_iterator->restriction.from.way) - { - ++restrictions_iterator; - continue; - } - - BOOST_ASSERT(way_start_and_end_iterator->way_id == - restrictions_iterator->restriction.from.way); - const NodeID via_node_id = restrictions_iterator->restriction.via.node; - - if (way_start_and_end_iterator->first_segment_source_id == via_node_id) - { - restrictions_iterator->restriction.from.node = - way_start_and_end_iterator->first_segment_target_id; - } - else if (way_start_and_end_iterator->last_segment_target_id == via_node_id) - { - restrictions_iterator->restriction.from.node = - way_start_and_end_iterator->last_segment_source_id; - } - ++restrictions_iterator; - } - - TIMER_STOP(fix_restriction_starts); - std::cout << "ok, after " << TIMER_SEC(fix_restriction_starts) << "s" << std::endl; - - std::cout << "[extractor] Sorting restrictions. by to ... " << std::flush; - TIMER_START(sort_restrictions_to); - stxxl::sort(restrictions_list.begin(), restrictions_list.end(), - CmpRestrictionContainerByTo(), stxxl_memory); - TIMER_STOP(sort_restrictions_to); - std::cout << "ok, after " << TIMER_SEC(sort_restrictions_to) << "s" << std::endl; - - std::cout << "[extractor] Fixing restriction ends ... " << std::flush; - TIMER_START(fix_restriction_ends); - restrictions_iterator = restrictions_list.begin(); - way_start_and_end_iterator = way_start_end_id_list.cbegin(); - while (way_start_and_end_iterator != way_start_end_id_list.cend() && - restrictions_iterator != restrictions_list.end()) - { - if (way_start_and_end_iterator->way_id < restrictions_iterator->restriction.to.way) - { - ++way_start_and_end_iterator; - continue; - } - if (way_start_and_end_iterator->way_id > restrictions_iterator->restriction.to.way) - { - ++restrictions_iterator; - continue; - } - BOOST_ASSERT(way_start_and_end_iterator->way_id == - restrictions_iterator->restriction.to.way); - const NodeID via_node_id = restrictions_iterator->restriction.via.node; - - if (way_start_and_end_iterator->first_segment_source_id == via_node_id) - { - restrictions_iterator->restriction.to.node = - way_start_and_end_iterator->first_segment_target_id; - } - else if (way_start_and_end_iterator->last_segment_target_id == via_node_id) - { - restrictions_iterator->restriction.to.node = - way_start_and_end_iterator->last_segment_source_id; - } - ++restrictions_iterator; - } - TIMER_STOP(fix_restriction_ends); - std::cout << "ok, after " << TIMER_SEC(fix_restriction_ends) << "s" << std::endl; - - // serialize restrictions - std::ofstream restrictions_out_stream; - unsigned written_restriction_count = 0; - restrictions_out_stream.open(restrictions_file_name.c_str(), std::ios::binary); - restrictions_out_stream.write((char *)&fingerprint, sizeof(FingerPrint)); - const auto count_position = restrictions_out_stream.tellp(); - restrictions_out_stream.write((char *)&written_restriction_count, sizeof(unsigned)); - - for (const auto &restriction_container : restrictions_list) - { - if (SPECIAL_NODEID != restriction_container.restriction.from.node && - SPECIAL_NODEID != restriction_container.restriction.to.node) - { - restrictions_out_stream.write((char *)&(restriction_container.restriction), - sizeof(TurnRestriction)); - ++written_restriction_count; - } - } - restrictions_out_stream.seekp(count_position); - restrictions_out_stream.write((char *)&written_restriction_count, sizeof(unsigned)); - - restrictions_out_stream.close(); - SimpleLogger().Write() << "usable restrictions: " << written_restriction_count; - std::ofstream file_out_stream; file_out_stream.open(output_file_name.c_str(), std::ios::binary); + const FingerPrint fingerprint = FingerPrint::GetValid(); file_out_stream.write((char *)&fingerprint, sizeof(FingerPrint)); - file_out_stream.write((char *)&number_of_used_nodes, sizeof(unsigned)); - std::cout << "[extractor] Confirming/Writing used nodes ... " << std::flush; - TIMER_START(write_nodes); - // identify all used nodes by a merging step of two sorted lists - auto node_iterator = all_nodes_list.begin(); - auto node_id_iterator = used_node_id_list.begin(); - while (node_id_iterator != used_node_id_list.end() && node_iterator != all_nodes_list.end()) - { - if (*node_id_iterator < node_iterator->node_id) - { - ++node_id_iterator; - continue; - } - if (*node_id_iterator > node_iterator->node_id) - { - ++node_iterator; - continue; - } - BOOST_ASSERT(*node_id_iterator == node_iterator->node_id); - file_out_stream.write((char *)&(*node_iterator), sizeof(ExternalMemoryNode)); + PrepareNodes(); + WriteNodes(file_out_stream); + PrepareEdges(); + WriteEdges(file_out_stream); - ++number_of_used_nodes; - ++node_id_iterator; - ++node_iterator; - } - - TIMER_STOP(write_nodes); - std::cout << "ok, after " << TIMER_SEC(write_nodes) << "s" << std::endl; - - std::cout << "[extractor] setting number of nodes ... " << std::flush; - std::ios::pos_type previous_file_position = file_out_stream.tellp(); - file_out_stream.seekp(std::ios::beg + sizeof(FingerPrint)); - file_out_stream.write((char *)&number_of_used_nodes, sizeof(unsigned)); - file_out_stream.seekp(previous_file_position); - - std::cout << "ok" << std::endl; - - // Sort edges by start. - std::cout << "[extractor] Sorting edges by start ... " << std::flush; - TIMER_START(sort_edges_by_start); - stxxl::sort(all_edges_list.begin(), all_edges_list.end(), CmpEdgeByStartID(), stxxl_memory); - TIMER_STOP(sort_edges_by_start); - std::cout << "ok, after " << TIMER_SEC(sort_edges_by_start) << "s" << std::endl; - - std::cout << "[extractor] Setting start coords ... " << std::flush; - TIMER_START(set_start_coords); - file_out_stream.write((char *)&number_of_used_edges, sizeof(unsigned)); - // Traverse list of edges and nodes in parallel and set start coord - node_iterator = all_nodes_list.begin(); - auto edge_iterator = all_edges_list.begin(); - while (edge_iterator != all_edges_list.end() && node_iterator != all_nodes_list.end()) - { - if (edge_iterator->start < node_iterator->node_id) - { - ++edge_iterator; - continue; - } - if (edge_iterator->start > node_iterator->node_id) - { - node_iterator++; - continue; - } - - BOOST_ASSERT(edge_iterator->start == node_iterator->node_id); - edge_iterator->source_coordinate.lat = node_iterator->lat; - edge_iterator->source_coordinate.lon = node_iterator->lon; - ++edge_iterator; - } - TIMER_STOP(set_start_coords); - std::cout << "ok, after " << TIMER_SEC(set_start_coords) << "s" << std::endl; - - // Sort Edges by target - std::cout << "[extractor] Sorting edges by target ... " << std::flush; - TIMER_START(sort_edges_by_target); - stxxl::sort(all_edges_list.begin(), all_edges_list.end(), CmpEdgeByTargetID(), - stxxl_memory); - TIMER_STOP(sort_edges_by_target); - std::cout << "ok, after " << TIMER_SEC(sort_edges_by_target) << "s" << std::endl; - - std::cout << "[extractor] Setting target coords ... " << std::flush; - TIMER_START(set_target_coords); - // Traverse list of edges and nodes in parallel and set target coord - node_iterator = all_nodes_list.begin(); - edge_iterator = all_edges_list.begin(); - - // Also serializes the edges - while (edge_iterator != all_edges_list.end() && node_iterator != all_nodes_list.end()) - { - if (edge_iterator->target < node_iterator->node_id) - { - ++edge_iterator; - continue; - } - if (edge_iterator->target > node_iterator->node_id) - { - ++node_iterator; - continue; - } - BOOST_ASSERT(edge_iterator->target == node_iterator->node_id); - if (edge_iterator->source_coordinate.lat != std::numeric_limits::min() && - edge_iterator->source_coordinate.lon != std::numeric_limits::min()) - { - BOOST_ASSERT(edge_iterator->speed != -1); - edge_iterator->target_coordinate.lat = node_iterator->lat; - edge_iterator->target_coordinate.lon = node_iterator->lon; - - const double distance = coordinate_calculation::euclidean_distance( - edge_iterator->source_coordinate.lat, edge_iterator->source_coordinate.lon, - node_iterator->lat, node_iterator->lon); - - const double weight = (distance * 10.) / (edge_iterator->speed / 3.6); - int integer_weight = std::max( - 1, (int)std::floor( - (edge_iterator->is_duration_set ? edge_iterator->speed : weight) + .5)); - // FIXME: This means we have a _minimum_ edge length of 1m - // maybe use dm as base unit? - const int integer_distance = std::max(1, (int)distance); - const short zero = 0; - const short one = 1; - const bool yes = true; - const bool no = false; - - file_out_stream.write((char *)&edge_iterator->start, sizeof(unsigned)); - file_out_stream.write((char *)&edge_iterator->target, sizeof(unsigned)); - file_out_stream.write((char *)&integer_distance, sizeof(int)); - switch (edge_iterator->direction) - { - case ExtractionWay::notSure: - file_out_stream.write((char *)&zero, sizeof(short)); - break; - case ExtractionWay::oneway: - file_out_stream.write((char *)&one, sizeof(short)); - break; - case ExtractionWay::bidirectional: - file_out_stream.write((char *)&zero, sizeof(short)); - break; - case ExtractionWay::opposite: - file_out_stream.write((char *)&one, sizeof(short)); - break; - default: - throw osrm::exception("edge has broken direction"); - } - - file_out_stream.write((char *)&integer_weight, sizeof(int)); - file_out_stream.write((char *)&edge_iterator->name_id, sizeof(unsigned)); - if (edge_iterator->is_roundabout) - { - file_out_stream.write((char *)&yes, sizeof(bool)); - } - else - { - file_out_stream.write((char *)&no, sizeof(bool)); - } - if (edge_iterator->is_in_tiny_cc) - { - file_out_stream.write((char *)&yes, sizeof(bool)); - } - else - { - file_out_stream.write((char *)&no, sizeof(bool)); - } - if (edge_iterator->is_access_restricted) - { - file_out_stream.write((char *)&yes, sizeof(bool)); - } - else - { - file_out_stream.write((char *)&no, sizeof(bool)); - } - - // cannot take adress of bit field, so use local - const TravelMode travel_mode = edge_iterator->travel_mode; - file_out_stream.write((char *)&travel_mode, sizeof(TravelMode)); - - if (edge_iterator->is_split) - { - file_out_stream.write((char *)&yes, sizeof(bool)); - } - else - { - file_out_stream.write((char *)&no, sizeof(bool)); - } - ++number_of_used_edges; - } - ++edge_iterator; - } - TIMER_STOP(set_target_coords); - std::cout << "ok, after " << TIMER_SEC(set_target_coords) << "s" << std::endl; - - std::cout << "[extractor] setting number of edges ... " << std::flush; - - file_out_stream.seekp(previous_file_position); - file_out_stream.write((char *)&number_of_used_edges, sizeof(unsigned)); file_out_stream.close(); - std::cout << "ok" << std::endl; - std::cout << "[extractor] writing street name index ... " << std::flush; - TIMER_START(write_name_index); - std::string name_file_streamName = (output_file_name + ".names"); - boost::filesystem::ofstream name_file_stream(name_file_streamName, std::ios::binary); + PrepareRestrictions(); + WriteRestrictions(restrictions_file_name); - unsigned total_length = 0; - std::vector name_lengths; - for (const std::string &temp_string : name_list) - { - const unsigned string_length = - std::min(static_cast(temp_string.length()), 255u); - name_lengths.push_back(string_length); - total_length += string_length; - } - - RangeTable<> table(name_lengths); - name_file_stream << table; - - name_file_stream.write((char *)&total_length, sizeof(unsigned)); - // write all chars consecutively - for (const std::string &temp_string : name_list) - { - const unsigned string_length = - std::min(static_cast(temp_string.length()), 255u); - name_file_stream.write(temp_string.c_str(), string_length); - } - - name_file_stream.close(); - TIMER_STOP(write_name_index); - std::cout << "ok, after " << TIMER_SEC(write_name_index) << "s" << std::endl; - - SimpleLogger().Write() << "Processed " << number_of_used_nodes << " nodes and " - << number_of_used_edges << " edges"; + WriteNames(name_file_name); } catch (const std::exception &e) { std::cerr << "Caught Execption:" << e.what() << std::endl; } } + +void ExtractionContainers::WriteNames(const std::string& names_file_name) const +{ + std::cout << "[extractor] writing street name index ... " << std::flush; + TIMER_START(write_name_index); + boost::filesystem::ofstream name_file_stream(names_file_name, std::ios::binary); + + unsigned total_length = 0; + std::vector name_lengths; + for (const std::string &temp_string : name_list) + { + const unsigned string_length = + std::min(static_cast(temp_string.length()), 255u); + name_lengths.push_back(string_length); + total_length += string_length; + } + + // builds and writes the index + RangeTable<> name_index_range(name_lengths); + name_file_stream << name_index_range; + + name_file_stream.write((char *)&total_length, sizeof(unsigned)); + // write all chars consecutively + for (const std::string &temp_string : name_list) + { + const unsigned string_length = + std::min(static_cast(temp_string.length()), 255u); + name_file_stream.write(temp_string.c_str(), string_length); + } + + name_file_stream.close(); + TIMER_STOP(write_name_index); + std::cout << "ok, after " << TIMER_SEC(write_name_index) << "s" << std::endl; +} + +void ExtractionContainers::PrepareNodes() +{ + std::cout << "[extractor] Sorting used nodes ... " << std::flush; + TIMER_START(sorting_used_nodes); + stxxl::sort(used_node_id_list.begin(), used_node_id_list.end(), Cmp(), stxxl_memory); + TIMER_STOP(sorting_used_nodes); + std::cout << "ok, after " << TIMER_SEC(sorting_used_nodes) << "s" << std::endl; + + std::cout << "[extractor] Erasing duplicate nodes ... " << std::flush; + TIMER_START(erasing_dups); + auto new_end = std::unique(used_node_id_list.begin(), used_node_id_list.end()); + used_node_id_list.resize(new_end - used_node_id_list.begin()); + TIMER_STOP(erasing_dups); + std::cout << "ok, after " << TIMER_SEC(erasing_dups) << "s" << std::endl; + + std::cout << "[extractor] Building node id map ... " << std::flush; + TIMER_START(id_map); + external_to_internal_node_id_map.reserve(used_node_id_list.size()); + for (NodeID i = 0u; i < used_node_id_list.size(); ++i) + external_to_internal_node_id_map[used_node_id_list[i]] = i; + TIMER_STOP(id_map); + std::cout << "ok, after " << TIMER_SEC(id_map) << "s" << std::endl; + + std::cout << "[extractor] Sorting all nodes ... " << std::flush; + TIMER_START(sorting_nodes); + stxxl::sort(all_nodes_list.begin(), all_nodes_list.end(), ExternalMemoryNodeSTXXLCompare(), + stxxl_memory); + TIMER_STOP(sorting_nodes); + std::cout << "ok, after " << TIMER_SEC(sorting_nodes) << "s" << std::endl; +} + +void ExtractionContainers::PrepareEdges() +{ + // Sort edges by start. + std::cout << "[extractor] Sorting edges by start ... " << std::flush; + TIMER_START(sort_edges_by_start); + stxxl::sort(all_edges_list.begin(), all_edges_list.end(), CmpEdgeByStartID(), stxxl_memory); + TIMER_STOP(sort_edges_by_start); + std::cout << "ok, after " << TIMER_SEC(sort_edges_by_start) << "s" << std::endl; + + std::cout << "[extractor] Setting start coords ... " << std::flush; + TIMER_START(set_start_coords); + // Traverse list of edges and nodes in parallel and set start coord + auto node_iterator = all_nodes_list.begin(); + auto edge_iterator = all_edges_list.begin(); + while (edge_iterator != all_edges_list.end() && node_iterator != all_nodes_list.end()) + { + if (edge_iterator->result.source < node_iterator->node_id) + { + edge_iterator->result.source = SPECIAL_NODEID; + ++edge_iterator; + continue; + } + if (edge_iterator->result.source > node_iterator->node_id) + { + node_iterator++; + continue; + } + + // remove loops + if (edge_iterator->result.source == edge_iterator->result.target) + { + edge_iterator->result.source = SPECIAL_NODEID; + edge_iterator->result.target = SPECIAL_NODEID; + ++edge_iterator; + continue; + } + + BOOST_ASSERT(edge_iterator->result.source == node_iterator->node_id); + + // assign new node id + auto id_iter = external_to_internal_node_id_map.find(node_iterator->node_id); + BOOST_ASSERT(id_iter != external_to_internal_node_id_map.end()); + edge_iterator->result.source = id_iter->second; + + edge_iterator->source_coordinate.lat = node_iterator->lat; + edge_iterator->source_coordinate.lon = node_iterator->lon; + ++edge_iterator; + } + TIMER_STOP(set_start_coords); + std::cout << "ok, after " << TIMER_SEC(set_start_coords) << "s" << std::endl; + + // Sort Edges by target + std::cout << "[extractor] Sorting edges by target ... " << std::flush; + TIMER_START(sort_edges_by_target); + stxxl::sort(all_edges_list.begin(), all_edges_list.end(), CmpEdgeByTargetID(), + stxxl_memory); + TIMER_STOP(sort_edges_by_target); + std::cout << "ok, after " << TIMER_SEC(sort_edges_by_target) << "s" << std::endl; + + // Compute edge weights + std::cout << "[extractor] Computing edge weights ... " << std::flush; + TIMER_START(compute_weights); + node_iterator = all_nodes_list.begin(); + edge_iterator = all_edges_list.begin(); + while (edge_iterator != all_edges_list.end() && node_iterator != all_nodes_list.end()) + { + // skip all invalid edges + if (edge_iterator->result.source == SPECIAL_NODEID) + { + ++edge_iterator; + continue; + } + + if (edge_iterator->result.target < node_iterator->node_id) + { + edge_iterator->result.target = SPECIAL_NODEID; + ++edge_iterator; + continue; + } + if (edge_iterator->result.target > node_iterator->node_id) + { + ++node_iterator; + continue; + } + + BOOST_ASSERT(edge_iterator->result.target == node_iterator->node_id); + BOOST_ASSERT(edge_iterator->weight_data.speed >= 0); + BOOST_ASSERT(edge_iterator->source_coordinate.lat != std::numeric_limits::min()); + BOOST_ASSERT(edge_iterator->source_coordinate.lon != std::numeric_limits::min()); + + const double distance = coordinate_calculation::euclidean_distance( + edge_iterator->source_coordinate.lat, edge_iterator->source_coordinate.lon, + node_iterator->lat, node_iterator->lon); + + const double weight = [distance](const InternalExtractorEdge::WeightData& data) { + switch (data.type) + { + case InternalExtractorEdge::WeightType::EDGE_DURATION: + case InternalExtractorEdge::WeightType::WAY_DURATION: + return data.duration * 10.; + break; + case InternalExtractorEdge::WeightType::SPEED: + return (distance * 10.) / (data.speed / 3.6); + break; + case InternalExtractorEdge::WeightType::INVALID: + osrm::exception("invalid weight type"); + } + return -1.0; + }(edge_iterator->weight_data); + + auto& edge = edge_iterator->result; + edge.weight = std::max(1, static_cast(std::floor(weight + .5))); + + // assign new node id + auto id_iter = external_to_internal_node_id_map.find(node_iterator->node_id); + BOOST_ASSERT(id_iter != external_to_internal_node_id_map.end()); + edge.target = id_iter->second; + + // orient edges consistently: source id < target id + // important for multi-edge removal + if (edge.source > edge.target) + { + std::swap(edge.source, edge.target); + + // std::swap does not work with bit-fields + bool temp = edge.forward; + edge.forward = edge.backward; + edge.backward = temp; + } + ++edge_iterator; + } + TIMER_STOP(compute_weights); + std::cout << "ok, after " << TIMER_SEC(compute_weights) << "s" << std::endl; + + // Sort edges by start. + std::cout << "[extractor] Sorting edges by renumbered start ... " << std::flush; + TIMER_START(sort_edges_by_renumbered_start); + stxxl::sort(all_edges_list.begin(), all_edges_list.end(), CmpEdgeByStartThenTargetID(), stxxl_memory); + TIMER_STOP(sort_edges_by_renumbered_start); + std::cout << "ok, after " << TIMER_SEC(sort_edges_by_renumbered_start) << "s" << std::endl; + + BOOST_ASSERT(all_edges_list.size() > 0); + for (unsigned i = 0; i < all_edges_list.size();) + { + // only invalid edges left + if (all_edges_list[i].result.source == SPECIAL_NODEID) + { + break; + } + // skip invalid edges + if (all_edges_list[i].result.target == SPECIAL_NODEID) + { + continue; + } + + unsigned start_idx = i; + NodeID source = all_edges_list[i].result.source; + NodeID target = all_edges_list[i].result.target; + + int min_forward_weight = std::numeric_limits::max(); + int min_backward_weight = std::numeric_limits::max(); + unsigned min_forward_idx = std::numeric_limits::max(); + unsigned min_backward_idx = std::numeric_limits::max(); + + // find minimal edge in both directions + while (all_edges_list[i].result.source == source && + all_edges_list[i].result.target == target) + { + if (all_edges_list[i].result.forward && all_edges_list[i].result.weight < min_forward_weight) + { + min_forward_idx = i; + } + if (all_edges_list[i].result.backward && all_edges_list[i].result.weight < min_backward_weight) + { + min_backward_idx = i; + } + + // this also increments the outer loop counter! + i++; + } + + BOOST_ASSERT(min_forward_idx == std::numeric_limits::max() || min_forward_idx < i); + BOOST_ASSERT(min_backward_idx == std::numeric_limits::max() || min_backward_idx < i); + BOOST_ASSERT(min_backward_idx != std::numeric_limits::max() || + min_forward_idx != std::numeric_limits::max()); + + if (min_backward_idx == min_forward_idx) + { + all_edges_list[min_forward_idx].result.is_split = false; + all_edges_list[min_forward_idx].result.forward = true; + all_edges_list[min_forward_idx].result.backward = true; + } + else + { + bool has_forward = min_forward_idx != std::numeric_limits::max(); + bool has_backward = min_backward_idx != std::numeric_limits::max(); + if (has_forward) + { + all_edges_list[min_forward_idx].result.forward = true; + all_edges_list[min_forward_idx].result.backward = false; + all_edges_list[min_forward_idx].result.is_split = has_backward; + } + if (has_backward) + { + std::swap(all_edges_list[min_backward_idx].result.source, + all_edges_list[min_backward_idx].result.target); + all_edges_list[min_backward_idx].result.forward = true; + all_edges_list[min_backward_idx].result.backward = false; + all_edges_list[min_backward_idx].result.is_split = has_forward; + } + } + + // invalidate all unused edges + for (unsigned j = start_idx; j < i; j++) + { + if (j == min_forward_idx || j == min_backward_idx) + { + continue; + } + all_edges_list[j].result.source = SPECIAL_NODEID; + all_edges_list[j].result.target = SPECIAL_NODEID; + } + } +} + +void ExtractionContainers::WriteEdges(std::ofstream& file_out_stream) const +{ + std::cout << "[extractor] Writing used egdes ... " << std::flush; + TIMER_START(write_edges); + // Traverse list of edges and nodes in parallel and set target coord + unsigned number_of_used_edges = 0; + + auto start_position = file_out_stream.tellp(); + file_out_stream.write((char *)&number_of_used_edges, sizeof(unsigned)); + + for (const auto& edge : all_edges_list) + { + if (edge.result.source == SPECIAL_NODEID || edge.result.target == SPECIAL_NODEID) + { + continue; + } + + file_out_stream.write((char*) &edge.result, sizeof(NodeBasedEdge)); + number_of_used_edges++; + } + TIMER_STOP(write_edges); + std::cout << "ok, after " << TIMER_SEC(write_edges) << "s" << std::endl; + + std::cout << "[extractor] setting number of edges ... " << std::flush; + file_out_stream.seekp(start_position); + file_out_stream.write((char *)&number_of_used_edges, sizeof(unsigned)); + std::cout << "ok" << std::endl; + + SimpleLogger().Write() << "Processed " << number_of_used_edges << " edges"; +} + +void ExtractionContainers::WriteNodes(std::ofstream& file_out_stream) const +{ + unsigned number_of_used_nodes = 0; + // write dummy value, will be overwritten later + file_out_stream.write((char *)&number_of_used_nodes, sizeof(unsigned)); + std::cout << "[extractor] Confirming/Writing used nodes ... " << std::flush; + TIMER_START(write_nodes); + // identify all used nodes by a merging step of two sorted lists + auto node_iterator = all_nodes_list.begin(); + auto node_id_iterator = used_node_id_list.begin(); + while (node_id_iterator != used_node_id_list.end() && node_iterator != all_nodes_list.end()) + { + if (*node_id_iterator < node_iterator->node_id) + { + ++node_id_iterator; + continue; + } + if (*node_id_iterator > node_iterator->node_id) + { + ++node_iterator; + continue; + } + BOOST_ASSERT(*node_id_iterator == node_iterator->node_id); + + file_out_stream.write((char *)&(*node_iterator), sizeof(ExternalMemoryNode)); + + ++number_of_used_nodes; + ++node_id_iterator; + ++node_iterator; + } + TIMER_STOP(write_nodes); + std::cout << "ok, after " << TIMER_SEC(write_nodes) << "s" << std::endl; + + std::cout << "[extractor] setting number of nodes ... " << std::flush; + std::ios::pos_type previous_file_position = file_out_stream.tellp(); + file_out_stream.seekp(std::ios::beg + sizeof(FingerPrint)); + file_out_stream.write((char *)&number_of_used_nodes, sizeof(unsigned)); + file_out_stream.seekp(previous_file_position); + std::cout << "ok" << std::endl; + + SimpleLogger().Write() << "Processed " << number_of_used_nodes << " nodes"; +} + +void ExtractionContainers::WriteRestrictions(const std::string& path) const +{ + // serialize restrictions + std::ofstream restrictions_out_stream; + unsigned written_restriction_count = 0; + restrictions_out_stream.open(path.c_str(), std::ios::binary); + const FingerPrint fingerprint = FingerPrint::GetValid(); + restrictions_out_stream.write((char *)&fingerprint, sizeof(FingerPrint)); + const auto count_position = restrictions_out_stream.tellp(); + restrictions_out_stream.write((char *)&written_restriction_count, sizeof(unsigned)); + + for (const auto &restriction_container : restrictions_list) + { + if (SPECIAL_NODEID != restriction_container.restriction.from.node && + SPECIAL_NODEID != restriction_container.restriction.via.node && + SPECIAL_NODEID != restriction_container.restriction.to.node) + { + restrictions_out_stream.write((char *)&(restriction_container.restriction), + sizeof(TurnRestriction)); + ++written_restriction_count; + } + } + restrictions_out_stream.seekp(count_position); + restrictions_out_stream.write((char *)&written_restriction_count, sizeof(unsigned)); + restrictions_out_stream.close(); + SimpleLogger().Write() << "usable restrictions: " << written_restriction_count; +} + +void ExtractionContainers::PrepareRestrictions() +{ + std::cout << "[extractor] Sorting used ways ... " << std::flush; + TIMER_START(sort_ways); + stxxl::sort(way_start_end_id_list.begin(), way_start_end_id_list.end(), + FirstAndLastSegmentOfWayStxxlCompare(), stxxl_memory); + TIMER_STOP(sort_ways); + std::cout << "ok, after " << TIMER_SEC(sort_ways) << "s" << std::endl; + + std::cout << "[extractor] Sorting " << restrictions_list.size() + << " restriction. by from... " << std::flush; + TIMER_START(sort_restrictions); + stxxl::sort(restrictions_list.begin(), restrictions_list.end(), + CmpRestrictionContainerByFrom(), stxxl_memory); + TIMER_STOP(sort_restrictions); + std::cout << "ok, after " << TIMER_SEC(sort_restrictions) << "s" << std::endl; + + std::cout << "[extractor] Fixing restriction starts ... " << std::flush; + TIMER_START(fix_restriction_starts); + auto restrictions_iterator = restrictions_list.begin(); + auto way_start_and_end_iterator = way_start_end_id_list.cbegin(); + + while (way_start_and_end_iterator != way_start_end_id_list.cend() && + restrictions_iterator != restrictions_list.end()) + { + if (way_start_and_end_iterator->way_id < restrictions_iterator->restriction.from.way) + { + ++way_start_and_end_iterator; + continue; + } + + if (way_start_and_end_iterator->way_id > restrictions_iterator->restriction.from.way) + { + SimpleLogger().Write(LogLevel::logDEBUG) << "Restriction references invalid way: " << restrictions_iterator->restriction.from.way; + restrictions_iterator->restriction.from.node = SPECIAL_NODEID; + ++restrictions_iterator; + continue; + } + + BOOST_ASSERT(way_start_and_end_iterator->way_id == + restrictions_iterator->restriction.from.way); + // we do not remap the via id yet, since we will need it for the to node as well + const NodeID via_node_id = restrictions_iterator->restriction.via.node; + + // check if via is actually valid, if not invalidate + auto via_id_iter = external_to_internal_node_id_map.find(via_node_id); + if(via_id_iter == external_to_internal_node_id_map.end()) + { + SimpleLogger().Write(LogLevel::logDEBUG) << "Restriction references invalid node: " << restrictions_iterator->restriction.via.node; + restrictions_iterator->restriction.via.node = SPECIAL_NODEID; + ++restrictions_iterator; + continue; + } + + if (way_start_and_end_iterator->first_segment_source_id == via_node_id) + { + // assign new from node id + auto id_iter = external_to_internal_node_id_map.find( + way_start_and_end_iterator->first_segment_target_id); + BOOST_ASSERT(id_iter != external_to_internal_node_id_map.end()); + restrictions_iterator->restriction.from.node = id_iter->second; + } + else if (way_start_and_end_iterator->last_segment_target_id == via_node_id) + { + // assign new from node id + auto id_iter = external_to_internal_node_id_map.find( + way_start_and_end_iterator->last_segment_source_id); + BOOST_ASSERT(id_iter != external_to_internal_node_id_map.end()); + restrictions_iterator->restriction.from.node = id_iter->second; + } + ++restrictions_iterator; + } + + TIMER_STOP(fix_restriction_starts); + std::cout << "ok, after " << TIMER_SEC(fix_restriction_starts) << "s" << std::endl; + + std::cout << "[extractor] Sorting restrictions. by to ... " << std::flush; + TIMER_START(sort_restrictions_to); + stxxl::sort(restrictions_list.begin(), restrictions_list.end(), + CmpRestrictionContainerByTo(), stxxl_memory); + TIMER_STOP(sort_restrictions_to); + std::cout << "ok, after " << TIMER_SEC(sort_restrictions_to) << "s" << std::endl; + + std::cout << "[extractor] Fixing restriction ends ... " << std::flush; + TIMER_START(fix_restriction_ends); + restrictions_iterator = restrictions_list.begin(); + way_start_and_end_iterator = way_start_end_id_list.cbegin(); + while (way_start_and_end_iterator != way_start_end_id_list.cend() && + restrictions_iterator != restrictions_list.end()) + { + if (way_start_and_end_iterator->way_id < restrictions_iterator->restriction.to.way) + { + ++way_start_and_end_iterator; + continue; + } + if (restrictions_iterator->restriction.from.node == SPECIAL_NODEID || + restrictions_iterator->restriction.via.node == SPECIAL_NODEID) + { + ++restrictions_iterator; + continue; + } + if (way_start_and_end_iterator->way_id > restrictions_iterator->restriction.to.way) + { + SimpleLogger().Write(LogLevel::logDEBUG) << "Restriction references invalid way: " << restrictions_iterator->restriction.to.way; + restrictions_iterator->restriction.to.way = SPECIAL_NODEID; + ++restrictions_iterator; + continue; + } + BOOST_ASSERT(way_start_and_end_iterator->way_id == + restrictions_iterator->restriction.to.way); + const NodeID via_node_id = restrictions_iterator->restriction.via.node; + + // assign new via node id + auto via_id_iter = external_to_internal_node_id_map.find(via_node_id); + BOOST_ASSERT(via_id_iter != external_to_internal_node_id_map.end()); + restrictions_iterator->restriction.via.node = via_id_iter->second; + + if (way_start_and_end_iterator->first_segment_source_id == via_node_id) + { + auto to_id_iter = external_to_internal_node_id_map.find( + way_start_and_end_iterator->first_segment_target_id); + BOOST_ASSERT(to_id_iter != external_to_internal_node_id_map.end()); + restrictions_iterator->restriction.to.node = to_id_iter->second; + } + else if (way_start_and_end_iterator->last_segment_target_id == via_node_id) + { + auto to_id_iter = external_to_internal_node_id_map.find( + way_start_and_end_iterator->last_segment_source_id); + BOOST_ASSERT(to_id_iter != external_to_internal_node_id_map.end()); + restrictions_iterator->restriction.to.node = to_id_iter->second; + } + ++restrictions_iterator; + } + TIMER_STOP(fix_restriction_ends); + std::cout << "ok, after " << TIMER_SEC(fix_restriction_ends) << "s" << std::endl; +} diff --git a/extractor/extraction_containers.hpp b/extractor/extraction_containers.hpp index d04f59549..ef71d059f 100644 --- a/extractor/extraction_containers.hpp +++ b/extractor/extraction_containers.hpp @@ -32,13 +32,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "first_and_last_segment_of_way.hpp" #include "../data_structures/external_memory_node.hpp" #include "../data_structures/restriction.hpp" -#include "../util/fingerprint.hpp" #include +#include /** * Uses external memory containers from stxxl to store all the data that * is collected by the extractor callbacks. + * + * The data is the filtered, aggregated and finally written to disk. */ class ExtractionContainers { @@ -49,6 +51,14 @@ class ExtractionContainers #else const static unsigned stxxl_memory = ((sizeof(std::size_t) == 4) ? INT_MAX : UINT_MAX); #endif + void PrepareNodes(); + void PrepareRestrictions(); + void PrepareEdges(); + + void WriteNodes(std::ofstream& file_out_stream) const; + void WriteRestrictions(const std::string& restrictions_file_name) const; + void WriteEdges(std::ofstream& file_out_stream) const; + void WriteNames(const std::string& names_file_name) const; public: using STXXLNodeIDVector = stxxl::vector; using STXXLNodeVector = stxxl::vector; @@ -63,14 +73,15 @@ class ExtractionContainers STXXLStringVector name_list; STXXLRestrictionsVector restrictions_list; STXXLWayIDStartEndVector way_start_end_id_list; - const FingerPrint fingerprint; + std::unordered_map external_to_internal_node_id_map; ExtractionContainers(); ~ExtractionContainers(); void PrepareData(const std::string &output_file_name, - const std::string &restrictions_file_name); + const std::string &restrictions_file_name, + const std::string &names_file_name); }; #endif /* EXTRACTION_CONTAINERS_HPP */ diff --git a/extractor/extraction_helper_functions.hpp b/extractor/extraction_helper_functions.hpp index d10200abb..e61e54344 100644 --- a/extractor/extraction_helper_functions.hpp +++ b/extractor/extraction_helper_functions.hpp @@ -102,7 +102,7 @@ unsigned parseDuration(const std::string &s) minutes = cast::string_to_int(result[1]); hours = cast::string_to_int(result[0]); } - return 10 * (3600 * hours + 60 * minutes + seconds); + return (3600 * hours + 60 * minutes + seconds); } } else if (iso_8601_duration_is_valid(s)) diff --git a/extractor/extractor.cpp b/extractor/extractor.cpp index fce0f8898..c1a0a2127 100644 --- a/extractor/extractor.cpp +++ b/extractor/extractor.cpp @@ -82,7 +82,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * .restrictions : Turn restrictions that are used my osrm-prepare to construct the edge-expanded graph * */ -int extractor::run(const ExtractorConfig &extractor_config) +int extractor::run() { try { @@ -91,20 +91,20 @@ int extractor::run(const ExtractorConfig &extractor_config) const unsigned recommended_num_threads = tbb::task_scheduler_init::default_num_threads(); const auto number_of_threads = - std::min(recommended_num_threads, extractor_config.requested_num_threads); + std::min(recommended_num_threads, config.requested_num_threads); tbb::task_scheduler_init init(number_of_threads); - SimpleLogger().Write() << "Input file: " << extractor_config.input_path.filename().string(); - SimpleLogger().Write() << "Profile: " << extractor_config.profile_path.filename().string(); + SimpleLogger().Write() << "Input file: " << config.input_path.filename().string(); + SimpleLogger().Write() << "Profile: " << config.profile_path.filename().string(); SimpleLogger().Write() << "Threads: " << number_of_threads; // setup scripting environment - ScriptingEnvironment scripting_environment(extractor_config.profile_path.string().c_str()); + ScriptingEnvironment scripting_environment(config.profile_path.string().c_str()); ExtractionContainers extraction_containers; auto extractor_callbacks = osrm::make_unique(extraction_containers); - const osmium::io::File input_file(extractor_config.input_path.string()); + const osmium::io::File input_file(config.input_path.string()); osmium::io::Reader reader(input_file); const osmium::io::Header header = reader.header(); @@ -131,7 +131,7 @@ int extractor::run(const ExtractorConfig &extractor_config) } SimpleLogger().Write() << "timestamp: " << timestamp; - boost::filesystem::ofstream timestamp_out(extractor_config.timestamp_file_name); + boost::filesystem::ofstream timestamp_out(config.timestamp_file_name); timestamp_out.write(timestamp.c_str(), timestamp.length()); timestamp_out.close(); @@ -236,12 +236,13 @@ int extractor::run(const ExtractorConfig &extractor_config) return 1; } - extraction_containers.PrepareData(extractor_config.output_file_name, - extractor_config.restriction_file_name); + extraction_containers.PrepareData(config.output_file_name, + config.restriction_file_name, + config.names_file_name); TIMER_STOP(extracting); SimpleLogger().Write() << "extraction finished after " << TIMER_SEC(extracting) << "s"; SimpleLogger().Write() << "To prepare the data for routing, run: " - << "./osrm-prepare " << extractor_config.output_file_name + << "./osrm-prepare " << config.output_file_name << std::endl; } catch (std::exception &e) diff --git a/extractor/extractor.hpp b/extractor/extractor.hpp index 8ea56c3cb..72defca05 100644 --- a/extractor/extractor.hpp +++ b/extractor/extractor.hpp @@ -30,8 +30,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "extractor_options.hpp" -struct extractor +class extractor { - int run(const ExtractorConfig &extractor_config); +public: + extractor(const ExtractorConfig &extractor_config) + : config(extractor_config) {} + int run(); +private: + ExtractorConfig config; }; #endif /* EXTRACTOR_HPP */ diff --git a/extractor/extractor_callbacks.cpp b/extractor/extractor_callbacks.cpp index deef831c3..bab309d1d 100644 --- a/extractor/extractor_callbacks.cpp +++ b/extractor/extractor_callbacks.cpp @@ -108,17 +108,40 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti << " of size " << input_way.nodes().size(); return; } + + InternalExtractorEdge::WeightData forward_weight_data; + InternalExtractorEdge::WeightData backward_weight_data; + if (0 < parsed_way.duration) { - // TODO: iterate all way segments and set duration corresponding to the length of each - // segment - const_cast(parsed_way).forward_speed = - parsed_way.duration / (input_way.nodes().size() - 1); - const_cast(parsed_way).backward_speed = - parsed_way.duration / (input_way.nodes().size() - 1); + const unsigned num_edges = (input_way.nodes().size() - 1); + // FIXME We devide by the numer of nodes here, but should rather consider + // the length of each segment. We would eigther have to compute the length + // of the whole way here (we can't: no node coordinates) or push that back + // to the container and keep a reference to the way. + forward_weight_data.duration = parsed_way.duration / num_edges; + forward_weight_data.type = InternalExtractorEdge::WeightType::WAY_DURATION; + backward_weight_data.duration = parsed_way.duration / num_edges; + backward_weight_data.type = InternalExtractorEdge::WeightType::WAY_DURATION; + } + else + { + if (parsed_way.forward_speed > 0 && + parsed_way.forward_travel_mode != TRAVEL_MODE_INACCESSIBLE) + { + forward_weight_data.speed = parsed_way.forward_speed; + forward_weight_data.type = InternalExtractorEdge::WeightType::SPEED; + } + if (parsed_way.backward_speed > 0 && + parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE) + { + backward_weight_data.speed = parsed_way.backward_speed; + backward_weight_data.type = InternalExtractorEdge::WeightType::SPEED; + } } - if (std::numeric_limits::epsilon() >= std::abs(-1. - parsed_way.forward_speed)) + if (forward_weight_data.type == InternalExtractorEdge::WeightType::INVALID + && backward_weight_data.type == InternalExtractorEdge::WeightType::INVALID) { SimpleLogger().Write(logDEBUG) << "found way with bogus speed, id: " << input_way.id(); return; @@ -144,36 +167,34 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti ((parsed_way.forward_speed != parsed_way.backward_speed) || (parsed_way.forward_travel_mode != parsed_way.backward_travel_mode)); - auto pair_wise_segment_split = + // lambda to add edge to container + auto pair_wise_segment_split_forward = [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) { - // SimpleLogger().Write() << "adding edge (" << first_node.ref() << "," << - // last_node.ref() << "), fwd speed: " << parsed_way.forward_speed; + const bool forward_only = split_edge || TRAVEL_MODE_INACCESSIBLE == parsed_way.backward_travel_mode; external_memory.all_edges_list.push_back(InternalExtractorEdge( - first_node.ref(), last_node.ref(), - ((split_edge || TRAVEL_MODE_INACCESSIBLE == parsed_way.backward_travel_mode) - ? ExtractionWay::oneway - : ExtractionWay::bidirectional), - parsed_way.forward_speed, name_id, parsed_way.roundabout, parsed_way.ignore_in_grid, - (0 < parsed_way.duration), parsed_way.is_access_restricted, - parsed_way.forward_travel_mode, split_edge)); + first_node.ref(), last_node.ref(), name_id, forward_weight_data, + true, !forward_only, parsed_way.roundabout, parsed_way.ignore_in_grid, + parsed_way.is_access_restricted, parsed_way.forward_travel_mode, split_edge)); external_memory.used_node_id_list.push_back(first_node.ref()); }; const bool is_opposite_way = TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode; + // traverse way in reverse in this case if (is_opposite_way) { + // why don't we have to swap the parsed_way.forward/backward speed here as well const_cast(parsed_way).forward_travel_mode = parsed_way.backward_travel_mode; const_cast(parsed_way).backward_travel_mode = TRAVEL_MODE_INACCESSIBLE; osrm::for_each_pair(input_way.nodes().crbegin(), input_way.nodes().crend(), - pair_wise_segment_split); + pair_wise_segment_split_forward); external_memory.used_node_id_list.push_back(input_way.nodes().front().ref()); } else { osrm::for_each_pair(input_way.nodes().cbegin(), input_way.nodes().cend(), - pair_wise_segment_split); + pair_wise_segment_split_forward); external_memory.used_node_id_list.push_back(input_way.nodes().back().ref()); } @@ -187,22 +208,19 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti if (split_edge) { // Only true if the way should be split - BOOST_ASSERT(parsed_way.backward_travel_mode > 0); + BOOST_ASSERT(parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE); auto pair_wise_segment_split_2 = [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) { - // SimpleLogger().Write() << "adding edge (" << last_node.ref() << "," << - // first_node.ref() << "), bwd speed: " << parsed_way.backward_speed; external_memory.all_edges_list.push_back(InternalExtractorEdge( - last_node.ref(), first_node.ref(), ExtractionWay::oneway, parsed_way.backward_speed, - name_id, parsed_way.roundabout, parsed_way.ignore_in_grid, - (0 < parsed_way.duration), parsed_way.is_access_restricted, - parsed_way.backward_travel_mode, split_edge)); + last_node.ref(), first_node.ref(), name_id, backward_weight_data, + true, false, parsed_way.roundabout, parsed_way.ignore_in_grid, + parsed_way.is_access_restricted, parsed_way.backward_travel_mode, split_edge)); + external_memory.used_node_id_list.push_back(last_node.ref()); }; if (is_opposite_way) { - // SimpleLogger().Write() << "opposite2"; osrm::for_each_pair(input_way.nodes().crbegin(), input_way.nodes().crend(), pair_wise_segment_split_2); external_memory.used_node_id_list.push_back(input_way.nodes().front().ref()); @@ -211,7 +229,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti { osrm::for_each_pair(input_way.nodes().cbegin(), input_way.nodes().cend(), pair_wise_segment_split_2); - external_memory.used_node_id_list.push_back(input_way.nodes().back().ref()); + external_memory.used_node_id_list.push_back(input_way.nodes().front().ref()); } external_memory.way_start_end_id_list.push_back( diff --git a/extractor/extractor_options.cpp b/extractor/extractor_options.cpp index 9ae5cd4c1..23cbacc74 100644 --- a/extractor/extractor_options.cpp +++ b/extractor/extractor_options.cpp @@ -135,6 +135,7 @@ void ExtractorOptions::GenerateOutputFilesNames(ExtractorConfig &extractor_confi boost::filesystem::path &input_path = extractor_config.input_path; extractor_config.output_file_name = input_path.string(); extractor_config.restriction_file_name = input_path.string(); + extractor_config.names_file_name = input_path.string(); extractor_config.timestamp_file_name = input_path.string(); std::string::size_type pos = extractor_config.output_file_name.find(".osm.bz2"); if (pos == std::string::npos) @@ -156,12 +157,14 @@ void ExtractorOptions::GenerateOutputFilesNames(ExtractorConfig &extractor_confi { extractor_config.output_file_name.append(".osrm"); extractor_config.restriction_file_name.append(".osrm.restrictions"); + extractor_config.names_file_name.append(".osrm.names"); extractor_config.timestamp_file_name.append(".osrm.timestamp"); } else { extractor_config.output_file_name.replace(pos, 5, ".osrm"); extractor_config.restriction_file_name.replace(pos, 5, ".osrm.restrictions"); + extractor_config.names_file_name.replace(pos, 5, ".osrm.names"); extractor_config.timestamp_file_name.replace(pos, 5, ".osrm.timestamp"); } } @@ -169,6 +172,7 @@ void ExtractorOptions::GenerateOutputFilesNames(ExtractorConfig &extractor_confi { extractor_config.output_file_name.replace(pos, 8, ".osrm"); extractor_config.restriction_file_name.replace(pos, 8, ".osrm.restrictions"); + extractor_config.names_file_name.replace(pos, 8, ".osrm.names"); extractor_config.timestamp_file_name.replace(pos, 8, ".osrm.timestamp"); } } diff --git a/extractor/extractor_options.hpp b/extractor/extractor_options.hpp index 5712ea01a..c164f6a12 100644 --- a/extractor/extractor_options.hpp +++ b/extractor/extractor_options.hpp @@ -48,6 +48,7 @@ struct ExtractorConfig std::string output_file_name; std::string restriction_file_name; + std::string names_file_name; std::string timestamp_file_name; unsigned requested_num_threads; diff --git a/extractor/internal_extractor_edge.hpp b/extractor/internal_extractor_edge.hpp index 27e1af146..15f3c0d66 100644 --- a/extractor/internal_extractor_edge.hpp +++ b/extractor/internal_extractor_edge.hpp @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../typedefs.h" #include "../data_structures/travel_mode.hpp" +#include "../data_structures/import_edge.hpp" #include @@ -37,83 +38,108 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. struct InternalExtractorEdge { - InternalExtractorEdge() - : start(0), target(0), speed(0), name_id(0), direction(0), is_roundabout(false), - is_in_tiny_cc(false), is_duration_set(false), is_access_restricted(false), - is_split(false), travel_mode(TRAVEL_MODE_INACCESSIBLE) + // specify the type of the weight data + enum class WeightType : char { + INVALID, + SPEED, + EDGE_DURATION, + WAY_DURATION, + }; + + struct WeightData + { + + WeightData() : duration(0.0), type(WeightType::INVALID) + { + } + + union + { + double duration; + double speed; + }; + WeightType type; + }; + + explicit InternalExtractorEdge() + : result(0, 0, 0, 0, false, false, false, false, false, + TRAVEL_MODE_INACCESSIBLE, false) { } - explicit InternalExtractorEdge(NodeID start, - NodeID target, - short direction, - double speed, - unsigned name_id, - bool is_roundabout, - bool is_in_tiny_cc, - bool is_duration_set, - bool is_access_restricted, - TravelMode travel_mode, - bool is_split) - : start(start), target(target), speed(speed), name_id(name_id), direction(direction), - is_roundabout(is_roundabout), is_in_tiny_cc(is_in_tiny_cc), - is_duration_set(is_duration_set), is_access_restricted(is_access_restricted), - is_split(is_split), travel_mode(travel_mode) + explicit InternalExtractorEdge(NodeID source, + NodeID target, + NodeID name_id, + const WeightData& weight_data, + bool forward, + bool backward, + bool roundabout, + bool in_tiny_cc, + bool access_restricted, + TravelMode travel_mode, + bool is_split) + : result(source, target, name_id, 0, forward, backward, roundabout, + in_tiny_cc, access_restricted, travel_mode, is_split), + weight_data(weight_data) { } + // data that will be written to disk + NodeBasedEdge result; + // intermediate edge weight + WeightData weight_data; + // coordinate of the source node + FixedPointCoordinate source_coordinate; + + // necessary static util functions for stxxl's sorting static InternalExtractorEdge min_value() { - return InternalExtractorEdge(0, 0, 0, 0, 0, false, false, false, false, - TRAVEL_MODE_INACCESSIBLE, false); + return InternalExtractorEdge(0, 0, 0, WeightData(), false, false, false, + false, false, TRAVEL_MODE_INACCESSIBLE, false); } static InternalExtractorEdge max_value() { - return InternalExtractorEdge(SPECIAL_NODEID, SPECIAL_NODEID, 0, 0, 0, false, false, false, - false, TRAVEL_MODE_INACCESSIBLE, false); + return InternalExtractorEdge(SPECIAL_NODEID, SPECIAL_NODEID, 0, WeightData(), false, false, + false, false, false, TRAVEL_MODE_INACCESSIBLE, false); + } +}; + +struct CmpEdgeByStartThenTargetID +{ + using value_type = InternalExtractorEdge; + bool operator()(const InternalExtractorEdge &lhs, const InternalExtractorEdge &rhs) const + { + return (lhs.result.source < rhs.result.source) || + ((lhs.result.source == rhs.result.source) && + (lhs.result.target < rhs.result.target)); } - NodeID start; - NodeID target; - double speed; - unsigned name_id; - short direction; - bool is_roundabout : 1; - bool is_in_tiny_cc : 1; - bool is_duration_set : 1; - bool is_access_restricted : 1; - bool is_split : 1; - TravelMode travel_mode : 4; - - FixedPointCoordinate source_coordinate; - FixedPointCoordinate target_coordinate; + value_type max_value() { return InternalExtractorEdge::max_value(); } + value_type min_value() { return InternalExtractorEdge::min_value(); } }; struct CmpEdgeByStartID { using value_type = InternalExtractorEdge; - bool operator()(const InternalExtractorEdge &a, const InternalExtractorEdge &b) const + bool operator()(const InternalExtractorEdge &lhs, const InternalExtractorEdge &rhs) const { - return a.start < b.start; + return lhs.result.source < rhs.result.source; } value_type max_value() { return InternalExtractorEdge::max_value(); } - value_type min_value() { return InternalExtractorEdge::min_value(); } }; struct CmpEdgeByTargetID { using value_type = InternalExtractorEdge; - - bool operator()(const InternalExtractorEdge &a, const InternalExtractorEdge &b) const + bool operator()(const InternalExtractorEdge &lhs, const InternalExtractorEdge &rhs) const { - return a.target < b.target; + return lhs.result.target < rhs.result.target; } value_type max_value() { return InternalExtractorEdge::max_value(); } - value_type min_value() { return InternalExtractorEdge::min_value(); } }; diff --git a/extractor/restriction_parser.cpp b/extractor/restriction_parser.cpp index 77dae9ce3..9b209b719 100644 --- a/extractor/restriction_parser.cpp +++ b/extractor/restriction_parser.cpp @@ -44,7 +44,7 @@ namespace { int lua_error_callback(lua_State *lua_state) { - luabind::object error_msg(luabind::from_stack(lua_state, -1)); + std::string error_msg = lua_tostring(lua_state, -1); std::ostringstream error_stream; error_stream << error_msg; throw osrm::exception("ERROR occured in profile script:\n" + error_stream.str()); diff --git a/extractor/scripting_environment.cpp b/extractor/scripting_environment.cpp index 972772246..b32ab4b25 100644 --- a/extractor/scripting_environment.cpp +++ b/extractor/scripting_environment.cpp @@ -53,7 +53,7 @@ auto get_value_by_key(T const &object, const char *key) -> decltype(object.get_v int lua_error_callback(lua_State *L) // This is so I can use my own function as an // exception handler, pcall_log() { - luabind::object error_msg(luabind::from_stack(L, -1)); + std::string error_msg = lua_tostring(L, -1); std::ostringstream error_stream; error_stream << error_msg; throw osrm::exception("ERROR occured in profile script:\n" + error_stream.str()); diff --git a/features/bicycle/access.feature b/features/bicycle/access.feature index 2087327ca..3a2fffbc0 100644 --- a/features/bicycle/access.feature +++ b/features/bicycle/access.feature @@ -120,7 +120,7 @@ Feature: Bike - Access tags on ways | no | | | | | private | | | | | agricultural | | | | - | forestery | | | | + | forestry | | | | | | yes | | x | | | permissive | | x | | | designated | | x | @@ -128,7 +128,7 @@ Feature: Bike - Access tags on ways | | no | | | | | private | | | | | agricultural | | | - | | forestery | | | + | | forestry | | | | | | yes | x | | | | permissive | x | | | | designated | x | @@ -136,7 +136,7 @@ Feature: Bike - Access tags on ways | | | no | | | | | private | | | | | agricultural | | - | | | forestery | | + | | | forestry | | Scenario: Bike - Access tags on both node and way Then routability should be diff --git a/features/bicycle/access_node.feature b/features/bicycle/access_node.feature index 5a6f2b5f9..05e94feae 100644 --- a/features/bicycle/access_node.feature +++ b/features/bicycle/access_node.feature @@ -7,20 +7,21 @@ Feature: Bike - Access tags on nodes Scenario: Bike - Access tag hierachy on nodes Then routability should be - | node/access | node/vehicle | node/bicycle | bothw | - | | | | x | - | yes | | | x | - | no | | | | - | | yes | | x | - | | no | | | - | no | yes | | x | - | yes | no | | | - | | | yes | x | - | | | no | | - | no | | yes | x | - | yes | | no | | - | | no | yes | x | - | | yes | no | | + | node/access | node/vehicle | node/bicycle | node/highway | bothw | + | | | | | x | + | yes | | | | x | + | no | | | | | + | | yes | | | x | + | | no | | | | + | no | yes | | | x | + | yes | no | | | | + | | | yes | | x | + | | | no | | | + | | | no | crossing | x | + | no | | yes | | x | + | yes | | no | | | + | | no | yes | | x | + | | yes | no | | | Scenario: Bike - Overwriting implied acccess on nodes doesn't overwrite way Then routability should be @@ -45,7 +46,7 @@ Feature: Bike - Access tags on nodes | no | | | | | private | | | | | agricultural | | | | - | forestery | | | | + | forestry | | | | | | yes | | x | | | permissive | | x | | | designated | | x | @@ -53,7 +54,7 @@ Feature: Bike - Access tags on nodes | | no | | | | | private | | | | | agricultural | | | - | | forestery | | | + | | forestry | | | | | | yes | x | | | | permissive | x | | | | designated | x | @@ -61,4 +62,4 @@ Feature: Bike - Access tags on nodes | | | no | | | | | private | | | | | agricultural | | - | | | forestery | | + | | | forestry | | diff --git a/features/bicycle/surface.feature b/features/bicycle/surface.feature index 216307b21..8d303aa12 100644 --- a/features/bicycle/surface.feature +++ b/features/bicycle/surface.feature @@ -16,7 +16,7 @@ Feature: Bike - Surfaces | cycleway | unpaved | 120s | | cycleway | fine_gravel | 120s | | cycleway | gravel | 120s | - | cycleway | pebbelstone | 120s | + | cycleway | pebblestone | 120s | | cycleway | dirt | 120s | | cycleway | earth | 120s | | cycleway | grass | 120s | diff --git a/features/car/maxspeed.feature b/features/car/maxspeed.feature index 781f4c617..84cfa4ff8 100644 --- a/features/car/maxspeed.feature +++ b/features/car/maxspeed.feature @@ -93,3 +93,22 @@ OSRM will use 4/5 of the projected free-flow speed. | primary | 15 | | 30 | 60 | 34 km/h | 59 km/h | | primary | 15 | 3 | 30 | 60 | 15 km/h | 30 km/h | + Scenario: Car - Single lane streets be ignored or incur a penalty + Then routability should be + + | highway | maxspeed | lanes | maxspeed:forward | maxspeed:backward | forw | backw | + | primary | | | | | 63 km/h | 63 km/h | + | primary | | 1 | | | 32 km/h | 32 km/h | + | primary | 60 | | | | 59 km/h | 59 km/h | + | primary | 60 | 1 | | | 30 km/h | 30 km/h | + | primary | | | 60 | | 59 km/h | 63 km/h | + | primary | | 1 | 60 | | 30 km/h | 32 km/h | + | primary | | | | 60 | 63 km/h | 59 km/h | + | primary | | 1 | | 60 | 32 km/h | 30 km/h | + | primary | 15 | | 60 | | 59 km/h | 23 km/h | + | primary | 15 | 1 | 60 | | 30 km/h | 7 km/h | + | primary | 15 | | | 60 | 23 km/h | 59 km/h | + | primary | 15 | 1 | | 60 | 7 km/h | 30 km/h | + | primary | 15 | | 30 | 60 | 34 km/h | 59 km/h | + | primary | 15 | 1 | 30 | 60 | 15 km/h | 30 km/h | + diff --git a/features/car/names.feature b/features/car/names.feature index ac4bfc8e2..b6a95ae7f 100644 --- a/features/car/names.feature +++ b/features/car/names.feature @@ -10,13 +10,13 @@ Feature: Car - Street names in instructions | | c | And the ways - | nodes | name | - | ab | My Way | - | bc | Your Way | + | nodes | name | ref | + | ab | My Way | | + | bc | Your Way | A1 | When I route I should get - | from | to | route | - | a | c | My Way,Your Way | + | from | to | route | + | a | c | My Way,Your Way (A1) | @todo Scenario: Car - Use way type to describe unnamed ways diff --git a/features/foot/access.feature b/features/foot/access.feature index 4d81d0e45..353076c40 100644 --- a/features/foot/access.feature +++ b/features/foot/access.feature @@ -51,7 +51,7 @@ Feature: Foot - Access tags on ways | no | | | | private | | | | agricultural | | | - | forestery | | | + | forestry | | | | | yes | x | | | permissive | x | | | designated | x | @@ -59,7 +59,7 @@ Feature: Foot - Access tags on ways | | no | | | | private | | | | agricultural | | - | | forestery | | + | | forestry | | Scenario: Foot - Access tags on both node and way Then routability should be diff --git a/features/foot/access_node.feature b/features/foot/access_node.feature index 5a4a1314e..8519d0564 100644 --- a/features/foot/access_node.feature +++ b/features/foot/access_node.feature @@ -39,7 +39,7 @@ Feature: Foot - Access tags on nodes | no | | | | private | | | | agricultural | | | - | forestery | | | + | forestry | | | | no | yes | x | | no | permissive | x | | no | designated | x | @@ -47,4 +47,4 @@ Feature: Foot - Access tags on nodes | yes | no | | | yes | private | | | yes | agricultural | | - | yes | forestery | | + | yes | forestry | | diff --git a/features/foot/surface.feature b/features/foot/surface.feature index 556c3d893..9bcd80c78 100644 --- a/features/foot/surface.feature +++ b/features/foot/surface.feature @@ -10,6 +10,6 @@ Feature: Foot - Surfaces | footway | | 145s ~10% | | footway | fine_gravel | 193s ~10% | | footway | gravel | 193s ~10% | - | footway | pebbelstone | 193s ~10% | + | footway | pebblestone | 193s ~10% | | footway | mud | 289s ~10% | | footway | sand | 289s ~10% | diff --git a/features/foot/way.feature b/features/foot/way.feature index 653c7736c..ff3314f1c 100644 --- a/features/foot/way.feature +++ b/features/foot/way.feature @@ -30,3 +30,9 @@ Feature: Foot - Accessability of different way types | pier | x | | cycleway | | | bridleway | | + + Scenario: Foot - Basic access + Then routability should be + | highway | leisure | forw | + | (nil) | track | x | + diff --git a/features/nearest/pick.feature b/features/nearest/pick.feature index 08e665243..75dbc6ea1 100644 --- a/features/nearest/pick.feature +++ b/features/nearest/pick.feature @@ -57,14 +57,13 @@ Feature: Locating Nearest node on a Way - pick closest way Scenario: Nearest - High lat/lon Given the node locations - | node | lat | lon | - | a | -85 | -180 | - | b | -85 | -160 | - | c | -85 | -140 | - | x | 75 | -180 | - | y | 75 | -180 | - | v | 1 | 1 | - | w | -1 | -1 | + | node | lat | lon | + | a | -85 | -180 | + | b | -85 | -160 | + | c | -85 | -140 | + | x | -84.999 | -180 | + | y | -84.999 | -160 | + | z | -84.999 | -140 | And the ways | nodes | @@ -73,6 +72,5 @@ Feature: Locating Nearest node on a Way - pick closest way When I request nearest I should get | in | out | | x | a | - | y | a | - | v | c | - | w | c | + | y | b | + | z | c | diff --git a/features/options/prepare/help.feature b/features/options/prepare/help.feature index 49497ae85..dae522d56 100644 --- a/features/options/prepare/help.feature +++ b/features/options/prepare/help.feature @@ -17,7 +17,7 @@ Feature: osrm-prepare command line options: help And stdout should contain "--profile" And stdout should contain "--threads" And stdout should contain 15 lines - And it should exit with code 0 + And it should exit with code 1 Scenario: osrm-prepare - Help, short When I run "osrm-prepare -h" diff --git a/features/step_definitions/data.rb b/features/step_definitions/data.rb index f1e58bae2..524fd322c 100644 --- a/features/step_definitions/data.rb +++ b/features/step_definitions/data.rb @@ -174,3 +174,7 @@ end Given /^data is loaded with datastore$/ do @load_method = 'datastore' end + +Given /^the HTTP method "([^"]*)"$/ do |method| + @http_method = method +end diff --git a/features/step_definitions/distance_matrix.rb b/features/step_definitions/distance_matrix.rb index f85bdcc07..8c4e927de 100644 --- a/features/step_definitions/distance_matrix.rb +++ b/features/step_definitions/distance_matrix.rb @@ -52,5 +52,5 @@ When /^I request a travel time matrix I should get$/ do |table| actual << r end end - table.routing_diff! actual + table.diff! actual end diff --git a/features/step_definitions/locate.rb b/features/step_definitions/locate.rb index 3e67be3c9..2fd8e627b 100644 --- a/features/step_definitions/locate.rb +++ b/features/step_definitions/locate.rb @@ -9,7 +9,7 @@ When /^I request locate I should get$/ do |table| out_node = find_node_by_name row['out'] raise "*** unknown out-node '#{row['out']}" unless out_node - response = request_locate("#{in_node.lat},#{in_node.lon}") + response = request_locate(in_node) if response.code == "200" && response.body.empty? == false json = JSON.parse response.body if json['status'] == 0 @@ -39,7 +39,7 @@ When /^I request locate I should get$/ do |table| actual << got end end - table.routing_diff! actual + table.diff! actual end When /^I request locate (\d+) times I should get$/ do |n,table| diff --git a/features/step_definitions/matching.rb b/features/step_definitions/matching.rb index 3f3181f64..e9b723460 100644 --- a/features/step_definitions/matching.rb +++ b/features/step_definitions/matching.rb @@ -91,6 +91,6 @@ When /^I match I should get$/ do |table| actual << got end end - table.routing_diff! actual + table.diff! actual end diff --git a/features/step_definitions/nearest.rb b/features/step_definitions/nearest.rb index 532c1f0d7..481c74167 100644 --- a/features/step_definitions/nearest.rb +++ b/features/step_definitions/nearest.rb @@ -9,7 +9,7 @@ When /^I request nearest I should get$/ do |table| out_node = find_node_by_name row['out'] raise "*** unknown out-node '#{row['out']}" unless out_node - response = request_nearest("#{in_node.lat},#{in_node.lon}") + response = request_nearest(in_node) if response.code == "200" && response.body.empty? == false json = JSON.parse response.body if json['status'] == 0 @@ -39,7 +39,7 @@ When /^I request nearest I should get$/ do |table| actual << got end end - table.routing_diff! actual + table.diff! actual end When /^I request nearest (\d+) times I should get$/ do |n,table| diff --git a/features/step_definitions/routability.rb b/features/step_definitions/routability.rb index dcbde5afa..a9241bef7 100644 --- a/features/step_definitions/routability.rb +++ b/features/step_definitions/routability.rb @@ -74,5 +74,5 @@ Then /^routability should be$/ do |table| actual << output_row end end - table.routing_diff! actual + table.diff! actual end diff --git a/features/step_definitions/routing.rb b/features/step_definitions/routing.rb index c25f5b7c8..597ea65d6 100644 --- a/features/step_definitions/routing.rb +++ b/features/step_definitions/routing.rb @@ -140,7 +140,7 @@ When /^I route I should get$/ do |table| actual << got end end - table.routing_diff! actual + table.diff! actual end When /^I route (\d+) times I should get$/ do |n,table| diff --git a/features/support/cucumber.rb b/features/support/cucumber.rb deleted file mode 100644 index a43082a7a..000000000 --- a/features/support/cucumber.rb +++ /dev/null @@ -1,76 +0,0 @@ -#monkey patch cucumber table class to reorder output. -#we always want failed rows to be shown right below the expected row. - -class Cucumber::Ast::Table - def routing_diff!(other_table, options={}) - options = {:missing_row => true, :surplus_row => true, :missing_col => true, :surplus_col => false}.merge(options) - - other_table = ensure_table(other_table) - other_table.convert_headers! - other_table.convert_columns! - ensure_green! - - convert_headers! - convert_columns! - - original_width = cell_matrix[0].length - other_table_cell_matrix = pad!(other_table.cell_matrix) - padded_width = cell_matrix[0].length - - missing_col = cell_matrix[0].detect{|cell| cell.status == :undefined} - surplus_col = padded_width > original_width - - require_diff_lcs - cell_matrix.extend(Diff::LCS) - changes = cell_matrix.diff(other_table_cell_matrix).flatten - - inserted = 0 - missing = 0 - - row_indices = Array.new(other_table_cell_matrix.length) {|n| n} - - last_change = nil - missing_row_pos = nil - insert_row_pos = nil - - changes.each do |change| - if(change.action == '-') - missing_row_pos = change.position + inserted - cell_matrix[missing_row_pos].each{|cell| cell.status = :undefined} - row_indices.insert(missing_row_pos, nil) - missing += 1 - else # '+' - #change index so we interleave instead - insert_row_pos = change.position + inserted + 1 - #insert_row_pos = change.position + missing #original - - inserted_row = change.element - inserted_row.each{|cell| cell.status = :comment} - cell_matrix.insert(insert_row_pos, inserted_row) - row_indices[insert_row_pos] = nil - inspect_rows(cell_matrix[missing_row_pos], inserted_row) if last_change && last_change.action == '-' - inserted += 1 - end - last_change = change - end - - other_table_cell_matrix.each_with_index do |other_row, i| - row_index = row_indices.index(i) - row = cell_matrix[row_index] if row_index - if row - (original_width..padded_width).each do |col_index| - surplus_cell = other_row[col_index] - row[col_index].value = surplus_cell.value if row[col_index] - end - end - end - - clear_cache! - should_raise = - missing_row_pos && options[:missing_row] || - insert_row_pos && options[:surplus_row] || - missing_col && options[:missing_col] || - surplus_col && options[:surplus_col] - raise Different.new(self) if should_raise - end -end \ No newline at end of file diff --git a/features/support/env.rb b/features/support/env.rb index f9a4fe436..ea74959ed 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -22,10 +22,10 @@ SHUTDOWN_TIMEOUT = 10 DEFAULT_LOAD_METHOD = 'datastore' OSRM_ROUTED_LOG_FILE = 'osrm-routed.log' -if ENV['OS']==/Windows.*/ then - TERMSIGNAL='TERM' -else +if ENV['OS']=~/Windows.*/ then TERMSIGNAL=9 +else + TERMSIGNAL='TERM' end @@ -72,8 +72,8 @@ end def verify_existance_of_binaries ["osrm-extract", "osrm-prepare", "osrm-routed"].each do |bin| - unless File.exists? "#{BIN_PATH}/#{bin}" - raise "*** #{BIN_PATH}/#{bin} is missing. Build failed?" + unless File.exists? "#{BIN_PATH}/#{bin}#{EXE}" + raise "*** #{BIN_PATH}/#{bin}#{EXE} is missing. Build failed?" end end end diff --git a/features/support/hooks.rb b/features/support/hooks.rb index 337014246..2ad49dcae 100644 --- a/features/support/hooks.rb +++ b/features/support/hooks.rb @@ -4,22 +4,16 @@ STRESS_TIMEOUT = 300 Before do |scenario| - # feature name + # fetch scenario and feature name, so we can use it in log files if needed case scenario - when Cucumber::Ast::Scenario + when Cucumber::RunningTestCase::Scenario @feature_name = scenario.feature.name - when Cucumber::Ast::OutlineTable::ExampleRow - @feature_name = scenario.scenario_outline.feature.name - end - - # scenario name - case scenario - when Cucumber::Ast::Scenario @scenario_title = scenario.name - when Cucumber::Ast::OutlineTable::ExampleRow + when Cucumber::RunningTestCase::ExampleRow + @feature_name = scenario.scenario_outline.feature.name @scenario_title = scenario.scenario_outline.name end - + @load_method = DEFAULT_LOAD_METHOD @query_params = {} @scenario_time = Time.now.strftime("%Y-%m-%dT%H:%m:%SZ") diff --git a/features/support/http.rb b/features/support/http.rb new file mode 100644 index 000000000..80dad8a85 --- /dev/null +++ b/features/support/http.rb @@ -0,0 +1,34 @@ +require 'net/http' + +def generate_request_url path + if @http_method.eql? "POST" + pos = path.index('?') - 1 + service = path[0..pos] + uri = URI.parse "#{HOST}/#{service}" + else + uri = URI.parse "#{HOST}/#{path}" + end +end + +def send_request uri, waypoints=[], options={}, timestamps=[] + @query = uri.to_s + Timeout.timeout(OSRM_TIMEOUT) do + if @http_method.eql? "POST" + datas = {} + if waypoints.length > 0 + datas[:loc] = waypoints.compact.map { |w| "#{w.lat},#{w.lon}" } + end + if timestamps.length > 0 + datas[:t] = timestamps.compact.map { |t| "#{t}" } + end + datas.merge! options + response = Net::HTTP.post_form uri, datas + else + response = Net::HTTP.get_response uri + end + end +rescue Errno::ECONNREFUSED => e + raise "*** osrm-routed is not running." +rescue Timeout::Error + raise "*** osrm-routed did not respond." +end diff --git a/features/support/locate.rb b/features/support/locate.rb index 900724703..a62e53aa9 100644 --- a/features/support/locate.rb +++ b/features/support/locate.rb @@ -1,17 +1,12 @@ require 'net/http' -def request_locate_url path +def request_locate_url path, node @query = path - uri = URI.parse "#{HOST}/#{path}" - Timeout.timeout(OSRM_TIMEOUT) do - Net::HTTP.get_response uri - end -rescue Errno::ECONNREFUSED => e - raise "*** osrm-routed is not running." -rescue Timeout::Error - raise "*** osrm-routed did not respond." + + uri = generate_request_url path + response = send_request uri, [node] end -def request_locate a - request_locate_url "locate?loc=#{a}" +def request_locate node + request_locate_url "locate?loc=#{node.lat},#{node.lon}", node end diff --git a/features/support/match.rb b/features/support/match.rb index bf51189a4..8bcf7a2c6 100644 --- a/features/support/match.rb +++ b/features/support/match.rb @@ -13,14 +13,8 @@ def request_matching trace=[], timestamps=[], options={} end params = (trace_params + defaults.merge(options).to_param).join('&') params = nil if params=="" - uri = URI.parse ["#{HOST}/match", params].compact.join('?') - @query = uri.to_s - Timeout.timeout(OSRM_TIMEOUT) do - Net::HTTP.get_response uri - end -rescue Errno::ECONNREFUSED => e - raise "*** osrm-routed is not running." -rescue Timeout::Error - raise "*** osrm-routed did not respond." + + uri = generate_request_url ("match" + '?' + params) + response = send_request uri, trace, options, timestamps end diff --git a/features/support/nearest.rb b/features/support/nearest.rb index 77fc351a9..af52b49e0 100644 --- a/features/support/nearest.rb +++ b/features/support/nearest.rb @@ -1,17 +1,12 @@ require 'net/http' -def request_nearest_url path +def request_nearest_url path, node @query = path - uri = URI.parse "#{HOST}/#{path}" - Timeout.timeout(OSRM_TIMEOUT) do - Net::HTTP.get_response uri - end -rescue Errno::ECONNREFUSED => e - raise "*** osrm-routed is not running." -rescue Timeout::Error - raise "*** osrm-routed did not respond." + + uri = generate_request_url path + response = send_request uri, [node] end -def request_nearest a - request_nearest_url "nearest?loc=#{a}" +def request_nearest node + request_nearest_url "nearest?loc=#{node.lat},#{node.lon}", node end diff --git a/features/support/route.rb b/features/support/route.rb index a8c78227e..ca353d6e6 100644 --- a/features/support/route.rb +++ b/features/support/route.rb @@ -15,15 +15,13 @@ def request_path path, waypoints=[], options={} locs = waypoints.compact.map { |w| "loc=#{w.lat},#{w.lon}" } params = (locs + options.to_param).join('&') params = nil if params=="" - uri = URI.parse ["#{HOST}/#{path}", params].compact.join('?') - @query = uri.to_s - Timeout.timeout(OSRM_TIMEOUT) do - Net::HTTP.get_response uri + + if params == nil + uri = generate_request_url (path) + else + uri = generate_request_url (path + '?' + params) end -rescue Errno::ECONNREFUSED => e - raise "*** osrm-routed is not running." -rescue Timeout::Error - raise "*** osrm-routed did not respond." + response = send_request uri, waypoints, options end def request_url path diff --git a/features/testbot/ferry.feature b/features/testbot/ferry.feature index 55160614b..21cefcb6f 100644 --- a/features/testbot/ferry.feature +++ b/features/testbot/ferry.feature @@ -173,3 +173,33 @@ Feature: Testbot - Handle ferry routes | from | to | route | time | | a | g | abcdefg | 23400s +-2 | | g | a | abcdefg | 23400s +-2 | + + @todo + Scenario: Testbot - Ferry duration formats + Given the node map + | a | c | e | g | i | k | m | o | q | s | + | b | d | f | h | j | l | n | p | r | t | + + And the ways + | nodes | route | duration | + | ab | ferry | 0:01 | + | cd | ferry | 00:01 | + | ef | ferry | 1:00 | + | gh | ferry | 01:00 | + | ij | ferry | 02:20 | + | kl | ferry | 10:00 | + | mn | ferry | 100:00 | + | op | ferry | 1000:00 | + | qr | ferry | 10000:00 | + + When I route I should get + | from | to | route | time | + | a | b | ab | 60s +-1 | + | c | d | cd | 60s +-1 | + | e | f | ef | 3600s +-1 | + | g | h | gh | 3600s +-1 | + | i | j | ij | 8400s +-1 | + | k | l | kl | 36000s +-1 | + | m | n | mn | 360000s +-1 | + | o | p | mn | 3600000s +-1 | + | q | r | mn | 36000000s +-1 | diff --git a/features/testbot/loop.feature b/features/testbot/loop.feature index 72cad33c0..c69e7a251 100644 --- a/features/testbot/loop.feature +++ b/features/testbot/loop.feature @@ -4,6 +4,25 @@ Feature: Avoid weird loops caused by rounding errors Background: Given the profile "testbot" + Scenario: Weired sidestreet loops + Given the node map + | a | 1 | b | 2 | c | 3 | d | + | | | | | | | | + | e | | f | | g | | h | + + And the ways + | nodes | + | aefghd | + | abcd | + | bf | + | cg | + + When I route I should get + | waypoints | route | turns | + | a,1,d | abcd,abcd | head,via,destination | + | a,2,d | abcd,abcd | head,via,destination | + | a,3,d | abcd,abcd | head,via,destination | + Scenario: Avoid weird loops 1 Given the node locations | node | lat | lon | diff --git a/features/testbot/post.feature b/features/testbot/post.feature new file mode 100644 index 000000000..ff2282406 --- /dev/null +++ b/features/testbot/post.feature @@ -0,0 +1,100 @@ +@post @testbot +Feature: POST request + + Background: + Given the profile "testbot" + And the HTTP method "POST" + + Scenario: Testbot - viaroute POST request + Given the node locations + | node | lat | lon | + | a | 55.68740 | 12.52430 | + | b | 55.68745 | 12.52409 | + | c | 55.68711 | 12.52383 | + | x | -55.68740 | 12.52430 | + | y | -55.68745 | 12.52409 | + | z | -55.68711 | 12.52383 | + + And the ways + | nodes | + | ab | + | bc | + | xy | + | yz | + + When I route I should get + | from | to | route | turns | + | a | c | ab,bc | head,left,destination | + | c | a | bc,ab | head,right,destination | + | x | z | xy,yz | head,right,destination | + | z | x | yz,xy | head,left,destination | + + Scenario: Testbot - match POST request + Given the node map + | a | b | c | d | + | e | f | g | h | + + And the ways + | nodes | oneway | + | abcd | yes | + | hgfe | yes | + + When I match I should get + | trace | matchings | + | dcba | hgfe | + + Scenario: Testbot - table POST request + 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 | 300 | 400 | 300 | + | y | 500 | 0 | 300 | 200 | + | d | 200 | 300 | 0 | 300 | + | e | 300 | 400 | 100 | 0 | + + Scenario: Testbot - locate POST request + Given the node locations + | node | lat | lon | + | a | -85 | -180 | + | b | 0 | 0 | + | c | 85 | 180 | + | x | -84 | -180 | + | y | 84 | 180 | + + And the ways + | nodes | + | abc | + + When I request locate I should get + | in | out | + | x | a | + | y | c | + + Scenario: Testbot - nearest POST request + Given the node locations + | node | lat | lon | + | a | -85 | -180 | + | b | -85 | -160 | + | c | -85 | -140 | + | x | -84.999 | -180 | + | y | -84.999 | -160 | + | z | -84.999 | -140 | + + And the ways + | nodes | + | abc | + + When I request nearest I should get + | in | out | + | x | a | + | y | b | + | z | c | diff --git a/features/testbot/snap.feature b/features/testbot/snap.feature index bee75bc44..01c590be5 100644 --- a/features/testbot/snap.feature +++ b/features/testbot/snap.feature @@ -98,30 +98,26 @@ Feature: Snap start/end point to the nearest way | b | x | xb | | c | x | xc | - Scenario: Find edges within 1km, and the same from 10km - Given a grid size of 1000 meters + Scenario: Find edges within 100m, and the same from 1km + Given a grid size of 100 meters Given the node map - | p | | | | | | | | | | | i | | | | | | | | | | | j | - | | | | | | | | | | | | | | | | | | | | | | | | - | | | | | | | | | | | | | | | | | | | | | | | | - | | | | | | | | | | | | | | | | | | | | | | | | - | | | | | | | | | | | | | | | | | | | | | | | | - | | | | | | | | | | | | | | | | | | | | | | | | - | | | | | | | | | | | | | | | | | | | | | | | | - | | | | | | | | | | 8 | | 1 | | 2 | | | | | | | | | | - | | | | | | | | | | | h | a | b | | | | | | | | | | | - | o | | | | | | | | | 7 | g | x | c | 3 | | | | | | | | | k | - | | | | | | | | | | | f | e | d | | | | | | | | | | | - | | | | | | | | | | 6 | | 5 | | 4 | | | | | | | | | | - | | | | | | | | | | | | | | | | | | | | | | | | - | | | | | | | | | | | | | | | | | | | | | | | | - | | | | | | | | | | | | | | | | | | | | | | | | - | | | | | | | | | | | | | | | | | | | | | | | | - | | | | | | | | | | | | | | | | | | | | | | | | - | | | | | | | | | | | | | | | | | | | | | | | | - | | | | | | | | | | | | | | | | | | | | | | | | - | | | | | | | | | | | | | | | | | | | | | | | | - | n | | | | | | | | | | | m | | | | | | | | | | | l | + | p | | | | | | | | i | | | | | | | | j | + | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | + | | | | | | | 8 | | 1 | | 2 | | | | | | | + | | | | | | | | h | a | b | | | | | | | | + | o | | | | | | 7 | g | x | c | 3 | | | | | | k | + | | | | | | | | f | e | d | | | | | | | | + | | | | | | | 6 | | 5 | | 4 | | | | | | | + | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | + | n | | | | | | | | m | | | | | | | | l | Given the ways | nodes | diff --git a/features/testbot/via.feature b/features/testbot/via.feature index 9eb8f556e..a70ee9f7f 100644 --- a/features/testbot/via.feature +++ b/features/testbot/via.feature @@ -52,6 +52,21 @@ Feature: Via points | a,c,f | ab,bcd,bcd,de,efg | | a,c,f,h | ab,bcd,bcd,de,efg,efg,gh | + Scenario: Duplicate via point + Given the node map + | x | | | | | | + | a | 1 | 2 | 3 | 4 | b | + | | | | | | | + + And the ways + | nodes | + | xa | + | ab | + + When I route I should get + | waypoints | route | turns | + | 1,1,4 | ab,ab | head,via,destination | + Scenario: Via points on ring of oneways # xa it to avoid only having a single ring, which cna trigger edge cases Given the node map @@ -77,7 +92,6 @@ Feature: Via points | 1,3,2 | ab,bc,cd,cd,de,ef,fa,ab,bc | 1600m +-1 | head,straight,straight,via,right,right,right,right,straight,destination | | 3,2,1 | cd,de,ef,fa,ab,bc,bc,cd,de,ef,fa,ab | 2400m +-1 | head,right,right,right,right,straight,via,straight,right,right,right,right,destination | - @bug Scenario: Via points on ring on the same oneway # xa it to avoid only having a single ring, which cna trigger edge cases Given the node map @@ -97,6 +111,6 @@ Feature: Via points | waypoints | route | distance | turns | | 1,3 | ab | 200m +-1 | head,destination | | 3,1 | ab,bc,cd,da,ab | 800m +-1 | head,right,right,right,right,destination | - | 1,2,3 | ab | 200m +-1 | head,destination | - | 1,3,2 | ab,bc,cd,da,ab | 1100m +-1 | head,right,right,right,right,destination | - | 3,2,1 | ab,bc,cd,da,ab,bc,cd,da,ab | 1600m +-1 | head,right,right,right,right,right,right,right,right,destination | + | 1,2,3 | ab,ab | 200m +-1 | head,via,destination | + | 1,3,2 | ab,ab,bc,cd,da,ab | 1100m +-1 | head,via,right,right,right,right,destination | + | 3,2,1 | ab,bc,cd,da,ab,ab,bc,cd,da,ab | 1800m | head,right,right,right,right,via,right,right,right,right,destination | diff --git a/include/osrm/route_parameters.hpp b/include/osrm/route_parameters.hpp index 9babbd763..bc413d73b 100644 --- a/include/osrm/route_parameters.hpp +++ b/include/osrm/route_parameters.hpp @@ -78,6 +78,8 @@ struct RouteParameters void setCompressionFlag(const bool flag); void addCoordinate(const boost::fusion::vector &received_coordinates); + + void getCoordinatesFromGeometry(const std::string geometry_string); short zoom_level; bool print_instructions; diff --git a/prepare.cpp b/prepare.cpp index af10df035..5de320463 100644 --- a/prepare.cpp +++ b/prepare.cpp @@ -26,10 +26,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "contractor/processing_chain.hpp" +#include "contractor/contractor_options.hpp" #include "util/simple_logger.hpp" #include +#include + #include #include @@ -37,21 +40,64 @@ int main(int argc, char *argv[]) { try { - return Prepare().Process(argc, argv); - } - catch (boost::program_options::too_many_positional_options_error &) - { - SimpleLogger().Write(logWARNING) << "Only one file can be specified"; - return 1; - } - catch (boost::program_options::error &e) - { - SimpleLogger().Write(logWARNING) << e.what(); - return 1; + LogPolicy::GetInstance().Unmute(); + ContractorConfig contractor_config; + + const return_code result = ContractorOptions::ParseArguments(argc, argv, contractor_config); + + if (return_code::fail == result) + { + return 1; + } + + if (return_code::exit == result) + { + return 0; + } + + ContractorOptions::GenerateOutputFilesNames(contractor_config); + + if (1 > contractor_config.requested_num_threads) + { + SimpleLogger().Write(logWARNING) << "Number of threads must be 1 or larger"; + return 1; + } + + const unsigned recommended_num_threads = tbb::task_scheduler_init::default_num_threads(); + + if (recommended_num_threads != contractor_config.requested_num_threads) + { + SimpleLogger().Write(logWARNING) << "The recommended number of threads is " + << recommended_num_threads + << "! This setting may have performance side-effects."; + } + + if (!boost::filesystem::is_regular_file(contractor_config.osrm_input_path)) + { + SimpleLogger().Write(logWARNING) + << "Input file " << contractor_config.osrm_input_path.string() << " not found!"; + return 1; + } + + if (!boost::filesystem::is_regular_file(contractor_config.profile_path)) + { + SimpleLogger().Write(logWARNING) << "Profile " << contractor_config.profile_path.string() + << " not found!"; + return 1; + } + + SimpleLogger().Write() << "Input file: " << contractor_config.osrm_input_path.filename().string(); + SimpleLogger().Write() << "Restrictions file: " << contractor_config.restrictions_path.filename().string(); + SimpleLogger().Write() << "Profile: " << contractor_config.profile_path.filename().string(); + SimpleLogger().Write() << "Threads: " << contractor_config.requested_num_threads; + + tbb::task_scheduler_init init(contractor_config.requested_num_threads); + + return Prepare(contractor_config).Run(); } catch (const std::exception &e) { - SimpleLogger().Write(logWARNING) << "Exception occured: " << e.what() << std::endl; + SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); return 1; } } diff --git a/profiles/bicycle.lua b/profiles/bicycle.lua index 497321ccf..f23606390 100644 --- a/profiles/bicycle.lua +++ b/profiles/bicycle.lua @@ -1,19 +1,21 @@ -require("lib/access") -require("lib/maxspeed") +-- Bicycle profile + +local find_access_tag = require("lib/access").find_access_tag +local limit = require("lib/maxspeed").limit -- Begin of globals barrier_whitelist = { [""] = true, ["cycle_barrier"] = true, ["bollard"] = true, ["entrance"] = true, ["cattle_grid"] = true, ["border_control"] = true, ["toll_booth"] = true, ["sally_port"] = true, ["gate"] = true, ["no"] = true } access_tag_whitelist = { ["yes"] = true, ["permissive"] = true, ["designated"] = true } -access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestery"] = true } +access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true } access_tag_restricted = { ["destination"] = true, ["delivery"] = true } access_tags_hierachy = { "bicycle", "vehicle", "access" } cycleway_tags = {["track"]=true,["lane"]=true,["opposite"]=true,["opposite_lane"]=true,["opposite_track"]=true,["share_busway"]=true,["sharrow"]=true,["shared"]=true } service_tag_restricted = { ["parking_aisle"] = true } restriction_exception_tags = { "bicycle", "vehicle", "access" } +unsafe_highway_list = { ["primary"] = true, ["secondary"] = true, ["tertiary"] = true, ["primary_link"] = true, ["secondary_link"] = true, ["tertiary_link"] = true} -default_speed = 15 - -walking_speed = 6 +local default_speed = 15 +local walking_speed = 6 bicycle_speeds = { ["cycleway"] = default_speed, @@ -79,8 +81,7 @@ surface_speeds = { ["unpaved"] = 6, ["fine_gravel"] = 6, ["gravel"] = 6, - ["fine_gravel"] = 6, - ["pebbelstone"] = 6, + ["pebblestone"] = 6, ["ground"] = 6, ["dirt"] = 6, ["earth"] = 6, @@ -89,24 +90,27 @@ surface_speeds = { ["sand"] = 3 } -take_minimum_of_speeds = true -obey_oneway = true -obey_bollards = false -use_restrictions = true -ignore_areas = true -- future feature -traffic_signal_penalty = 5 -u_turn_penalty = 20 -use_turn_restrictions = false -turn_penalty = 60 -turn_bias = 1.4 +traffic_signal_penalty = 2 +use_turn_restrictions = false +local obey_oneway = true +local obey_bollards = false +local ignore_areas = true +local u_turn_penalty = 20 +local turn_penalty = 60 +local turn_bias = 1.4 +-- reduce the driving speed by 30% for unsafe roads +-- local safety_penalty = 0.7 +local safety_penalty = 1.0 +local use_public_transport = true +local fallback_names = true --modes -mode_normal = 1 -mode_pushing = 2 -mode_ferry = 3 -mode_train = 4 -mode_movable_bridge = 5 +local mode_normal = 1 +local mode_pushing = 2 +local mode_ferry = 3 +local mode_train = 4 +local mode_movable_bridge = 5 local function parse_maxspeed(source) if not source then @@ -122,7 +126,6 @@ local function parse_maxspeed(source) return n end - function get_exceptions(vector) for i,v in ipairs(restriction_exception_tags) do vector:Add(v) @@ -130,29 +133,31 @@ function get_exceptions(vector) end function node_function (node, result) - local barrier = node:get_value_by_key("barrier") - local access = Access.find_access_tag(node, access_tags_hierachy) - local traffic_signal = node:get_value_by_key("highway") + -- parse access and barrier tags + local highway = node:get_value_by_key("highway") + local is_crossing = highway and highway == "crossing" - -- flag node if it carries a traffic light - if traffic_signal and traffic_signal == "traffic_signals" then - result.traffic_lights = true - end + local access = find_access_tag(node, access_tags_hierachy) + if access and access ~= "" then + -- access restrictions on crossing nodes are not relevant for + -- the traffic on the road + if access_tag_blacklist[access] and not is_crossing then + result.barrier = true + end + else + local barrier = node:get_value_by_key("barrier") + if barrier and "" ~= barrier then + if not barrier_whitelist[barrier] then + result.barrier = true + end + end + end - -- parse access and barrier tags - if access and access ~= "" then - if access_tag_blacklist[access] then - result.barrier = true - else - result.barrier = false - end - elseif barrier and barrier ~= "" then - if barrier_whitelist[barrier] then - result.barrier = false - else - result.barrier = true - end - end + -- check if node is a traffic light + local tag = node:get_value_by_key("highway") + if tag and "traffic_signals" == tag then + result.traffic_lights = true; + end end function way_function (way, result) @@ -165,8 +170,8 @@ function way_function (way, result) local public_transport = way:get_value_by_key("public_transport") local bridge = way:get_value_by_key("bridge") if (not highway or highway == '') and - (not route or route == '') and - (not railway or railway=='') and + (not use_public_transport or not route or route == '') and + (not 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 @@ -181,7 +186,7 @@ function way_function (way, result) end -- access - local access = Access.find_access_tag(way, access_tags_hierachy) + local access = find_access_tag(way, access_tags_hierachy) if access and access_tag_blacklist[access] then return end @@ -213,7 +218,8 @@ function way_function (way, result) result.name = ref elseif name and "" ~= name then result.name = name - elseif highway then + -- TODO find a better solution for encoding way type + elseif fallback_names and highway then -- if no name exists, use way type -- this encoding scheme is excepted to be a temporary solution result.name = "{highway:"..highway.."}" @@ -246,15 +252,16 @@ function way_function (way, result) result.forward_speed = route_speeds[route] result.backward_speed = route_speeds[route] end - elseif railway and platform_speeds[railway] then + -- public transport + elseif use_public_transport and railway and platform_speeds[railway] then -- railway platforms (old tagging scheme) result.forward_speed = platform_speeds[railway] result.backward_speed = platform_speeds[railway] - elseif platform_speeds[public_transport] then + elseif use_public_transport and platform_speeds[public_transport] then -- public_transport platforms (new tagging platform) result.forward_speed = platform_speeds[public_transport] result.backward_speed = platform_speeds[public_transport] - elseif railway and railway_speeds[railway] then + elseif use_public_transport and railway and railway_speeds[railway] then result.forward_mode = mode_train result.backward_mode = mode_train -- railways @@ -270,6 +277,10 @@ function way_function (way, result) -- regular ways result.forward_speed = bicycle_speeds[highway] result.backward_speed = bicycle_speeds[highway] + if safety_penalty < 1 and unsafe_highway_list[highway] then + result.forward_speed = result.forward_speed * safety_penalty + result.backward_speed = result.backward_speed * safety_penalty + end elseif access and access_tag_whitelist[access] then -- unknown way, but valid access tag result.forward_speed = default_speed @@ -391,7 +402,7 @@ function way_function (way, result) end -- maxspeed - MaxSpeed.limit( result, maxspeed, maxspeed_forward, maxspeed_backward ) + limit( result, maxspeed, maxspeed_forward, maxspeed_backward ) end function turn_function (angle) diff --git a/profiles/car.lua b/profiles/car.lua index 813afd19c..8c68bc994 100644 --- a/profiles/car.lua +++ b/profiles/car.lua @@ -1,6 +1,8 @@ --- Begin of globals ---require("lib/access") --function temporarily inlined +-- Car profile +local find_access_tag = require("lib/access").find_access_tag + +-- Begin of globals barrier_whitelist = { ["cattle_grid"] = true, ["border_control"] = true, ["checkpoint"] = true, ["toll_booth"] = true, ["sally_port"] = true, ["gate"] = true, ["lift_gate"] = true, ["no"] = true, ["entrance"] = true } access_tag_whitelist = { ["yes"] = true, ["motorcar"] = true, ["motor_vehicle"] = true, ["vehicle"] = true, ["permissive"] = true, ["designated"] = true } access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true, ["emergency"] = true, ["psv"] = true } @@ -130,10 +132,9 @@ maxspeed_table = { traffic_signal_penalty = 2 use_turn_restrictions = true -local take_minimum_of_speeds = false local obey_oneway = true local obey_bollards = true -local ignore_areas = true -- future feature +local ignore_areas = true local u_turn_penalty = 20 local abs = math.abs @@ -147,16 +148,6 @@ local mode_normal = 1 local mode_ferry = 2 local mode_movable_bridge = 3 -local function find_access_tag(source, access_tags_hierachy) - for i,v in ipairs(access_tags_hierachy) do - local access_tag = source:get_value_by_key(v) - if access_tag and "" ~= access_tag then - return access_tag - end - end - return "" -end - function get_exceptions(vector) for i,v in ipairs(restriction_exception_tags) do vector:Add(v) @@ -187,6 +178,7 @@ local function parse_maxspeed(source) return n end +-- FIXME Why was this commented out? -- function turn_function (angle) -- -- print ("called at angle " .. angle ) -- local index = math.abs(math.floor(angle/10+0.5))+1 -- +1 'coz LUA starts as idx 1 @@ -198,7 +190,7 @@ end function node_function (node, result) -- parse access and barrier tags local access = find_access_tag(node, access_tags_hierachy) - if access ~= "" then + if access and access ~= "" then if access_tag_blacklist[access] then result.barrier = true end @@ -249,12 +241,6 @@ function way_function (way, result) return end - local width = math.huge - local width_string = way:get_value_by_key("width") - if width_string and tonumber(width_string:match("%d*")) then - width = tonumber(width_string:match("%d*")) - end - -- Check if we are allowed to access the way local access = find_access_tag(way, access_tags_hierachy) if access_tag_blacklist[access] then @@ -353,9 +339,14 @@ function way_function (way, result) local service = way:get_value_by_key("service") -- Set the name that will be used for instructions - if ref and "" ~= ref then + local has_ref = ref and "" ~= ref + local has_name = name and "" ~= name + + if has_name and has_ref then + result.name = name .. " (" .. ref .. ")" + elseif has_ref then result.name = ref - elseif name and "" ~= name then + elseif has_name then result.name = name -- else -- result.name = highway -- if no name exists, use way type @@ -407,12 +398,25 @@ function way_function (way, result) result.ignore_in_grid = true end + local width = math.huge + local lanes = math.huge + if result.forward_speed > 0 or result.backward_speed > 0 then + local width_string = way:get_value_by_key("width") + if width_string and tonumber(width_string:match("%d*")) then + width = tonumber(width_string:match("%d*")) + end + + local lanes_string = way:get_value_by_key("lanes") + if lanes_string and tonumber(lanes_string:match("%d*")) then + lanes = tonumber(lanes_string:match("%d*")) + end + end -- scale speeds to get better avg driving times if result.forward_speed > 0 then local scaled_speed = result.forward_speed*speed_reduction + 11; local penalized_speed = math.huge - if width <= 3 then + if width <= 3 or lanes <= 1 then penalized_speed = result.forward_speed / 2; end result.forward_speed = math.min(penalized_speed, scaled_speed) @@ -421,16 +425,10 @@ function way_function (way, result) if result.backward_speed > 0 then local scaled_speed = result.backward_speed*speed_reduction + 11; local penalized_speed = math.huge - if width <= 3 then + if width <= 3 or lanes <= 1 then penalized_speed = result.backward_speed / 2; end result.backward_speed = math.min(penalized_speed, scaled_speed) end end --- These are wrappers to parse vectors of nodes and ways and thus to speed up any tracing JIT -function node_vector_function(vector) - for v in vector.nodes do - node_function(v) - end -end diff --git a/profiles/foot.lua b/profiles/foot.lua index 5a370971a..4b3bc1cca 100644 --- a/profiles/foot.lua +++ b/profiles/foot.lua @@ -1,10 +1,11 @@ -- Foot profile -require("lib/access") +local find_access_tag = require("lib/access").find_access_tag +-- Begin of globals barrier_whitelist = { [""] = true, ["cycle_barrier"] = true, ["bollard"] = true, ["entrance"] = true, ["cattle_grid"] = true, ["border_control"] = true, ["toll_booth"] = true, ["sally_port"] = true, ["gate"] = true, ["no"] = true} access_tag_whitelist = { ["yes"] = true, ["foot"] = true, ["permissive"] = true, ["designated"] = true } -access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestery"] = true } +access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true } access_tag_restricted = { ["destination"] = true, ["delivery"] = true } access_tags_hierachy = { "foot", "access" } service_tag_restricted = { ["parking_aisle"] = true } @@ -35,32 +36,36 @@ speeds = { } route_speeds = { - ["ferry"] = 5 + ["ferry"] = 5 } platform_speeds = { - ["platform"] = walking_speed + ["platform"] = walking_speed } amenity_speeds = { - ["parking"] = walking_speed, - ["parking_entrance"] = walking_speed + ["parking"] = walking_speed, + ["parking_entrance"] = walking_speed } man_made_speeds = { - ["pier"] = walking_speed + ["pier"] = walking_speed } surface_speeds = { - ["fine_gravel"] = walking_speed*0.75, - ["gravel"] = walking_speed*0.75, - ["pebbelstone"] = walking_speed*0.75, - ["mud"] = walking_speed*0.5, - ["sand"] = walking_speed*0.5 + ["fine_gravel"] = walking_speed*0.75, + ["gravel"] = walking_speed*0.75, + ["pebblestone"] = walking_speed*0.75, + ["mud"] = walking_speed*0.5, + ["sand"] = walking_speed*0.5 } -traffic_signal_penalty = 2 -u_turn_penalty = 2 +leisure_speeds = { + ["track"] = walking_speed +} + +traffic_signal_penalty = 2 +u_turn_penalty = 2 use_turn_restrictions = false --modes @@ -68,144 +73,150 @@ local mode_normal = 1 local mode_ferry = 2 function get_exceptions(vector) - for i,v in ipairs(restriction_exception_tags) do - vector:Add(v) - end + for i,v in ipairs(restriction_exception_tags) do + vector:Add(v) + end end function node_function (node, result) - local barrier = node:get_value_by_key("barrier") - local access = Access.find_access_tag(node, access_tags_hierachy) - local traffic_signal = node:get_value_by_key("highway") + local barrier = node:get_value_by_key("barrier") + local access = find_access_tag(node, access_tags_hierachy) + local traffic_signal = node:get_value_by_key("highway") - -- flag node if it carries a traffic light - if traffic_signal and traffic_signal == "traffic_signals" then - result.traffic_light = true - end + -- flag node if it carries a traffic light + if traffic_signal and traffic_signal == "traffic_signals" then + result.traffic_light = true + end - -- parse access and barrier tags - if access and access ~= "" then - if access_tag_blacklist[access] then - result.barrier = true - else - result.barrier = false - end - elseif barrier and barrier ~= "" then - if barrier_whitelist[barrier] then - result.barrier = false - else - result.barrier = true - end - end + -- parse access and barrier tags + if access and access ~= "" then + if access_tag_blacklist[access] then + result.barrier = true + else + result.barrier = false + end + elseif barrier and barrier ~= "" then + if barrier_whitelist[barrier] then + result.barrier = false + else + result.barrier = true + end + end - return 1 + return 1 end function way_function (way, result) - -- initial routability check, filters out buildings, boundaries, etc - local highway = way:get_value_by_key("highway") - 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") - if (not highway or highway == '') and - (not route or route == '') and - (not railway or railway=='') and - (not amenity or amenity=='') and - (not man_made or man_made=='') and - (not public_transport or public_transport=='') - then - return - end + -- initial routability check, filters out buildings, boundaries, etc + local highway = way:get_value_by_key("highway") + local leisure = way:get_value_by_key("leisure") + 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") + if (not highway or highway == '') and + (not leisure or leisure == '') and + (not route or route == '') and + (not railway or railway=='') and + (not amenity or amenity=='') and + (not man_made or man_made=='') and + (not public_transport or public_transport=='') + then + return + end - -- don't route on ways that are still under construction - if highway=='construction' then - return - end + -- don't route on ways that are still under construction + if highway=='construction' then + return + end - -- access - local access = Access.find_access_tag(way, access_tags_hierachy) - if access_tag_blacklist[access] then - return - end + -- access + local access = find_access_tag(way, access_tags_hierachy) + if access_tag_blacklist[access] then + return + end - local name = way:get_value_by_key("name") - local ref = way:get_value_by_key("ref") - local junction = way:get_value_by_key("junction") - local onewayClass = way:get_value_by_key("oneway:foot") - local duration = way:get_value_by_key("duration") - local service = way:get_value_by_key("service") - local area = way:get_value_by_key("area") - local foot = way:get_value_by_key("foot") - local surface = way:get_value_by_key("surface") + local name = way:get_value_by_key("name") + local ref = way:get_value_by_key("ref") + local junction = way:get_value_by_key("junction") + local onewayClass = way:get_value_by_key("oneway:foot") + local duration = way:get_value_by_key("duration") + local service = way:get_value_by_key("service") + local area = way:get_value_by_key("area") + local foot = way:get_value_by_key("foot") + local surface = way:get_value_by_key("surface") - -- name - if ref and "" ~= ref and name and "" ~= name then - result.name = name .. ' / ' .. ref + -- name + if ref and "" ~= ref and name and "" ~= name then + result.name = name .. ' / ' .. ref elseif ref and "" ~= ref then - result.name = ref - elseif name and "" ~= name then - result.name = name - elseif highway then - result.name = "{highway:"..highway.."}" -- if no name exists, use way type - -- this encoding scheme is excepted to be a temporary solution - end + result.name = ref + elseif name and "" ~= name then + result.name = name + elseif highway then + result.name = "{highway:"..highway.."}" -- if no name exists, use way type + -- this encoding scheme is excepted to be a temporary solution + end -- roundabouts - if "roundabout" == junction then - result.roundabout = true; - end + if "roundabout" == junction then + result.roundabout = true; + end -- speed - if route_speeds[route] then - -- ferries (doesn't cover routes tagged using relations) - result.ignore_in_grid = true - if duration and durationIsValid(duration) then - result.duration = math.max( 1, parseDuration(duration) ) - else - result.forward_speed = route_speeds[route] - result.backward_speed = route_speeds[route] - end - result.forward_mode = mode_ferry - result.backward_mode = mode_ferry - elseif railway and platform_speeds[railway] then - -- railway platforms (old tagging scheme) - result.forward_speed = platform_speeds[railway] - result.backward_speed = platform_speeds[railway] - elseif platform_speeds[public_transport] then - -- public_transport platforms (new tagging platform) - result.forward_speed = platform_speeds[public_transport] - result.backward_speed = platform_speeds[public_transport] - elseif amenity and amenity_speeds[amenity] then - -- parking areas - result.forward_speed = amenity_speeds[amenity] - result.backward_speed = amenity_speeds[amenity] - elseif speeds[highway] then - -- regular ways - result.forward_speed = speeds[highway] - result.backward_speed = speeds[highway] - elseif access and access_tag_whitelist[access] then - -- unknown way, but valid access tag - result.forward_speed = walking_speed - result.backward_speed = walking_speed - end + if route_speeds[route] then + -- ferries (doesn't cover routes tagged using relations) + result.ignore_in_grid = true + if duration and durationIsValid(duration) then + result.duration = math.max( 1, parseDuration(duration) ) + else + result.forward_speed = route_speeds[route] + result.backward_speed = route_speeds[route] + end + result.forward_mode = mode_ferry + result.backward_mode = mode_ferry + elseif railway and platform_speeds[railway] then + -- railway platforms (old tagging scheme) + result.forward_speed = platform_speeds[railway] + result.backward_speed = platform_speeds[railway] + elseif platform_speeds[public_transport] then + -- public_transport platforms (new tagging platform) + result.forward_speed = platform_speeds[public_transport] + result.backward_speed = platform_speeds[public_transport] + elseif amenity and amenity_speeds[amenity] then + -- parking areas + result.forward_speed = amenity_speeds[amenity] + result.backward_speed = amenity_speeds[amenity] + elseif leisure and leisure_speeds[leisure] then + -- running tracks + result.forward_speed = leisure_speeds[leisure] + result.backward_speed = leisure_speeds[leisure] + elseif speeds[highway] then + -- regular ways + result.forward_speed = speeds[highway] + result.backward_speed = speeds[highway] + elseif access and access_tag_whitelist[access] then + -- unknown way, but valid access tag + result.forward_speed = walking_speed + result.backward_speed = walking_speed + end - -- oneway - if onewayClass == "yes" or onewayClass == "1" or onewayClass == "true" then - result.backward_mode = 0 - elseif onewayClass == "no" or onewayClass == "0" or onewayClass == "false" then - -- nothing to do - elseif onewayClass == "-1" then - result.forward_mode = 0 - end + -- oneway + if onewayClass == "yes" or onewayClass == "1" or onewayClass == "true" then + result.backward_mode = 0 + elseif onewayClass == "no" or onewayClass == "0" or onewayClass == "false" then + -- nothing to do + elseif onewayClass == "-1" then + result.forward_mode = 0 + end - -- surfaces - if surface then - surface_speed = surface_speeds[surface] - if surface_speed then - result.forward_speed = math.min(result.forward_speed, surface_speed) - result.backward_speed = math.min(result.backward_speed, surface_speed) - end + -- surfaces + if surface then + surface_speed = surface_speeds[surface] + if surface_speed then + result.forward_speed = math.min(result.forward_speed, surface_speed) + result.backward_speed = math.min(result.backward_speed, surface_speed) end + end end diff --git a/profiles/lib/access.lua b/profiles/lib/access.lua index 76d2e2c89..3fd4ff605 100644 --- a/profiles/lib/access.lua +++ b/profiles/lib/access.lua @@ -1,13 +1,15 @@ local ipairs = ipairs -module "Access" +local Access = {} -function find_access_tag(source,access_tags_hierachy) +function Access.find_access_tag(source,access_tags_hierachy) for i,v in ipairs(access_tags_hierachy) do local tag = source:get_value_by_key(v) if tag and tag ~= '' then return tag end end - return nil + return "" end + +return Access diff --git a/profiles/lib/maxspeed.lua b/profiles/lib/maxspeed.lua index aca344a79..0dd9b822d 100644 --- a/profiles/lib/maxspeed.lua +++ b/profiles/lib/maxspeed.lua @@ -1,8 +1,8 @@ local math = math -module "MaxSpeed" +local MaxSpeed = {} -function limit(way,max,maxf,maxb) +function MaxSpeed.limit(way,max,maxf,maxb) if maxf and maxf>0 then way.forward_speed = math.min(way.forward_speed, maxf) elseif max and max>0 then @@ -15,3 +15,5 @@ function limit(way,max,maxf,maxb) way.backward_speed = math.min(way.backward_speed, max) end end + +return MaxSpeed diff --git a/routing_algorithms/alternative_path.hpp b/routing_algorithms/alternative_path.hpp index 1dcc02096..327bb329f 100644 --- a/routing_algorithms/alternative_path.hpp +++ b/routing_algorithms/alternative_path.hpp @@ -103,8 +103,8 @@ class AlternativeRouting final int upper_bound_to_shortest_path_distance = INVALID_EDGE_WEIGHT; NodeID middle_node = SPECIAL_NODEID; const EdgeWeight min_edge_offset = - std::min(phantom_node_pair.source_phantom.GetForwardWeightPlusOffset(), - phantom_node_pair.source_phantom.GetReverseWeightPlusOffset()); + std::min(-phantom_node_pair.source_phantom.GetForwardWeightPlusOffset(), + -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset()); if (phantom_node_pair.source_phantom.forward_node_id != SPECIAL_NODEID) { diff --git a/routing_algorithms/shortest_path.hpp b/routing_algorithms/shortest_path.hpp index 3dedc2b76..eaa6d3c53 100644 --- a/routing_algorithms/shortest_path.hpp +++ b/routing_algorithms/shortest_path.hpp @@ -93,8 +93,8 @@ class ShortestPathRouting final const bool allow_u_turn = current_leg > 0 && uturn_indicators.size() > current_leg && uturn_indicators[current_leg - 1]; const EdgeWeight min_edge_offset = - std::min(phantom_node_pair.source_phantom.GetForwardWeightPlusOffset(), - phantom_node_pair.source_phantom.GetReverseWeightPlusOffset()); + std::min(-phantom_node_pair.source_phantom.GetForwardWeightPlusOffset(), + -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset()); // insert new starting nodes into forward heap, adjusted by previous distances. if ((allow_u_turn || search_from_1st_node) && @@ -272,11 +272,13 @@ class ShortestPathRouting final if (start_id_of_leg1 != last_id_of_packed_legs1) { packed_legs1 = packed_legs2; + distance1 = distance2; BOOST_ASSERT(start_id_of_leg1 == temporary_packed_leg1.front()); } else if (start_id_of_leg2 != last_id_of_packed_legs2) { packed_legs2 = packed_legs1; + distance2 = distance1; BOOST_ASSERT(start_id_of_leg2 == temporary_packed_leg2.front()); } } diff --git a/server/api_grammar.hpp b/server/api_grammar.hpp index a629cfbf7..470954f74 100644 --- a/server/api_grammar.hpp +++ b/server/api_grammar.hpp @@ -42,7 +42,7 @@ template struct APIGrammar : qi::grammar> -(uturns); query = ('?') >> (+(zoom | output | jsonp | checksum | location | hint | timestamp | u | cmp | language | instruction | geometry | alt_route | old_API | num_results | - matching_beta | gps_precision | classify)); + matching_beta | gps_precision | classify | locs)); zoom = (-qi::lit('&')) >> qi::lit('z') >> '=' >> qi::short_[boost::bind(&HandlerT::setZoomLevel, handler, ::_1)]; @@ -83,17 +83,20 @@ template struct APIGrammar : qi::grammar> qi::lit("classify") >> '=' >> qi::bool_[boost::bind(&HandlerT::setClassify, handler, ::_1)]; + locs = (-qi::lit('&')) >> qi::lit("locs") >> '=' >> + stringforPolyline[boost::bind(&HandlerT::getCoordinatesFromGeometry, handler, ::_1)]; string = +(qi::char_("a-zA-Z")); stringwithDot = +(qi::char_("a-zA-Z0-9_.-")); stringwithPercent = +(qi::char_("a-zA-Z0-9_.-") | qi::char_('[') | qi::char_(']') | (qi::char_('%') >> qi::char_("0-9A-Z") >> qi::char_("0-9A-Z"))); + stringforPolyline = +(qi::char_("a-zA-Z0-9_.-[]{}@?|\\%~`^")); } qi::rule api_call, query; qi::rule service, zoom, output, string, jsonp, checksum, location, hint, timestamp, stringwithDot, stringwithPercent, language, instruction, geometry, cmp, alt_route, u, - uturns, old_API, num_results, matching_beta, gps_precision, classify; + uturns, old_API, num_results, matching_beta, gps_precision, classify, locs, stringforPolyline; HandlerT *handler; }; diff --git a/server/request_parser.cpp b/server/request_parser.cpp index 584dcbeeb..502e6e0b8 100644 --- a/server/request_parser.cpp +++ b/server/request_parser.cpp @@ -42,7 +42,8 @@ namespace http RequestParser::RequestParser() : state(internal_state::method_start), current_header({"", ""}), - selected_compression(no_compression) + selected_compression(no_compression), is_post_header(false), + content_length(0) { } @@ -58,6 +59,11 @@ RequestParser::parse(request ¤t_request, char *begin, char *end) } } osrm::tribool result = osrm::tribool::indeterminate; + + if(state == internal_state::post_request && content_length <= 0) + { + result = osrm::tribool::yes; + } return std::make_tuple(result, selected_compression); } @@ -70,8 +76,39 @@ osrm::tribool RequestParser::consume(request ¤t_request, const char input) { return osrm::tribool::no; } + if(input == 'P') + { + state = internal_state::post_O; + return osrm::tribool::indeterminate; + } state = internal_state::method; return osrm::tribool::indeterminate; + case internal_state::post_O: + if(input == 'O') + { + state = internal_state::post_S; + return osrm::tribool::indeterminate; + } + return osrm::tribool::no; + case internal_state::post_S: + if(input == 'S') + { + state = internal_state::post_T; + return osrm::tribool::indeterminate; + } + return osrm::tribool::no; + case internal_state::post_T: + if(input == 'T') + { + is_post_header = true; + state = internal_state::method; + return osrm::tribool::indeterminate; + } + return osrm::tribool::no; + case internal_state::post_request: + current_request.uri.push_back(input); + --content_length; + return osrm::tribool::indeterminate; case internal_state::method: if (input == ' ') { @@ -204,6 +241,24 @@ osrm::tribool RequestParser::consume(request ¤t_request, const char input) { current_request.agent = current_header.value; } + if (boost::iequals(current_header.name, "Content-Length")) + { + try + { + content_length = std::stoi(current_header.value); + } + catch (const std::exception &e) + { + // Ignore the header if the parameter isn't an int + } + } + if (boost::iequals(current_header.name, "Content-Type")) + { + if (!boost::icontains(current_header.value, "application/x-www-form-urlencoded")) + { + return osrm::tribool::no; + } + } if (input == '\r') { @@ -272,7 +327,19 @@ osrm::tribool RequestParser::consume(request ¤t_request, const char input) return osrm::tribool::indeterminate; } return osrm::tribool::no; - default: // expecting_newline_3 + case internal_state::expecting_newline_3: + if(input == '\n') + { + if(is_post_header) + { + current_request.uri.push_back('?'); + state = internal_state::post_request; + return osrm::tribool::indeterminate; + } + return osrm::tribool::yes; + } + return osrm::tribool::no; + default: // should never be reached return input == '\n' ? osrm::tribool::yes : osrm::tribool::no; } } diff --git a/server/request_parser.hpp b/server/request_parser.hpp index 2b6bf6944..3724613a7 100644 --- a/server/request_parser.hpp +++ b/server/request_parser.hpp @@ -80,11 +80,17 @@ class RequestParser space_before_header_value, header_value, expecting_newline_2, - expecting_newline_3 + expecting_newline_3, + post_O, + post_S, + post_T, + post_request } state; header current_header; compression_type selected_compression; + bool is_post_header; + int content_length; }; } // namespace http diff --git a/tools/components.cpp b/tools/components.cpp index 7ac64f8d9..660d9225e 100644 --- a/tools/components.cpp +++ b/tools/components.cpp @@ -76,12 +76,68 @@ void DeleteFileIfExists(const std::string &file_name) } } +void LoadRestrictions(const char* path, std::vector& restriction_list) +{ + std::ifstream input_stream(path, std::ios::binary); + if (!input_stream.is_open()) + { + throw osrm::exception("Cannot open restriction file"); + } + loadRestrictionsFromFile(input_stream, restriction_list); +} + +std::size_t LoadGraph(const char* path, + std::vector& coordinate_list, + std::vector& barrier_node_list, + std::vector& graph_edge_list) +{ + std::ifstream input_stream(path, std::ifstream::in | std::ifstream::binary); + if (!input_stream.is_open()) + { + throw osrm::exception("Cannot open osrm file"); + } + + // load graph data + std::vector edge_list; + std::vector traffic_light_node_list; + + auto number_of_nodes = loadNodesFromFile(input_stream, barrier_node_list, + traffic_light_node_list, + coordinate_list); + + loadEdgesFromFile(input_stream, edge_list); + + traffic_light_node_list.clear(); + traffic_light_node_list.shrink_to_fit(); + + // Building an node-based graph + for (const auto &input_edge : edge_list) + { + if (input_edge.source == input_edge.target) + { + continue; + } + + if (input_edge.forward) + { + graph_edge_list.emplace_back(input_edge.source, input_edge.target, + (std::max)(input_edge.weight, 1), input_edge.name_id); + } + if (input_edge.backward) + { + graph_edge_list.emplace_back(input_edge.target, input_edge.source, + (std::max)(input_edge.weight, 1), input_edge.name_id); + } + } + + return number_of_nodes; +} + int main(int argc, char *argv[]) { std::vector coordinate_list; std::vector restriction_list; - std::vector bollard_node_list; - std::vector traffic_lights_list; + std::vector barrier_node_list; LogPolicy::GetInstance().Unmute(); try @@ -95,83 +151,10 @@ int main(int argc, char *argv[]) } SimpleLogger().Write() << "Using restrictions from file: " << argv[2]; - std::ifstream restriction_ifstream(argv[2], std::ios::binary); - const FingerPrint fingerprint_orig; - FingerPrint fingerprint_loaded; - restriction_ifstream.read(reinterpret_cast(&fingerprint_loaded), - sizeof(FingerPrint)); - // check fingerprint and warn if necessary - if (!fingerprint_loaded.TestGraphUtil(fingerprint_orig)) - { - SimpleLogger().Write(logWARNING) << argv[2] << " was prepared with a different build. " - "Reprocess to get rid of this warning."; - } - - if (!restriction_ifstream.good()) - { - throw osrm::exception("Could not access files"); - } - uint32_t usable_restrictions = 0; - restriction_ifstream.read(reinterpret_cast(&usable_restrictions), sizeof(uint32_t)); - restriction_list.resize(usable_restrictions); - - // load restrictions - if (usable_restrictions > 0) - { - restriction_ifstream.read(reinterpret_cast(&restriction_list[0]), - usable_restrictions * sizeof(TurnRestriction)); - } - restriction_ifstream.close(); - - std::ifstream input_stream(argv[1], std::ifstream::in | std::ifstream::binary); - if (!input_stream.is_open()) - { - throw osrm::exception("Cannot open osrm file"); - } - - // load graph data - std::vector edge_list; - const NodeID number_of_nodes = - readBinaryOSRMGraphFromStream(input_stream, edge_list, bollard_node_list, - traffic_lights_list, &coordinate_list, restriction_list); - input_stream.close(); - - BOOST_ASSERT_MSG(restriction_list.size() == usable_restrictions, - "size of restriction_list changed"); - - SimpleLogger().Write() << restriction_list.size() << " restrictions, " - << bollard_node_list.size() << " bollard nodes, " - << traffic_lights_list.size() << " traffic lights"; - - traffic_lights_list.clear(); - traffic_lights_list.shrink_to_fit(); - - // Building an node-based graph std::vector graph_edge_list; -// DeallocatingVector graph_edge_list; - for (const auto &input_edge : edge_list) - { - if (input_edge.source == input_edge.target) - { - continue; - } - - if (input_edge.forward) - { - graph_edge_list.emplace_back(input_edge.source, input_edge.target, - (std::max)(input_edge.weight, 1), input_edge.name_id); - } - if (input_edge.backward) - { - graph_edge_list.emplace_back(input_edge.target, input_edge.source, - (std::max)(input_edge.weight, 1), input_edge.name_id); - } - } - edge_list.clear(); - edge_list.shrink_to_fit(); - BOOST_ASSERT_MSG(0 == edge_list.size() && 0 == edge_list.capacity(), - "input edge vector not properly deallocated"); + auto number_of_nodes = LoadGraph(argv[1], coordinate_list, barrier_node_list, graph_edge_list); + LoadRestrictions(argv[2], restriction_list); tbb::parallel_sort(graph_edge_list.begin(), graph_edge_list.end()); const auto graph = std::make_shared(number_of_nodes, graph_edge_list); @@ -181,9 +164,8 @@ int main(int argc, char *argv[]) SimpleLogger().Write() << "Starting SCC graph traversal"; RestrictionMap restriction_map(restriction_list); - auto tarjan = osrm::make_unique>(graph, - restriction_map, - bollard_node_list); + auto tarjan = osrm::make_unique>(graph, restriction_map, + barrier_node_list); tarjan->run(); SimpleLogger().Write() << "identified: " << tarjan->get_number_of_components() << " many components"; diff --git a/tools/graph_compare.cpp b/tools/graph_compare.cpp deleted file mode 100644 index 8adc06e78..000000000 --- a/tools/graph_compare.cpp +++ /dev/null @@ -1,199 +0,0 @@ -#include "../data_structures/dynamic_graph.hpp" -#include "../data_structures/import_edge.hpp" -#include "../data_structures/query_node.hpp" -#include "../data_structures/restriction.hpp" -#include "../data_structures/static_graph.hpp" -#include "../util/fingerprint.hpp" -#include "../util/graph_loader.hpp" -#include "../util/integer_range.hpp" -#include "../util/make_unique.hpp" -#include "../util/osrm_exception.hpp" -#include "../util/simple_logger.hpp" - -#include "../typedefs.h" - -#include -#include - -struct TarjanEdgeData -{ - TarjanEdgeData() : distance(INVALID_EDGE_WEIGHT), name_id(INVALID_NAMEID) {} - TarjanEdgeData(unsigned distance, unsigned name_id) : distance(distance), name_id(name_id) {} - unsigned distance; - unsigned name_id; -}; - -using StaticTestGraph = StaticGraph; -using DynamicTestGraph = StaticGraph; -using StaticEdge = StaticTestGraph::InputEdge; -using DynamicEdge = DynamicTestGraph::InputEdge; - -int main(int argc, char *argv[]) -{ - std::vector coordinate_list; - std::vector restriction_list; - std::vector bollard_node_list; - std::vector traffic_lights_list; - - LogPolicy::GetInstance().Unmute(); - try - { - // enable logging - if (argc < 3) - { - SimpleLogger().Write(logWARNING) << "usage:\n" << argv[0] - << " "; - return -1; - } - - SimpleLogger().Write() << "Using restrictions from file: " << argv[2]; - std::ifstream restriction_ifstream(argv[2], std::ios::binary); - const FingerPrint fingerprint_orig; - FingerPrint fingerprint_loaded; - restriction_ifstream.read(reinterpret_cast(&fingerprint_loaded), - sizeof(FingerPrint)); - - // check fingerprint and warn if necessary - if (!fingerprint_loaded.TestGraphUtil(fingerprint_orig)) - { - SimpleLogger().Write(logWARNING) << argv[2] << " was prepared with a different build. " - "Reprocess to get rid of this warning."; - } - - if (!restriction_ifstream.good()) - { - throw osrm::exception("Could not access files"); - } - uint32_t usable_restrictions = 0; - restriction_ifstream.read(reinterpret_cast(&usable_restrictions), sizeof(uint32_t)); - restriction_list.resize(usable_restrictions); - - // load restrictions - if (usable_restrictions > 0) - { - restriction_ifstream.read(reinterpret_cast(&restriction_list[0]), - usable_restrictions * sizeof(TurnRestriction)); - } - restriction_ifstream.close(); - - std::ifstream input_stream(argv[1], std::ifstream::in | std::ifstream::binary); - if (!input_stream.is_open()) - { - throw osrm::exception("Cannot open osrm file"); - } - - // load graph data - std::vector edge_list; - const NodeID number_of_nodes = - readBinaryOSRMGraphFromStream(input_stream, edge_list, bollard_node_list, - traffic_lights_list, &coordinate_list, restriction_list); - input_stream.close(); - - BOOST_ASSERT_MSG(restriction_list.size() == usable_restrictions, - "size of restriction_list changed"); - - SimpleLogger().Write() << restriction_list.size() << " restrictions, " - << bollard_node_list.size() << " bollard nodes, " - << traffic_lights_list.size() << " traffic lights"; - - traffic_lights_list.clear(); - traffic_lights_list.shrink_to_fit(); - - // Building an node-based graph - std::vector static_graph_edge_list; - std::vector dynamic_graph_edge_list; - for (const auto &input_edge : edge_list) - { - if (input_edge.source == input_edge.target) - { - continue; - } - - if (input_edge.forward) - { - static_graph_edge_list.emplace_back(input_edge.source, input_edge.target, - (std::max)(input_edge.weight, 1), - input_edge.name_id); - dynamic_graph_edge_list.emplace_back(input_edge.source, input_edge.target, - (std::max)(input_edge.weight, 1), - input_edge.name_id); - } - if (input_edge.backward) - { - dynamic_graph_edge_list.emplace_back(input_edge.target, input_edge.source, - (std::max)(input_edge.weight, 1), - input_edge.name_id); - static_graph_edge_list.emplace_back(input_edge.target, input_edge.source, - (std::max)(input_edge.weight, 1), - input_edge.name_id); - } - } - edge_list.clear(); - edge_list.shrink_to_fit(); - BOOST_ASSERT_MSG(0 == edge_list.size() && 0 == edge_list.capacity(), - "input edge vector not properly deallocated"); - - tbb::parallel_sort(static_graph_edge_list.begin(), static_graph_edge_list.end()); - tbb::parallel_sort(dynamic_graph_edge_list.begin(), dynamic_graph_edge_list.end()); - - auto static_graph = - osrm::make_unique(number_of_nodes, static_graph_edge_list); - auto dynamic_graph = - osrm::make_unique(number_of_nodes, dynamic_graph_edge_list); - - SimpleLogger().Write() << "Starting static/dynamic graph comparison"; - - BOOST_ASSERT(static_graph->GetNumberOfNodes() == dynamic_graph->GetNumberOfNodes()); - BOOST_ASSERT(static_graph->GetNumberOfEdges() == dynamic_graph->GetNumberOfEdges()); - for (const auto node : osrm::irange(0u, static_graph->GetNumberOfNodes())) - { - const auto static_range = static_graph->GetAdjacentEdgeRange(node); - const auto dynamic_range = dynamic_graph->GetAdjacentEdgeRange(node); - SimpleLogger().Write() << "checking node " << node << "/" - << static_graph->GetNumberOfNodes(); - - BOOST_ASSERT(static_range.size() == dynamic_range.size()); - const auto static_begin = static_graph->BeginEdges(node); - const auto dynamic_begin = dynamic_graph->BeginEdges(node); - - // check raw interface - for (const auto i : osrm::irange(0u, static_range.size())) - { - const auto static_target = static_graph->GetTarget(static_begin + i); - const auto dynamic_target = dynamic_graph->GetTarget(dynamic_begin + i); - BOOST_ASSERT(static_target == dynamic_target); - - const auto static_data = static_graph->GetEdgeData(static_begin + i); - const auto dynamic_data = dynamic_graph->GetEdgeData(dynamic_begin + i); - - BOOST_ASSERT(static_data.distance == dynamic_data.distance); - BOOST_ASSERT(static_data.name_id == dynamic_data.name_id); - } - - // check range interface - std::vector static_target_ids, dynamic_target_ids; - std::vector static_edge_data, dynamic_edge_data; - for (const auto static_id : static_range) - { - static_target_ids.push_back(static_graph->GetTarget(static_id)); - static_edge_data.push_back(static_graph->GetEdgeData(static_id)); - } - for (const auto dynamic_id : dynamic_range) - { - dynamic_target_ids.push_back(dynamic_graph->GetTarget(dynamic_id)); - dynamic_edge_data.push_back(dynamic_graph->GetEdgeData(dynamic_id)); - } - BOOST_ASSERT(static_target_ids.size() == dynamic_target_ids.size()); - BOOST_ASSERT(std::equal(std::begin(static_target_ids), std::end(static_target_ids), - std::begin(dynamic_target_ids))); - } - - SimpleLogger().Write() << "Graph comparison finished successfully"; - } - catch (const std::exception &e) - { - SimpleLogger().Write(logWARNING) << "[exception] " << e.what(); - } - - return 0; -} diff --git a/unit_tests/algorithms/duration_parsing.cpp b/unit_tests/algorithms/duration_parsing.cpp index ec3b8191a..e3035dce7 100644 --- a/unit_tests/algorithms/duration_parsing.cpp +++ b/unit_tests/algorithms/duration_parsing.cpp @@ -41,24 +41,24 @@ BOOST_AUTO_TEST_CASE(all_necessary_test) BOOST_AUTO_TEST_CASE(common_durations_get_translated) { - BOOST_CHECK_EQUAL(parseDuration("00:01"), 600); - BOOST_CHECK_EQUAL(parseDuration("00:01:01"), 610); - BOOST_CHECK_EQUAL(parseDuration("01:01"), 36600); + BOOST_CHECK_EQUAL(parseDuration("00:01"), 60); + BOOST_CHECK_EQUAL(parseDuration("00:01:01"), 61); + BOOST_CHECK_EQUAL(parseDuration("01:01"), 3660); // check all combinations of iso duration tokens - BOOST_CHECK_EQUAL(parseDuration("PT1M1S"), 610); - BOOST_CHECK_EQUAL(parseDuration("PT1H1S"), 36010); - BOOST_CHECK_EQUAL(parseDuration("PT15M"), 9000); - BOOST_CHECK_EQUAL(parseDuration("PT15S"), 150); - BOOST_CHECK_EQUAL(parseDuration("PT15H"), 540000); - BOOST_CHECK_EQUAL(parseDuration("PT1H15M"), 45000); - BOOST_CHECK_EQUAL(parseDuration("PT1H15M1S"), 45010); + BOOST_CHECK_EQUAL(parseDuration("PT1M1S"), 61); + BOOST_CHECK_EQUAL(parseDuration("PT1H1S"), 3601); + BOOST_CHECK_EQUAL(parseDuration("PT15M"), 900); + BOOST_CHECK_EQUAL(parseDuration("PT15S"), 15); + BOOST_CHECK_EQUAL(parseDuration("PT15H"), 54000); + BOOST_CHECK_EQUAL(parseDuration("PT1H15M"), 4500); + BOOST_CHECK_EQUAL(parseDuration("PT1H15M1S"), 4501); } BOOST_AUTO_TEST_CASE(iso_8601_durations_case_insensitive) { - BOOST_CHECK_EQUAL(parseDuration("PT15m"), 9000); - BOOST_CHECK_EQUAL(parseDuration("PT1h15m"), 45000); + BOOST_CHECK_EQUAL(parseDuration("PT15m"), 900); + BOOST_CHECK_EQUAL(parseDuration("PT1h15m"), 4500); } BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests/algorithms/geometry_string.cpp b/unit_tests/algorithms/geometry_string.cpp new file mode 100644 index 000000000..035b3217c --- /dev/null +++ b/unit_tests/algorithms/geometry_string.cpp @@ -0,0 +1,74 @@ +/* + +Copyright (c) 2015, Project OSRM contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include + +#include "../../algorithms/polyline_compressor.hpp" +#include "../../data_structures/coordinate_calculation.hpp" + +#include + +#include +#include + +BOOST_AUTO_TEST_CASE(geometry_string) +{ + // Polyline string for the 5 coordinates + const std::string polyline = "_gjaR_gjaR_pR_ibE_pR_ibE_pR_ibE_pR_ibE"; + PolylineCompressor pc; + std::vector coords = pc.decode_string(polyline); + + // Test coordinates; these would be the coordinates we give the loc parameter, + // e.g. loc=10.00,10.0&loc=10.01,10.1... + FixedPointCoordinate coord1(10.00 * COORDINATE_PRECISION, 10.0 * COORDINATE_PRECISION); + FixedPointCoordinate coord2(10.01 * COORDINATE_PRECISION, 10.1 * COORDINATE_PRECISION); + FixedPointCoordinate coord3(10.02 * COORDINATE_PRECISION, 10.2 * COORDINATE_PRECISION); + FixedPointCoordinate coord4(10.03 * COORDINATE_PRECISION, 10.3 * COORDINATE_PRECISION); + FixedPointCoordinate coord5(10.04 * COORDINATE_PRECISION, 10.4 * COORDINATE_PRECISION); + + // Put the test coordinates into the vector for comparison + std::vector cmp_coords; + cmp_coords.emplace_back(coord1); + cmp_coords.emplace_back(coord2); + cmp_coords.emplace_back(coord3); + cmp_coords.emplace_back(coord4); + cmp_coords.emplace_back(coord5); + + BOOST_CHECK_EQUAL(cmp_coords.size(), coords.size()); + + for(unsigned i = 0; i < cmp_coords.size(); ++i) + { + const double cmp1_lat = coords.at(i).lat; + const double cmp2_lat = cmp_coords.at(i).lat; + BOOST_CHECK_CLOSE(cmp1_lat, cmp2_lat, 0.0001); + + const double cmp1_lon = coords.at(i).lon; + const double cmp2_lon = cmp_coords.at(i).lon; + BOOST_CHECK_CLOSE(cmp1_lon, cmp2_lon, 0.0001); + } +} diff --git a/util/fingerprint.hpp b/util/fingerprint.hpp index 82eb1364a..3894fc80f 100644 --- a/util/fingerprint.hpp +++ b/util/fingerprint.hpp @@ -29,23 +29,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define FINGERPRINT_H #include +#include // implements a singleton, i.e. there is one and only one conviguration object class FingerPrint { public: - FingerPrint(); - FingerPrint(const FingerPrint &) = delete; - ~FingerPrint(); + static FingerPrint GetValid(); const boost::uuids::uuid &GetFingerPrint() const; - bool IsMagicNumberOK() const; + bool IsMagicNumberOK(const FingerPrint &other) const; bool TestGraphUtil(const FingerPrint &other) const; bool TestPrepare(const FingerPrint &other) const; bool TestRTree(const FingerPrint &other) const; bool TestQueryObjects(const FingerPrint &other) const; private: - const unsigned magic_number; + unsigned magic_number; char md5_prepare[33]; char md5_tree[33]; char md5_graph[33]; @@ -54,6 +53,10 @@ class FingerPrint // initialize to {6ba7b810-9dad-11d1-80b4-00c04fd430c8} boost::uuids::uuid named_uuid; bool has_64_bits; + }; +static_assert(std::is_trivial::value, "FingerPrint needs to be trivial."); + + #endif /* FingerPrint_H */ diff --git a/util/fingerprint_impl.hpp.in b/util/fingerprint_impl.hpp.in index 9259afd37..98bb122d3 100644 --- a/util/fingerprint_impl.hpp.in +++ b/util/fingerprint_impl.hpp.in @@ -40,35 +40,38 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #cmakedefine MD5GRAPH "${MD5GRAPH}" #cmakedefine MD5OBJECTS "${MD5OBJECTS}" -FingerPrint::FingerPrint() : magic_number(1297240911) +FingerPrint FingerPrint::GetValid() { - md5_prepare[32] = md5_tree[32] = md5_graph[32] = md5_objects[32] = '\0'; + FingerPrint fingerprint; - boost::uuids::name_generator gen(named_uuid); + fingerprint.magic_number = 1297240911; + fingerprint.md5_prepare[32] = fingerprint.md5_tree[32] = fingerprint.md5_graph[32] = fingerprint.md5_objects[32] = '\0'; + + boost::uuids::name_generator gen(fingerprint.named_uuid); std::string temp_string; - std::memcpy(md5_prepare, MD5PREPARE, strlen(MD5PREPARE)); - temp_string += md5_prepare; - std::memcpy(md5_tree, MD5RTREE, 32); - temp_string += md5_tree; - std::memcpy(md5_graph, MD5GRAPH, 32); - temp_string += md5_graph; - std::memcpy(md5_objects, MD5OBJECTS, 32); - temp_string += md5_objects; + std::memcpy(fingerprint.md5_prepare, MD5PREPARE, 32); + temp_string += fingerprint.md5_prepare; + std::memcpy(fingerprint.md5_tree, MD5RTREE, 32); + temp_string += fingerprint.md5_tree; + std::memcpy(fingerprint.md5_graph, MD5GRAPH, 32); + temp_string += fingerprint.md5_graph; + std::memcpy(fingerprint.md5_objects, MD5OBJECTS, 32); + temp_string += fingerprint.md5_objects; - named_uuid = gen(temp_string); - has_64_bits = HAS64BITS; + fingerprint.named_uuid = gen(temp_string); + fingerprint.has_64_bits = HAS64BITS; + + return fingerprint; } -FingerPrint::~FingerPrint() {} - const boost::uuids::uuid &FingerPrint::GetFingerPrint() const { return named_uuid; } -bool FingerPrint::IsMagicNumberOK() const { return 1297240911 == magic_number; } +bool FingerPrint::IsMagicNumberOK(const FingerPrint& other) const { return other.magic_number == magic_number; } bool FingerPrint::TestGraphUtil(const FingerPrint &other) const { - if (!other.IsMagicNumberOK()) + if (!IsMagicNumberOK(other)) { throw osrm::exception("hsgr input file misses magic number. Check or reprocess the file"); } @@ -77,7 +80,7 @@ bool FingerPrint::TestGraphUtil(const FingerPrint &other) const bool FingerPrint::TestPrepare(const FingerPrint &other) const { - if (!other.IsMagicNumberOK()) + if (!IsMagicNumberOK(other)) { throw osrm::exception("osrm input file misses magic number. Check or reprocess the file"); } @@ -86,7 +89,7 @@ bool FingerPrint::TestPrepare(const FingerPrint &other) const bool FingerPrint::TestRTree(const FingerPrint &other) const { - if (!other.IsMagicNumberOK()) + if (!IsMagicNumberOK(other)) { throw osrm::exception("r-tree input file misses magic number. Check or reprocess the file"); } @@ -95,7 +98,7 @@ bool FingerPrint::TestRTree(const FingerPrint &other) const bool FingerPrint::TestQueryObjects(const FingerPrint &other) const { - if (!other.IsMagicNumberOK()) + if (!IsMagicNumberOK(other)) { throw osrm::exception("missing magic number. Check or reprocess the file"); } diff --git a/util/graph_loader.hpp b/util/graph_loader.hpp index d741334ba..71bc9e9f3 100644 --- a/util/graph_loader.hpp +++ b/util/graph_loader.hpp @@ -52,243 +52,57 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -template -NodeID read_undirected_osrm_stream(std::istream &input_stream, - std::vector &edge_list, - std::vector &coordinate_list) +/** + * Reads the .restrictions file and loads it to a vector. + * The since the restrictions reference nodes using their external node id, + * we need to renumber it to the new internal id. +*/ +unsigned loadRestrictionsFromFile(std::istream& input_stream, + std::vector& restriction_list) { - const FingerPrint fingerprint_orig; + const FingerPrint fingerprint_valid = FingerPrint::GetValid(); FingerPrint fingerprint_loaded; - input_stream.read(reinterpret_cast(&fingerprint_loaded), sizeof(FingerPrint)); - - if (!fingerprint_loaded.TestGraphUtil(fingerprint_orig)) + unsigned number_of_usable_restrictions = 0; + input_stream.read((char *)&fingerprint_loaded, sizeof(FingerPrint)); + if (!fingerprint_loaded.TestPrepare(fingerprint_valid)) { - SimpleLogger().Write(logWARNING) << ".osrm was prepared with different build.\n" + SimpleLogger().Write(logWARNING) << ".restrictions was prepared with different build.\n" "Reprocess to get rid of this warning."; } - std::unordered_map ext_to_int_id_map; - - NodeID n; - input_stream.read(reinterpret_cast(&n), sizeof(NodeID)); - SimpleLogger().Write() << "Importing n = " << n << " nodes "; - - ExternalMemoryNode current_node; - for (NodeID i = 0; i < n; ++i) + input_stream.read((char *)&number_of_usable_restrictions, sizeof(unsigned)); + restriction_list.resize(number_of_usable_restrictions); + if (number_of_usable_restrictions > 0) { - input_stream.read(reinterpret_cast(¤t_node), sizeof(ExternalMemoryNode)); - coordinate_list.emplace_back(current_node.lat, current_node.lon); - ext_to_int_id_map.emplace(current_node.node_id, i); - // if (current_node.barrier) - // { - // barrier_node_list.emplace_back(i); - // } - // if (current_node.traffic_lights) - // { - // traffic_light_node_list.emplace_back(i); - // } + input_stream.read((char *) restriction_list.data(), + number_of_usable_restrictions * sizeof(TurnRestriction)); } - // tighten vector sizes - // barrier_node_list.shrink_to_fit(); - // traffic_light_node_list.shrink_to_fit(); - - // renumber nodes in turn restrictions - // for (TurnRestriction ¤t_restriction : restriction_list) - // { - // auto internal_id_iter = ext_to_int_id_map.find(current_restriction.from.node); - // if (internal_id_iter == ext_to_int_id_map.end()) - // { - // SimpleLogger().Write(logDEBUG) << "Unmapped from node " << - // current_restriction.from.node - // << " of restriction"; - // continue; - // } - // current_restriction.from.node = internal_id_iter->second; - - // internal_id_iter = ext_to_int_id_map.find(current_restriction.via.node); - // if (internal_id_iter == ext_to_int_id_map.end()) - // { - // SimpleLogger().Write(logDEBUG) << "Unmapped via node " << - // current_restriction.via.node - // << " of restriction"; - // continue; - // } - - // current_restriction.via.node = internal_id_iter->second; - - // internal_id_iter = ext_to_int_id_map.find(current_restriction.to.node); - // if (internal_id_iter == ext_to_int_id_map.end()) - // { - // SimpleLogger().Write(logDEBUG) << "Unmapped to node " << current_restriction.to.node - // << " of restriction"; - // continue; - // } - // current_restriction.to.node = internal_id_iter->second; - // } - - EdgeWeight weight; - NodeID source, target; - unsigned nameID; - int length; - short dir; // direction (0 = open, 1 = forward, 2+ = open) - bool is_roundabout, ignore_in_grid, is_access_restricted, is_split; - TravelMode travel_mode; - - EdgeID m; - input_stream.read(reinterpret_cast(&m), sizeof(unsigned)); - edge_list.reserve(m); - SimpleLogger().Write() << " and " << m << " edges "; - - for (EdgeID i = 0; i < m; ++i) - { - input_stream.read(reinterpret_cast(&source), sizeof(unsigned)); - input_stream.read(reinterpret_cast(&target), sizeof(unsigned)); - input_stream.read(reinterpret_cast(&length), sizeof(int)); - input_stream.read(reinterpret_cast(&dir), sizeof(short)); - input_stream.read(reinterpret_cast(&weight), sizeof(int)); - input_stream.read(reinterpret_cast(&nameID), sizeof(unsigned)); - input_stream.read(reinterpret_cast(&is_roundabout), sizeof(bool)); - input_stream.read(reinterpret_cast(&ignore_in_grid), sizeof(bool)); - input_stream.read(reinterpret_cast(&is_access_restricted), sizeof(bool)); - input_stream.read(reinterpret_cast(&travel_mode), sizeof(TravelMode)); - input_stream.read(reinterpret_cast(&is_split), sizeof(bool)); - - BOOST_ASSERT_MSG(length > 0, "loaded null length edge"); - BOOST_ASSERT_MSG(weight > 0, "loaded null weight"); - BOOST_ASSERT_MSG(0 <= dir && dir <= 2, "loaded bogus direction"); - - // bool forward = true; - // bool backward = true; - // if (1 == dir) - // { - // backward = false; - // } - // if (2 == dir) - // { - // forward = false; - // } - - // translate the external NodeIDs to internal IDs - auto internal_id_iter = ext_to_int_id_map.find(source); - if (ext_to_int_id_map.find(source) == ext_to_int_id_map.end()) - { -#ifndef NDEBUG - SimpleLogger().Write(logWARNING) << " unresolved source NodeID: " << source; -#endif - continue; - } - source = internal_id_iter->second; - internal_id_iter = ext_to_int_id_map.find(target); - if (ext_to_int_id_map.find(target) == ext_to_int_id_map.end()) - { -#ifndef NDEBUG - SimpleLogger().Write(logWARNING) << "unresolved target NodeID : " << target; -#endif - continue; - } - target = internal_id_iter->second; - BOOST_ASSERT_MSG(source != SPECIAL_NODEID && target != SPECIAL_NODEID, - "nonexisting source or target"); - - if (source > target) - { - std::swap(source, target); - // std::swap(forward, backward); - } - - edge_list.emplace_back(source, target); - } - ext_to_int_id_map.clear(); - - tbb::parallel_sort(edge_list.begin(), edge_list.end()); - - // for (unsigned i = 1; i < edge_list.size(); ++i) - // { - // if ((edge_list[i - 1].target == edge_list[i].target) && - // (edge_list[i - 1].source == edge_list[i].source)) - // { - // const bool edge_flags_equivalent = (edge_list[i - 1].forward == edge_list[i].forward) - // && - // (edge_list[i - 1].backward == - // edge_list[i].backward); - // const bool edge_flags_are_superset1 = - // (edge_list[i - 1].forward && edge_list[i - 1].backward) && - // (edge_list[i].forward != edge_list[i].backward); - // const bool edge_flags_are_superset_2 = - // (edge_list[i].forward && edge_list[i].backward) && - // (edge_list[i - 1].forward != edge_list[i - 1].backward); - - // if (edge_flags_equivalent) - // { - // edge_list[i].weight = std::min(edge_list[i - 1].weight, edge_list[i].weight); - // edge_list[i - 1].source = SPECIAL_NODEID; - // } - // else if (edge_flags_are_superset1) - // { - // if (edge_list[i - 1].weight <= edge_list[i].weight) - // { - // // edge i-1 is smaller and goes in both directions. Throw away the other edge - // edge_list[i].source = SPECIAL_NODEID; - // } - // else - // { - // // edge i-1 is open in both directions, but edge i is smaller in one - // direction. - // // Close edge i-1 in this direction - // edge_list[i - 1].forward = !edge_list[i].forward; - // edge_list[i - 1].backward = !edge_list[i].backward; - // } - // } - // else if (edge_flags_are_superset_2) - // { - // if (edge_list[i - 1].weight <= edge_list[i].weight) - // { - // // edge i-1 is smaller for one direction. edge i is open in both. close edge - // i - // // in the other direction - // edge_list[i].forward = !edge_list[i - 1].forward; - // edge_list[i].backward = !edge_list[i - 1].backward; - // } - // else - // { - // // edge i is smaller and goes in both direction. Throw away edge i-1 - // edge_list[i - 1].source = SPECIAL_NODEID; - // } - // } - // } - // } - // const auto new_end_iter = - // std::remove_if(edge_list.begin(), edge_list.end(), [](const EdgeT &edge) - // { - // return edge.source == SPECIAL_NODEID || edge.target == SPECIAL_NODEID; - // }); - // edge_list.erase(new_end_iter, edge_list.end()); // remove excess candidates. - // edge_list.shrink_to_fit(); - SimpleLogger().Write() << "Graph loaded ok and has " << edge_list.size() << " edges"; - return n; + return number_of_usable_restrictions; } -template -NodeID readBinaryOSRMGraphFromStream(std::istream &input_stream, - std::vector &edge_list, - std::vector &barrier_node_list, - std::vector &traffic_light_node_list, - std::vector *int_to_ext_node_id_map, - std::vector &restriction_list) + +/** + * Reads the beginning of an .osrm file and produces: + * - list of barrier nodes + * - list of traffic lights + * - nodes indexed by their internal (non-osm) id + */ +NodeID loadNodesFromFile(std::istream &input_stream, + std::vector &barrier_node_list, + std::vector &traffic_light_node_list, + std::vector &node_array) { - const FingerPrint fingerprint_orig; + const FingerPrint fingerprint_valid = FingerPrint::GetValid(); FingerPrint fingerprint_loaded; input_stream.read(reinterpret_cast(&fingerprint_loaded), sizeof(FingerPrint)); - if (!fingerprint_loaded.TestGraphUtil(fingerprint_orig)) + if (!fingerprint_loaded.TestPrepare(fingerprint_valid)) { SimpleLogger().Write(logWARNING) << ".osrm was prepared with different build.\n" "Reprocess to get rid of this warning."; } - std::unordered_map ext_to_int_id_map; - NodeID n; input_stream.read(reinterpret_cast(&n), sizeof(NodeID)); SimpleLogger().Write() << "Importing n = " << n << " nodes "; @@ -297,9 +111,7 @@ NodeID readBinaryOSRMGraphFromStream(std::istream &input_stream, for (NodeID i = 0; i < n; ++i) { input_stream.read(reinterpret_cast(¤t_node), sizeof(ExternalMemoryNode)); - int_to_ext_node_id_map->emplace_back(current_node.lat, current_node.lon, - current_node.node_id); - ext_to_int_id_map.emplace(current_node.node_id, i); + node_array.emplace_back(current_node.lat, current_node.lon, current_node.node_id); if (current_node.barrier) { barrier_node_list.emplace_back(i); @@ -314,175 +126,48 @@ NodeID readBinaryOSRMGraphFromStream(std::istream &input_stream, barrier_node_list.shrink_to_fit(); traffic_light_node_list.shrink_to_fit(); - // renumber nodes in turn restrictions - for (TurnRestriction ¤t_restriction : restriction_list) - { - auto internal_id_iter = ext_to_int_id_map.find(current_restriction.from.node); - if (internal_id_iter == ext_to_int_id_map.end()) - { - SimpleLogger().Write(logDEBUG) << "Unmapped from node " << current_restriction.from.node - << " of restriction"; - continue; - } - current_restriction.from.node = internal_id_iter->second; - - internal_id_iter = ext_to_int_id_map.find(current_restriction.via.node); - if (internal_id_iter == ext_to_int_id_map.end()) - { - SimpleLogger().Write(logDEBUG) << "Unmapped via node " << current_restriction.via.node - << " of restriction"; - continue; - } - - current_restriction.via.node = internal_id_iter->second; - - internal_id_iter = ext_to_int_id_map.find(current_restriction.to.node); - if (internal_id_iter == ext_to_int_id_map.end()) - { - SimpleLogger().Write(logDEBUG) << "Unmapped to node " << current_restriction.to.node - << " of restriction"; - continue; - } - current_restriction.to.node = internal_id_iter->second; - } - - EdgeWeight weight; - NodeID source, target; - unsigned nameID; - int length; - short dir; // direction (0 = open, 1 = forward, 2+ = open) - bool is_roundabout, ignore_in_grid, is_access_restricted, is_split; - TravelMode travel_mode; + return n; +} +/** + * Reads a .osrm file and produces the edges. + */ +NodeID loadEdgesFromFile(std::istream &input_stream, + std::vector &edge_list) +{ EdgeID m; input_stream.read(reinterpret_cast(&m), sizeof(unsigned)); - edge_list.reserve(m); + edge_list.resize(m); SimpleLogger().Write() << " and " << m << " edges "; - for (EdgeID i = 0; i < m; ++i) - { - input_stream.read(reinterpret_cast(&source), sizeof(unsigned)); - input_stream.read(reinterpret_cast(&target), sizeof(unsigned)); - input_stream.read(reinterpret_cast(&length), sizeof(int)); - input_stream.read(reinterpret_cast(&dir), sizeof(short)); - input_stream.read(reinterpret_cast(&weight), sizeof(int)); - input_stream.read(reinterpret_cast(&nameID), sizeof(unsigned)); - input_stream.read(reinterpret_cast(&is_roundabout), sizeof(bool)); - input_stream.read(reinterpret_cast(&ignore_in_grid), sizeof(bool)); - input_stream.read(reinterpret_cast(&is_access_restricted), sizeof(bool)); - input_stream.read(reinterpret_cast(&travel_mode), sizeof(TravelMode)); - input_stream.read(reinterpret_cast(&is_split), sizeof(bool)); + input_stream.read((char *) edge_list.data(), m * sizeof(NodeBasedEdge)); - BOOST_ASSERT_MSG(length > 0, "loaded null length edge"); - BOOST_ASSERT_MSG(weight > 0, "loaded null weight"); - BOOST_ASSERT_MSG(0 <= dir && dir <= 2, "loaded bogus direction"); + BOOST_ASSERT(edge_list.size() > 0); - bool forward = true; - bool backward = true; - if (1 == dir) - { - backward = false; - } - if (2 == dir) - { - forward = false; - } - - // translate the external NodeIDs to internal IDs - auto internal_id_iter = ext_to_int_id_map.find(source); - if (ext_to_int_id_map.find(source) == ext_to_int_id_map.end()) - { #ifndef NDEBUG - SimpleLogger().Write(logWARNING) << " unresolved source NodeID: " << source; -#endif - continue; - } - source = internal_id_iter->second; - internal_id_iter = ext_to_int_id_map.find(target); - if (ext_to_int_id_map.find(target) == ext_to_int_id_map.end()) - { -#ifndef NDEBUG - SimpleLogger().Write(logWARNING) << "unresolved target NodeID : " << target; -#endif - continue; - } - target = internal_id_iter->second; - BOOST_ASSERT_MSG(source != SPECIAL_NODEID && target != SPECIAL_NODEID, - "nonexisting source or target"); - - if (source > target) - { - std::swap(source, target); - std::swap(forward, backward); - } - - edge_list.emplace_back(source, target, nameID, weight, forward, backward, is_roundabout, - ignore_in_grid, is_access_restricted, travel_mode, is_split); - } - ext_to_int_id_map.clear(); - - tbb::parallel_sort(edge_list.begin(), edge_list.end()); - - for (unsigned i = 1; i < edge_list.size(); ++i) + SimpleLogger().Write() << "Validating loaded edges..."; + std::sort(edge_list.begin(), edge_list.end(), + [](const NodeBasedEdge& lhs, const NodeBasedEdge& rhs) + { + return (lhs.source < rhs.source) || (lhs.source == rhs.source && lhs.target < rhs.target); + }); + for (auto i = 1u; i < edge_list.size(); ++i) { - if ((edge_list[i - 1].target == edge_list[i].target) && - (edge_list[i - 1].source == edge_list[i].source)) - { - const bool edge_flags_equivalent = (edge_list[i - 1].forward == edge_list[i].forward) && - (edge_list[i - 1].backward == edge_list[i].backward); - const bool edge_flags_are_superset1 = - (edge_list[i - 1].forward && edge_list[i - 1].backward) && - (edge_list[i].forward != edge_list[i].backward); - const bool edge_flags_are_superset_2 = - (edge_list[i].forward && edge_list[i].backward) && - (edge_list[i - 1].forward != edge_list[i - 1].backward); + const auto& edge = edge_list[i]; + const auto& prev_edge = edge_list[i-1]; - if (edge_flags_equivalent) - { - edge_list[i].weight = std::min(edge_list[i - 1].weight, edge_list[i].weight); - edge_list[i - 1].source = SPECIAL_NODEID; - } - else if (edge_flags_are_superset1) - { - if (edge_list[i - 1].weight <= edge_list[i].weight) - { - // edge i-1 is smaller and goes in both directions. Throw away the other edge - edge_list[i].source = SPECIAL_NODEID; - } - else - { - // edge i-1 is open in both directions, but edge i is smaller in one direction. - // Close edge i-1 in this direction - edge_list[i - 1].forward = !edge_list[i].forward; - edge_list[i - 1].backward = !edge_list[i].backward; - } - } - else if (edge_flags_are_superset_2) - { - if (edge_list[i - 1].weight <= edge_list[i].weight) - { - // edge i-1 is smaller for one direction. edge i is open in both. close edge i - // in the other direction - edge_list[i].forward = !edge_list[i - 1].forward; - edge_list[i].backward = !edge_list[i - 1].backward; - } - else - { - // edge i is smaller and goes in both direction. Throw away edge i-1 - edge_list[i - 1].source = SPECIAL_NODEID; - } - } - } + BOOST_ASSERT_MSG(edge.weight > 0, "loaded null weight"); + BOOST_ASSERT_MSG(edge.forward, "edge must be oriented in forward direction"); + BOOST_ASSERT_MSG(edge.travel_mode != TRAVEL_MODE_INACCESSIBLE, "loaded non-accessible"); + + BOOST_ASSERT_MSG(edge.source != edge.target, "loaded edges contain a loop"); + BOOST_ASSERT_MSG(edge.source != prev_edge.source || edge.target != prev_edge.target, "loaded edges contain a multi edge"); } - const auto new_end_iter = - std::remove_if(edge_list.begin(), edge_list.end(), [](const EdgeT &edge) - { - return edge.source == SPECIAL_NODEID || edge.target == SPECIAL_NODEID; - }); - edge_list.erase(new_end_iter, edge_list.end()); // remove excess candidates. - edge_list.shrink_to_fit(); +#endif + SimpleLogger().Write() << "Graph loaded ok and has " << edge_list.size() << " edges"; - return n; + + return m; } template @@ -502,9 +187,10 @@ unsigned readHSGRFromStream(const boost::filesystem::path &hsgr_file, boost::filesystem::ifstream hsgr_input_stream(hsgr_file, std::ios::binary); - FingerPrint fingerprint_loaded, fingerprint_orig; + const FingerPrint fingerprint_valid = FingerPrint::GetValid(); + FingerPrint fingerprint_loaded; hsgr_input_stream.read(reinterpret_cast(&fingerprint_loaded), sizeof(FingerPrint)); - if (!fingerprint_loaded.TestGraphUtil(fingerprint_orig)) + if (!fingerprint_loaded.TestGraphUtil(fingerprint_valid)) { SimpleLogger().Write(logWARNING) << ".hsgr was prepared with different build.\n" "Reprocess to get rid of this warning."; diff --git a/util/iso_8601_duration_parser.hpp b/util/iso_8601_duration_parser.hpp index 1981c8982..3e558cee2 100644 --- a/util/iso_8601_duration_parser.hpp +++ b/util/iso_8601_duration_parser.hpp @@ -90,7 +90,7 @@ template struct iso_8601_grammar : qi::grammar unsigned get_duration() const { - unsigned temp = 10 * (3600 * hours + 60 * minutes + seconds); + unsigned temp = (3600 * hours + 60 * minutes + seconds); if (temp == 0) { temp = std::numeric_limits::max(); diff --git a/util/lua_util.hpp b/util/lua_util.hpp index af6277c59..6dbdc8bc1 100644 --- a/util/lua_util.hpp +++ b/util/lua_util.hpp @@ -55,11 +55,10 @@ inline bool lua_function_exists(lua_State *lua_state, const char *name) // See http://lua-users.org/wiki/PackagePath for details on the package.path syntax. inline void luaAddScriptFolderToLoadPath(lua_State *lua_state, const char *file_name) { - const boost::filesystem::path profile_path(file_name); + boost::filesystem::path profile_path = boost::filesystem::canonical(file_name); std::string folder = profile_path.parent_path().string(); // TODO: This code is most probably not Windows safe since it uses UNIX'ish path delimiters - const std::string lua_code = - "package.path = \"" + folder + "/?.lua;profiles/?.lua;\" .. package.path"; + const std::string lua_code = "package.path = \"" + folder + "/?.lua;\" .. package.path"; luaL_dostring(lua_state, lua_code.c_str()); }