This change adds support for disabling datasets, such that specific files are not loaded into memory when running OSRM. This enables users to not pay the memory cost for features they do not intend to use. Initially, there are two options: - ROUTE_GEOMETRY, for disabling overview, steps, annotations and waypoints. - ROUTE_STEPS, for disabling steps only. Attempts to query features for which the datasets are disabled will lead to a DisabledDatasetException being returned.
1762 lines
57 KiB
C++
1762 lines
57 KiB
C++
#ifndef OSRM_BINDINGS_NODE_SUPPORT_HPP
|
|
#define OSRM_BINDINGS_NODE_SUPPORT_HPP
|
|
|
|
#include "nodejs/json_v8_renderer.hpp"
|
|
#include "engine/api/flatbuffers/fbresult_generated.h"
|
|
#include "osrm/approach.hpp"
|
|
#include "osrm/bearing.hpp"
|
|
#include "osrm/coordinate.hpp"
|
|
#include "osrm/engine_config.hpp"
|
|
#include "osrm/json_container.hpp"
|
|
#include "osrm/match_parameters.hpp"
|
|
#include "osrm/nearest_parameters.hpp"
|
|
#include "osrm/osrm.hpp"
|
|
#include "osrm/route_parameters.hpp"
|
|
#include "osrm/status.hpp"
|
|
#include "osrm/storage_config.hpp"
|
|
#include "osrm/table_parameters.hpp"
|
|
#include "osrm/tile_parameters.hpp"
|
|
#include "osrm/trip_parameters.hpp"
|
|
#include "util/json_renderer.hpp"
|
|
#include <napi.h>
|
|
|
|
#include <boost/assert.hpp>
|
|
#include <boost/optional.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <exception>
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
namespace node_osrm
|
|
{
|
|
|
|
using engine_config_ptr = std::unique_ptr<osrm::EngineConfig>;
|
|
using route_parameters_ptr = std::unique_ptr<osrm::RouteParameters>;
|
|
using trip_parameters_ptr = std::unique_ptr<osrm::TripParameters>;
|
|
using tile_parameters_ptr = std::unique_ptr<osrm::TileParameters>;
|
|
using match_parameters_ptr = std::unique_ptr<osrm::MatchParameters>;
|
|
using nearest_parameters_ptr = std::unique_ptr<osrm::NearestParameters>;
|
|
using table_parameters_ptr = std::unique_ptr<osrm::TableParameters>;
|
|
|
|
struct PluginParameters
|
|
{
|
|
bool renderToBuffer = false;
|
|
};
|
|
|
|
using ObjectOrString = typename mapbox::util::variant<osrm::json::Object, std::string>;
|
|
|
|
template <typename ResultT> inline Napi::Value render(const Napi::Env &env, const ResultT &result);
|
|
|
|
template <> Napi::Value inline render(const Napi::Env &env, const std::string &result)
|
|
{
|
|
return Napi::Buffer<char>::Copy(env, result.data(), result.size());
|
|
}
|
|
|
|
template <> Napi::Value inline render(const Napi::Env &env, const ObjectOrString &result)
|
|
{
|
|
if (result.is<osrm::json::Object>())
|
|
{
|
|
// Convert osrm::json object tree into matching v8 object tree
|
|
Napi::Value value;
|
|
renderToV8(env, value, result.get<osrm::json::Object>());
|
|
return value;
|
|
}
|
|
else
|
|
{
|
|
// Return the string object as a node Buffer
|
|
return Napi::Buffer<char>::Copy(
|
|
env, result.get<std::string>().data(), result.get<std::string>().size());
|
|
}
|
|
}
|
|
|
|
inline bool IsUnsignedInteger(const Napi::Value &value)
|
|
{
|
|
if (!value.IsNumber())
|
|
{
|
|
return false;
|
|
}
|
|
const auto doubleValue = value.ToNumber().DoubleValue();
|
|
return doubleValue >= 0.0 && std::floor(doubleValue) == doubleValue;
|
|
}
|
|
|
|
inline void ParseResult(const osrm::Status &result_status, osrm::json::Object &result)
|
|
{
|
|
const auto code_iter = result.values.find("code");
|
|
const auto end_iter = result.values.end();
|
|
|
|
BOOST_ASSERT(code_iter != end_iter);
|
|
|
|
if (result_status == osrm::Status::Error)
|
|
{
|
|
throw std::logic_error(code_iter->second.get<osrm::json::String>().value.c_str());
|
|
}
|
|
|
|
result.values.erase(code_iter);
|
|
const auto message_iter = result.values.find("message");
|
|
if (message_iter != end_iter)
|
|
{
|
|
result.values.erase(message_iter);
|
|
}
|
|
}
|
|
|
|
inline void ParseResult(const osrm::Status & /*result_status*/, const std::string & /*unused*/) {}
|
|
inline void ParseResult(const osrm::Status &result_status,
|
|
const flatbuffers::FlatBufferBuilder &fbs_builder)
|
|
{
|
|
auto fbs_result = osrm::engine::api::fbresult::GetFBResult(fbs_builder.GetBufferPointer());
|
|
|
|
if (result_status == osrm::Status::Error)
|
|
{
|
|
BOOST_ASSERT(fbs_result->code());
|
|
throw std::logic_error(fbs_result->code()->message()->c_str());
|
|
}
|
|
}
|
|
|
|
inline void ThrowError(const Napi::Env &env, const char *message)
|
|
{
|
|
Napi::Error::New(env, message).ThrowAsJavaScriptException();
|
|
}
|
|
|
|
inline void ThrowTypeError(const Napi::Env &env, const char *message)
|
|
{
|
|
Napi::TypeError::New(env, message).ThrowAsJavaScriptException();
|
|
}
|
|
|
|
inline engine_config_ptr argumentsToEngineConfig(const Napi::CallbackInfo &args)
|
|
{
|
|
Napi::HandleScope scope(args.Env());
|
|
auto engine_config = std::make_unique<osrm::EngineConfig>();
|
|
|
|
if (args.Length() == 0)
|
|
{
|
|
return engine_config;
|
|
}
|
|
else if (args.Length() > 1)
|
|
{
|
|
ThrowError(args.Env(), "Only accepts one parameter");
|
|
return engine_config_ptr();
|
|
}
|
|
|
|
BOOST_ASSERT(args.Length() == 1);
|
|
|
|
if (args[0].IsString())
|
|
{
|
|
engine_config->storage_config = osrm::StorageConfig(args[0].ToString().Utf8Value());
|
|
engine_config->use_shared_memory = false;
|
|
return engine_config;
|
|
}
|
|
else if (!args[0].IsObject())
|
|
{
|
|
ThrowError(args.Env(), "Parameter must be a path or options object");
|
|
return engine_config_ptr();
|
|
}
|
|
|
|
BOOST_ASSERT(args[0].IsObject());
|
|
auto params = args[0].As<Napi::Object>();
|
|
|
|
auto path = params.Get("path");
|
|
if (path.IsEmpty())
|
|
return engine_config_ptr();
|
|
|
|
auto memory_file = params.Get("memory_file");
|
|
if (memory_file.IsEmpty())
|
|
return engine_config_ptr();
|
|
|
|
auto shared_memory = params.Get("shared_memory");
|
|
if (shared_memory.IsEmpty())
|
|
return engine_config_ptr();
|
|
|
|
auto mmap_memory = params.Get("mmap_memory");
|
|
if (mmap_memory.IsEmpty())
|
|
return engine_config_ptr();
|
|
|
|
if (!memory_file.IsUndefined())
|
|
{
|
|
if (path.IsUndefined())
|
|
{
|
|
ThrowError(args.Env(), "memory_file option requires a path to a file.");
|
|
return engine_config_ptr();
|
|
}
|
|
|
|
engine_config->memory_file = memory_file.ToString().Utf8Value();
|
|
}
|
|
|
|
auto dataset_name = params.Get("dataset_name");
|
|
if (dataset_name.IsEmpty())
|
|
return engine_config_ptr();
|
|
if (!dataset_name.IsUndefined())
|
|
{
|
|
if (dataset_name.IsString())
|
|
{
|
|
engine_config->dataset_name = dataset_name.ToString().Utf8Value();
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "dataset_name needs to be a string");
|
|
return engine_config_ptr();
|
|
}
|
|
}
|
|
|
|
auto disable_feature_dataset = params.Get("disable_feature_dataset");
|
|
if (disable_feature_dataset.IsArray())
|
|
{
|
|
Napi::Array datasets = disable_feature_dataset.As<Napi::Array>();
|
|
for (uint32_t i = 0; i < datasets.Length(); ++i)
|
|
{
|
|
Napi::Value dataset = datasets.Get(i);
|
|
if (!dataset.IsString())
|
|
{
|
|
ThrowError(args.Env(), "disable_feature_dataset list option must be a string");
|
|
return engine_config_ptr();
|
|
}
|
|
auto dataset_str = dataset.ToString().Utf8Value();
|
|
if (dataset_str == "ROUTE_GEOMETRY")
|
|
{
|
|
engine_config->disable_feature_dataset.push_back(
|
|
osrm::storage::FeatureDataset::ROUTE_GEOMETRY);
|
|
}
|
|
else if (dataset_str == "ROUTE_STEPS")
|
|
{
|
|
engine_config->disable_feature_dataset.push_back(
|
|
osrm::storage::FeatureDataset::ROUTE_STEPS);
|
|
}
|
|
else
|
|
{
|
|
ThrowError(
|
|
args.Env(),
|
|
"disable_feature_dataset array can include 'ROUTE_GEOMETRY', 'ROUTE_STEPS'.");
|
|
return engine_config_ptr();
|
|
}
|
|
}
|
|
}
|
|
else if (!disable_feature_dataset.IsUndefined())
|
|
{
|
|
ThrowError(args.Env(),
|
|
"disable_feature_dataset option must be an array and can include the string "
|
|
"values 'ROUTE_GEOMETRY', 'ROUTE_STEPS'.");
|
|
return engine_config_ptr();
|
|
}
|
|
|
|
if (!path.IsUndefined())
|
|
{
|
|
engine_config->storage_config = osrm::StorageConfig(path.ToString().Utf8Value(),
|
|
engine_config->disable_feature_dataset);
|
|
|
|
engine_config->use_shared_memory = false;
|
|
}
|
|
if (!shared_memory.IsUndefined())
|
|
{
|
|
if (shared_memory.IsBoolean())
|
|
{
|
|
engine_config->use_shared_memory = shared_memory.ToBoolean().Value();
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "Shared_memory option must be a boolean");
|
|
return engine_config_ptr();
|
|
}
|
|
}
|
|
if (!mmap_memory.IsUndefined())
|
|
{
|
|
if (mmap_memory.IsBoolean())
|
|
{
|
|
engine_config->use_mmap = mmap_memory.ToBoolean().Value();
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "mmap_memory option must be a boolean");
|
|
return engine_config_ptr();
|
|
}
|
|
}
|
|
|
|
if (path.IsUndefined() && !engine_config->use_shared_memory)
|
|
{
|
|
ThrowError(args.Env(),
|
|
"Shared_memory must be enabled if no path is "
|
|
"specified");
|
|
return engine_config_ptr();
|
|
}
|
|
|
|
auto algorithm = params.Get("algorithm");
|
|
if (algorithm.IsEmpty())
|
|
return engine_config_ptr();
|
|
|
|
if (algorithm.IsString())
|
|
{
|
|
auto algorithm_str = algorithm.ToString().Utf8Value();
|
|
if (algorithm_str == "CH")
|
|
{
|
|
engine_config->algorithm = osrm::EngineConfig::Algorithm::CH;
|
|
}
|
|
else if (algorithm_str == "CoreCH")
|
|
{
|
|
engine_config->algorithm = osrm::EngineConfig::Algorithm::CH;
|
|
}
|
|
else if (algorithm_str == "MLD")
|
|
{
|
|
engine_config->algorithm = osrm::EngineConfig::Algorithm::MLD;
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "algorithm option must be one of 'CH', 'CoreCH', or 'MLD'.");
|
|
return engine_config_ptr();
|
|
}
|
|
}
|
|
else if (!algorithm.IsUndefined())
|
|
{
|
|
ThrowError(args.Env(),
|
|
"algorithm option must be a string and one of 'CH', 'CoreCH', or 'MLD'.");
|
|
return engine_config_ptr();
|
|
}
|
|
|
|
// Set EngineConfig system-wide limits on construction, if requested
|
|
|
|
auto max_locations_trip = params.Get("max_locations_trip");
|
|
auto max_locations_viaroute = params.Get("max_locations_viaroute");
|
|
auto max_locations_distance_table = params.Get("max_locations_distance_table");
|
|
auto max_locations_map_matching = params.Get("max_locations_map_matching");
|
|
auto max_results_nearest = params.Get("max_results_nearest");
|
|
auto max_alternatives = params.Get("max_alternatives");
|
|
auto max_radius_map_matching = params.Get("max_radius_map_matching");
|
|
auto default_radius = params.Get("default_radius");
|
|
|
|
if (!max_locations_trip.IsUndefined() && !max_locations_trip.IsNumber())
|
|
{
|
|
ThrowError(args.Env(), "max_locations_trip must be an integral number");
|
|
return engine_config_ptr();
|
|
}
|
|
if (!max_locations_viaroute.IsUndefined() && !max_locations_viaroute.IsNumber())
|
|
{
|
|
ThrowError(args.Env(), "max_locations_viaroute must be an integral number");
|
|
return engine_config_ptr();
|
|
}
|
|
if (!max_locations_distance_table.IsUndefined() && !max_locations_distance_table.IsNumber())
|
|
{
|
|
ThrowError(args.Env(), "max_locations_distance_table must be an integral number");
|
|
return engine_config_ptr();
|
|
}
|
|
if (!max_locations_map_matching.IsUndefined() && !max_locations_map_matching.IsNumber())
|
|
{
|
|
ThrowError(args.Env(), "max_locations_map_matching must be an integral number");
|
|
return engine_config_ptr();
|
|
}
|
|
if (!max_results_nearest.IsUndefined() && !max_results_nearest.IsNumber())
|
|
{
|
|
ThrowError(args.Env(), "max_results_nearest must be an integral number");
|
|
return engine_config_ptr();
|
|
}
|
|
if (!max_alternatives.IsUndefined() && !max_alternatives.IsNumber())
|
|
{
|
|
ThrowError(args.Env(), "max_alternatives must be an integral number");
|
|
return engine_config_ptr();
|
|
}
|
|
if (!max_radius_map_matching.IsUndefined() && max_radius_map_matching.IsString() &&
|
|
max_radius_map_matching.ToString().Utf8Value() != "unlimited")
|
|
{
|
|
ThrowError(args.Env(), "max_radius_map_matching must be unlimited or an integral number");
|
|
return engine_config_ptr();
|
|
}
|
|
if (!default_radius.IsUndefined() && default_radius.IsString() &&
|
|
default_radius.ToString().Utf8Value() != "unlimited")
|
|
{
|
|
ThrowError(args.Env(), "default_radius must be unlimited or an integral number");
|
|
return engine_config_ptr();
|
|
}
|
|
|
|
if (max_locations_trip.IsNumber())
|
|
engine_config->max_locations_trip = max_locations_trip.ToNumber().Int32Value();
|
|
if (max_locations_viaroute.IsNumber())
|
|
engine_config->max_locations_viaroute = max_locations_viaroute.ToNumber().Int32Value();
|
|
if (max_locations_distance_table.IsNumber())
|
|
engine_config->max_locations_distance_table =
|
|
max_locations_distance_table.ToNumber().Int32Value();
|
|
if (max_locations_map_matching.IsNumber())
|
|
engine_config->max_locations_map_matching =
|
|
max_locations_map_matching.ToNumber().Int32Value();
|
|
if (max_results_nearest.IsNumber())
|
|
engine_config->max_results_nearest = max_results_nearest.ToNumber().Int32Value();
|
|
if (max_alternatives.IsNumber())
|
|
engine_config->max_alternatives = max_alternatives.ToNumber().Int32Value();
|
|
|
|
if (max_radius_map_matching.IsNumber())
|
|
engine_config->max_radius_map_matching = max_radius_map_matching.ToNumber().DoubleValue();
|
|
else if (max_radius_map_matching.IsString() &&
|
|
max_radius_map_matching.ToString().Utf8Value() == "unlimited")
|
|
engine_config->max_radius_map_matching = -1.0;
|
|
|
|
if (default_radius.IsNumber())
|
|
engine_config->default_radius = default_radius.ToNumber().DoubleValue();
|
|
else if (default_radius.IsString() && default_radius.ToString().Utf8Value() == "unlimited")
|
|
engine_config->default_radius = -1.0;
|
|
|
|
return engine_config;
|
|
}
|
|
|
|
inline boost::optional<std::vector<osrm::Coordinate>>
|
|
parseCoordinateArray(const Napi::Array &coordinates_array)
|
|
{
|
|
Napi::HandleScope scope(coordinates_array.Env());
|
|
boost::optional<std::vector<osrm::Coordinate>> resulting_coordinates;
|
|
std::vector<osrm::Coordinate> temp_coordinates;
|
|
|
|
for (uint32_t i = 0; i < coordinates_array.Length(); ++i)
|
|
{
|
|
Napi::Value coordinate = coordinates_array.Get(i);
|
|
if (coordinate.IsEmpty())
|
|
return resulting_coordinates;
|
|
|
|
if (!coordinate.IsArray())
|
|
{
|
|
ThrowError(coordinates_array.Env(), "Coordinates must be an array of (lon/lat) pairs");
|
|
return resulting_coordinates;
|
|
}
|
|
|
|
Napi::Array coordinate_pair = coordinate.As<Napi::Array>();
|
|
if (coordinate_pair.Length() != 2)
|
|
{
|
|
ThrowError(coordinates_array.Env(), "Coordinates must be an array of (lon/lat) pairs");
|
|
return resulting_coordinates;
|
|
}
|
|
|
|
if (!coordinate_pair.Get(static_cast<uint32_t>(0)).IsNumber() ||
|
|
!coordinate_pair.Get(static_cast<uint32_t>(1)).IsNumber())
|
|
{
|
|
ThrowError(coordinates_array.Env(),
|
|
"Each member of a coordinate pair must be a number");
|
|
return resulting_coordinates;
|
|
}
|
|
|
|
double lon = coordinate_pair.Get(static_cast<uint32_t>(0)).As<Napi::Number>().DoubleValue();
|
|
double lat = coordinate_pair.Get(static_cast<uint32_t>(1)).As<Napi::Number>().DoubleValue();
|
|
|
|
if (std::isnan(lon) || std::isnan(lat) || std::isinf(lon) || std::isinf(lat))
|
|
{
|
|
ThrowError(coordinates_array.Env(), "Lng/Lat coordinates must be valid numbers");
|
|
return resulting_coordinates;
|
|
}
|
|
|
|
if (lon > 180 || lon < -180 || lat > 90 || lat < -90)
|
|
{
|
|
ThrowError(coordinates_array.Env(),
|
|
"Lng/Lat coordinates must be within world bounds "
|
|
"(-180 < lng < 180, -90 < lat < 90)");
|
|
return resulting_coordinates;
|
|
}
|
|
|
|
temp_coordinates.emplace_back(osrm::util::FloatLongitude{std::move(lon)},
|
|
osrm::util::FloatLatitude{std::move(lat)});
|
|
}
|
|
|
|
resulting_coordinates = boost::make_optional(std::move(temp_coordinates));
|
|
return resulting_coordinates;
|
|
}
|
|
|
|
// Parses all the non-service specific parameters
|
|
template <typename ParamType>
|
|
inline bool argumentsToParameter(const Napi::CallbackInfo &args,
|
|
ParamType ¶ms,
|
|
bool requires_multiple_coordinates)
|
|
{
|
|
Napi::HandleScope scope(args.Env());
|
|
|
|
if (args.Length() < 2)
|
|
{
|
|
ThrowTypeError(args.Env(), "Two arguments required");
|
|
return false;
|
|
}
|
|
|
|
if (!args[0].IsObject())
|
|
{
|
|
ThrowTypeError(args.Env(), "First arg must be an object");
|
|
return false;
|
|
}
|
|
|
|
Napi::Object obj = args[0].As<Napi::Object>();
|
|
|
|
Napi::Value coordinates = obj.Get("coordinates");
|
|
if (coordinates.IsEmpty())
|
|
return false;
|
|
|
|
if (coordinates.IsUndefined())
|
|
{
|
|
ThrowError(args.Env(), "Must provide a coordinates property");
|
|
return false;
|
|
}
|
|
else if (coordinates.IsArray())
|
|
{
|
|
auto coordinates_array = coordinates.As<Napi::Array>();
|
|
if (coordinates_array.Length() < 2 && requires_multiple_coordinates)
|
|
{
|
|
ThrowError(args.Env(), "At least two coordinates must be provided");
|
|
return false;
|
|
}
|
|
else if (!requires_multiple_coordinates && coordinates_array.Length() != 1)
|
|
{
|
|
ThrowError(args.Env(), "Exactly one coordinate pair must be provided");
|
|
return false;
|
|
}
|
|
auto maybe_coordinates = parseCoordinateArray(coordinates_array);
|
|
if (maybe_coordinates)
|
|
{
|
|
std::copy(maybe_coordinates->begin(),
|
|
maybe_coordinates->end(),
|
|
std::back_inserter(params->coordinates));
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (!coordinates.IsUndefined())
|
|
{
|
|
BOOST_ASSERT(!coordinates.IsArray());
|
|
ThrowError(args.Env(), "Coordinates must be an array of (lon/lat) pairs");
|
|
return false;
|
|
}
|
|
|
|
if (obj.Has("approaches"))
|
|
{
|
|
Napi::Value approaches = obj.Get("approaches");
|
|
if (approaches.IsEmpty())
|
|
return false;
|
|
|
|
if (!approaches.IsArray())
|
|
{
|
|
ThrowError(args.Env(), "Approaches must be an arrays of strings");
|
|
return false;
|
|
}
|
|
|
|
auto approaches_array = approaches.As<Napi::Array>();
|
|
|
|
if (approaches_array.Length() != params->coordinates.size())
|
|
{
|
|
ThrowError(args.Env(),
|
|
"Approaches array must have the same length as coordinates array");
|
|
return false;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < approaches_array.Length(); ++i)
|
|
{
|
|
Napi::Value approach_raw = approaches_array.Get(i);
|
|
if (approach_raw.IsEmpty())
|
|
return false;
|
|
|
|
if (approach_raw.IsNull())
|
|
{
|
|
params->approaches.emplace_back();
|
|
}
|
|
else if (approach_raw.IsString())
|
|
{
|
|
std::string approach_str = approach_raw.ToString().Utf8Value();
|
|
if (approach_str == "curb")
|
|
{
|
|
params->approaches.push_back(osrm::Approach::CURB);
|
|
}
|
|
else if (approach_str == "unrestricted")
|
|
{
|
|
params->approaches.push_back(osrm::Approach::UNRESTRICTED);
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(),
|
|
"'approaches' param must be one of [curb, unrestricted]");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "Approach must be a string: [curb, unrestricted] or null");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obj.Has("bearings"))
|
|
{
|
|
Napi::Value bearings = obj.Get("bearings");
|
|
if (bearings.IsEmpty())
|
|
return false;
|
|
|
|
if (!bearings.IsArray())
|
|
{
|
|
ThrowError(args.Env(), "Bearings must be an array of arrays of numbers");
|
|
return false;
|
|
}
|
|
|
|
auto bearings_array = bearings.As<Napi::Array>();
|
|
|
|
if (bearings_array.Length() != params->coordinates.size())
|
|
{
|
|
ThrowError(args.Env(), "Bearings array must have the same length as coordinates array");
|
|
return false;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < bearings_array.Length(); ++i)
|
|
{
|
|
Napi::Value bearing_raw = bearings_array.Get(i);
|
|
if (bearing_raw.IsEmpty())
|
|
return false;
|
|
|
|
if (bearing_raw.IsNull())
|
|
{
|
|
params->bearings.emplace_back();
|
|
}
|
|
else if (bearing_raw.IsArray())
|
|
{
|
|
auto bearing_pair = bearing_raw.As<Napi::Array>();
|
|
if (bearing_pair.Length() == 2)
|
|
{
|
|
if (!bearing_pair.Get(static_cast<uint32_t>(0)).IsNumber() ||
|
|
!bearing_pair.Get(static_cast<uint32_t>(1)).IsNumber())
|
|
{
|
|
ThrowError(args.Env(), "Bearing values need to be numbers in range 0..360");
|
|
return false;
|
|
}
|
|
|
|
const auto bearing =
|
|
bearing_pair.Get(static_cast<uint32_t>(0)).ToNumber().Int32Value();
|
|
const auto range =
|
|
bearing_pair.Get(static_cast<uint32_t>(1)).ToNumber().Int32Value();
|
|
|
|
if (bearing < 0 || bearing > 360 || range < 0 || range > 180)
|
|
{
|
|
ThrowError(args.Env(), "Bearing values need to be in range 0..360, 0..180");
|
|
return false;
|
|
}
|
|
|
|
params->bearings.push_back(
|
|
osrm::Bearing{static_cast<short>(bearing), static_cast<short>(range)});
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "Bearing must be an array of [bearing, range] or null");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "Bearing must be an array of [bearing, range] or null");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obj.Has("hints"))
|
|
{
|
|
Napi::Value hints = obj.Get("hints");
|
|
if (hints.IsEmpty())
|
|
return false;
|
|
|
|
if (!hints.IsArray())
|
|
{
|
|
ThrowError(args.Env(), "Hints must be an array of strings/null");
|
|
return false;
|
|
}
|
|
|
|
Napi::Array hints_array = hints.As<Napi::Array>();
|
|
|
|
if (hints_array.Length() != params->coordinates.size())
|
|
{
|
|
ThrowError(args.Env(), "Hints array must have the same length as coordinates array");
|
|
return false;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < hints_array.Length(); ++i)
|
|
{
|
|
Napi::Value hint = hints_array.Get(i);
|
|
if (hint.IsEmpty())
|
|
return false;
|
|
|
|
if (hint.IsString())
|
|
{
|
|
if (hint.ToString().Utf8Value().length() == 0)
|
|
{
|
|
ThrowError(args.Env(), "Hint cannot be an empty string");
|
|
return false;
|
|
}
|
|
|
|
params->hints.emplace_back(
|
|
osrm::engine::Hint::FromBase64(hint.ToString().Utf8Value()));
|
|
}
|
|
else if (hint.IsNull())
|
|
{
|
|
params->hints.emplace_back();
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "Hint must be null or string");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obj.Has("radiuses"))
|
|
{
|
|
Napi::Value radiuses = obj.Get("radiuses");
|
|
if (radiuses.IsEmpty())
|
|
return false;
|
|
|
|
if (!radiuses.IsArray())
|
|
{
|
|
ThrowError(args.Env(), "Radiuses must be an array of non-negative doubles or null");
|
|
return false;
|
|
}
|
|
|
|
Napi::Array radiuses_array = radiuses.As<Napi::Array>();
|
|
|
|
if (radiuses_array.Length() != params->coordinates.size())
|
|
{
|
|
ThrowError(args.Env(), "Radiuses array must have the same length as coordinates array");
|
|
return false;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < radiuses_array.Length(); ++i)
|
|
{
|
|
Napi::Value radius = radiuses_array.Get(i);
|
|
if (radius.IsEmpty())
|
|
return false;
|
|
|
|
if (radius.IsNull())
|
|
{
|
|
params->radiuses.emplace_back();
|
|
}
|
|
else if (radius.IsNumber() && radius.ToNumber().DoubleValue() >= 0)
|
|
{
|
|
params->radiuses.push_back(radius.ToNumber().DoubleValue());
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "Radius must be non-negative double or null");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obj.Has("generate_hints"))
|
|
{
|
|
Napi::Value generate_hints = obj.Get("generate_hints");
|
|
if (generate_hints.IsEmpty())
|
|
return false;
|
|
|
|
if (!generate_hints.IsBoolean())
|
|
{
|
|
ThrowError(args.Env(), "generate_hints must be of type Boolean");
|
|
return false;
|
|
}
|
|
|
|
params->generate_hints = generate_hints.ToBoolean().Value();
|
|
}
|
|
|
|
if (obj.Has("skip_waypoints"))
|
|
{
|
|
Napi::Value skip_waypoints = obj.Get("skip_waypoints");
|
|
if (skip_waypoints.IsEmpty())
|
|
return false;
|
|
|
|
if (!skip_waypoints.IsBoolean())
|
|
{
|
|
ThrowError(args.Env(), "skip_waypoints must be of type Boolean");
|
|
return false;
|
|
}
|
|
|
|
params->skip_waypoints = skip_waypoints.ToBoolean().Value();
|
|
}
|
|
|
|
if (obj.Has("exclude"))
|
|
{
|
|
Napi::Value exclude = obj.Get("exclude");
|
|
if (exclude.IsEmpty())
|
|
return false;
|
|
|
|
if (!exclude.IsArray())
|
|
{
|
|
ThrowError(args.Env(), "Exclude must be an array of strings or empty");
|
|
return false;
|
|
}
|
|
|
|
Napi::Array exclude_array = exclude.As<Napi::Array>();
|
|
|
|
for (uint32_t i = 0; i < exclude_array.Length(); ++i)
|
|
{
|
|
Napi::Value class_name = exclude_array.Get(i);
|
|
if (class_name.IsEmpty())
|
|
return false;
|
|
|
|
if (class_name.IsString())
|
|
{
|
|
std::string class_name_str = class_name.ToString().Utf8Value();
|
|
params->exclude.emplace_back(std::move(class_name_str));
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "Exclude must be an array of strings or empty");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obj.Has("format"))
|
|
{
|
|
Napi::Value format = obj.Get("format");
|
|
if (format.IsEmpty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!format.IsString())
|
|
{
|
|
ThrowError(args.Env(), "format must be a string: \"json\" or \"flatbuffers\"");
|
|
return false;
|
|
}
|
|
|
|
std::string format_str = format.ToString().Utf8Value();
|
|
if (format_str == "json")
|
|
{
|
|
params->format = osrm::engine::api::BaseParameters::OutputFormatType::JSON;
|
|
}
|
|
else if (format_str == "flatbuffers")
|
|
{
|
|
params->format = osrm::engine::api::BaseParameters::OutputFormatType::FLATBUFFERS;
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "format must be a string: \"json\" or \"flatbuffers\"");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (obj.Has("snapping"))
|
|
{
|
|
Napi::Value snapping = obj.Get("snapping");
|
|
if (snapping.IsEmpty())
|
|
return false;
|
|
|
|
if (!snapping.IsString())
|
|
{
|
|
ThrowError(args.Env(), "Snapping must be a string: [default, any]");
|
|
return false;
|
|
}
|
|
|
|
std::string snapping_str = snapping.ToString().Utf8Value();
|
|
|
|
if (snapping_str == "default")
|
|
{
|
|
params->snapping = osrm::RouteParameters::SnappingType::Default;
|
|
}
|
|
else if (snapping_str == "any")
|
|
{
|
|
params->snapping = osrm::RouteParameters::SnappingType::Any;
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "'snapping' param must be one of [default, any]");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename ParamType>
|
|
inline bool parseCommonParameters(const Napi::Object &obj, ParamType ¶ms)
|
|
{
|
|
if (obj.Has("steps"))
|
|
{
|
|
auto steps = obj.Get("steps");
|
|
if (steps.IsEmpty())
|
|
return false;
|
|
|
|
if (steps.IsBoolean())
|
|
{
|
|
params->steps = steps.ToBoolean().Value();
|
|
}
|
|
else
|
|
{
|
|
ThrowError(obj.Env(), "'steps' param must be a boolean");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (obj.Has("annotations"))
|
|
{
|
|
auto annotations = obj.Get("annotations");
|
|
if (annotations.IsEmpty())
|
|
return false;
|
|
|
|
if (annotations.IsBoolean())
|
|
{
|
|
params->annotations = annotations.ToBoolean().Value();
|
|
params->annotations_type = params->annotations
|
|
? osrm::RouteParameters::AnnotationsType::All
|
|
: osrm::RouteParameters::AnnotationsType::None;
|
|
}
|
|
else if (annotations.IsArray())
|
|
{
|
|
Napi::Array annotations_array = annotations.As<Napi::Array>();
|
|
for (std::size_t i = 0; i < annotations_array.Length(); i++)
|
|
{
|
|
std::string annotations_str = annotations_array.Get(i).ToString().Utf8Value();
|
|
|
|
if (annotations_str == "duration")
|
|
{
|
|
params->annotations_type =
|
|
params->annotations_type | osrm::RouteParameters::AnnotationsType::Duration;
|
|
}
|
|
else if (annotations_str == "nodes")
|
|
{
|
|
params->annotations_type =
|
|
params->annotations_type | osrm::RouteParameters::AnnotationsType::Nodes;
|
|
}
|
|
else if (annotations_str == "distance")
|
|
{
|
|
params->annotations_type =
|
|
params->annotations_type | osrm::RouteParameters::AnnotationsType::Distance;
|
|
}
|
|
else if (annotations_str == "weight")
|
|
{
|
|
params->annotations_type =
|
|
params->annotations_type | osrm::RouteParameters::AnnotationsType::Weight;
|
|
}
|
|
else if (annotations_str == "datasources")
|
|
{
|
|
params->annotations_type = params->annotations_type |
|
|
osrm::RouteParameters::AnnotationsType::Datasources;
|
|
}
|
|
else if (annotations_str == "speed")
|
|
{
|
|
params->annotations_type =
|
|
params->annotations_type | osrm::RouteParameters::AnnotationsType::Speed;
|
|
}
|
|
else
|
|
{
|
|
ThrowError(obj.Env(), "this 'annotations' param is not supported");
|
|
return false;
|
|
}
|
|
|
|
params->annotations =
|
|
params->annotations_type != osrm::RouteParameters::AnnotationsType::None;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ThrowError(obj.Env(), "this 'annotations' param is not supported");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (obj.Has("geometries"))
|
|
{
|
|
Napi::Value geometries = obj.Get("geometries");
|
|
if (geometries.IsEmpty())
|
|
return false;
|
|
|
|
if (!geometries.IsString())
|
|
{
|
|
ThrowError(obj.Env(), "Geometries must be a string: [polyline, polyline6, geojson]");
|
|
return false;
|
|
}
|
|
std::string geometries_str = geometries.ToString().Utf8Value();
|
|
|
|
if (geometries_str == "polyline")
|
|
{
|
|
params->geometries = osrm::RouteParameters::GeometriesType::Polyline;
|
|
}
|
|
else if (geometries_str == "polyline6")
|
|
{
|
|
params->geometries = osrm::RouteParameters::GeometriesType::Polyline6;
|
|
}
|
|
else if (geometries_str == "geojson")
|
|
{
|
|
params->geometries = osrm::RouteParameters::GeometriesType::GeoJSON;
|
|
}
|
|
else
|
|
{
|
|
ThrowError(obj.Env(),
|
|
"'geometries' param must be one of [polyline, polyline6, geojson]");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (obj.Has("overview"))
|
|
{
|
|
Napi::Value overview = obj.Get("overview");
|
|
if (overview.IsEmpty())
|
|
return false;
|
|
|
|
if (!overview.IsString())
|
|
{
|
|
ThrowError(obj.Env(), "Overview must be a string: [simplified, full, false]");
|
|
return false;
|
|
}
|
|
|
|
std::string overview_str = overview.ToString().Utf8Value();
|
|
|
|
if (overview_str == "simplified")
|
|
{
|
|
params->overview = osrm::RouteParameters::OverviewType::Simplified;
|
|
}
|
|
else if (overview_str == "full")
|
|
{
|
|
params->overview = osrm::RouteParameters::OverviewType::Full;
|
|
}
|
|
else if (overview_str == "false")
|
|
{
|
|
params->overview = osrm::RouteParameters::OverviewType::False;
|
|
}
|
|
else
|
|
{
|
|
ThrowError(obj.Env(), "'overview' param must be one of [simplified, full, false]");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
inline PluginParameters argumentsToPluginParameters(
|
|
const Napi::CallbackInfo &args,
|
|
const boost::optional<osrm::engine::api::BaseParameters::OutputFormatType> &output_format = {})
|
|
{
|
|
if (args.Length() < 3 || !args[1].IsObject())
|
|
{
|
|
// output to buffer by default for Flatbuffers
|
|
return {output_format == osrm::engine::api::BaseParameters::OutputFormatType::FLATBUFFERS};
|
|
}
|
|
Napi::Object obj = args[1].As<Napi::Object>();
|
|
if (obj.Has("format"))
|
|
{
|
|
Napi::Value format = obj.Get("format");
|
|
if (format.IsEmpty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if (!format.IsString())
|
|
{
|
|
ThrowError(args.Env(), "format must be a string: \"object\" or \"buffer\"");
|
|
return {};
|
|
}
|
|
|
|
std::string format_str = format.ToString().Utf8Value();
|
|
|
|
if (format_str == "object")
|
|
{
|
|
if (output_format == osrm::engine::api::BaseParameters::OutputFormatType::FLATBUFFERS)
|
|
{
|
|
ThrowError(args.Env(), "Flatbuffers result can only output to buffer.");
|
|
return {true};
|
|
}
|
|
return {false};
|
|
}
|
|
else if (format_str == "buffer")
|
|
{
|
|
return {true};
|
|
}
|
|
else if (format_str == "json_buffer")
|
|
{
|
|
if (output_format &&
|
|
output_format != osrm::engine::api::BaseParameters::OutputFormatType::JSON)
|
|
{
|
|
ThrowError(args.Env(),
|
|
"Deprecated `json_buffer` can only be used with JSON format");
|
|
}
|
|
return {true};
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "format must be a string: \"object\" or \"buffer\"");
|
|
return {};
|
|
}
|
|
}
|
|
|
|
// output to buffer by default for Flatbuffers
|
|
return {output_format == osrm::engine::api::BaseParameters::OutputFormatType::FLATBUFFERS};
|
|
}
|
|
|
|
inline route_parameters_ptr argumentsToRouteParameter(const Napi::CallbackInfo &args,
|
|
bool requires_multiple_coordinates)
|
|
{
|
|
route_parameters_ptr params = std::make_unique<osrm::RouteParameters>();
|
|
bool has_base_params = argumentsToParameter(args, params, requires_multiple_coordinates);
|
|
if (!has_base_params)
|
|
return route_parameters_ptr();
|
|
|
|
Napi::Object obj = args[0].As<Napi::Object>();
|
|
|
|
if (obj.Has("continue_straight"))
|
|
{
|
|
auto value = obj.Get("continue_straight");
|
|
if (value.IsEmpty())
|
|
return route_parameters_ptr();
|
|
|
|
if (!value.IsBoolean() && !value.IsNull())
|
|
{
|
|
ThrowError(args.Env(), "'continue_straight' param must be boolean or null");
|
|
return route_parameters_ptr();
|
|
}
|
|
if (value.IsBoolean())
|
|
{
|
|
params->continue_straight = value.ToBoolean().Value();
|
|
}
|
|
}
|
|
|
|
if (obj.Has("alternatives"))
|
|
{
|
|
auto value = obj.Get("alternatives");
|
|
if (value.IsEmpty())
|
|
return route_parameters_ptr();
|
|
|
|
if (value.IsBoolean())
|
|
{
|
|
params->alternatives = value.ToBoolean().Value();
|
|
params->number_of_alternatives = value.ToBoolean().Value() ? 1u : 0u;
|
|
}
|
|
else if (value.IsNumber())
|
|
{
|
|
params->alternatives = value.ToBoolean().Value();
|
|
params->number_of_alternatives = value.ToNumber().Int32Value();
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "'alternatives' param must be boolean or number");
|
|
return route_parameters_ptr();
|
|
}
|
|
}
|
|
|
|
if (obj.Has("waypoints"))
|
|
{
|
|
Napi::Value waypoints = obj.Get("waypoints");
|
|
if (waypoints.IsEmpty())
|
|
return route_parameters_ptr();
|
|
|
|
// must be array
|
|
if (!waypoints.IsArray())
|
|
{
|
|
ThrowError(
|
|
args.Env(),
|
|
"Waypoints must be an array of integers corresponding to the input coordinates.");
|
|
return route_parameters_ptr();
|
|
}
|
|
|
|
auto waypoints_array = waypoints.As<Napi::Array>();
|
|
// must have at least two elements
|
|
if (waypoints_array.Length() < 2)
|
|
{
|
|
ThrowError(args.Env(), "At least two waypoints must be provided");
|
|
return route_parameters_ptr();
|
|
}
|
|
auto coords_size = params->coordinates.size();
|
|
auto waypoints_array_size = waypoints_array.Length();
|
|
|
|
const auto first_index =
|
|
waypoints_array.Get(static_cast<uint32_t>(0)).ToNumber().Uint32Value();
|
|
const auto last_index =
|
|
waypoints_array.Get(waypoints_array_size - 1).ToNumber().Uint32Value();
|
|
if (first_index != 0 || last_index != coords_size - 1)
|
|
{
|
|
ThrowError(args.Env(),
|
|
"First and last waypoints values must correspond to first and last "
|
|
"coordinate indices");
|
|
return route_parameters_ptr();
|
|
}
|
|
|
|
for (uint32_t i = 0; i < waypoints_array_size; ++i)
|
|
{
|
|
Napi::Value waypoint_value = waypoints_array.Get(i);
|
|
// all elements must be numbers
|
|
if (!waypoint_value.IsNumber())
|
|
{
|
|
ThrowError(args.Env(), "Waypoint values must be an array of integers");
|
|
return route_parameters_ptr();
|
|
}
|
|
// check that the waypoint index corresponds with an inpute coordinate
|
|
const auto index = waypoint_value.ToNumber().Uint32Value();
|
|
if (index >= coords_size)
|
|
{
|
|
ThrowError(args.Env(),
|
|
"Waypoints must correspond with the index of an input coordinate");
|
|
return route_parameters_ptr();
|
|
}
|
|
params->waypoints.emplace_back(index);
|
|
}
|
|
|
|
if (!params->waypoints.empty())
|
|
{
|
|
for (std::size_t i = 0; i < params->waypoints.size() - 1; i++)
|
|
{
|
|
if (params->waypoints[i] >= params->waypoints[i + 1])
|
|
{
|
|
ThrowError(args.Env(), "Waypoints must be supplied in increasing order");
|
|
return route_parameters_ptr();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool parsedSuccessfully = parseCommonParameters(obj, params);
|
|
if (!parsedSuccessfully)
|
|
{
|
|
return route_parameters_ptr();
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
inline tile_parameters_ptr argumentsToTileParameters(const Napi::CallbackInfo &args,
|
|
bool /*unused*/)
|
|
{
|
|
tile_parameters_ptr params = std::make_unique<osrm::TileParameters>();
|
|
|
|
if (args.Length() < 2)
|
|
{
|
|
ThrowTypeError(args.Env(), "Coordinate object and callback required");
|
|
return tile_parameters_ptr();
|
|
}
|
|
|
|
if (!args[0].IsArray())
|
|
{
|
|
ThrowTypeError(args.Env(), "Parameter must be an array [x, y, z]");
|
|
return tile_parameters_ptr();
|
|
}
|
|
|
|
Napi::Array array = args[0].As<Napi::Array>();
|
|
|
|
if (array.Length() != 3)
|
|
{
|
|
ThrowTypeError(args.Env(), "Parameter must be an array [x, y, z]");
|
|
return tile_parameters_ptr();
|
|
}
|
|
|
|
Napi::Value x = array.Get(static_cast<uint32_t>(0));
|
|
Napi::Value y = array.Get(static_cast<uint32_t>(1));
|
|
Napi::Value z = array.Get(static_cast<uint32_t>(2));
|
|
if (x.IsEmpty() || y.IsEmpty() || z.IsEmpty())
|
|
return tile_parameters_ptr();
|
|
|
|
if (!IsUnsignedInteger(x) && !x.IsUndefined())
|
|
{
|
|
ThrowError(args.Env(), "Tile x coordinate must be unsigned interger");
|
|
return tile_parameters_ptr();
|
|
}
|
|
if (!IsUnsignedInteger(y) && !y.IsUndefined())
|
|
{
|
|
ThrowError(args.Env(), "Tile y coordinate must be unsigned interger");
|
|
return tile_parameters_ptr();
|
|
}
|
|
if (!IsUnsignedInteger(z) && !z.IsUndefined())
|
|
{
|
|
ThrowError(args.Env(), "Tile z coordinate must be unsigned interger");
|
|
return tile_parameters_ptr();
|
|
}
|
|
|
|
params->x = x.ToNumber().Uint32Value();
|
|
params->y = y.ToNumber().Uint32Value();
|
|
params->z = z.ToNumber().Uint32Value();
|
|
|
|
if (!params->IsValid())
|
|
{
|
|
ThrowError(args.Env(), "Invalid tile coordinates");
|
|
return tile_parameters_ptr();
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
inline nearest_parameters_ptr argumentsToNearestParameter(const Napi::CallbackInfo &args,
|
|
bool requires_multiple_coordinates)
|
|
{
|
|
nearest_parameters_ptr params = std::make_unique<osrm::NearestParameters>();
|
|
bool has_base_params = argumentsToParameter(args, params, requires_multiple_coordinates);
|
|
if (!has_base_params)
|
|
return nearest_parameters_ptr();
|
|
|
|
Napi::Object obj = args[0].As<Napi::Object>();
|
|
if (obj.IsEmpty())
|
|
return nearest_parameters_ptr();
|
|
|
|
if (obj.Has("number"))
|
|
{
|
|
Napi::Value number = obj.Get("number");
|
|
|
|
if (!IsUnsignedInteger(number))
|
|
{
|
|
ThrowError(args.Env(), "Number must be an integer greater than or equal to 1");
|
|
return nearest_parameters_ptr();
|
|
}
|
|
else
|
|
{
|
|
unsigned number_value = number.ToNumber().Uint32Value();
|
|
|
|
if (number_value < 1)
|
|
{
|
|
ThrowError(args.Env(), "Number must be an integer greater than or equal to 1");
|
|
return nearest_parameters_ptr();
|
|
}
|
|
|
|
params->number_of_results = number_value;
|
|
}
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
inline table_parameters_ptr argumentsToTableParameter(const Napi::CallbackInfo &args,
|
|
bool requires_multiple_coordinates)
|
|
{
|
|
table_parameters_ptr params = std::make_unique<osrm::TableParameters>();
|
|
bool has_base_params = argumentsToParameter(args, params, requires_multiple_coordinates);
|
|
if (!has_base_params)
|
|
return table_parameters_ptr();
|
|
|
|
Napi::Object obj = args[0].As<Napi::Object>();
|
|
if (obj.IsEmpty())
|
|
return table_parameters_ptr();
|
|
|
|
if (obj.Has("sources"))
|
|
{
|
|
Napi::Value sources = obj.Get("sources");
|
|
if (sources.IsEmpty())
|
|
return table_parameters_ptr();
|
|
|
|
if (!sources.IsArray())
|
|
{
|
|
ThrowError(args.Env(), "Sources must be an array of indices (or undefined)");
|
|
return table_parameters_ptr();
|
|
}
|
|
|
|
Napi::Array sources_array = sources.As<Napi::Array>();
|
|
for (uint32_t i = 0; i < sources_array.Length(); ++i)
|
|
{
|
|
Napi::Value source = sources_array.Get(i);
|
|
if (source.IsEmpty())
|
|
return table_parameters_ptr();
|
|
|
|
if (IsUnsignedInteger(source))
|
|
{
|
|
size_t source_value = source.ToNumber().Uint32Value();
|
|
if (source_value >= params->coordinates.size())
|
|
{
|
|
ThrowError(args.Env(),
|
|
"Source indices must be less than the number of coordinates");
|
|
return table_parameters_ptr();
|
|
}
|
|
|
|
params->sources.push_back(source.ToNumber().Uint32Value());
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "Source must be an integer");
|
|
return table_parameters_ptr();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obj.Has("destinations"))
|
|
{
|
|
Napi::Value destinations = obj.Get("destinations");
|
|
if (destinations.IsEmpty())
|
|
return table_parameters_ptr();
|
|
|
|
if (!destinations.IsArray())
|
|
{
|
|
ThrowError(args.Env(), "Destinations must be an array of indices (or undefined)");
|
|
return table_parameters_ptr();
|
|
}
|
|
|
|
Napi::Array destinations_array = destinations.As<Napi::Array>();
|
|
for (uint32_t i = 0; i < destinations_array.Length(); ++i)
|
|
{
|
|
Napi::Value destination = destinations_array.Get(i);
|
|
if (destination.IsEmpty())
|
|
return table_parameters_ptr();
|
|
|
|
if (IsUnsignedInteger(destination))
|
|
{
|
|
size_t destination_value = destination.ToNumber().Uint32Value();
|
|
if (destination_value >= params->coordinates.size())
|
|
{
|
|
ThrowError(args.Env(),
|
|
"Destination indices must be less than the number "
|
|
"of coordinates");
|
|
return table_parameters_ptr();
|
|
}
|
|
|
|
params->destinations.push_back(destination_value);
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "Destination must be an integer");
|
|
return table_parameters_ptr();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obj.Has("annotations"))
|
|
{
|
|
Napi::Value annotations = obj.Get("annotations");
|
|
if (annotations.IsEmpty())
|
|
return table_parameters_ptr();
|
|
|
|
if (!annotations.IsArray())
|
|
{
|
|
ThrowError(args.Env(),
|
|
"Annotations must an array containing 'duration' or 'distance', or both");
|
|
return table_parameters_ptr();
|
|
}
|
|
|
|
params->annotations = osrm::TableParameters::AnnotationsType::None;
|
|
|
|
Napi::Array annotations_array = annotations.As<Napi::Array>();
|
|
for (std::size_t i = 0; i < annotations_array.Length(); ++i)
|
|
{
|
|
std::string annotations_str = annotations_array.Get(i).ToString().Utf8Value();
|
|
|
|
if (annotations_str == "duration")
|
|
{
|
|
params->annotations =
|
|
params->annotations | osrm::TableParameters::AnnotationsType::Duration;
|
|
}
|
|
else if (annotations_str == "distance")
|
|
{
|
|
params->annotations =
|
|
params->annotations | osrm::TableParameters::AnnotationsType::Distance;
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "this 'annotations' param is not supported");
|
|
return table_parameters_ptr();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obj.Has("fallback_speed"))
|
|
{
|
|
auto fallback_speed = obj.Get("fallback_speed");
|
|
|
|
if (!fallback_speed.IsNumber())
|
|
{
|
|
ThrowError(args.Env(), "fallback_speed must be a number");
|
|
return table_parameters_ptr();
|
|
}
|
|
else if (fallback_speed.ToNumber().DoubleValue() <= 0)
|
|
{
|
|
ThrowError(args.Env(), "fallback_speed must be > 0");
|
|
return table_parameters_ptr();
|
|
}
|
|
|
|
params->fallback_speed = fallback_speed.ToNumber().DoubleValue();
|
|
}
|
|
|
|
if (obj.Has("fallback_coordinate"))
|
|
{
|
|
auto fallback_coordinate = obj.Get("fallback_coordinate");
|
|
|
|
if (!fallback_coordinate.IsString())
|
|
{
|
|
ThrowError(args.Env(), "fallback_coordinate must be a string: [input, snapped]");
|
|
return table_parameters_ptr();
|
|
}
|
|
|
|
std::string fallback_coordinate_str = fallback_coordinate.ToString().Utf8Value();
|
|
|
|
if (fallback_coordinate_str == "snapped")
|
|
{
|
|
params->fallback_coordinate_type =
|
|
osrm::TableParameters::FallbackCoordinateType::Snapped;
|
|
}
|
|
else if (fallback_coordinate_str == "input")
|
|
{
|
|
params->fallback_coordinate_type = osrm::TableParameters::FallbackCoordinateType::Input;
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "'fallback_coordinate' param must be one of [input, snapped]");
|
|
return table_parameters_ptr();
|
|
}
|
|
}
|
|
|
|
if (obj.Has("scale_factor"))
|
|
{
|
|
auto scale_factor = obj.Get("scale_factor");
|
|
|
|
if (!scale_factor.IsNumber())
|
|
{
|
|
ThrowError(args.Env(), "scale_factor must be a number");
|
|
return table_parameters_ptr();
|
|
}
|
|
else if (scale_factor.ToNumber().DoubleValue() <= 0)
|
|
{
|
|
ThrowError(args.Env(), "scale_factor must be > 0");
|
|
return table_parameters_ptr();
|
|
}
|
|
|
|
params->scale_factor = scale_factor.ToNumber().DoubleValue();
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
inline trip_parameters_ptr argumentsToTripParameter(const Napi::CallbackInfo &args,
|
|
bool requires_multiple_coordinates)
|
|
{
|
|
trip_parameters_ptr params = std::make_unique<osrm::TripParameters>();
|
|
bool has_base_params = argumentsToParameter(args, params, requires_multiple_coordinates);
|
|
if (!has_base_params)
|
|
return trip_parameters_ptr();
|
|
|
|
Napi::Object obj = args[0].As<Napi::Object>();
|
|
|
|
bool parsedSuccessfully = parseCommonParameters(obj, params);
|
|
if (!parsedSuccessfully)
|
|
{
|
|
return trip_parameters_ptr();
|
|
}
|
|
|
|
if (obj.Has("roundtrip"))
|
|
{
|
|
auto roundtrip = obj.Get("roundtrip");
|
|
if (roundtrip.IsEmpty())
|
|
return trip_parameters_ptr();
|
|
|
|
if (roundtrip.IsBoolean())
|
|
{
|
|
params->roundtrip = roundtrip.ToBoolean().Value();
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "'roundtrip' param must be a boolean");
|
|
return trip_parameters_ptr();
|
|
}
|
|
}
|
|
|
|
if (obj.Has("source"))
|
|
{
|
|
Napi::Value source = obj.Get("source");
|
|
if (source.IsEmpty())
|
|
return trip_parameters_ptr();
|
|
|
|
if (!source.IsString())
|
|
{
|
|
ThrowError(args.Env(), "Source must be a string: [any, first]");
|
|
return trip_parameters_ptr();
|
|
}
|
|
|
|
std::string source_str = source.ToString().Utf8Value();
|
|
|
|
if (source_str == "first")
|
|
{
|
|
params->source = osrm::TripParameters::SourceType::First;
|
|
}
|
|
else if (source_str == "any")
|
|
{
|
|
params->source = osrm::TripParameters::SourceType::Any;
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "'source' param must be one of [any, first]");
|
|
return trip_parameters_ptr();
|
|
}
|
|
}
|
|
|
|
if (obj.Has("destination"))
|
|
{
|
|
Napi::Value destination = obj.Get("destination");
|
|
if (destination.IsEmpty())
|
|
return trip_parameters_ptr();
|
|
|
|
if (!destination.IsString())
|
|
{
|
|
ThrowError(args.Env(), "Destination must be a string: [any, last]");
|
|
return trip_parameters_ptr();
|
|
}
|
|
|
|
std::string destination_str = destination.ToString().Utf8Value();
|
|
|
|
if (destination_str == "last")
|
|
{
|
|
params->destination = osrm::TripParameters::DestinationType::Last;
|
|
}
|
|
else if (destination_str == "any")
|
|
{
|
|
params->destination = osrm::TripParameters::DestinationType::Any;
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "'destination' param must be one of [any, last]");
|
|
return trip_parameters_ptr();
|
|
}
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
inline match_parameters_ptr argumentsToMatchParameter(const Napi::CallbackInfo &args,
|
|
bool requires_multiple_coordinates)
|
|
{
|
|
match_parameters_ptr params = std::make_unique<osrm::MatchParameters>();
|
|
bool has_base_params = argumentsToParameter(args, params, requires_multiple_coordinates);
|
|
if (!has_base_params)
|
|
return match_parameters_ptr();
|
|
|
|
Napi::Object obj = args[0].As<Napi::Object>();
|
|
|
|
if (obj.Has("timestamps"))
|
|
{
|
|
Napi::Value timestamps = obj.Get("timestamps");
|
|
if (timestamps.IsEmpty())
|
|
return match_parameters_ptr();
|
|
|
|
if (!timestamps.IsArray())
|
|
{
|
|
ThrowError(args.Env(), "Timestamps must be an array of integers (or undefined)");
|
|
return match_parameters_ptr();
|
|
}
|
|
|
|
Napi::Array timestamps_array = timestamps.As<Napi::Array>();
|
|
|
|
if (params->coordinates.size() != timestamps_array.Length())
|
|
{
|
|
ThrowError(args.Env(),
|
|
"Timestamp array must have the same size as the coordinates "
|
|
"array");
|
|
return match_parameters_ptr();
|
|
}
|
|
|
|
for (uint32_t i = 0; i < timestamps_array.Length(); ++i)
|
|
{
|
|
Napi::Value timestamp = timestamps_array.Get(i);
|
|
if (timestamp.IsEmpty())
|
|
return match_parameters_ptr();
|
|
|
|
if (!timestamp.IsNumber())
|
|
{
|
|
ThrowError(args.Env(), "Timestamps array items must be numbers");
|
|
return match_parameters_ptr();
|
|
}
|
|
params->timestamps.emplace_back(timestamp.ToNumber().Int64Value());
|
|
}
|
|
}
|
|
|
|
if (obj.Has("gaps"))
|
|
{
|
|
Napi::Value gaps = obj.Get("gaps");
|
|
if (gaps.IsEmpty())
|
|
return match_parameters_ptr();
|
|
|
|
if (!gaps.IsString())
|
|
{
|
|
ThrowError(args.Env(), "Gaps must be a string: [split, ignore]");
|
|
return match_parameters_ptr();
|
|
}
|
|
|
|
std::string gaps_str = gaps.ToString().Utf8Value();
|
|
|
|
if (gaps_str == "split")
|
|
{
|
|
params->gaps = osrm::MatchParameters::GapsType::Split;
|
|
}
|
|
else if (gaps_str == "ignore")
|
|
{
|
|
params->gaps = osrm::MatchParameters::GapsType::Ignore;
|
|
}
|
|
else
|
|
{
|
|
ThrowError(args.Env(), "'gaps' param must be one of [split, ignore]");
|
|
return match_parameters_ptr();
|
|
}
|
|
}
|
|
|
|
if (obj.Has("tidy"))
|
|
{
|
|
Napi::Value tidy = obj.Get("tidy");
|
|
if (tidy.IsEmpty())
|
|
return match_parameters_ptr();
|
|
|
|
if (!tidy.IsBoolean())
|
|
{
|
|
ThrowError(args.Env(), "tidy must be of type Boolean");
|
|
return match_parameters_ptr();
|
|
}
|
|
|
|
params->tidy = tidy.ToBoolean().Value();
|
|
}
|
|
|
|
if (obj.Has("waypoints"))
|
|
{
|
|
Napi::Value waypoints = obj.Get("waypoints");
|
|
if (waypoints.IsEmpty())
|
|
return match_parameters_ptr();
|
|
|
|
// must be array
|
|
if (!waypoints.IsArray())
|
|
{
|
|
ThrowError(
|
|
args.Env(),
|
|
"Waypoints must be an array of integers corresponding to the input coordinates.");
|
|
return match_parameters_ptr();
|
|
}
|
|
|
|
auto waypoints_array = waypoints.As<Napi::Array>();
|
|
// must have at least two elements
|
|
if (waypoints_array.Length() < 2)
|
|
{
|
|
ThrowError(args.Env(), "At least two waypoints must be provided");
|
|
return match_parameters_ptr();
|
|
}
|
|
auto coords_size = params->coordinates.size();
|
|
auto waypoints_array_size = waypoints_array.Length();
|
|
|
|
const auto first_index =
|
|
waypoints_array.Get(static_cast<uint32_t>(0)).ToNumber().Uint32Value();
|
|
const auto last_index =
|
|
waypoints_array.Get(waypoints_array_size - 1).ToNumber().Uint32Value();
|
|
if (first_index != 0 || last_index != coords_size - 1)
|
|
{
|
|
ThrowError(args.Env(),
|
|
"First and last waypoints values must correspond to first and last "
|
|
"coordinate indices");
|
|
return match_parameters_ptr();
|
|
}
|
|
|
|
for (uint32_t i = 0; i < waypoints_array_size; ++i)
|
|
{
|
|
Napi::Value waypoint_value = waypoints_array.Get(i);
|
|
// all elements must be numbers
|
|
if (!waypoint_value.IsNumber())
|
|
{
|
|
ThrowError(args.Env(), "Waypoint values must be an array of integers");
|
|
return match_parameters_ptr();
|
|
}
|
|
// check that the waypoint index corresponds with an inpute coordinate
|
|
const auto index = waypoint_value.ToNumber().Uint32Value();
|
|
if (index >= coords_size)
|
|
{
|
|
ThrowError(args.Env(),
|
|
"Waypoints must correspond with the index of an input coordinate");
|
|
return match_parameters_ptr();
|
|
}
|
|
params->waypoints.emplace_back(index);
|
|
}
|
|
}
|
|
|
|
bool parsedSuccessfully = parseCommonParameters(obj, params);
|
|
if (!parsedSuccessfully)
|
|
{
|
|
return match_parameters_ptr();
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
} // namespace node_osrm
|
|
|
|
#endif
|