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;