-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Expand file tree
/
Copy pathviaroute.cpp
More file actions
197 lines (172 loc) · 7.94 KB
/
viaroute.cpp
File metadata and controls
197 lines (172 loc) · 7.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#include "engine/plugins/viaroute.hpp"
#include "engine/api/route_api.hpp"
#include "engine/routing_algorithms.hpp"
#include "engine/status.hpp"
#include "util/for_each_pair.hpp"
#include "util/integer_range.hpp"
#include <cstdlib>
#include <algorithm>
#include <string>
#include <vector>
namespace osrm::engine::plugins
{
ViaRoutePlugin::ViaRoutePlugin(int max_locations_viaroute,
int max_alternatives,
std::optional<double> default_radius)
: BasePlugin(default_radius), max_locations_viaroute(max_locations_viaroute),
max_alternatives(max_alternatives)
{
}
Status ViaRoutePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
const api::RouteParameters &route_parameters,
osrm::engine::api::ResultT &result) const
{
BOOST_ASSERT(route_parameters.IsValid());
if (!algorithms.HasShortestPathSearch() && route_parameters.coordinates.size() > 2)
{
return Error("NotImplemented",
"Shortest path search is not implemented for the chosen search algorithm. "
"Only two coordinates supported.",
result);
}
if (!algorithms.HasDirectShortestPathSearch() && !algorithms.HasShortestPathSearch())
{
return Error(
"NotImplemented",
"Direct shortest path search is not implemented for the chosen search algorithm.",
result);
}
if (max_locations_viaroute > 0 &&
(static_cast<int>(route_parameters.coordinates.size()) > max_locations_viaroute))
{
return Error("TooBig",
"Number of entries " + std::to_string(route_parameters.coordinates.size()) +
" is higher than current maximum (" +
std::to_string(max_locations_viaroute) + ")",
result);
}
// Takes care of alternatives=n and alternatives=true
if ((route_parameters.number_of_alternatives > static_cast<unsigned>(max_alternatives)) ||
(route_parameters.alternatives && max_alternatives == 0))
{
return Error("TooBig",
"Requested number of alternatives is higher than current maximum (" +
std::to_string(max_alternatives) + ")",
result);
}
if (!CheckAllCoordinates(route_parameters.coordinates))
{
return Error("InvalidValue", "Invalid coordinate value.", result);
}
// Error: first and last points should be waypoints
if (!route_parameters.waypoints.empty() &&
(route_parameters.waypoints[0] != 0 ||
route_parameters.waypoints.back() != (route_parameters.coordinates.size() - 1)))
{
return Error(
"InvalidValue", "First and last coordinates must be specified as waypoints.", result);
}
if (!CheckAlgorithms(route_parameters, algorithms, result))
return Status::Error;
if (!route_parameters.bearings.empty() && !default_radius.has_value() &&
route_parameters.radiuses.size() != route_parameters.bearings.size())
{
return Error(
"InvalidOptions", "Number of radiuses does not match number of bearings", result);
}
const auto &facade = algorithms.GetFacade();
auto phantom_node_pairs = GetPhantomNodes(facade, route_parameters);
if (phantom_node_pairs.size() != route_parameters.coordinates.size())
{
return Error("NoSegment",
MissingPhantomErrorMessage(phantom_node_pairs, route_parameters.coordinates),
result);
}
BOOST_ASSERT(phantom_node_pairs.size() == route_parameters.coordinates.size());
auto snapped_phantoms = SnapPhantomNodes(std::move(phantom_node_pairs));
api::RouteAPI route_api{facade, route_parameters};
// TODO: in v6 we should remove the boolean and only keep the number parameter.
// For now just force them to be in sync. and keep backwards compatibility.
const auto wants_alternatives =
(max_alternatives > 0) &&
(route_parameters.alternatives || route_parameters.number_of_alternatives > 0);
const auto number_of_alternatives = std::max(1u, route_parameters.number_of_alternatives);
InternalManyRoutesResult routes;
// Alternatives do not support vias, only direct s,t queries supported
// See the implementation notes and high-level outline.
// https://github.com/Project-OSRM/osrm-backend/issues/3905
if (2 == snapped_phantoms.size() && algorithms.HasAlternativePathSearch() && wants_alternatives)
{
routes = algorithms.AlternativePathSearch({snapped_phantoms[0], snapped_phantoms[1]},
number_of_alternatives);
}
else if (2 == snapped_phantoms.size() && algorithms.HasDirectShortestPathSearch())
{
routes = algorithms.DirectShortestPathSearch({snapped_phantoms[0], snapped_phantoms[1]});
}
else
{
routes =
algorithms.ShortestPathSearch(snapped_phantoms, route_parameters.continue_straight);
}
// The post condition for all path searches is we have at least one route in our result.
// This route might be invalid by means of INVALID_EDGE_WEIGHT as shortest path weight.
BOOST_ASSERT(!routes.routes.empty());
// we can only know this after the fact, different SCC ids still
// allow for connection in one direction.
if (routes.routes[0].is_valid())
{
auto collapse_legs = !route_parameters.waypoints.empty();
if (collapse_legs)
{
std::vector<bool> waypoint_legs(route_parameters.coordinates.size(), false);
std::for_each(route_parameters.waypoints.begin(),
route_parameters.waypoints.end(),
[&](const std::size_t waypoint_index)
{
BOOST_ASSERT(waypoint_index < waypoint_legs.size());
waypoint_legs[waypoint_index] = true;
});
// First and last coordinates should always be waypoints
// This gets validated earlier, but double-checking here, jic
BOOST_ASSERT(waypoint_legs.front());
BOOST_ASSERT(waypoint_legs.back());
for (std::size_t i = 0; i < routes.routes.size(); i++)
{
routes.routes[i] = CollapseInternalRouteResult(routes.routes[i], waypoint_legs);
}
}
route_api.MakeResponse(routes, snapped_phantoms, result);
}
else
{
const auto all_in_same_component =
[](const std::vector<PhantomNodeCandidates> &waypoint_candidates)
{
return std::any_of(waypoint_candidates.front().begin(),
waypoint_candidates.front().end(),
// For each of the first possible phantoms, check if all other
// positions in the list have a phantom from the same component.
[&](const PhantomNode &phantom)
{
const auto component_id = phantom.component.id;
return std::all_of(
std::next(waypoint_candidates.begin()),
std::end(waypoint_candidates),
[component_id](const PhantomNodeCandidates &candidates) {
return candidatesHaveComponent(candidates, component_id);
});
});
};
if (!all_in_same_component(snapped_phantoms))
{
return Error("NoRoute", "Impossible route between points", result);
}
else
{
return Error("NoRoute", "No route found between points", result);
}
}
return Status::Ok;
}
} // namespace osrm::engine::plugins