Merge branch 'Project-OSRM:master' into require_radius_when_using_bearings

This commit is contained in:
Whytro 2023-04-28 02:43:42 +09:00 committed by GitHub
commit ba23f5b587
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1011 additions and 60 deletions

View File

@ -1,5 +1,7 @@
# Unreleased
- Changes from 5.27.1
- Features
- ADDED: Add support for a default_radius flag. [#6575](https://github.com/Project-OSRM/osrm-backend/pull/6575)
- Build:
- ADDED: Add CI job which builds OSRM with gcc 12. [#6455](https://github.com/Project-OSRM/osrm-backend/pull/6455)
- CHANGED: Upgrade to clang-tidy 15. [#6439](https://github.com/Project-OSRM/osrm-backend/pull/6439)
@ -8,6 +10,7 @@
- NodeJS:
- CHANGED: Use node-api instead of NAN. [#6452](https://github.com/Project-OSRM/osrm-backend/pull/6452)
- Misc:
- CHANGED: keep libosrm* in the docker image for downstream linking [#6602](https://github.com/Project-OSRM/osrm-backend/pull/6602)
- CHANGED: Move vector in CSVFilesParser instead copying it. [#6470](https://github.com/Project-OSRM/osrm-backend/pull/6470)
- REMOVED: Get rid of unused functions in util/json_util.hpp. [#6446](https://github.com/Project-OSRM/osrm-backend/pull/6446)
- FIXED: Apply workaround for Conan installation issue on CI. [#6442](https://github.com/Project-OSRM/osrm-backend/pull/6442)
@ -18,6 +21,8 @@
- CHANGED: Make edge metrics strongly typed [#6420](https://github.com/Project-OSRM/osrm-backend/pull/6420)
- FIXED: Typo in file name src/util/timed_historgram.cpp -> src/util/timed_histogram.cpp [#6428](https://github.com/Project-OSRM/osrm-backend/issues/6428)
- CHANGED: Replace boost::string_ref with std::string_view [#6433](https://github.com/Project-OSRM/osrm-backend/pull/6433)
- ADDED: Print tracebacks for Lua runtime errors [#6564](https://github.com/Project-OSRM/osrm-backend/pull/6564)
- FIXED: Added a variable to preprocessor guard in file osrm-backend/include/util/range_table.hpp to solve build error. [#6596](https://github.com/Project-OSRM/osrm-backend/pull/6596)
- Routing:
- FIXED: Fix adding traffic signal penalties during compression [#6419](https://github.com/Project-OSRM/osrm-backend/pull/6419)
# 5.27.1

View File

@ -37,7 +37,7 @@ RUN NPROC=${BUILD_CONCURRENCY:-$(nproc)} && \
cd ../profiles && \
cp -r * /opt && \
strip /usr/local/bin/* && \
rm -rf /src /usr/local/lib/libosrm*
rm -rf /src
# Multistage build to reduce image size - https://docs.docker.com/engine/userguide/eng-image/multistage-build/#use-multi-stage-builds

View File

@ -38,6 +38,7 @@ var osrm = new OSRM('network.osrm');
- `options.max_radius_map_matching` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** Max. radius size supported in map matching query (default: 5).
- `options.max_results_nearest` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** Max. results supported in nearest query (default: unlimited).
- `options.max_alternatives` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** Max. number of alternatives supported in alternative routes query (default: 3).
- `options.default_radius` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** Default radius for queries (default: unlimited).
### route

View File

@ -300,7 +300,7 @@ And the relations
The setting looks perfectly fine at first glance. However, it is not well defined.
The forbidden right turn could be either a superfluous addition, forbidding the turn `cb` to `be`, or actually refer to the turn `ab` to `bd` to say that a turn is forbidden here.
To model turn-restrictions correctly and unique, we need to split segments that contribute to the restriction into the smallest possible parts.
To model turn-restrictions correctly and uniquely, we need to split segments that contribute to the restriction into the smallest possible parts.
E.g. the above scenario could correctly be expressed as:
```
@ -360,7 +360,7 @@ When I route I should get
And the test reports `turn right` for the route `a->e`, where before it said `slight right`.
If you changed the turn angles, obviously you can expect changes in the distinction between `slight right` and `right`.
If you change the turn angles, obviously you can expect changes in the distinction between `slight right` and `right`.
In such a case it is, of course, reasonable to change the expected route to report `right` instead of `slight right`. You should consider inspecting the actual turn angles at `b` to see if you feel that change is justified.
However, you should never adjust the test itself.
@ -390,9 +390,9 @@ In this case we would see a very slight turn angle. If your change now reports d
### Consider Post-Processing Impacts
Some changes you might see could look completely unrelated. To understand the impact of your changes, you can make use of the debugging utilities you can finde in `util/debug.hpp` (and potentially other related headers).
Some changes you might see could look completely unrelated. To understand the impact of your changes, you can make use of the debugging utilities you can find in `util/debug.hpp` (and potentially other related headers).
If your test is inspecting a series of turns (remember, a turn not necessarily equals an instruction), you could see interaction with post-processing.
If your test is inspecting a series of turns (remember, a turn does not necessarily equals an instruction), you could see interaction with post-processing.
To see the unprocessed turns, you should print the steps at the end of step assembly (`assembleSteps` in `engine/guidance/assemble_steps.hpp`).
If you see unexpected changes, you can consider adding the `locations` field to your test to study what location a turn is reported at.

View File

@ -22,6 +22,7 @@ Feature: osrm-routed command line options: help
And stdout should contain "--max-trip-size"
And stdout should contain "--max-table-size"
And stdout should contain "--max-matching-size"
And stdout should contain "--default-radius"
And it should exit successfully
Scenario: osrm-routed - Help, short
@ -42,6 +43,7 @@ Feature: osrm-routed command line options: help
And stdout should contain "--max-trip-size"
And stdout should contain "--max-table-size"
And stdout should contain "--max-matching-size"
And stdout should contain "--default-radius"
And it should exit successfully
Scenario: osrm-routed - Help, long
@ -62,4 +64,5 @@ Feature: osrm-routed command line options: help
And stdout should contain "--max-table-size"
And stdout should contain "--max-table-size"
And stdout should contain "--max-matching-size"
And stdout should contain "--default-radius"
And it should exit successfully

View File

@ -43,12 +43,16 @@ template <typename Algorithm> class Engine final : public EngineInterface
{
public:
explicit Engine(const EngineConfig &config)
: route_plugin(config.max_locations_viaroute, config.max_alternatives), //
table_plugin(config.max_locations_distance_table), //
nearest_plugin(config.max_results_nearest), //
trip_plugin(config.max_locations_trip), //
match_plugin(config.max_locations_map_matching, config.max_radius_map_matching), //
tile_plugin() //
: route_plugin(config.max_locations_viaroute,
config.max_alternatives,
config.default_radius), //
table_plugin(config.max_locations_distance_table, config.default_radius), //
nearest_plugin(config.max_results_nearest, config.default_radius), //
trip_plugin(config.max_locations_trip, config.default_radius), //
match_plugin(config.max_locations_map_matching,
config.max_radius_map_matching,
config.default_radius), //
tile_plugin() //
{
if (config.use_shared_memory)

View File

@ -83,6 +83,7 @@ struct EngineConfig final
int max_locations_map_matching = -1;
double max_radius_map_matching = -1.0;
int max_results_nearest = -1;
boost::optional<double> default_radius;
int max_alternatives = 3; // set an arbitrary upper bound; can be adjusted by user
bool use_shared_memory = true;
boost::filesystem::path memory_file;

View File

@ -20,8 +20,10 @@ class MatchPlugin : public BasePlugin
using CandidateLists = routing_algorithms::CandidateLists;
static const constexpr double RADIUS_MULTIPLIER = 3;
MatchPlugin(const int max_locations_map_matching, const double max_radius_map_matching)
: max_locations_map_matching(max_locations_map_matching),
MatchPlugin(const int max_locations_map_matching,
const double max_radius_map_matching,
const boost::optional<double> default_radius)
: BasePlugin(default_radius), max_locations_map_matching(max_locations_map_matching),
max_radius_map_matching(max_radius_map_matching)
{
}

View File

@ -13,7 +13,7 @@ namespace osrm::engine::plugins
class NearestPlugin final : public BasePlugin
{
public:
explicit NearestPlugin(const int max_results);
explicit NearestPlugin(const int max_results, const boost::optional<double> default_radius);
Status HandleRequest(const RoutingAlgorithmsInterface &algorithms,
const api::NearestParameters &params,

View File

@ -27,6 +27,10 @@ namespace osrm::engine::plugins
class BasePlugin
{
protected:
BasePlugin() = default;
BasePlugin(const boost::optional<double> default_radius_) : default_radius(default_radius_) {}
bool CheckAllCoordinates(const std::vector<util::Coordinate> &coordinates) const
{
return !std::any_of(
@ -237,7 +241,7 @@ class BasePlugin
phantom_nodes[i] = facade.NearestPhantomNodes(
parameters.coordinates[i],
number_of_results,
use_radiuses ? parameters.radiuses[i] : boost::none,
use_radiuses ? parameters.radiuses[i] : default_radius,
use_bearings ? parameters.bearings[i] : boost::none,
use_approaches && parameters.approaches[i] ? parameters.approaches[i].get()
: engine::Approach::UNRESTRICTED);
@ -279,7 +283,7 @@ class BasePlugin
alternatives[i] = facade.NearestCandidatesWithAlternativeFromBigComponent(
parameters.coordinates[i],
use_radiuses ? parameters.radiuses[i] : boost::none,
use_radiuses ? parameters.radiuses[i] : default_radius,
use_bearings ? parameters.bearings[i] : boost::none,
use_approaches && parameters.approaches[i] ? parameters.approaches[i].get()
: engine::Approach::UNRESTRICTED,
@ -320,6 +324,8 @@ class BasePlugin
return std::string("Could not find a matching segment for coordinate ") +
std::to_string(missing_index);
}
const boost::optional<double> default_radius;
};
} // namespace osrm::engine::plugins

View File

@ -14,7 +14,8 @@ namespace osrm::engine::plugins
class TablePlugin final : public BasePlugin
{
public:
explicit TablePlugin(const int max_locations_distance_table);
explicit TablePlugin(const int max_locations_distance_table,
const boost::optional<double> default_radius);
Status HandleRequest(const RoutingAlgorithmsInterface &algorithms,
const api::TableParameters &params,

View File

@ -32,7 +32,10 @@ class TripPlugin final : public BasePlugin
const bool roundtrip) const;
public:
explicit TripPlugin(const int max_locations_trip_) : max_locations_trip(max_locations_trip_) {}
explicit TripPlugin(const int max_locations_trip_, boost::optional<double> default_radius)
: BasePlugin(default_radius), max_locations_trip(max_locations_trip_)
{
}
Status HandleRequest(const RoutingAlgorithmsInterface &algorithms,
const api::TripParameters &parameters,

View File

@ -25,7 +25,9 @@ class ViaRoutePlugin final : public BasePlugin
const int max_alternatives;
public:
explicit ViaRoutePlugin(int max_locations_viaroute, int max_alternatives);
explicit ViaRoutePlugin(int max_locations_viaroute,
int max_alternatives,
boost::optional<double> default_radius);
Status HandleRequest(const RoutingAlgorithmsInterface &algorithms,
const api::RouteParameters &route_parameters,

View File

@ -41,10 +41,10 @@ struct LuaScriptingContext final
bool has_way_function = false;
bool has_segment_function = false;
sol::function turn_function;
sol::function way_function;
sol::function node_function;
sol::function segment_function;
sol::protected_function turn_function;
sol::protected_function way_function;
sol::protected_function node_function;
sol::protected_function segment_function;
int api_version = 4;
sol::table profile_table;

View File

@ -285,6 +285,7 @@ inline engine_config_ptr argumentsToEngineConfig(const Napi::CallbackInfo &args)
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())
{
@ -316,6 +317,11 @@ inline engine_config_ptr argumentsToEngineConfig(const Napi::CallbackInfo &args)
ThrowError(args.Env(), "max_alternatives must be an integral number");
return engine_config_ptr();
}
if (!default_radius.IsUndefined() && !default_radius.IsNumber())
{
ThrowError(args.Env(), "default_radius must be an integral number");
return engine_config_ptr();
}
if (max_locations_trip.IsNumber())
engine_config->max_locations_trip = max_locations_trip.ToNumber().Int32Value();
@ -333,6 +339,8 @@ inline engine_config_ptr argumentsToEngineConfig(const Napi::CallbackInfo &args)
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();
if (default_radius.IsNumber())
engine_config->default_radius = default_radius.ToNumber().DoubleValue();
return engine_config;
}

View File

@ -79,10 +79,10 @@ template <unsigned BLOCK_SIZE, storage::Ownership Ownership> class RangeTable
unsigned last_length = 0;
unsigned lengths_prefix_sum = 0;
unsigned block_idx = 0;
unsigned block_counter = 0;
BlockT block;
#ifndef BOOST_ASSERT_IS_VOID
unsigned block_sum = 0;
unsigned block_counter = 0;
#endif
for (const unsigned l : lengths)
{
@ -109,7 +109,9 @@ template <unsigned BLOCK_SIZE, storage::Ownership Ownership> class RangeTable
if (BLOCK_SIZE == block_idx)
{
diff_blocks.push_back(block);
#ifndef BOOST_ASSERT_IS_VOID
block_counter++;
#endif
}
// we can only store strings with length 255

View File

@ -6,3 +6,12 @@
# #1 0x7f7ae595d13e (/usr/lib/x86_64-linux-gnu/libtbb.so.2+0x2213e)
leak:libtbb.so
# The extract-tests leak some memory in the tests to confirm that
# lua errors print tracebacks.
# This appears to be because when these tests throw exceptions, the
# osmium objects being processed are not freed. In production this doesn't
# matter, as the exceptions bring down the entire osrm-extract process. In the
# tests, we catch the error to make sure it occurs, which is why the
# leaksanitizer flags it.
leak:extract-tests

View File

@ -181,24 +181,29 @@ Status MatchPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
if (tidied.parameters.radiuses.empty())
{
search_radiuses.resize(tidied.parameters.coordinates.size(),
routing_algorithms::DEFAULT_GPS_PRECISION * RADIUS_MULTIPLIER);
default_radius.has_value()
? *default_radius
: routing_algorithms::DEFAULT_GPS_PRECISION * RADIUS_MULTIPLIER);
}
else
{
search_radiuses.resize(tidied.parameters.coordinates.size());
std::transform(tidied.parameters.radiuses.begin(),
tidied.parameters.radiuses.end(),
search_radiuses.begin(),
[](const boost::optional<double> &maybe_radius) {
if (maybe_radius)
{
return *maybe_radius * RADIUS_MULTIPLIER;
}
else
{
return routing_algorithms::DEFAULT_GPS_PRECISION * RADIUS_MULTIPLIER;
}
});
std::transform(
tidied.parameters.radiuses.begin(),
tidied.parameters.radiuses.end(),
search_radiuses.begin(),
[default_radius = this->default_radius](const boost::optional<double> &maybe_radius) {
if (maybe_radius)
{
return *maybe_radius * RADIUS_MULTIPLIER;
}
else
{
return default_radius.has_value()
? *default_radius
: routing_algorithms::DEFAULT_GPS_PRECISION * RADIUS_MULTIPLIER;
}
});
}
auto candidates_lists =

View File

@ -10,7 +10,10 @@
namespace osrm::engine::plugins
{
NearestPlugin::NearestPlugin(const int max_results_) : max_results{max_results_} {}
NearestPlugin::NearestPlugin(const int max_results_, const boost::optional<double> default_radius_)
: BasePlugin(default_radius_), max_results{max_results_}
{
}
Status NearestPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
const api::NearestParameters &params,

View File

@ -14,8 +14,9 @@
namespace osrm::engine::plugins
{
TablePlugin::TablePlugin(const int max_locations_distance_table)
: max_locations_distance_table(max_locations_distance_table)
TablePlugin::TablePlugin(const int max_locations_distance_table,
const boost::optional<double> default_radius)
: BasePlugin(default_radius), max_locations_distance_table(max_locations_distance_table)
{
}

View File

@ -15,8 +15,11 @@
namespace osrm::engine::plugins
{
ViaRoutePlugin::ViaRoutePlugin(int max_locations_viaroute, int max_alternatives)
: max_locations_viaroute(max_locations_viaroute), max_alternatives(max_alternatives)
ViaRoutePlugin::ViaRoutePlugin(int max_locations_viaroute,
int max_alternatives,
boost::optional<double> default_radius)
: BasePlugin(default_radius), max_locations_viaroute(max_locations_viaroute),
max_alternatives(max_alternatives)
{
}

View File

@ -91,6 +91,19 @@ struct to_lua_object : public boost::static_visitor<sol::object>
};
} // namespace
// Handle a lua error thrown in a protected function by printing the traceback and bubbling
// exception up to caller. Lua errors are generally unrecoverable, so this exception should not be
// caught but instead should terminate the process. The point of having this error handler rather
// than just using unprotected Lua functions which terminate the process automatically is that this
// function provides more useful error messages including Lua tracebacks and line numbers.
void handle_lua_error(sol::protected_function_result &luares)
{
sol::error luaerr = luares;
std::string msg = luaerr.what();
std::cerr << msg << std::endl;
throw util::exception("Lua error (see stderr for traceback)");
}
Sol2ScriptingEnvironment::Sol2ScriptingEnvironment(
const std::string &file_name,
const std::vector<boost::filesystem::path> &location_dependent_data_paths)
@ -550,10 +563,16 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context)
std::numeric_limits<TurnPenalty::value_type>::max());
// call initialize function
sol::function setup_function = function_table.value()["setup"];
sol::protected_function setup_function = function_table.value()["setup"];
if (!setup_function.valid())
throw util::exception("Profile must have an setup() function.");
sol::optional<sol::table> profile_table = setup_function();
auto setup_result = setup_function();
if (!setup_result.valid())
handle_lua_error(setup_result);
sol::optional<sol::table> profile_table = setup_result;
if (profile_table == sol::nullopt)
throw util::exception("Profile setup() must return a table.");
else
@ -1123,7 +1142,9 @@ void Sol2ScriptingEnvironment::ProcessTurn(ExtractionTurn &turn)
case 2:
if (context.has_turn_penalty_function)
{
context.turn_function(context.profile_table, std::ref(turn));
auto luares = context.turn_function(context.profile_table, std::ref(turn));
if (!luares.valid())
handle_lua_error(luares);
// Turn weight falls back to the duration value in deciseconds
// or uses the extracted unit-less weight value
@ -1138,7 +1159,9 @@ void Sol2ScriptingEnvironment::ProcessTurn(ExtractionTurn &turn)
case 1:
if (context.has_turn_penalty_function)
{
context.turn_function(std::ref(turn));
auto luares = context.turn_function(std::ref(turn));
if (!luares.valid())
handle_lua_error(luares);
// Turn weight falls back to the duration value in deciseconds
// or uses the extracted unit-less weight value
@ -1184,24 +1207,28 @@ void Sol2ScriptingEnvironment::ProcessSegment(ExtractionSegment &segment)
if (context.has_segment_function)
{
sol::protected_function_result luares;
switch (context.api_version)
{
case 4:
case 3:
case 2:
context.segment_function(context.profile_table, std::ref(segment));
luares = context.segment_function(context.profile_table, std::ref(segment));
break;
case 1:
context.segment_function(std::ref(segment));
luares = context.segment_function(std::ref(segment));
break;
case 0:
context.segment_function(std::ref(segment.source),
std::ref(segment.target),
segment.distance,
segment.duration);
luares = context.segment_function(std::ref(segment.source),
std::ref(segment.target),
segment.distance,
segment.duration);
segment.weight = segment.duration; // back-compatibility fallback to duration
break;
}
if (!luares.valid())
handle_lua_error(luares);
}
}
@ -1211,20 +1238,27 @@ void LuaScriptingContext::ProcessNode(const osmium::Node &node,
{
BOOST_ASSERT(state.lua_state() != nullptr);
sol::protected_function_result luares;
// TODO check for api version, make sure luares is always set
switch (api_version)
{
case 4:
case 3:
node_function(profile_table, std::cref(node), std::ref(result), std::cref(relations));
luares =
node_function(profile_table, std::cref(node), std::ref(result), std::cref(relations));
break;
case 2:
node_function(profile_table, std::cref(node), std::ref(result));
luares = node_function(profile_table, std::cref(node), std::ref(result));
break;
case 1:
case 0:
node_function(std::cref(node), std::ref(result));
luares = node_function(std::cref(node), std::ref(result));
break;
}
if (!luares.valid())
handle_lua_error(luares);
}
void LuaScriptingContext::ProcessWay(const osmium::Way &way,
@ -1233,20 +1267,27 @@ void LuaScriptingContext::ProcessWay(const osmium::Way &way,
{
BOOST_ASSERT(state.lua_state() != nullptr);
sol::protected_function_result luares;
// TODO check for api version, make sure luares is always set
switch (api_version)
{
case 4:
case 3:
way_function(profile_table, std::cref(way), std::ref(result), std::cref(relations));
luares =
way_function(profile_table, std::cref(way), std::ref(result), std::cref(relations));
break;
case 2:
way_function(profile_table, std::cref(way), std::ref(result));
luares = way_function(profile_table, std::cref(way), std::ref(result));
break;
case 1:
case 0:
way_function(std::cref(way), std::ref(result));
luares = way_function(std::cref(way), std::ref(result));
break;
}
if (!luares.valid())
handle_lua_error(luares);
}
} // namespace osrm::extractor

View File

@ -81,6 +81,7 @@ Napi::Object Engine::Init(Napi::Env env, Napi::Object exports)
* @param {Number} [options.max_radius_map_matching] Max. radius size supported in map matching query (default: 5).
* @param {Number} [options.max_results_nearest] Max. results supported in nearest query (default: unlimited).
* @param {Number} [options.max_alternatives] Max. number of alternatives supported in alternative routes query (default: 3).
* @param {Number} [options.default_radius] Default radius for queries (default: unlimited).
*
* @class OSRM
*

View File

@ -147,7 +147,10 @@ inline unsigned generateServerProgramOptions(const int argc,
"Max. number of alternatives supported in the MLD route query") //
("max-matching-radius",
value<double>(&config.max_radius_map_matching)->default_value(-1.0),
"Max. radius size supported in map matching query. Default: unlimited.");
"Max. radius size supported in map matching query. Default: unlimited.") //
("default-radius",
value<boost::optional<double>>(&config.default_radius),
"Default radius size for queries. Default: unlimited.");
// hidden options, will be allowed on command line, but will not be shown to the user
boost::program_options::options_description hidden_options("Hidden options");

View File

@ -0,0 +1,140 @@
-- copy of testbot with process_node throwing a runtime error
api_version = 4
function setup()
return {
properties = {
continue_straight_at_waypoint = true,
max_speed_for_map_matching = 30/3.6, --km -> m/s
weight_name = 'duration',
process_call_tagless_node = false,
u_turn_penalty = 20,
traffic_light_penalty = 7, -- seconds
use_turn_restrictions = true
},
classes = {"motorway", "toll", "TooWords2"},
excludable = {
{["motorway"] = true},
{["toll"] = true},
{["motorway"] = true, ["toll"] = true}
},
default_speed = 24,
speeds = {
primary = 36,
secondary = 18,
tertiary = 12,
steps = 6
}
}
end
function process_node (profile, node, result)
if (2 < nil) then
print("2 is less than nil")
end
-- check if node is a traffic light
-- TODO: a way to set the penalty value
end
function process_way (profile, way, result)
local highway = way:get_value_by_key("highway")
local toll = way:get_value_by_key("toll")
local name = way:get_value_by_key("name")
local oneway = way:get_value_by_key("oneway")
local route = way:get_value_by_key("route")
local duration = way:get_value_by_key("duration")
local maxspeed = tonumber(way:get_value_by_key ( "maxspeed"))
local maxspeed_forward = tonumber(way:get_value_by_key( "maxspeed:forward"))
local maxspeed_backward = tonumber(way:get_value_by_key( "maxspeed:backward"))
local junction = way:get_value_by_key("junction")
if name then
result.name = name
end
result.forward_mode = mode.driving
result.backward_mode = mode.driving
if duration and durationIsValid(duration) then
result.duration = math.max( 1, parseDuration(duration) )
result.forward_mode = mode.route
result.backward_mode = mode.route
else
local speed_forw = profile.speeds[highway] or profile.default_speed
local speed_back = speed_forw
if highway == "river" then
local temp_speed = speed_forw
result.forward_mode = mode.river_down
result.backward_mode = mode.river_up
speed_forw = temp_speed*1.5
speed_back = temp_speed/1.5
elseif highway == "steps" then
result.forward_mode = mode.steps_down
result.backward_mode = mode.steps_up
end
if maxspeed_forward ~= nil and maxspeed_forward > 0 then
speed_forw = maxspeed_forward
else
if maxspeed ~= nil and maxspeed > 0 and speed_forw > maxspeed then
speed_forw = maxspeed
end
end
if maxspeed_backward ~= nil and maxspeed_backward > 0 then
speed_back = maxspeed_backward
else
if maxspeed ~=nil and maxspeed > 0 and speed_back > maxspeed then
speed_back = maxspeed
end
end
result.forward_speed = speed_forw
result.backward_speed = speed_back
end
if oneway == "no" or oneway == "0" or oneway == "false" then
-- nothing to do
elseif oneway == "-1" then
result.forward_mode = mode.inaccessible
elseif oneway == "yes" or oneway == "1" or oneway == "true" or junction == "roundabout" then
result.backward_mode = mode.inaccessible
end
if highway == 'motorway' then
result.forward_classes["motorway"] = true
result.backward_classes["motorway"] = true
end
if toll == "yes" then
result.forward_classes["toll"] = true
result.backward_classes["toll"] = true
end
if junction == 'roundabout' then
result.roundabout = true
end
end
function process_turn (profile, turn)
if turn.is_u_turn then
turn.duration = turn.duration + profile.properties.u_turn_penalty
turn.weight = turn.weight + profile.properties.u_turn_penalty
end
if turn.has_traffic_light then
turn.duration = turn.duration + profile.properties.traffic_light_penalty
end
end
return {
setup = setup,
process_way = process_way,
process_node = process_node,
process_turn = process_turn
}

View File

@ -0,0 +1,143 @@
-- A copy of testbot with process_segment throwing a runtime error
api_version = 4
function setup()
return {
properties = {
continue_straight_at_waypoint = true,
max_speed_for_map_matching = 30/3.6, --km -> m/s
weight_name = 'duration',
process_call_tagless_node = false,
u_turn_penalty = 20,
traffic_light_penalty = 7, -- seconds
use_turn_restrictions = true
},
classes = {"motorway", "toll", "TooWords2"},
excludable = {
{["motorway"] = true},
{["toll"] = true},
{["motorway"] = true, ["toll"] = true}
},
default_speed = 24,
speeds = {
primary = 36,
secondary = 18,
tertiary = 12,
steps = 6
}
}
end
function process_node (profile, node, result)
-- check if node is a traffic light
-- TODO: a way to set the penalty value
end
function process_way (profile, way, result)
local highway = way:get_value_by_key("highway")
local toll = way:get_value_by_key("toll")
local name = way:get_value_by_key("name")
local oneway = way:get_value_by_key("oneway")
local route = way:get_value_by_key("route")
local duration = way:get_value_by_key("duration")
local maxspeed = tonumber(way:get_value_by_key ( "maxspeed"))
local maxspeed_forward = tonumber(way:get_value_by_key( "maxspeed:forward"))
local maxspeed_backward = tonumber(way:get_value_by_key( "maxspeed:backward"))
local junction = way:get_value_by_key("junction")
if name then
result.name = name
end
result.forward_mode = mode.driving
result.backward_mode = mode.driving
if duration and durationIsValid(duration) then
result.duration = math.max( 1, parseDuration(duration) )
result.forward_mode = mode.route
result.backward_mode = mode.route
else
local speed_forw = profile.speeds[highway] or profile.default_speed
local speed_back = speed_forw
if highway == "river" then
local temp_speed = speed_forw
result.forward_mode = mode.river_down
result.backward_mode = mode.river_up
speed_forw = temp_speed*1.5
speed_back = temp_speed/1.5
elseif highway == "steps" then
result.forward_mode = mode.steps_down
result.backward_mode = mode.steps_up
end
if maxspeed_forward ~= nil and maxspeed_forward > 0 then
speed_forw = maxspeed_forward
else
if maxspeed ~= nil and maxspeed > 0 and speed_forw > maxspeed then
speed_forw = maxspeed
end
end
if maxspeed_backward ~= nil and maxspeed_backward > 0 then
speed_back = maxspeed_backward
else
if maxspeed ~=nil and maxspeed > 0 and speed_back > maxspeed then
speed_back = maxspeed
end
end
result.forward_speed = speed_forw
result.backward_speed = speed_back
end
if oneway == "no" or oneway == "0" or oneway == "false" then
-- nothing to do
elseif oneway == "-1" then
result.forward_mode = mode.inaccessible
elseif oneway == "yes" or oneway == "1" or oneway == "true" or junction == "roundabout" then
result.backward_mode = mode.inaccessible
end
if highway == 'motorway' then
result.forward_classes["motorway"] = true
result.backward_classes["motorway"] = true
end
if toll == "yes" then
result.forward_classes["toll"] = true
result.backward_classes["toll"] = true
end
if junction == 'roundabout' then
result.roundabout = true
end
end
function process_turn (profile, turn)
if turn.is_u_turn then
turn.duration = turn.duration + profile.properties.u_turn_penalty
turn.weight = turn.weight + profile.properties.u_turn_penalty
end
if turn.has_traffic_light then
turn.duration = turn.duration + profile.properties.traffic_light_penalty
end
end
function process_segment (profile, segment)
if (2 < nil) then
print("2 is less than nil")
end
end
return {
setup = setup,
process_way = process_way,
process_node = process_node,
process_turn = process_turn,
process_segment = process_segment
}

View File

@ -0,0 +1,140 @@
-- Copy of testbot profile, with setup throwing a runtime error
api_version = 4
function setup()
if (2 < nil) then -- arithmetic with nil should error
return {}
end
return {
properties = {
continue_straight_at_waypoint = true,
max_speed_for_map_matching = 30/3.6, --km -> m/s
weight_name = 'duration',
process_call_tagless_node = false,
u_turn_penalty = 20,
traffic_light_penalty = 7, -- seconds
use_turn_restrictions = true
},
classes = {"motorway", "toll", "TooWords2"},
excludable = {
{["motorway"] = true},
{["toll"] = true},
{["motorway"] = true, ["toll"] = true}
},
default_speed = 24,
speeds = {
primary = 36,
secondary = 18,
tertiary = 12,
steps = 6
}
}
end
function process_node (profile, node, result)
-- check if node is a traffic light
-- TODO: a way to set the penalty value
end
function process_way (profile, way, result)
local highway = way:get_value_by_key("highway")
local toll = way:get_value_by_key("toll")
local name = way:get_value_by_key("name")
local oneway = way:get_value_by_key("oneway")
local route = way:get_value_by_key("route")
local duration = way:get_value_by_key("duration")
local maxspeed = tonumber(way:get_value_by_key ( "maxspeed"))
local maxspeed_forward = tonumber(way:get_value_by_key( "maxspeed:forward"))
local maxspeed_backward = tonumber(way:get_value_by_key( "maxspeed:backward"))
local junction = way:get_value_by_key("junction")
if name then
result.name = name
end
result.forward_mode = mode.driving
result.backward_mode = mode.driving
if duration and durationIsValid(duration) then
result.duration = math.max( 1, parseDuration(duration) )
result.forward_mode = mode.route
result.backward_mode = mode.route
else
local speed_forw = profile.speeds[highway] or profile.default_speed
local speed_back = speed_forw
if highway == "river" then
local temp_speed = speed_forw
result.forward_mode = mode.river_down
result.backward_mode = mode.river_up
speed_forw = temp_speed*1.5
speed_back = temp_speed/1.5
elseif highway == "steps" then
result.forward_mode = mode.steps_down
result.backward_mode = mode.steps_up
end
if maxspeed_forward ~= nil and maxspeed_forward > 0 then
speed_forw = maxspeed_forward
else
if maxspeed ~= nil and maxspeed > 0 and speed_forw > maxspeed then
speed_forw = maxspeed
end
end
if maxspeed_backward ~= nil and maxspeed_backward > 0 then
speed_back = maxspeed_backward
else
if maxspeed ~=nil and maxspeed > 0 and speed_back > maxspeed then
speed_back = maxspeed
end
end
result.forward_speed = speed_forw
result.backward_speed = speed_back
end
if oneway == "no" or oneway == "0" or oneway == "false" then
-- nothing to do
elseif oneway == "-1" then
result.forward_mode = mode.inaccessible
elseif oneway == "yes" or oneway == "1" or oneway == "true" or junction == "roundabout" then
result.backward_mode = mode.inaccessible
end
if highway == 'motorway' then
result.forward_classes["motorway"] = true
result.backward_classes["motorway"] = true
end
if toll == "yes" then
result.forward_classes["toll"] = true
result.backward_classes["toll"] = true
end
if junction == 'roundabout' then
result.roundabout = true
end
end
function process_turn (profile, turn)
if turn.is_u_turn then
turn.duration = turn.duration + profile.properties.u_turn_penalty
turn.weight = turn.weight + profile.properties.u_turn_penalty
end
if turn.has_traffic_light then
turn.duration = turn.duration + profile.properties.traffic_light_penalty
end
end
return {
setup = setup,
process_way = process_way,
process_node = process_node,
process_turn = process_turn
}

View File

@ -0,0 +1,140 @@
-- a copy of testbot with process_turn throwing an error
api_version = 4
function setup()
return {
properties = {
continue_straight_at_waypoint = true,
max_speed_for_map_matching = 30/3.6, --km -> m/s
weight_name = 'duration',
process_call_tagless_node = false,
u_turn_penalty = 20,
traffic_light_penalty = 7, -- seconds
use_turn_restrictions = true
},
classes = {"motorway", "toll", "TooWords2"},
excludable = {
{["motorway"] = true},
{["toll"] = true},
{["motorway"] = true, ["toll"] = true}
},
default_speed = 24,
speeds = {
primary = 36,
secondary = 18,
tertiary = 12,
steps = 6
}
}
end
function process_node (profile, node, result)
-- check if node is a traffic light
-- TODO: a way to set the penalty value
end
function process_way (profile, way, result)
local highway = way:get_value_by_key("highway")
local toll = way:get_value_by_key("toll")
local name = way:get_value_by_key("name")
local oneway = way:get_value_by_key("oneway")
local route = way:get_value_by_key("route")
local duration = way:get_value_by_key("duration")
local maxspeed = tonumber(way:get_value_by_key ( "maxspeed"))
local maxspeed_forward = tonumber(way:get_value_by_key( "maxspeed:forward"))
local maxspeed_backward = tonumber(way:get_value_by_key( "maxspeed:backward"))
local junction = way:get_value_by_key("junction")
if name then
result.name = name
end
result.forward_mode = mode.driving
result.backward_mode = mode.driving
if duration and durationIsValid(duration) then
result.duration = math.max( 1, parseDuration(duration) )
result.forward_mode = mode.route
result.backward_mode = mode.route
else
local speed_forw = profile.speeds[highway] or profile.default_speed
local speed_back = speed_forw
if highway == "river" then
local temp_speed = speed_forw
result.forward_mode = mode.river_down
result.backward_mode = mode.river_up
speed_forw = temp_speed*1.5
speed_back = temp_speed/1.5
elseif highway == "steps" then
result.forward_mode = mode.steps_down
result.backward_mode = mode.steps_up
end
if maxspeed_forward ~= nil and maxspeed_forward > 0 then
speed_forw = maxspeed_forward
else
if maxspeed ~= nil and maxspeed > 0 and speed_forw > maxspeed then
speed_forw = maxspeed
end
end
if maxspeed_backward ~= nil and maxspeed_backward > 0 then
speed_back = maxspeed_backward
else
if maxspeed ~=nil and maxspeed > 0 and speed_back > maxspeed then
speed_back = maxspeed
end
end
result.forward_speed = speed_forw
result.backward_speed = speed_back
end
if oneway == "no" or oneway == "0" or oneway == "false" then
-- nothing to do
elseif oneway == "-1" then
result.forward_mode = mode.inaccessible
elseif oneway == "yes" or oneway == "1" or oneway == "true" or junction == "roundabout" then
result.backward_mode = mode.inaccessible
end
if highway == 'motorway' then
result.forward_classes["motorway"] = true
result.backward_classes["motorway"] = true
end
if toll == "yes" then
result.forward_classes["toll"] = true
result.backward_classes["toll"] = true
end
if junction == 'roundabout' then
result.roundabout = true
end
end
function process_turn (profile, turn)
if (2 < nil) then
print("2 is less than nil")
end
if turn.is_u_turn then
turn.duration = turn.duration + profile.properties.u_turn_penalty
turn.weight = turn.weight + profile.properties.u_turn_penalty
end
if turn.has_traffic_light then
turn.duration = turn.duration + profile.properties.traffic_light_penalty
end
end
return {
setup = setup,
process_way = process_way,
process_node = process_node,
process_turn = process_turn
}

View File

@ -0,0 +1,140 @@
-- copy of testbot with process_way throwing a runtime error
api_version = 4
function setup()
return {
properties = {
continue_straight_at_waypoint = true,
max_speed_for_map_matching = 30/3.6, --km -> m/s
weight_name = 'duration',
process_call_tagless_node = false,
u_turn_penalty = 20,
traffic_light_penalty = 7, -- seconds
use_turn_restrictions = true
},
classes = {"motorway", "toll", "TooWords2"},
excludable = {
{["motorway"] = true},
{["toll"] = true},
{["motorway"] = true, ["toll"] = true}
},
default_speed = 24,
speeds = {
primary = 36,
secondary = 18,
tertiary = 12,
steps = 6
}
}
end
function process_node (profile, node, result)
-- check if node is a traffic light
-- TODO: a way to set the penalty value
end
function process_way (profile, way, result)
if (2 < nil) then
print("2 less than nil")
end
local highway = way:get_value_by_key("highway")
local toll = way:get_value_by_key("toll")
local name = way:get_value_by_key("name")
local oneway = way:get_value_by_key("oneway")
local route = way:get_value_by_key("route")
local duration = way:get_value_by_key("duration")
local maxspeed = tonumber(way:get_value_by_key ( "maxspeed"))
local maxspeed_forward = tonumber(way:get_value_by_key( "maxspeed:forward"))
local maxspeed_backward = tonumber(way:get_value_by_key( "maxspeed:backward"))
local junction = way:get_value_by_key("junction")
if name then
result.name = name
end
result.forward_mode = mode.driving
result.backward_mode = mode.driving
if duration and durationIsValid(duration) then
result.duration = math.max( 1, parseDuration(duration) )
result.forward_mode = mode.route
result.backward_mode = mode.route
else
local speed_forw = profile.speeds[highway] or profile.default_speed
local speed_back = speed_forw
if highway == "river" then
local temp_speed = speed_forw
result.forward_mode = mode.river_down
result.backward_mode = mode.river_up
speed_forw = temp_speed*1.5
speed_back = temp_speed/1.5
elseif highway == "steps" then
result.forward_mode = mode.steps_down
result.backward_mode = mode.steps_up
end
if maxspeed_forward ~= nil and maxspeed_forward > 0 then
speed_forw = maxspeed_forward
else
if maxspeed ~= nil and maxspeed > 0 and speed_forw > maxspeed then
speed_forw = maxspeed
end
end
if maxspeed_backward ~= nil and maxspeed_backward > 0 then
speed_back = maxspeed_backward
else
if maxspeed ~=nil and maxspeed > 0 and speed_back > maxspeed then
speed_back = maxspeed
end
end
result.forward_speed = speed_forw
result.backward_speed = speed_back
end
if oneway == "no" or oneway == "0" or oneway == "false" then
-- nothing to do
elseif oneway == "-1" then
result.forward_mode = mode.inaccessible
elseif oneway == "yes" or oneway == "1" or oneway == "true" or junction == "roundabout" then
result.backward_mode = mode.inaccessible
end
if highway == 'motorway' then
result.forward_classes["motorway"] = true
result.backward_classes["motorway"] = true
end
if toll == "yes" then
result.forward_classes["toll"] = true
result.backward_classes["toll"] = true
end
if junction == 'roundabout' then
result.roundabout = true
end
end
function process_turn (profile, turn)
if turn.is_u_turn then
turn.duration = turn.duration + profile.properties.u_turn_penalty
turn.weight = turn.weight + profile.properties.u_turn_penalty
end
if turn.has_traffic_light then
turn.duration = turn.duration + profile.properties.traffic_light_penalty
end
end
return {
setup = setup,
process_way = process_way,
process_node = process_node,
process_turn = process_turn
}

View File

@ -112,6 +112,18 @@ test('constructor: throws if dataset_name is not a string', function(assert) {
assert.throws(function() { new OSRM({dataset_name: "unsued_name___", shared_memory: true}); }, /Could not find shared memory region/, 'Does not accept wrong name');
});
test('constructor: takes a default_radius argument', function(assert) {
assert.plan(1);
var osrm = new OSRM({algorithm: 'MLD', path: monaco_mld_path, default_radius: 1});
assert.ok(osrm);
});
test('constructor: throws if default_radius is not a number', function(assert) {
assert.plan(2);
assert.throws(function() { new OSRM({algorithm: 'MLD', path: monaco_mld_path, default_radius: 'abc'}); }, /default_radius must be an integral number/, 'Does not accept string');
assert.ok(new OSRM({algorithm: 'MLD', path: monaco_mld_path, default_radius: 1}), 'Does accept number');
});
test('constructor: parses custom limits', function(assert) {
assert.plan(1);
var osrm = new OSRM({

View File

@ -1,10 +1,32 @@
#include <boost/test/unit_test.hpp>
#include "osrm/exception.hpp"
#include "osrm/extractor.hpp"
#include "osrm/extractor_config.hpp"
#include <boost/algorithm/string.hpp>
#include <thread>
// utility class to redirect stderr so we can test it
// inspired by https://stackoverflow.com/questions/5405016
class redirect_stderr
{
// constructor: accept a pointer to a buffer where stderr will be redirected
public:
redirect_stderr(std::streambuf *buf)
// store the original buffer for later (original buffer returned by rdbuf)
: old(std::cerr.rdbuf(buf))
{
}
// destructor: restore the original cerr, regardless of how this class gets destroyed
~redirect_stderr() { std::cerr.rdbuf(old); }
// place to store the buffer
private:
std::streambuf *old;
};
BOOST_AUTO_TEST_SUITE(library_extract)
BOOST_AUTO_TEST_CASE(test_extract_with_invalid_config)
@ -26,4 +48,114 @@ BOOST_AUTO_TEST_CASE(test_extract_with_valid_config)
BOOST_CHECK_NO_THROW(osrm::extract(config));
}
BOOST_AUTO_TEST_CASE(test_setup_runtime_error)
{
osrm::ExtractorConfig config;
config.input_path = OSRM_TEST_DATA_DIR "/monaco.osm.pbf";
config.UseDefaultOutputNames(OSRM_TEST_DATA_DIR "/monaco.osm.pbf");
config.profile_path = OSRM_TEST_DATA_DIR "/profiles/bad_setup.lua";
config.small_component_size = 1000;
config.requested_num_threads = std::thread::hardware_concurrency();
std::stringstream output;
{
redirect_stderr redir(output.rdbuf());
BOOST_CHECK_THROW(osrm::extract(config), osrm::util::exception);
}
// We just look for the line number, file name, and error message. This avoids portability
// issues since the output contains the full path to the file, which may change between systems
BOOST_CHECK(boost::algorithm::contains(output.str(),
"bad_setup.lua:6: attempt to compare number with nil"));
}
BOOST_AUTO_TEST_CASE(test_way_runtime_error)
{
osrm::ExtractorConfig config;
config.input_path = OSRM_TEST_DATA_DIR "/monaco.osm.pbf";
config.UseDefaultOutputNames(OSRM_TEST_DATA_DIR "/monaco.osm.pbf");
config.profile_path = OSRM_TEST_DATA_DIR "/profiles/bad_way.lua";
config.small_component_size = 1000;
config.requested_num_threads = std::thread::hardware_concurrency();
std::stringstream output;
{
redirect_stderr redir(output.rdbuf());
BOOST_CHECK_THROW(osrm::extract(config), osrm::util::exception);
}
// We just look for the line number, file name, and error message. This avoids portability
// issues since the output contains the full path to the file, which may change between systems
BOOST_CHECK(boost::algorithm::contains(output.str(),
"bad_way.lua:41: attempt to compare number with nil"));
}
BOOST_AUTO_TEST_CASE(test_node_runtime_error)
{
osrm::ExtractorConfig config;
config.input_path = OSRM_TEST_DATA_DIR "/monaco.osm.pbf";
config.UseDefaultOutputNames(OSRM_TEST_DATA_DIR "/monaco.osm.pbf");
config.profile_path = OSRM_TEST_DATA_DIR "/profiles/bad_node.lua";
config.small_component_size = 1000;
config.requested_num_threads = std::thread::hardware_concurrency();
std::stringstream output;
{
redirect_stderr redir(output.rdbuf());
BOOST_CHECK_THROW(osrm::extract(config), osrm::util::exception);
}
// We just look for the line number, file name, and error message. This avoids portability
// issues since the output contains the full path to the file, which may change between systems
BOOST_CHECK(boost::algorithm::contains(output.str(),
"bad_node.lua:36: attempt to compare number with nil"));
}
BOOST_AUTO_TEST_CASE(test_segment_runtime_error)
{
osrm::ExtractorConfig config;
config.input_path = OSRM_TEST_DATA_DIR "/monaco.osm.pbf";
config.UseDefaultOutputNames(OSRM_TEST_DATA_DIR "/monaco.osm.pbf");
config.profile_path = OSRM_TEST_DATA_DIR "/profiles/bad_segment.lua";
config.small_component_size = 1000;
config.requested_num_threads = std::thread::hardware_concurrency();
std::stringstream output;
{
redirect_stderr redir(output.rdbuf());
BOOST_CHECK_THROW(osrm::extract(config), osrm::util::exception);
}
// We just look for the line number, file name, and error message. This avoids portability
// issues since the output contains the full path to the file, which may change between systems
BOOST_CHECK(boost::algorithm::contains(
output.str(), "bad_segment.lua:132: attempt to compare number with nil"));
}
BOOST_AUTO_TEST_CASE(test_turn_runtime_error)
{
osrm::ExtractorConfig config;
config.input_path = OSRM_TEST_DATA_DIR "/monaco.osm.pbf";
config.UseDefaultOutputNames(OSRM_TEST_DATA_DIR "/monaco.osm.pbf");
config.profile_path = OSRM_TEST_DATA_DIR "/profiles/bad_turn.lua";
config.small_component_size = 1000;
config.requested_num_threads = std::thread::hardware_concurrency();
std::stringstream output;
{
redirect_stderr redir(output.rdbuf());
BOOST_CHECK_THROW(osrm::extract(config), osrm::util::exception);
}
// We just look for the line number, file name, and error message. This avoids portability
// issues since the output contains the full path to the file, which may change between systems
BOOST_CHECK(boost::algorithm::contains(output.str(),
"bad_turn.lua:122: attempt to compare number with nil"));
}
BOOST_AUTO_TEST_SUITE_END()