Add a multiplier to the matrix (#5298)

* add a multiplier to the matrix

* add rounding

* remove scale_factor restrictions

* clamp for overflow error

* update check to match error message

* enforce clamping on < 0 and increase test coverage

* add an invalid scale_factor value to node tests

* increase test coverage

* changelog
This commit is contained in:
Kajari Ghosh 2018-12-10 13:41:44 -05:00 committed by GitHub
parent c4238c4ed6
commit 2e17f3010a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 197 additions and 14 deletions

View File

@ -2,6 +2,8 @@
- Changes from 5.20.0 - Changes from 5.20.0
- Features: - Features:
- ADDED: all waypoints in responses now contain a distance property between the original coordinate and the snapped location. [#5255](https://github.com/Project-OSRM/osrm-backend/pull/5255) - ADDED: all waypoints in responses now contain a distance property between the original coordinate and the snapped location. [#5255](https://github.com/Project-OSRM/osrm-backend/pull/5255)
- Table:
- ADDED: new parameter `scale_factor` which will scale the cell `duration` values by this factor. [#5298](https://github.com/Project-OSRM/osrm-backend/pull/5298)
# 5.20.0 # 5.20.0
- Changes from 5.19.0: - Changes from 5.19.0:

View File

@ -235,9 +235,10 @@ In addition to the [general options](#general-options) the following options are
|------------|--------------------------------------------------|---------------------------------------------| |------------|--------------------------------------------------|---------------------------------------------|
|sources |`{index};{index}[;{index} ...]` or `all` (default)|Use location with given index as source. | |sources |`{index};{index}[;{index} ...]` or `all` (default)|Use location with given index as source. |
|destinations|`{index};{index}[;{index} ...]` or `all` (default)|Use location with given index as destination.| |destinations|`{index};{index}[;{index} ...]` or `all` (default)|Use location with given index as destination.|
|annotations |`duration` (default), `distance`, or `duration,distance`|Return the requested table or tables in response. Note that computing the `distances` table is currently only implemented for CH. If `annotations=distance` or `annotations=duration,distance` is requested when running a MLD router, a `NotImplemented` error will be returned. | |annotations |`duration` (default), `distance`, or `duration,distance`|Return the requested table or tables in response. |
|fallback_speed|`double > 0`|If no route found between a source/destination pair, calculate the as-the-crow-flies distance, then use this speed to estimate duration.| |fallback_speed|`double > 0`| If no route found between a source/destination pair, calculate the as-the-crow-flies distance, then use this speed to estimate duration.|
|fallback_coordinate|`input` (default), or `snapped`| When using a `fallback_speed`, use the user-supplied coordinate (`input`), or the snapped location (`snapped`) for calculating distances.| |fallback_coordinate|`input` (default), or `snapped`| When using a `fallback_speed`, use the user-supplied coordinate (`input`), or the snapped location (`snapped`) for calculating distances.|
|scale_factor|`double > 0`| Use in conjunction with `annotations=durations`. Scales the table `duration` values by this number.|
Unlike other array encoded options, the length of `sources` and `destinations` can be **smaller or equal** Unlike other array encoded options, the length of `sources` and `destinations` can be **smaller or equal**
to number of input locations; to number of input locations;

View File

@ -131,6 +131,7 @@ tables. Optionally returns distance table.
- `options.approaches` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)?** Keep waypoints on curb side. Can be `null` (unrestricted, default) or `curb`. - `options.approaches` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)?** Keep waypoints on curb side. Can be `null` (unrestricted, default) or `curb`.
- `options.fallback_speed` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** Replace `null` responses in result with as-the-crow-flies estimates based on `fallback_speed`. Value is in metres/second. - `options.fallback_speed` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** Replace `null` responses in result with as-the-crow-flies estimates based on `fallback_speed`. Value is in metres/second.
- `options.fallback_coordinate` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** Either `input` (default) or `snapped`. If using a `fallback_speed`, use either the user-supplied coordinate (`input`), or the snapped coordinate (`snapped`) for calculating the as-the-crow-flies diestance between two points. - `options.fallback_coordinate` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** Either `input` (default) or `snapped`. If using a `fallback_speed`, use either the user-supplied coordinate (`input`), or the snapped coordinate (`snapped`) for calculating the as-the-crow-flies diestance between two points.
- `options.scale_factor` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** Multiply the table duration values in the table by this number for more controlled input into a route optimization solver.
- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** - `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)**
**Examples** **Examples**

View File

@ -580,4 +580,96 @@ Feature: Basic Duration Matrix
| a | 0 | 30 | 18 | 24 | | a | 0 | 30 | 18 | 24 |
| b | 30 | 0 | 12 | 18 | | b | 30 | 0 | 12 | 18 |
| f | 18 | 12 | 0 | 30 | | f | 18 | 12 | 0 | 30 |
| 1 | 24 | 18 | 30 | 0 | | 1 | 24 | 18 | 30 | 0 |
Scenario: Testbot - Travel time matrix of minimal network with scale factor
Given the query options
| scale_factor | 2 |
Given the node map
"""
a b
"""
And the ways
| nodes |
| ab |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 20 |
| b | 20 | 0 |
Scenario: Testbot - Test fallback speeds and scale factor
Given a grid size of 300 meters
Given the extract extra arguments "--small-component-size 4"
Given the query options
| scale_factor | 2 |
| fallback_speed | 5 |
| fallback_coordinate | snapped |
Given the node map
"""
a b f h 1
d e g i
"""
And the ways
| nodes |
| abeda |
| fhigf |
When I request a travel time matrix I should get
| | a | b | f | 1 |
| a | 0 | 60 | 36 | 48 |
| b | 60 | 0 | 24 | 36 |
| f | 36 | 24 | 0 | 60 |
| 1 | 48 | 36 | 60 | 0 |
When I request a travel time matrix I should get
| | a | b | f | 1 |
| a | 0 | 60 | 36 | 48 |
When I request a travel time matrix I should get
| | a |
| a | 0 |
| b | 60 |
| f | 36 |
| 1 | 48 |
Scenario: Testbot - Travel time matrix of minimal network with overflow scale factor
Given the query options
| scale_factor | 2147483647 |
Given the node map
"""
a b
"""
And the ways
| nodes |
| ab |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 214748364.6 |
| b | 214748364.6 | 0 |
Scenario: Testbot - Travel time matrix of minimal network with fraction scale factor
Given the query options
| scale_factor | 0.5 |
Given the node map
"""
a b
"""
And the ways
| nodes |
| ab |
When I request a travel time matrix I should get
| | a | b |
| a | 0 | 5 |
| b | 5 | 0 |

View File

@ -79,6 +79,8 @@ struct TableParameters : public BaseParameters
AnnotationsType annotations = AnnotationsType::Duration; AnnotationsType annotations = AnnotationsType::Duration;
double scale_factor = 1;
TableParameters() = default; TableParameters() = default;
template <typename... Args> template <typename... Args>
TableParameters(std::vector<std::size_t> sources_, TableParameters(std::vector<std::size_t> sources_,
@ -105,10 +107,13 @@ struct TableParameters : public BaseParameters
const AnnotationsType annotations_, const AnnotationsType annotations_,
double fallback_speed_, double fallback_speed_,
FallbackCoordinateType fallback_coordinate_type_, FallbackCoordinateType fallback_coordinate_type_,
double scale_factor_,
Args... args_) Args... args_)
: BaseParameters{std::forward<Args>(args_)...}, sources{std::move(sources_)}, : BaseParameters{std::forward<Args>(args_)...}, sources{std::move(sources_)},
destinations{std::move(destinations_)}, fallback_speed{fallback_speed_}, destinations{std::move(destinations_)}, fallback_speed{fallback_speed_},
fallback_coordinate_type{fallback_coordinate_type_}, annotations{annotations_} fallback_coordinate_type{fallback_coordinate_type_}, annotations{annotations_},
scale_factor{scale_factor_}
{ {
} }
@ -135,6 +140,9 @@ struct TableParameters : public BaseParameters
if (fallback_speed < 0) if (fallback_speed < 0)
return false; return false;
if (scale_factor <= 0)
return false;
return true; return true;
} }
}; };

View File

@ -1229,6 +1229,24 @@ argumentsToTableParameter(const Nan::FunctionCallbackInfo<v8::Value> &args,
} }
} }
if (obj->Has(Nan::New("scale_factor").ToLocalChecked()))
{
auto scale_factor = obj->Get(Nan::New("scale_factor").ToLocalChecked());
if (!scale_factor->IsNumber())
{
Nan::ThrowError("scale_factor must be a number");
return table_parameters_ptr();
}
else if (scale_factor->NumberValue() <= 0)
{
Nan::ThrowError("scale_factor must be > 0");
return table_parameters_ptr();
}
params->scale_factor = static_cast<double>(scale_factor->NumberValue());
}
return params; return params;
} }

View File

@ -56,16 +56,20 @@ struct TableParametersGrammar : public BaseParametersGrammar<Iterator, Signature
engine::api::TableParameters::FallbackCoordinateType::Input)( engine::api::TableParameters::FallbackCoordinateType::Input)(
"snapped", engine::api::TableParameters::FallbackCoordinateType::Snapped); "snapped", engine::api::TableParameters::FallbackCoordinateType::Snapped);
scale_factor_rule =
qi::lit("scale_factor=") >
(double_)[ph::bind(&engine::api::TableParameters::scale_factor, qi::_r1) = qi::_1];
table_rule = destinations_rule(qi::_r1) | sources_rule(qi::_r1); table_rule = destinations_rule(qi::_r1) | sources_rule(qi::_r1);
root_rule = root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") >
BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") > -('?' > (table_rule(qi::_r1) | base_rule(qi::_r1) | scale_factor_rule(qi::_r1) |
-('?' > (table_rule(qi::_r1) | base_rule(qi::_r1) | fallback_speed_rule(qi::_r1) | fallback_speed_rule(qi::_r1) |
(qi::lit("fallback_coordinate=") > (qi::lit("fallback_coordinate=") >
fallback_coordinate_type fallback_coordinate_type
[ph::bind(&engine::api::TableParameters::fallback_coordinate_type, [ph::bind(&engine::api::TableParameters::fallback_coordinate_type,
qi::_r1) = qi::_1])) % qi::_r1) = qi::_1])) %
'&'); '&');
} }
TableParametersGrammar(qi::rule<Iterator, Signature> &root_rule_) : BaseGrammar(root_rule_) TableParametersGrammar(qi::rule<Iterator, Signature> &root_rule_) : BaseGrammar(root_rule_)
@ -94,6 +98,7 @@ struct TableParametersGrammar : public BaseParametersGrammar<Iterator, Signature
qi::rule<Iterator, Signature> sources_rule; qi::rule<Iterator, Signature> sources_rule;
qi::rule<Iterator, Signature> destinations_rule; qi::rule<Iterator, Signature> destinations_rule;
qi::rule<Iterator, Signature> fallback_speed_rule; qi::rule<Iterator, Signature> fallback_speed_rule;
qi::rule<Iterator, Signature> scale_factor_rule;
qi::rule<Iterator, std::size_t()> size_t_; qi::rule<Iterator, std::size_t()> size_t_;
qi::symbols<char, engine::api::TableParameters::AnnotationsType> annotations; qi::symbols<char, engine::api::TableParameters::AnnotationsType> annotations;
qi::rule<Iterator, engine::api::TableParameters::AnnotationsType()> annotations_list; qi::rule<Iterator, engine::api::TableParameters::AnnotationsType()> annotations_list;

View File

@ -96,7 +96,7 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
} }
// Scan table for null results - if any exist, replace with distance estimates // Scan table for null results - if any exist, replace with distance estimates
if (params.fallback_speed > 0) if (params.fallback_speed > 0 || params.scale_factor > 0)
{ {
for (std::size_t row = 0; row < num_sources; row++) for (std::size_t row = 0; row < num_sources; row++)
{ {
@ -104,7 +104,8 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
{ {
const auto &table_index = row * num_destinations + column; const auto &table_index = row * num_destinations + column;
BOOST_ASSERT(table_index < result_tables_pair.first.size()); BOOST_ASSERT(table_index < result_tables_pair.first.size());
if (result_tables_pair.first[table_index] == MAXIMAL_EDGE_DURATION) if (params.fallback_speed > 0 &&
result_tables_pair.first[table_index] == MAXIMAL_EDGE_DURATION)
{ {
const auto &source = const auto &source =
snapped_phantoms[params.sources.empty() ? row : params.sources[row]]; snapped_phantoms[params.sources.empty() ? row : params.sources[row]];
@ -127,6 +128,23 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
result_tables_pair.second[table_index] = distance_estimate; result_tables_pair.second[table_index] = distance_estimate;
} }
} }
if (params.scale_factor > 0 && params.scale_factor != 1 &&
result_tables_pair.first[table_index] != MAXIMAL_EDGE_DURATION &&
result_tables_pair.first[table_index] != 0)
{
EdgeDuration diff =
MAXIMAL_EDGE_DURATION / result_tables_pair.first[table_index];
if (params.scale_factor >= diff)
{
result_tables_pair.first[table_index] = MAXIMAL_EDGE_DURATION - 1;
}
else
{
result_tables_pair.first[table_index] = std::lround(
result_tables_pair.first[table_index] * params.scale_factor);
}
}
} }
} }
} }

View File

@ -61,6 +61,11 @@ std::string getWrongOptionHelp(const engine::api::TableParameters &parameters)
help = "fallback_speed must be > 0"; help = "fallback_speed must be > 0";
} }
if (parameters.scale_factor <= 0)
{
help = "scale_factor must be > 0";
}
return help; return help;
} }
} // anon. ns } // anon. ns

View File

@ -260,5 +260,23 @@ tables.forEach(function(annotation) {
}); });
}); });
test('table: ' + annotation + ' table in Monaco with invalid scale factor', function(assert) {
assert.plan(3);
var osrm = new OSRM({path: mld_data_path, algorithm: 'MLD'});
var options = {
coordinates: two_test_coordinates,
annotations: [annotation.slice(0,-1)],
scale_factor: -1
};
assert.throws(()=>osrm.table(options, (err, res) => {}), /scale_factor must be > 0/, "should throw on invalid scale_factor value");
options.scale_factor = '-1';
assert.throws(()=>osrm.table(options, (err, res) => {}), /scale_factor must be a number/, "should throw on invalid scale_factor value");
options.scale_factor = 0;
assert.throws(()=>osrm.table(options, (err, res) => {}), /scale_factor must be > 0/, "should throw on invalid scale_factor value");
});
}); });

View File

@ -91,6 +91,10 @@ BOOST_AUTO_TEST_CASE(invalid_table_urls)
49UL); 49UL);
BOOST_CHECK_EQUAL(testInvalidOptions<TableParameters>("1,2;3,4?fallback_coordinate=asdf"), BOOST_CHECK_EQUAL(testInvalidOptions<TableParameters>("1,2;3,4?fallback_coordinate=asdf"),
28UL); 28UL);
BOOST_CHECK_EQUAL(
testInvalidOptions<TableParameters>("1,2;3,4?annotations=durations&scale_factor=-1"), 28UL);
BOOST_CHECK_EQUAL(
testInvalidOptions<TableParameters>("1,2;3,4?annotations=durations&scale_factor=0"), 28UL);
} }
BOOST_AUTO_TEST_CASE(valid_route_hint) BOOST_AUTO_TEST_CASE(valid_route_hint)
@ -565,6 +569,17 @@ BOOST_AUTO_TEST_CASE(valid_table_urls)
BOOST_CHECK_EQUAL(result_7->annotations & TableParameters::AnnotationsType::Distance, true); BOOST_CHECK_EQUAL(result_7->annotations & TableParameters::AnnotationsType::Distance, true);
CHECK_EQUAL_RANGE(reference_7.sources, result_7->sources); CHECK_EQUAL_RANGE(reference_7.sources, result_7->sources);
CHECK_EQUAL_RANGE(reference_7.destinations, result_7->destinations); CHECK_EQUAL_RANGE(reference_7.destinations, result_7->destinations);
auto result_8 = parseParameters<TableParameters>("1,2;3,4?sources=all&destinations=all&"
"annotations=duration&fallback_speed=1&"
"fallback_coordinate=snapped&scale_factor=2");
BOOST_CHECK(result_8);
CHECK_EQUAL_RANGE(reference_1.sources, result_3->sources);
CHECK_EQUAL_RANGE(reference_1.destinations, result_3->destinations);
CHECK_EQUAL_RANGE(reference_1.bearings, result_3->bearings);
CHECK_EQUAL_RANGE(reference_1.radiuses, result_3->radiuses);
CHECK_EQUAL_RANGE(reference_1.approaches, result_3->approaches);
CHECK_EQUAL_RANGE(reference_1.coordinates, result_3->coordinates);
} }
BOOST_AUTO_TEST_CASE(valid_match_urls) BOOST_AUTO_TEST_CASE(valid_match_urls)