Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Distance matrices #960

Merged
merged 22 commits into from
Aug 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ef36202
Add Input member for distance matrices storage.
jcoupey Aug 3, 2023
cfde79a
For ongoing dev only: log existing matrices after call to set_matrice…
jcoupey Aug 3, 2023
a1b9143
Parse custom distances if present in json input.
jcoupey Aug 3, 2023
4cd0a4a
Create empty distances matrix if not manually set.
jcoupey Aug 3, 2023
8ca9003
Add wrapper struct to return both durations and distances matrices in…
jcoupey Aug 3, 2023
dcb9253
Switch Wrapper::get_matrix to get_matrices.
jcoupey Aug 3, 2023
40505b8
Adjust client code in Input to store distances from get_matrices.
jcoupey Aug 3, 2023
203c76b
Consistently replace previous Distance type with UserDistance.
jcoupey Aug 3, 2023
d49cdab
Template Wrapper::round_cost static function.
jcoupey Aug 3, 2023
adbfe7e
Retrieve distances from libosrm.
jcoupey Aug 3, 2023
9125ce5
Comment out the debug logging to please CI.
jcoupey Aug 3, 2023
3e7d850
Rename extra_args to routing_args and simpligy build_query.
jcoupey Aug 3, 2023
d377dbf
Add HttpWrapper member for distance key in output.
jcoupey Aug 3, 2023
38f1dda
Add generic distances handling in HttpWrapper::get_matrices.
jcoupey Aug 3, 2023
8609224
Adjust osrm-routed wrapper for distances handling.
jcoupey Aug 3, 2023
6407002
Adjust ORS wrapper for distances handling.
jcoupey Aug 3, 2023
7f3a68e
Adjust Valhalla wrapper for distances handling.
jcoupey Aug 3, 2023
894551b
Remove logging matrices.
jcoupey Aug 3, 2023
b892134
Mention storing distances matrices in changelog.
jcoupey Aug 3, 2023
3fa74e0
Update comment in HttpWrapper::get_matrices.
jcoupey Aug 12, 2023
1d1e0d4
Remove unused include.
jcoupey Aug 12, 2023
249a468
Add missing error for location_index over distances matrix size.
jcoupey Aug 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Added

- Recommendation on how to cite in publications (#943)
- Store distance matrices (#956)

### Changed

Expand Down
5 changes: 3 additions & 2 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,11 +281,12 @@ integers filed under the `profile` key, then under:

- `durations` for a custom travel-time matrix that will be used for
all checks against timing constraints;
- `distances` for a custom distance matrix;
- `costs` for a custom cost matrix that will be used within all route
cost evaluations.

If only the `durations` value is provided, it's implied that it should
also be used for costs evaluations.
If only the `durations` matrix is provided, internal costs are derived from
durations based on vehicles `costs` properties.

Example of describing different matrices for different vehicle
profiles:
Expand Down
51 changes: 30 additions & 21 deletions src/routing/http_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ HttpWrapper::HttpWrapper(const std::string& profile,
Server server,
std::string matrix_service,
std::string matrix_durations_key,
std::string matrix_distances_key,
std::string route_service,
std::string extra_args)
std::string routing_args)
: Wrapper(profile),
_server(std::move(server)),
_matrix_service(std::move(matrix_service)),
_matrix_durations_key(std::move(matrix_durations_key)),
_matrix_distances_key(std::move(matrix_distances_key)),
_route_service(std::move(route_service)),
_extra_args(std::move(extra_args)) {
_routing_args(std::move(routing_args)) {
}

std::string HttpWrapper::send_then_receive(const std::string& query) const {
Expand Down Expand Up @@ -144,8 +146,7 @@ void HttpWrapper::parse_response(rapidjson::Document& json_result,
#endif
}

Matrix<UserCost>
HttpWrapper::get_matrix(const std::vector<Location>& locs) const {
Matrices HttpWrapper::get_matrices(const std::vector<Location>& locs) const {
std::string query = this->build_query(locs, _matrix_service);
std::string json_string = this->run_query(query);

Expand All @@ -161,25 +162,34 @@ HttpWrapper::get_matrix(const std::vector<Location>& locs) const {
}
assert(json_result[_matrix_durations_key.c_str()].Size() == m_size);

// Build matrix while checking for unfound routes ('null' values) to
// avoid unexpected behavior.
Matrix<UserCost> m(m_size);
if (!json_result.HasMember(_matrix_distances_key.c_str())) {
throw RoutingException("Missing " + _matrix_distances_key + ".");
}
assert(json_result[_matrix_distances_key.c_str()].Size() == m_size);

// Build matrices while checking for unfound routes ('null' values)
// to avoid unexpected behavior.
Matrices m(m_size);

std::vector<unsigned> nb_unfound_from_loc(m_size, 0);
std::vector<unsigned> nb_unfound_to_loc(m_size, 0);

for (rapidjson::SizeType i = 0; i < m_size; ++i) {
const auto& line = json_result[_matrix_durations_key.c_str()][i];
assert(line.Size() == m_size);
for (rapidjson::SizeType j = 0; j < line.Size(); ++j) {
if (duration_value_is_null(line[j])) {
const auto& duration_line = json_result[_matrix_durations_key.c_str()][i];
const auto& distance_line = json_result[_matrix_distances_key.c_str()][i];
assert(duration_line.Size() == m_size);
assert(distance_line.Size() == m_size);
for (rapidjson::SizeType j = 0; j < m_size; ++j) {
if (duration_value_is_null(duration_line[j]) or
distance_value_is_null(distance_line[j])) {
// No route found between i and j. Just storing info as we
// don't know yet which location is responsible between i
// and j.
++nb_unfound_from_loc[i];
++nb_unfound_to_loc[j];
} else {
m[i][j] = get_duration_value(line[j]);
m.durations[i][j] = get_duration_value(duration_line[j]);
m.distances[i][j] = get_distance_value(distance_line[j]);
}
}
}
Expand Down Expand Up @@ -208,8 +218,7 @@ void HttpWrapper::add_route_info(Route& route) const {
}
assert(!non_break_locations.empty());

std::string query =
build_query(non_break_locations, _route_service, _extra_args);
std::string query = build_query(non_break_locations, _route_service);

std::string json_string = this->run_query(query);

Expand All @@ -218,7 +227,7 @@ void HttpWrapper::add_route_info(Route& route) const {
this->check_response(json_result, _route_service);

// Total distance and route geometry.
route.distance = round_cost(get_total_distance(json_result));
route.distance = round_cost<UserDistance>(get_total_distance(json_result));
route.geometry = get_geometry(json_result);

auto nb_legs = get_legs_number(json_result);
Expand All @@ -245,17 +254,17 @@ void HttpWrapper::add_route_info(Route& route) const {
for (unsigned b = 1; b <= number_breaks_after[i]; ++b) {
auto& break_step = route.steps[steps_rank + b];
if (next_duration == 0) {
break_step.distance = round_cost(sum_distance);
break_step.distance = round_cost<UserDistance>(sum_distance);
} else {
break_step.distance =
round_cost(sum_distance +
((break_step.duration - step.duration) * next_distance) /
next_duration);
break_step.distance = round_cost<UserDistance>(
sum_distance +
((break_step.duration - step.duration) * next_distance) /
next_duration);
}
}

sum_distance += next_distance;
next_step.distance = round_cost(sum_distance);
next_step.distance = round_cost<UserDistance>(sum_distance);

steps_rank += number_breaks_after[i] + 1;
}
Expand Down
17 changes: 12 additions & 5 deletions src/routing/http_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,36 +28,43 @@ class HttpWrapper : public Wrapper {
const Server _server;
const std::string _matrix_service;
const std::string _matrix_durations_key;
const std::string _matrix_distances_key;
const std::string _route_service;
const std::string _extra_args;
const std::string _routing_args;

HttpWrapper(const std::string& profile,
Server server,
std::string matrix_service,
std::string matrix_durations_key,
std::string matrix_distances_key,
std::string route_service,
std::string extra_args);
std::string routing_args);

std::string run_query(const std::string& query) const;

static void parse_response(rapidjson::Document& json_result,
const std::string& json_content);

virtual std::string build_query(const std::vector<Location>& locations,
const std::string& service,
const std::string& extra_args = "") const = 0;
const std::string& service) const = 0;

virtual void check_response(const rapidjson::Document& json_result,
const std::string& service) const = 0;

Matrix<UserCost> get_matrix(const std::vector<Location>& locs) const override;
Matrices get_matrices(const std::vector<Location>& locs) const override;

virtual bool
duration_value_is_null(const rapidjson::Value& matrix_entry) const = 0;

virtual bool
distance_value_is_null(const rapidjson::Value& matrix_entry) const = 0;

virtual UserDuration
get_duration_value(const rapidjson::Value& matrix_entry) const = 0;

virtual UserDistance
get_distance_value(const rapidjson::Value& matrix_entry) const = 0;

virtual double get_total_distance(const rapidjson::Value& result) const = 0;

virtual unsigned get_legs_number(const rapidjson::Value& result) const = 0;
Expand Down
40 changes: 25 additions & 15 deletions src/routing/libosrm_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ LibosrmWrapper::LibosrmWrapper(const std::string& profile)
: Wrapper(profile), _config(get_config(profile)), _osrm(_config) {
}

Matrix<UserCost>
LibosrmWrapper::get_matrix(const std::vector<Location>& locs) const {
Matrices LibosrmWrapper::get_matrices(const std::vector<Location>& locs) const {
osrm::TableParameters params;
params.annotations = osrm::engine::api::TableParameters::AnnotationsType::All;

for (auto const& location : locs) {
assert(location.has_coordinates());
params.coordinates
Expand All @@ -51,34 +52,43 @@ LibosrmWrapper::get_matrix(const std::vector<Location>& locs) const {
": " + result.values["message"].get<osrm::json::String>().value);
}

auto& table = result.values["durations"].get<osrm::json::Array>();
const auto& durations = result.values["durations"].get<osrm::json::Array>();
const auto& distances = result.values["distances"].get<osrm::json::Array>();

// Expected matrix size.
std::size_t m_size = locs.size();
assert(table.values.size() == m_size);
assert(durations.values.size() == m_size);
assert(distances.values.size() == m_size);

// Build matrix while checking for unfound routes to avoid
// unexpected behavior (OSRM raises 'null').
Matrix<UserCost> m(m_size);
Matrices m(m_size);

std::vector<unsigned> nb_unfound_from_loc(m_size, 0);
std::vector<unsigned> nb_unfound_to_loc(m_size, 0);

std::string reason;
for (std::size_t i = 0; i < m_size; ++i) {
const auto& line = table.values.at(i).get<osrm::json::Array>();
assert(line.values.size() == m_size);
const auto& duration_line = durations.values.at(i).get<osrm::json::Array>();
const auto& distance_line = distances.values.at(i).get<osrm::json::Array>();
assert(duration_line.values.size() == m_size);
assert(distance_line.values.size() == m_size);

for (std::size_t j = 0; j < m_size; ++j) {
const auto& el = line.values.at(j);
if (el.is<osrm::json::Null>()) {
const auto& duration_el = duration_line.values.at(j);
const auto& distance_el = distance_line.values.at(j);
if (duration_el.is<osrm::json::Null>() or
distance_el.is<osrm::json::Null>()) {
// No route found between i and j. Just storing info as we
// don't know yet which location is responsible between i
// and j.
++nb_unfound_from_loc[i];
++nb_unfound_to_loc[j];
} else {
auto cost = round_cost(el.get<osrm::json::Number>().value);
m[i][j] = cost;
m.durations[i][j] =
round_cost<UserDuration>(duration_el.get<osrm::json::Number>().value);
m.distances[i][j] =
round_cost<UserDistance>(distance_el.get<osrm::json::Number>().value);
}
}
}
Expand Down Expand Up @@ -130,8 +140,8 @@ void LibosrmWrapper::add_route_info(Route& route) const {
auto& json_route = result_routes.values.at(0).get<osrm::json::Object>();

// Total distance and route geometry.
route.distance =
round_cost(json_route.values["distance"].get<osrm::json::Number>().value);
route.distance = round_cost<UserDistance>(
json_route.values["distance"].get<osrm::json::Number>().value);
route.geometry =
std::move(json_route.values["geometry"].get<osrm::json::String>().value);

Expand Down Expand Up @@ -170,13 +180,13 @@ void LibosrmWrapper::add_route_info(Route& route) const {
// non-breaks steps.
for (unsigned b = 1; b <= number_breaks_after[i]; ++b) {
auto& break_step = route.steps[steps_rank + b];
break_step.distance = round_cost(
break_step.distance = round_cost<UserDistance>(
sum_distance + ((break_step.duration - step.duration) * next_distance) /
next_duration);
}

sum_distance += next_distance;
next_step.distance = round_cost(sum_distance);
next_step.distance = round_cost<UserDistance>(sum_distance);

steps_rank += number_breaks_after[i] + 1;
}
Expand Down
4 changes: 2 additions & 2 deletions src/routing/libosrm_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ class LibosrmWrapper : public Wrapper {
public:
LibosrmWrapper(const std::string& profile);

virtual Matrix<UserCost>
get_matrix(const std::vector<Location>& locs) const override;
virtual Matrices
get_matrices(const std::vector<Location>& locs) const override;

virtual void add_route_info(Route& route) const override;
};
Expand Down
23 changes: 18 additions & 5 deletions src/routing/ors_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ OrsWrapper::OrsWrapper(const std::string& profile, const Server& server)
server,
"matrix",
"durations",
"distances",
"directions",
"\"geometry_simplify\":\"false\",\"continue_straight\":"
"\"false\"") {
}

std::string OrsWrapper::build_query(const std::vector<Location>& locations,
const std::string& service,
const std::string& extra_args) const {
const std::string& service) const {
// Adding locations.
std::string body = "{\"";
if (service == "directions") {
Expand All @@ -38,8 +38,11 @@ std::string OrsWrapper::build_query(const std::vector<Location>& locations,
}
body.pop_back(); // Remove trailing ','.
body += "]";
if (!extra_args.empty()) {
body += "," + extra_args;
if (service == _route_service) {
body += "," + _routing_args;
} else {
assert(service == _matrix_service);
body += ",\"metrics\":[\"duration\",\"distance\"]";
}
body += "}";

Expand Down Expand Up @@ -70,9 +73,19 @@ bool OrsWrapper::duration_value_is_null(
return matrix_entry.IsNull();
}

bool OrsWrapper::distance_value_is_null(
const rapidjson::Value& matrix_entry) const {
return matrix_entry.IsNull();
}

UserDuration
OrsWrapper::get_duration_value(const rapidjson::Value& matrix_entry) const {
return round_cost(matrix_entry.GetDouble());
return round_cost<UserDuration>(matrix_entry.GetDouble());
}

UserDistance
OrsWrapper::get_distance_value(const rapidjson::Value& matrix_entry) const {
return round_cost<UserDistance>(matrix_entry.GetDouble());
}

double OrsWrapper::get_total_distance(const rapidjson::Value& result) const {
Expand Down
9 changes: 7 additions & 2 deletions src/routing/ors_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,23 @@ namespace vroom::routing {
class OrsWrapper : public HttpWrapper {
private:
std::string build_query(const std::vector<Location>& locations,
const std::string& service,
const std::string& extra_args) const override;
const std::string& service) const override;

void check_response(const rapidjson::Document& json_result,
const std::string& service) const override;

bool
duration_value_is_null(const rapidjson::Value& matrix_entry) const override;

bool
distance_value_is_null(const rapidjson::Value& matrix_entry) const override;

UserDuration
get_duration_value(const rapidjson::Value& matrix_entry) const override;

UserDistance
get_distance_value(const rapidjson::Value& matrix_entry) const override;

double get_total_distance(const rapidjson::Value& result) const override;

unsigned get_legs_number(const rapidjson::Value& result) const override;
Expand Down
Loading