diff --git a/.gitignore b/.gitignore index 2647c65ba72..3537b76fc18 100644 --- a/.gitignore +++ b/.gitignore @@ -46,7 +46,8 @@ Thumbs.db ####################### /build/ /example/build/ -/test/data/monaco* +/test/data/berlin* +/test/bindings/node/data/berlin* /cmake/postinst # Eclipse related files # @@ -86,7 +87,7 @@ stxxl.errlog /test/cache /test/speeds.csv /test/penalties.csv -/test/data/monaco.* +/test/data/berlin.* node_modules # Deprecated config file # @@ -96,4 +97,7 @@ node_modules *.swp # local lua debugging file -debug.lua \ No newline at end of file +debug.lua + +# node-osrm artifacts +lib/binding diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000000..fe081a6dfcd --- /dev/null +++ b/.npmignore @@ -0,0 +1,10 @@ +* +!README.md +!CHANGELOG.md +!CONTRIBUTING.MD +!LICENCE.TXT +!package.json +!example +!lib/*.js +!profiles/* +!profiles/lib/* diff --git a/.travis.yml b/.travis.yml index e18181c1342..67206688030 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,7 +58,7 @@ matrix: apt: sources: ['ubuntu-toolchain-r-test'] packages: ['libstdc++-5-dev'] - env: CLANG_VERSION='4.0.0' BUILD_TYPE='Release' ENABLE_MASON=ON ENABLE_SANITIZER=ON + env: CLANG_VERSION='4.0.0' BUILD_TYPE='Release' ENABLE_MASON=ON ENABLE_SANITIZER=ON ENABLE_NODE_BINDINGS=ON # Release Builds - os: linux @@ -67,7 +67,7 @@ matrix: apt: sources: ['ubuntu-toolchain-r-test'] packages: ['libstdc++-5-dev'] - env: CLANG_VERSION='4.0.0' BUILD_TYPE='Release' ENABLE_MASON=ON RUN_CLANG_FORMAT=ON ENABLE_LTO=ON + env: CLANG_VERSION='4.0.0' BUILD_TYPE='Release' ENABLE_MASON=ON RUN_CLANG_FORMAT=ON ENABLE_LTO=ON ENABLE_NODE_BINDINGS=ON - os: linux compiler: "gcc-6-release" @@ -81,7 +81,7 @@ matrix: compiler: "gcc-6-release-i686" env: > TARGET_ARCH='i686' CCOMPILER='gcc-6' CXXCOMPILER='g++-6' BUILD_TYPE='Release' - CFLAGS='-m32 -msse2 -mfpmath=sse' CXXFLAGS='-m32 -msse2 -mfpmath=sse' CHECK_HEADERS=yes + CFLAGS='-m32 -msse2 -mfpmath=sse' CXXFLAGS='-m32 -msse2 -mfpmath=sse' - os: linux compiler: "gcc-4.9-release" @@ -142,7 +142,7 @@ before_install: - echo "Using ${JOBS} jobs" - source ./scripts/install_node.sh 4 - npm install -g "npm@>=3" # Upgrade to npm >v2 to reduce size of downloaded dependencies - - npm install + - npm install --ignore-scripts # Bootstrap cmake to be able to run mason - CMAKE_URL="https://mason-binaries.s3.amazonaws.com/${TRAVIS_OS_NAME}-x86_64/cmake/${CMAKE_VERSION}.tar.gz" - CMAKE_DIR="mason_packages/${TRAVIS_OS_NAME}-x86_64/cmake/${CMAKE_VERSION}" @@ -173,7 +173,7 @@ install: - export OSRM_BUILD_DIR="$(pwd)/build-osrm" - mkdir ${OSRM_BUILD_DIR} && pushd ${OSRM_BUILD_DIR} - export CC=${CCOMPILER} CXX=${CXXCOMPILER} - - cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DENABLE_MASON=${ENABLE_MASON:-OFF} -DENABLE_ASSERTIONS=${ENABLE_ASSERTIONS:-OFF} -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS:-OFF} -DENABLE_COVERAGE=${ENABLE_COVERAGE:-OFF} -DENABLE_SANITIZER=${ENABLE_SANITIZER:-OFF} -DBUILD_TOOLS=ON -DENABLE_CCACHE=ON + - cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DENABLE_MASON=${ENABLE_MASON:-OFF} -DENABLE_ASSERTIONS=${ENABLE_ASSERTIONS:-OFF} -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS:-OFF} -DENABLE_COVERAGE=${ENABLE_COVERAGE:-OFF} -DENABLE_SANITIZER=${ENABLE_SANITIZER:-OFF} -DBUILD_TOOLS=ON -DENABLE_CCACHE=ON -DENABLE_NODE_BINDINGS=${ENABLE_NODE_BINDINGS:-OFF} - echo "travis_fold:start:MAKE" - make --jobs=${JOBS} - make tests --jobs=${JOBS} @@ -185,7 +185,6 @@ install: if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then sudo ldconfig fi - - if [[ ${CHECK_HEADERS} == yes ]] ; then make check-headers ; fi - popd - mkdir example/build && pushd example/build - cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} @@ -196,7 +195,7 @@ install: script: - if [[ $TARGET_ARCH == armhf ]] ; then echo "Skip tests for $TARGET_ARCH" && exit 0 ; fi - make -C test/data benchmark - - ./example/build/osrm-example test/data/monaco_CH.osrm + - ./example/build/osrm-example test/data/berlin_CH.osrm # All tests assume to be run from the build directory - pushd ${OSRM_BUILD_DIR} - ./unit_tests/library-tests @@ -213,3 +212,11 @@ after_success: if [ -n "${ENABLE_COVERAGE}" ]; then bash <(curl -s https://codecov.io/bash) fi + + - | + if [ -n "${ENABLE_NODE_BINDINGS}" ]; then + nvm install 4 + nvm use 4 + source ./scripts/travis/build.sh + ./scripts/travis/publish.sh + fi diff --git a/CHANGELOG.md b/CHANGELOG.md index aec596ce44f..ab4c3127e5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # 5.7.0 - Changes from 5.6 + - NodeJs Bindings + - Merged https://github.com/Project-OSRM/node-osrm into repository. Build via `cmake .. -DCMAKE_BUILD_TYPE=Release -DENABLE_NODE_BINDINGS=On -DENABLE_MASON=On`. - Internals - Shared memory notification via conditional variables on Linux or semaphore queue on OS X and Windows with a limit of 128 OSRM Engine instances - Files diff --git a/CMakeLists.txt b/CMakeLists.txt index ed46cdd7fff..fe271ab80d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ option(ENABLE_SANITIZER "Use memory sanitizer for Debug build" OFF) option(ENABLE_LTO "Use LTO if available" OFF) option(ENABLE_FUZZING "Fuzz testing using LLVM's libFuzzer" OFF) option(ENABLE_GOLD_LINKER "Use GNU gold linker if available" ON) +option(ENABLE_NODE_BINDINGS "Build NodeJs bindings" OFF) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") @@ -800,6 +801,11 @@ add_custom_target(uninstall add_subdirectory(unit_tests) add_subdirectory(src/benchmarks) +if (ENABLE_NODE_BINDINGS) + add_subdirectory(src/nodejs) +endif() + + if (ENABLE_FUZZING) # Requires libosrm being built with sanitizers; make configurable and default to ubsan set(FUZZ_SANITIZER "undefined" CACHE STRING "Sanitizer to be used for Fuzz testing") @@ -815,18 +821,21 @@ if (ENABLE_FUZZING) endif () -## add headers sanity check target that includes all headers independently -set(check_headers_dir "${PROJECT_BINARY_DIR}/check-headers") -file(GLOB_RECURSE headers_to_check - ${PROJECT_BINARY_DIR}/*.hpp - ${PROJECT_SOURCE_DIR}/include/*.hpp) -foreach(header ${headers_to_check}) - get_filename_component(filename ${header} NAME_WE) - set(filename "${check_headers_dir}/${filename}.cpp") - if (NOT EXISTS ${filename}) - file(WRITE ${filename} "#include \"${header}\"\n") - endif() - list(APPEND sources ${filename}) -endforeach() -add_library(check-headers STATIC EXCLUDE_FROM_ALL ${sources}) -set_target_properties(check-headers PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${check_headers_dir}) +# add headers sanity check target that includes all headers independently +# make sure we have all deps for the nodejs sub project's includes (nan, node) +if (ENABLE_NODE_BINDINGS) + set(check_headers_dir "${PROJECT_BINARY_DIR}/check-headers") + file(GLOB_RECURSE headers_to_check + ${PROJECT_BINARY_DIR}/*.hpp + ${PROJECT_SOURCE_DIR}/include/*.hpp) + foreach(header ${headers_to_check}) + get_filename_component(filename ${header} NAME_WE) + set(filename "${check_headers_dir}/${filename}.cpp") + if (NOT EXISTS ${filename}) + file(WRITE ${filename} "#include \"${header}\"\n") + endif() + list(APPEND sources ${filename}) + endforeach() + add_library(check-headers STATIC EXCLUDE_FROM_ALL ${sources}) + set_target_properties(check-headers PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${check_headers_dir}) +endif() diff --git a/appveyor-build.bat b/appveyor-build.bat index d2a746a61cf..203534fea0e 100644 --- a/appveyor-build.bat +++ b/appveyor-build.bat @@ -133,12 +133,12 @@ unit_tests\%Configuration%\server-tests.exe IF %ERRORLEVEL% NEQ 0 GOTO ERROR ECHO running library-tests.exe ... -SET test_region=monaco +SET test_region=berlin SET test_osm=%test_region%.osm.pbf SET test_osm_ch=%test_region%_CH.osm.pbf SET test_osm_corech=%test_region%_CoreCH.osm.pbf SET test_osm_mld=%test_region%_MLD.osm.pbf -IF NOT EXIST %test_osm% powershell Invoke-WebRequest https://s3.amazonaws.com/mapbox/osrm/testing/monaco.osm.pbf -OutFile %test_osm% +IF NOT EXIST %test_osm% powershell Invoke-WebRequest https://s3.amazonaws.com/mapbox/osrm/testing/berlin.osm.pbf -OutFile %test_osm% COPY %test_osm% %test_osm_ch% COPY %test_osm% %test_osm_corech% COPY %test_osm% %test_osm_mld% diff --git a/cmake/nodejs/FindNodeJS.cmake b/cmake/nodejs/FindNodeJS.cmake new file mode 100644 index 00000000000..6f502d07482 --- /dev/null +++ b/cmake/nodejs/FindNodeJS.cmake @@ -0,0 +1,587 @@ +# Copyright (c) 2015, Colin Taylor +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +# FindNodeJS.cmake CMake module vendored from the node-cmake project (v1.2). + +# This script uses CMake 3.1+ features +if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 3.1.0) + message(FATAL_ERROR "FindNodeJS.cmake uses CMake 3.1+ features") +endif() + +# Force a build type to be set (ignored on config based generators) +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type" FORCE) +endif() + +# Capture module information +set(NodeJS_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) +get_filename_component(NodeJS_MODULE_NAME ${NodeJS_MODULE_PATH} NAME) + +# Allow users to specify the installed location of the Node.js package +set(NodeJS_ROOT_DIR "" CACHE PATH + "The root directory of the node.js installed package") + +# Allow users to specify that downloaded sources should be used +option(NodeJS_DOWNLOAD "Download the required source files" Off) + +# Allow users to force downloading of node packages +option(NodeJS_FORCE_DOWNLOAD "Download the source files every time" Off) + +# Allow users to force archive extraction +option(NodeJS_FORCE_EXTRACT "Extract the archive every time" Off) + +# Make libc++ the default when compiling with clang +option(NodeJS_USE_CLANG_STDLIB "Use libc++ when compiling with clang" On) +if(APPLE) + set(NodeJS_USE_CLANG_STDLIB On CACHE BOOL "" FORCE) +endif() + +if(WIN32) + # Allow users to specify that the executable should be downloaded + option(NodeJS_DOWNLOAD_EXECUTABLE + "Download matching executable if available" Off + ) +endif() + +# Try to find the node.js executable +# The node executable under linux may not be the correct program +find_program(NodeJS_EXECUTABLE + NAMES node + PATHS ${NodeJS_ROOT_DIR} + PATH_SUFFIXES nodejs node +) +set(NodeJS_VALIDATE_EXECUTABLE 1) +if(NodeJS_EXECUTABLE) + execute_process( + COMMAND ${NodeJS_EXECUTABLE} --version + RESULT_VARIABLE NodeJS_VALIDATE_EXECUTABLE + OUTPUT_VARIABLE NodeJS_INSTALLED_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + execute_process( + COMMAND ${NodeJS_EXECUTABLE} -p "process.platform" + OUTPUT_VARIABLE NodeJS_INSTALLED_PLATFORM + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + execute_process( + COMMAND ${NodeJS_EXECUTABLE} -p "process.arch" + OUTPUT_VARIABLE NodeJS_INSTALLED_ARCH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +endif() + +# If node isn't the node.js binary, try the nodejs binary +if(NOT NodeJS_VALIDATE_EXECUTABLE EQUAL 0) + find_program(NodeJS_EXECUTABLE + NAMES nodejs + PATHS ${NodeJS_ROOT_DIR} + PATH_SUFFIXES nodejs node + ) + set(NodeJS_VALIDATE_EXECUTABLE 1) + if(NodeJS_EXECUTABLE) + execute_process( + COMMAND ${NodeJS_EXECUTABLE} --version + RESULT_VARIABLE NodeJS_VALIDATE_EXECUTABLE + OUTPUT_VARIABLE NodeJS_INSTALLED_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + endif() + + if(NOT NodeJS_VALIDATE_EXECUTABLE EQUAL 0) + message(WARNING "Node.js executable could not be found. \ + Set NodeJS_ROOT_DIR to the installed location of the executable or \ + install Node.js to its default location.") + endif() +endif() + +# Determine if a variant is set in the components +list(APPEND NodeJS_OTHER_COMPONENTS + X64 IA32 ARM WIN32 LINUX DARWIN +) +set(NodeJS_COMPONENTS_CONTAINS_VARIANT False) +foreach(NodeJS_COMPONENT ${NodeJS_FIND_COMPONENTS}) + list(FIND NodeJS_OTHER_COMPONENTS ${NodeJS_COMPONENT} NodeJS_OTHER_INDEX) + if(NodeJS_OTHER_INDEX EQUAL -1) + set(NodeJS_COMPONENTS_CONTAINS_VARIANT True) + break() + endif() +endforeach() + +# Get the targeted version of Node.js (or one of its derivatives) +if(NOT NodeJS_VERSION) + if(NodeJS_FIND_VERSION) + set(NodeJS_VERSION ${NodeJS_FIND_VERSION}) + elseif(NodeJS_INSTALLED_VERSION AND NOT NodeJS_COMPONENTS_CONTAINS_VARIANT) + string(SUBSTRING ${NodeJS_INSTALLED_VERSION} 1 -1 NodeJS_VERSION) + else() + message(FATAL_ERROR "Node.js version is not set. Set the VERSION \ + property of the find_package command to the required version of the \ + Node.js sources") + endif() +endif() + +# Determine the target platform for the compiled module +# Uses several mechanisms in order: +# +# 1. CMake cache (allows overriding on the command line) +# 2. Node architecture when binary is available +# 3. CMake architecture +# +set(NodeJS_PLATFORM "" CACHE STRING "Target node.js platform for module") +if(NOT NodeJS_PLATFORM) + if(NodeJS_EXECUTABLE) + set(NodeJS_PLATFORM ${NodeJS_INSTALLED_PLATFORM}) + elseif(WIN32) + set(NodeJS_PLATFORM "win32") + elseif(UNIX) + if(APPLE) + set(NodeJS_PLATFORM "darwin") + else() + set(NodeJS_PLATFORM "linux") + endif() + else() + message(FATAL_ERROR "Node.js platform is not set. Add the platform \ + to the find_package components section or set NodeJS_PLATFORM in the \ + cache.") + endif() +endif() + +# Convenience variables for the platform type +if(NodeJS_PLATFORM STREQUAL "win32") + set(NodeJS_PLATFORM_WIN32 True) + set(NodeJS_PLATFORM_LINUX False) + set(NodeJS_PLATFORM_DARWIN False) +elseif(NodeJS_PLATFORM STREQUAL "linux") + set(NodeJS_PLATFORM_WIN32 False) + set(NodeJS_PLATFORM_LINUX True) + set(NodeJS_PLATFORM_DARWIN False) +elseif(NodeJS_PLATFORM STREQUAL "darwin") + set(NodeJS_PLATFORM_WIN32 False) + set(NodeJS_PLATFORM_LINUX False) + set(NodeJS_PLATFORM_DARWIN True) +endif() + +# Determine the target architecture for the compiled module +# Uses several mechanisms in order: +# +# 1. CMake cache (allows overriding on the command line) +# 2. Node architecture when binary is available +# 3. Compiler architecture under MSVC +# +set(NodeJS_ARCH "" CACHE STRING "Target node.js architecture for module") +if(NOT NodeJS_ARCH) + if(NodeJS_EXECUTABLE) + set(NodeJS_ARCH ${NodeJS_INSTALLED_ARCH}) + elseif(MSVC) + if(CMAKE_CL_64) + set(NodeJS_ARCH "x64") + else() + set(NodeJS_ARCH "ia32") + endif() + else() + message(FATAL_ERROR "Node.js architecture is not set. Add the \ + architecture to the find_package components section or set NodeJS_ARCH \ + in the cache.") + endif() +endif() + +# Convenience variables for the architecture +if(NodeJS_ARCH STREQUAL "x64") + set(NodeJS_ARCH_X64 True) + set(NodeJS_ARCH_IA32 False) + set(NodeJS_ARCH_ARM False) +elseif(NodeJS_ARCH STREQUAL "ia32") + set(NodeJS_ARCH_X64 False) + set(NodeJS_ARCH_IA32 True) + set(NodeJS_ARCH_ARM False) +elseif(NodeJS_ARCH STREQUAL "arm") + set(NodeJS_ARCH_X64 False) + set(NodeJS_ARCH_IA32 False) + set(NodeJS_ARCH_ARM True) +endif() + +# Include helper functions +include(util/NodeJSUtil) + +# Default variant name +# Used by the installed header comparison below +set(NodeJS_DEFAULT_VARIANT_NAME "node.js") + +# Variables for Node.js artifacts across variants +# Specify all of these variables for each new variant +set(NodeJS_VARIANT_NAME "") # The printable name of the variant +set(NodeJS_VARIANT_BASE "") # A file name safe version of the variant +set(NodeJS_URL "") # The URL for the artifacts +set(NodeJS_SOURCE_PATH "") # The URL path of the source archive +set(NodeJS_CHECKSUM_PATH "") # The URL path of the checksum file +set(NodeJS_CHECKSUM_TYPE "") # The checksum type (algorithm) +set(NodeJS_WIN32_LIBRARY_PATH "") # The URL path of the windows library +set(NodeJS_WIN32_BINARY_PATH "") # The URL path of the windows executable +set(NodeJS_WIN32_LIBRARY_NAME "") # The name of the windows library +set(NodeJS_WIN32_BINARY_NAME "") # The name of the windows executable + +set(NodeJS_DEFAULT_INCLUDE True) # Enable default include behavior +set(NodeJS_DEFAULT_LIBS True) # Include the default libraries +set(NodeJS_HAS_WIN32_PREFIX True) # Does the variant use library prefixes +set(NodeJS_HAS_WIN32_BINARY True) # Does the variant have win32 executables +set(NodeJS_HAS_OPENSSL True) # Does the variant include openssl headers +set(NodeJS_HEADER_VERSION 0.12.7) # Version after header-only archives start +set(NodeJS_SHA256_VERSION 0.7.0) # Version after sha256 checksums start +set(NodeJS_PREFIX_VERSION 0.12.7) # Version after windows prefixing starts +set(NodeJS_CXX11R_VERSION 0.12.7) # Version after c++11 is required +set(NodeJS_SOURCE_INCLUDE True) # Use the include paths from a source archive +set(NodeJS_HEADER_INCLUDE False) # Use the include paths from a header archive +set(NodeJS_INCLUDE_PATHS "") # Set of header dirs inside the source archive +set(NodeJS_LIBRARIES "") # The set of libraries to link with addon +set(NodeJS_WIN32_DELAYLOAD "") # Set of executables to delayload on windows + +# NodeJS variants +# Selects download target based on configured component +# Include NodeJS last to provide default configurations when omitted +file( + GLOB NodeJS_SUPPORTED_VARIANTS + RELATIVE ${CMAKE_CURRENT_LIST_DIR}/variants + ${CMAKE_CURRENT_LIST_DIR}/variants/* +) +foreach(NodeJS_SUPPORTED_VARIANT ${NodeJS_SUPPORTED_VARIANTS}) + get_filename_component(NodeJS_SUPPORTED_VARIANT_NAME + ${NodeJS_SUPPORTED_VARIANT} NAME_WE + ) + if(NOT NodeJS_SUPPORTED_VARIANT_NAME STREQUAL "NodeJS") + include(variants/${NodeJS_SUPPORTED_VARIANT_NAME}) + endif() +endforeach() +include(variants/NodeJS) + +# Populate version variables, including version components +set(NodeJS_VERSION_STRING "v${NodeJS_VERSION}") + +# Populate the remaining version variables +string(REPLACE "." ";" NodeJS_VERSION_PARTS ${NodeJS_VERSION}) +list(GET NodeJS_VERSION_PARTS 0 NodeJS_VERSION_MAJOR) +list(GET NodeJS_VERSION_PARTS 1 NodeJS_VERSION_MINOR) +list(GET NodeJS_VERSION_PARTS 2 NodeJS_VERSION_PATCH) + +# If the version we're looking for is the version that is installed, +# try finding the required headers. Don't do this under windows (where +# headers are not part of the installed content), when the user has +# specified that headers should be downloaded or when using a variant other +# than the default +if((NOT NodeJS_PLATFORM_WIN32) AND (NOT NodeJS_DOWNLOAD) AND + NodeJS_VARIANT_NAME STREQUAL NodeJS_DEFAULT_VARIANT_NAME AND + NodeJS_INSTALLED_VERSION STREQUAL NodeJS_VERSION_STRING AND + NodeJS_INSTALLED_PLATFORM STREQUAL NodeJS_PLATFORM AND + NodeJS_INSTALLED_ARCH STREQUAL NodeJS_ARCH) + # node.h is really generic and too easy for cmake to find the wrong + # file, so use the directory as a guard, and then just tack it on to + # the actual path + # + # Specifically ran into this under OSX, where python contains a node.h + # that gets found instead + find_path(NodeJS_INCLUDE_PARENT node/node.h) + set(NodeJS_INCLUDE_DIRS ${NodeJS_INCLUDE_PARENT}/node) + + # Under all systems that support this, there are no libraries required + # for linking (symbols are resolved via the main executable at runtime) + set(NodeJS_LIBRARIES "") + +# Otherwise, headers and required libraries must be downloaded to the project +# to supplement what is installed +else() + # Create a folder for downloaded artifacts + set(NodeJS_DOWNLOAD_PATH + ${CMAKE_CURRENT_BINARY_DIR}/${NodeJS_VARIANT_BASE} + ) + set(NodeJS_DOWNLOAD_PATH ${NodeJS_DOWNLOAD_PATH}-${NodeJS_VERSION_STRING}) + file(MAKE_DIRECTORY ${NodeJS_DOWNLOAD_PATH}) + + # Download the checksum file for validating all other downloads + # Conveniently, if this doesn't download correctly, the setup fails + # due to checksum failures + set(NodeJS_CHECKSUM_FILE ${NodeJS_DOWNLOAD_PATH}/CHECKSUM) + nodejs_download( + ${NodeJS_URL}/${NodeJS_CHECKSUM_PATH} + ${NodeJS_CHECKSUM_FILE} + ${NodeJS_FORCE_DOWNLOAD} + ) + file(READ ${NodeJS_CHECKSUM_FILE} NodeJS_CHECKSUM_DATA) + + # Download and extract the main source archive + set(NodeJS_SOURCE_FILE ${NodeJS_DOWNLOAD_PATH}/headers.tar.gz) + nodejs_checksum( + ${NodeJS_CHECKSUM_DATA} ${NodeJS_SOURCE_PATH} NodeJS_SOURCE_CHECKSUM + ) + nodejs_download( + ${NodeJS_URL}/${NodeJS_SOURCE_PATH} + ${NodeJS_SOURCE_FILE} + ${NodeJS_SOURCE_CHECKSUM} + ${NodeJS_CHECKSUM_TYPE} + ${NodeJS_FORCE_DOWNLOAD} + ) + set(NodeJS_HEADER_PATH ${NodeJS_DOWNLOAD_PATH}/src) + nodejs_extract( + ${NodeJS_SOURCE_FILE} + ${NodeJS_HEADER_PATH} + ${NodeJS_FORCE_EXTRACT} + ) + + # Populate include directories from the extracted source archive + foreach(NodeJS_HEADER_BASE ${NodeJS_INCLUDE_PATHS}) + set(NodeJS_INCLUDE_DIR ${NodeJS_HEADER_PATH}/${NodeJS_HEADER_BASE}) + if(NOT EXISTS ${NodeJS_INCLUDE_DIR}) + message(FATAL_ERROR "Include does not exist: ${NodeJS_INCLUDE_DIR}") + endif() + list(APPEND NodeJS_INCLUDE_DIRS ${NodeJS_INCLUDE_DIR}) + endforeach() + + # Download required library files when targeting windows + if(NodeJS_PLATFORM_WIN32) + # Download the windows library + set(NodeJS_WIN32_LIBRARY_FILE + ${NodeJS_DOWNLOAD_PATH}/lib/${NodeJS_ARCH} + ) + set(NodeJS_WIN32_LIBRARY_FILE + ${NodeJS_WIN32_LIBRARY_FILE}/${NodeJS_WIN32_LIBRARY_NAME} + ) + nodejs_checksum( + ${NodeJS_CHECKSUM_DATA} ${NodeJS_WIN32_LIBRARY_PATH} + NodeJS_WIN32_LIBRARY_CHECKSUM + ) + nodejs_download( + ${NodeJS_URL}/${NodeJS_WIN32_LIBRARY_PATH} + ${NodeJS_WIN32_LIBRARY_FILE} + ${NodeJS_WIN32_LIBRARY_CHECKSUM} + ${NodeJS_CHECKSUM_TYPE} + ${NodeJS_FORCE_DOWNLOAD} + ) + list(APPEND NodeJS_LIBRARIES ${NodeJS_WIN32_LIBRARY_FILE}) + + # If provided, download the windows executable + if(NodeJS_WIN32_BINARY_PATH AND + NodeJS_DOWNLOAD_EXECUTABLE) + set(NodeJS_WIN32_BINARY_FILE + ${NodeJS_DOWNLOAD_PATH}/lib/${NodeJS_ARCH} + ) + set(NodeJS_WIN32_BINARY_FILE + ${NodeJS_WIN32_BINARY_FILE}/${NodeJS_WIN32_BINARY_NAME} + ) + nodejs_checksum( + ${NodeJS_CHECKSUM_DATA} ${NodeJS_WIN32_BINARY_PATH} + NodeJS_WIN32_BINARY_CHECKSUM + ) + nodejs_download( + ${NodeJS_URL}/${NodeJS_WIN32_BINARY_PATH} + ${NodeJS_WIN32_BINARY_FILE} + ${NodeJS_WIN32_BINARY_CHECKSUM} + ${NodeJS_CHECKSUM_TYPE} + ${NodeJS_FORCE_DOWNLOAD} + ) + endif() + endif() +endif() + +# Support windows delay loading +if(NodeJS_PLATFORM_WIN32) + list(APPEND NodeJS_LINK_FLAGS /IGNORE:4199) + set(NodeJS_WIN32_DELAYLOAD_CONDITION "") + foreach(NodeJS_WIN32_DELAYLOAD_BINARY ${NodeJS_WIN32_DELAYLOAD}) + list(APPEND NodeJS_LINK_FLAGS + /DELAYLOAD:${NodeJS_WIN32_DELAYLOAD_BINARY} + ) + list(APPEND NodeJS_WIN32_DELAYLOAD_CONDITION + "_stricmp(info->szDll, \"${NodeJS_WIN32_DELAYLOAD_BINARY}\") != 0" + ) + endforeach() + string(REPLACE ";" " &&\n " + NodeJS_WIN32_DELAYLOAD_CONDITION + "${NodeJS_WIN32_DELAYLOAD_CONDITION}" + ) + configure_file( + ${NodeJS_MODULE_PATH}/src/win_delay_load_hook.c + ${CMAKE_CURRENT_BINARY_DIR}/win_delay_load_hook.c @ONLY + ) + list(APPEND NodeJS_ADDITIONAL_SOURCES + ${CMAKE_CURRENT_BINARY_DIR}/win_delay_load_hook.c + ) +endif() + +# Allow undefined symbols on OSX +if(NodeJS_PLATFORM_DARWIN) + list(APPEND NodeJS_LINK_FLAGS "-undefined dynamic_lookup") +endif() + +# Use libc++ when clang is the compiler by default +if(NodeJS_USE_CLANG_STDLIB AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*") + list(APPEND NodeJS_COMPILE_OPTIONS -stdlib=libc++) +endif() + +# Require c++11 support after a specific point, but only if the user hasn't +# specified an override +if(NOT NodeJS_CXX_STANDARD) + if(NodeJS_VERSION VERSION_GREATER NodeJS_CXX11R_VERSION) + set(NodeJS_CXX_STANDARD 11) + else() + set(NodeJS_CXX_STANDARD 98) + endif() +endif() + +# Set required definitions +list(APPEND NodeJS_DEFINITIONS BUILDING_NODE_EXTENSION) +if(NodeJS_PLATFORM_DARWIN) + list(APPEND NodeJS_DEFINITIONS _DARWIN_USE_64_BIT_INODE=1) +endif() +if(NOT NodeJS_PLATFORM_WIN32) + list(APPEND NodeJS_DEFINITIONS + _LARGEFILE_SOURCE + _FILE_OFFSET_BITS=64 + ) +endif() + +function(add_nodejs_module NAME) + # Build a shared library for the module + add_library(${NAME} SHARED ${ARGN} ${NodeJS_ADDITIONAL_SOURCES}) + + # Include required headers + # Find and include Nan as well (always available as its a + # dependency of this module) + nodejs_find_module_fallback(nan ${CMAKE_CURRENT_SOURCE_DIR} NAN_PATH) + target_include_directories(${NAME} + PUBLIC ${NodeJS_INCLUDE_DIRS} + PUBLIC ${NAN_PATH} + ) + + # Set module properties + # This ensures proper naming of the module library across all platforms + get_target_property(COMPILE_OPTIONS ${NAME} COMPILE_OPTIONS) + if(NOT COMPILE_OPTIONS) + set(COMPILE_OPTIONS "") + endif() + set(COMPILE_OPTIONS ${COMPILE_OPTIONS} ${NodeJS_COMPILE_OPTIONS}) + get_target_property(LINK_FLAGS ${NAME} LINK_FLAGS) + if(NOT LINK_FLAGS) + set(LINK_FLAGS "") + endif() + foreach(NodeJS_LINK_FLAG ${NodeJS_LINK_FLAGS}) + set(LINK_FLAGS "${LINK_FLAGS} ${NodeJS_LINK_FLAG}") + endforeach() + set_target_properties(${NAME} PROPERTIES + PREFIX "" + SUFFIX ".node" + MACOSX_RPATH ON + POSITION_INDEPENDENT_CODE TRUE + COMPILE_OPTIONS "${COMPILE_OPTIONS}" + LINK_FLAGS "${LINK_FLAGS}" + CXX_STANDARD_REQUIRED TRUE + CXX_STANDARD ${NodeJS_CXX_STANDARD} + ) + + # Output the module in a per build type directory + # This makes builds consistent with visual studio and other generators + # that build by configuration + if(NOT CMAKE_CONFIGURATION_TYPES) + set_property(TARGET ${NAME} PROPERTY LIBRARY_OUTPUT_DIRECTORY + ${CMAKE_BUILD_TYPE} + ) + endif() + + # Set any required complier flags + # Mostly used under windows + target_compile_definitions(${NAME} PRIVATE ${NodeJS_DEFINITIONS}) + + # Link against required NodeJS libraries + target_link_libraries(${NAME} ${NodeJS_LIBRARIES}) +endfunction() + +# Write out the configuration for node scripts +configure_file( + ${NodeJS_MODULE_PATH}/build.json.in + ${CMAKE_CURRENT_BINARY_DIR}/build.json @ONLY +) + +# Make sure we haven't violated the version-to-standard mapping +if(NodeJS_VERSION VERSION_GREATER NodeJS_CXX11R_VERSION AND + NodeJS_CXX_STANDARD EQUAL 98) + message(FATAL_ERROR "${NodeJS_VARIANT_NAME} ${NodeJS_VERSION} \ + requires C++11 or newer to build") +endif() + +# This is a find_package file, handle the standard invocation +include(FindPackageHandleStandardArgs) +set(NodeJS_TARGET "${NodeJS_VARIANT_NAME} ${NodeJS_PLATFORM}/${NodeJS_ARCH}") +find_package_handle_standard_args(NodeJS + FOUND_VAR NodeJS_FOUND + REQUIRED_VARS NodeJS_TARGET NodeJS_INCLUDE_DIRS + VERSION_VAR NodeJS_VERSION +) + +# Mark variables that users shouldn't modify +mark_as_advanced( + NodeJS_VALIDATE_EXECUTABLE + NodeJS_OTHER_COMPONENTS + NodeJS_COMPONENTS_CONTAINS_VARIANT + NodeJS_COMPONENT + NodeJS_OTHER_INDEX + NodeJS_VERSION_STRING + NodeJS_VERSION_MAJOR + NodeJS_VERSION_MINOR + NodeJS_VERSION_PATCH + NodeJS_VERSION_TWEAK + NodeJS_PLATFORM + NodeJS_PLATFORM_WIN32 + NodeJS_PLATFORM_LINUX + NodeJS_PLATFORM_DARWIN + NodeJS_ARCH + NodeJS_ARCH_X64 + NodeJS_ARCH_IA32 + NodeJS_ARCH_ARM + NodeJS_DEFAULT_VARIANT_NAME + NodeJS_VARIANT_BASE + NodeJS_VARIANT_NAME + NodeJS_URL + NodeJS_SOURCE_PATH + NodeJS_CHECKSUM_PATH + NodeJS_CHECKSUM_TYPE + NodeJS_WIN32_LIBRARY_PATH + NodeJS_WIN32_BINARY_PATH + NodeJS_WIN32_LIBRARY_NAME + NodeJS_WIN32_BINARY_NAME + NodeJS_DEFAULT_INCLUDE + NodeJS_DEFAULT_LIBS + NodeJS_HAS_WIN32_BINARY + NodeJS_HEADER_VERSION + NodeJS_SHA256_VERISON + NodeJS_PREFIX_VERSION + NodeJS_SOURCE_INCLUDE + NodeJS_HEADER_INCLUDE + NodeJS_INCLUDE_PATHS + NodeJS_WIN32_DELAYLOAD + NodeJS_DOWNLOAD_PATH + NodeJS_CHECKSUM_FILE + NodeJS_CHECKSUM_DATA + NodeJS_SOURCE_FILE + NodeJS_SOURCE_CHECKSUM + NodeJS_HEADER_PATH + NodeJS_HEADER_BASE + NodeJS_INCLUDE_DIR + NodeJS_WIN32_LIBRARY_FILE + NodeJS_WIN32_LIBRARY_CHECKSUM + NodeJS_WIN32_BINARY_FILE + NodeJS_WIN32_BINARY_CHECKSUM + NodeJS_NAN_PATH + NodeJS_LINK_FLAGS + NodeJS_COMPILE_OPTIONS + NodeJS_ADDITIONAL_SOURCES + NodeJS_WIN32_DELAYLOAD_CONDITION + NodeJS_WIN32_DELAYLOAD_BINARY + NodeJS_TARGET +) diff --git a/cmake/nodejs/build.json.in b/cmake/nodejs/build.json.in new file mode 100644 index 00000000000..d006ad161a1 --- /dev/null +++ b/cmake/nodejs/build.json.in @@ -0,0 +1,10 @@ +{ + "build_type": "@CMAKE_BUILD_TYPE@", + "generator": "@CMAKE_GENERATOR@", + "toolset": "@CMAKE_GENERATOR_TOOLSET@", + "platform": "@CMAKE_GENERATOR_PLATFORM@", + "variant": "@NodeJS_VARIANT_BASE@", + "version": "@NodeJS_VERSION@", + "download": "@NodeJS_DOWNLOAD@", + "standard": "@NodeJS_CXX_STANDARD@" +} \ No newline at end of file diff --git a/cmake/nodejs/util/Github.cmake b/cmake/nodejs/util/Github.cmake new file mode 100644 index 00000000000..8c2e2e91025 --- /dev/null +++ b/cmake/nodejs/util/Github.cmake @@ -0,0 +1,28 @@ +set(GITHUB_API_TOKEN $ENV{GITHUB_API_TOKEN}) + +set(GITHUB_AUTH "") +if(GITHUB_API_TOKEN) + set(GITHUB_AUTH "?access_token=${GITHUB_API_TOKEN}") +endif() + +set(GITHUB_API_URL "https://api.github.com") + +function(github_get_rate_limit VAR) + set(RATE_LIMIT_FILE ${CMAKE_CURRENT_BINARY_DIR}/GITHUBRATE) + set(RATE_LIMIT_URL ${GITHUB_API_URL}/rate_limit${GITHUB_AUTH}) + nodejs_download( + ${RATE_LIMIT_URL} + ${RATE_LIMIT_FILE} + ON + ) + file(READ ${RATE_LIMIT_FILE} RATE_LIMIT_DATA) + string(REGEX MATCH "\"remaining\": ([0-9]+)," + RATE_LIMIT_MATCH ${RATE_LIMIT_DATA}) + set(${VAR} ${CMAKE_MATCH_1} PARENT_SCOPE) +endfunction() + +mark_as_advanced( + GITHUB_AUTH + GITHUB_API_TOKEN + GITHUB_API_URL +) \ No newline at end of file diff --git a/cmake/nodejs/util/NodeJSUtil.cmake b/cmake/nodejs/util/NodeJSUtil.cmake new file mode 100644 index 00000000000..f1276be7ea9 --- /dev/null +++ b/cmake/nodejs/util/NodeJSUtil.cmake @@ -0,0 +1,166 @@ +function(nodejs_check_file FILE) + set(MESSAGE "File ${FILE} does not exist or is empty") + if(ARGC GREATER 1) + set(MESSAGE ${ARGV1}) + endif() + + # Make sure the file has contents + file(READ ${FILE} FILE_CONTENT LIMIT 1 HEX) + if(NOT FILE_CONTENT) + file(REMOVE ${FILE}) + message(FATAL_ERROR ${MESSAGE}) + endif() +endfunction() + +function(nodejs_download URL FILE) + # Function optionally takes a checksum and a checksum type, and + # a force value + # Either can be specified without the other, but checksum must come first + if(ARGC GREATER 2) + set(CHECKSUM ${ARGV2}) + if(CHECKSUM STREQUAL "On" OR CHECKSUM STREQUAL "ON" OR + CHECKSUM STREQUAL "True" OR CHECKSUM STREQUAL "TRUE" OR + CHECKSUM STREQUAL "Off" OR CHECKSUM STREQUAL "OFF" OR + CHECKSUM STREQUAL "False" OR CHECKSUM STREQUAL "FALSE") + set(FORCE ${CHECKSUM}) + unset(CHECKSUM) + elseif(ARGC GREATER 3) + set(TYPE ${ARGV3}) + else() + message(FATAL_ERROR "Checksum type must be specified") + endif() + elseif(ARGC GREATER 4) + set(CHECKSUM ${ARGV2}) + set(TYPE ${ARGV3}) + set(FORCE ${ARGV4}) + endif() + + # If the file exists, no need to download it again unless its being forced + if(NOT FORCE AND EXISTS ${FILE}) + return() + endif() + + # Download the file + message(STATUS "Downloading: ${URL}") + file(DOWNLOAD + ${URL} + ${FILE} + SHOW_PROGRESS + ) + + # Make sure the file has contents + nodejs_check_file(${FILE} "Unable to download ${URL}") + + # If a checksum is provided, validate the downloaded file + if(CHECKSUM) + message(STATUS "Validating: ${FILE}") + file(${TYPE} ${FILE} DOWNLOAD_CHECKSUM) + message(STATUS "Checksum: ${CHECKSUM}") + message(STATUS "Download: ${DOWNLOAD_CHECKSUM}") + if(NOT CHECKSUM STREQUAL DOWNLOAD_CHECKSUM) + file(REMOVE ${FILE}) + message(FATAL_ERROR "Validation failure: ${FILE}") + endif() + endif() +endfunction() + +function(nodejs_checksum DATA FILE VAR) + string(REGEX MATCH "([A-Fa-f0-9]+)[\t ]+${FILE}" CHECKSUM_MATCH ${DATA}) + if(CMAKE_MATCH_1) + set(${VAR} ${CMAKE_MATCH_1} PARENT_SCOPE) + else() + message(FATAL_ERROR "Unable to extract file checksum") + endif() +endfunction() + +function(nodejs_extract FILE DIR) + # Function optionally takes a force value + if(ARGC GREATER 2) + set(FORCE ${ARGV2}) + endif() + + # If the archvie has been extracted, no need to extract again unless it + # is being forced + if(NOT FORCE AND EXISTS ${DIR}) + return() + endif() + + # Make a temporary directory for extracting the output + set(EXTRACT_DIR ${CMAKE_CURRENT_BINARY_DIR}/extract) + if(EXISTS ${EXTRACT_DIR}) + file(REMOVE_RECURSE ${EXTRACT_DIR}) + endif() + file(MAKE_DIRECTORY ${EXTRACT_DIR}) + + # Extract the archive + execute_process( + COMMAND ${CMAKE_COMMAND} -E tar xfz ${FILE} + WORKING_DIRECTORY ${EXTRACT_DIR} + ) + + # If only one element is extracted, the archive contained a nested + # folder; use the inner folder as the extracted folder + file(GLOB EXTRACT_CHILDREN ${EXTRACT_DIR}/*) + list(LENGTH EXTRACT_CHILDREN NUM_CHILDREN) + set(TARGET_DIR ${EXTRACT_DIR}) + if(NUM_CHILDREN EQUAL 1) + list(GET EXTRACT_CHILDREN 0 TARGET_DIR) + endif() + + # Move the folder to the target path + if(EXISTS ${DIR}) + file(REMOVE_RECURSE ${DIR}) + endif() + file(RENAME ${TARGET_DIR} ${DIR}) + + # Make sure to clean up the extraction folder when the inner folder + # is used + file(REMOVE_RECURSE ${EXTRACT_DIR}) +endfunction() + +function(nodejs_find_module NAME BASE PATH) + # Find a node module using the same search path that require uses + # without needing a node binary + set(ROOT ${BASE}) + set(DRIVE "^[A-Za-z]?:?/$") + + # Walk up the directory tree until at the root + while(NOT ROOT MATCHES ${DRIVE} AND NOT + EXISTS ${ROOT}/node_modules/${NAME}) + get_filename_component(ROOT ${ROOT} DIRECTORY) + endwhile() + + # Operate like the CMake find_* functions, returning NOTFOUND if the + # module can't be found + if(ROOT MATCHES ${DRIVE}) + set(${PATH} ${NAME}-NOTFOUND PARENT_SCOPE) + else() + set(${PATH} ${ROOT}/node_modules/${NAME} PARENT_SCOPE) + endif() +endfunction() + +macro(nodejs_find_module_fallback NAME BASE PATH) + # Look in the provided path first + # If the module isn't found, try searching from the module + nodejs_find_module(${NAME} ${BASE} ${PATH}) + if(NOT ${PATH}) + nodejs_find_module(${NAME} ${NodeJS_MODULE_PATH} ${PATH}) + endif() +endmacro() + +function(nodejs_get_version URL VAR) + set(NWJS_LATEST_RELEASE_URL + "${NWJS_URL_BASE}/latest/${NodeJS_CHECKSUM_PATH}") + set(VERSION_FILE ${CMAKE_CURRENT_BINARY_DIR}/VERSION) + nodejs_download( + ${URL} + ${VERSION_FILE} + ON + ) + nodejs_check_file(${VERSION_FILE}) + file(READ ${VERSION_FILE} VERSION_DATA) + string(REGEX MATCH "v([0-9]+\.[0-9]+\.[0-9]+)" + VERSION_MATCH ${VERSION_DATA} + ) + set(${VAR} ${CMAKE_MATCH_1} PARENT_SCOPE) +endfunction() \ No newline at end of file diff --git a/cmake/nodejs/variants/Electron.cmake b/cmake/nodejs/variants/Electron.cmake new file mode 100644 index 00000000000..0087727e3e0 --- /dev/null +++ b/cmake/nodejs/variants/Electron.cmake @@ -0,0 +1,81 @@ +set(ELECTRON_VARIANT_BASE "electron") +set(ELECTRON_WIN32_BINARY_NAME "${ELECTRON_VARIANT_BASE}.exe") +list(APPEND NodeJS_WIN32_DELAYLOAD ${ELECTRON_WIN32_BINARY_NAME}) + +if(NodeJS_FIND_REQUIRED_ELECTRON OR + NodeJS_VARIANT STREQUAL ${ELECTRON_VARIANT_BASE}) + if(NodeJS_VERSION STREQUAL "latest") + include(util/Github) + github_get_rate_limit(GITHUB_RATE_LIMIT) + + # Handle determining the latest release + # Very complicated, due to electron not following the "latest" + # convention of other variants + set(ELECTRON_LATEST_RELEASE_FILE ${CMAKE_CURRENT_BINARY_DIR}/ELECTRON) + set(ELECTRON_LATEST_RELEASE_URL + ${GITHUB_API_URL}/repos/atom/electron/releases/latest${GITHUB_AUTH} + ) + if(GITHUB_RATE_LIMIT GREATER 0) + nodejs_download( + ${ELECTRON_LATEST_RELEASE_URL} + ${ELECTRON_LATEST_RELEASE_FILE} + ON + ) + endif() + nodejs_check_file( + ${ELECTRON_LATEST_RELEASE_FILE} + "Releases file could not be downloaded, likely \ + because github rate limit was exceeded. Wait until the limit \ + passes or set GITHUB_API_TOKEN in your environment to a valid \ + github developer token." + ) + file(READ ${ELECTRON_LATEST_RELEASE_FILE} ELECTRON_LATEST_RELEASE_DATA) + string(REGEX MATCH "\"tag_name\"\: \"v([0-9]+\.[0-9]+\.[0-9]+)\"" + ELECTRON_LATEST_RELEASE_MATCH ${ELECTRON_LATEST_RELEASE_DATA}) + set(NodeJS_VERSION ${CMAKE_MATCH_1}) + endif() + + set(NodeJS_VARIANT_NAME "Electron.js") + + # SHASUMS of any kind is inaccessible prior to 0.16.0 + if(NodeJS_VERSION VERSION_LESS 0.16.0) + message(FATAL_ERROR "Electron is only supported for versions >= 0.16.0") + endif() + + # Electron switched to IOJS after 0.25.0 + # Probably needs to be bounded on the upper side if/when they switch + # back to node mainline due to iojs-node merge + set(NodeJS_VARIANT_BASE "node") + if(NodeJS_VERSION VERSION_GREATER 0.25.0) + set(NodeJS_VARIANT_BASE "iojs") + endif() + + # Url is hard to get, because it will immediately resolve to a CDN + # Extracted from the electron website + set(NodeJS_URL + "https://atom.io/download/atom-shell/v${NodeJS_VERSION}" + ) + + # Headers become available for IOJS base ONLY! + # Variant base switch above handles this + set(NodeJS_HEADER_VERSION 0.30.1) + + # Header only archive uses source style paths + set(NodeJS_DEFAULT_INCLUDE False) + + # Hard to determine, but versions seem to start at 16, and SHA256 is + # available + set(NodeJS_SHA256_VERSION 0.15.9) + + # C++11 and Prefixing start after the IOJS switch + # Will carry forward after node mainline so no need for upper bound (whew) + set(NodeJS_PREFIX_VERSION 0.25.0) + set(NodeJS_CXX11R_VERSION 0.25.0) + + # The executable is not provided on the CDN + # In theory, I could support a BINARY_URL to get this from github + set(NodeJS_HAS_WIN32_BINARY False) + + # OpenSSL isn't included in the headers + set(NodeJS_HAS_OPENSSL False) +endif() \ No newline at end of file diff --git a/cmake/nodejs/variants/IOJS.cmake b/cmake/nodejs/variants/IOJS.cmake new file mode 100644 index 00000000000..7293e6c3a8b --- /dev/null +++ b/cmake/nodejs/variants/IOJS.cmake @@ -0,0 +1,25 @@ +set(IOJS_URL_BASE "https://iojs.org/dist") +set(IOJS_VARIANT_BASE "iojs") +set(IOJS_WIN32_BINARY_NAME "${IOJS_VARIANT_BASE}.exe") +list(APPEND NodeJS_WIN32_DELAYLOAD ${IOJS_WIN32_BINARY_NAME}) + +if(NodeJS_FIND_REQUIRED_IOJS OR NodeJS_VARIANT STREQUAL ${IOJS_VARIANT_BASE}) + if(NodeJS_VERSION STREQUAL "latest") + set(IOJS_LATEST_RELEASE_URL + "${IOJS_URL_BASE}/latest/SHASUMS256.txt") + nodejs_get_version(${IOJS_LATEST_RELEASE_URL} NodeJS_VERSION) + endif() + + set(NodeJS_VARIANT_NAME "io.js") + set(NodeJS_VARIANT_BASE ${IOJS_VARIANT_BASE}) + set(NodeJS_URL "${IOJS_URL_BASE}/v${NodeJS_VERSION}") + set(NodeJS_HEADER_VERSION 2.3.1) + set(NodeJS_WIN32_BINARY_NAME "${IOJS_WIN32_BINARY_NAME}") +endif() + +mark_as_advanced( + IOJS_URL_BASE + IOJS_VARIANT_BASE + IOJS_WIN32_BINARY_NAME + IOJS_LATEST_RELEASE_URL +) \ No newline at end of file diff --git a/cmake/nodejs/variants/NWJS.cmake b/cmake/nodejs/variants/NWJS.cmake new file mode 100644 index 00000000000..4ee7ac87f7e --- /dev/null +++ b/cmake/nodejs/variants/NWJS.cmake @@ -0,0 +1,30 @@ +set(NWJS_URL_BASE "http://dl.nwjs.io") +set(NWJS_VARIANT_BASE "nw") +set(NWJS_WIN32_BINARY_NAME "${NWJS_VARIANT_BASE}.exe") +list(APPEND NodeJS_WIN32_DELAYLOAD ${NWJS_WIN32_BINARY_NAME}) + +if(NodeJS_FIND_REQUIRED_NWJS OR NodeJS_VARIANT STREQUAL ${NWJS_VARIANT_BASE}) + set(NodeJS_CHECKSUM_PATH "MD5SUMS") + set(NodeJS_CHECKSUM_TYPE "MD5") + + if(NodeJS_VERSION STREQUAL "latest") + set(NWJS_LATEST_RELEASE_URL + "${NWJS_URL_BASE}/latest/${NodeJS_CHECKSUM_PATH}") + nodejs_get_version(${NWJS_LATEST_RELEASE_URL} NodeJS_VERSION) + endif() + + set(NodeJS_VARIANT_NAME "nw.js") + set(NodeJS_VARIANT_BASE ${NWJS_VARIANT_BASE}) + set(NodeJS_URL "${NWJS_URL_BASE}/v${NodeJS_VERSION}") + set(NodeJS_SOURCE_PATH "nw-headers-v${NodeJS_VERSION}.tar.gz") + set(NodeJS_DEFAULT_INCLUDE False) + set(NodeJS_HAS_WIN32_PREFIX False) + set(NodeJS_HAS_WIN32_BINARY False) +endif() + +mark_as_advanced( + NWJS_URL_BASE + NWJS_VARIANT_BASE + NWJS_WIN32_BINARY_NAME + NWJS_LATEST_RELEASE_URL +) \ No newline at end of file diff --git a/cmake/nodejs/variants/NodeJS.cmake b/cmake/nodejs/variants/NodeJS.cmake new file mode 100644 index 00000000000..c11827846dd --- /dev/null +++ b/cmake/nodejs/variants/NodeJS.cmake @@ -0,0 +1,131 @@ +set(NodeJS_URL_BASE http://nodejs.org/dist) +set(NodeJS_DEFAULT_VARIANT_BASE "node") +set(NodeJS_DEFAULT_WIN32_BINARY_NAME "${NodeJS_DEFAULT_VARIANT_BASE}.exe") +list(APPEND NodeJS_WIN32_DELAYLOAD ${NodeJS_DEFAULT_WIN32_BINARY_NAME}) + +if(NodeJS_VERSION STREQUAL "latest") + set(NodeJS_LATEST_RELEASE_URL + "${NodeJS_URL_BASE}/latest/SHASUMS256.txt") + nodejs_get_version(${NodeJS_LATEST_RELEASE_URL} NodeJS_VERSION) +endif() + +if(NOT NodeJS_VARIANT_NAME) + set(NodeJS_VARIANT_NAME ${NodeJS_DEFAULT_VARIANT_NAME}) +endif() +if(NOT NodeJS_VARIANT_BASE) + set(NodeJS_VARIANT_BASE ${NodeJS_DEFAULT_VARIANT_BASE}) +endif() +if(NOT NodeJS_URL) + set(NodeJS_URL "${NodeJS_URL_BASE}/v${NodeJS_VERSION}") +endif() + +if(NOT NodeJS_SOURCE_PATH) + set(NodeJS_SOURCE_PATH "${NodeJS_VARIANT_BASE}-v${NodeJS_VERSION}") + # Use the headers archive when its available + if(NodeJS_VERSION VERSION_GREATER ${NodeJS_HEADER_VERSION}) + set(NodeJS_SOURCE_PATH "${NodeJS_SOURCE_PATH}-headers") + endif() + set(NodeJS_SOURCE_PATH "${NodeJS_SOURCE_PATH}.tar.gz") +endif() + +if(NodeJS_DEFAULT_INCLUDE AND + NodeJS_VERSION VERSION_GREATER ${NodeJS_HEADER_VERSION}) + set(NodeJS_SOURCE_INCLUDE False) + set(NodeJS_HEADER_INCLUDE True) +endif() + +if(NodeJS_SOURCE_INCLUDE) + list(APPEND NodeJS_INCLUDE_PATHS + src + deps/uv/include + deps/v8/include + deps/zlib + ) + # OpenSSL is an optional header + if(NodeJS_HAS_OPENSSL) + list(APPEND NodeJS_INCLUDE_PATHS + deps/openssl/openssl/include + ) + endif() +endif() +if(NodeJS_HEADER_INCLUDE) + set(NodeJS_INCLUDE_PATHS include/node) +endif() + +if(NOT NodeJS_CHECKSUM_TYPE) + # Use SHA256 when available + if(NodeJS_VERSION VERSION_GREATER ${NodeJS_SHA256_VERSION}) + set(NodeJS_CHECKSUM_TYPE "SHA256") + else() + set(NodeJS_CHECKSUM_TYPE "SHA1") + endif() +endif() + +if(NOT NodeJS_CHECKSUM_PATH) + set(NodeJS_CHECKSUM_PATH "SHASUMS") + if(NodeJS_CHECKSUM_TYPE STREQUAL "SHA256") + set(NodeJS_CHECKSUM_PATH "${NodeJS_CHECKSUM_PATH}256") + endif() + set(NodeJS_CHECKSUM_PATH "${NodeJS_CHECKSUM_PATH}.txt") +endif() + +# Library and binary are based on variant base +if(NOT NodeJS_WIN32_LIBRARY_NAME) + set(NodeJS_WIN32_LIBRARY_NAME ${NodeJS_VARIANT_BASE}.lib) +endif() +if(NOT NodeJS_WIN32_BINARY_NAME) + set(NodeJS_WIN32_BINARY_NAME ${NodeJS_VARIANT_BASE}.exe) +endif() + +if(NOT NodeJS_WIN32_LIBRARY_PATH) + # The library location is prefixed after a specific version + if(NodeJS_HAS_WIN32_PREFIX AND + NodeJS_VERSION VERSION_GREATER ${NodeJS_PREFIX_VERSION}) + set(NodeJS_WIN32_LIBRARY_PATH "win-") + if(NodeJS_ARCH_IA32) + set(NodeJS_WIN32_LIBRARY_PATH "${NodeJS_WIN32_LIBRARY_PATH}x86/") + endif() + endif() + # 64-bit versions are prefixed + if(NodeJS_ARCH_X64) + set(NodeJS_WIN32_LIBRARY_PATH "${NodeJS_WIN32_LIBRARY_PATH}x64/") + endif() + set(NodeJS_WIN32_LIBRARY_PATH + "${NodeJS_WIN32_LIBRARY_PATH}${NodeJS_WIN32_LIBRARY_NAME}" + ) +endif() + +if(NodeJS_HAS_WIN32_BINARY AND NOT NodeJS_WIN32_BINARY_PATH) + # The executable location is prefixed after a specific version + if(NodeJS_HAS_WIN32_PREFIX AND + NodeJS_VERSION VERSION_GREATER ${NodeJS_PREFIX_VERSION}) + set(NodeJS_WIN32_BINARY_PATH "win-") + if(NodeJS_ARCH_IA32) + set(NodeJS_WIN32_BINARY_PATH "${NodeJS_WIN32_BINARY_PATH}x86/") + endif() + endif() + # 64-bit versions are prefixed + if(NodeJS_ARCH_X64) + set(NodeJS_WIN32_BINARY_PATH "${NodeJS_WIN32_BINARY_PATH}x64/") + endif() + set(NodeJS_WIN32_BINARY_PATH + "${NodeJS_WIN32_BINARY_PATH}${NodeJS_WIN32_BINARY_NAME}" + ) +endif() + +# Specify windows libraries +# XXX: This may need to be version/variant specific in the future +if(NodeJS_DEFAULT_LIBS AND NodeJS_PLATFORM_WIN32) + list(APPEND NodeJS_LIBRARIES + kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib + odbc32.lib DelayImp.lib + ) +endif() + +mark_as_advanced( + NodeJS_URL_BASE + NodeJS_DEFAULT_VARIANT_BASE + NodeJS_DEFAULT_WIN32_BINARY_NAME + NodeJS_LATEST_RELEASE_URL +) \ No newline at end of file diff --git a/docs/bindings/node/api.md b/docs/bindings/node/api.md new file mode 100644 index 00000000000..bbe8fcb45eb --- /dev/null +++ b/docs/bindings/node/api.md @@ -0,0 +1,333 @@ +# OSRM + +The `OSRM` method is the main constructor for creating an OSRM instance. An OSRM instance requires a `.osrm` network, +which is prepared by the OSRM Backend C++ library. + +You can create such a `.osrm` file by running the OSRM binaries we ship in `node_modules/osrm/lib/binding/` and default +profiles (e.g. for setting speeds and determining road types to route on) in `node_modules/osrm/profiles/`: + + node_modules/osrm/lib/binding/osrm-extract data.osm.pbf -p node_modules/osrm/profiles/car.lua + node_modules/osrm/lib/binding/osrm-contract data.osrm + +Consult the [osrm-backend](https://github.com/Project-OSRM/osrm-backend) documentation or further details. + +Once you have a complete `network.osrm` file, you can calculate networks in javascript with this library using the +methods below. To create an OSRM instance with your network you need to construct an instance like this: + +```javascript +var osrm = new OSRM('network.osrm'); +``` + +#### Methods + +| Service | Description | +| -------------------------- | --------------------------------------------------------- | +| [`osrm.route`](#route) | shortest path between given coordinates | +| [`osrm.nearest`](#nearest) | returns the nearest street segment for a given coordinate | +| [`osrm.table`](#table) | computes distance tables for given coordinates | +| [`osrm.match`](#match) | matches given coordinates to the road network | +| [`osrm.trip`](#trip) | Compute the shortest trip between given coordinates | +| [`osrm.tile`](#tile) | Return vector tiles containing debugging info | + +#### General Options + +Each OSRM method (except for `OSRM.tile()`) has set of general options as well as unique options, outlined below. + +| Option | Values | Description | Format | +| --------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | +| coordinates | `array` of `coordinate` elements: `[{coordinate}, ...]` | The coordinates this request will use. | `array` with `[{lon},{lat}]` values, in decimal degrees | +| bearings | `array` of `bearing` elements: `[{bearing}, ...]` | Limits the search to segments with given bearing in degrees towards true north in clockwise direction. | `null` or `array` with `[{value},{range}]` `integer 0 .. 360,integer 0 .. 180` | +| radiuses | `array` of `radius` elements: `[{radius}, ...]` | Limits the search to given radius in meters. | `null` or `double >= 0` or `unlimited` (default) | +| hints | `array` of `hint` elements: `[{hint}, ...]` | Hint to derive position in street network. | Base64 `string` | +| generate\_hints | `true` (default) or `false` | Adds a Hint to the response which can be used in subsequent requests, see `hints` parameter. | `Boolean` | + +## route + +Returns the fastest route between two or more coordinates while visiting the waypoints in order. + +**Parameters** + +- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object literal containing parameters for the route query. + - `options.alternatives` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)]** Search for alternative routes and return as well. _Please note that even if an alternative route is requested, a result cannot be guaranteed._ (optional, default `false`) + - `options.steps` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)]** Return route steps for each route leg. (optional, default `false`) + - `options.annotations` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)] or \[[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)>]** Return annotations for each route leg for duration, nodes, distance, weight, datasources and/or speed. Annotations can be `false` or `true` (no/full annotations) or an array of strings with `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed`. (optional, default `false`) + - `options.geometries` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** Returned route geometry format (influences overview and per step). Can also be `geojson`. (optional, default `polyline`) + - `options.overview` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** Add overview geometry either `full`, `simplified` according to highest zoom level it could be display on, or not at all (`false`). (optional, default `simplified`) + - `options.continue_straight` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)]** Forces the route to keep going straight at waypoints and don't do a uturn even if it would be faster. Default value depends on the profile. `null`/`true`/`false` +- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** + +**Examples** + +```javascript +var osrm = new OSRM("berlin-latest.osrm"); +osrm.route({coordinates: [[13.438640,52.519930], [13.415852, 52.513191]]}, function(err, result) { + if(err) throw err; + console.log(result.waypoints); // array of Waypoint objects representing all waypoints in order + console.log(result.routes); // array of Route objects ordered by descending recommendation rank +}); +``` + +Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** An array of [Waypoint](#waypoint) objects representing all waypoints in order AND an array of [`Route`](#route) objects ordered by descending recommendation rank. + +## nearest + +Snaps a coordinate to the street network and returns the nearest n matches. + +Note: `coordinates` in the general options only supports a single `{longitude},{latitude}` entry. + +**Parameters** + +- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object literal containing parameters for the nearest query. + - `options.number` **\[[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)]** Number of nearest segments that should be returned. + Must be an integer greater than or equal to `1`. (optional, default `1`) +- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** + +**Examples** + +```javascript +var osrm = new OSRM('network.osrm'); +var options = { + coordinates: [[13.388860,52.517037]], + number: 3, + bearings: [[0,20]] +}; +osrm.nearest(options, function(err, response) { + console.log(response.waypoints); // array of Waypoint objects +}); +``` + +Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** containing `waypoints`. +**`waypoints`**: array of [`Ẁaypoint`](#waypoint) objects sorted by distance to the input coordinate. +Each object has an additional `distance` property, which is the distance in meters to the supplied +input coordinate. + +## table + +Computes duration tables for the given locations. Allows for both symmetric and asymmetric tables. + +**Parameters** + +- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object literal containing parameters for the table query. + - `options.sources` **\[[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)]** An array of `index` elements (`0 <= integer < #coordinates`) to use + location with given index as source. Default is to use all. + - `options.destinations` **\[[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)]** An array of `index` elements (`0 <= integer < #coordinates`) to use location with given index as destination. Default is to use all. +- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** + +**Examples** + +```javascript +var osrm = new OSRM('network.osrm'); +var options = { + coordinates: [ + [13.388860,52.517037], + [13.397634,52.529407], + [13.428555,52.523219] + ] +}; +osrm.table(options, function(err, response) { + console.log(response.durations); // array of arrays, matrix in row-major order + console.log(response.sources); // array of Waypoint objects + console.log(response.destinations); // array of Waypoint objects +}); +``` + +Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** containing `durations`, `sources`, and `destinations`. +**`durations`**: array of arrays that stores the matrix in row-major order. `durations[i][j]` +gives the travel time from the i-th waypoint to the j-th waypoint. Values are given in seconds. +**`sources`**: array of [`Ẁaypoint`](#waypoint) objects describing all sources in order. +**`destinations`**: array of [`Ẁaypoint`](#waypoint) objects describing all destinations in order. + +## tile + +This generates [Mapbox Vector Tiles](https://mapbox.com/vector-tiles) that can be viewed with a +vector-tile capable slippy-map viewer. The tiles contain road geometries and metadata that can +be used to examine the routing graph. The tiles are generated directly from the data in-memory, +so are in sync with actual routing results, and let you examine which roads are actually routable, +and what weights they have applied. + +**Parameters** + +- `ZXY` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** an array consisting of `x`, `y`, and `z` values representing tile coordinates like + [wiki.openstreetmap.org/wiki/Slippy_map_tilenames](https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames) + and are supported by vector tile viewers like [Mapbox GL JS]\(. +- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** + +**Examples** + +```javascript +var osrm = new OSRM('network.osrm'); +osrm.tile([0, 0, 0], function(err, response) { + if (err) throw err; + fs.writeFileSync('./tile.vector.pbf', response); // write the buffer to a file +}); +``` + +Returns **[Buffer](https://nodejs.org/api/buffer.html)** contains a Protocol Buffer encoded vector tile. + +## match + +Map matching matches given GPS points to the road network in the most plausible way. +Please note the request might result multiple sub-traces. Large jumps in the timestamps +(>60s) or improbable transitions lead to trace splits if a complete matching could +not be found. The algorithm might not be able to match all points. Outliers are removed +if they can not be matched successfully. + +**Parameters** + +- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object literal containing parameters for the match query. + - `options.steps` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)]** Return route steps for each route. (optional, default `false`) + - `options.annotations` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)] or \[[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)>]** Return annotations for each route leg for duration, nodes, distance, weight, datasources and/or speed. Annotations can be `false` or `true` (no/full annotations) or an array of strings with `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed`. (optional, default `false`) + - `options.geometries` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** Returned route geometry format (influences overview + and per step). Can also be `geojson`. (optional, default `polyline`) + - `options.overview` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** Add overview geometry either `full`, `simplified` + according to highest zoom level it could be display on, or not at all (`false`). (optional, default `simplified`) + - `options.timestamps` **\[[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)>]** Timestamp of the input location (integers, UNIX-like timestamp). + - `options.radiuses` **\[[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)]** Standard deviation of GPS precision used for map matching. + If applicable use GPS accuracy (`double >= 0`, default `5m`). +- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** + +**Examples** + +```javascript +var osrm = new OSRM('network.osrm'); +var options = { + coordinates: [[13.393252,52.542648],[13.39478,52.543079],[13.397389,52.542107]], + timestamps: [1424684612, 1424684616, 1424684620] +}; +osrm.match(options, function(err, response) { + if (err) throw err; + console.log(response.tracepoints); // array of Waypoint objects + console.log(response.matchings); // array of Route objects +}); +``` + +Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** containing `tracepoints` and `matchings`. +**`tracepoints`** Array of [`Ẁaypoint`](#waypoint) objects representing all points of the trace in order. +If the trace point was ommited by map matching because it is an outlier, the entry will be null. Each +`Waypoint` object includes two additional properties, 1) `matchings_index`: Index to the +[`Route`](#route) object in matchings the sub-trace was matched to, 2) `waypoint_index`: Index of +the waypoint inside the matched route. +**`matchings`** is an array of [`Route`](#route) objects that +assemble the trace. Each `Route` object has an additional `confidence` property, which is the confidence of +the matching. float value between `0` and `1`. `1` is very confident that the matching is correct. + +## trip + +The trip plugin solves the Traveling Salesman Problem using a greedy heuristic (farthest-insertion algorithm). The returned path does not have to be the fastest path, as TSP is NP-hard it is only an approximation. Note that all input coordinates have to be connected for the trip service to work. + +**Parameters** +- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object literal containing parameters for the trip query. + - `options.roundtrip` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)]** Return route is a roundtrip. (optional, default `true`) + - `options.source` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** Return route starts at `any` coordinate. Can also be `first`. (optional, default `any`) + - `options.destination` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** Return route ends at `any` coordinate. Can also be `last`. (optional, default `any`) + - `options.steps` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)]** Return route steps for each route. (optional, default `false`) + - `options.annotations` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)] or \[[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)>]** Return annotations for each route leg for duration, nodes, distance, weight, datasources and/or speed. Annotations can be `false` or `true` (no/full annotations) or an array of strings with `duration`, `nodes`, `distance`, `weight`, `datasources`, `speed`. (optional, default `false`) + - `options.geometries` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** Returned route geometry format (influences overview + and per step). Can also be `geojson`. (optional, default `polyline`) + - `options.overview` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** Add overview geometry either `full`, `simplified` (optional, default `simplified`) +- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** + +**Fixing Start and End Points** + +It is possible to explicitly set the start or end coordinate of the trip. When source is set to `first`, the first coordinate is used as start coordinate of the trip in the output. When destination is set to `last`, the last coordinate will be used as destination of the trip in the returned output. If you specify `any`, any of the coordinates can be used as the first or last coordinate in the output. + +However, if `source=any&destination=any` the returned round-trip will still start at the first input coordinate by default. + +Currently, not all combinations of `roundtrip`, `source` and `destination` are supported. +Right now, the following combinations are possible: + +| roundtrip | source | destination | supported | +| :-- | :-- | :-- | :-- | +| true | first | last | **yes** | +| true | first | any | **yes** | +| true | any | last | **yes** | +| true | any | any | **yes** | +| false | first | last | **yes** | +| false | first | any | no | +| false | any | last | no | +| false | any | any | no | + +**Examples** + +Roundtrip Request +```javascript +var osrm = new OSRM('network.osrm'); +var options = { + coordinates: [ + [13.36761474609375, 52.51663871100423], + [13.374481201171875, 52.506191342034576] + ] +} +osrm.trip(options, function(err, response) { + if (err) throw err; + console.log(response.waypoints); // array of Waypoint objects + console.log(response.trips); // array of Route objects +}); +``` + +Non Roundtrip Request +```javascript +var osrm = new OSRM('network.osrm'); +var options = { + coordinates: [ + [13.36761474609375, 52.51663871100423], + [13.374481201171875, 52.506191342034576] + ], + source: "first", + destination: "last", + roundtrip: false +} +osrm.trip(options, function(err, response) { + if (err) throw err; + console.log(response.waypoints); // array of Waypoint objects + console.log(response.trips); // array of Route objects +}); +``` + +Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** containing `waypoints` and `trips`. +**`waypoints`**: an array of [`Ẁaypoint`](#waypoint) objects representing all waypoints in input order. +Each Waypoint object has the following additional properties, 1) `trips_index`: index to trips of the +sub-trip the point was matched to, and 2) `waypoint_index`: index of the point in the trip. +**`trips`**: an array of [`Route`](#route) objects that assemble the trace. + +# Responses + +Responses + +## Route + +Represents a route through (potentially multiple) waypoints. + +**Parameters** + +- `exteral` **documentation** in [`osrm-backend`](https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md#route) + +## RouteLeg + +Represents a route between two waypoints. + +**Parameters** + +- `exteral` **documentation** in [`osrm-backend`](https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md#routeleg) + +## RouteStep + +A step consists of a maneuver such as a turn or merge, followed by a distance of travel along a single way to the subsequent step. + +**Parameters** + +- `exteral` **documentation** in [`osrm-backend`](https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md#routestep) + +## StepManeuver + +**Parameters** + +- `exteral` **documentation** in [`osrm-backend`](https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md#stepmanuever) + +## Waypoint + +Object used to describe waypoint on a route. + +**Parameters** + +- `exteral` **documentation** in [`osrm-backend`](https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md#waypoint) diff --git a/docs/bindings/node/releasing.md b/docs/bindings/node/releasing.md new file mode 100644 index 00000000000..63c49f5cbc3 --- /dev/null +++ b/docs/bindings/node/releasing.md @@ -0,0 +1,86 @@ +# Releasing + +Releasing a new version of `node-osrm` is mostly automated using Travis CI. + +The version of `node-osrm` is locked to the same version as `osrm-backend`. Every `node-osrm` should have a `osrm-backend` release of the same version. Of course, only release a `node-osrm` after the release has been tagged in `osrm-backend`. + +These steps all happen on `master`. After the release is out, create a branch using the MAJOR.MINOR version of the release to document code changes made for that version. + +### Steps to release + +1. Update the `osrm_release` field in `package.json` to the corresonding git tag in `osrm-backend.` + + Confirm the desired OSRM branch and commit to `master`. + +1. Bump node-osrm version + + Update the `CHANGELOG.md` and the `package.json` version if needed. + +1. Check that Travis CI [builds are passing](https://travis-ci.org/Project-OSRM/node-osrm) for the latest commit on `master`. + +1. Publishing binaries + + If travis builds are passing then it's time to publish binaries by committing with a message containing `[publish binary]`. Use an empty commit for this. + + ``` + git commit --allow-empty -m "[publish binary] vMAJOR.MINOR.PATCH" + ``` + +1. Test + + Locally you can now test binaries. Cleanup, re-install, and run the tests like: + + ``` + make clean + npm install # will pull remote binaries + npm ls # confirm deps are correct + make test + ``` + +1. Tag + + Once binaries are published for Linux and OS X then its time to tag a new release and add the changelog to the tag: + + ``` + git tag vMAJOR.MINOR.PATCH -a + git push --tags + ``` + +1. Publish node-osrm. **we only do this for stable releases** + + First ensure your local `node-pre-gyp` is up to date: + + ``` + npm ls + ``` + + This is important because it is bundled during packaging. + + If you see any errors then do: + + ``` + rm -rf node_modules/node-pre-gyp + npm install node-pre-gyp + ``` + + Now we're ready to publish `node-osrm` to : + + ``` + npm publish + ``` + + Dependent apps can now pull from the npm registry like: + + ``` + "dependencies": { + "osrm": "^MAJOR.MINOR.PATCH" + } + ``` + + Or can still pull from the github tag like: + + ``` + "dependencies": { + "osrm": "https://github.com/Project-OSRM/node-osrm/archive/vMAJOR.MINOR.PATCH.tar.gz" + } + ``` diff --git a/docs/testing.md b/docs/testing.md index 884ff848863..5f608fb6ef9 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -46,12 +46,6 @@ You should see the compiled binaries in `build/unit_tests`, you can then run eac ./engine-tests ``` -For `library-tests` you will need to provide a path to the test data: - -``` -./library-tests ../../test/data/monaco.osrm -``` - ## Cucumber For a general introduction on cucumber in our testsuite, have a look at [the wiki](https://github.com/Project-OSRM/osrm-backend/wiki/Cucumber-Test-Suite). diff --git a/example/example.cpp b/example/example.cpp index 3ad13e6b2bb..711ae317ac3 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -39,9 +39,9 @@ int main(int argc, const char *argv[]) // The following shows how to use the Route service; configure this service RouteParameters params; - // Route in monaco - params.coordinates.push_back({util::FloatLongitude{7.419758}, util::FloatLatitude{43.731142}}); - params.coordinates.push_back({util::FloatLongitude{7.419505}, util::FloatLatitude{43.736825}}); + // Route in Berlin: Alexanderplatz to Hackescher Markt + params.coordinates.push_back({util::FloatLongitude{13.414307}, util::FloatLatitude{52.521835}}); + params.coordinates.push_back({util::FloatLongitude{13.402290}, util::FloatLatitude{52.523728}}); // Response is in JSON format json::Object result; @@ -59,7 +59,7 @@ int main(int argc, const char *argv[]) const auto duration = route.values["duration"].get().value; // Warn users if extract does not contain the default Berlin coordinates from above - if (distance == 0 or duration == 0) + if (distance == 0 || duration == 0) { std::cout << "Note: distance or duration is zero. "; std::cout << "You are probably doing a query outside of the OSM extract.\n\n"; diff --git a/example/example.js b/example/example.js new file mode 100644 index 00000000000..516f02a766f --- /dev/null +++ b/example/example.js @@ -0,0 +1,32 @@ +process.env.UV_THREADPOOL_SIZE = Math.ceil(require('os').cpus().length * 1.5); + +var express = require('express'); +var OSRM = require('..'); +var path = require('path'); + +var app = express(); +var osrm = new OSRM(path.join(__dirname,"../test/data/berlin_CH.osrm")); + +// Accepts a query like: +// http://localhost:8888?start=13.414307,52.521835&end=13.402290,52.523728 +app.get('/', function(req, res) { + if (!req.query.start || !req.query.end) { + return res.json({"error":"invalid start and end query"}); + } + var coordinates = []; + var start = req.query.start.split(','); + coordinates.push([+start[0],+start[1]]); + var end = req.query.end.split(','); + coordinates.push([+end[0],+end[1]]); + var query = { + coordinates: coordinates, + alternateRoute: req.query.alternatives !== 'false' + }; + osrm.route(query, function(err, result) { + if (err) return res.json({"error":err.message}); + return res.json(result); + }); +}); + +console.log('Listening on port: ' + 8888); +app.listen(8888); diff --git a/include/nodejs/json_v8_renderer.hpp b/include/nodejs/json_v8_renderer.hpp new file mode 100644 index 00000000000..bfe7b4060f4 --- /dev/null +++ b/include/nodejs/json_v8_renderer.hpp @@ -0,0 +1,65 @@ +#ifndef OSRM_BINDINGS_NODE_JSON_V8_RENDERER_HPP +#define OSRM_BINDINGS_NODE_JSON_V8_RENDERER_HPP + +#include "osrm/json_container.hpp" + +#include + +#include + +namespace node_osrm +{ + +struct V8Renderer +{ + explicit V8Renderer(v8::Local &_out) : out(_out) {} + + void operator()(const osrm::json::String &string) const + { + out = Nan::New(std::cref(string.value)).ToLocalChecked(); + } + + void operator()(const osrm::json::Number &number) const { out = Nan::New(number.value); } + + void operator()(const osrm::json::Object &object) const + { + v8::Local obj = Nan::New(); + for (const auto &keyValue : object.values) + { + v8::Local child; + mapbox::util::apply_visitor(V8Renderer(child), keyValue.second); + obj->Set(Nan::New(keyValue.first).ToLocalChecked(), child); + } + out = obj; + } + + void operator()(const osrm::json::Array &array) const + { + v8::Local a = Nan::New(array.values.size()); + for (auto i = 0u; i < array.values.size(); ++i) + { + v8::Local child; + mapbox::util::apply_visitor(V8Renderer(child), array.values[i]); + a->Set(i, child); + } + out = a; + } + + void operator()(const osrm::json::True &) const { out = Nan::New(true); } + + void operator()(const osrm::json::False &) const { out = Nan::New(false); } + + void operator()(const osrm::json::Null &) const { out = Nan::Null(); } + + private: + v8::Local &out; +}; + +inline void renderToV8(v8::Local &out, const osrm::json::Object &object) +{ + osrm::json::Value value = object; + mapbox::util::apply_visitor(V8Renderer(out), value); +} +} + +#endif // JSON_V8_RENDERER_HPP diff --git a/include/nodejs/node_osrm.hpp b/include/nodejs/node_osrm.hpp new file mode 100644 index 00000000000..c82a5423533 --- /dev/null +++ b/include/nodejs/node_osrm.hpp @@ -0,0 +1,41 @@ +#ifndef OSRM_BINDINGS_NODE_HPP +#define OSRM_BINDINGS_NODE_HPP + +#include "osrm/osrm_fwd.hpp" + +#include + +#include + +namespace node_osrm +{ + +struct Engine final : public Nan::ObjectWrap +{ + using Base = Nan::ObjectWrap; + + static NAN_MODULE_INIT(Init); + + static NAN_METHOD(New); + + static NAN_METHOD(route); + static NAN_METHOD(nearest); + static NAN_METHOD(table); + static NAN_METHOD(tile); + static NAN_METHOD(match); + static NAN_METHOD(trip); + + Engine(osrm::EngineConfig &config); + + // Thread-safe singleton accessor + static Nan::Persistent &constructor(); + + // Ref-counted OSRM alive even after shutdown until last callback is done + std::shared_ptr this_; +}; + +} // ns node_osrm + +NODE_MODULE(osrm, node_osrm::Engine::Init) + +#endif diff --git a/include/nodejs/node_osrm_support.hpp b/include/nodejs/node_osrm_support.hpp new file mode 100644 index 00000000000..2ddde9a36a1 --- /dev/null +++ b/include/nodejs/node_osrm_support.hpp @@ -0,0 +1,922 @@ +#ifndef OSRM_BINDINGS_NODE_SUPPORT_HPP +#define OSRM_BINDINGS_NODE_SUPPORT_HPP + +#include "nodejs/json_v8_renderer.hpp" + +#include "osrm/bearing.hpp" +#include "osrm/coordinate.hpp" +#include "osrm/engine_config.hpp" +#include "osrm/json_container.hpp" +#include "osrm/match_parameters.hpp" +#include "osrm/nearest_parameters.hpp" +#include "osrm/osrm.hpp" +#include "osrm/route_parameters.hpp" +#include "osrm/status.hpp" +#include "osrm/storage_config.hpp" +#include "osrm/table_parameters.hpp" +#include "osrm/tile_parameters.hpp" +#include "osrm/trip_parameters.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace node_osrm +{ + +using engine_config_ptr = std::unique_ptr; +using route_parameters_ptr = std::unique_ptr; +using trip_parameters_ptr = std::unique_ptr; +using tile_parameters_ptr = std::unique_ptr; +using match_parameters_ptr = std::unique_ptr; +using nearest_parameters_ptr = std::unique_ptr; +using table_parameters_ptr = std::unique_ptr; + +template inline v8::Local render(const ResultT &result); + +template <> v8::Local inline render(const std::string &result) +{ + return Nan::CopyBuffer(result.data(), result.size()).ToLocalChecked(); +} + +template <> v8::Local inline render(const osrm::json::Object &result) +{ + v8::Local value; + renderToV8(value, result); + return value; +} + +inline void ParseResult(const osrm::Status &result_status, osrm::json::Object &result) +{ + const auto code_iter = result.values.find("code"); + const auto end_iter = result.values.end(); + + BOOST_ASSERT(code_iter != end_iter); + + if (result_status == osrm::Status::Error) + { + throw std::logic_error(code_iter->second.get().value.c_str()); + } + + result.values.erase(code_iter); + const auto message_iter = result.values.find("message"); + if (message_iter != end_iter) + { + result.values.erase(message_iter); + } +} + +inline void ParseResult(const osrm::Status &result_status, const std::string & /*unused*/) {} + +inline engine_config_ptr argumentsToEngineConfig(const Nan::FunctionCallbackInfo &args) +{ + Nan::HandleScope scope; + auto engine_config = boost::make_unique(); + + if (args.Length() == 0) + { + return engine_config; + } + else if (args.Length() > 1) + { + Nan::ThrowError("Only accepts one parameter"); + return engine_config_ptr(); + } + + BOOST_ASSERT(args.Length() == 1); + + if (args[0]->IsString()) + { + engine_config->storage_config = osrm::StorageConfig( + *v8::String::Utf8Value(Nan::To(args[0]).ToLocalChecked())); + engine_config->use_shared_memory = false; + return engine_config; + } + else if (!args[0]->IsObject()) + { + Nan::ThrowError("Parameter must be a path or options object"); + return engine_config_ptr(); + } + + BOOST_ASSERT(args[0]->IsObject()); + auto params = Nan::To(args[0]).ToLocalChecked(); + + auto path = params->Get(Nan::New("path").ToLocalChecked()); + auto shared_memory = params->Get(Nan::New("shared_memory").ToLocalChecked()); + if (!path->IsUndefined()) + { + engine_config->storage_config = + osrm::StorageConfig(*v8::String::Utf8Value(Nan::To(path).ToLocalChecked())); + } + if (!shared_memory->IsUndefined()) + { + if (shared_memory->IsBoolean()) + { + engine_config->use_shared_memory = Nan::To(shared_memory).FromJust(); + } + else + { + Nan::ThrowError("Shared_memory option must be a boolean"); + return engine_config_ptr(); + } + } + + if (path->IsUndefined() && !engine_config->use_shared_memory) + { + Nan::ThrowError("Shared_memory must be enabled if no path is " + "specified"); + return engine_config_ptr(); + } + + return engine_config; +} + +inline boost::optional> +parseCoordinateArray(const v8::Local &coordinates_array) +{ + Nan::HandleScope scope; + boost::optional> resulting_coordinates; + std::vector temp_coordinates; + + for (uint32_t i = 0; i < coordinates_array->Length(); ++i) + { + v8::Local coordinate = coordinates_array->Get(i); + + if (!coordinate->IsArray()) + { + Nan::ThrowError("Coordinates must be an array of (lon/lat) pairs"); + return resulting_coordinates; + } + + v8::Local coordinate_pair = v8::Local::Cast(coordinate); + if (coordinate_pair->Length() != 2) + { + Nan::ThrowError("Coordinates must be an array of (lon/lat) pairs"); + return resulting_coordinates; + } + + if (!coordinate_pair->Get(0)->IsNumber() || !coordinate_pair->Get(1)->IsNumber()) + { + Nan::ThrowError("Each member of a coordinate pair must be a number"); + return resulting_coordinates; + } + + double lon = coordinate_pair->Get(0)->NumberValue(); + double lat = coordinate_pair->Get(1)->NumberValue(); + + if (std::isnan(lon) || std::isnan(lat) || std::isinf(lon) || std::isinf(lat)) + { + Nan::ThrowError("Lng/Lat coordinates must be valid numbers"); + return resulting_coordinates; + } + + if (lon > 180 || lon < -180 || lat > 90 || lat < -90) + { + Nan::ThrowError("Lng/Lat coordinates must be within world bounds " + "(-180 < lng < 180, -90 < lat < 90)"); + return resulting_coordinates; + } + + temp_coordinates.emplace_back(osrm::util::FloatLongitude{std::move(lon)}, + osrm::util::FloatLatitude{std::move(lat)}); + } + + resulting_coordinates = boost::make_optional(std::move(temp_coordinates)); + return resulting_coordinates; +} + +// Parses all the non-service specific parameters +template +inline bool argumentsToParameter(const Nan::FunctionCallbackInfo &args, + ParamType ¶ms, + bool requires_multiple_coordinates) +{ + Nan::HandleScope scope; + + if (args.Length() < 2) + { + Nan::ThrowTypeError("Two arguments required"); + return false; + } + + if (!args[0]->IsObject()) + { + Nan::ThrowTypeError("First arg must be an object"); + return false; + } + + v8::Local obj = Nan::To(args[0]).ToLocalChecked(); + + v8::Local coordinates = obj->Get(Nan::New("coordinates").ToLocalChecked()); + if (coordinates->IsUndefined()) + { + Nan::ThrowError("Must provide a coordinates property"); + return false; + } + else if (coordinates->IsArray()) + { + auto coordinates_array = v8::Local::Cast(coordinates); + if (coordinates_array->Length() < 2 && requires_multiple_coordinates) + { + Nan::ThrowError("At least two coordinates must be provided"); + return false; + } + else if (!requires_multiple_coordinates && coordinates_array->Length() != 1) + { + Nan::ThrowError("Exactly one coordinate pair must be provided"); + return false; + } + auto maybe_coordinates = parseCoordinateArray(coordinates_array); + if (maybe_coordinates) + { + std::copy(maybe_coordinates->begin(), + maybe_coordinates->end(), + std::back_inserter(params->coordinates)); + } + else + { + return false; + } + } + else if (!coordinates->IsUndefined()) + { + BOOST_ASSERT(!coordinates->IsArray()); + Nan::ThrowError("Coordinates must be an array of (lon/lat) pairs"); + return false; + } + + if (obj->Has(Nan::New("bearings").ToLocalChecked())) + { + v8::Local bearings = obj->Get(Nan::New("bearings").ToLocalChecked()); + + if (!bearings->IsArray()) + { + Nan::ThrowError("Bearings must be an array of arrays of numbers"); + return false; + } + + auto bearings_array = v8::Local::Cast(bearings); + + if (bearings_array->Length() != params->coordinates.size()) + { + Nan::ThrowError("Bearings array must have the same length as coordinates array"); + return false; + } + + for (uint32_t i = 0; i < bearings_array->Length(); ++i) + { + v8::Local bearing_raw = bearings_array->Get(i); + + if (bearing_raw->IsNull()) + { + params->bearings.emplace_back(); + } + else if (bearing_raw->IsArray()) + { + auto bearing_pair = v8::Local::Cast(bearing_raw); + if (bearing_pair->Length() == 2) + { + if (!bearing_pair->Get(0)->IsNumber() || !bearing_pair->Get(1)->IsNumber()) + { + Nan::ThrowError("Bearing values need to be numbers in range 0..360"); + return false; + } + + const auto bearing = static_cast(bearing_pair->Get(0)->NumberValue()); + const auto range = static_cast(bearing_pair->Get(1)->NumberValue()); + + if (bearing < 0 || bearing > 360 || range < 0 || range > 180) + { + Nan::ThrowError("Bearing values need to be in range 0..360, 0..180"); + return false; + } + + params->bearings.push_back(osrm::Bearing{bearing, range}); + } + else + { + Nan::ThrowError("Bearing must be an array of [bearing, range] or null"); + return false; + } + } + else + { + Nan::ThrowError("Bearing must be an array of [bearing, range] or null"); + return false; + } + } + } + + if (obj->Has(Nan::New("hints").ToLocalChecked())) + { + v8::Local hints = obj->Get(Nan::New("hints").ToLocalChecked()); + + if (!hints->IsArray()) + { + Nan::ThrowError("Hints must be an array of strings/null"); + return false; + } + + v8::Local hints_array = v8::Local::Cast(hints); + + if (hints_array->Length() != params->coordinates.size()) + { + Nan::ThrowError("Hints array must have the same length as coordinates array"); + return false; + } + + for (uint32_t i = 0; i < hints_array->Length(); ++i) + { + v8::Local hint = hints_array->Get(i); + if (hint->IsString()) + { + if (hint->ToString()->Length() == 0) + { + Nan::ThrowError("Hint cannot be an empty string"); + return false; + } + + params->hints.push_back( + osrm::engine::Hint::FromBase64(*v8::String::Utf8Value(hint))); + } + else if (hint->IsNull()) + { + params->hints.emplace_back(); + } + else + { + Nan::ThrowError("Hint must be null or string"); + return false; + } + } + } + + if (obj->Has(Nan::New("radiuses").ToLocalChecked())) + { + v8::Local radiuses = obj->Get(Nan::New("radiuses").ToLocalChecked()); + + if (!radiuses->IsArray()) + { + Nan::ThrowError("Radiuses must be an array of non-negative doubles or null"); + return false; + } + + v8::Local radiuses_array = v8::Local::Cast(radiuses); + + if (radiuses_array->Length() != params->coordinates.size()) + { + Nan::ThrowError("Radiuses array must have the same length as coordinates array"); + return false; + } + + for (uint32_t i = 0; i < radiuses_array->Length(); ++i) + { + v8::Local radius = radiuses_array->Get(i); + if (radius->IsNull()) + { + params->radiuses.emplace_back(); + } + else if (radius->IsNumber() && radius->NumberValue() >= 0) + { + params->radiuses.push_back(static_cast(radius->NumberValue())); + } + else + { + Nan::ThrowError("Radius must be non-negative double or null"); + return false; + } + } + } + + if (obj->Has(Nan::New("generate_hints").ToLocalChecked())) + { + v8::Local generate_hints = obj->Get(Nan::New("generate_hints").ToLocalChecked()); + + if (!generate_hints->IsBoolean()) + { + Nan::ThrowError("generate_hints must be of type Boolean"); + return false; + } + + params->generate_hints = generate_hints->BooleanValue(); + } + + return true; +} + +template +inline bool parseCommonParameters(const v8::Local &obj, ParamType ¶ms) +{ + if (obj->Has(Nan::New("steps").ToLocalChecked())) + { + auto steps = obj->Get(Nan::New("steps").ToLocalChecked()); + if (steps->IsBoolean()) + { + params->steps = steps->BooleanValue(); + } + else + { + Nan::ThrowError("'steps' param must be a boolean"); + return false; + } + } + + if (obj->Has(Nan::New("annotations").ToLocalChecked())) + { + auto annotations = obj->Get(Nan::New("annotations").ToLocalChecked()); + if (annotations->IsBoolean()) + { + params->annotations = annotations->BooleanValue(); + } + else if (annotations->IsArray()) + { + v8::Local annotations_array = v8::Local::Cast(annotations); + for (std::size_t i = 0; i < annotations_array->Length(); i++) + { + const Nan::Utf8String annotations_utf8str(annotations_array->Get(i)); + std::string annotations_str{*annotations_utf8str, + *annotations_utf8str + annotations_utf8str.length()}; + + if (annotations_str == "duration") + { + params->annotations_type = + params->annotations_type | osrm::RouteParameters::AnnotationsType::Duration; + } + else if (annotations_str == "nodes") + { + params->annotations_type = + params->annotations_type | osrm::RouteParameters::AnnotationsType::Nodes; + } + else if (annotations_str == "distance") + { + params->annotations_type = + params->annotations_type | osrm::RouteParameters::AnnotationsType::Distance; + } + else if (annotations_str == "weight") + { + params->annotations_type = + params->annotations_type | osrm::RouteParameters::AnnotationsType::Weight; + } + else if (annotations_str == "datasources") + { + params->annotations_type = params->annotations_type | + osrm::RouteParameters::AnnotationsType::Datasources; + } + else if (annotations_str == "speed") + { + params->annotations_type = + params->annotations_type | osrm::RouteParameters::AnnotationsType::Speed; + } + else + { + Nan::ThrowError("this 'annotations' param is not supported"); + return false; + } + } + } + else + { + Nan::ThrowError("this 'annotations' param is not supported"); + return false; + } + } + + if (obj->Has(Nan::New("geometries").ToLocalChecked())) + { + v8::Local geometries = obj->Get(Nan::New("geometries").ToLocalChecked()); + + if (!geometries->IsString()) + { + Nan::ThrowError("Geometries must be a string: [polyline, polyline6, geojson]"); + return false; + } + const Nan::Utf8String geometries_utf8str(geometries); + std::string geometries_str{*geometries_utf8str, + *geometries_utf8str + geometries_utf8str.length()}; + + if (geometries_str == "polyline") + { + params->geometries = osrm::RouteParameters::GeometriesType::Polyline; + } + else if (geometries_str == "polyline6") + { + params->geometries = osrm::RouteParameters::GeometriesType::Polyline6; + } + else if (geometries_str == "geojson") + { + params->geometries = osrm::RouteParameters::GeometriesType::GeoJSON; + } + else + { + Nan::ThrowError("'geometries' param must be one of [polyline, polyline6, geojson]"); + return false; + } + } + + if (obj->Has(Nan::New("overview").ToLocalChecked())) + { + v8::Local overview = obj->Get(Nan::New("overview").ToLocalChecked()); + + if (!overview->IsString()) + { + Nan::ThrowError("Overview must be a string: [simplified, full, false]"); + return false; + } + + const Nan::Utf8String overview_utf8str(overview); + std::string overview_str{*overview_utf8str, *overview_utf8str + overview_utf8str.length()}; + + if (overview_str == "simplified") + { + params->overview = osrm::RouteParameters::OverviewType::Simplified; + } + else if (overview_str == "full") + { + params->overview = osrm::RouteParameters::OverviewType::Full; + } + else if (overview_str == "false") + { + params->overview = osrm::RouteParameters::OverviewType::False; + } + else + { + Nan::ThrowError("'overview' param must be one of [simplified, full, false]"); + return false; + } + } + + return true; +} + +inline route_parameters_ptr +argumentsToRouteParameter(const Nan::FunctionCallbackInfo &args, + bool requires_multiple_coordinates) +{ + route_parameters_ptr params = boost::make_unique(); + bool has_base_params = argumentsToParameter(args, params, requires_multiple_coordinates); + if (!has_base_params) + return route_parameters_ptr(); + + v8::Local obj = Nan::To(args[0]).ToLocalChecked(); + + if (obj->Has(Nan::New("continue_straight").ToLocalChecked())) + { + auto value = obj->Get(Nan::New("continue_straight").ToLocalChecked()); + if (!value->IsBoolean() && !value->IsNull()) + { + Nan::ThrowError("'continue_straight' parama must be boolean or null"); + } + if (value->IsBoolean()) + { + params->continue_straight = value->BooleanValue(); + } + } + + if (obj->Has(Nan::New("alternatives").ToLocalChecked())) + { + auto value = obj->Get(Nan::New("alternatives").ToLocalChecked()); + if (!value->IsBoolean()) + { + Nan::ThrowError("'alternatives' parama must be boolean"); + } + params->alternatives = value->BooleanValue(); + } + + bool parsedSuccessfully = parseCommonParameters(obj, params); + if (!parsedSuccessfully) + { + return route_parameters_ptr(); + } + + return params; +} + +inline tile_parameters_ptr +argumentsToTileParameters(const Nan::FunctionCallbackInfo &args, bool /*unused*/) +{ + tile_parameters_ptr params = boost::make_unique(); + + if (args.Length() < 2) + { + Nan::ThrowTypeError("Coordinate object and callback required"); + return tile_parameters_ptr(); + } + + if (!args[0]->IsArray()) + { + Nan::ThrowTypeError("Parameter must be an array [x, y, z]"); + return tile_parameters_ptr(); + } + + v8::Local array = v8::Local::Cast(args[0]); + + if (array->Length() != 3) + { + Nan::ThrowTypeError("Parameter must be an array [x, y, z]"); + return tile_parameters_ptr(); + } + + v8::Local x = array->Get(0); + v8::Local y = array->Get(1); + v8::Local z = array->Get(2); + + if (!x->IsUint32() && !x->IsUndefined()) + { + Nan::ThrowError("Tile x coordinate must be unsigned interger"); + return tile_parameters_ptr(); + } + if (!y->IsUint32() && !y->IsUndefined()) + { + Nan::ThrowError("Tile y coordinate must be unsigned interger"); + return tile_parameters_ptr(); + } + if (!z->IsUint32() && !z->IsUndefined()) + { + Nan::ThrowError("Tile z coordinate must be unsigned interger"); + return tile_parameters_ptr(); + } + + params->x = x->Uint32Value(); + params->y = y->Uint32Value(); + params->z = z->Uint32Value(); + + if (!params->IsValid()) + { + Nan::ThrowError("Invalid tile coordinates"); + return tile_parameters_ptr(); + } + + return params; +} + +inline nearest_parameters_ptr +argumentsToNearestParameter(const Nan::FunctionCallbackInfo &args, + bool requires_multiple_coordinates) +{ + nearest_parameters_ptr params = boost::make_unique(); + bool has_base_params = argumentsToParameter(args, params, requires_multiple_coordinates); + if (!has_base_params) + return nearest_parameters_ptr(); + + v8::Local obj = Nan::To(args[0]).ToLocalChecked(); + + if (obj->Has(Nan::New("number").ToLocalChecked())) + { + v8::Local number = obj->Get(Nan::New("number").ToLocalChecked()); + + if (!number->IsUint32()) + { + Nan::ThrowError("Number must be an integer greater than or equal to 1"); + return nearest_parameters_ptr(); + } + else + { + unsigned number_value = static_cast(number->NumberValue()); + + if (number_value < 1) + { + Nan::ThrowError("Number must be an integer greater than or equal to 1"); + return nearest_parameters_ptr(); + } + + params->number_of_results = static_cast(number->NumberValue()); + } + } + + return params; +} + +inline table_parameters_ptr +argumentsToTableParameter(const Nan::FunctionCallbackInfo &args, + bool requires_multiple_coordinates) +{ + table_parameters_ptr params = boost::make_unique(); + bool has_base_params = argumentsToParameter(args, params, requires_multiple_coordinates); + if (!has_base_params) + return table_parameters_ptr(); + + v8::Local obj = Nan::To(args[0]).ToLocalChecked(); + + if (obj->Has(Nan::New("sources").ToLocalChecked())) + { + v8::Local sources = obj->Get(Nan::New("sources").ToLocalChecked()); + + if (!sources->IsArray()) + { + Nan::ThrowError("Sources must be an array of indices (or undefined)"); + return table_parameters_ptr(); + } + + v8::Local sources_array = v8::Local::Cast(sources); + for (uint32_t i = 0; i < sources_array->Length(); ++i) + { + v8::Local source = sources_array->Get(i); + if (source->IsUint32()) + { + size_t source_value = static_cast(source->NumberValue()); + if (source_value > params->coordinates.size()) + { + Nan::ThrowError( + "Source indices must be less than or equal to the number of coordinates"); + return table_parameters_ptr(); + } + + params->sources.push_back(static_cast(source->NumberValue())); + } + else + { + Nan::ThrowError("Source must be an integer"); + return table_parameters_ptr(); + } + } + } + + if (obj->Has(Nan::New("destinations").ToLocalChecked())) + { + v8::Local destinations = obj->Get(Nan::New("destinations").ToLocalChecked()); + + if (!destinations->IsArray()) + { + Nan::ThrowError("Destinations must be an array of indices (or undefined)"); + return table_parameters_ptr(); + } + + v8::Local destinations_array = v8::Local::Cast(destinations); + for (uint32_t i = 0; i < destinations_array->Length(); ++i) + { + v8::Local destination = destinations_array->Get(i); + if (destination->IsUint32()) + { + size_t destination_value = static_cast(destination->NumberValue()); + if (destination_value > params->coordinates.size()) + { + Nan::ThrowError("Destination indices must be less than or equal to the number " + "of coordinates"); + return table_parameters_ptr(); + } + + params->destinations.push_back(static_cast(destination->NumberValue())); + } + else + { + Nan::ThrowError("Destination must be an integer"); + return table_parameters_ptr(); + } + } + } + + return params; +} + +inline trip_parameters_ptr +argumentsToTripParameter(const Nan::FunctionCallbackInfo &args, + bool requires_multiple_coordinates) +{ + trip_parameters_ptr params = boost::make_unique(); + bool has_base_params = argumentsToParameter(args, params, requires_multiple_coordinates); + if (!has_base_params) + return trip_parameters_ptr(); + + v8::Local obj = Nan::To(args[0]).ToLocalChecked(); + + bool parsedSuccessfully = parseCommonParameters(obj, params); + if (!parsedSuccessfully) + { + return trip_parameters_ptr(); + } + + if (obj->Has(Nan::New("roundtrip").ToLocalChecked())) + { + auto roundtrip = obj->Get(Nan::New("roundtrip").ToLocalChecked()); + if (roundtrip->IsBoolean()) + { + params->roundtrip = roundtrip->BooleanValue(); + } + else + { + Nan::ThrowError("'roundtrip' param must be a boolean"); + return trip_parameters_ptr(); + } + } + + if (obj->Has(Nan::New("source").ToLocalChecked())) + { + v8::Local source = obj->Get(Nan::New("source").ToLocalChecked()); + + if (!source->IsString()) + { + Nan::ThrowError("Source must be a string: [any, first]"); + return trip_parameters_ptr(); + } + + std::string source_str = *v8::String::Utf8Value(source); + + if (source_str == "first") + { + params->source = osrm::TripParameters::SourceType::First; + } + else if (source_str == "any") + { + params->source = osrm::TripParameters::SourceType::Any; + } + else + { + Nan::ThrowError("'source' param must be one of [any, first]"); + return trip_parameters_ptr(); + } + } + + if (obj->Has(Nan::New("destination").ToLocalChecked())) + { + v8::Local destination = obj->Get(Nan::New("destination").ToLocalChecked()); + + if (!destination->IsString()) + { + Nan::ThrowError("Destination must be a string: [any, last]"); + return trip_parameters_ptr(); + } + + std::string destination_str = *v8::String::Utf8Value(destination); + + if (destination_str == "last") + { + params->destination = osrm::TripParameters::DestinationType::Last; + } + else if (destination_str == "any") + { + params->destination = osrm::TripParameters::DestinationType::Any; + } + else + { + Nan::ThrowError("'destination' param must be one of [any, last]"); + return trip_parameters_ptr(); + } + } + + return params; +} + +inline match_parameters_ptr +argumentsToMatchParameter(const Nan::FunctionCallbackInfo &args, + bool requires_multiple_coordinates) +{ + match_parameters_ptr params = boost::make_unique(); + bool has_base_params = argumentsToParameter(args, params, requires_multiple_coordinates); + if (!has_base_params) + return match_parameters_ptr(); + + v8::Local obj = Nan::To(args[0]).ToLocalChecked(); + + if (obj->Has(Nan::New("timestamps").ToLocalChecked())) + { + v8::Local timestamps = obj->Get(Nan::New("timestamps").ToLocalChecked()); + + if (!timestamps->IsArray()) + { + Nan::ThrowError("Timestamps must be an array of integers (or undefined)"); + return match_parameters_ptr(); + } + + v8::Local timestamps_array = v8::Local::Cast(timestamps); + + if (params->coordinates.size() != timestamps_array->Length()) + { + Nan::ThrowError("Timestamp array must have the same size as the coordinates " + "array"); + return match_parameters_ptr(); + } + + for (uint32_t i = 0; i < timestamps_array->Length(); ++i) + { + v8::Local timestamp = timestamps_array->Get(i); + if (!timestamp->IsNumber()) + { + Nan::ThrowError("Timestamps array items must be numbers"); + return match_parameters_ptr(); + } + params->timestamps.emplace_back(static_cast(timestamp->NumberValue())); + } + } + + bool parsedSuccessfully = parseCommonParameters(obj, params); + if (!parsedSuccessfully) + { + return match_parameters_ptr(); + } + + return params; +} + +} // ns node_osrm + +#endif diff --git a/lib/binding/.gitkeep b/lib/binding/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 00000000000..6b62312d974 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,2 @@ +var OSRM = module.exports = require('./binding/node-osrm.node').OSRM; +OSRM.version = require('../package.json').version; diff --git a/package.json b/package.json index 84547068dfa..bc65c0b7b48 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "osrm-backend-test-suite", - "version": "0.0.0", + "name": "osrm", + "version": "5.7.0", "private": true, "description": "The Open Source Routing Machine is a high performance routing engine written in C++14 designed to run on OpenStreetMap data.", "dependencies": { @@ -12,7 +12,11 @@ "polyline": "^0.2.0", "request": "^2.69.0", "rimraf": "^2.5.4", - "xmlbuilder": "^4.2.1" + "xmlbuilder": "^4.2.1", + + "nan": "^2.1.0", + "node-cmake": "^1.2.1", + "node-pre-gyp": "~0.6.30" }, "bin": { "cucumber": "./node_modules/cucumber/bin/cucumber.js" @@ -28,7 +32,10 @@ "test": "npm run lint && node ./node_modules/cucumber/bin/cucumber.js features/ -p verify && node ./node_modules/cucumber/bin/cucumber.js features/ -p mld", "clean-test": "rm -rf test/cache", "cucumber": "./node_modules/cucumber/bin/cucumber.js", - "build-api-docs": "./scripts/build_api_docs.sh" + "build-api-docs": "./scripts/build_api_docs.sh", + + "preinstall": "npm install node-pre-gyp", + "install": "node-pre-gyp install --fallback-to-build=false" }, "repository": { "type": "git", @@ -45,6 +52,17 @@ }, "devDependencies": { "docbox": "^1.0.2", - "eslint": "^2.4.0" + "eslint": "^2.4.0", + + "aws-sdk": "~2.0.31", + "tape": "^4.2.2" + }, + "main": "lib/index.js", + "binary": { + "module_name": "node-osrm", + "module_path": "./lib/binding/", + "host": "https://mapbox-node-binary.s3.amazonaws.com", + "remote_path": "./{name}/v{version}/{configuration}/", + "package_name": "{node_abi}-{platform}-{arch}.tar.gz" } } diff --git a/scripts/poly2req.js b/scripts/poly2req.js index 369e8a91ede..e2dd3fc4ddd 100755 --- a/scripts/poly2req.js +++ b/scripts/poly2req.js @@ -51,8 +51,8 @@ else if (process.argv.length > 2 && process.argv[2] == "dc") } else if (process.argv.length > 2) { - let monaco_poly_path = process.argv[2]; - let poly_data = fs.readFileSync(monaco_poly_path, 'utf-8'); + let poly_path = process.argv[2]; + let poly_data = fs.readFileSync(poly_path, 'utf-8'); // lets assume there is only one ring // cut of name and ring number and the two END statements diff --git a/scripts/travis/build.sh b/scripts/travis/build.sh new file mode 100755 index 00000000000..48d3d14d256 --- /dev/null +++ b/scripts/travis/build.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +set -eu +set -o pipefail + +# defaults +export ENABLE_COVERAGE=${ENABLE_COVERAGE:-"Off"} +export BUILD_TYPE=${BUILD_TYPE:-"Release"} +export NODE=${NODE:-4} + +export CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +export DEPS_DIR="$(pwd)/deps" +export PATH=${DEPS_DIR}/bin:${PATH} +mkdir -p ${DEPS_DIR} + +export CLANG_VERSION="${CLANG_VERSION:-4.0.0}" +export CCACHE_VERSION=3.3.1 +export CMAKE_VERSION=3.7.2 + +source ${CURRENT_DIR}/travis_helper.sh + +# ensure we start inside the root directory (two level up) +cd ${CURRENT_DIR}/../../ + +if [[ ! $(which wget) ]]; then + echo "echo wget must be installed"; + exit 1; +fi; + +SYSTEM_NAME=$(uname -s) +if [[ "${SYSTEM_NAME}" == "Darwin" ]]; then + OS_NAME="osx" +elif [[ "${SYSTEM_NAME}" == "Linux" ]]; then + OS_NAME="linux" +fi + +# FIXME This should be replaced by proper calls to mason but we currently have a chicken-egg problem +# since we rely on osrm-backend to ship mason for us. Once we merged this into osrm-backend this will not be needed. +CMAKE_URL="https://s3.amazonaws.com/mason-binaries/${OS_NAME}-x86_64/cmake/${CMAKE_VERSION}.tar.gz" +echo "Downloading cmake from ${CMAKE_URL} ..." +wget --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C ${DEPS_DIR} || exit 1 +CCACHE_URL="https://s3.amazonaws.com/mason-binaries/${OS_NAME}-x86_64/ccache/${CCACHE_VERSION}.tar.gz" +echo "Downloading ccache from ${CCACHE_URL} ..." +wget --quiet -O - ${CCACHE_URL} | tar --strip-components=1 -xz -C ${DEPS_DIR} || exit 1 +# install clang for linux but use the xcode version on OSX +if [[ "${OS_NAME}" != "osx" ]]; then + CLANG_URL="https://s3.amazonaws.com/mason-binaries/${OS_NAME}-x86_64/clang++/${CLANG_VERSION}.tar.gz" + echo "Downloading clang from ${CLANG_URL} ..." + wget --quiet -O - ${CLANG_URL} | tar --strip-components=1 -xz -C ${DEPS_DIR} || exit 1 + export CCOMPILER='clang' + export CXXCOMPILER='clang++' + export CC='clang' + export CXX='clang++' +fi + +if [[ "${OS_NAME}" == "osx" ]]; then + if [[ -f /etc/sysctl.conf ]] && [[ $(grep shmmax /etc/sysctl.conf) ]]; then + echo "Note: found shmmax setting in /etc/sysctl.conf, not modifying" + else + echo "WARNING: Did not find shmmax setting in /etc/sysctl.conf, adding now (requires sudo and restarting)..." + sudo sysctl -w kern.sysv.shmmax=4294967296 + sudo sysctl -w kern.sysv.shmall=1048576 + sudo sysctl -w kern.sysv.shmseg=128 + fi +fi + + +echo "Now build node-osrm and dependencies" +export VERBOSE=1 +if [[ "${ENABLE_COVERAGE}" == "On" ]]; then + mapbox_time "make" make -j4 coverage +else + mkdir -p build + pushd build + cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DENABLE_NODE_BINDINGS=On -DENABLE_MASON=On + mapbox_time "make" make -j4 + popd +fi + +## run tests, with backtrace support +#if [[ "${OS_NAME}" == "linux" ]]; then +# ulimit -c unlimited -S +# RESULT=0 +# mapbox_time "make-test" make tests || RESULT=$? +# for i in $(find ./ -maxdepth 1 -name 'core*' -print); +# do gdb $(which node) $i -ex "thread apply all bt" -ex "set pagination 0" -batch; +# done; +# if [[ ${RESULT} != 0 ]]; then exit $RESULT; fi +#else +# # todo: coredump support on OS X +# RESULT=0 +# mapbox_time "make-test" make tests || RESULT=$? +# if [[ ${RESULT} != 0 ]]; then exit $RESULT; fi +#fi + + +set +eu +set +o pipefail diff --git a/scripts/travis/is_pr_merge.sh b/scripts/travis/is_pr_merge.sh new file mode 100755 index 00000000000..6e94c1a704c --- /dev/null +++ b/scripts/travis/is_pr_merge.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +set -eu pipefail + +: ' + +This script is designed to detect if a gitsha represents a normal +push commit (to any branch) or whether it represents travis attempting +to merge between the origin and the upstream branch. + +For more details see: https://docs.travis-ci.com/user/pull-requests + +' + +# Get the commit message via git log +# This should always be the exact text the developer provided +COMMIT_LOG=$(git log --format=%B --no-merges -n 1 | tr -d '\n') + +# Get the commit message via git show +# If the gitsha represents a merge then this will +# look something like "Merge e3b1981 into 615d2a3" +# Otherwise it will be the same as the "git log" output +COMMIT_SHOW=$(git show -s --format=%B | tr -d '\n') + +if [[ "${COMMIT_LOG}" != "${COMMIT_SHOW}" ]]; then + echo true +fi diff --git a/scripts/travis/publish.sh b/scripts/travis/publish.sh new file mode 100755 index 00000000000..5e18aea0fc4 --- /dev/null +++ b/scripts/travis/publish.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +set -eu +set -o pipefail + +# should be set for debug builds +export NPM_FLAGS=${NPM_FLAGS:-} + +echo "node version is:" +which node +node -v + +echo "dumping binary meta..." +./node_modules/.bin/node-pre-gyp reveal ${NPM_FLAGS} + +# enforce that binary has proper ORIGIN flags so that +# it can portably find libtbb.so in the same directory +if [[ $(uname -s) == 'Linux' ]]; then + readelf -d ./lib/binding/node-osrm.node > readelf-output.txt + if grep -q 'Flags: ORIGIN' readelf-output.txt; then + echo "Found ORIGIN flag in readelf output" + cat readelf-output.txt + else + echo "*** Error: Could not found ORIGIN flag in readelf output" + cat readelf-output.txt + exit 1 + fi +fi + +echo "determining publishing status..." + +if [[ $(./scripts/travis/is_pr_merge.sh) ]]; then + echo "Skipping publishing because this is a PR merge commit" +else + echo "This is a push commit, continuing to package..." + ./node_modules/.bin/node-pre-gyp package ${NPM_FLAGS} + + export COMMIT_MESSAGE=$(git log --format=%B --no-merges | head -n 1 | tr -d '\n') + echo "Commit message: ${COMMIT_MESSAGE}" + + if [[ ${COMMIT_MESSAGE} =~ "[publish binary]" ]]; then + echo "Publishing" + ./node_modules/.bin/node-pre-gyp publish ${NPM_FLAGS} + elif [[ ${COMMIT_MESSAGE} =~ "[republish binary]" ]]; then + echo "*** Error: Republishing is disallowed for this repository" + exit 1 + #./node_modules/.bin/node-pre-gyp unpublish publish ${NPM_FLAGS} + else + echo "Skipping publishing" + fi; +fi diff --git a/scripts/travis/travis_helper.sh b/scripts/travis/travis_helper.sh new file mode 100644 index 00000000000..ff23f0a4bff --- /dev/null +++ b/scripts/travis/travis_helper.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +# This script is sourced, so do not set -e or -o pipefail here. Doing so would +# bleed into Travis' wrapper script, which messes with their workflow, e.g. +# preventing after_failure scripts to be triggered. + +function mapbox_time_start { + local name=$1 + mapbox_timer_name=$name + + mapbox_fold start $name + + mapbox_timer_id=$(printf %08x $(( RANDOM * RANDOM ))) + eval "mapbox_start_time_$mapbox_timer_id=$(mapbox_nanoseconds)" + echo -en "travis_time:start:$mapbox_timer_id\n" +} + +function mapbox_time_finish { + local name=${1:-$mapbox_timer_name} + local timer_id=${2:-$mapbox_timer_id} + local timer_start="mapbox_start_time_$timer_id" + eval local start_time=\${$timer_start} + local end_time=$(mapbox_nanoseconds) + local duration=$(($end_time-$start_time)) + echo -en "travis_time:end:$timer_id:start=$start_time,finish=$end_time,duration=$duration\n" +} + +function mapbox_time { + local name=$1 ; shift + mapbox_time_start $name + local timer_id=$mapbox_timer_id + echo "\$ $@" + # note: we capture the return code here + # so that we can ensure mapbox_time_finish is called + # and an error is trickled up correctly + local RESULT=0 + $@ || RESULT=$? + mapbox_time_finish $name $timer_id + if [[ ${RESULT} != 0 ]]; then + echo "$name failed with ${RESULT}" + # note: we use false here instead of exit this this script is sourced + # and exit would abort the shell which we don't want + false + else + mapbox_fold end $name + fi +} + +function mapbox_fold { + local action=$1 + local name=$2 + local ANSI_CLEAR="\e[0m" + echo -en "travis_fold:${action}:${name}\r${ANSI_CLEAR}" +} + +function mapbox_nanoseconds { + local cmd="date" + local format="+%s%N" + local os=$(uname -s) + + if hash gdate > /dev/null 2>&1; then + cmd="gdate" # use gdate if available + elif [[ "$os" = Darwin ]]; then + format="+%s000000000" # fallback to second precision on darwin (does not support %N) + fi + + $cmd -u $format +} + +export JOBS +export -f mapbox_fold +export -f mapbox_nanoseconds +export -f mapbox_time +export -f mapbox_time_start +export -f mapbox_time_finish diff --git a/src/benchmarks/match.cpp b/src/benchmarks/match.cpp index 788ab2f0bc0..09b0739b962 100644 --- a/src/benchmarks/match.cpp +++ b/src/benchmarks/match.cpp @@ -36,7 +36,7 @@ int main(int argc, const char *argv[]) try // Routing machine with several services (such as Route, Table, Nearest, Trip, Match) OSRM osrm{config}; - // Route in monaco + // Match traces to the road network in our Berlin test dataset MatchParameters params; params.overview = RouteParameters::OverviewType::False; params.steps = false; @@ -45,170 +45,123 @@ int main(int argc, const char *argv[]) try using osrm::util::FloatLatitude; using osrm::util::FloatLongitude; + // Grab trace, or: go to geojson.io, create linestring. + // Extract coordinates: jq '.features[].geometry.coordinates[]' coordinates.json + params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.422176599502563}, FloatLatitude{43.73754595167546}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.421715259552002}, FloatLatitude{43.73744517900973}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.421489953994752}, FloatLatitude{43.73738316497729}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.421286106109619}, FloatLatitude{43.737274640266}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.420910596847533}, FloatLatitude{43.73714285999499}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.420696020126342}, FloatLatitude{43.73699557581948}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.42049217224121}, FloatLatitude{43.73690255404829}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.420309782028198}, FloatLatitude{43.73672426191624}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.420159578323363}, FloatLatitude{43.7366622471372}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.420148849487305}, FloatLatitude{43.736623487867654}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.419934272766113}, FloatLatitude{43.73647620241466}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.419805526733398}, FloatLatitude{43.736228141885455}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.419601678848267}, FloatLatitude{43.736142870841206}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.419376373291015}, FloatLatitude{43.735956824504974}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.419247627258301}, FloatLatitude{43.73574752168583}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.419043779373169}, FloatLatitude{43.73566224995717}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.418732643127442}, FloatLatitude{43.735406434042645}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.418657541275024}, FloatLatitude{43.735321161828274}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.418593168258667}, FloatLatitude{43.73521263337983}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.418367862701416}, FloatLatitude{43.73508084857086}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.418346405029297}, FloatLatitude{43.73484828643578}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.4180567264556885}, FloatLatitude{43.734437424456566}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.417809963226318}, FloatLatitude{43.73414284243448}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.417863607406615}, FloatLatitude{43.73375523230292}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.417809963226318}, FloatLatitude{43.73386376339265}}); - params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.417895793914795}, FloatLatitude{43.73365445325776}}); + FloatCoordinate{FloatLongitude{13.410401344299316}, FloatLatitude{52.522749270442254}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.418067455291747}, FloatLatitude{43.73343739012297}}); + FloatCoordinate{FloatLongitude{13.410615921020508}, FloatLatitude{52.52284066124772}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.41803526878357}, FloatLatitude{43.73319706930599}}); + FloatCoordinate{FloatLongitude{13.410787582397461}, FloatLatitude{52.522932051863044}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.418024539947509}, FloatLatitude{43.73295674752463}}); + FloatCoordinate{FloatLongitude{13.411259651184082}, FloatLatitude{52.52333677944541}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.417906522750854}, FloatLatitude{43.73284821479115}}); + FloatCoordinate{FloatLongitude{13.411538600921629}, FloatLatitude{52.52341511338546}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.417917251586914}, FloatLatitude{43.7327551865773}}); + FloatCoordinate{FloatLongitude{13.411903381347656}, FloatLatitude{52.52374150329884}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.417434453964233}, FloatLatitude{43.73281720540258}}); + FloatCoordinate{FloatLongitude{13.412246704101562}, FloatLatitude{52.523950391570665}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.4173808097839355}, FloatLatitude{43.73307303237796}}); + FloatCoordinate{FloatLongitude{13.410637378692625}, FloatLatitude{52.52398955801103}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.41750955581665}, FloatLatitude{43.73328234454499}}); + FloatCoordinate{FloatLongitude{13.409242630004881}, FloatLatitude{52.52413316799366}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.417563199996948}, FloatLatitude{43.73352266501975}}); + FloatCoordinate{FloatLongitude{13.407998085021973}, FloatLatitude{52.52448566323317}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.41750955581665}, FloatLatitude{43.733770736756355}}); + FloatCoordinate{FloatLongitude{13.40705394744873}, FloatLatitude{52.52474676899426}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.417466640472412}, FloatLatitude{43.73409632935116}}); + FloatCoordinate{FloatLongitude{13.406410217285156}, FloatLatitude{52.5249948180297}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.417230606079102}, FloatLatitude{43.73428238146768}}); + FloatCoordinate{FloatLongitude{13.406989574432373}, FloatLatitude{52.525686736883024}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.41724133491516}, FloatLatitude{43.73405756842078}}); + FloatCoordinate{FloatLongitude{13.407375812530518}, FloatLatitude{52.52628726139225}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.4169838428497314}, FloatLatitude{43.73449168940785}}); + FloatCoordinate{FloatLongitude{13.406217098236084}, FloatLatitude{52.52663973934549}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.41701602935791}, FloatLatitude{43.734615723397525}}); + FloatCoordinate{FloatLongitude{13.405036926269531}, FloatLatitude{52.52696610529863}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.41704821586609}, FloatLatitude{43.73487929477265}}); + FloatCoordinate{FloatLongitude{13.404350280761717}, FloatLatitude{52.52717497823596}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.41725206375122}, FloatLatitude{43.734949063471895}}); + FloatCoordinate{FloatLongitude{13.404221534729004}, FloatLatitude{52.5265222470087}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.4173808097839355}, FloatLatitude{43.73533666587628}}); + FloatCoordinate{FloatLongitude{13.40383529663086}, FloatLatitude{52.526039219655445}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.41750955581665}, FloatLatitude{43.735623490040375}}); + FloatCoordinate{FloatLongitude{13.402740955352783}, FloatLatitude{52.526300316181675}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.417799234390259}, FloatLatitude{43.73577852955704}}); + FloatCoordinate{FloatLongitude{13.401474952697754}, FloatLatitude{52.52666584871098}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.4180781841278085}, FloatLatitude{43.735972328388435}}); + FloatCoordinate{FloatLongitude{13.400874137878418}, FloatLatitude{52.527370795712564}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.41850733757019}, FloatLatitude{43.73608860738618}}); + FloatCoordinate{FloatLongitude{13.400616645812988}, FloatLatitude{52.52780159108807}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.418850660324096}, FloatLatitude{43.736228141885455}}); + FloatCoordinate{FloatLongitude{13.399865627288817}, FloatLatitude{52.52756661231615}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.419086694717407}, FloatLatitude{43.73636767605958}}); + FloatCoordinate{FloatLongitude{13.399114608764648}, FloatLatitude{52.52744912245876}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.419333457946777}, FloatLatitude{43.73664674343239}}); + FloatCoordinate{FloatLongitude{13.39802026748657}, FloatLatitude{52.527266359833675}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.419633865356444}, FloatLatitude{43.73676302112054}}); + FloatCoordinate{FloatLongitude{13.398470878601072}, FloatLatitude{52.52648308282661}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.419784069061279}, FloatLatitude{43.737096349241845}}); + FloatCoordinate{FloatLongitude{13.398964405059813}, FloatLatitude{52.52538647154948}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.420030832290649}, FloatLatitude{43.73720487427631}}); + FloatCoordinate{FloatLongitude{13.398363590240479}, FloatLatitude{52.52542563670941}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.419601678848267}, FloatLatitude{43.73708084564945}}); + FloatCoordinate{FloatLongitude{13.39780569076538}, FloatLatitude{52.525347306354654}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.419333457946777}, FloatLatitude{43.73708084564945}}); + FloatCoordinate{FloatLongitude{13.397247791290283}, FloatLatitude{52.525190645226104}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.419043779373169}, FloatLatitude{43.737158363571325}}); + FloatCoordinate{FloatLongitude{13.396217823028564}, FloatLatitude{52.52494259729653}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.418915033340454}, FloatLatitude{43.737305647346446}}); + FloatCoordinate{FloatLongitude{13.395531177520752}, FloatLatitude{52.52452482919627}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.41848587989807}, FloatLatitude{43.7374916894919}}); + FloatCoordinate{FloatLongitude{13.39482307434082}, FloatLatitude{52.524472607904364}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.418271303176879}, FloatLatitude{43.73746843425534}}); + FloatCoordinate{FloatLongitude{13.39359998703003}, FloatLatitude{52.5246814926995}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.417960166931152}, FloatLatitude{43.73744517900973}}); + FloatCoordinate{FloatLongitude{13.392891883850098}, FloatLatitude{52.52490343170594}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.417885065078735}, FloatLatitude{43.737212626056944}}); + FloatCoordinate{FloatLongitude{13.392398357391357}, FloatLatitude{52.5239765025348}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.417563199996948}, FloatLatitude{43.73703433484817}}); + FloatCoordinate{FloatLongitude{13.391926288604736}, FloatLatitude{52.52310177678706}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.4173057079315186}, FloatLatitude{43.73692580950463}}); + FloatCoordinate{FloatLongitude{13.39184045791626}, FloatLatitude{52.52222703362077}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.417144775390625}, FloatLatitude{43.7367707729584}}); + FloatCoordinate{FloatLongitude{13.39184045791626}, FloatLatitude{52.521169485041774}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.416973114013672}, FloatLatitude{43.73653821738638}}); + FloatCoordinate{FloatLongitude{13.39184045791626}, FloatLatitude{52.52039915585348}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.416855096817017}, FloatLatitude{43.73639868360965}}); + FloatCoordinate{FloatLongitude{13.39205503463745}, FloatLatitude{52.519681040207885}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.4167799949646}, FloatLatitude{43.736142870841206}}); + FloatCoordinate{FloatLongitude{13.392269611358643}, FloatLatitude{52.51900208371135}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.41675853729248}, FloatLatitude{43.735848297208605}}); + FloatCoordinate{FloatLongitude{13.392527103424072}, FloatLatitude{52.51812725890996}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.416619062423706}, FloatLatitude{43.73567000193752}}); + FloatCoordinate{FloatLongitude{13.392677307128904}, FloatLatitude{52.51750050804369}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.416543960571288}, FloatLatitude{43.735406434042645}}); + FloatCoordinate{FloatLongitude{13.393385410308838}, FloatLatitude{52.51735687637764}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.416479587554932}, FloatLatitude{43.73529790574875}}); + FloatCoordinate{FloatLongitude{13.394951820373535}, FloatLatitude{52.517474393230245}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.416415214538574}, FloatLatitude{43.73515061703527}}); + FloatCoordinate{FloatLongitude{13.396711349487305}, FloatLatitude{52.51735687637764}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.416350841522218}, FloatLatitude{43.73490255101476}}); + FloatCoordinate{FloatLongitude{13.398127555847168}, FloatLatitude{52.517696368649815}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.416340112686156}, FloatLatitude{43.73475526132885}}); + FloatCoordinate{FloatLongitude{13.399629592895508}, FloatLatitude{52.51773554066627}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.416222095489501}, FloatLatitude{43.73446068087028}}); + FloatCoordinate{FloatLongitude{13.400981426239014}, FloatLatitude{52.51829700239765}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.416243553161621}, FloatLatitude{43.73430563794159}}); + FloatCoordinate{FloatLongitude{13.403105735778809}, FloatLatitude{52.51887151395141}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.416050434112548}, FloatLatitude{43.73403431185051}}); + FloatCoordinate{FloatLongitude{13.40355634689331}, FloatLatitude{52.51966798345114}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.415814399719239}, FloatLatitude{43.73382500231174}}); + FloatCoordinate{FloatLongitude{13.404908180236816}, FloatLatitude{52.52007274110608}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.415750026702881}, FloatLatitude{43.73354592178871}}); + FloatCoordinate{FloatLongitude{13.40555191040039}, FloatLatitude{52.520529721073366}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.415513992309569}, FloatLatitude{43.73347615145474}}); + FloatCoordinate{FloatLongitude{13.407869338989258}, FloatLatitude{52.52144366674759}}); params.coordinates.push_back( - FloatCoordinate{FloatLongitude{7.415342330932617}, FloatLatitude{43.733251335381205}}); + FloatCoordinate{FloatLongitude{13.408942222595215}, FloatLatitude{52.52203119321206}}); TIMER_START(routes); auto NUM = 100; diff --git a/src/nodejs/CMakeLists.txt b/src/nodejs/CMakeLists.txt new file mode 100644 index 00000000000..b21917fa71b --- /dev/null +++ b/src/nodejs/CMakeLists.txt @@ -0,0 +1,54 @@ +# node-cmake requires CMake 3.1 features; for the osrm project we only +# require CMake 2.8.11 so that we can build e.g. on Trusty by default. +cmake_minimum_required(VERSION 3.1) + +message(STATUS "Building node-osrm") + +set(BINDING_DIR "${PROJECT_SOURCE_DIR}/lib/binding") + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/nodejs") +include(FindNodeJS) + +set(NodeJS_CXX_STANDARD 14 CACHE INTERNAL "Use C++14" FORCE) +set(NodeJS_DOWNLOAD ON CACHE INTERNAL "Download node.js sources" FORCE) +set(NodeJS_USE_CLANG_STDLIB OFF CACHE BOOL "Don't use libc++ by default" FORCE) + +find_package(NodeJS REQUIRED) +add_nodejs_module(node-osrm node_osrm.cpp) +target_link_libraries(node-osrm osrm) + +# node-osrm artifacts in ${BINDING_DIR} to depend targets on +set(ARTIFACTS "") + +set(OSRM_BINARIES osrm-extract osrm-contract osrm-routed osrm-datastore osrm-components) +foreach(binary ${OSRM_BINARIES}) + add_custom_command(OUTPUT ${BINDING_DIR}/${binary} + COMMAND ${CMAKE_COMMAND} -E copy $ ${BINDING_DIR} + DEPENDS ${binary} ${BINDING_DIR}) + list(APPEND ARTIFACTS "${BINDING_DIR}/${binary}") +endforeach(binary) + +# For mason-enabled builds we copy over tbb's shared objects for packaging. +# TODO: consider using statically linked tbb library (for node-osrm only!) +if (ENABLE_MASON) + foreach(libpath ${MASON_PACKAGE_tbb_LIBRARY_DIRS}) + file(GLOB TBBGlob ${libpath}/*.*) + foreach(filepath ${TBBGlob}) + get_filename_component(filename ${filepath} NAME) + add_custom_command(OUTPUT "${BINDING_DIR}/${filename}" + COMMAND ${CMAKE_COMMAND} -E copy ${filepath} ${BINDING_DIR} + DEPENDS ${filepath} ${BINDING_DIR}) + list(APPEND ARTIFACTS "${BINDING_DIR}/${filename}") + endforeach() + endforeach() +endif() + + +add_custom_command(OUTPUT ${BINDING_DIR}/node-osrm.node + COMMAND ${CMAKE_COMMAND} -E copy $ ${BINDING_DIR} + DEPENDS node-osrm ${BINDING_DIR}) +list(APPEND ARTIFACTS "${BINDING_DIR}/node-osrm.node") + + +message(STATUS "node-osrm artifacts will be copied to: ${BINDING_DIR}") +add_custom_target(copy_artifacts ALL DEPENDS ${ARTIFACTS}) diff --git a/src/nodejs/node_osrm.cpp b/src/nodejs/node_osrm.cpp new file mode 100644 index 00000000000..d70af09e958 --- /dev/null +++ b/src/nodejs/node_osrm.cpp @@ -0,0 +1,507 @@ +#include "osrm/engine_config.hpp" +#include "osrm/osrm.hpp" + +#include "osrm/match_parameters.hpp" +#include "osrm/nearest_parameters.hpp" +#include "osrm/route_parameters.hpp" +#include "osrm/table_parameters.hpp" +#include "osrm/tile_parameters.hpp" +#include "osrm/trip_parameters.hpp" + +#include +#include +#include + +#include "nodejs/node_osrm.hpp" +#include "nodejs/node_osrm_support.hpp" + +namespace node_osrm +{ + +Engine::Engine(osrm::EngineConfig &config) : Base(), this_(std::make_shared(config)) {} + +Nan::Persistent &Engine::constructor() +{ + static Nan::Persistent init; + return init; +} + +NAN_MODULE_INIT(Engine::Init) +{ + const auto whoami = Nan::New("OSRM").ToLocalChecked(); + + auto fnTp = Nan::New(New); + fnTp->InstanceTemplate()->SetInternalFieldCount(1); + fnTp->SetClassName(whoami); + + SetPrototypeMethod(fnTp, "route", route); + SetPrototypeMethod(fnTp, "nearest", nearest); + SetPrototypeMethod(fnTp, "table", table); + SetPrototypeMethod(fnTp, "tile", tile); + SetPrototypeMethod(fnTp, "match", match); + SetPrototypeMethod(fnTp, "trip", trip); + + const auto fn = Nan::GetFunction(fnTp).ToLocalChecked(); + + constructor().Reset(fn); + + Nan::Set(target, whoami, fn); +} + +/** + * + * The `OSRM` method is the main constructor for creating an OSRM instance. An OSRM instance + * requires a `.osrm` network, + * which is prepared by the OSRM Backend C++ library. Once you have a complete `network.osrm` file, + * you can calculate + * networks in javascript with this library using the methods below. To create an OSRM instance with + * your network + * you need to construct an instance like this: + * + * ```javascript + * var osrm = new OSRM('network.osrm'); + * ``` + * + * #### Methods + * + * | Service | Description | + * |-----------------------------|-----------------------------------------------------------| + * | [`osrm.route`](#route) | shortest path between given coordinates | + * | [`osrm.nearest`](#nearest) | returns the nearest street segment for a given coordinate | + * | [`osrm.table`](#table) | computes distance tables for given coordinates | + * | [`osrm.match`](#match) | matches given coordinates to the road network | + * | [`osrm.trip`](#trip) | computes the shortest trip between given coordinates | + * | [`osrm.tile`](#tile) | Return vector tiles containing debugging info | + * + * #### General Options + * + * Each OSRM method (except for `OSRM.tile()`) has set of general options as well as unique options, + * outlined below. + * + * | Option | Values | Description + * | Format | + * | ----------- | ------------------------------------------------------- | + * ------------------------------------------------------------------------------------------------------ + * | ------------------------------------------------------------------------------ | + * | coordinates | `array` of `coordinate` elements: `[{coordinate}, ...]` | The coordinates this + * request will use. | `array` with + * `[{lon},{lat}]` values, in decimal degrees | + * | bearings | `array` of `bearing` elements: `[{bearing}, ...]` | Limits the search to + * segments with given bearing in degrees towards true north in clockwise direction. | `null` or + * `array` with `[{value},{range}]` `integer 0 .. 360,integer 0 .. 180` | + * | radiuses | `array` of `radius` elements: `[{radius}, ...]` | Limits the search to + * given radius in meters. | `null` or + * `double >= 0` or `unlimited` (default) | + * | hints | `array` of `hint` elements: `[{hint}, ...]` | Hint to derive position + * in street network. | Base64 `string` + * | + * + * @class OSRM + * + */ +NAN_METHOD(Engine::New) +{ + if (info.IsConstructCall()) + { + try + { + auto config = argumentsToEngineConfig(info); + if (!config) + return; + + auto *const self = new Engine(*config); + self->Wrap(info.This()); + } + catch (const std::exception &ex) + { + return Nan::ThrowTypeError(ex.what()); + } + + info.GetReturnValue().Set(info.This()); + } + else + { + return Nan::ThrowTypeError( + "Cannot call constructor as function, you need to use 'new' keyword"); + } +} + +template +inline void async(const Nan::FunctionCallbackInfo &info, + ParameterParser argsToParams, + ServiceMemFn service, + bool requires_multiple_coordinates) +{ + auto params = argsToParams(info, requires_multiple_coordinates); + if (!params) + return; + + BOOST_ASSERT(params->IsValid()); + + if (!info[info.Length() - 1]->IsFunction()) + return Nan::ThrowTypeError("last argument must be a callback function"); + + auto *const self = Nan::ObjectWrap::Unwrap(info.Holder()); + using ParamPtr = decltype(params); + + struct Worker final : Nan::AsyncWorker + { + using Base = Nan::AsyncWorker; + + Worker(std::shared_ptr osrm_, + ParamPtr params_, + ServiceMemFn service, + Nan::Callback *callback) + : Base(callback), osrm{std::move(osrm_)}, service{std::move(service)}, + params{std::move(params_)} + { + } + + void Execute() override try + { + const auto status = ((*osrm).*(service))(*params, result); + ParseResult(status, result); + } + catch (const std::exception &e) + { + SetErrorMessage(e.what()); + } + + void HandleOKCallback() override + { + Nan::HandleScope scope; + + const constexpr auto argc = 2u; + v8::Local argv[argc] = {Nan::Null(), render(result)}; + + callback->Call(argc, argv); + } + + // Keeps the OSRM object alive even after shutdown until we're done with callback + std::shared_ptr osrm; + ServiceMemFn service; + const ParamPtr params; + + // All services return json::Object .. except for Tile! + using ObjectOrString = + typename std::conditional::value, + std::string, + osrm::json::Object>::type; + + ObjectOrString result; + }; + + auto *callback = new Nan::Callback{info[info.Length() - 1].As()}; + Nan::AsyncQueueWorker(new Worker{self->this_, std::move(params), service, callback}); +} + +/** + * Returns the fastest route between two or more coordinates while visiting the waypoints in order. + * + * @name route + * @memberof OSRM + * @param {Object} options Object literal containing parameters for the route query. + * @param {Boolean} [options.alternatives=false] Search for alternative routes and return as well. + * *Please note that even if an alternative route is requested, a result cannot be guaranteed.* + * @param {Boolean} [options.steps=false] Return route steps for each route leg. + * @param {Boolean} or {Array} [options.annotations=false] Return annotations for each route leg. + * Can be `false`, `true` or an array with strings of `duration`, `nodes`, `distance`, `weight`, + * `datasources`, `speed`. + * @param {String} [options.geometries=polyline] Returned route geometry format (influences overview + * and per step). Can also be `geojson`. + * @param {String} [options.overview=simplified] Add overview geometry either `full`, `simplified` + * according to highest zoom level it could be display on, or not at all (`false`). + * @param {Boolean} [options.continue_straight] Forces the route to keep going straight at waypoints + * and don't do a uturn even if it would be faster. Default value depends on the profile. + * `null`/`true`/`false` + * @param {Function} callback + * + * @returns {Object} An array of [Waypoint](#waypoint) objects representing all waypoints in order + * AND an array of [`Route`](#route) objects ordered by descending recommendation rank. + * + * @example + * var osrm = new OSRM("berlin-latest.osrm"); + * osrm.route({coordinates: [[52.519930,13.438640], [52.513191,13.415852]]}, function(err, result) { + * if(err) throw err; + * console.log(result.waypoints); // array of Waypoint objects representing all waypoints in order + * console.log(result.routes); // array of Route objects ordered by descending recommendation rank + * }); + */ +NAN_METHOD(Engine::route) // +{ + async(info, &argumentsToRouteParameter, &osrm::OSRM::Route, true); +} + +/** + * Snaps a coordinate to the street network and returns the nearest n matches. + * + * Note: `coordinates` in the general options only supports a single `{longitude},{latitude}` entry. + * + * @name nearest + * @memberof OSRM + * @param {Object} options - Object literal containing parameters for the nearest query. + * @param {Number} [options.number=1] Number of nearest segments that should be returned. + * Must be an integer greater than or equal to `1`. + * @param {Function} callback + * + * @returns {Object} containing `waypoints`. + * **`waypoints`**: array of [`Ẁaypoint`](#waypoint) objects sorted by distance to the input + * coordinate. + * Each object has an additional `distance` property, which is the distance in meters to the + * supplied + * input coordinate. + * + * @example + * var osrm = new OSRM('network.osrm'); + * var options = { + * coordinates: [[13.388860,52.517037]], + * number: 3, + * bearings: [[0,20]] + * }; + * osrm.nearest(options, function(err, response) { + * console.log(response.waypoints); // array of Waypoint objects + * }); + */ +NAN_METHOD(Engine::nearest) // +{ + async(info, &argumentsToNearestParameter, &osrm::OSRM::Nearest, false); +} + +/** + * Computes duration tables for the given locations. Allows for both symmetric and asymmetric + * tables. + * + * @name table + * @memberof OSRM + * @param {Object} options - Object literal containing parameters for the table query. + * @param {Array} [options.sources] An array of `index` elements (`0 <= integer < #coordinates`) to + * use + * location with given index as source. Default is to use all. + * @param {Array} [options.destinations] An array of `index` elements (`0 <= integer < + * #coordinates`) to use location with given index as destination. Default is to use all. + * @param {Function} callback + * + * @returns {Object} containing `durations`, `sources`, and `destinations`. + * **`durations`**: array of arrays that stores the matrix in row-major order. `durations[i][j]` + * gives the travel time from the i-th waypoint to the j-th waypoint. Values are given in seconds. + * **`sources`**: array of [`Ẁaypoint`](#waypoint) objects describing all sources in order. + * **`destinations`**: array of [`Ẁaypoint`](#waypoint) objects describing all destinations in + * order. + * + * @example + * var osrm = new OSRM('network.osrm'); + * var options = { + * coordinates: [ + * [13.388860,52.517037], + * [13.397634,52.529407], + * [13.428555,52.523219] + * ] + * }; + * osrm.table(options, function(err, response) { + * console.log(response.durations); // array of arrays, matrix in row-major order + * console.log(response.sources); // array of Waypoint objects + * console.log(response.destinations); // array of Waypoint objects + * }); + */ +NAN_METHOD(Engine::table) // +{ + async(info, &argumentsToTableParameter, &osrm::OSRM::Table, true); +} + +/** + * This generates [Mapbox Vector Tiles](https://mapbox.com/vector-tiles) that can be viewed with a + * vector-tile capable slippy-map viewer. The tiles contain road geometries and metadata that can + * be used to examine the routing graph. The tiles are generated directly from the data in-memory, + * so are in sync with actual routing results, and let you examine which roads are actually + * routable, + * and what weights they have applied. + * + * @name tile + * @memberof OSRM + * @param {Array} ZXY - an array consisting of `x`, `y`, and `z` values representing tile + * coordinates like + * [wiki.openstreetmap.org/wiki/Slippy_map_tilenames](https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames) + * and are supported by vector tile viewers like [Mapbox GL + * JS](https://www.mapbox.com/mapbox-gl-js/api/. + * @param {Function} callback + * + * @returns {Buffer} contains a Protocol Buffer encoded vector tile. + * + * @example + * var osrm = new OSRM('network.osrm'); + * osrm.tile([0, 0, 0], function(err, response) { + * if (err) throw err; + * fs.writeFileSync('./tile.vector.pbf', response); // write the buffer to a file + * }); + */ +NAN_METHOD(Engine::tile) +{ + async(info, &argumentsToTileParameters, &osrm::OSRM::Tile, {/*unused*/}); +} + +/** + * Map matching matches given GPS points to the road network in the most plausible way. + * Please note the request might result multiple sub-traces. Large jumps in the timestamps + * (>60s) or improbable transitions lead to trace splits if a complete matching could + * not be found. The algorithm might not be able to match all points. Outliers are removed + * if they can not be matched successfully. + * + * @name match + * @memberof OSRM + * @param {Object} options - Object literal containing parameters for the match query. + * @param {Boolean} [options.steps=false] Return route steps for each route. + * @param {Boolean} or {Array} [options.annotations=false] Return annotations for each route leg. + * Can be `false`, `true` or an array with strings of `duration`, `nodes`, `distance`, `weight`, + * `datasources`, `speed`. + * @param {String} [options.geometries=polyline] Returned route geometry format (influences overview + * and per step). Can also be `geojson`. + * @param {String} [options.overview=simplified] Add overview geometry either `full`, `simplified` + * according to highest zoom level it could be display on, or not at all (`false`). + * @param {Array} [options.timestamps] Timestamp of the input location (integers, UNIX-like + * timestamp). + * @param {Array} [options.radiuses] Standard deviation of GPS precision used for map matching. + * If applicable use GPS accuracy (`double >= 0`, default `5m`). + * @param {Function} callback + * + * @returns {Object} containing `tracepoints` and `matchings`. + * **`tracepoints`** Array of [`Ẁaypoint`](#waypoint) objects representing all points of the trace + * in order. + * If the trace point was ommited by map matching because it is an outlier, the entry will be null. + * Each + * `Waypoint` object includes two additional properties, 1) `matchings_index`: Index to the + * [`Route`](#route) object in matchings the sub-trace was matched to, 2) `waypoint_index`: Index of + * the waypoint inside the matched route. + * **`matchings`** is an array of [`Route`](#route) objects that + * assemble the trace. Each `Route` object has an additional `confidence` property, which is the + * confidence of + * the matching. float value between `0` and `1`. `1` is very confident that the matching is + * correct. + * + * @example + * var osrm = new OSRM('network.osrm'); + * var options = { + * coordinates: [[13.393252,52.542648],[13.39478,52.543079],[13.397389,52.542107]], + * timestamps: [1424684612, 1424684616, 1424684620] + * }; + * osrm.match(options, function(err, response) { + * if (err) throw err; + * console.log(response.tracepoints); // array of Waypoint objects + * console.log(response.matchings); // array of Route objects + * }); + * + */ +NAN_METHOD(Engine::match) // +{ + async(info, &argumentsToMatchParameter, &osrm::OSRM::Match, true); +} + +/** + * The trip plugin solves the Traveling Salesman Problem using a greedy heuristic + * (farthest-insertion algorithm) for 10 or * more waypoints and uses brute force for less than 10 + * waypoints. The returned path does not have to be the shortest path, * as TSP is NP-hard it is + * only an approximation. + * Note that all input coordinates have to be connected for the trip service to work. + * + * @name trip + * @memberof OSRM + * @param {Object} options - Object literal containing parameters for the trip query. + * @param {Boolean} [options.steps=false] Return route steps for each route. + * @param {Boolean} or {Array} [options.annotations=false] Return annotations for each route leg. + * Can be `false`, `true` or an array with strings of `duration`, `nodes`, `distance`, `weight`, + * `datasources`, `speed`. + * @param {String} [options.geometries=polyline] Returned route geometry format (influences overview + * and per step). Can also be `geojson`. + * @param {String} [options.overview=simplified] Add overview geometry either `full`, `simplified` + * @param {Function} callback + * @param {Boolean} [options.roundtrip=true] Return route is a roundtrip. + * @param {String} [options.source=any] Return route starts at `any` or `first` coordinate. + * @param {String} [options.destination=any] Return route ends at `any` or `last` coordinate. + * + * @returns {Object} containing `waypoints` and `trips`. + * **`waypoints`**: an array of [`Waypoint`](#waypoint) objects representing all waypoints in input + * order. + * Each Waypoint object has the following additional properties, 1) `trips_index`: index to trips of + * the + * sub-trip the point was matched to, and 2) `waypoint_index`: index of the point in the trip. + * **`trips`**: an array of [`Route`](#route) objects that assemble the trace. + * + * @example + * var osrm = new OSRM('network.osrm'); + * var options = { + * coordinates: [ + * [13.36761474609375, 52.51663871100423], + * [13.374481201171875, 52.506191342034576] + * ] + * } + * osrm.trip(options, function(err, response) { + * if (err) throw err; + * console.log(response.waypoints); // array of Waypoint objects + * console.log(response.trips); // array of Route objects + * }); + */ +NAN_METHOD(Engine::trip) // +{ + async(info, &argumentsToTripParameter, &osrm::OSRM::Trip, true); +} + +/** + * Responses + * @class Responses + */ + +/** + * Represents a route through (potentially multiple) waypoints. + * + * @name Route + * @memberof Responses + * + * @param {documentation} exteral in + * [`osrm-backend`](https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md#route) + * + */ + +/** + * Represents a route between two waypoints. + * + * @name RouteLeg + * @memberof Responses + * + * @param {documentation} exteral in + * [`osrm-backend`](https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md#routeleg) + * + */ + +/** + * A step consists of a maneuver such as a turn or merge, followed by a distance of travel along a + * single way to the subsequent step. + * + * @name RouteStep + * @memberof Responses + * + * @param {documentation} exteral in + * [`osrm-backend`](https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md#routestep) + * + */ + +/** + * + * @name StepManeuver + * @memberof Responses + * + * @param {documentation} exteral in + * [`osrm-backend`](https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md#stepmaneuver) + * + */ + +/** + * Object used to describe waypoint on a route. + * + * @name Waypoint + * @memberof Responses + * + * @param {documentation} exteral in + * [`osrm-backend`](https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md#waypoint) + * + */ + +} // namespace node_osrm diff --git a/test/data/Makefile b/test/data/Makefile index 31f4b0fc5bf..f9ebb2afcd7 100755 --- a/test/data/Makefile +++ b/test/data/Makefile @@ -1,4 +1,4 @@ -DATA_NAME:=monaco +DATA_NAME:=berlin DATA_URL:=https://s3.amazonaws.com/mapbox/osrm/testing/$(DATA_NAME).osm.pbf DATA_POLY_URL:=https://s3.amazonaws.com/mapbox/osrm/testing/$(DATA_NAME).poly OSRM_BUILD_DIR?=../../build diff --git a/test/data/data.md5sum b/test/data/data.md5sum index 9bcfd33c273..92c5239c7d8 100644 --- a/test/data/data.md5sum +++ b/test/data/data.md5sum @@ -1,2 +1,2 @@ -2b8dd9343d5e615afc9c67bcc7028a63 monaco.osm.pbf -b0788991ab3791d53c1c20b6281f81ad monaco.poly +54707104e9f411d1a3032c00fb1276c6 berlin.osm.pbf +031d988f23fc2a30371c9ef8d2a49bae berlin.poly diff --git a/test/nodejs/index.js b/test/nodejs/index.js new file mode 100644 index 00000000000..7cca6f93c94 --- /dev/null +++ b/test/nodejs/index.js @@ -0,0 +1,58 @@ +var OSRM = require('../../'); +var test = require('tape'); +var berlin_path = require('./osrm-data-path').data_path; + +test('constructor: throws if new keyword is not used', function(assert) { + assert.plan(1); + assert.throws(function() { OSRM(); }, + /Cannot call constructor as function, you need to use 'new' keyword/); +}); + +test('constructor: uses defaults with no parameter', function(assert) { + assert.plan(1); + var osrm = new OSRM(); + assert.ok(osrm); +}); + +test('constructor: does not accept more than one parameter', function(assert) { + assert.plan(1); + assert.throws(function() { new OSRM({}, {}); }, + /Only accepts one parameter/); +}); + +test('constructor: throws if necessary files do not exist', function(assert) { + assert.plan(1); + assert.throws(function() { new OSRM("missing.osrm"); }, + /Invalid file paths/); +}); + +test('constructor: takes a shared memory argument', function(assert) { + assert.plan(1); + var osrm = new OSRM({path: berlin_path, shared_memory: false}); + assert.ok(osrm); +}); + +test('constructor: throws if shared_memory==false with no path defined', function(assert) { + assert.plan(1); + assert.throws(function() { var osrm = new OSRM({shared_memory: false}); }, + /Shared_memory must be enabled if no path is specified/); +}); + +test('constructor: throws if given a non-bool shared_memory option', function(assert) { + assert.plan(1); + assert.throws(function() { var osrm = new OSRM({path: berlin_path, shared_memory: "a"}); }, + /Shared_memory option must be a boolean/); +}); + +test('constructor: throws if given a non-string/obj argument', function(assert) { + assert.plan(1); + assert.throws(function() { var osrm = new OSRM(true); }, + /Parameter must be a path or options object/); +}); + +require('./route.js'); +require('./trip.js'); +require('./match.js'); +require('./tile.js'); +require('./table.js'); +require('./nearest.js'); diff --git a/test/nodejs/match.js b/test/nodejs/match.js new file mode 100644 index 00000000000..3a22f897463 --- /dev/null +++ b/test/nodejs/match.js @@ -0,0 +1,195 @@ +var OSRM = require('../../'); +var test = require('tape'); +var berlin_path = require('./osrm-data-path').data_path; + +test('match: match in Berlin', function(assert) { + assert.plan(5); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.393252,52.542648],[13.39478,52.543079],[13.397389,52.542107]], + timestamps: [1424684612, 1424684616, 1424684620] + }; + osrm.match(options, function(err, response) { + assert.ifError(err); + assert.equal(response.matchings.length, 1); + assert.ok(response.matchings.every(function(m) { + return !!m.distance && !!m.duration && Array.isArray(m.legs) && !!m.geometry && m.confidence > 0; + })) + assert.equal(response.tracepoints.length, 3); + assert.ok(response.tracepoints.every(function(t) { + return !!t.hint && !isNaN(t.matchings_index) && !isNaN(t.waypoint_index) && !!t.name; + })); + }); +}); + +test('match: match in Berlin without timestamps', function(assert) { + assert.plan(3); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.393252,52.542648],[13.39478,52.543079],[13.397389,52.542107]] + }; + osrm.match(options, function(err, response) { + assert.ifError(err); + assert.equal(response.tracepoints.length, 3); + assert.equal(response.matchings.length, 1); + }); +}); + +test('match: match in Berlin without geometry compression', function(assert) { + assert.plan(4); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.393252,52.542648],[13.39478,52.543079],[13.397389,52.542107]], + geometries: 'geojson' + }; + osrm.match(options, function(err, response) { + assert.ifError(err); + assert.equal(response.matchings.length, 1); + assert.ok(response.matchings[0].geometry instanceof Object); + assert.ok(Array.isArray(response.matchings[0].geometry.coordinates)); + }); +}); + +test('match: match in Berlin with geometry compression', function(assert) { + assert.plan(3); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.393252,52.542648],[13.39478,52.543079],[13.397389,52.542107]] + }; + osrm.match(options, function(err, response) { + assert.ifError(err); + assert.equal(response.matchings.length, 1); + assert.equal('string', typeof response.matchings[0].geometry); + }); +}); + +test('match: match in Berlin with speed annotations options', function(assert) { + assert.plan(12); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.393252,52.542648],[13.39478,52.543079],[13.397389,52.542107]], + timestamps: [1424684612, 1424684616, 1424684620], + radiuses: [4.07, 4.07, 4.07], + steps: true, + annotations: ['speed'], + overview: 'false', + geometries: 'geojson' + }; + osrm.match(options, function(err, response) { + assert.ifError(err); + assert.equal(response.matchings.length, 1); + assert.ok(response.matchings[0].confidence > 0, 'has confidence'); + assert.ok(response.matchings[0].legs.every((l) => {return l.steps.length > 0; }), 'every leg has steps'); + assert.ok(response.matchings[0].legs.every((l) => {return l.annotation; }), 'every leg has annotations'); + assert.ok(response.matchings[0].legs.every((l) => {return l.annotation.speed; }), 'every leg has annotations for speed'); + assert.notOk(response.matchings[0].legs.every((l) => {return l.annotation.weight; }), 'has no annotations for weight') + assert.notOk(response.matchings[0].legs.every((l) => {return l.annotation.datasources; }), 'has no annotations for datasources') + assert.notOk(response.matchings[0].legs.every((l) => {return l.annotation.duration; }), 'has no annotations for duration') + assert.notOk(response.matchings[0].legs.every((l) => {return l.annotation.distance; }), 'has no annotations for distance') + assert.notOk(response.matchings[0].legs.every((l) => {return l.annotation.nodes; }), 'has no annotations for nodes') + assert.equal(undefined, response.matchings[0].geometry); + }); +}); + + +test('match: match in Berlin with several (duration, distance, nodes) annotations options', function(assert) { + assert.plan(12); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.393252,52.542648],[13.39478,52.543079],[13.397389,52.542107]], + timestamps: [1424684612, 1424684616, 1424684620], + radiuses: [4.07, 4.07, 4.07], + steps: true, + annotations: ['duration','distance','nodes'], + overview: 'false', + geometries: 'geojson' + }; + osrm.match(options, function(err, response) { + assert.ifError(err); + assert.equal(response.matchings.length, 1); + assert.ok(response.matchings[0].confidence > 0, 'has confidence'); + assert.ok(response.matchings[0].legs.every((l) => {return l.steps.length > 0; }), 'every leg has steps'); + assert.ok(response.matchings[0].legs.every((l) => {return l.annotation; }), 'every leg has annotations'); + assert.ok(response.matchings[0].legs.every((l) => {return l.annotation.distance; }), 'every leg has annotations for distance'); + assert.ok(response.matchings[0].legs.every((l) => {return l.annotation.duration; }), 'every leg has annotations for durations'); + assert.ok(response.matchings[0].legs.every((l) => {return l.annotation.nodes; }), 'every leg has annotations for nodes'); + assert.notOk(response.matchings[0].legs.every((l) => {return l.annotation.weight; }), 'has no annotations for weight') + assert.notOk(response.matchings[0].legs.every((l) => {return l.annotation.datasources; }), 'has no annotations for datasources') + assert.notOk(response.matchings[0].legs.every((l) => {return l.annotation.speed; }), 'has no annotations for speed') + assert.equal(undefined, response.matchings[0].geometry); + }); +}); + +test('match: match in Berlin with all options', function(assert) { + assert.plan(8); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.393252,52.542648],[13.39478,52.543079],[13.397389,52.542107]], + timestamps: [1424684612, 1424684616, 1424684620], + radiuses: [4.07, 4.07, 4.07], + steps: true, + annotations: true, + overview: 'false', + geometries: 'geojson' + }; + osrm.match(options, function(err, response) { + assert.ifError(err); + assert.equal(response.matchings.length, 1); + assert.ok(response.matchings[0].confidence > 0, 'has confidence'); + assert.ok(response.matchings[0].legs.every((l) => {return l.steps.length > 0; }), 'every leg has steps'); + assert.ok(response.matchings[0].legs.every((l) => {return l.annotation; }), 'every leg has annotations'); + assert.ok(response.matchings[0].legs.every((l) => {return l.annotation.distance; }), 'every leg has annotations for distance'); + assert.ok(response.matchings[0].legs.every((l) => {return l.annotation.duration; }), 'every leg has annotations for durations'); + assert.equal(undefined, response.matchings[0].geometry); + }); +}); + +test('match: throws on missing arguments', function(assert) { + assert.plan(1); + var osrm = new OSRM(berlin_path); + assert.throws(function() { osrm.match({}) }, + /Two arguments required/); +}); + +test('match: throws on non-object arg', function(assert) { + assert.plan(1); + var osrm = new OSRM(berlin_path); + assert.throws(function() { osrm.match(null, function(err, response) {}) }, + /First arg must be an object/); +}); + +test('match: throws on invalid coordinates param', function(assert) { + assert.plan(4); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: '' + }; + assert.throws(function() { osrm.match(options, function(err, response) {}) }, + /Coordinates must be an array of \(lon\/lat\) pairs/); + options.coordinates = [[13.393252,52.542648]]; + assert.throws(function() { osrm.match(options, function(err, response) {}) }, + /At least two coordinates must be provided/); + options.coordinates = [13.393252,52.542648]; + assert.throws(function() { osrm.match(options, function(err, response) {}) }, + /Coordinates must be an array of \(lon\/lat\) pairs/); + options.coordinates = [[13.393252],[52.542648]]; + assert.throws(function() { osrm.match(options, function(err, response) {}) }, + /Coordinates must be an array of \(lon\/lat\) pairs/); +}); + +test('match: throws on invalid timestamps param', function(assert) { + assert.plan(3); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.393252,52.542648],[13.39478,52.543079],[13.397389,52.542107]], + timestamps: 'timestamps' + }; + assert.throws(function() { osrm.match(options, function(err, response) {}) }, + /Timestamps must be an array of integers \(or undefined\)/); + options.timestamps = ['invalid', 'timestamp', 'array']; + assert.throws(function() { osrm.match(options, function(err, response) {}) }, + /Timestamps array items must be numbers/); + options.timestamps = [1424684612, 1424684616]; + assert.throws(function() { osrm.match(options, function(err, response) {}) }, + /Timestamp array must have the same size as the coordinates array/); +}); diff --git a/test/nodejs/nearest.js b/test/nodejs/nearest.js new file mode 100644 index 00000000000..d4197b1e127 --- /dev/null +++ b/test/nodejs/nearest.js @@ -0,0 +1,51 @@ +var OSRM = require('../../'); +var test = require('tape'); +var berlin_path = require('./osrm-data-path').data_path; + +test('nearest', function(assert) { + assert.plan(4); + var osrm = new OSRM(berlin_path); + osrm.nearest({ + coordinates: [[13.333086, 52.4224]] + }, function(err, result) { + assert.ifError(err); + assert.equal(result.waypoints.length, 1); + assert.equal(result.waypoints[0].location.length, 2); + assert.ok(result.waypoints[0].hasOwnProperty('name')); + }); +}); + +test('nearest: can ask for multiple nearest pts', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + osrm.nearest({ + coordinates: [[13.333086, 52.4224]], + number: 3 + }, function(err, result) { + assert.ifError(err); + assert.equal(result.waypoints.length, 3); + }); +}); + +test('nearest: throws on invalid args', function(assert) { + assert.plan(6); + var osrm = new OSRM(berlin_path); + var options = {}; + assert.throws(function() { osrm.nearest(options); }, + /Two arguments required/); + assert.throws(function() { osrm.nearest(null, function(err, res) {}); }, + /First arg must be an object/); + options.coordinates = [52.4224]; + assert.throws(function() { osrm.nearest(options, function(err, res) {}); }, + /Coordinates must be an array of /); + options.coordinates = [[13.333086, 52.4224],[13.333086, 52.5224]]; + assert.throws(function() { osrm.nearest(options, function(err, res) {}); }, + /Exactly one coordinate pair must be provided/); + options.coordinates = [[13.333086, 52.4224]]; + options.number = 3.14159; + assert.throws(function() { osrm.nearest(options, function(err, res) {}); }, + /Number must be an integer greater than or equal to 1/); + options.number = 0; + assert.throws(function() { osrm.nearest(options, function(err, res) {}); }, + /Number must be an integer greater than or equal to 1/); +}); diff --git a/test/nodejs/osrm-data-path.js b/test/nodejs/osrm-data-path.js new file mode 100644 index 00000000000..713aa28cb68 --- /dev/null +++ b/test/nodejs/osrm-data-path.js @@ -0,0 +1,8 @@ +var path = require('path'); + +if (process.env.OSRM_DATA_PATH !== undefined) { + exports.data_path = path.join(path.resolve(process.env.OSRM_DATA_PATH), "berlin_CH.osrm"); + console.log('Setting custom data path to ' + exports.data_path); +} else { + exports.data_path = path.resolve(path.join(__dirname, "../data/berlin_CH.osrm")); +} diff --git a/test/nodejs/route.js b/test/nodejs/route.js new file mode 100644 index 00000000000..e83920678d3 --- /dev/null +++ b/test/nodejs/route.js @@ -0,0 +1,471 @@ +var OSRM = require('../../'); +var test = require('tape'); +var berlin_path = require('./osrm-data-path').data_path; + +test('route: routes Berlin', function(assert) { + assert.plan(5); + var osrm = new OSRM(berlin_path); + osrm.route({coordinates: [[13.43864,52.51993],[13.415852,52.513191]]}, function(err, route) { + assert.ifError(err); + assert.ok(route.waypoints); + assert.ok(route.routes); + assert.ok(route.routes.length); + assert.ok(route.routes[0].geometry); + }); +}); + +test('route: throws with too few or invalid args', function(assert) { + assert.plan(3); + var osrm = new OSRM(berlin_path); + assert.throws(function() { osrm.route({coordinates: [[13.43864,52.51993],[13.415852,52.513191]]}) }, + /Two arguments required/); + assert.throws(function() { osrm.route(null, function(err, route) {}) }, + /First arg must be an object/); + assert.throws(function() { osrm.route({coordinates: [[13.43864,52.51993],[13.415852,52.513191]]}, true)}, + /last argument must be a callback function/); +}); + +test('route: provides no alternatives by default, but when requested', function(assert) { + assert.plan(6); + var osrm = new OSRM(berlin_path); + var options = {coordinates: [[13.302383,52.490516], [13.418427,52.522070]]}; + + osrm.route(options, function(err, route) { + assert.ifError(err); + assert.ok(route.routes); + assert.equal(route.routes.length, 1); + }); + options.alternatives = true; + osrm.route(options, function(err, route) { + assert.ifError(err); + assert.ok(route.routes); + assert.equal(route.routes.length, 2); + }); +}); + +test('route: throws with bad params', function(assert) { + assert.plan(11); + var osrm = new OSRM(berlin_path); + assert.throws(function () { osrm.route({coordinates: []}, function(err) {}) }); + assert.throws(function() { osrm.route({}, function(err, route) {}) }, + /Must provide a coordinates property/); + assert.throws(function() { osrm.route({coordinates: null}, function(err, route) {}) }, + /Coordinates must be an array of \(lon\/lat\) pairs/); + assert.throws(function() { osrm.route({coordinates: [13.438640, 52.519930]}, function(err, route) {}) }, + /Coordinates must be an array of \(lon\/lat\) pairs/); + assert.throws(function() { osrm.route({coordinates: [[true, '52.519930'], [13.438640, 52.519930]]}, function(err, route) {}) }, + /Each member of a coordinate pair must be a number/); + assert.throws(function() { osrm.route({coordinates: [[213.43864,252.51993],[413.415852,552.513191]]}, function(err, route) {}) }, + /Lng\/Lat coordinates must be within world bounds \(-180 < lng < 180, -90 < lat < 90\)/); + assert.throws(function() { osrm.route({coordinates: [[13.438640], [52.519930]]}, function(err, route) {}) }, + /Coordinates must be an array of \(lon\/lat\) pairs/); + assert.throws(function() { osrm.route({coordinates: [[13.43864,52.51993],[13.415852,52.513191]], hints: null}, function(err, route) {}) }, + /Hints must be an array of strings\/null/); + assert.throws(function() { osrm.route({coordinates: [[13.43864,52.51993],[13.415852,52.513191]], steps: null}, function(err, route) {}) }); + assert.throws(function() { osrm.route({coordinates: [[13.43864,52.51993],[13.415852,52.513191]], annotations: null}, function(err, route) {}) }); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + alternateRoute: false, + hints: [13.438640, 52.519930] + }; + assert.throws(function() { osrm.route(options, function(err, route) {}) }, + /Hint must be null or string/); +}); + +test('route: routes Berlin using shared memory', function(assert) { + assert.plan(2); + var osrm = new OSRM(); + osrm.route({coordinates: [[13.43864,52.51993],[13.415852,52.513191]]}, function(err, route) { + assert.ifError(err); + assert.ok(Array.isArray(route.routes)); + }); +}); + +test('route: routes Berlin with geometry compression', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + }; + osrm.route(options, function(err, route) { + assert.ifError(err); + assert.equal('string', typeof route.routes[0].geometry); + }); +}); + +test('route: routes Berlin without geometry compression', function(assert) { + assert.plan(4); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + geometries: 'geojson' + }; + osrm.route(options, function(err, route) { + assert.ifError(err); + assert.ok(Array.isArray(route.routes)); + assert.ok(Array.isArray(route.routes[0].geometry.coordinates)); + assert.equal(route.routes[0].geometry.type, 'LineString'); + }); +}); + +test('Test polyline6 geometries option', function(assert) { + assert.plan(6); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + continue_straight: false, + overview: 'false', + geometries: 'polyline6', + steps: true + }; + osrm.route(options, function(err, first) { + assert.ifError(err); + assert.ok(first.routes); + assert.equal(first.routes.length, 1); + assert.notOk(first.routes[0].geometry); + assert.ok(first.routes[0].legs[0]); + assert.equal(typeof first.routes[0].legs[0].steps[0].geometry, 'string'); + }); +}); + +test('route: routes Berlin with speed annotations options', function(assert) { + assert.plan(17); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + continue_straight: false, + overview: 'false', + geometries: 'polyline', + steps: true, + annotations: ['speed'] + }; + osrm.route(options, function(err, first) { + assert.ifError(err); + assert.ok(first.routes); + assert.ok(first.routes[0].legs.every(function(l) { return Array.isArray(l.steps) && l.steps.length > 0; })); + assert.equal(first.routes.length, 1); + assert.notOk(first.routes[0].geometry); + assert.ok(first.routes[0].legs[0]); + assert.ok(first.routes[0].legs.every(l => { return l.steps.length > 0; }), 'every leg has steps'); + assert.ok(first.routes[0].legs.every(l => { return l.annotation;}), 'every leg has annotations'); + assert.ok(first.routes[0].legs.every(l => { return l.annotation.speed;}), 'every leg has annotations for speed'); + assert.notOk(first.routes[0].legs.every(l => { return l.annotation.weight; }), 'has no annotations for weight') + assert.notOk(first.routes[0].legs.every(l => { return l.annotation.datasources; }), 'has no annotations for datasources') + assert.notOk(first.routes[0].legs.every(l => { return l.annotation.duration; }), 'has no annotations for duration') + assert.notOk(first.routes[0].legs.every(l => { return l.annotation.distance; }), 'has no annotations for distance') + assert.notOk(first.routes[0].legs.every(l => { return l.annotation.nodes; }), 'has no annotations for nodes') + + options.overview = 'full'; + osrm.route(options, function(err, full) { + assert.ifError(err); + options.overview = 'simplified'; + osrm.route(options, function(err, simplified) { + assert.ifError(err); + assert.notEqual(full.routes[0].geometry, simplified.routes[0].geometry); + }); + }); + }); +}); + +test('route: routes Berlin with several (duration, distance, nodes) annotations options', function(assert) { + assert.plan(17); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + continue_straight: false, + overview: 'false', + geometries: 'polyline', + steps: true, + annotations: ['duration','distance','nodes'] + }; + osrm.route(options, function(err, first) { + assert.ifError(err); + assert.ok(first.routes); + assert.ok(first.routes[0].legs.every(function(l) { return Array.isArray(l.steps) && l.steps.length > 0; })); + assert.equal(first.routes.length, 1); + assert.notOk(first.routes[0].geometry); + assert.ok(first.routes[0].legs[0]); + assert.ok(first.routes[0].legs.every(l => { return l.steps.length > 0; }), 'every leg has steps'); + assert.ok(first.routes[0].legs.every(l => { return l.annotation;}), 'every leg has annotations'); + assert.ok(first.routes[0].legs.every(l => { return l.annotation.distance;}), 'every leg has annotations for distance'); + assert.ok(first.routes[0].legs.every(l => { return l.annotation.duration;}), 'every leg has annotations for durations'); + assert.ok(first.routes[0].legs.every(l => { return l.annotation.nodes;}), 'every leg has annotations for nodes'); + assert.notOk(first.routes[0].legs.every(l => { return l.annotation.weight; }), 'has no annotations for weight') + assert.notOk(first.routes[0].legs.every(l => { return l.annotation.datasources; }), 'has no annotations for datasources') + assert.notOk(first.routes[0].legs.every(l => { return l.annotation.speed; }), 'has no annotations for speed') + + options.overview = 'full'; + osrm.route(options, function(err, full) { + assert.ifError(err); + options.overview = 'simplified'; + osrm.route(options, function(err, simplified) { + assert.ifError(err); + assert.notEqual(full.routes[0].geometry, simplified.routes[0].geometry); + }); + }); + }); +}); + +test('route: routes Berlin with options', function(assert) { + assert.plan(11); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + continue_straight: false, + overview: 'false', + geometries: 'polyline', + steps: true, + annotations: true + }; + osrm.route(options, function(err, first) { + assert.ifError(err); + assert.ok(first.routes); + assert.ok(first.routes[0].legs.every(function(l) { return Array.isArray(l.steps) && l.steps.length > 0; })); + assert.equal(first.routes.length, 1); + assert.notOk(first.routes[0].geometry); + assert.ok(first.routes[0].legs[0]); + assert.ok(first.routes[0].legs.every(l => { return l.steps.length > 0; }), 'every leg has steps'); + assert.ok(first.routes[0].legs.every(l => { return l.annotation;}), 'every leg has annotations'); + + options.overview = 'full'; + osrm.route(options, function(err, full) { + assert.ifError(err); + options.overview = 'simplified'; + osrm.route(options, function(err, simplified) { + assert.ifError(err); + assert.notEqual(full.routes[0].geometry, simplified.routes[0].geometry); + }); + }); + }); +}); + +test('route: routes Berlin with options', function(assert) { + assert.plan(11); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + continue_straight: false, + overview: 'false', + geometries: 'polyline', + steps: true, + annotations: true + }; + osrm.route(options, function(err, first) { + assert.ifError(err); + assert.ok(first.routes); + assert.ok(first.routes[0].legs.every(function(l) { return Array.isArray(l.steps) && l.steps.length > 0; })); + assert.equal(first.routes.length, 1); + assert.notOk(first.routes[0].geometry); + assert.ok(first.routes[0].legs[0]); + assert.ok(first.routes[0].legs.every(l => { return l.steps.length > 0; }), 'every leg has steps'); + assert.ok(first.routes[0].legs.every(l => { return l.annotation;}), 'every leg has annotations'); + + options.overview = 'full'; + osrm.route(options, function(err, full) { + assert.ifError(err); + options.overview = 'simplified'; + osrm.route(options, function(err, simplified) { + assert.ifError(err); + assert.notEqual(full.routes[0].geometry, simplified.routes[0].geometry); + }); + }); + }); +}); + +test('route: invalid route options', function(assert) { + assert.plan(8); + var osrm = new OSRM(berlin_path); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + continue_straight: [] + }, function(err, route) {}); }, + /must be boolean/); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + alternatives: [] + }, function(err, route) {}); }, + /must be boolean/); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + geometries: true + }, function(err, route) {}); }, + /Geometries must be a string: \[polyline, polyline6, geojson\]/); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + overview: false + }, function(err, route) {}); }, + /Overview must be a string: \[simplified, full, false\]/); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + overview: false + }, function(err, route) {}); }, + /Overview must be a string: \[simplified, full, false\]/); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + overview: 'maybe' + }, function(err, route) {}); }, + /'overview' param must be one of \[simplified, full, false\]/); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + geometries: 'maybe' + }, function(err, route) {}); }, + /'geometries' param must be one of \[polyline, polyline6, geojson\]/); + assert.throws(function() { osrm.route({ + coordinates: [[NaN, -NaN],[Infinity, -Infinity]] + }, function(err, route) {}); }, + /Lng\/Lat coordinates must be valid numbers/); +}); + +test('route: integer bearing values no longer supported', function(assert) { + assert.plan(1); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + bearings: [200, 250], + }; + assert.throws(function() { osrm.route(options, function(err, route) {}); }, + /Bearing must be an array of \[bearing, range\] or null/); +}); + +test('route: valid bearing values', function(assert) { + assert.plan(4); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + bearings: [[200, 180], [250, 180]], + }; + osrm.route(options, function(err, route) { + assert.ifError(err); + assert.ok(route.routes[0]); + }); + options.bearings = [null, [360, 180]]; + osrm.route(options, function(err, route) { + assert.ifError(err); + assert.ok(route.routes[0]); + }); +}); + +test('route: invalid bearing values', function(assert) { + assert.plan(6); + var osrm = new OSRM(berlin_path); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + bearings: [[400, 180], [-250, 180]], + }, function(err, route) {}) }, + /Bearing values need to be in range 0..360, 0..180/); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + bearings: [[200], [250, 180]], + }, function(err, route) {}) }, + /Bearing must be an array of/); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + bearings: [[400, 109], [100, 720]], + }, function(err, route) {}) }, + /Bearing values need to be in range 0..360, 0..180/); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + bearings: 400, + }, function(err, route) {}) }, + /Bearings must be an array of arrays of numbers/); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + bearings: [[100, 100]], + }, function(err, route) {}) }, + /Bearings array must have the same length as coordinates array/); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + bearings: [Infinity, Infinity], + }, function(err, route) {}) }, + /Bearing must be an array of \[bearing, range\] or null/); +}); + +test('route: routes Berlin with hints', function(assert) { + assert.plan(5); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]] + }; + osrm.route(options, function(err, first) { + assert.ifError(err); + assert.ok(first.waypoints); + var hints = first.waypoints.map(function(wp) { return wp.hint; }); + assert.ok(hints.every(function(h) { return typeof h === 'string'; })); + + options.hints = hints; + + osrm.route(options, function(err, second) { + assert.ifError(err); + assert.deepEqual(first, second); + }); + }); +}); + +test('route: routes Berlin with null hints', function(assert) { + assert.plan(1); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + hints: [null, null] + }; + osrm.route(options, function(err, route) { + assert.ifError(err); + }); +}); + +test('route: throws on bad hints', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + hints: ['', ''] + }, function(err, route) {})}, /Hint cannot be an empty string/); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + hints: [null] + }, function(err, route) {})}, /Hints array must have the same length as coordinates array/); +}); + +test('route: routes Berlin with valid radius values', function(assert) { + assert.plan(3); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + radiuses: [100, 100] + }; + osrm.route(options, function(err, route) { + assert.ifError(err); + }); + options.radiuses = [null, null]; + osrm.route(options, function(err, route) { + assert.ifError(err); + }); + options.radiuses = [100, null]; + osrm.route(options, function(err, route) { + assert.ifError(err); + }); +}); + +test('route: throws on bad radiuses', function(assert) { + assert.plan(3); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + radiuses: [10, 10] + }; + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + radiuses: 10 + }, function(err, route) {}) }, + /Radiuses must be an array of non-negative doubles or null/); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + radiuses: ['magic', 'numbers'] + }, function(err, route) {}) }, + /Radius must be non-negative double or null/); + assert.throws(function() { osrm.route({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + radiuses: [10] + }, function(err, route) {}) }, + /Radiuses array must have the same length as coordinates array/); +}); diff --git a/test/nodejs/table.js b/test/nodejs/table.js new file mode 100644 index 00000000000..a1b05365c7c --- /dev/null +++ b/test/nodejs/table.js @@ -0,0 +1,161 @@ +var OSRM = require('../../'); +var test = require('tape'); +var berlin_path = require('./osrm-data-path').data_path; + +test('table: distance table in Berlin', function(assert) { + assert.plan(9); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]] + }; + osrm.table(options, function(err, table) { + assert.ifError(err); + assert.ok(Array.isArray(table.durations), 'result must be an array'); + var row_count = table.durations.length; + for (var i = 0; i < row_count; ++i) { + var column = table.durations[i]; + var column_count = column.length; + assert.equal(row_count, column_count); + for (var j = 0; j < column_count; ++j) { + if (i == j) { + // check that diagonal is zero + assert.equal(0, column[j], 'diagonal must be zero'); + } else { + // everything else is non-zero + assert.notEqual(0, column[j], 'other entries must be non-zero'); + } + } + } + assert.equal(options.coordinates.length, row_count); + }); +}); + +test('table: distance table in Berlin with sources/destinations', function(assert) { + assert.plan(6); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + sources: [0], + destinations: [0,1] + }; + osrm.table(options, function(err, table) { + assert.ifError(err); + assert.ok(Array.isArray(table.durations), 'result must be an array'); + var row_count = table.durations.length; + for (var i = 0; i < row_count; ++i) { + var column = table.durations[i]; + var column_count = column.length; + assert.equal(options.destinations.length, column_count); + for (var j = 0; j < column_count; ++j) { + if (i == j) { + // check that diagonal is zero + assert.equal(0, column[j], 'diagonal must be zero'); + } else { + // everything else is non-zero + assert.notEqual(0, column[j], 'other entries must be non-zero'); + } + } + } + assert.equal(options.sources.length, row_count); + }); +}); + +test('table: throws on invalid arguments', function(assert) { + assert.plan(14); + var osrm = new OSRM(berlin_path); + var options = {}; + assert.throws(function() { osrm.table(options); }, + /Two arguments required/); + options.coordinates = null; + assert.throws(function() { osrm.table(options, function() {}); }, + /Coordinates must be an array of \(lon\/lat\) pairs/); + options.coordinates = [[13.393252,52.542648]]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /At least two coordinates must be provided/); + options.coordinates = [13.393252,52.542648]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Coordinates must be an array of \(lon\/lat\) pairs/); + options.coordinates = [[13.393252],[52.542648]]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Coordinates must be an array of \(lon\/lat\) pairs/); + + options.coordinates = [[13.393252,52.542648],[13.393252,52.542648]]; + options.sources = true; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Sources must be an array of indices \(or undefined\)/); + options.sources = [0, 4]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Source indices must be less than or equal to the number of coordinates/); + options.sources = [0.3, 1.1]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Source must be an integer/); + + options.destinations = true; + delete options.sources; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Destinations must be an array of indices \(or undefined\)/); + options.destinations = [0, 4]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Destination indices must be less than or equal to the number of coordinates/); + options.destinations = [0.3, 1.1]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Destination must be an integer/); + + // does not throw: the following two have been changed in OSRM v5 + options.sources = [0, 1]; + delete options.destinations; + assert.doesNotThrow(function() { osrm.table(options, function(err, response) {}) }, + /Both sources and destinations need to be specified/); + options.destinations = [0, 1]; + assert.doesNotThrow(function() { osrm.table(options, function(err, response) {}) }, + /You can either specify sources and destinations, or coordinates/); + + assert.throws(function() { osrm.route({coordinates: [[13.43864,52.51993],[13.415852,52.513191]], generate_hints: null}, function(err, route) {}) }, + /generate_hints must be of type Boolean/); +}); + +test('table: throws on invalid arguments', function(assert) { + assert.plan(1); + var osrm = new OSRM(berlin_path); + assert.throws(function() { osrm.table(null, function() {}); }, + /First arg must be an object/); +}); + +test('table: distance table in Berlin with hints', function(assert) { + assert.plan(5); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + generate_hints: true // true is default but be explicit here + }; + osrm.table(options, function(err, table) { + console.log(table); + assert.ifError(err); + + function assertHasHints(waypoint) { + assert.notStrictEqual(waypoint.hint, undefined); + } + + table.sources.map(assertHasHints); + table.destinations.map(assertHasHints); + }); +}); + +test('table: distance table in Berlin without hints', function(assert) { + assert.plan(5); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + generate_hints: false // true is default + }; + osrm.table(options, function(err, table) { + assert.ifError(err); + + function assertHasNoHints(waypoint) { + assert.strictEqual(waypoint.hint, undefined); + } + + table.sources.map(assertHasNoHints); + table.destinations.map(assertHasNoHints); + }); +}); diff --git a/test/nodejs/tile.js b/test/nodejs/tile.js new file mode 100644 index 00000000000..a273df63ec2 --- /dev/null +++ b/test/nodejs/tile.js @@ -0,0 +1,23 @@ +var OSRM = require('../../'); +var test = require('tape'); +var berlin_path = "test/data/berlin-latest.osrm"; + +test.test('tile check size coarse', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + osrm.tile([17603, 10747, 15], function(err, result) { + assert.ifError(err); + assert.ok(result.length > 35000); + }); +}); + +// FIXME the size of the tile that is returned depends on the architecture +// See issue #3343 in osrm-backend +test.skip('tile', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + osrm.tile([17603, 10747, 15], function(err, result) { + assert.ifError(err); + assert.equal(result.length, 35970); + }); +}); diff --git a/test/nodejs/trip.js b/test/nodejs/trip.js new file mode 100644 index 00000000000..52b62031fc4 --- /dev/null +++ b/test/nodejs/trip.js @@ -0,0 +1,333 @@ +var OSRM = require('../../'); +var test = require('tape'); +var berlin_path = require('./osrm-data-path').data_path; + +test('trip: trip in Berlin', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + osrm.trip({coordinates: [[13.36761474609375,52.51663871100423],[13.374481201171875,52.506191342034576]]}, function(err, trip) { + assert.ifError(err); + for (t = 0; t < trip.trips.length; t++) { + assert.ok(trip.trips[t].geometry); + } + }); +}); + +test('trip: trip with many locations in Berlin', function(assert) { + assert.plan(4); + var osrm = new OSRM(berlin_path); + var opts = {coordinates: [[13.36761474609375,52.51663871100423],[13.374481201171875,52.506191342034576],[13.404693603515625,52.50535544522142],[13.388900756835938,52.50159371284434],[13.386840820312498,52.518727886767266],[13.4088134765625,52.528754547664185],[13.41156005859375,52.51705655410405],[13.420486450195312,52.512042174642346],[13.413619995117188,52.50368360390624],[13.36212158203125,52.504101570196205],[13.35113525390625,52.52248815280757],[13.36761474609375,52.53460237630516],[13.383407592773438,52.53710835019913],[13.392333984375,52.536690697815736],[13.42529296875,52.532931647583325],[13.399200439453125,52.52415927884915],[13.390960693359375,52.51956352925745],[13.375167846679688,52.533349335723294],[13.37860107421875,52.520399155853454],[13.355255126953125,52.52081696319122],[13.385467529296875,52.5143405029259],[13.398857116699219,52.513086884218325],[13.399200439453125,52.50744515744915],[13.409500122070312,52.49783165855699],[13.424949645996094,52.500339730516934],[13.440055847167969,52.50786308797268],[13.428382873535156,52.511624283857785],[13.437652587890625,52.50451953251202],[13.443145751953125,52.5199813445422],[13.431129455566406,52.52520370034151],[13.418426513671875,52.52896341209634],[13.429069519042969,52.517474393230245],[13.418083190917969,52.528127948407935],[13.405036926269531,52.52833681581998],[13.384437561035156,52.53084314728766],[13.374481201171875,52.53084314728766],[13.3978271484375,52.532305107923925],[13.418769836425781,52.526039219655445],[13.441085815429688,52.51642978796417],[13.448638916015625,52.51601193890388],[13.44623565673828,52.50535544522142],[13.430442810058594,52.502638670794546],[13.358688354492188,52.520190250694526],[13.358001708984375,52.531887409851336],[13.367271423339842,52.528545682238736],[13.387870788574219,52.52958999943304],[13.406410217285156,52.53961418106945],[13.399543762207031,52.50556442091497],[13.374824523925781,52.50389258754797],[13.386154174804688,52.51099744023003],[13.40229034423828,52.49657756892365]] +}; + osrm.trip(opts, function(err, trip) { + assert.ifError(err); + for (t = 0; t < trip.trips.length; t++) { + assert.ok(trip.trips[t].geometry); + } + assert.equal(opts.coordinates.length, trip.waypoints.length); + var indexMap = trip.waypoints.map(function(wp, i) { + return [i, wp.waypoint_index]; + }); + assert.ok(!indexMap.every(function(tuple) { return tuple[0] === tuple[1]; })); + }); +}); + +test('trip: throws with too few or invalid args', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + assert.throws(function() { osrm.trip({coordinates: [[13.43864,52.51993],[13.415852,52.513191]]}) }, + /Two arguments required/); + assert.throws(function() { osrm.trip(null, function(err, trip) {}) }, + /First arg must be an object/); +}); + +test('trip: throws with bad params', function(assert) { + assert.plan(14); + var osrm = new OSRM(berlin_path); + assert.throws(function () { osrm.trip({coordinates: []}, function(err) {}) }); + assert.throws(function() { osrm.trip({}, function(err, trip) {}) }, + /Must provide a coordinates property/); + assert.throws(function() { osrm.trip({ + coordinates: null + }, function(err, trip) {}) }, + /Coordinates must be an array of \(lon\/lat\) pairs/); + assert.throws(function() { osrm.trip({ + coordinates: [13.438640, 52.519930] + }, function(err, trip) {}) }, + /Coordinates must be an array of \(lon\/lat\) pairs/); + assert.throws(function() { osrm.trip({ + coordinates: [[13.438640], [52.519930]] + }, function(err, trip) {}) }, + /Coordinates must be an array of \(lon\/lat\) pairs/); + assert.throws(function() { osrm.trip({ + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + hints: null + }, function(err, trip) {}) }, + /Hints must be an array of strings\/null/); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + printInstructions: false, + hints: [13.438640, 52.519930] + }; + assert.throws(function() { osrm.trip(options, function(err, trip) {}); }, + /Hint must be null or string/); + options.hints = [null]; + assert.throws(function() { osrm.trip(options, function(err, trip) {}); }, + /Hints array must have the same length as coordinates array/); + delete options.hints; + options.geometries = 'false'; + assert.throws(function() { osrm.trip(options, function(err, trip) {}); }, + /'geometries' param must be one of \[polyline, polyline6, geojson\]/); + delete options.geometries; + options.source = false; + assert.throws(function() { osrm.trip(options, function(err, trip) {}); }, + /Source must be a string: \[any, first\]/); + options.source = 'false'; + assert.throws(function() { osrm.trip(options, function(err, trip) {}); }, + /'source' param must be one of \[any, first\]/); + delete options.source; + options.destination = true; + assert.throws(function() { osrm.trip(options, function(err, trip) {}); }, + /Destination must be a string: \[any, last\]/); + options.destination = 'true'; + assert.throws(function() { osrm.trip(options, function(err, trip) {}); }, + /'destination' param must be one of \[any, last\]/); + options.roundtrip = 'any'; + assert.throws(function() { osrm.trip(options, function(err, trip) {}); }, + /'roundtrip' param must be a boolean/); +}); + +test('trip: routes Berlin using shared memory', function(assert) { + assert.plan(2); + var osrm = new OSRM(); + osrm.trip({coordinates: [[13.43864,52.51993],[13.415852,52.513191]]}, function(err, trip) { + assert.ifError(err); + for (t = 0; t < trip.trips.length; t++) { + assert.ok(trip.trips[t].geometry); + } + }); +}); + +test('trip: routes Berlin with hints', function(assert) { + assert.plan(5); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + steps: false + }; + osrm.trip(options, function(err, first) { + assert.ifError(err); + for (t = 0; t < first.trips.length; t++) { + assert.ok(first.trips[t].geometry); + } + var hints = first.waypoints.map(function(wp) { return wp.hint; }); + assert.ok(hints.every(function(h) { return typeof h === 'string'; })); + options.hints = hints; + + osrm.trip(options, function(err, second) { + assert.ifError(err); + assert.deepEqual(first, second); + }); + }); +}); + +test('trip: trip through Berlin with geometry compression', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]] + }; + osrm.trip(options, function(err, trip) { + assert.ifError(err); + for (t = 0; t < trip.trips.length; t++) { + assert.equal('string', typeof trip.trips[t].geometry); + } + }); +}); + +test('trip: trip through Berlin without geometry compression', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + geometries: 'geojson' + }; + osrm.trip(options, function(err, trip) { + assert.ifError(err); + for (t = 0; t < trip.trips.length; t++) { + assert.ok(Array.isArray(trip.trips[t].geometry.coordinates)); + } + }); +}); + +test('trip: trip through Berlin with speed annotations options', function(assert) { + assert.plan(12); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + steps: true, + annotations: ['speed'], + overview: 'false' + }; + osrm.trip(options, function(err, trip) { + assert.ifError(err); + assert.equal(trip.trips.length, 1); + for (t = 0; t < trip.trips.length; t++) { + assert.ok(trip.trips[t]); + assert.ok(trip.trips[t].legs.every(function(l) { return l.steps.length > 0; }), 'every leg has steps') + assert.ok(trip.trips[t].legs.every(function(l) { return l.annotation; }), 'every leg has annotations') + assert.ok(trip.trips[t].legs.every(function(l) { return l.annotation.speed; }), 'every leg has annotations for speed') + assert.notOk(trip.trips[t].legs.every(function(l) { return l.annotation.weight; }), 'has no annotations for weight') + assert.notOk(trip.trips[t].legs.every(function(l) { return l.annotation.datasources; }), 'has no annotations for datasources') + assert.notOk(trip.trips[t].legs.every(function(l) { return l.annotation.duration; }), 'has no annotations for duration') + assert.notOk(trip.trips[t].legs.every(function(l) { return l.annotation.distance; }), 'has no annotations for distance') + assert.notOk(trip.trips[t].legs.every(function(l) { return l.annotation.nodes; }), 'has no annotations for nodes') + assert.notOk(trip.trips[t].geometry); + } + }); +}); + +test('trip: trip through Berlin with several (duration, distance, nodes) annotations options', function(assert) { + assert.plan(12); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + steps: true, + annotations: ['duration', 'distance', 'nodes'], + overview: 'false' + }; + osrm.trip(options, function(err, trip) { + assert.ifError(err); + assert.equal(trip.trips.length, 1); + for (t = 0; t < trip.trips.length; t++) { + assert.ok(trip.trips[t]); + assert.ok(trip.trips[t].legs.every(function(l) { return l.steps.length > 0; }), 'every leg has steps') + assert.ok(trip.trips[t].legs.every(function(l) { return l.annotation; }), 'every leg has annotations') + assert.ok(trip.trips[t].legs.every(function(l) { return l.annotation.duration; }), 'every leg has annotations for duration') + assert.ok(trip.trips[t].legs.every(function(l) { return l.annotation.distance; }), 'every leg has annotations for distance') + assert.ok(trip.trips[t].legs.every(function(l) { return l.annotation.nodes; }), 'every leg has annotations for nodes') + assert.notOk(trip.trips[t].legs.every(function(l) { return l.annotation.weight; }), 'has no annotations for weight') + assert.notOk(trip.trips[t].legs.every(function(l) { return l.annotation.datasources; }), 'has no annotations for datasources') + assert.notOk(trip.trips[t].legs.every(function(l) { return l.annotation.speed; }), 'has no annotations for speed') + assert.notOk(trip.trips[t].geometry); + } + }); +}); + +test('trip: trip through Berlin with options', function(assert) { + assert.plan(6); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + steps: true, + annotations: true, + overview: 'false' + }; + osrm.trip(options, function(err, trip) { + assert.ifError(err); + assert.equal(trip.trips.length, 1); + for (t = 0; t < trip.trips.length; t++) { + assert.ok(trip.trips[t]); + assert.ok(trip.trips[t].legs.every(function(l) { return l.steps.length > 0; }), 'every leg has steps') + assert.ok(trip.trips[t].legs.every(function(l) { return l.annotation; }), 'every leg has annotations') + assert.notOk(trip.trips[t].geometry); + } + }); +}); + +test('trip: routes Berlin with null hints', function(assert) { + assert.plan(1); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + printInstructions: false, + hints: [null, null] + }; + osrm.trip(options, function(err, second) { + assert.ifError(err); + }); +}); + +test('trip: service combinations that are not implemented', function(assert) { + assert.plan(3); + var osrm = new OSRM(berlin_path); + + // fixed start, non-roundtrip + var options = { + coordinates: [[13.43864,52.51993],[13.415852,52.513191]], + source: 'first', + roundtrip: false + }; + osrm.trip(options, function(err, second) { + assert.equal('NotImplemented', err.message); + }); + + // fixed start, fixed end, non-roundtrip + options.source = 'any'; + options.destination = 'any'; + osrm.trip(options, function(err, second) { + assert.equal('NotImplemented', err.message); + }); + + // fixed end, non-roundtrip + delete options.source; + options.destination = 'last'; + osrm.trip(options, function(err, second) { + assert.equal('NotImplemented', err.message); + }); + +}); + +test('trip: fixed start and end combinations', function(assert) { + var osrm = new OSRM(berlin_path); + + var options = { + coordinates: [[13.36761474609375,52.51663871100423],[13.374481201171875,52.506191342034576]], + source: 'first', + destination: 'last', + roundtrip: false, + geometries: 'geojson' + }; + + // fixed start and end, non-roundtrip + osrm.trip(options, function(err, fseTrip) { + assert.ifError(err); + assert.equal(206.8, fseTrip.trips[0].duration); + assert.equal(1, fseTrip.trips.length); + var coordinates = fseTrip.trips[0].geometry.coordinates; + assert.equal(15, coordinates.length); + assert.notEqual(JSON.stringify(coordinates[0]), JSON.stringify(coordinates[coordinates.length - 1])); + }); + + // variations of roundtrip + + var roundtripChecks = function(options) { + osrm.trip(options, function(err, trip) { + assert.ifError(err); + assert.equal(1, trip.trips.length); + assert.equal(422, Math.round(trip.trips[0].duration)); + var coordinates = trip.trips[0].geometry.coordinates; + assert.equal(29, coordinates.length); + assert.equal(JSON.stringify(coordinates[0]), JSON.stringify(coordinates[coordinates.length - 1])); + }); + } + + // roundtrip, source and destination not specified + roundtripChecks({coordinates: options.coordinates, geometries: options.geometries}); + + // roundtrip, fixed destination + options.roundtrip = true; + delete options.source; + roundtripChecks(options); + + //roundtrip, fixed source + delete options.destination; + options.source = 'first'; + roundtripChecks(options); + + // roundtrip, non-fixed source, non-fixed destination + options.source = 'any'; + options.destination = 'any'; + roundtripChecks(options); + + assert.end(); +}); diff --git a/unit_tests/library/coordinates.hpp b/unit_tests/library/coordinates.hpp index e2cb7e8db72..22f7185b204 100644 --- a/unit_tests/library/coordinates.hpp +++ b/unit_tests/library/coordinates.hpp @@ -5,7 +5,7 @@ #include -// Somewhere in 2b8dd9343d5e615afc9c67bcc7028a63 Monaco +// Somewhere in d41d8cd98f00b204e9800998ecf8427e Berlin // Convenience aliases using Longitude = osrm::util::FloatLongitude; @@ -15,21 +15,21 @@ using Locations = std::vector; inline Location get_dummy_location() { - return {osrm::util::FloatLongitude{7.437069}, osrm::util::FloatLatitude{43.749249}}; + return {osrm::util::FloatLongitude{13.388860}, osrm::util::FloatLatitude{52.517037}}; } inline Locations get_locations_in_small_component() { - return {{Longitude{7.438023}, Latitude{43.746465}}, - {Longitude{7.439263}, Latitude{43.746543}}, - {Longitude{7.438190}, Latitude{43.747560}}}; + return {{Longitude{13.459765}, Latitude{52.543193}}, + {Longitude{13.461455}, Latitude{52.542381}}, + {Longitude{13.462940}, Latitude{52.541774}}}; } inline Locations get_locations_in_big_component() { - return {{Longitude{7.415800}, Latitude{43.734132}}, - {Longitude{7.417710}, Latitude{43.736721}}, - {Longitude{7.421315}, Latitude{43.738814}}}; + return {{Longitude{13.442631}, Latitude{52.551110}}, + {Longitude{13.441193}, Latitude{52.549506}}, + {Longitude{13.439648}, Latitude{52.547705}}}; } #endif diff --git a/unit_tests/library/extract.cpp b/unit_tests/library/extract.cpp index b65f4b9c3ed..9c205fca7e3 100644 --- a/unit_tests/library/extract.cpp +++ b/unit_tests/library/extract.cpp @@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(test_extract_with_invalid_config) BOOST_AUTO_TEST_CASE(test_extract_with_valid_config) { osrm::ExtractorConfig config; - config.input_path = {OSRM_TEST_DATA_DIR "/monaco.osm.pbf"}; + config.input_path = OSRM_TEST_DATA_DIR "/berlin.osm.pbf"; config.requested_num_threads = tbb::task_scheduler_init::default_num_threads(); BOOST_CHECK_NO_THROW(osrm::extract(config)); } diff --git a/unit_tests/library/limits.cpp b/unit_tests/library/limits.cpp index 912c89cbc71..d6edec10090 100644 --- a/unit_tests/library/limits.cpp +++ b/unit_tests/library/limits.cpp @@ -28,7 +28,7 @@ BOOST_AUTO_TEST_CASE(test_trip_limits) using namespace osrm; EngineConfig config; - config.storage_config = {OSRM_TEST_DATA_DIR "/monaco_CH.osrm"}; + config.storage_config = {OSRM_TEST_DATA_DIR "/berlin_CH.osrm"}; config.use_shared_memory = false; config.max_locations_trip = 2; @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(test_route_limits) using namespace osrm; EngineConfig config; - config.storage_config = {OSRM_TEST_DATA_DIR "/monaco_CH.osrm"}; + config.storage_config = {OSRM_TEST_DATA_DIR "/berlin_CH.osrm"}; config.use_shared_memory = false; config.max_locations_viaroute = 2; @@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE(test_table_limits) using namespace osrm; EngineConfig config; - config.storage_config = {OSRM_TEST_DATA_DIR "/monaco_CH.osrm"}; + config.storage_config = {OSRM_TEST_DATA_DIR "/berlin_CH.osrm"}; config.use_shared_memory = false; config.max_locations_distance_table = 2; @@ -109,7 +109,7 @@ BOOST_AUTO_TEST_CASE(test_match_limits) using namespace osrm; EngineConfig config; - config.storage_config = {OSRM_TEST_DATA_DIR "/monaco_CH.osrm"}; + config.storage_config = {OSRM_TEST_DATA_DIR "/berlin_CH.osrm"}; config.use_shared_memory = false; config.max_locations_map_matching = 2; @@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(test_nearest_limits) using namespace osrm; EngineConfig config; - config.storage_config = {OSRM_TEST_DATA_DIR "/monaco_CH.osrm"}; + config.storage_config = {OSRM_TEST_DATA_DIR "/berlin_CH.osrm"}; config.use_shared_memory = false; config.max_results_nearest = 2; diff --git a/unit_tests/library/match.cpp b/unit_tests/library/match.cpp index 60fedb0c169..20626a3017f 100644 --- a/unit_tests/library/match.cpp +++ b/unit_tests/library/match.cpp @@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(test_match) { using namespace osrm; - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); MatchParameters params; params.coordinates.push_back(get_dummy_location()); diff --git a/unit_tests/library/nearest.cpp b/unit_tests/library/nearest.cpp index 6f8a25d2cee..a4e5f6067ab 100644 --- a/unit_tests/library/nearest.cpp +++ b/unit_tests/library/nearest.cpp @@ -16,7 +16,7 @@ BOOST_AUTO_TEST_SUITE(nearest) BOOST_AUTO_TEST_CASE(test_nearest_response) { - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); using namespace osrm; @@ -43,7 +43,7 @@ BOOST_AUTO_TEST_CASE(test_nearest_response) BOOST_AUTO_TEST_CASE(test_nearest_response_no_coordinates) { - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); using namespace osrm; @@ -59,7 +59,7 @@ BOOST_AUTO_TEST_CASE(test_nearest_response_no_coordinates) BOOST_AUTO_TEST_CASE(test_nearest_response_multiple_coordinates) { - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); using namespace osrm; @@ -77,7 +77,7 @@ BOOST_AUTO_TEST_CASE(test_nearest_response_multiple_coordinates) BOOST_AUTO_TEST_CASE(test_nearest_response_for_location_in_small_component) { - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); using namespace osrm; diff --git a/unit_tests/library/options.cpp b/unit_tests/library/options.cpp index 50eb9f534bc..4889d6ae998 100644 --- a/unit_tests/library/options.cpp +++ b/unit_tests/library/options.cpp @@ -14,7 +14,7 @@ BOOST_AUTO_TEST_CASE(test_ch) using namespace osrm; EngineConfig config; config.use_shared_memory = false; - config.storage_config = storage::StorageConfig(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + config.storage_config = storage::StorageConfig(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); config.algorithm = EngineConfig::Algorithm::CH; OSRM osrm{config}; } @@ -24,7 +24,7 @@ BOOST_AUTO_TEST_CASE(test_corech) using namespace osrm; EngineConfig config; config.use_shared_memory = false; - config.storage_config = storage::StorageConfig(OSRM_TEST_DATA_DIR "/monaco_CoreCH.osrm"); + config.storage_config = storage::StorageConfig(OSRM_TEST_DATA_DIR "/berlin_CoreCH.osrm"); config.algorithm = EngineConfig::Algorithm::CoreCH; OSRM osrm{config}; } @@ -34,7 +34,7 @@ BOOST_AUTO_TEST_CASE(test_mld) using namespace osrm; EngineConfig config; config.use_shared_memory = false; - config.storage_config = storage::StorageConfig(OSRM_TEST_DATA_DIR "/monaco_MLD.osrm"); + config.storage_config = storage::StorageConfig(OSRM_TEST_DATA_DIR "/berlin_MLD.osrm"); config.algorithm = EngineConfig::Algorithm::MLD; OSRM osrm{config}; } diff --git a/unit_tests/library/route.cpp b/unit_tests/library/route.cpp index ed7a42d1635..e8df12c0afa 100644 --- a/unit_tests/library/route.cpp +++ b/unit_tests/library/route.cpp @@ -17,7 +17,7 @@ BOOST_AUTO_TEST_SUITE(route) BOOST_AUTO_TEST_CASE(test_route_same_coordinates_fixture) { - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); using namespace osrm; @@ -25,44 +25,38 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates_fixture) params.steps = true; params.coordinates.push_back(get_dummy_location()); params.coordinates.push_back(get_dummy_location()); + params.generate_hints = false; json::Object result; const auto rc = osrm.Route(params, result); BOOST_CHECK(rc == Status::Ok); - // unset snapping dependent hint - for (auto &itr : result.values["waypoints"].get().values) - itr.get().values["hint"] = ""; - - const auto location = json::Array{{{7.437070}, {43.749248}}}; + const auto location = json::Array{{{13.3888}, {52.517033}}}; json::Object reference{ {{"code", "Ok"}, {"waypoints", - json::Array{ - {json::Object{ - {{"name", "Boulevard du Larvotto"}, {"location", location}, {"hint", ""}}}, - json::Object{ - {{"name", "Boulevard du Larvotto"}, {"location", location}, {"hint", ""}}}}}}, + json::Array{{json::Object{{{"name", "Friedrichstraße"}, {"location", location}}}, + json::Object{{{"name", "Friedrichstraße"}, {"location", location}}}}}}, {"routes", json::Array{{json::Object{ {{"distance", 0.}, {"duration", 0.}, {"weight", 0.}, {"weight_name", "routability"}, - {"geometry", "yw_jGupkl@??"}, + {"geometry", "mfp_I__vpA??"}, {"legs", json::Array{{json::Object{ {{"distance", 0.}, {"duration", 0.}, {"weight", 0.}, - {"summary", "Boulevard du Larvotto"}, + {"summary", "Friedrichstraße"}, {"steps", json::Array{{{json::Object{{{"duration", 0.}, {"distance", 0.}, {"weight", 0.}, - {"geometry", "yw_jGupkl@??"}, - {"name", "Boulevard du Larvotto"}, + {"geometry", "mfp_I__vpA??"}, + {"name", "Friedrichstraße"}, {"mode", "driving"}, {"maneuver", json::Object{{ @@ -81,8 +75,8 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates_fixture) json::Object{{{"duration", 0.}, {"distance", 0.}, {"weight", 0.}, - {"geometry", "yw_jGupkl@"}, - {"name", "Boulevard du Larvotto"}, + {"geometry", "mfp_I__vpA"}, + {"name", "Friedrichstraße"}, {"mode", "driving"}, {"maneuver", json::Object{{{"location", location}, @@ -103,7 +97,7 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates_fixture) BOOST_AUTO_TEST_CASE(test_route_same_coordinates) { - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); using namespace osrm; @@ -255,7 +249,7 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates) BOOST_AUTO_TEST_CASE(test_route_response_for_locations_in_small_component) { - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); using namespace osrm; @@ -290,7 +284,7 @@ BOOST_AUTO_TEST_CASE(test_route_response_for_locations_in_small_component) BOOST_AUTO_TEST_CASE(test_route_response_for_locations_in_big_component) { - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); using namespace osrm; @@ -325,7 +319,7 @@ BOOST_AUTO_TEST_CASE(test_route_response_for_locations_in_big_component) BOOST_AUTO_TEST_CASE(test_route_response_for_locations_across_components) { - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); using namespace osrm; @@ -362,7 +356,7 @@ BOOST_AUTO_TEST_CASE(test_route_response_for_locations_across_components) BOOST_AUTO_TEST_CASE(test_route_user_disables_generating_hints) { - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); using namespace osrm; @@ -382,7 +376,7 @@ BOOST_AUTO_TEST_CASE(test_route_user_disables_generating_hints) BOOST_AUTO_TEST_CASE(speed_annotation_matches_duration_and_distance) { - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); using namespace osrm; @@ -416,7 +410,7 @@ BOOST_AUTO_TEST_CASE(speed_annotation_matches_duration_and_distance) BOOST_AUTO_TEST_CASE(test_manual_setting_of_annotations_property) { - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); using namespace osrm; diff --git a/unit_tests/library/table.cpp b/unit_tests/library/table.cpp index 928f3d3d2d3..b3834c70620 100644 --- a/unit_tests/library/table.cpp +++ b/unit_tests/library/table.cpp @@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(test_table_three_coords_one_source_one_dest_matrix) { using namespace osrm; - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); TableParameters params; params.coordinates.push_back(get_dummy_location()); @@ -66,7 +66,7 @@ BOOST_AUTO_TEST_CASE(test_table_three_coords_one_source_matrix) { using namespace osrm; - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); TableParameters params; params.coordinates.push_back(get_dummy_location()); @@ -113,7 +113,7 @@ BOOST_AUTO_TEST_CASE(test_table_three_coordinates_matrix) { using namespace osrm; - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); TableParameters params; params.coordinates.push_back(get_dummy_location()); diff --git a/unit_tests/library/tile.cpp b/unit_tests/library/tile.cpp index 212469ea5f7..b9635c06409 100644 --- a/unit_tests/library/tile.cpp +++ b/unit_tests/library/tile.cpp @@ -24,16 +24,17 @@ BOOST_AUTO_TEST_CASE(test_tile) { using namespace osrm; - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); - // This tile should contain most of monaco - TileParameters params{17059, 11948, 15}; + // Tile within Berlin dataset at Hackescher Markt (13.40294, 52.52330) + TileParameters params{140831, 85967, 18}; std::string result; const auto rc = osrm.Tile(params, result); BOOST_CHECK(rc == Status::Ok); - BOOST_CHECK(result.size() > 114000); + BOOST_CHECK_GT(result.size(), 1500); + BOOST_CHECK_LT(result.size(), 2500); protozero::pbf_reader tile_message(result); tile_message.next(); @@ -208,22 +209,24 @@ BOOST_AUTO_TEST_CASE(test_tile) } BOOST_CHECK_EQUAL(number_of_turn_keys, 3); - BOOST_CHECK(number_of_turns_found > 700); + BOOST_CHECK_GT(number_of_turns_found, 10); // roughly ten turns in the tile } BOOST_AUTO_TEST_CASE(test_tile_turns) { using namespace osrm; - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); - // Small tile where we can test all the values - TileParameters params{272953, 191177, 19}; + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); + + // Tile within Berlin dataset at Hackescher Markt (13.40294, 52.52330) + TileParameters params{140831, 85967, 18}; std::string result; const auto rc = osrm.Tile(params, result); BOOST_CHECK(rc == Status::Ok); - BOOST_CHECK_GT(result.size(), 128); + BOOST_CHECK_GT(result.size(), 1500); + BOOST_CHECK_LT(result.size(), 2500); protozero::pbf_reader tile_message(result); tile_message.next(); @@ -331,7 +334,7 @@ BOOST_AUTO_TEST_CASE(test_tile_turns) } std::sort(actual_turn_penalties.begin(), actual_turn_penalties.end()); const std::vector expected_turn_penalties = { - 0, 0, 0, 0, 0, 0, .1f, .1f, .3f, .4f, 1.2f, 1.9f, 5.3f, 5.5f, 5.8f, 7.1f, 7.2f, 7.2f}; + 0., 0., 0., 0., 0., 0., 0., 0., 0.1, 0.7, 5.2, 7.1, 7.4}; CHECK_EQUAL_RANGE(actual_turn_penalties, expected_turn_penalties); // Verify the expected turn angles @@ -343,7 +346,7 @@ BOOST_AUTO_TEST_CASE(test_tile_turns) } std::sort(actual_turn_angles.begin(), actual_turn_angles.end()); const std::vector expected_turn_angles = { - -122, -120, -117, -65, -57, -30, -28, -3, -2, 2, 3, 28, 30, 57, 65, 117, 120, 122}; + -142, -118, -49, -13, -4, -2, -2, 2, 4, 13, 34, 49, 118}; CHECK_EQUAL_RANGE(actual_turn_angles, expected_turn_angles); // Verify the expected bearings @@ -355,7 +358,7 @@ BOOST_AUTO_TEST_CASE(test_tile_turns) } std::sort(actual_turn_bearings.begin(), actual_turn_bearings.end()); const std::vector expected_turn_bearings = { - 49, 49, 107, 107, 169, 169, 171, 171, 229, 229, 257, 257, 286, 286, 349, 349, 352, 352}; + 75, 75, 124, 124, 128, 242, 242, 255, 304, 304, 306, 308, 308}; CHECK_EQUAL_RANGE(actual_turn_bearings, expected_turn_bearings); } @@ -363,7 +366,7 @@ BOOST_AUTO_TEST_CASE(test_tile_speeds) { using namespace osrm; - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); // Small tile so we can test all the values // TileParameters params{272953, 191177, 19}; @@ -499,26 +502,7 @@ BOOST_AUTO_TEST_CASE(test_tile_speeds) actual_names.push_back(string_vals[i]); } std::sort(actual_names.begin(), actual_names.end()); - const std::vector expected_names = {"Avenue du Carnier", - "Avenue du Carnier", - "Avenue du Carnier", - "Avenue du Carnier", - "Avenue du Carnier", - "Avenue du Maréchal Foch", - "Avenue du Maréchal Foch", - "Avenue du Maréchal Foch", - "Avenue du Maréchal Foch", - "Avenue du Maréchal Foch", - "Avenue du Maréchal Foch", - "Avenue du Professeur Langevin", - "Avenue du Professeur Langevin", - "Avenue du Professeur Langevin", - "Montée de la Crémaillère", - "Montée de la Crémaillère", - "Rue Jules Ferry", - "Rue Jules Ferry", - "Rue Professeur Calmette", - "Rue Professeur Calmette"}; + const std::vector expected_names = {}; BOOST_CHECK(actual_names == expected_names); } diff --git a/unit_tests/library/trip.cpp b/unit_tests/library/trip.cpp index f3509c6b4ca..fd63954e7c0 100644 --- a/unit_tests/library/trip.cpp +++ b/unit_tests/library/trip.cpp @@ -18,7 +18,7 @@ BOOST_AUTO_TEST_CASE(test_roundtrip_response_for_locations_in_small_component) { using namespace osrm; - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); const auto locations = get_locations_in_small_component(); TripParameters params; @@ -60,7 +60,7 @@ BOOST_AUTO_TEST_CASE(test_roundtrip_response_for_locations_in_big_component) { using namespace osrm; - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); const auto locations = get_locations_in_big_component(); TripParameters params; @@ -102,7 +102,7 @@ BOOST_AUTO_TEST_CASE(test_roundtrip_response_for_locations_across_components) { using namespace osrm; - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); const auto small = get_locations_in_small_component(); const auto big = get_locations_in_big_component(); @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(test_tfse_1) { using namespace osrm; - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); const auto locations = get_locations_in_small_component(); TripParameters params; @@ -194,7 +194,7 @@ BOOST_AUTO_TEST_CASE(test_tfse_2) { using namespace osrm; - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); const auto locations = get_locations_in_big_component(); TripParameters params; @@ -265,7 +265,7 @@ BOOST_AUTO_TEST_CASE(test_tfse_illegal_parameters) { using namespace osrm; - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); const auto locations = get_locations_in_big_component(); auto params = osrm::TripParameters(); @@ -315,7 +315,7 @@ BOOST_AUTO_TEST_CASE(test_tfse_illegal_parameters) BOOST_AUTO_TEST_CASE(test_tfse_legal_parameters) { using namespace osrm; - auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/monaco_CH.osrm"); + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/berlin_CH.osrm"); const auto locations = get_locations_in_big_component(); json::Object result; TripParameters params;