From a4b9c861126eca5b7276bd53819511db0fcd4cd8 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Mon, 21 Dec 2015 21:25:10 +0100 Subject: [PATCH 01/25] Change release name to track develop --- .travis.yml | 2 +- bootstrap.sh | 2 +- package.json | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6c10cde..7fcc0e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,7 +41,7 @@ matrix: env: global: - JOBS: "3" - - OSRM_RELEASE: "v4.8.1" + - OSRM_RELEASE: "develop" - secure: KitzGZjoDblX/3heajcvssGz0JnJ/k02dr2tu03ksUV+6MogC3RSQudqyKY57+f8VyZrcllN/UOlJ0Q/3iG38Oz8DljC+7RZxtkVmE1SFBoOezKCdhcvWM12G3uqPs7hhrRxuUgIh0C//YXEkulUrqa2H1Aj2xeen4E3FAqEoy0= - secure: WLGmxl6VTVWhXGm6X83GYNYzPNsvTD+9usJOKM5YBLAdG7cnOBQBNiCCUKc9OZMMZVUr3ec2/iigakH5Y8Yc+U6AlWKzlORyqWLuk4nFuoedu62x6ocQkTkuOc7mHiYhKd21xTGMYauaZRS6kugv4xkpGES2UjI2T8cjZ+LN2jU= diff --git a/bootstrap.sh b/bootstrap.sh index ed75790..0160e04 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -10,7 +10,7 @@ CURRENT_DIR=$(pwd) # default to clang CXX=${CXX:-clang++} TARGET=${TARGET:-Release} -OSRM_RELEASE=${OSRM_RELEASE:-"v4.8.1"} +OSRM_RELEASE=${OSRM_RELEASE:-"develop"} OSRM_REPO=${OSRM_REPO:-"https://github.com/Project-OSRM/osrm-backend.git"} function all_deps() { diff --git a/package.json b/package.json index a663c1f..941f978 100644 --- a/package.json +++ b/package.json @@ -4,19 +4,18 @@ "url": "https://github.com/Project-OSRM/node-osrm", "homepage": "http://project-osrm.org", "author": "Dane Springmeyer ", - "version": "4.8.1", + "version": "develop", "main": "./lib/osrm.js", "bugs": { "email": "dane@mapbox.com", "url": "https://github.com/Project-OSRM/node-osrm/issues" }, "keywords": [ - "routing", - "geocoding" + "routing" ], "repository": { "type": "git", - "url": "git://github.com/Project-OSRM/osrm-backend.git" + "url": "git://github.com/Project-OSRM/node-osrm.git" }, "binary": { "module_name": "osrm", From 69c0169317542eb854774348eac9131db67f08a2 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Mon, 21 Dec 2015 20:27:08 +0100 Subject: [PATCH 02/25] Don't specify sub-node version --- .travis.yml | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7fcc0e8..a1503f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,27 +16,33 @@ sudo: false matrix: include: - # Coverage - - os: osx - compiler: clang - env: NODE_VERSION="0.10" COVERAGE=true TARGET=Debug NPM_FLAGS="--debug" # Linux - os: linux compiler: gcc - env: NODE_VERSION="0.12.0" TARGET=Release + env: NODE_VERSION="5" TARGET=Release + - os: linux + compiler: gcc + env: NODE_VERSION="4" TARGET=Release + - os: linux + compiler: gcc + env: NODE_VERSION="0.12" TARGET=Release - os: linux compiler: gcc - env: NODE_VERSION="0.10.36" TARGET=Release + env: NODE_VERSION="0.10" TARGET=Release - os: linux compiler: gcc - env: NODE_VERSION="0.10.36" TARGET=Debug NPM_FLAGS="--debug" + env: NODE_VERSION="0.10" TARGET=Debug NPM_FLAGS="--debug" # OS X - os: osx compiler: clang - env: NODE_VERSION="0.12.0" TARGET=Release + env: NODE_VERSION="0.12" TARGET=Release + - os: osx + compiler: clang + env: NODE_VERSION="0.10" TARGET=Release + # Coverage - os: osx compiler: clang - env: NODE_VERSION="0.10.36" TARGET=Release + env: NODE_VERSION="0.10" COVERAGE=true TARGET=Debug NPM_FLAGS="--debug" env: global: @@ -54,7 +60,9 @@ before_install: export PYTHONPATH=$(pwd)/mason_packages/.link/lib/python2.7/site-packages; else export PYTHONPATH=$(pwd)/mason_packages/.link/lib/python/site-packages; - fi; + brew install md5sha1sum + fi +# Mac OS X does not have nvm installed - source ./scripts/install_node.sh ${NODE_VERSION} install: @@ -74,5 +82,8 @@ script: - make test || 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 -- if [[ ${COVERAGE} == true ]]; then ./mason_packages/.link/bin/cpp-coveralls --exclude node_modules --exclude mason_packages --exclude tests --build-root build --gcov-options '\-lp' --exclude doc --exclude build/Release/obj/gen; fi; -- if [[ ${COVERAGE} != true ]]; then ./scripts/publish.sh; fi; +- if [[ ${COVERAGE} == true ]]; then + ./mason_packages/.link/bin/cpp-coveralls --exclude node_modules --exclude mason_packages --exclude tests --build-root build --gcov-options '\-lp' --exclude doc --exclude build/Release/obj/gen; + else + ./scripts/publish.sh; + fi; From 8273426649e404997704e38275476e5f28a1e526 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Mon, 21 Dec 2015 20:29:53 +0100 Subject: [PATCH 03/25] Cache OSRM and only build master/develop --- .travis.yml | 15 +++++++++++- bootstrap.sh | 66 ++++++++++++++++++++++++++++------------------------ 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/.travis.yml b/.travis.yml index a1503f8..34c6b80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,19 @@ language: cpp git: depth: 10 +notifications: + email: false + +branches: + only: + - master + - develop + +cache: + directories: + - node_modules + - deps + addons: apt: sources: @@ -60,7 +73,7 @@ before_install: export PYTHONPATH=$(pwd)/mason_packages/.link/lib/python2.7/site-packages; else export PYTHONPATH=$(pwd)/mason_packages/.link/lib/python/site-packages; - brew install md5sha1sum + brew install md5sha1sum; fi # Mac OS X does not have nvm installed - source ./scripts/install_node.sh ${NODE_VERSION} diff --git a/bootstrap.sh b/bootstrap.sh index 0160e04..934665c 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -12,6 +12,7 @@ CXX=${CXX:-clang++} TARGET=${TARGET:-Release} OSRM_RELEASE=${OSRM_RELEASE:-"develop"} OSRM_REPO=${OSRM_REPO:-"https://github.com/Project-OSRM/osrm-backend.git"} +OSRM_DIR=deps/osrm-backend-${TARGET} function all_deps() { dep cmake 3.2.2 & @@ -62,6 +63,36 @@ function localize() { move_tool osrm-prepare } +function build_osrm() { + mkdir -p ${OSRM_DIR} + git clone ${OSRM_REPO} ${OSRM_DIR} + pushd ${OSRM_DIR} + + echo "Using OSRM ${OSRM_RELEASE}" + echo "Using OSRM ${OSRM_REPO}" + git checkout . + git checkout ${OSRM_RELEASE} + + # workaround https://github.com/Project-OSRM/node-osrm/issues/92 + perl -i -p -e "s/-fprofile-arcs -ftest-coverage//g;" CMakeLists.txt + perl -i -p -e "s/\${CMAKE_CXX_FLAGS} -flto/\${CMAKE_CXX_FLAGS}/g;" CMakeLists.txt + + rm -rf build + mkdir -p build + cd build + cmake ../ -DCMAKE_INSTALL_PREFIX=${MASON_HOME} \ + -DCMAKE_CXX_COMPILER="$CXX" \ + -DBoost_NO_SYSTEM_PATHS=ON \ + -DTBB_INSTALL_DIR=${MASON_HOME} \ + -DCMAKE_INCLUDE_PATH=${MASON_HOME}/include \ + -DCMAKE_LIBRARY_PATH=${MASON_HOME}/lib \ + -DCMAKE_BUILD_TYPE=${TARGET} \ + -DCMAKE_EXE_LINKER_FLAGS="${LINK_FLAGS}" + make -j${JOBS} && make install + + popd +} + function main() { if [[ ! -d ./.mason ]]; then git clone --depth 1 https://github.com/mapbox/mason.git ./.mason @@ -96,44 +127,17 @@ function main() { fi # make sure we rebuild if previous build was not successful - if [[ ! -f osrm-backend-${TARGET}/build/osrm-extract ]] || [[ ! -f ${MASON_HOME}/bin/osrm-extract ]]; then - mkdir -p osrm-backend-${TARGET} - git clone ${OSRM_REPO} osrm-backend-${TARGET} - cd osrm-backend-${TARGET} - - echo "Using OSRM ${OSRM_RELEASE}" - echo "Using OSRM ${OSRM_REPO}" - git checkout . - git checkout ${OSRM_RELEASE} - - # workaround https://github.com/Project-OSRM/node-osrm/issues/92 - perl -i -p -e "s/-fprofile-arcs -ftest-coverage//g;" CMakeLists.txt - perl -i -p -e "s/\${CMAKE_CXX_FLAGS} -flto/\${CMAKE_CXX_FLAGS}/g;" CMakeLists.txt - - rm -rf build - mkdir -p build - cd build - cmake ../ -DCMAKE_INSTALL_PREFIX=${MASON_HOME} \ - -DCMAKE_CXX_COMPILER="$CXX" \ - -DBoost_NO_SYSTEM_PATHS=ON \ - -DTBB_INSTALL_DIR=${MASON_HOME} \ - -DCMAKE_INCLUDE_PATH=${MASON_HOME}/include \ - -DCMAKE_LIBRARY_PATH=${MASON_HOME}/lib \ - -DCMAKE_BUILD_TYPE=${TARGET} \ - -DCMAKE_EXE_LINKER_FLAGS="${LINK_FLAGS}" - make -j${JOBS} - make install - + if [[ ! -f ${OSRM_DIR}/build/osrm-extract ]] || [[ ! -f ${MASON_HOME}/bin/osrm-extract ]] || + [[ ! -f ${OSRM_DIR}/build/osrm-prepare ]] || [[ ! -f ${MASON_HOME}/bin/osrm-prepare ]] || + [[ ! -f ${OSRM_DIR}/build/osrm-datastore ]] || [[ ! -f ${MASON_HOME}/bin/osrm-datastore ]]; then + build_osrm fi - cd ${CURRENT_DIR} - localize #if [[ `uname -s` == 'Darwin' ]]; then otool -L ./lib/binding/* || true; fi #if [[ `uname -s` == 'Linux' ]]; then readelf -d ./lib/binding/* || true; fi echo "success: now run 'npm install --build-from-source'" - } main From 00be9f915449e30dcfb7f4047b42d95a8aacd32f Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Mon, 21 Dec 2015 22:20:47 +0100 Subject: [PATCH 04/25] Silence npm semver --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 941f978..b87b601 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "url": "https://github.com/Project-OSRM/node-osrm", "homepage": "http://project-osrm.org", "author": "Dane Springmeyer ", - "version": "develop", + "version": "4.9.0-develop.0", "main": "./lib/osrm.js", "bugs": { "email": "dane@mapbox.com", From 478dae4c6a35566705095890e636ddbb4cf2eb4e Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Mon, 21 Dec 2015 22:48:38 +0100 Subject: [PATCH 05/25] Update package.js --- package.json | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index b87b601..93df629 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,16 @@ "author": "Dane Springmeyer ", "version": "4.9.0-develop.0", "main": "./lib/osrm.js", + "license": "BSD", "bugs": { "email": "dane@mapbox.com", "url": "https://github.com/Project-OSRM/node-osrm/issues" }, "keywords": [ - "routing" + "routing", + "matching", + "distance table", + "TSP" ], "repository": { "type": "git", @@ -31,17 +35,12 @@ "bundledDependencies": [ "node-pre-gyp" ], - "licenses": [ - { - "type": "BSD" - } - ], "devDependencies": { "tape": "^4.2.2", "aws-sdk": "~2.0.31" }, "engines": { - "node": "0.10.x" + "node": ">=0.10 <=0.12" }, "scripts": { "prepublish": "npm ls", From 1f9fd4d55cebb94ef8999b0d8b5b20e30838a86c Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Mon, 21 Dec 2015 23:12:59 +0100 Subject: [PATCH 06/25] Update README --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1caf169..7c753f0 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,11 @@ Provides read-only bindings to the [Open Source Routing Machine - OSRM](https://github.com/Project-OSRM/osrm-backend), a routing engine for OpenStreetMap data implementing high-performance algorithms for shortest paths in road networks. -[![Build Status](https://travis-ci.org/Project-OSRM/node-osrm.svg?branch=master)](https://travis-ci.org/Project-OSRM/node-osrm) -[![Coverage Status](https://coveralls.io/repos/Project-OSRM/node-osrm/badge.svg?branch=sudo-false)](https://coveralls.io/r/Project-OSRM/node-osrm?branch=sudo-false) + +| build config | branch | status | +|:-------------|:--------|:------------| +| Linux/OS X | master | [![Build Status](https://travis-ci.org/Project-OSRM/node-osrm.svg?branch=master)](https://travis-ci.org/Project-OSRM/node-osrm) [![Coverage Status](https://coveralls.io/repos/Project-OSRM/node-osrm/badge.svg?branch=master)](https://coveralls.io/r/Project-OSRM/node-osrm?branch=master) | +| Linux/OS X | develop | [![Build Status](https://travis-ci.org/Project-OSRM/node-osrm.svg?branch=develop)](https://travis-ci.org/Project-OSRM/node-osrm) [![Coverage Status](https://coveralls.io/repos/Project-OSRM/node-osrm/badge.svg?branch=develop)](https://coveralls.io/r/Project-OSRM/node-osrm?branch=develop) | # Depends From 2a1f810db9da8de696ec5fe0d64092f79073fa51 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 14 Dec 2015 19:18:32 -0800 Subject: [PATCH 07/25] fix linking on osx in debug mode --- binding.gyp | 3 ++- bootstrap.sh | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/binding.gyp b/binding.gyp index 8649589..81aee29 100644 --- a/binding.gyp +++ b/binding.gyp @@ -15,7 +15,8 @@ './src/' ], 'libraries': [ - ' Date: Mon, 14 Dec 2015 22:15:58 -0800 Subject: [PATCH 08/25] remove flto workaround now upstreamed in https://github.com/Project-OSRM/osrm-backend/commit/119fb635769fe5e7ba6a7b39f46a8a448882bacf - refs #92 --- bootstrap.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bootstrap.sh b/bootstrap.sh index 0e84d4d..12ec790 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -69,10 +69,6 @@ function build_osrm() { git checkout . git checkout ${OSRM_RELEASE} - # workaround https://github.com/Project-OSRM/node-osrm/issues/92 - perl -i -p -e "s/-fprofile-arcs -ftest-coverage//g;" CMakeLists.txt - perl -i -p -e "s/\${CMAKE_CXX_FLAGS} -flto/\${CMAKE_CXX_FLAGS}/g;" CMakeLists.txt - rm -rf build mkdir -p build cd build From c6d74f9e2c5db47b649c872a49294043f6e4990a Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Tue, 15 Dec 2015 18:10:08 +0100 Subject: [PATCH 09/25] Port to NAN2 --- package.json | 2 +- src/json_v8_renderer.hpp | 23 +- src/node_osrm.cpp | 864 ++++++++++++++++++--------------------- 3 files changed, 406 insertions(+), 483 deletions(-) diff --git a/package.json b/package.json index 93df629..eb7848b 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "package_name": "{node_abi}-{platform}-{arch}.tar.gz" }, "dependencies": { - "nan": "~1.8.4", + "nan": "^2.1.0", "node-pre-gyp": "~0.6.7" }, "bundledDependencies": [ diff --git a/src/json_v8_renderer.hpp b/src/json_v8_renderer.hpp index 14be58e..a25cc06 100644 --- a/src/json_v8_renderer.hpp +++ b/src/json_v8_renderer.hpp @@ -45,27 +45,30 @@ namespace json struct v8_renderer : mapbox::util::static_visitor<> { - explicit v8_renderer(v8::Local& _out) : out(_out) {} + explicit v8_renderer(v8::Local &_out) : out(_out) {} - void operator()(const String &string) const { out = NanNew(std::cref(string.value)); } + void operator()(const String &string) const + { + out = Nan::New(std::cref(string.value)).ToLocalChecked(); + } - void operator()(const Number &number) const { out = NanNew(number.value); } + void operator()(const Number &number) const { out = Nan::New(number.value); } void operator()(const Object &object) const { - v8::Local obj = NanNew(); - for (const auto& keyValue : object.values) + v8::Local obj = Nan::New(); + for (const auto &keyValue : object.values) { v8::Local child; mapbox::util::apply_visitor(v8_renderer(child), keyValue.second); - obj->Set(NanNew(keyValue.first), child); + obj->Set(Nan::New(keyValue.first).ToLocalChecked(), child); } out = obj; } void operator()(const Array &array) const { - v8::Local a = NanNew(array.values.size()); + v8::Local a = Nan::New(array.values.size()); for (auto i = 0u; i < array.values.size(); ++i) { v8::Local child; @@ -75,11 +78,11 @@ struct v8_renderer : mapbox::util::static_visitor<> out = a; } - void operator()(const True &) const { out = NanNew(true); } + void operator()(const True &) const { out = Nan::New(true); } - void operator()(const False &) const { out = NanNew(false); } + void operator()(const False &) const { out = Nan::New(false); } - void operator()(const Null &) const { out = NanNull(); } + void operator()(const Null &) const { out = Nan::Null(); } private: v8::Local &out; diff --git a/src/node_osrm.cpp b/src/node_osrm.cpp index d5f63ca..e719a85 100644 --- a/src/node_osrm.cpp +++ b/src/node_osrm.cpp @@ -4,594 +4,507 @@ #include // OSRM +#include #include #include #include -#include + +#include // STL #include #include -namespace node_osrm { +namespace node_osrm +{ -typedef std::unique_ptr osrm_ptr; -typedef std::unique_ptr route_parameters_ptr; -namespace { -template -std::unique_ptr make_unique(Types &&... Args) +using osrm_ptr = std::unique_ptr; +using libosrm_config_ptr = std::unique_ptr; +using route_parameters_ptr = std::unique_ptr; +namespace +{ +template std::unique_ptr make_unique(Types &&... Args) { return (std::unique_ptr(new T(std::forward(Args)...))); } } -using namespace v8; - -class Engine final : public node::ObjectWrap { -public: - static Persistent constructor; - static void Initialize(Handle); - static NAN_METHOD(New); - static NAN_METHOD(route); - static NAN_METHOD(locate); - static NAN_METHOD(nearest); - static NAN_METHOD(table); - static NAN_METHOD(match); - static NAN_METHOD(trip); - - static void Run(_NAN_METHOD_ARGS, route_parameters_ptr); - static void AsyncRun(uv_work_t*); - static void AfterRun(uv_work_t*); - -private: - Engine(libosrm_config &lib_config) - : ObjectWrap(), - this_(make_unique(lib_config)) - {} - - osrm_ptr this_; -}; - -Persistent Engine::constructor; - -void Engine::Initialize(Handle target) { - NanScope(); - Local lcons = NanNew(Engine::New); - lcons->InstanceTemplate()->SetInternalFieldCount(1); - lcons->SetClassName(NanNew("OSRM")); - NODE_SET_PROTOTYPE_METHOD(lcons, "route", route); - NODE_SET_PROTOTYPE_METHOD(lcons, "locate", locate); - NODE_SET_PROTOTYPE_METHOD(lcons, "nearest", nearest); - NODE_SET_PROTOTYPE_METHOD(lcons, "table", table); - NODE_SET_PROTOTYPE_METHOD(lcons, "match", match); - NODE_SET_PROTOTYPE_METHOD(lcons, "trip", trip); - lcons->Set(NanNew("libosrm_version"), NanNew(LIBOSRM_GIT_REVISION)); - target->Set(NanNew("OSRM"), lcons->GetFunction()); - NanAssignPersistent(constructor, lcons); -} - -NAN_METHOD(Engine::New) +// Supports +libosrm_config_ptr argumentsToLibOSRMConfig(const Nan::FunctionCallbackInfo &args) { - NanScope(); - - if (!args.IsConstructCall()) { - NanThrowTypeError("Cannot call constructor as function, you need to use 'new' keyword"); - NanReturnUndefined(); - } - - try { - libosrm_config lib_config; - if (args.Length() == 1) { - std::string base; - bool shared_memory_defined = false; - if (args[0]->IsString()) { - base = *String::Utf8Value(args[0]->ToString()); - } else if (args[0]->IsObject()) { - Local params = args[0]->ToObject(); - - Local path = params->Get(NanNew("path")); - if (!path->IsUndefined()) { - base = *String::Utf8Value(path->ToString()); - } - - Local max_locations_distance_table = params->Get(NanNew("distance_table")); - if (!max_locations_distance_table->IsUndefined()) { - if (!max_locations_distance_table->IsUint32()) { - NanThrowError("the maximum number of locations in the distance table must be an unsigned integer"); - NanReturnUndefined(); - } else { - lib_config.max_locations_distance_table = max_locations_distance_table->ToUint32()->Value(); - } - } - - Local shared_memory = params->Get(NanNew("shared_memory")); - if (!shared_memory->IsUndefined()) { - if (!shared_memory->IsBoolean()) { - NanThrowError("shared_memory option must be a boolean"); - NanReturnUndefined(); - } else { - lib_config.use_shared_memory = shared_memory->ToBoolean()->Value(); - if (base.empty() && lib_config.use_shared_memory == false) { - NanThrowError("shared_memory must be enabled if no path is specified"); - NanReturnUndefined(); - } - shared_memory_defined = true; - } - } - - } else { - NanThrowError("first argument must be a path string or params object"); - NanReturnUndefined(); - } + auto lib_config = make_unique(); - if (!base.empty()) { - lib_config.server_paths["base"] = base; - } - if (shared_memory_defined == false && !base.empty()) { - lib_config.use_shared_memory = false; - } - } - auto im = new Engine(lib_config); - im->Wrap(args.This()); - NanReturnValue(args.This()); - } catch (std::exception const& ex) { - NanThrowTypeError(ex.what()); - NanReturnUndefined(); + if (args.Length() == 0) + { + return lib_config; + } + else if (args.Length() > 1) + { + return libosrm_config_ptr(); } -} -struct RunQueryBaton { - uv_work_t request; - Engine * machine; - osrm::json::Object result; - route_parameters_ptr params; - Persistent cb; - std::string error; -}; + BOOST_ASSERT(args.Length() == 1); -NAN_METHOD(Engine::route) -{ - NanScope(); + if (args[0]->IsString()) + { + lib_config->server_paths["base"] = + *v8::String::Utf8Value(Nan::To(args[0]).ToLocalChecked()); + lib_config->use_shared_memory = false; + return lib_config; + } + else if (!args[0]->IsObject()) + { + Nan::ThrowError("parameter must be path or options object."); + return libosrm_config_ptr(); + } + + BOOST_ASSERT(args[0]->IsObject()); + auto params = Nan::To(args[0]).ToLocalChecked(); - if (args.Length() < 2) { - NanThrowTypeError("two arguments required"); - NanReturnUndefined(); + auto path = params->Get(Nan::New("path").ToLocalChecked()); + auto shared_memory = params->Get(Nan::New("shared_memory").ToLocalChecked()); + if (!path->IsUndefined()) + { + lib_config->server_paths["base"] = + *v8::String::Utf8Value(Nan::To(path).ToLocalChecked()); + } + if (!shared_memory->IsUndefined()) + { + lib_config->use_shared_memory = Nan::To(shared_memory).FromJust(); } - if (!args[0]->IsObject()) { - NanThrowTypeError("first arg must be an object"); - NanReturnUndefined(); + if (path->IsUndefined() && !lib_config->use_shared_memory) + { + Nan::ThrowError("shared_memory must be enabled if no path is " + "specified"); + return libosrm_config_ptr(); } - Local obj = args[0]->ToObject(); + return lib_config; +} - route_parameters_ptr params = make_unique(); +boost::optional> parseCoordinateArray(const v8::Local &coordinates_array) +{ + 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); - params->zoom_level = 18; //no generalization - params->print_instructions = false; //turn by turn instructions - params->alternate_route = true; //get an alternate route, too - params->geometry = true; //retrieve geometry of route - params->compression = true; //polyline encoding - params->check_sum = 0; //see wiki - params->service = "viaroute"; //that's routing - params->output_format = "json"; - params->jsonp_parameter = ""; //set for jsonp wrapping - params->language = ""; //unused atm + if (!coordinate->IsArray()) + { + Nan::ThrowError("coordinates must be an array of (lat/long) 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 (lat/long) pairs"); + return resulting_coordinates; + } - if (!obj->Has(NanNew("coordinates"))) { - NanThrowError("must provide a coordinates property"); - NanReturnUndefined(); + temp_coordinates.emplace_back( + static_cast(coordinate_pair->Get(0)->NumberValue() * COORDINATE_PRECISION), + static_cast(coordinate_pair->Get(1)->NumberValue() * COORDINATE_PRECISION)); } + resulting_coordinates = + boost::make_optional>(std::move(temp_coordinates)); + return resulting_coordinates; +} + +// Parses all the non-service specific parameters +route_parameters_ptr argumentsToParameter(const Nan::FunctionCallbackInfo &args) +{ + auto params = make_unique(); - Local coordinates = obj->Get(NanNew("coordinates")); - if (!coordinates->IsArray()) { - NanThrowError("coordinates must be an array of (lat/long) pairs"); - NanReturnUndefined(); + if (args.Length() < 2) + { + Nan::ThrowTypeError("two arguments required"); + return route_parameters_ptr(); } - Local coordinates_array = Local::Cast(coordinates); - if (coordinates_array->Length() < 2) { - NanThrowError("at least two coordinates must be provided"); - NanReturnUndefined(); + if (!args[0]->IsObject()) + { + Nan::ThrowTypeError("first arg must be an object"); + return route_parameters_ptr(); } - for (uint32_t i = 0; i < coordinates_array->Length(); ++i) { - Local coordinate = coordinates_array->Get(i); + v8::Local obj = Nan::To(args[0]).ToLocalChecked(); - if (!coordinate->IsArray()) { - NanThrowError("coordinates must be an array of (lat/long) pairs"); - NanReturnUndefined(); + v8::Local coordinates = obj->Get(Nan::New("coordinates").ToLocalChecked()); + if (coordinates->IsUndefined()) + { + Nan::ThrowError("must provide a coordinates property"); + return route_parameters_ptr(); + } + else if (coordinates->IsArray()) + { + auto coordinates_array = v8::Local::Cast(coordinates); + if (coordinates_array->Length() < 2) + { + Nan::ThrowError("at least two coordinates must be provided"); + return route_parameters_ptr(); } - - Local coordinate_pair = Local::Cast(coordinate); - if (coordinate_pair->Length() != 2) { - NanThrowError("coordinates must be an array of (lat/long) pairs"); - NanReturnUndefined(); + auto maybe_coordinates = parseCoordinateArray(coordinates_array); + if (maybe_coordinates) + { + std::copy(maybe_coordinates->begin(), maybe_coordinates->end(), std::back_inserter(params->coordinates)); + params->is_source.insert(params->is_source.end(), maybe_coordinates->size(), true); + params->is_destination.insert(params->is_destination.end(), maybe_coordinates->size(), true); + } + else + { + return route_parameters_ptr(); } - - params->coordinates.emplace_back(static_cast(coordinate_pair->Get(0)->NumberValue()*COORDINATE_PRECISION), - static_cast(coordinate_pair->Get(1)->NumberValue()*COORDINATE_PRECISION)); } - - if (obj->Has(NanNew("alternateRoute"))) { - params->alternate_route = obj->Get(NanNew("alternateRoute"))->BooleanValue(); + else + { + Nan::ThrowError("coordinates must be an array of (lat/long) pairs"); + return route_parameters_ptr(); } - if (obj->Has(NanNew("checksum"))) { - params->check_sum = static_cast(obj->Get(NanNew("checksum"))->Uint32Value()); + if (obj->Has(Nan::New("alternateRoute").ToLocalChecked())) + { + params->alternate_route = + obj->Get(Nan::New("alternateRoute").ToLocalChecked())->BooleanValue(); } - if (obj->Has(NanNew("zoomLevel"))) { - params->zoom_level = static_cast(obj->Get(NanNew("zoomLevel"))->Int32Value()); + if (obj->Has(Nan::New("checksum").ToLocalChecked())) + { + params->check_sum = + static_cast(obj->Get(Nan::New("checksum").ToLocalChecked())->Uint32Value()); } - if (obj->Has(NanNew("printInstructions"))) { - params->print_instructions = obj->Get(NanNew("printInstructions"))->BooleanValue(); + if (obj->Has(Nan::New("zoomLevel").ToLocalChecked())) + { + params->zoom_level = + static_cast(obj->Get(Nan::New("zoomLevel").ToLocalChecked())->Int32Value()); } - if (obj->Has(NanNew("geometry"))) { - params->geometry = obj->Get(NanNew("geometry"))->BooleanValue(); + if (obj->Has(Nan::New("printInstructions").ToLocalChecked())) + { + params->print_instructions = + obj->Get(Nan::New("printInstructions").ToLocalChecked())->BooleanValue(); } - if (obj->Has(NanNew("compression"))) { - params->compression = obj->Get(NanNew("compression"))->BooleanValue(); + if (obj->Has(Nan::New("geometry").ToLocalChecked())) + { + params->geometry = obj->Get(Nan::New("geometry").ToLocalChecked())->BooleanValue(); } - if (obj->Has(NanNew("jsonpParameter"))) { - params->jsonp_parameter = *v8::String::Utf8Value(obj->Get(NanNew("jsonpParameter"))); + if (obj->Has(Nan::New("compression").ToLocalChecked())) + { + params->compression = obj->Get(Nan::New("compression").ToLocalChecked())->BooleanValue(); } - if (obj->Has(NanNew("hints"))) { - Local hints = obj->Get(NanNew("hints")); + if (obj->Has(Nan::New("hints").ToLocalChecked())) + { + v8::Local hints = obj->Get(Nan::New("hints").ToLocalChecked()); - if (!hints->IsArray()) { - NanThrowError("hints must be an array of strings/null"); - NanReturnUndefined(); + if (!hints->IsArray()) + { + Nan::ThrowError("hints must be an array of strings/null"); + return route_parameters_ptr(); } - Local hints_array = Local::Cast(hints); - for (uint32_t i = 0; i < hints_array->Length(); ++i) { - Local hint = hints_array->Get(i); - if (hint->IsString()) { + v8::Local hints_array = v8::Local::Cast(hints); + for (uint32_t i = 0; i < hints_array->Length(); ++i) + { + v8::Local hint = hints_array->Get(i); + if (hint->IsString()) + { params->hints.push_back(*v8::String::Utf8Value(hint)); - } else if(hint->IsNull()){ + } + else if (hint->IsNull()) + { params->hints.push_back(""); - }else{ - NanThrowError("hint must be null or string"); - NanReturnUndefined(); + } + else + { + Nan::ThrowError("hint must be null or string"); + return route_parameters_ptr(); } } } - Run(args, std::move(params)); - NanReturnUndefined(); + BOOST_ASSERT(params->is_source.size() == params->is_destination.size()); + BOOST_ASSERT(params->coordinates.size() == params->is_destination.size()); + + return params; } -NAN_METHOD(Engine::locate) +class Engine final : public Nan::ObjectWrap { - NanScope(); - if (args.Length() < 2) { - NanThrowTypeError("two arguments required"); - NanReturnUndefined(); - } + public: + static void Initialize(v8::Handle); + static void New(const Nan::FunctionCallbackInfo &args); - Local coordinate = args[0]; - if (!coordinate->IsArray()) { - NanThrowError("first argument must be an array of lat, long"); - NanReturnUndefined(); - } + static void route(const Nan::FunctionCallbackInfo &args); + static void nearest(const Nan::FunctionCallbackInfo &args); + static void table(const Nan::FunctionCallbackInfo &args); + static void match(const Nan::FunctionCallbackInfo &args); + static void trip(const Nan::FunctionCallbackInfo &args); - Local coordinate_pair = Local::Cast(coordinate); - if (coordinate_pair->Length() != 2) { - NanThrowError("first argument must be an array of lat, long"); - NanReturnUndefined(); + static void Run(const Nan::FunctionCallbackInfo &args, route_parameters_ptr); + static void AsyncRun(uv_work_t *); + static void AfterRun(uv_work_t *); + + private: + Engine(LibOSRMConfig lib_config) + : Nan::ObjectWrap(), this_(make_unique(std::move(lib_config))) + { } - route_parameters_ptr params = make_unique(); + static Nan::Persistent constructor; + osrm_ptr this_; +}; - params->service = "locate"; - params->coordinates.emplace_back(static_cast(coordinate_pair->Get(0)->NumberValue()*COORDINATE_PRECISION), - static_cast(coordinate_pair->Get(1)->NumberValue()*COORDINATE_PRECISION)); +Nan::Persistent Engine::constructor; - Run(args, std::move(params)); - NanReturnUndefined(); +void Engine::Initialize(v8::Handle target) +{ + v8::Local function_template = Nan::New(Engine::New); + + function_template->InstanceTemplate()->SetInternalFieldCount(1); + function_template->SetClassName(Nan::New("OSRM").ToLocalChecked()); + + SetPrototypeMethod(function_template, "route", route); + SetPrototypeMethod(function_template, "nearest", nearest); + SetPrototypeMethod(function_template, "table", table); + SetPrototypeMethod(function_template, "match", match); + SetPrototypeMethod(function_template, "trip", trip); + + constructor.Reset(function_template->GetFunction()); + Nan::Set(target, Nan::New("OSRM").ToLocalChecked(), function_template->GetFunction()); } -NAN_METHOD(Engine::match) +void Engine::New(const Nan::FunctionCallbackInfo &args) { - NanScope(); - if (args.Length() < 2) { - NanThrowTypeError("two arguments required"); - NanReturnUndefined(); + if (!args.IsConstructCall()) + { + Nan::ThrowTypeError("Cannot call constructor as function, you need to use 'new' " + "keyword"); + return; } - if (args[0]->IsNull() || args[0]->IsUndefined()) { - NanThrowError("first arg must be an object"); - NanReturnUndefined(); + try + { + auto lib_config_ptr = argumentsToLibOSRMConfig(args); + if (!lib_config_ptr) + { + return; + } + auto engine_ptr = new Engine(std::move(*lib_config_ptr)); + engine_ptr->Wrap(args.This()); + args.GetReturnValue().Set(args.This()); } - Local obj = args[0]->ToObject(); - - Local coordinates = obj->Get(NanNew("coordinates")); - if (!coordinates->IsArray()) { - NanThrowError("coordinates must be an array of (lat/long) pairs"); - NanReturnUndefined(); + catch (std::exception const &ex) + { + Nan::ThrowTypeError(ex.what()); } +} - Local coordinates_array = Local::Cast(coordinates); - if (coordinates_array->Length() < 2) { - NanThrowError("at least two coordinates must be provided"); - NanReturnUndefined(); - } +struct RunQueryBaton +{ + uv_work_t request; + Engine *machine; + osrm::json::Object result; + route_parameters_ptr params; + Nan::Persistent cb; + std::string error; +}; - Local timestamps = obj->Get(NanNew("timestamps")); - if (!timestamps->IsArray() && !timestamps->IsUndefined()) { - NanThrowError("timestamps must be an array of integers (or undefined)"); - NanReturnUndefined(); - } +void Engine::route(const Nan::FunctionCallbackInfo &args) +{ + auto params = argumentsToParameter(args); + if (!params) + return; - route_parameters_ptr params = make_unique(); - params->service = "match"; + params->service = "viaroute"; - if (obj->Has(NanNew("classify"))) { - params->classify = obj->Get(NanNew("classify"))->BooleanValue(); - } - if (obj->Has(NanNew("gps_precision"))) { - params->gps_precision = obj->Get(NanNew("gps_precision"))->NumberValue(); + Run(args, std::move(params)); +} + +void Engine::match(const Nan::FunctionCallbackInfo &args) +{ + auto params = argumentsToParameter(args); + if (!params) + return; + + v8::Local obj = Nan::To(args[0]).ToLocalChecked(); + + v8::Local timestamps = obj->Get(Nan::New("timestamps").ToLocalChecked()); + if (!timestamps->IsArray() && !timestamps->IsUndefined()) + { + Nan::ThrowError("timestamps must be an array of integers (or undefined)"); + return; } - if (obj->Has(NanNew("matching_beta"))) { - params->matching_beta = obj->Get(NanNew("matching_beta"))->NumberValue(); + + if (obj->Has(Nan::New("classify").ToLocalChecked())) + { + params->classify = obj->Get(Nan::New("classify").ToLocalChecked())->BooleanValue(); } - if (obj->Has(NanNew("geometry"))) { - params->geometry = obj->Get(NanNew("geometry"))->BooleanValue(); + if (obj->Has(Nan::New("gps_precision").ToLocalChecked())) + { + params->gps_precision = obj->Get(Nan::New("gps_precision").ToLocalChecked())->NumberValue(); } - if (obj->Has(NanNew("compression"))) { - params->compression = obj->Get(NanNew("compression"))->BooleanValue(); + if (obj->Has(Nan::New("matching_beta").ToLocalChecked())) + { + params->matching_beta = obj->Get(Nan::New("matching_beta").ToLocalChecked())->NumberValue(); } - if (timestamps->IsArray()) { - Local timestamps_array = Local::Cast(timestamps); - if (coordinates_array->Length() != timestamps_array->Length()) { - NanThrowError("timestamp array must have the same size as the coordinates array"); - NanReturnUndefined(); + if (timestamps->IsArray()) + { + 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; } // add all timestamps - for (uint32_t i = 0; i < timestamps_array->Length(); ++i) { - if (!timestamps_array->Get(i)->IsNumber()) { - NanThrowError("timestamps array items must be numbers"); - NanReturnUndefined(); + for (uint32_t i = 0; i < timestamps_array->Length(); ++i) + { + if (!timestamps_array->Get(i)->IsNumber()) + { + Nan::ThrowError("timestamps array items must be numbers"); + return; } - params->timestamps.emplace_back(static_cast(timestamps_array->Get(i)->NumberValue())); + params->timestamps.emplace_back( + static_cast(timestamps_array->Get(i)->NumberValue())); } } - // add all coordinates - for (uint32_t i = 0; i < coordinates_array->Length(); ++i) { - Local coordinate = coordinates_array->Get(i); - - if (!coordinate->IsArray()) { - NanThrowError("coordinates must be an array of (lat/long) pairs"); - NanReturnUndefined(); - } - - Local coordinate_pair = Local::Cast(coordinate); - if (coordinate_pair->Length() != 2) { - NanThrowError("coordinates must be an array of (lat/long) pairs"); - NanReturnUndefined(); - } - - params->coordinates.emplace_back(static_cast(coordinate_pair->Get(0)->NumberValue()*COORDINATE_PRECISION), - static_cast(coordinate_pair->Get(1)->NumberValue()*COORDINATE_PRECISION)); - - } + params->service = "match"; Run(args, std::move(params)); - NanReturnUndefined(); } // uses the same options as viaroute -NAN_METHOD(Engine::trip) +void Engine::trip(const Nan::FunctionCallbackInfo &args) { - NanScope(); - - if (args.Length() < 2) { - NanThrowTypeError("two arguments required"); - NanReturnUndefined(); - } + auto params = argumentsToParameter(args); + if (!params) + return; - if (!args[0]->IsObject()) { - NanThrowTypeError("first arg must be an object"); - NanReturnUndefined(); - } + params->service = "trip"; - Local obj = args[0]->ToObject(); + Run(args, std::move(params)); +} - route_parameters_ptr params = make_unique(); +void Engine::table(const Nan::FunctionCallbackInfo &args) +{ + auto params = argumentsToParameter(args); + if (!params) + return; - params->zoom_level = 18; //no generalization - params->print_instructions = false; //turn by turn instructions - params->alternate_route = false; //get an alternate route, too - params->geometry = true; //retrieve geometry of route - params->compression = true; //polyline encoding - params->check_sum = 0; //see wiki - params->service = "trip"; //that's routing - params->output_format = "json"; - params->jsonp_parameter = ""; //set for jsonp wrapping - params->language = ""; //unused atm + v8::Local obj = Nan::To(args[0]).ToLocalChecked(); - if (!obj->Has(NanNew("coordinates"))) { - NanThrowError("must provide a coordinates property"); - NanReturnUndefined(); + v8::Local sources = obj->Get(Nan::New("sources").ToLocalChecked()); + if (!sources->IsArray() && !sources->IsUndefined()) + { + Nan::ThrowError("sources must be an array of coordinates (or undefined)"); + return; } - - Local coordinates = obj->Get(NanNew("coordinates")); - if (!coordinates->IsArray()) { - NanThrowError("coordinates must be an array of (lat/long) pairs"); - NanReturnUndefined(); + v8::Local destinations = obj->Get(Nan::New("destinations").ToLocalChecked()); + if (!destinations->IsArray() && !destinations->IsUndefined()) + { + Nan::ThrowError("destinations must be an array of coordinates (or undefined)"); + return; } - Local coordinates_array = Local::Cast(coordinates); - if (coordinates_array->Length() < 2) { - NanThrowError("at least two coordinates must be provided"); - NanReturnUndefined(); + if (destinations->IsUndefined() != sources->IsUndefined()) + { + Nan::ThrowError("Both sources and destinations need to be specified"); + return; } - for (uint32_t i = 0; i < coordinates_array->Length(); ++i) { - Local coordinate = coordinates_array->Get(i); + if (!destinations->IsUndefined() && !sources->IsUndefined() && params->coordinates.size() > 0) + { + Nan::ThrowError("You can either specifiy sources and destinations, or coordinates"); + return; + } - if (!coordinate->IsArray()) { - NanThrowError("coordinates must be an array of (lat/long) pairs"); - NanReturnUndefined(); + if (sources->IsArray()) + { + auto sources_array = v8::Local::Cast(sources); + auto maybe_sources = parseCoordinateArray(sources_array); + if (maybe_sources) + { + std::copy(maybe_sources->begin(), maybe_sources->end(), std::back_inserter(params->coordinates)); + params->is_source.insert(params->is_source.end(), maybe_sources->size(), true); + params->is_destination.insert(params->is_destination.end(), maybe_sources->size(), false); } - - Local coordinate_pair = Local::Cast(coordinate); - if (coordinate_pair->Length() != 2) { - NanThrowError("coordinates must be an array of (lat/long) pairs"); - NanReturnUndefined(); + else + { + return; } - - params->coordinates.emplace_back(static_cast(coordinate_pair->Get(0)->NumberValue()*COORDINATE_PRECISION), - static_cast(coordinate_pair->Get(1)->NumberValue()*COORDINATE_PRECISION)); - } - - if (obj->Has(NanNew("checksum"))) { - params->check_sum = static_cast(obj->Get(NanNew("checksum"))->Uint32Value()); - } - - if (obj->Has(NanNew("zoomLevel"))) { - params->zoom_level = static_cast(obj->Get(NanNew("zoomLevel"))->Int32Value()); - } - - if (obj->Has(NanNew("printInstructions"))) { - params->print_instructions = obj->Get(NanNew("printInstructions"))->BooleanValue(); } - - if (obj->Has(NanNew("geometry"))) { - params->geometry = obj->Get(NanNew("geometry"))->BooleanValue(); - } - - if (obj->Has(NanNew("compression"))) { - params->compression = obj->Get(NanNew("compression"))->BooleanValue(); - } - - if (obj->Has(NanNew("jsonpParameter"))) { - params->jsonp_parameter = *v8::String::Utf8Value(obj->Get(NanNew("jsonpParameter"))); - } - - if (obj->Has(NanNew("hints"))) { - Local hints = obj->Get(NanNew("hints")); - - if (!hints->IsArray()) { - NanThrowError("hints must be an array of strings/null"); - NanReturnUndefined(); + if (destinations->IsArray()) + { + auto destinations_array = v8::Local::Cast(destinations); + auto maybe_destinations = parseCoordinateArray(destinations_array); + if (maybe_destinations) + { + std::copy(maybe_destinations->begin(), maybe_destinations->end(), std::back_inserter(params->coordinates)); + params->is_source.insert(params->is_source.end(), maybe_destinations->size(), false); + params->is_destination.insert(params->is_destination.end(), maybe_destinations->size(), true); } - - Local hints_array = Local::Cast(hints); - for (uint32_t i = 0; i < hints_array->Length(); ++i) { - Local hint = hints_array->Get(i); - if (hint->IsString()) { - params->hints.push_back(*v8::String::Utf8Value(hint)); - } else if(hint->IsNull()){ - params->hints.push_back(""); - }else{ - NanThrowError("hint must be null or string"); - NanReturnUndefined(); - } + else + { + return; } } - Run(args, std::move(params)); - NanReturnUndefined(); -} - -NAN_METHOD(Engine::table) -{ - NanScope(); - if (args.Length() < 2) { - NanThrowTypeError("two arguments required"); - NanReturnUndefined(); - } - - if (args[0]->IsNull() || args[0]->IsUndefined()) { - NanThrowError("first arg must be an object"); - NanReturnUndefined(); - } - Local obj = args[0]->ToObject(); - - Local coordinates = obj->Get(NanNew("coordinates")); - if (!coordinates->IsArray()) { - NanThrowError("coordinates must be an array of (lat/long) pairs"); - NanReturnUndefined(); - } + BOOST_ASSERT(params->is_source.size() == params->is_destination.size()); + BOOST_ASSERT(params->coordinates.size() == params->is_destination.size()); - Local coordinates_array = Local::Cast(coordinates); - if (coordinates_array->Length() < 2) { - NanThrowError("at least two coordinates must be provided"); - NanReturnUndefined(); - } - - route_parameters_ptr params = make_unique(); params->service = "table"; - // add all coordinates - for (uint32_t i = 0; i < coordinates_array->Length(); ++i) { - Local coordinate = coordinates_array->Get(i); - - if (!coordinate->IsArray()) { - NanThrowError("coordinates must be an array of (lat/long) pairs"); - NanReturnUndefined(); - } - - Local coordinate_pair = Local::Cast(coordinate); - if (coordinate_pair->Length() != 2) { - NanThrowError("coordinates must be an array of (lat/long) pairs"); - NanReturnUndefined(); - } - - params->coordinates.emplace_back(static_cast(coordinate_pair->Get(0)->NumberValue()*COORDINATE_PRECISION), - static_cast(coordinate_pair->Get(1)->NumberValue()*COORDINATE_PRECISION)); - } - Run(args, std::move(params)); - NanReturnUndefined(); } -NAN_METHOD(Engine::nearest) +void Engine::nearest(const Nan::FunctionCallbackInfo &args) { - NanScope(); - if (args.Length() < 2) { - NanThrowTypeError("two arguments required"); - NanReturnUndefined(); + if (args.Length() < 2) + { + Nan::ThrowTypeError("two arguments required"); + return; } - Local coordinate = args[0]; - if (!coordinate->IsArray()) { - NanThrowError("first argument must be an array of lat, long"); - NanReturnUndefined(); + v8::Local coordinate = args[0]; + if (!coordinate->IsArray()) + { + Nan::ThrowError("first argument must be an array of lat, long"); + return; } - Local coordinate_pair = Local::Cast(coordinate); - if (coordinate_pair->Length() != 2) { - NanThrowError("first argument must be an array of lat, long"); - NanReturnUndefined(); + v8::Local coordinate_pair = v8::Local::Cast(coordinate); + if (coordinate_pair->Length() != 2) + { + Nan::ThrowError("first argument must be an array of lat, long"); + return; } route_parameters_ptr params = make_unique(); params->service = "nearest"; - params->coordinates.emplace_back(static_cast(coordinate_pair->Get(0)->NumberValue()*COORDINATE_PRECISION), - static_cast(coordinate_pair->Get(1)->NumberValue()*COORDINATE_PRECISION)); + params->coordinates.emplace_back( + static_cast(coordinate_pair->Get(0)->NumberValue() * COORDINATE_PRECISION), + static_cast(coordinate_pair->Get(1)->NumberValue() * COORDINATE_PRECISION)); Run(args, std::move(params)); - NanReturnUndefined(); } -void Engine::Run(_NAN_METHOD_ARGS, route_parameters_ptr params) +void Engine::Run(const Nan::FunctionCallbackInfo &args, route_parameters_ptr params) { - NanScope(); - Local callback = args[args.Length()-1]; + v8::Local callback = args[args.Length() - 1]; - if (!callback->IsFunction()) { - NanThrowTypeError("last argument must be a callback function"); + if (!callback->IsFunction()) + { + Nan::ThrowTypeError("last argument must be a callback function"); return; } @@ -600,46 +513,53 @@ void Engine::Run(_NAN_METHOD_ARGS, route_parameters_ptr params) closure->machine = ObjectWrap::Unwrap(args.This()); closure->params = std::move(params); closure->error = ""; - NanAssignPersistent(closure->cb, callback.As()); - uv_queue_work(uv_default_loop(), &closure->request, AsyncRun, reinterpret_cast(AfterRun)); + closure->cb.Reset(callback.As()); + uv_queue_work(uv_default_loop(), &closure->request, AsyncRun, + reinterpret_cast(AfterRun)); closure->machine->Ref(); return; } -void Engine::AsyncRun(uv_work_t* req) { +void Engine::AsyncRun(uv_work_t *req) +{ RunQueryBaton *closure = static_cast(req->data); - try { + try + { closure->machine->this_->RunQuery(*closure->params, closure->result); - } catch(std::exception const& ex) { + } + catch (std::exception const &ex) + { closure->error = ex.what(); } } -void Engine::AfterRun(uv_work_t* req) { - NanScope(); +void Engine::AfterRun(uv_work_t *req) +{ RunQueryBaton *closure = static_cast(req->data); - TryCatch try_catch; - if (closure->error.size() > 0) { - Local argv[1] = { NanError(closure->error.c_str()) }; - NanMakeCallback(NanGetCurrentContext()->Global(), NanNew(closure->cb), 1, argv); - } else { - Local result; + Nan::TryCatch try_catch; + if (closure->error.size() > 0) + { + v8::Local argv[1] = {Nan::Error(closure->error.c_str())}; + Nan::MakeCallback(Nan::GetCurrentContext()->Global(), Nan::New(closure->cb), 1, argv); + } + else + { + v8::Local result; osrm::json::render(result, closure->result); - Local argv[2] = { NanNull(), result }; - NanMakeCallback(NanGetCurrentContext()->Global(), NanNew(closure->cb), 2, argv); + v8::Local argv[2] = {Nan::Null(), result}; + Nan::MakeCallback(Nan::GetCurrentContext()->Global(), Nan::New(closure->cb), 2, argv); } - if (try_catch.HasCaught()) { - node::FatalException(try_catch); + if (try_catch.HasCaught()) + { + Nan::FatalException(try_catch); } closure->machine->Unref(); - NanDisposePersistent(closure->cb); + closure->cb.Reset(); delete closure; } extern "C" { - static void start(Handle target) { - Engine::Initialize(target); - } +static void start(v8::Handle target) { Engine::Initialize(target); } } } // namespace node_osrm From 1167f7d6f5295f8775eb29eb9208be43964c5036 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Tue, 15 Dec 2015 20:33:36 +0100 Subject: [PATCH 10/25] Fix LibOSRMConfig code --- src/node_osrm.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/node_osrm.cpp b/src/node_osrm.cpp index e719a85..c40e08c 100644 --- a/src/node_osrm.cpp +++ b/src/node_osrm.cpp @@ -252,8 +252,8 @@ class Engine final : public Nan::ObjectWrap static void AfterRun(uv_work_t *); private: - Engine(LibOSRMConfig lib_config) - : Nan::ObjectWrap(), this_(make_unique(std::move(lib_config))) + Engine(LibOSRMConfig &lib_config) + : Nan::ObjectWrap(), this_(make_unique(lib_config)) { } @@ -296,7 +296,7 @@ void Engine::New(const Nan::FunctionCallbackInfo &args) { return; } - auto engine_ptr = new Engine(std::move(*lib_config_ptr)); + auto engine_ptr = new Engine(*lib_config_ptr); engine_ptr->Wrap(args.This()); args.GetReturnValue().Set(args.This()); } From 718b3fe562c16e72f0d140323274af7ffbcca21e Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Tue, 15 Dec 2015 22:31:23 +0100 Subject: [PATCH 11/25] Remove distance_table parameter test. This parameter is now removed. Needs to be enforced by used. --- test/osrm.test.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/test/osrm.test.js b/test/osrm.test.js index c352e44..5519360 100644 --- a/test/osrm.test.js +++ b/test/osrm.test.js @@ -15,14 +15,6 @@ test('constructor: throws if necessary files do not exist', function(assert) { /hsgr not found/); }); -test('constructor: takes a distance table length argument', function(assert) { - assert.plan(1); - var osrm = new OSRM({path: "berlin-latest.osrm", distance_table: 30000}); - osrm.route({coordinates: [[52.519930,13.438640], [52.513191,13.415852]]}, function(err, route) { - assert.ifError(err); - }); -}); - test('constructor: takes a shared memory argument', function(assert) { assert.plan(1); var osrm = new OSRM({path: "berlin-latest.osrm", shared_memory: false}); @@ -43,12 +35,6 @@ test('constructor: throws if given a non-bool shared_memory option', function(as /shared_memory option must be a boolean/); }); -test('constructor: throws if given a non-uint distance_table option', function(assert) { - assert.plan(1); - assert.throws(function() { var osrm = new OSRM({path: "berlin-latest.osrm", distance_table: -4}); }, - /the maximum number of locations in the distance table must be an unsigned integer/); -}); - test('constructor: throws if given a non-string/obj argument', function(assert) { assert.plan(1); assert.throws(function() { var osrm = new OSRM(true); }, From 9924156940e227b18d27874a5bb896e054f44cd7 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Tue, 15 Dec 2015 22:32:04 +0100 Subject: [PATCH 12/25] Remove locate tests --- test/osrm.test.js | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/test/osrm.test.js b/test/osrm.test.js index 5519360..4f7fbb1 100644 --- a/test/osrm.test.js +++ b/test/osrm.test.js @@ -562,31 +562,3 @@ test('nearest: throws on invalid args', function(assert) { assert.throws(function() { osrm.nearest(options, function(err, res) {}); }, /first argument must be an array of lat, long/); }); - -////////////////////////////// LOCATE TESTS //////////////////////////////////// - -test('locate', function(assert) { - assert.plan(3); - var osrm = new OSRM("berlin-latest.osrm"); - osrm.locate([52.4224, 13.333086], function(err, result) { - assert.ifError(err); - assert.equal(result.status, 0); - assert.equal(result.mapped_coordinate.length, 2); - }); -}); - -test('locate: throws on incorrect number of args', function(assert) { - assert.plan(1); - var osrm = new OSRM("berlin-latest.osrm"); - assert.throws(function() { osrm.locate([52.4224, 13.333086]) }, - /two arguments required/); -}); - -test('locate: throws on invalid coordinate arg', function(assert) { - assert.plan(2); - var osrm = new OSRM("berlin-latest.osrm"); - assert.throws(function() { osrm.locate(52.4224, function(err, result) {}) }, - /first argument must be an array of lat, long/); - assert.throws(function() { osrm.locate([52.4224], function(err, result) {}) }, - /first argument must be an array of lat, long/); -}); From fcead02d225fc0afab666cc63babc2c663505f82 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Wed, 16 Dec 2015 16:53:40 +0100 Subject: [PATCH 13/25] Add OSRM clang-format --- .clang-format | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..f5577f5 --- /dev/null +++ b/.clang-format @@ -0,0 +1,54 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +ConstructorInitializerIndentWidth: 4 +AlignEscapedNewlinesLeft: false +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AllowShortFunctionsOnASingleLine: true +AlwaysBreakTemplateDeclarations: false +AlwaysBreakBeforeMultilineStrings: false +BreakBeforeBinaryOperators: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BinPackParameters: false +ColumnLimit: 100 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +DerivePointerBinding: false +ExperimentalAutoDetectBinPacking: false +IndentCaseLabels: false +MaxEmptyLinesToKeep: 1 +KeepEmptyLinesAtTheStartOfBlocks: true +NamespaceIndentation: None +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakString: 1000 +PenaltyBreakFirstLessLess: 120 +PenaltyExcessCharacter: 1000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerBindsToType: false +SpacesBeforeTrailingComments: 1 +Cpp11BracedListStyle: true +Standard: Cpp11 +IndentWidth: 4 +TabWidth: 8 +UseTab: Never +BreakBeforeBraces: Allman +IndentFunctionDeclarationAfterType: false +SpacesInParentheses: false +SpacesInAngles: false +SpaceInEmptyParentheses: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: true +SpaceBeforeAssignmentOperators: true +ContinuationIndentWidth: 4 +CommentPragmas: '^ IWYU pragma:' +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +SpaceBeforeParens: ControlStatements +... + From d18e34c32ef5839965aa1846b3ca3b5196af33bc Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Wed, 16 Dec 2015 18:10:43 +0100 Subject: [PATCH 14/25] Add missing includes and clangformat --- src/node_osrm.cpp | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/node_osrm.cpp b/src/node_osrm.cpp index c40e08c..f64050f 100644 --- a/src/node_osrm.cpp +++ b/src/node_osrm.cpp @@ -10,10 +10,17 @@ #include #include +#include // STL #include #include +#include +#include +#include +#include +#include +#include namespace node_osrm { @@ -83,7 +90,8 @@ libosrm_config_ptr argumentsToLibOSRMConfig(const Nan::FunctionCallbackInfo> parseCoordinateArray(const v8::Local &coordinates_array) +boost::optional> +parseCoordinateArray(const v8::Local &coordinates_array) { boost::optional> resulting_coordinates; std::vector temp_coordinates; @@ -109,8 +117,7 @@ boost::optional> parseCoordinateArray(const v8 static_cast(coordinate_pair->Get(0)->NumberValue() * COORDINATE_PRECISION), static_cast(coordinate_pair->Get(1)->NumberValue() * COORDINATE_PRECISION)); } - resulting_coordinates = - boost::make_optional>(std::move(temp_coordinates)); + resulting_coordinates = boost::make_optional(std::move(temp_coordinates)); return resulting_coordinates; } @@ -150,9 +157,11 @@ route_parameters_ptr argumentsToParameter(const Nan::FunctionCallbackInfobegin(), maybe_coordinates->end(), std::back_inserter(params->coordinates)); + std::copy(maybe_coordinates->begin(), maybe_coordinates->end(), + std::back_inserter(params->coordinates)); params->is_source.insert(params->is_source.end(), maybe_coordinates->size(), true); - params->is_destination.insert(params->is_destination.end(), maybe_coordinates->size(), true); + params->is_destination.insert(params->is_destination.end(), maybe_coordinates->size(), + true); } else { @@ -252,10 +261,7 @@ class Engine final : public Nan::ObjectWrap static void AfterRun(uv_work_t *); private: - Engine(LibOSRMConfig &lib_config) - : Nan::ObjectWrap(), this_(make_unique(lib_config)) - { - } + Engine(LibOSRMConfig &lib_config) : Nan::ObjectWrap(), this_(make_unique(lib_config)) {} static Nan::Persistent constructor; osrm_ptr this_; @@ -434,9 +440,11 @@ void Engine::table(const Nan::FunctionCallbackInfo &args) auto maybe_sources = parseCoordinateArray(sources_array); if (maybe_sources) { - std::copy(maybe_sources->begin(), maybe_sources->end(), std::back_inserter(params->coordinates)); + std::copy(maybe_sources->begin(), maybe_sources->end(), + std::back_inserter(params->coordinates)); params->is_source.insert(params->is_source.end(), maybe_sources->size(), true); - params->is_destination.insert(params->is_destination.end(), maybe_sources->size(), false); + params->is_destination.insert(params->is_destination.end(), maybe_sources->size(), + false); } else { @@ -449,9 +457,11 @@ void Engine::table(const Nan::FunctionCallbackInfo &args) auto maybe_destinations = parseCoordinateArray(destinations_array); if (maybe_destinations) { - std::copy(maybe_destinations->begin(), maybe_destinations->end(), std::back_inserter(params->coordinates)); + std::copy(maybe_destinations->begin(), maybe_destinations->end(), + std::back_inserter(params->coordinates)); params->is_source.insert(params->is_source.end(), maybe_destinations->size(), false); - params->is_destination.insert(params->is_destination.end(), maybe_destinations->size(), true); + params->is_destination.insert(params->is_destination.end(), maybe_destinations->size(), + true); } else { From 49a218b77c2fac0d598073165b5d124798193f34 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Thu, 17 Dec 2015 04:22:13 +0100 Subject: [PATCH 15/25] Move sharedmemory to own command --- Makefile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index d85a603..3d5ec0a 100755 --- a/Makefile +++ b/Makefile @@ -53,10 +53,12 @@ berlin-latest.osrm: berlin-latest.osm.pbf ./lib/binding/osrm-extract berlin-latest.osm.pbf -p test/data/car.lua berlin-latest.osrm.hsgr: berlin-latest.osrm - ./lib/binding/osrm-prepare berlin-latest.osrm -p test/data/car.lua && \ - ./lib/binding/osrm-datastore berlin-latest.osrm + ./lib/binding/osrm-prepare berlin-latest.osrm -p test/data/car.lua -test: berlin-latest.osrm.hsgr +shm: berlin-lastest.osrm.hsgr + ./lib/binding/osrm-datastore berlin-latest.osrm + +test: shm npm test -.PHONY: test clean build +.PHONY: test clean build shm From 5011f4d6fad4428083243d39de6e2ee8fba952be Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Thu, 17 Dec 2015 18:32:48 +0100 Subject: [PATCH 16/25] Refresh data prepare --- .travis.yml | 6 +- Makefile | 27 +-- test/data/Makefile | 35 ++++ test/data/blank.edges | 0 test/data/blank.fileIndex | 0 test/data/blank.hsgr | 0 test/data/blank.names | 0 test/data/blank.nodes | 0 test/data/blank.ramIndex | 0 test/data/blank.timestamp | 0 test/data/car.lua | 402 -------------------------------------- test/data/data.md5sum | 3 + 12 files changed, 50 insertions(+), 423 deletions(-) create mode 100755 test/data/Makefile delete mode 100644 test/data/blank.edges delete mode 100644 test/data/blank.fileIndex delete mode 100644 test/data/blank.hsgr delete mode 100644 test/data/blank.names delete mode 100644 test/data/blank.nodes delete mode 100644 test/data/blank.ramIndex delete mode 100644 test/data/blank.timestamp delete mode 100644 test/data/car.lua create mode 100644 test/data/data.md5sum diff --git a/.travis.yml b/.travis.yml index 34c6b80..4d3e540 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,7 @@ addons: sources: - ubuntu-toolchain-r-test packages: + - coreutils - g++-4.8 - gdb @@ -67,11 +68,12 @@ env: before_install: - scripts/validate_tag.sh - export COVERAGE=${COVERAGE:-false} -- if [[ $(uname -s) == 'Linux' ]]; then +- | + if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then export CXX="g++-4.8"; export CC="gcc-4.8"; export PYTHONPATH=$(pwd)/mason_packages/.link/lib/python2.7/site-packages; - else + elif [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then export PYTHONPATH=$(pwd)/mason_packages/.link/lib/python/site-packages; brew install md5sha1sum; fi diff --git a/Makefile b/Makefile index 3d5ec0a..463a92d 100755 --- a/Makefile +++ b/Makefile @@ -1,4 +1,8 @@ #http://www.gnu.org/prep/standards/html_node/Standard-Targets.html#Standard-Targets +TOOL_ROOT?=$(shell pwd)/lib/binding +OSRM_DATASTORE:=$(TOOL_ROOT)/osrm-datastore +export TOOL_ROOT +export OSRM_DATASTORE all: build @@ -26,37 +30,22 @@ verbose: pkgconfig ./node_modules clean: @rm -rf ./build - rm -rf ./lib/binding/ + rm -rf ./lib/binding/* rm -rf ./node_modules/ rm -f ./*tgz - rm -f ./*.osrm* rm -rf ./mason_packages rm -rf ./osrm-backend-* grind: valgrind --leak-check=full node node_modules/.bin/_mocha -testpack: - rm -f ./*tgz - npm pack - tar -ztvf *tgz - rm -f ./*tgz - rebuild: @make clean @make -berlin-latest.osm.pbf: - wget http://download.geofabrik.de/europe/germany/berlin-latest.osm.pbf - -berlin-latest.osrm: berlin-latest.osm.pbf - ./lib/binding/osrm-extract berlin-latest.osm.pbf -p test/data/car.lua - -berlin-latest.osrm.hsgr: berlin-latest.osrm - ./lib/binding/osrm-prepare berlin-latest.osrm -p test/data/car.lua - -shm: berlin-lastest.osrm.hsgr - ./lib/binding/osrm-datastore berlin-latest.osrm +shm: ./test/data/Makefile + $(MAKE) -C ./test/data + $(OSRM_DATASTORE) ./test/data/berlin-latest.osrm test: shm npm test diff --git a/test/data/Makefile b/test/data/Makefile new file mode 100755 index 0000000..47a0488 --- /dev/null +++ b/test/data/Makefile @@ -0,0 +1,35 @@ +CAR_PROFILE_URL:=https://raw.githubusercontent.com/Project-OSRM/osrm-backend/$(OSRM_RELEASE)/profiles/car.lua +PROFILE_LIB_URL:=https://raw.githubusercontent.com/Project-OSRM/osrm-backend/$(OSRM_RELEASE)/profiles/lib/access.lua +BERLIN_URL:=https://s3.amazonaws.com/mapbox/node-osrm/testing/berlin-latest.osm.pbf +OSRM_EXTRACT:=$(TOOL_ROOT)/osrm-extract +OSRM_PREPARE:=$(TOOL_ROOT)/osrm-prepare + +all: berlin-latest.osrm.hsgr + +lib/access.lua: + mkdir -p lib + wget $(PROFILE_LIB_URL) -O lib/access.lua + +car.lua: lib/access.lua + wget $(CAR_PROFILE_URL) -O car.lua + +clean: + rm berlin-latest.* + +berlin-latest.osm.pbf: + wget $(BERLIN_URL) -O berlin-latest.osm.pbf + +berlin-latest.osrm: berlin-latest.osm.pbf car.lua $(OSRM_EXTRACT) + @echo "Verifiyng data file integrity..." + md5sum -c data.md5sum + @echo "Running osrm-extract..." + $(OSRM_EXTRACT) berlin-latest.osm.pbf -p car.lua + +berlin-latest.osrm.hsgr: berlin-latest.osrm car.lua $(OSRM_PREPARE) + @echo "Running osrm-prepare..." + $(OSRM_PREPARE) berlin-latest.osrm -p car.lua + +checksum: + md5sum lib/access.lua car.lua berlin-latest.osm.pbf > data.md5sum + +.PHONY: clean checksum diff --git a/test/data/blank.edges b/test/data/blank.edges deleted file mode 100644 index e69de29..0000000 diff --git a/test/data/blank.fileIndex b/test/data/blank.fileIndex deleted file mode 100644 index e69de29..0000000 diff --git a/test/data/blank.hsgr b/test/data/blank.hsgr deleted file mode 100644 index e69de29..0000000 diff --git a/test/data/blank.names b/test/data/blank.names deleted file mode 100644 index e69de29..0000000 diff --git a/test/data/blank.nodes b/test/data/blank.nodes deleted file mode 100644 index e69de29..0000000 diff --git a/test/data/blank.ramIndex b/test/data/blank.ramIndex deleted file mode 100644 index e69de29..0000000 diff --git a/test/data/blank.timestamp b/test/data/blank.timestamp deleted file mode 100644 index e69de29..0000000 diff --git a/test/data/car.lua b/test/data/car.lua deleted file mode 100644 index ad50096..0000000 --- a/test/data/car.lua +++ /dev/null @@ -1,402 +0,0 @@ --- Begin of globals ---require("lib/access") --function temporarily inlined - -barrier_whitelist = { ["cattle_grid"] = true, ["border_control"] = true, ["checkpoint"] = true, ["toll_booth"] = true, ["sally_port"] = true, ["gate"] = true, ["lift_gate"] = true, ["no"] = true, ["entrance"] = true } -access_tag_whitelist = { ["yes"] = true, ["motorcar"] = true, ["motor_vehicle"] = true, ["vehicle"] = true, ["permissive"] = true, ["designated"] = true } -access_tag_blacklist = { ["no"] = true, ["private"] = true, ["agricultural"] = true, ["forestry"] = true, ["emergency"] = true } -access_tag_restricted = { ["destination"] = true, ["delivery"] = true } -access_tags = { "motorcar", "motor_vehicle", "vehicle" } -access_tags_hierachy = { "motorcar", "motor_vehicle", "vehicle", "access" } -service_tag_restricted = { ["parking_aisle"] = true } -ignore_in_grid = { ["ferry"] = true } -restriction_exception_tags = { "motorcar", "motor_vehicle", "vehicle" } - -speed_profile = { - ["motorway"] = 90, - ["motorway_link"] = 45, - ["trunk"] = 85, - ["trunk_link"] = 40, - ["primary"] = 65, - ["primary_link"] = 30, - ["secondary"] = 55, - ["secondary_link"] = 25, - ["tertiary"] = 40, - ["tertiary_link"] = 20, - ["unclassified"] = 25, - ["residential"] = 25, - ["living_street"] = 10, - ["service"] = 15, --- ["track"] = 5, - ["ferry"] = 5, - ["shuttle_train"] = 10, - ["default"] = 10 -} - - --- surface/trackype/smoothness --- values were estimated from looking at the photos at the relevant wiki pages - --- max speed for surfaces -surface_speeds = { - ["asphalt"] = nil, -- nil mean no limit. removing the line has the same effect - ["concrete"] = nil, - ["concrete:plates"] = nil, - ["concrete:lanes"] = nil, - ["paved"] = nil, - - ["cement"] = 80, - ["compacted"] = 80, - ["fine_gravel"] = 80, - - ["paving_stones"] = 60, - ["metal"] = 60, - ["bricks"] = 60, - - ["grass"] = 40, - ["wood"] = 40, - ["sett"] = 40, - ["grass_paver"] = 40, - ["gravel"] = 40, - ["unpaved"] = 40, - ["ground"] = 40, - ["dirt"] = 40, - ["pebblestone"] = 40, - ["tartan"] = 40, - - ["cobblestone"] = 30, - ["clay"] = 30, - - ["earth"] = 20, - ["stone"] = 20, - ["rocky"] = 20, - ["sand"] = 20, - - ["mud"] = 10 -} - --- max speed for tracktypes -tracktype_speeds = { - ["grade1"] = 60, - ["grade2"] = 40, - ["grade3"] = 30, - ["grade4"] = 25, - ["grade5"] = 20 -} - --- max speed for smoothnesses -smoothness_speeds = { - ["intermediate"] = 80, - ["bad"] = 40, - ["very_bad"] = 20, - ["horrible"] = 10, - ["very_horrible"] = 5, - ["impassable"] = 0 -} - --- http://wiki.openstreetmap.org/wiki/Speed_limits -maxspeed_table_default = { - ["urban"] = 50, - ["rural"] = 90, - ["trunk"] = 110, - ["motorway"] = 130 -} - --- List only exceptions -maxspeed_table = { - ["ch:rural"] = 80, - ["ch:trunk"] = 100, - ["ch:motorway"] = 120, - ["de:living_street"] = 7, - ["ru:living_street"] = 20, - ["ru:urban"] = 60, - ["ua:urban"] = 60, - ["at:rural"] = 100, - ["de:rural"] = 100, - ["at:trunk"] = 100, - ["cz:trunk"] = 0, - ["ro:trunk"] = 100, - ["cz:motorway"] = 0, - ["de:motorway"] = 0, - ["ru:motorway"] = 110, - ["gb:nsl_single"] = (60*1609)/1000, - ["gb:nsl_dual"] = (70*1609)/1000, - ["gb:motorway"] = (70*1609)/1000, - ["uk:nsl_single"] = (60*1609)/1000, - ["uk:nsl_dual"] = (70*1609)/1000, - ["uk:motorway"] = (70*1609)/1000 -} - -traffic_signal_penalty = 2 -use_turn_restrictions = true - -local take_minimum_of_speeds = false -local obey_oneway = true -local obey_bollards = true -local ignore_areas = true -- future feature -local u_turn_penalty = 20 - -local abs = math.abs -local min = math.min -local max = math.max - -local speed_reduction = 0.8 - ---modes -local mode_normal = 1 -local mode_ferry = 2 - -local function find_access_tag(source, access_tags_hierachy) - for i,v in ipairs(access_tags_hierachy) do - local access_tag = source:get_value_by_key(v) - if access_tag and "" ~= access_tag then - return access_tag - end - end - return "" -end - -function get_exceptions(vector) - for i,v in ipairs(restriction_exception_tags) do - vector:Add(v) - end -end - -local function parse_maxspeed(source) - if not source then - return 0 - end - local n = tonumber(source:match("%d*")) - if n then - if string.match(source, "mph") or string.match(source, "mp/h") then - n = (n*1609)/1000; - end - else - -- parse maxspeed like FR:urban - source = string.lower(source) - n = maxspeed_table[source] - if not n then - local highway_type = string.match(source, "%a%a:(%a+)") - n = maxspeed_table_default[highway_type] - if not n then - n = 0 - end - end - end - return n -end - --- function turn_function (angle) --- -- print ("called at angle " .. angle ) --- local index = math.abs(math.floor(angle/10+0.5))+1 -- +1 'coz LUA starts as idx 1 --- local penalty = turn_cost_table[index] --- -- print ("index: " .. index .. ", bias: " .. penalty ) --- return penalty --- end - -function node_function (node, result) - -- parse access and barrier tags - local access = find_access_tag(node, access_tags_hierachy) - if access ~= "" then - if access_tag_blacklist[access] then - result.barrier = true - end - else - local barrier = node:get_value_by_key("barrier") - if barrier and "" ~= barrier then - if barrier_whitelist[barrier] then - return - else - result.barrier = true - end - end - end - - -- check if node is a traffic light - local tag = node:get_value_by_key("highway") - if tag and "traffic_signals" == tag then - result.traffic_lights = true; - end -end - -function way_function (way, result) - local highway = way:get_value_by_key("highway") - local route = way:get_value_by_key("route") - - if not ((highway and highway ~= "") or (route and route ~= "")) then - return - end - - -- we dont route over areas - local area = way:get_value_by_key("area") - if ignore_areas and area and "yes" == area then - return - end - - -- check if oneway tag is unsupported - local oneway = way:get_value_by_key("oneway") - if oneway and "reversible" == oneway then - return - end - - local impassable = way:get_value_by_key("impassable") - if impassable and "yes" == impassable then - return - end - - local status = way:get_value_by_key("status") - if status and "impassable" == status then - return - end - - -- Check if we are allowed to access the way - local access = find_access_tag(way, access_tags_hierachy) - if access_tag_blacklist[access] then - return - end - - -- Handling ferries and piers - local route_speed = speed_profile[route] - if(route_speed and route_speed > 0) then - highway = route; - local duration = way:get_value_by_key("duration") - if duration and durationIsValid(duration) then - result.duration = max( parseDuration(duration), 1 ); - end - result.forward_mode = mode_ferry - result.backward_mode = mode_ferry - result.forward_speed = route_speed - result.backward_speed = route_speed - end - - -- leave early of this way is not accessible - if "" == highway then - return - end - - if result.forward_speed == -1 then - local highway_speed = speed_profile[highway] - local max_speed = parse_maxspeed( way:get_value_by_key("maxspeed") ) - -- Set the avg speed on the way if it is accessible by road class - if highway_speed then - if max_speed and max_speed > highway_speed then - result.forward_speed = max_speed - result.backward_speed = max_speed - -- max_speed = math.huge - else - result.forward_speed = highway_speed - result.backward_speed = highway_speed - end - else - -- Set the avg speed on ways that are marked accessible - if access_tag_whitelist[access] then - result.forward_speed = speed_profile["default"] - result.backward_speed = speed_profile["default"] - end - end - if 0 == max_speed then - max_speed = math.huge - end - result.forward_speed = min(result.forward_speed, max_speed) - result.backward_speed = min(result.backward_speed, max_speed) - end - - if -1 == result.forward_speed and -1 == result.backward_speed then - return - end - - -- reduce speed on bad surfaces - local surface = way:get_value_by_key("surface") - local tracktype = way:get_value_by_key("tracktype") - local smoothness = way:get_value_by_key("smoothness") - - if surface and surface_speeds[surface] then - result.forward_speed = math.min(surface_speeds[surface], result.forward_speed) - result.backward_speed = math.min(surface_speeds[surface], result.backward_speed) - end - if tracktype and tracktype_speeds[tracktype] then - result.forward_speed = math.min(tracktype_speeds[tracktype], result.forward_speed) - result.backward_speed = math.min(tracktype_speeds[tracktype], result.backward_speed) - end - if smoothness and smoothness_speeds[smoothness] then - result.forward_speed = math.min(smoothness_speeds[smoothness], result.forward_speed) - result.backward_speed = math.min(smoothness_speeds[smoothness], result.backward_speed) - end - - -- parse the remaining tags - local name = way:get_value_by_key("name") - local ref = way:get_value_by_key("ref") - local junction = way:get_value_by_key("junction") - -- local barrier = way:get_value_by_key("barrier", "") - -- local cycleway = way:get_value_by_key("cycleway", "") - local service = way:get_value_by_key("service") - - -- Set the name that will be used for instructions - if ref and "" ~= ref then - result.name = ref - elseif name and "" ~= name then - result.name = name --- else - -- result.name = highway -- if no name exists, use way type - end - - if junction and "roundabout" == junction then - result.roundabout = true; - end - - -- Set access restriction flag if access is allowed under certain restrictions only - if access ~= "" and access_tag_restricted[access] then - result.is_access_restricted = true - end - - -- Set access restriction flag if service is allowed under certain restrictions only - if service and service ~= "" and service_tag_restricted[service] then - result.is_access_restricted = true - end - - -- Set direction according to tags on way - if obey_oneway then - if oneway == "-1" then - result.forward_mode = 0 - elseif oneway == "yes" or - oneway == "1" or - oneway == "true" or - junction == "roundabout" or - (highway == "motorway_link" and oneway ~="no") or - (highway == "motorway" and oneway ~= "no") then - result.backward_mode = 0 - end - end - - -- Override speed settings if explicit forward/backward maxspeeds are given - local maxspeed_forward = parse_maxspeed(way:get_value_by_key( "maxspeed:forward")) - local maxspeed_backward = parse_maxspeed(way:get_value_by_key( "maxspeed:backward")) - if maxspeed_forward and maxspeed_forward > 0 then - if 0 ~= result.forward_mode and 0 ~= result.backward_mode then - result.backward_speed = result.forward_speed - end - result.forward_speed = maxspeed_forward - end - if maxspeed_backward and maxspeed_backward > 0 then - result.backward_speed = maxspeed_backward - end - - -- Override general direction settings of there is a specific one for our mode of travel - if ignore_in_grid[highway] then - result.ignore_in_grid = true - end - - -- scale speeds to get better avg driving times - if result.forward_speed > 0 then - result.forward_speed = result.forward_speed*speed_reduction + 11; - end - if result.backward_speed > 0 then - result.backward_speed = result.backward_speed*speed_reduction + 11; - end -end - --- These are wrappers to parse vectors of nodes and ways and thus to speed up any tracing JIT -function node_vector_function(vector) - for v in vector.nodes do - node_function(v) - end -end diff --git a/test/data/data.md5sum b/test/data/data.md5sum new file mode 100644 index 0000000..9953cb6 --- /dev/null +++ b/test/data/data.md5sum @@ -0,0 +1,3 @@ +db2ddaa47d4cb3f6403638c2b22c88c8 lib/access.lua +0dd83131d425cbb1b50c036a35529cfc car.lua +045af81d07eb9f22e5718db13cf337e4 berlin-latest.osm.pbf From b9eedc17e7170fa1c7412988e637fe52b15c8342 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Thu, 17 Dec 2015 21:22:25 +0100 Subject: [PATCH 17/25] Add HandleScope to each function that creates Local<> objects --- Makefile | 4 ---- src/node_osrm.cpp | 11 +++++++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 463a92d..6d15d95 100755 --- a/Makefile +++ b/Makefile @@ -39,10 +39,6 @@ clean: grind: valgrind --leak-check=full node node_modules/.bin/_mocha -rebuild: - @make clean - @make - shm: ./test/data/Makefile $(MAKE) -C ./test/data $(OSRM_DATASTORE) ./test/data/berlin-latest.osrm diff --git a/src/node_osrm.cpp b/src/node_osrm.cpp index f64050f..33d2ed9 100644 --- a/src/node_osrm.cpp +++ b/src/node_osrm.cpp @@ -39,6 +39,7 @@ template std::unique_ptr make_unique(Types &&... Ar // Supports libosrm_config_ptr argumentsToLibOSRMConfig(const Nan::FunctionCallbackInfo &args) { + Nan::HandleScope scope; auto lib_config = make_unique(); if (args.Length() == 0) @@ -93,6 +94,7 @@ libosrm_config_ptr argumentsToLibOSRMConfig(const Nan::FunctionCallbackInfo> parseCoordinateArray(const v8::Local &coordinates_array) { + Nan::HandleScope scope; boost::optional> resulting_coordinates; std::vector temp_coordinates; @@ -124,6 +126,7 @@ parseCoordinateArray(const v8::Local &coordinates_array) // Parses all the non-service specific parameters route_parameters_ptr argumentsToParameter(const Nan::FunctionCallbackInfo &args) { + Nan::HandleScope scope; auto params = make_unique(); if (args.Length() < 2) @@ -288,6 +291,7 @@ void Engine::Initialize(v8::Handle target) void Engine::New(const Nan::FunctionCallbackInfo &args) { + Nan::HandleScope scope; if (!args.IsConstructCall()) { Nan::ThrowTypeError("Cannot call constructor as function, you need to use 'new' " @@ -324,6 +328,7 @@ struct RunQueryBaton void Engine::route(const Nan::FunctionCallbackInfo &args) { + Nan::HandleScope scope; auto params = argumentsToParameter(args); if (!params) return; @@ -335,6 +340,7 @@ void Engine::route(const Nan::FunctionCallbackInfo &args) void Engine::match(const Nan::FunctionCallbackInfo &args) { + Nan::HandleScope scope; auto params = argumentsToParameter(args); if (!params) return; @@ -392,6 +398,7 @@ void Engine::match(const Nan::FunctionCallbackInfo &args) // uses the same options as viaroute void Engine::trip(const Nan::FunctionCallbackInfo &args) { + Nan::HandleScope scope; auto params = argumentsToParameter(args); if (!params) return; @@ -403,6 +410,7 @@ void Engine::trip(const Nan::FunctionCallbackInfo &args) void Engine::table(const Nan::FunctionCallbackInfo &args) { + Nan::HandleScope scope; auto params = argumentsToParameter(args); if (!params) return; @@ -479,6 +487,7 @@ void Engine::table(const Nan::FunctionCallbackInfo &args) void Engine::nearest(const Nan::FunctionCallbackInfo &args) { + Nan::HandleScope scope; if (args.Length() < 2) { Nan::ThrowTypeError("two arguments required"); @@ -510,6 +519,7 @@ void Engine::nearest(const Nan::FunctionCallbackInfo &args) void Engine::Run(const Nan::FunctionCallbackInfo &args, route_parameters_ptr params) { + Nan::HandleScope scope; v8::Local callback = args[args.Length() - 1]; if (!callback->IsFunction()) @@ -545,6 +555,7 @@ void Engine::AsyncRun(uv_work_t *req) void Engine::AfterRun(uv_work_t *req) { + Nan::HandleScope scope; RunQueryBaton *closure = static_cast(req->data); Nan::TryCatch try_catch; if (closure->error.size() > 0) From 50ea6781190c0c4b547aebace770d87df4a8140d Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Thu, 17 Dec 2015 21:32:33 +0100 Subject: [PATCH 18/25] Fix and restructure tests --- package.json | 2 +- src/node_osrm.cpp | 38 +++- test/index.js | 48 ++++ test/match.js | 124 ++++++++++ test/nearest.js | 26 +++ test/osrm.test.js | 564 ---------------------------------------------- test/route.js | 151 +++++++++++++ test/table.js | 59 +++++ test/trip.js | 166 ++++++++++++++ 9 files changed, 610 insertions(+), 568 deletions(-) create mode 100644 test/index.js create mode 100644 test/match.js create mode 100644 test/nearest.js delete mode 100644 test/osrm.test.js create mode 100644 test/route.js create mode 100644 test/table.js create mode 100644 test/trip.js diff --git a/package.json b/package.json index eb7848b..da1b275 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,6 @@ "scripts": { "prepublish": "npm ls", "install": "node-pre-gyp install --fallback-to-build", - "test": "node test/osrm.test.js" + "test": "node test/index.js" } } diff --git a/src/node_osrm.cpp b/src/node_osrm.cpp index 33d2ed9..efe0ff7 100644 --- a/src/node_osrm.cpp +++ b/src/node_osrm.cpp @@ -62,7 +62,7 @@ libosrm_config_ptr argumentsToLibOSRMConfig(const Nan::FunctionCallbackInfoIsObject()) { - Nan::ThrowError("parameter must be path or options object."); + Nan::ThrowError("parameter must be a path or options object"); return libosrm_config_ptr(); } @@ -78,7 +78,15 @@ libosrm_config_ptr argumentsToLibOSRMConfig(const Nan::FunctionCallbackInfoIsUndefined()) { - lib_config->use_shared_memory = Nan::To(shared_memory).FromJust(); + if (shared_memory->IsBoolean()) + { + lib_config->use_shared_memory = Nan::To(shared_memory).FromJust(); + } + else + { + Nan::ThrowError("shared_memory option must be a boolean"); + return libosrm_config_ptr(); + } } if (path->IsUndefined() && !lib_config->use_shared_memory) @@ -162,6 +170,7 @@ route_parameters_ptr argumentsToParameter(const Nan::FunctionCallbackInfobegin(), maybe_coordinates->end(), std::back_inserter(params->coordinates)); + params->uturns.insert(params->uturns.end(), maybe_coordinates->size(), false); params->is_source.insert(params->is_source.end(), maybe_coordinates->size(), true); params->is_destination.insert(params->is_destination.end(), maybe_coordinates->size(), true); @@ -450,6 +459,7 @@ void Engine::table(const Nan::FunctionCallbackInfo &args) { std::copy(maybe_sources->begin(), maybe_sources->end(), std::back_inserter(params->coordinates)); + params->uturns.insert(params->uturns.end(), maybe_sources->size(), false); params->is_source.insert(params->is_source.end(), maybe_sources->size(), true); params->is_destination.insert(params->is_destination.end(), maybe_sources->size(), false); @@ -467,6 +477,7 @@ void Engine::table(const Nan::FunctionCallbackInfo &args) { std::copy(maybe_destinations->begin(), maybe_destinations->end(), std::back_inserter(params->coordinates)); + params->uturns.insert(params->uturns.end(), maybe_destinations->size(), false); params->is_source.insert(params->is_source.end(), maybe_destinations->size(), false); params->is_destination.insert(params->is_destination.end(), maybe_destinations->size(), true); @@ -545,7 +556,28 @@ void Engine::AsyncRun(uv_work_t *req) RunQueryBaton *closure = static_cast(req->data); try { - closure->machine->this_->RunQuery(*closure->params, closure->result); + const auto result_code = closure->machine->this_->RunQuery(*closure->params, closure->result); + const auto message_iter = closure->result.values.find("status_message"); + const auto end_iter = closure->result.values.end(); + + // 4xx : Invalid request + // 207 : No route found + // 208 : No edge found + if (result_code / 100 == 4 || result_code == 207 || result_code == 208) + { + if (message_iter != end_iter) + { + throw std::logic_error(closure->result.values["status_message"].get().value.c_str()); + } + else + { + throw std::logic_error("invalid request"); + } + } + if (message_iter != end_iter) + { + closure->result.values.erase(message_iter); + } } catch (std::exception const &ex) { diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..9621897 --- /dev/null +++ b/test/index.js @@ -0,0 +1,48 @@ +var OSRM = require('../'); +var test = require('tape'); +var berlin_path = "test/data/berlin-latest.osrm"; + +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: throws if necessary files do not exist', function(assert) { + assert.plan(1); + assert.throws(function() { new OSRM("missing.osrm"); }, + /hsgr not found/); +}); + +test('constructor: takes a shared memory argument', function(assert) { + assert.plan(1); + var osrm = new OSRM({path: berlin_path, shared_memory: false}); + osrm.route({coordinates: [[52.519930,13.438640], [52.513191,13.415852]]}, function(err, route) { + assert.ifError(err); + }); +}); + +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('./table.js'); +require('./nearest.js'); + diff --git a/test/match.js b/test/match.js new file mode 100644 index 0000000..a510dc7 --- /dev/null +++ b/test/match.js @@ -0,0 +1,124 @@ +var OSRM = require('../'); +var test = require('tape'); +var berlin_path = "test/data/berlin-latest.osrm"; + +test('match: match in Berlin', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.542648,13.393252], [52.543079,13.394780], [52.542107,13.397389]], + timestamps: [1424684612, 1424684616, 1424684620] + }; + osrm.match(options, function(err, response) { + assert.ifError(err); + assert.equal(response.matchings.length, 1); + }); +}); + +test('match: match in Berlin without timestamps', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.542648,13.393252], [52.543079,13.394780], [52.542107,13.397389]] + }; + osrm.match(options, function(err, response) { + assert.ifError(err); + assert.equal(response.matchings.length, 1); + }); +}); + +test('match: match in Berlin with geometry compression', function(assert) { + assert.plan(3); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.542648,13.393252], [52.543079,13.394780], [52.542107,13.397389]] + }; + 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 without geometry compression', function(assert) { + assert.plan(3); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.542648,13.393252], [52.543079,13.394780], [52.542107,13.397389]], + compression: false + }; + osrm.match(options, function(err, response) { + assert.ifError(err); + assert.equal(response.matchings.length, 1); + assert.ok(response.matchings[0].geometry instanceof Array); + }); +}); + +test('match: match in Berlin with all options', function(assert) { + assert.plan(4); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.542648,13.393252], [52.543079,13.394780], [52.542107,13.397389]], + timestamps: [1424684612, 1424684616, 1424684620], + classify: true, + gps_precision: 4.07, + matching_beta: 10.0, + geometry: false + }; + osrm.match(options, function(err, response) { + assert.ifError(err); + assert.equal(response.matchings.length, 1); + assert.equal(response.matchings[0].confidence > 0, true); + 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 (lat/long) pairs"); + options.coordinates = [[52.542648,13.393252]]; + assert.throws(function() { osrm.match(options, function(err, response) {}) }, + /at least two coordinates must be provided/); + options.coordinates = [52.542648,13.393252]; + assert.throws(function() { osrm.match(options, function(err, response) {}) }, + "coordinates must be an array of (lat/long) pairs"); + options.coordinates = [[52.542648],[13.393252]]; + assert.throws(function() { osrm.match(options, function(err, response) {}) }, + "coordinates must be an array of (lat/long) pairs"); +}); + +test('match: throws on invalid timestamps param', function(assert) { + assert.plan(3); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.542648,13.393252], [52.543079,13.394780], [52.542107,13.397389]], + 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/nearest.js b/test/nearest.js new file mode 100644 index 0000000..5324d31 --- /dev/null +++ b/test/nearest.js @@ -0,0 +1,26 @@ +var OSRM = require('../'); +var test = require('tape'); +var berlin_path = "test/data/berlin-latest.osrm"; + +test('nearest', function(assert) { + assert.plan(3); + var osrm = new OSRM(berlin_path); + osrm.nearest([52.4224, 13.333086], function(err, result) { + assert.ifError(err); + assert.equal(result.mapped_coordinate.length, 2); + assert.ok(result.hasOwnProperty('name')); + }); +}); + +test('nearest: throws on invalid args', function(assert) { + assert.plan(3); + 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 argument must be an array of lat, long/); + options.coordinates = [52.4224]; + assert.throws(function() { osrm.nearest(options, function(err, res) {}); }, + /first argument must be an array of lat, long/); +}); diff --git a/test/osrm.test.js b/test/osrm.test.js deleted file mode 100644 index 4f7fbb1..0000000 --- a/test/osrm.test.js +++ /dev/null @@ -1,564 +0,0 @@ -var OSRM = require('../'); -var test = require('tape'); - -///////////////////////////// CONSTRUCTOR TESTS //////////////////////////////// - -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: throws if necessary files do not exist', function(assert) { - assert.plan(1); - assert.throws(function() { new OSRM("missing.osrm"); }, - /hsgr not found/); -}); - -test('constructor: takes a shared memory argument', function(assert) { - assert.plan(1); - var osrm = new OSRM({path: "berlin-latest.osrm", shared_memory: false}); - osrm.route({coordinates: [[52.519930,13.438640], [52.513191,13.415852]]}, function(err, route) { - assert.ifError(err); - }); -}); - -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-latest.osrm", 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); }, - /first argument must be a path string or params object/); -}); - -/////////////////////////////// ROUTE TESTS //////////////////////////////////// - -test('route: routes Berlin', function(assert) { - assert.plan(2); - var osrm = new OSRM("berlin-latest.osrm"); - osrm.route({coordinates: [[52.519930,13.438640], [52.513191,13.415852]]}, function(err, route) { - assert.ifError(err); - assert.equal(route.status_message, 'Found route between points'); - }); -}); - -test('route: throws with too few or invalid args', function(assert) { - assert.plan(2); - var osrm = new OSRM("berlin-latest.osrm"); - assert.throws(function() { osrm.route({coordinates: [[52.519930,13.438640], [52.513191,13.415852]]}) }, - /two arguments required/); - assert.throws(function() { osrm.route(null, function(err, route) {}) }, - /first arg must be an object/); -}); - -test('route: throws with bad params', function(assert) { - assert.plan(7); - var osrm = new OSRM("berlin-latest.osrm"); - 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 (lat/long) pairs"); - assert.throws(function() { osrm.route({coordinates: [52.519930, 13.438640]}, function(err, route) {}) }, - "coordinates must be an array of (lat/long) pairs"); - assert.throws(function() { osrm.route({coordinates: [[52.519930], [13.438640]]}, function(err, route) {}) }, - "coordinates must be an array of (lat/long) pairs"); - assert.throws(function() { osrm.route({coordinates: [[52.519930,13.438640], [52.513191,13.415852]], hints: null}, function(err, route) {}) }, - "hints must be an array of strings/null"); - var options = { - coordinates: [[52.519930,13.438640], [52.513191,13.415852]], - alternateRoute: false, - printInstructions: false, - hints: [[52.519930,13.438640]] - }; - assert.throws(function() { osrm.route(options, function(err, route) {}) }, - /hint must be null or string/); -}); - -test('route: takes jsonp parameter', function(assert) { - assert.plan(2); - var osrm = new OSRM("berlin-latest.osrm"); - osrm.route({coordinates: [[52.519930,13.438640], [52.513191,13.415852]], jsonpParameter: 'function'}, function(err, route) { - assert.ifError(err); - assert.equal(route.status_message, 'Found route between points'); - }); -}); - -if (process.platform === 'darwin') { - // shared memory does not work on Mac OS for now. - test.skip('route: routes Berlin using shared memory', function(assert) {}); -} else { - test('route: routes Berlin using shared memory', function(assert) { - assert.plan(2); - var osrm = new OSRM(); - osrm.route({coordinates: [[52.519930,13.438640], [52.513191,13.415852]]}, function(err, route) { - assert.ifError(err); - assert.equal(route.status_message, 'Found route between points'); - }); - }); -} - -test('route: routes Berlin with geometry compression', function(assert) { - assert.plan(2); - var osrm = new OSRM("berlin-latest.osrm"); - var options = { - coordinates: [[52.519930,13.438640], [52.513191,13.415852]], - }; - osrm.route(options, function(err, route) { - assert.ifError(err); - assert.equal('string', typeof route.route_geometry); - }); -}); - -test('route: routes Berlin without geometry compression', function(assert) { - assert.plan(3); - var osrm = new OSRM("berlin-latest.osrm"); - var options = { - coordinates: [[52.519930,13.438640], [52.513191,13.415852]], - compression: false - }; - osrm.route(options, function(err, route) { - assert.ifError(err); - assert.equal(route.status_message,'Found route between points'); - assert.ok(Array.isArray(route.route_geometry)); - }); -}); - -test('route: routes Berlin with options', function(assert) { - assert.plan(5); - var osrm = new OSRM("berlin-latest.osrm"); - var options = { - coordinates: [[52.519930,13.438640], [52.513191,13.415852]], - zoomLevel: 17, - alternateRoute: false, - printInstructions: false, - geometry: false - }; - osrm.route(options, function(err, route) { - assert.ifError(err); - assert.equal(route.status_message,'Found route between points'); - assert.equal(undefined, route.route_instructions); - assert.equal(undefined, route.alternative_geometries); - assert.equal(undefined, route.route_geometry); - }); -}); - -test('route: routes Berlin with hints', function(assert) { - assert.plan(5); - var osrm = new OSRM("berlin-latest.osrm"); - var options = { - coordinates: [[52.519930,13.438640], [52.513191,13.415852]], - alternateRoute: false, - printInstructions: false - }; - osrm.route(options, function(err, first) { - assert.ifError(err); - assert.equal(first.status_message, 'Found route between points'); - var checksum = first.hint_data.checksum; - assert.equal("number", typeof(checksum)); - - options.hints = first.hint_data.locations; - options.checksum = checksum; - - 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-latest.osrm"); - var options = { - coordinates: [[52.519930,13.438640], [52.513191,13.415852]], - alternateRoute: false, - printInstructions: false, - hints: ['', '', ''] - }; - osrm.route(options, function(err, second) { - assert.ifError(err); - }); -}); - - -/////////////////////////////// TRIP TESTS //////////////////////////////////// - -test('trip: trip in Berlin', function(assert) { - assert.plan(2); - var osrm = new OSRM("berlin-latest.osrm"); - osrm.trip({coordinates: [[52.519930,13.438640], [52.513191,13.415852]]}, function(err, trip) { - assert.ifError(err); - for (t = 0; t < trip.trips.length; t++) { - assert.equal(trip.trips[t].status_message, 'Found route between points'); - } - }); -}); - -test('trip: trip with many locations in Berlin', function(assert) { - assert.plan(2); - var osrm = new OSRM("berlin-latest.osrm"); - osrm.trip({coordinates: [[52.51663871100423,13.36761474609375],[52.506191342034576,13.374481201171875],[52.50535544522142,13.404693603515625],[52.50159371284434,13.388900756835938],[52.518727886767266,13.386840820312498],[52.528754547664185,13.4088134765625],[52.51705655410405,13.41156005859375],[52.512042174642346,13.420486450195312],[52.50368360390624,13.413619995117188],[52.504101570196205,13.36212158203125],[52.52248815280757,13.35113525390625],[52.53460237630516,13.36761474609375],[52.53710835019913,13.383407592773438],[52.536690697815736,13.392333984375],[52.532931647583325,13.42529296875],[52.52415927884915,13.399200439453125],[52.51956352925745,13.390960693359375],[52.533349335723294,13.375167846679688],[52.520399155853454,13.37860107421875],[52.52081696319122,13.355255126953125],[52.5143405029259,13.385467529296875],[52.513086884218325,13.398857116699219],[52.50744515744915,13.399200439453125],[52.49783165855699,13.409500122070312],[52.500339730516934,13.424949645996094],[52.50786308797268,13.440055847167969],[52.511624283857785,13.428382873535156],[52.50451953251202,13.437652587890625],[52.5199813445422,13.443145751953125],[52.52520370034151,13.431129455566406],[52.52896341209634,13.418426513671875],[52.517474393230245,13.429069519042969],[52.528127948407935,13.418083190917969],[52.52833681581998,13.405036926269531],[52.53084314728766,13.384437561035156],[52.53084314728766,13.374481201171875],[52.532305107923925,13.3978271484375],[52.526039219655445,13.418769836425781],[52.51642978796417,13.441085815429688],[52.51601193890388,13.448638916015625],[52.50535544522142,13.44623565673828],[52.502638670794546,13.430442810058594],[52.520190250694526,13.358688354492188],[52.531887409851336,13.358001708984375],[52.528545682238736,13.367271423339842],[52.52958999943304,13.387870788574219],[52.53961418106945,13.406410217285156],[52.50556442091497,13.399543762207031],[52.50389258754797,13.374824523925781],[52.51099744023003,13.386154174804688],[52.49657756892365,13.40229034423828]]}, function(err, trip) { - assert.ifError(err); - for (t = 0; t < trip.trips.length; t++) { - assert.equal(trip.trips[t].status_message, 'Found route between points'); - } - }); -}); - -test('trip: throws with too few or invalid args', function(assert) { - assert.plan(2); - var osrm = new OSRM("berlin-latest.osrm"); - assert.throws(function() { osrm.trip({coordinates: [[52.519930,13.438640], [52.513191,13.415852]]}) }, - /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(7); - var osrm = new OSRM("berlin-latest.osrm"); - 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 (lat/long) pairs"); - assert.throws(function() { osrm.trip({coordinates: [52.519930, 13.438640]}, function(err, trip) {}) }, - "coordinates must be an array of (lat/long) pairs"); - assert.throws(function() { osrm.trip({coordinates: [[52.519930], [13.438640]]}, function(err, trip) {}) }, - "coordinates must be an array of (lat/long) pairs"); - assert.throws(function() { osrm.trip({coordinates: [[52.519930,13.438640], [52.513191,13.415852]], hints: null}, function(err, trip) {}) }, - "hints must be an array of strings/null"); - var options = { - coordinates: [[52.519930,13.438640], [52.513191,13.415852]], - printInstructions: false, - hints: [[52.519930,13.438640]] - }; - assert.throws(function() { osrm.trip(options, function(err, trip) {}) }, - /hint must be null or string/); -}); - - -if (process.platform === 'darwin') { - // shared memory does not work on Mac OS for now. - test.skip('trip: routes Berlin using shared memory', function(assert) {}); -} else { - test('trip: routes Berlin using shared memory', function(assert) { - assert.plan(2); - var osrm = new OSRM(); - osrm.trip({coordinates: [[52.519930,13.438640], [52.513191,13.415852]]}, function(err, trip) { - assert.ifError(err); - for (t = 0; t < trip.trips.length; t++) { - assert.equal(trip.trips[t].status_message, 'Found route between points'); - } - }); - }); -} - -test('trip: routes Berlin with hints', function(assert) { - assert.plan(5); - var osrm = new OSRM("berlin-latest.osrm"); - var options = { - coordinates: [[52.519930,13.438640], [52.513191,13.415852]], - printInstructions: false - }; - osrm.trip(options, function(err, first) { - assert.ifError(err); - for (t = 0; t < first.trips.length; t++) { - assert.equal(first.trips[t].status_message, 'Found route between points'); - var checksum = first.trips[t].hint_data.checksum; - options.checksum = checksum; - assert.equal("number", typeof(checksum)); - - options.hints = []; - options.hints.length = options.coordinates.length; - - for (p = 0; p < first.trips[t].permutation.length; p++) { - options.hints[first.trips[t].permutation[p]] = first.trips[t].hint_data.locations[p]; - } - } - - 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-latest.osrm"); - var options = { - coordinates: [[52.519930,13.438640], [52.513191,13.415852]] - }; - osrm.trip(options, function(err, trip) { - assert.ifError(err); - for (t = 0; t < trip.trips.length; t++) { - assert.equal('string', typeof trip.trips[t].route_geometry); - } - }); -}); - -test('trip: trip through Berlin without geometry compression', function(assert) { - assert.plan(2); - var osrm = new OSRM("berlin-latest.osrm"); - var options = { - coordinates: [[52.519930,13.438640], [52.513191,13.415852]], - compression: false - }; - osrm.trip(options, function(err, trip) { - assert.ifError(err); - for (t = 0; t < trip.trips.length; t++) { - assert.ok(Array.isArray(trip.trips[t].route_geometry), "Geometry is Array"); - } - }); -}); - -test('trip: trip through Berlin with options', function(assert) { - assert.plan(5); - var osrm = new OSRM("berlin-latest.osrm"); - var options = { - coordinates: [[52.519930,13.438640], [52.513191,13.415852]], - zoomLevel: 17, - printInstructions: false, - geometry: false - }; - osrm.trip(options, function(err, trip) { - assert.ifError(err); - for (t = 0; t < trip.trips.length; t++) { - assert.equal(trip.trips[t].status_message, 'Found route between points'); - assert.equal(undefined, trip.trips[t].route_instructions); - assert.equal(undefined, trip.trips[t].alternative_geometries); - assert.equal(undefined, trip.trips[t].route_geometry); - } - }); -}); - -test('trip: routes Berlin with null hints', function(assert) { - assert.plan(1); - var osrm = new OSRM("berlin-latest.osrm"); - var options = { - coordinates: [[52.519930,13.438640], [52.513191,13.415852]], - printInstructions: false, - hints: ['', '', ''] - }; - osrm.trip(options, function(err, second) { - assert.ifError(err); - }); -}); - -/////////////////////////////// TABLE TESTS //////////////////////////////////// - -test('table: distance table in Berlin', function(assert) { - assert.plan(9); - var osrm = new OSRM("berlin-latest.osrm"); - var options = { - coordinates: [[52.519930,13.438640], [52.513191,13.415852]] - }; - osrm.table(options, function(err, table) { - assert.ifError(err); - assert.ok(Array.isArray(table.distance_table), 'result must be an array'); - var row_count = table.distance_table.length; - for (var i = 0; i < row_count; ++i) { - var column = table.distance_table[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: throws on invalid arguments', function(assert) { - assert.plan(5); - var osrm = new OSRM("berlin-latest.osrm"); - 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 (lat/long) pairs"); - options.coordinates = [[52.542648,13.393252]]; - assert.throws(function() { osrm.table(options, function(err, response) {}) }, - /at least two coordinates must be provided/); - options.coordinates = [52.542648,13.393252]; - assert.throws(function() { osrm.table(options, function(err, response) {}) }, - "coordinates must be an array of (lat/long) pairs"); - options.coordinates = [[52.542648],[13.393252]]; - assert.throws(function() { osrm.table(options, function(err, response) {}) }, - "coordinates must be an array of (lat/long) pairs"); -}); - -test('table: throws on invalid arguments', function(assert) { - assert.plan(1); - var osrm = new OSRM("berlin-latest.osrm"); - assert.throws(function() { osrm.table(null, function() {}); }, - /first arg must be an object/); -}); - -/////////////////////////////// MATCH TESTS //////////////////////////////////// - -test('match: match in Berlin', function(assert) { - assert.plan(2); - var osrm = new OSRM("berlin-latest.osrm"); - var options = { - coordinates: [[52.542648,13.393252], [52.543079,13.394780], [52.542107,13.397389]], - timestamps: [1424684612, 1424684616, 1424684620] - }; - osrm.match(options, function(err, response) { - assert.ifError(err); - assert.equal(response.matchings.length, 1); - }); -}); - -test('match: match in Berlin without timestamps', function(assert) { - assert.plan(2); - var osrm = new OSRM("berlin-latest.osrm"); - var options = { - coordinates: [[52.542648,13.393252], [52.543079,13.394780], [52.542107,13.397389]] - }; - osrm.match(options, function(err, response) { - assert.ifError(err); - assert.equal(response.matchings.length, 1); - }); -}); - -test('match: match in Berlin with geometry compression', function(assert) { - assert.plan(3); - var osrm = new OSRM("berlin-latest.osrm"); - var options = { - coordinates: [[52.542648,13.393252], [52.543079,13.394780], [52.542107,13.397389]] - }; - 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 without geometry compression', function(assert) { - assert.plan(3); - var osrm = new OSRM("berlin-latest.osrm"); - var options = { - coordinates: [[52.542648,13.393252], [52.543079,13.394780], [52.542107,13.397389]], - compression: false - }; - osrm.match(options, function(err, response) { - assert.ifError(err); - assert.equal(response.matchings.length, 1); - assert.ok(response.matchings[0].geometry instanceof Array); - }); -}); - -test('match: match in Berlin with all options', function(assert) { - assert.plan(4); - var osrm = new OSRM("berlin-latest.osrm"); - var options = { - coordinates: [[52.542648,13.393252], [52.543079,13.394780], [52.542107,13.397389]], - timestamps: [1424684612, 1424684616, 1424684620], - classify: true, - gps_precision: 4.07, - matching_beta: 10.0, - geometry: false - }; - osrm.match(options, function(err, response) { - assert.ifError(err); - assert.equal(response.matchings.length, 1); - assert.equal(response.matchings[0].confidence > 0, true); - assert.equal(undefined, response.matchings[0].geometry); - }); -}); - -test('match: throws on missing arguments', function(assert) { - assert.plan(1); - var osrm = new OSRM("berlin-latest.osrm"); - 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-latest.osrm"); - 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-latest.osrm"); - var options = { - coordinates: '' - }; - assert.throws(function() { osrm.match(options, function(err, response) {}) }, - "coordinates must be an array of (lat/long) pairs"); - options.coordinates = [[52.542648,13.393252]]; - assert.throws(function() { osrm.match(options, function(err, response) {}) }, - /at least two coordinates must be provided/); - options.coordinates = [52.542648,13.393252]; - assert.throws(function() { osrm.match(options, function(err, response) {}) }, - "coordinates must be an array of (lat/long) pairs"); - options.coordinates = [[52.542648],[13.393252]]; - assert.throws(function() { osrm.match(options, function(err, response) {}) }, - "coordinates must be an array of (lat/long) pairs"); -}); - -test('match: throws on invalid timestamps param', function(assert) { - assert.plan(3); - var osrm = new OSRM("berlin-latest.osrm"); - var options = { - coordinates: [[52.542648,13.393252], [52.543079,13.394780], [52.542107,13.397389]], - 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/); -}); - -////////////////////////////// NEAREST TESTS /////////////////////////////////// - -test('nearest', function(assert) { - assert.plan(4); - var osrm = new OSRM("berlin-latest.osrm"); - osrm.nearest([52.4224, 13.333086], function(err, result) { - assert.ifError(err); - assert.equal(result.status, 0); - assert.equal(result.mapped_coordinate.length, 2); - assert.ok(result.hasOwnProperty('name')); - }); -}); - -test('nearest: throws on invalid args', function(assert) { - assert.plan(3); - var osrm = new OSRM("berlin-latest.osrm"); - var options = {}; - assert.throws(function() { osrm.nearest(options); }, - /two arguments required/); - assert.throws(function() { osrm.nearest(null, function(err, res) {}); }, - /first argument must be an array of lat, long/); - options.coordinates = [52.4224]; - assert.throws(function() { osrm.nearest(options, function(err, res) {}); }, - /first argument must be an array of lat, long/); -}); diff --git a/test/route.js b/test/route.js new file mode 100644 index 0000000..0f97e57 --- /dev/null +++ b/test/route.js @@ -0,0 +1,151 @@ +var OSRM = require('../'); +var test = require('tape'); +var berlin_path = "test/data/berlin-latest.osrm"; + +test('route: routes Berlin', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + osrm.route({coordinates: [[52.519930,13.438640], [52.513191,13.415852]]}, function(err, route) { + assert.ifError(err); + assert.ok(route.route_summary); + }); +}); + +test('route: throws with too few or invalid args', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + assert.throws(function() { osrm.route({coordinates: [[52.519930,13.438640], [52.513191,13.415852]]}) }, + /two arguments required/); + assert.throws(function() { osrm.route(null, function(err, route) {}) }, + /first arg must be an object/); +}); + +test('route: throws with bad params', function(assert) { + assert.plan(7); + 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 (lat/long) pairs"); + assert.throws(function() { osrm.route({coordinates: [52.519930, 13.438640]}, function(err, route) {}) }, + "coordinates must be an array of (lat/long) pairs"); + assert.throws(function() { osrm.route({coordinates: [[52.519930], [13.438640]]}, function(err, route) {}) }, + "coordinates must be an array of (lat/long) pairs"); + assert.throws(function() { osrm.route({coordinates: [[52.519930,13.438640], [52.513191,13.415852]], hints: null}, function(err, route) {}) }, + "hints must be an array of strings/null"); + var options = { + coordinates: [[52.519930,13.438640], [52.513191,13.415852]], + alternateRoute: false, + printInstructions: false, + hints: [[52.519930,13.438640]] + }; + assert.throws(function() { osrm.route(options, function(err, route) {}) }, + /hint must be null or string/); +}); + +test('route: takes jsonp parameter', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + osrm.route({coordinates: [[52.519930,13.438640], [52.513191,13.415852]], jsonpParameter: 'function'}, function(err, route) { + assert.ifError(err); + assert.ok(route.route_summary); + }); +}); + +if (process.platform === 'darwin') { + // shared memory does not work on Mac OS for now. + test.skip('route: routes Berlin using shared memory', function(assert) {}); +} else { + test('route: routes Berlin using shared memory', function(assert) { + assert.plan(2); + var osrm = new OSRM(); + osrm.route({coordinates: [[52.519930,13.438640], [52.513191,13.415852]]}, function(err, route) { + assert.ifError(err); + assert.ok(route.route_summary); + }); + }); +} + +test('route: routes Berlin with geometry compression', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.519930,13.438640], [52.513191,13.415852]], + }; + osrm.route(options, function(err, route) { + assert.ifError(err); + assert.equal('string', typeof route.route_geometry); + }); +}); + +test('route: routes Berlin without geometry compression', function(assert) { + assert.plan(3); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.519930,13.438640], [52.513191,13.415852]], + compression: false + }; + osrm.route(options, function(err, route) { + assert.ifError(err); + assert.ok(route.route_summary); + assert.ok(Array.isArray(route.route_geometry)); + }); +}); + +test('route: routes Berlin with options', function(assert) { + assert.plan(5); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.519930,13.438640], [52.513191,13.415852]], + zoomLevel: 17, + alternateRoute: false, + printInstructions: false, + geometry: false + }; + osrm.route(options, function(err, route) { + assert.ifError(err); + assert.ok(route.route_summary); + assert.notOk(route.route_instructions); + assert.notOk(route.alternative_geometries); + assert.notOk(route.route_geometry); + }); +}); + +test('route: routes Berlin with hints', function(assert) { + assert.plan(5); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.519930,13.438640], [52.513191,13.415852]], + alternateRoute: false, + printInstructions: false + }; + osrm.route(options, function(err, first) { + assert.ifError(err); + assert.ok(first.route_summary); + var checksum = first.hint_data.checksum; + assert.equal("number", typeof(checksum)); + + options.hints = first.hint_data.locations; + options.checksum = checksum; + + 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: [[52.519930,13.438640], [52.513191,13.415852]], + alternateRoute: false, + printInstructions: false, + hints: ['', '', ''] + }; + osrm.route(options, function(err, second) { + assert.ifError(err); + }); +}); diff --git a/test/table.js b/test/table.js new file mode 100644 index 0000000..c62a2c1 --- /dev/null +++ b/test/table.js @@ -0,0 +1,59 @@ +var OSRM = require('../'); +var test = require('tape'); +var berlin_path = "test/data/berlin-latest.osrm"; + +test('table: distance table in Berlin', function(assert) { + assert.plan(9); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.519930,13.438640], [52.513191,13.415852]] + }; + osrm.table(options, function(err, table) { + assert.ifError(err); + assert.ok(Array.isArray(table.distance_table), 'result must be an array'); + var row_count = table.distance_table.length; + for (var i = 0; i < row_count; ++i) { + var column = table.distance_table[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: throws on invalid arguments', function(assert) { + assert.plan(5); + 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 (lat/long) pairs"); + options.coordinates = [[52.542648,13.393252]]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /at least two coordinates must be provided/); + options.coordinates = [52.542648,13.393252]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + "coordinates must be an array of (lat/long) pairs"); + options.coordinates = [[52.542648],[13.393252]]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + "coordinates must be an array of (lat/long) pairs"); +}); + +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/); +}); + diff --git a/test/trip.js b/test/trip.js new file mode 100644 index 0000000..85230ed --- /dev/null +++ b/test/trip.js @@ -0,0 +1,166 @@ +var OSRM = require('../'); +var test = require('tape'); +var berlin_path = "test/data/berlin-latest.osrm"; + +test('trip: trip in Berlin', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + osrm.trip({coordinates: [[52.51663871100423,13.36761474609375],[52.506191342034576,13.374481201171875]]}, function(err, trip) { + assert.ifError(err); + for (t = 0; t < trip.trips.length; t++) { + assert.ok(trip.trips[t].route_summary); + } + }); +}); + +test('trip: trip with many locations in Berlin', function(assert) { + assert.plan(3); + var osrm = new OSRM(berlin_path); + osrm.trip({coordinates: [[52.51663871100423,13.36761474609375],[52.506191342034576,13.374481201171875],[52.50535544522142,13.404693603515625],[52.50159371284434,13.388900756835938],[52.518727886767266,13.386840820312498],[52.528754547664185,13.4088134765625],[52.51705655410405,13.41156005859375],[52.512042174642346,13.420486450195312],[52.50368360390624,13.413619995117188],[52.504101570196205,13.36212158203125],[52.52248815280757,13.35113525390625],[52.53460237630516,13.36761474609375],[52.53710835019913,13.383407592773438],[52.536690697815736,13.392333984375],[52.532931647583325,13.42529296875],[52.52415927884915,13.399200439453125],[52.51956352925745,13.390960693359375],[52.533349335723294,13.375167846679688],[52.520399155853454,13.37860107421875],[52.52081696319122,13.355255126953125],[52.5143405029259,13.385467529296875],[52.513086884218325,13.398857116699219],[52.50744515744915,13.399200439453125],[52.49783165855699,13.409500122070312],[52.500339730516934,13.424949645996094],[52.50786308797268,13.440055847167969],[52.511624283857785,13.428382873535156],[52.50451953251202,13.437652587890625],[52.5199813445422,13.443145751953125],[52.52520370034151,13.431129455566406],[52.52896341209634,13.418426513671875],[52.517474393230245,13.429069519042969],[52.528127948407935,13.418083190917969],[52.52833681581998,13.405036926269531],[52.53084314728766,13.384437561035156],[52.53084314728766,13.374481201171875],[52.532305107923925,13.3978271484375],[52.526039219655445,13.418769836425781],[52.51642978796417,13.441085815429688],[52.51601193890388,13.448638916015625],[52.50535544522142,13.44623565673828],[52.502638670794546,13.430442810058594],[52.520190250694526,13.358688354492188],[52.531887409851336,13.358001708984375],[52.528545682238736,13.367271423339842],[52.52958999943304,13.387870788574219],[52.53961418106945,13.406410217285156],[52.50556442091497,13.399543762207031],[52.50389258754797,13.374824523925781],[52.51099744023003,13.386154174804688],[52.49657756892365,13.40229034423828]]}, function(err, trip) { + assert.ifError(err); + for (t = 0; t < trip.trips.length; t++) { + assert.ok(trip.trips[t].route_summary); + } + }); +}); + +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: [[52.519930,13.438640], [52.513191,13.415852]]}) }, + /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(7); + 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 (lat/long) pairs"); + assert.throws(function() { osrm.trip({coordinates: [52.519930, 13.438640]}, function(err, trip) {}) }, + "coordinates must be an array of (lat/long) pairs"); + assert.throws(function() { osrm.trip({coordinates: [[52.519930], [13.438640]]}, function(err, trip) {}) }, + "coordinates must be an array of (lat/long) pairs"); + assert.throws(function() { osrm.trip({coordinates: [[52.519930,13.438640], [52.513191,13.415852]], hints: null}, function(err, trip) {}) }, + "hints must be an array of strings/null"); + var options = { + coordinates: [[52.519930,13.438640], [52.513191,13.415852]], + printInstructions: false, + hints: [[52.519930,13.438640]] + }; + assert.throws(function() { osrm.trip(options, function(err, trip) {}) }, + /hint must be null or string/); +}); + +if (process.platform === 'darwin') { + // shared memory does not work on Mac OS for now. + test.skip('trip: routes Berlin using shared memory', function(assert) {}); +} else { + test('trip: routes Berlin using shared memory', function(assert) { + assert.plan(2); + var osrm = new OSRM(); + osrm.trip({coordinates: [[52.519930,13.438640], [52.513191,13.415852]]}, function(err, trip) { + assert.ifError(err); + for (t = 0; t < trip.trips.length; t++) { + assert.ok(trip.trips[t].route_summary); + } + }); + }); +} + +test('trip: routes Berlin with hints', function(assert) { + assert.plan(5); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.519930,13.438640], [52.513191,13.415852]], + printInstructions: false + }; + osrm.trip(options, function(err, first) { + assert.ifError(err); + for (t = 0; t < first.trips.length; t++) { + assert.ok(first.trips[t].route_summary); + var checksum = first.trips[t].hint_data.checksum; + options.checksum = checksum; + assert.equal("number", typeof(checksum)); + + options.hints = []; + options.hints.length = options.coordinates.length; + + for (p = 0; p < first.trips[t].permutation.length; p++) { + options.hints[first.trips[t].permutation[p]] = first.trips[t].hint_data.locations[p]; + } + } + + 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: [[52.519930,13.438640], [52.513191,13.415852]] + }; + osrm.trip(options, function(err, trip) { + assert.ifError(err); + for (t = 0; t < trip.trips.length; t++) { + assert.equal('string', typeof trip.trips[t].route_geometry); + } + }); +}); + +test('trip: trip through Berlin without geometry compression', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.519930,13.438640], [52.513191,13.415852]], + compression: false + }; + osrm.trip(options, function(err, trip) { + assert.ifError(err); + for (t = 0; t < trip.trips.length; t++) { + assert.ok(Array.isArray(trip.trips[t].route_geometry), "Geometry is Array"); + } + }); +}); + +test('trip: trip through Berlin with options', function(assert) { + assert.plan(5); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.519930,13.438640], [52.513191,13.415852]], + zoomLevel: 17, + printInstructions: false, + geometry: false + }; + osrm.trip(options, function(err, trip) { + assert.ifError(err); + for (t = 0; t < trip.trips.length; t++) { + assert.ok(trip.trips[t]); + assert.notOk(trip.trips[t].route_instructions); + assert.notOk(trip.trips[t].alternative_geometries); + assert.notOk(trip.trips[t].route_geometry); + } + }); +}); + +test('trip: routes Berlin with null hints', function(assert) { + assert.plan(1); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.519930,13.438640], [52.513191,13.415852]], + printInstructions: false, + hints: ['', '', ''] + }; + osrm.trip(options, function(err, second) { + assert.ifError(err); + }); +}); + From 54f56149b8a92bbb67f7ab7777ff85d3462cbbca Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Tue, 22 Dec 2015 19:53:36 +0100 Subject: [PATCH 19/25] Update API documentation --- API.md | 82 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/API.md b/API.md index 19836a8..f147a85 100644 --- a/API.md +++ b/API.md @@ -6,41 +6,21 @@ Creates a new `osrm` instance * `options` **`Object`** An object containing osrm options. * `options.path` **`String`** Path to the [.osrm preprocessed file](https://github.com/Project-OSRM/osrm-backend/wiki/Running-OSRM#creating-the-hierarchy). If `path` is the the only option, it can be used directly as a string. - * `options.shared_memory` **`[Boolean]`** Allows you to share data among a number of processes and the shared memory used is persistent. It stays in the system until it is explicitly removed. - * `options.distance_table` **`[Number]`** The maximum number of locations in the distance table. + * `options.shared_memory` **`[Boolean]`** Allows you to share data among a number of processes and the shared memory used is persistent. It stays in the system until it is explicitly removed. Use `osrm-datastore` to load new data into memory. Conflicts with `path`. ### Examples ```js var OSRM = require('osrm'); -var osrm = new OSRM('berlin-latest.osrm'); -``` - -Returns `Object` The osrm instance. - - - - -## `osrm.locate` - -Returns coordinate snapped to nearest node - -### Parameters - -* `point` **`Array`** Latitude, longitude pair to locate on the network. - - -### Examples -```js var osrm = new OSRM('berlin-latest.osrm'); -osrm.locate([52.4224, 13.333086], function(err, result) { - if(err) throw err; -}); +var osrm = new OSRM({shared_memory: true}); +var osrm = new OSRM(); // same as above +var osrm = new OSRM({path: 'berlin-latest.osrm', shared_memory: true}); // Error only can use either ``` -Returns node Location of the nearest node as a latitude, longitude pair. +Returns `Object` The osrm instance. ## `osrm.match` @@ -51,8 +31,8 @@ Matches given coordinates to the road network * `coordinates` **`Array>`** The point to match as a latitude, longitude array. * `timestamps` **`Array`** An array of UNIX style timestamps corresponding to the input coordinates (eg: 1424684612). * `classify` **`[Boolean]`** Return a confidence value for this matching. (optional, default `false`) -* `gps_precision` **`[Number]`** Specify gps precision as standart deviation in meters. (optional, default `-1`) -* `matching_beta` **`[Number]`** Specify beta value for matching algorithm. (optional, default `-1`) +* `gps_precision` **`[Number]`** Specify gps precision as standart deviation in meters. (optional, default `5`) +* `matching_beta` **`[Number]`** Specify beta value for matching algorithm. (optional, default `5`) * `geometry` **`[Boolean]`** Return the route geometry. (optional, default `true`) * `compression` **`[Boolean]`** Compress route geometry as a polyline; geometry is an array of [lat, lng] pairs if false. (optional, default `true`) @@ -70,7 +50,12 @@ osrm.match(options, function(err, response) { }); ``` -Returns matchings Array of MatchResults, each containing an object for a partial sub-matching of the trace. +## Errors + +* `No route found` No route between the given coordinates could be found. +* `Could not find a matching segment for coordinate` There is not street segment that matches the filters provided. + +Returns `Object` containing `matchings` Array of `MatchResult` ## `osrm.nearest` @@ -81,7 +66,16 @@ Computes the nearest street segment for a given coordinate. * `point` **`Array`** coordinates of the query point as a latitude, longitude array +### Examples + +```js +var osrm = new OSRM('berlin-latest.osrm'); +osrm.nearest([52.542648,13.393252], function(err, response) { + if(err) throw err; +}); +``` +Returns `NearestResult` object ## `osrm.route` @@ -118,25 +112,42 @@ Computes distance tables for the given via points. Currently all pair-wise dista ### Parameters * `coordinates` **`Array>`** Array of coordinate pairs as latitude, longitude representing the via points to be computed. +* `sources` **`Array>`** Array of coordinate pairs as latitude, longitude representing the via points to be computed. Conflicts with `coordinates`. +* `destinations` **`Array>`** Array of coordinate pairs as latitude, longitude representing the via points to be computed. Conflicts with `coordinates`. ### Examples +Computes a 2x2 matrix. + ```js var osrm = new OSRM("berlin-latest.osrm"); var options = { coordinates: [[52.519930,13.438640], [52.513191,13.415852]] -}; +}; +osrm.table(options, function(err, table) { + if(err) throw err +}); +``` + +Computes a 1x2 matrix. + +```js +var osrm = new OSRM("berlin-latest.osrm"); +var options = { + sources: [[52.519930,13.438640]], + destinations: [[52.519930,13.438640], [52.513191,13.415852]] +}; osrm.table(options, function(err, table) { if(err) throw err }); ``` -Returns `TableResult` +Returns `TableResult` object. ## `osrm.trip` -Calculates a short roundtrip that visits every given coordinates +Calculates a short roundtrip that visits every given coordinate. ### Parameters @@ -166,6 +177,7 @@ Returns `TripResult`. | name | type | description | | ---- | ---- | ----------- | | `matched_points` | `Array>` | Coordinates the points snapped to the road network as latitude, longitude pairs. | +| `matched_names` | `Array` | Name of the street the points snapped to. | | `indices` | `Array` | Array that gives the indices of the matched coordinates in the original trace. | | `geometry` | `String` | Geometry of the matched trace in the road network, compressed as a [polyline](https://github.com/mapbox/polyline) with 6 decimals of precision. | | `confidence` | `Number` | Value between 0 and 1, where 1 is very confident. Please note that the correctness of this value depends highly on [the assumptions about the sample rate](https://github.com/Project-OSRM/osrm-backend/wiki/Server-api#service-match). | @@ -179,7 +191,6 @@ Returns `TripResult`. | name | type | description | | ---- | ---- | ----------- | -| `status` | `Number` | 0 if passed, undefined if failed. | | `mapped_coordinate` | `Array` | Array that contains the latitude, longitude pair for the snapped coordinate. | | `name` | `String` | Name of the street the coordinate snapped to. | @@ -193,8 +204,6 @@ Returns `TripResult`. | name | type | description | | ---- | ---- | ----------- | -| `status` | `Number` | 0 if passed, undefined if failed. | -| `status_message` | `String` | Information about the query results. | | `via_indices` | `Array` | Array of node indices corresponding to the via coordinates. | | `route_geometry` | `String` | Geometry of the suggested route, compressed as a [polyline](https://github.com/mapbox/polyline). | | `route_summary` | `Object` | Object literal containing an overview of the suggested route. | @@ -206,8 +215,9 @@ Returns `TripResult`. | `found_alternative` | `Boolean` | Value will be `true` if an alternitive route was requested and found. Set options.alternateRoute to `true` to attempt to find an alternate route. | | `route_name` | `Array` | An array of the most prominent street names along the route. | | `hint_data` | `Object` | Object literal containing necessary data for speeding up similar queries. | -| `hint_data.locations` | `Array` | An array of [polyline](https://github.com/mapbox/polyline) strings used for incremental hinting. | +| `hint_data.locations` | `Array` | An array of Base64 strings used for incremental hinting. | | `hint_data.checksum` | `Number` | [Checksum](https://en.wikipedia.org/wiki/Checksum) of the network dataset. | +| `route_instructions` | `Array** | Array of route instructions. | @@ -219,5 +229,7 @@ Returns `TripResult`. | name | type | description | | ---- | ---- | ----------- | | `distance_table` | `Array>` | Array of arrays that stores the matrix in [row-major order](https://en.wikipedia.org/wiki/Row-major_order). `distance_table[i][j]` gives the travel time from the i-th via to the j-th via point. Values are given in 10th of a second. | +| `source_coodinates` | `Array>` | Array of `[lat, lon]` pairs representing the snapped coordinate of the source for each row. | +| `destination_coodinates` | `Array>` | Array of `[lat, lon]` pairs representing the snapped coordinate of the destination for each column. | From 5ef7850fd29e038c65619df264851a801801c32c Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Wed, 23 Dec 2015 04:52:04 +0100 Subject: [PATCH 20/25] Add bearings parameter --- API.md | 26 +++++++++++++----------- src/node_osrm.cpp | 50 +++++++++++++++++++++++++++++++++++++++++++++-- test/route.js | 41 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 13 deletions(-) diff --git a/API.md b/API.md index f147a85..09a27f5 100644 --- a/API.md +++ b/API.md @@ -28,14 +28,15 @@ Matches given coordinates to the road network ### Parameters -* `coordinates` **`Array>`** The point to match as a latitude, longitude array. -* `timestamps` **`Array`** An array of UNIX style timestamps corresponding to the input coordinates (eg: 1424684612). -* `classify` **`[Boolean]`** Return a confidence value for this matching. (optional, default `false`) -* `gps_precision` **`[Number]`** Specify gps precision as standart deviation in meters. (optional, default `5`) -* `matching_beta` **`[Number]`** Specify beta value for matching algorithm. (optional, default `5`) -* `geometry` **`[Boolean]`** Return the route geometry. (optional, default `true`) -* `compression` **`[Boolean]`** Compress route geometry as a polyline; geometry is an array of [lat, lng] pairs if false. (optional, default `true`) +* `options` **`Object`** Object literal containing parameters for the match query. + * `options.coordinates` **`Array>`** The point to match as a latitude, longitude array. + * `options.bearings` **`Array`** or **`Array>`** Either list of approximate bearing values of the segments to snap to or `[bearing, range]` + * `options.timestamps` **`Array`** An array of UNIX style timestamps corresponding to the input coordinates (eg: 1424684612). + * `options.classify` **`[Boolean]`** Return a confidence value for this matching. (optional, default `false`) + * `options.gps_precision` **`[Number]`** Specify gps precision as standart deviation in meters. (optional, default `5`) + * `options.matching_beta` **`[Number]`** Specify beta value for matching algorithm. (optional, default `5`) +For other parameters see [osrm.route Parameters](#route_parameters). `trip` does not support computing alternatives. ### Examples @@ -85,6 +86,7 @@ Computes a route between coordinates over the network. * `options` **`Object`** Object literal containing parameters for the route query. * `options.coordinates` **`Array>`** Via points to route represented by an array of number arrays expressing coordinate pairs as latitude, longitude. + * `options.bearings` **`Array`** or **`Array>`** Either list of approximate bearing values of the segments to snap to or `[bearing, range]` * `options.alternateRoute` **`[Boolean]`** Return an alternate route. (optional, default `false`) * `options.checksum` **`[Number]`** [Checksum](https://en.wikipedia.org/wiki/Checksum) of the network dataset. * `options.zoomLevel` **`[Number]`** Determines the level of generalization. The default zoom 18 performs no generalization. (optional, default `18`) @@ -111,9 +113,10 @@ Computes distance tables for the given via points. Currently all pair-wise dista ### Parameters -* `coordinates` **`Array>`** Array of coordinate pairs as latitude, longitude representing the via points to be computed. -* `sources` **`Array>`** Array of coordinate pairs as latitude, longitude representing the via points to be computed. Conflicts with `coordinates`. -* `destinations` **`Array>`** Array of coordinate pairs as latitude, longitude representing the via points to be computed. Conflicts with `coordinates`. +* `options` **`Object`** Object literal containing parameters for the table query. + * `options.coordinates` **`Array>`** Array of coordinate pairs as latitude, longitude representing the via points to be computed. + * `options.sources` **`Array>`** Array of coordinate pairs as latitude, longitude representing the via points to be computed. Conflicts with `coordinates`. + * `options.destinations` **`Array>`** Array of coordinate pairs as latitude, longitude representing the via points to be computed. Conflicts with `coordinates`. ### Examples @@ -151,7 +154,8 @@ Calculates a short roundtrip that visits every given coordinate. ### Parameters -* `coordinates` **`Array>`** The point to match as a latitude, longitude array. +* `options` **`Object`** Object literal containing parameters for the trip query. +* `options.coordinates` **`Array>`** The point to match as a latitude, longitude array. For other parameters see [osrm.route Parameters](#route_parameters). `trip` does not support computing alternatives. diff --git a/src/node_osrm.cpp b/src/node_osrm.cpp index efe0ff7..ee3d178 100644 --- a/src/node_osrm.cpp +++ b/src/node_osrm.cpp @@ -186,6 +186,49 @@ route_parameters_ptr argumentsToParameter(const Nan::FunctionCallbackInfoHas(Nan::New("bearings").ToLocalChecked())) + { + v8::Local bearings = obj->Get(Nan::New("bearings").ToLocalChecked()); + auto bearings_array = v8::Local::Cast(bearings); + for (auto i = 0u; i < bearings_array->Length(); ++i) + { + v8::Local value = bearings_array->Get(i); + int bearing; + boost::optional bearing_range; + if (value->IsArray()) + { + auto bearing_and_range = v8::Local::Cast(value); + if (bearing_and_range->Length() == 2) + { + bearing = static_cast(bearing_and_range->Get(0)->NumberValue()); + bearing_range = boost::make_optional(static_cast(bearing_and_range->Get(1)->NumberValue())); + } + else + { + Nan::ThrowError("Bearing must be an array of [bearing, range]"); + return route_parameters_ptr(); + } + } + else if (value->IsNumber()) + { + bearing = static_cast(value->NumberValue()); + } + else + { + Nan::ThrowError("Bearing needs to be integer or pair of integers"); + return route_parameters_ptr(); + } + + if (bearing < 0 || bearing >= 360) + { + Nan::ThrowError("Bearing needs to be in range 0..360"); + return route_parameters_ptr(); + } + + params->bearings.emplace_back(bearing, bearing_range); + } + } + if (obj->Has(Nan::New("alternateRoute").ToLocalChecked())) { params->alternate_route = @@ -556,7 +599,8 @@ void Engine::AsyncRun(uv_work_t *req) RunQueryBaton *closure = static_cast(req->data); try { - const auto result_code = closure->machine->this_->RunQuery(*closure->params, closure->result); + const auto result_code = + closure->machine->this_->RunQuery(*closure->params, closure->result); const auto message_iter = closure->result.values.find("status_message"); const auto end_iter = closure->result.values.end(); @@ -567,7 +611,9 @@ void Engine::AsyncRun(uv_work_t *req) { if (message_iter != end_iter) { - throw std::logic_error(closure->result.values["status_message"].get().value.c_str()); + throw std::logic_error(closure->result.values["status_message"] + .get() + .value.c_str()); } else { diff --git a/test/route.js b/test/route.js index 0f97e57..f672ee3 100644 --- a/test/route.js +++ b/test/route.js @@ -112,6 +112,47 @@ test('route: routes Berlin with options', function(assert) { }); }); +test('route: integer bearing values', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.519930,13.438640], [52.513191,13.415852]], + bearings: [200, 250], + }; + osrm.route(options, function(err, route) { + assert.ifError(err); + assert.ok(route.route_summary); + }); +}); + +test('route: array bearing values', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + var options = { + coordinates: [[52.519930,13.438640], [52.513191,13.415852]], + bearings: [[200, 180], [250, 180]], + }; + osrm.route(options, function(err, route) { + assert.ifError(err); + assert.ok(route.route_summary); + }); +}); + +test('route: invalid bearing values', function(assert) { + assert.plan(2); + var osrm = new OSRM(berlin_path); + assert.throws(function() { osrm.route({ + coordinates: [[52.519930,13.438640], [52.513191,13.415852]], + bearings: [[400, 180], [-250, 180]], + }, function(err, route) {}) }, + /Bearing needs to be in range/); + assert.throws(function() { osrm.route({ + coordinates: [[52.519930,13.438640], [52.513191,13.415852]], + bearings: [[200], [250, 180]], + }, function(err, route) {}) }, + /Bearing must be an array of/); +}); + test('route: routes Berlin with hints', function(assert) { assert.plan(5); var osrm = new OSRM(berlin_path); From b14495213bcb5ca4a393e0b11025ba23e57b0cd5 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Wed, 23 Dec 2015 09:43:38 +0100 Subject: [PATCH 21/25] Add tests and fix sources and destinations parameter --- src/node_osrm.cpp | 16 +++++++++------- test/index.js | 12 ++++++++++++ test/table.js | 29 +++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/node_osrm.cpp b/src/node_osrm.cpp index ee3d178..a2c061d 100644 --- a/src/node_osrm.cpp +++ b/src/node_osrm.cpp @@ -48,6 +48,7 @@ libosrm_config_ptr argumentsToLibOSRMConfig(const Nan::FunctionCallbackInfo 1) { + Nan::ThrowError("only accepts one parameter"); return libosrm_config_ptr(); } @@ -132,7 +133,7 @@ parseCoordinateArray(const v8::Local &coordinates_array) } // Parses all the non-service specific parameters -route_parameters_ptr argumentsToParameter(const Nan::FunctionCallbackInfo &args) +route_parameters_ptr argumentsToParameter(const Nan::FunctionCallbackInfo &args, bool requires_coordinates) { Nan::HandleScope scope; auto params = make_unique(); @@ -152,7 +153,7 @@ route_parameters_ptr argumentsToParameter(const Nan::FunctionCallbackInfo obj = Nan::To(args[0]).ToLocalChecked(); v8::Local coordinates = obj->Get(Nan::New("coordinates").ToLocalChecked()); - if (coordinates->IsUndefined()) + if (coordinates->IsUndefined() && requires_coordinates) { Nan::ThrowError("must provide a coordinates property"); return route_parameters_ptr(); @@ -180,8 +181,9 @@ route_parameters_ptr argumentsToParameter(const Nan::FunctionCallbackInfoIsUndefined()) { + BOOST_ASSERT(!coordinates->IsArray()); Nan::ThrowError("coordinates must be an array of (lat/long) pairs"); return route_parameters_ptr(); } @@ -381,7 +383,7 @@ struct RunQueryBaton void Engine::route(const Nan::FunctionCallbackInfo &args) { Nan::HandleScope scope; - auto params = argumentsToParameter(args); + auto params = argumentsToParameter(args, true); if (!params) return; @@ -393,7 +395,7 @@ void Engine::route(const Nan::FunctionCallbackInfo &args) void Engine::match(const Nan::FunctionCallbackInfo &args) { Nan::HandleScope scope; - auto params = argumentsToParameter(args); + auto params = argumentsToParameter(args, true); if (!params) return; @@ -451,7 +453,7 @@ void Engine::match(const Nan::FunctionCallbackInfo &args) void Engine::trip(const Nan::FunctionCallbackInfo &args) { Nan::HandleScope scope; - auto params = argumentsToParameter(args); + auto params = argumentsToParameter(args, true); if (!params) return; @@ -463,7 +465,7 @@ void Engine::trip(const Nan::FunctionCallbackInfo &args) void Engine::table(const Nan::FunctionCallbackInfo &args) { Nan::HandleScope scope; - auto params = argumentsToParameter(args); + auto params = argumentsToParameter(args, false); if (!params) return; diff --git a/test/index.js b/test/index.js index 9621897..13da3e0 100644 --- a/test/index.js +++ b/test/index.js @@ -8,6 +8,18 @@ test('constructor: throws if new keyword is not used', function(assert) { /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"); }, diff --git a/test/table.js b/test/table.js index c62a2c1..43b7011 100644 --- a/test/table.js +++ b/test/table.js @@ -30,6 +30,35 @@ test('table: distance table in Berlin', function(assert) { }); }); +test('table: distance table in Berlin with sources/destinations', function(assert) { + assert.plan(6); + var osrm = new OSRM(berlin_path); + var options = { + sources: [[52.519930,13.438640]], + destinations: [[52.519930,13.438640], [52.513191,13.415852]] + }; + osrm.table(options, function(err, table) { + assert.ifError(err); + assert.ok(Array.isArray(table.distance_table), 'result must be an array'); + var row_count = table.distance_table.length; + for (var i = 0; i < row_count; ++i) { + var column = table.distance_table[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(5); var osrm = new OSRM(berlin_path); From 860500c213e5b4261c17e70a5184e8f62ff7f53f Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Wed, 23 Dec 2015 09:55:47 +0100 Subject: [PATCH 22/25] Add more tests for souces/destinations --- src/node_osrm.cpp | 2 +- test/table.js | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/node_osrm.cpp b/src/node_osrm.cpp index a2c061d..2763618 100644 --- a/src/node_osrm.cpp +++ b/src/node_osrm.cpp @@ -492,7 +492,7 @@ void Engine::table(const Nan::FunctionCallbackInfo &args) if (!destinations->IsUndefined() && !sources->IsUndefined() && params->coordinates.size() > 0) { - Nan::ThrowError("You can either specifiy sources and destinations, or coordinates"); + Nan::ThrowError("You can either specify sources and destinations, or coordinates"); return; } diff --git a/test/table.js b/test/table.js index 43b7011..1b630b9 100644 --- a/test/table.js +++ b/test/table.js @@ -60,7 +60,7 @@ test('table: distance table in Berlin with sources/destinations', function(asser }); test('table: throws on invalid arguments', function(assert) { - assert.plan(5); + assert.plan(7); var osrm = new OSRM(berlin_path); var options = {}; assert.throws(function() { osrm.table(options); }, @@ -77,6 +77,13 @@ test('table: throws on invalid arguments', function(assert) { options.coordinates = [[52.542648],[13.393252]]; assert.throws(function() { osrm.table(options, function(err, response) {}) }, "coordinates must be an array of (lat/long) pairs"); + options.coordinates = [[52.542648,13.393252], [52.542648,13.393252]]; + options.sources = [[52.542648,13.393252], [52.542648,13.393252]]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /Both sources and destinations need to be specified/); + options.destinations = [[52.542648,13.393252], [52.542648,13.393252]]; + assert.throws(function() { osrm.table(options, function(err, response) {}) }, + /You can either specify sources and destinations, or coordinates/); }); test('table: throws on invalid arguments', function(assert) { From 93a08b27b06e7e83dbefced269db88021dbd4f89 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Wed, 23 Dec 2015 10:36:38 +0100 Subject: [PATCH 23/25] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 879a12b..01be628 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ## node-osrm changelog +### 4.9.0 + - Uses OSRM 4.9.0 + - `table` now supports `sources` and `destinations` options for asymmetric queries + - `status` and `status_message` were removed. `No route found` (207) and `No matching edge segment found` (208) now throw `Error` Objects (as well as all `400` errors). + - Ported to NAN2 to support for Node 4.x and 5.x + ### 4.8.1 - Update OSRM to v4.8.1 From 4ae72c23c8e60df2e114b50ef85cedb02647e54e Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Thu, 24 Dec 2015 11:26:49 +0100 Subject: [PATCH 24/25] Don't use sharedmemory test on MacOS X --- test/index.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test/index.js b/test/index.js index 13da3e0..0965df0 100644 --- a/test/index.js +++ b/test/index.js @@ -8,11 +8,16 @@ test('constructor: throws if new keyword is not used', function(assert) { /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); -}); +if (process.platform === 'darwin') { + // shared memory does not work on Mac OS for now. + test.skip('constructor: uses defaults with no parameter', function(assert) {}); +} else { + 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); From d90215dc6da55da1235e4a776a76bfc577412076 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Thu, 24 Dec 2015 11:39:06 +0100 Subject: [PATCH 25/25] Use v4.9.0 osrm-backend tag --- .travis.yml | 2 +- bootstrap.sh | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4d3e540..f89a0ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,7 +61,7 @@ matrix: env: global: - JOBS: "3" - - OSRM_RELEASE: "develop" + - OSRM_RELEASE: "v4.9.0" - secure: KitzGZjoDblX/3heajcvssGz0JnJ/k02dr2tu03ksUV+6MogC3RSQudqyKY57+f8VyZrcllN/UOlJ0Q/3iG38Oz8DljC+7RZxtkVmE1SFBoOezKCdhcvWM12G3uqPs7hhrRxuUgIh0C//YXEkulUrqa2H1Aj2xeen4E3FAqEoy0= - secure: WLGmxl6VTVWhXGm6X83GYNYzPNsvTD+9usJOKM5YBLAdG7cnOBQBNiCCUKc9OZMMZVUr3ec2/iigakH5Y8Yc+U6AlWKzlORyqWLuk4nFuoedu62x6ocQkTkuOc7mHiYhKd21xTGMYauaZRS6kugv4xkpGES2UjI2T8cjZ+LN2jU= diff --git a/bootstrap.sh b/bootstrap.sh index 12ec790..a6d5281 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -10,7 +10,7 @@ CURRENT_DIR=$(pwd) # default to clang CXX=${CXX:-clang++} TARGET=${TARGET:-Release} -OSRM_RELEASE=${OSRM_RELEASE:-"develop"} +OSRM_RELEASE=${OSRM_RELEASE:-"v4.9.0"} OSRM_REPO=${OSRM_REPO:-"https://github.com/Project-OSRM/osrm-backend.git"} OSRM_DIR=deps/osrm-backend-${TARGET} diff --git a/package.json b/package.json index da1b275..bc9571c 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "url": "https://github.com/Project-OSRM/node-osrm", "homepage": "http://project-osrm.org", "author": "Dane Springmeyer ", - "version": "4.9.0-develop.0", + "version": "4.9.0", "main": "./lib/osrm.js", "license": "BSD", "bugs": {