Trip with Fixed Start and End points (TFSE) (#3408)

* fixed start and end trip feature to trip service
This commit is contained in:
Kajari Ghosh
2017-02-10 05:13:20 -05:00
committed by GitHub
parent 3e2db47cc8
commit 2218658969
15 changed files with 895 additions and 277 deletions
+28 -1
View File
@@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "engine/api/route_parameters.hpp"
#include <boost/optional.hpp>
#include <vector>
namespace osrm
@@ -47,7 +48,33 @@ namespace api
*/
struct TripParameters : public RouteParameters
{
// bool IsValid() const; Falls back to base class
TripParameters() = default;
enum class SourceType
{
Any,
First
};
enum class DestinationType
{
Any,
Last
};
template <typename... Args>
TripParameters(SourceType source_,
DestinationType destination_,
bool roundtrip_,
Args &&... args_)
: RouteParameters{std::forward<Args>(args_)...}, source{source_}, destination{destination_},
roundtrip{roundtrip_}
{
}
SourceType source = SourceType::Any;
DestinationType destination = DestinationType::Any;
bool roundtrip = true;
bool IsValid() const { return RouteParameters::IsValid(); }
};
}
}
+2 -1
View File
@@ -36,7 +36,8 @@ class TripPlugin final : public BasePlugin
InternalRouteResult ComputeRoute(const std::shared_ptr<const datafacade::BaseDataFacade> facade,
const std::vector<PhantomNode> &phantom_node_list,
const std::vector<NodeID> &trip) const;
const std::vector<NodeID> &trip,
const bool roundtrip) const;
public:
explicit TripPlugin(const int max_locations_trip_)
+41 -26
View File
@@ -11,6 +11,7 @@
#include <cstdlib>
#include <iterator>
#include <limits>
#include <numeric>
#include <string>
#include <vector>
@@ -25,62 +26,76 @@ namespace trip
EdgeWeight ReturnDistance(const util::DistTableWrapper<EdgeWeight> &dist_table,
const std::vector<NodeID> &location_order,
const EdgeWeight min_route_dist,
const std::size_t component_size)
const std::size_t number_of_locations)
{
EdgeWeight route_dist = 0;
std::size_t i = 0;
while (i < location_order.size() && (route_dist < min_route_dist))
std::size_t current_index = 0;
while (current_index < location_order.size() && (route_dist < min_route_dist))
{
route_dist += dist_table(location_order[i], location_order[(i + 1) % component_size]);
BOOST_ASSERT_MSG(dist_table(location_order[i], location_order[(i + 1) % component_size]) !=
std::size_t next_index = (current_index + 1) % number_of_locations;
auto edge_weight = dist_table(location_order[current_index], location_order[next_index]);
// If the edge_weight is very large (INVALID_EDGE_WEIGHT) then the algorithm will not choose
// this edge in final minimal path. So instead of computing all the permutations after this
// large edge, discard this edge right here and don't consider the path after this edge.
if (edge_weight == INVALID_EDGE_WEIGHT)
{
return INVALID_EDGE_WEIGHT;
}
else
{
route_dist += edge_weight;
}
// This boost assert should not be reached if TFSE table
BOOST_ASSERT_MSG(dist_table(location_order[current_index], location_order[next_index]) !=
INVALID_EDGE_WEIGHT,
"invalid route found");
++i;
++current_index;
}
return route_dist;
}
// computes the route by computing all permutations and selecting the shortest
template <typename NodeIDIterator>
std::vector<NodeID> BruteForceTrip(const NodeIDIterator start,
const NodeIDIterator end,
const std::size_t number_of_locations,
std::vector<NodeID> BruteForceTrip(const std::size_t number_of_locations,
const util::DistTableWrapper<EdgeWeight> &dist_table)
{
(void)number_of_locations; // unused
const auto component_size = std::distance(start, end);
std::vector<NodeID> perm(start, end);
std::vector<NodeID> route = perm;
// set initial order in which nodes are visited to 0, 1, 2, 3, ...
std::vector<NodeID> node_order(number_of_locations);
std::iota(std::begin(node_order), std::end(node_order), 0);
std::vector<NodeID> route = node_order;
EdgeWeight min_route_dist = INVALID_EDGE_WEIGHT;
// check length of all possible permutation of the component ids
BOOST_ASSERT_MSG(perm.size() > 0, "no permutation given");
BOOST_ASSERT_MSG(*(std::max_element(std::begin(perm), std::end(perm))) < number_of_locations,
BOOST_ASSERT_MSG(node_order.size() > 0, "no order permutation given");
BOOST_ASSERT_MSG(*(std::max_element(std::begin(node_order), std::end(node_order))) <
number_of_locations,
"invalid node id");
BOOST_ASSERT_MSG(*(std::min_element(std::begin(node_order), std::end(node_order))) >= 0,
"invalid node id");
BOOST_ASSERT_MSG(*(std::min_element(std::begin(perm), std::end(perm))) >= 0, "invalid node id");
do
{
const auto new_distance = ReturnDistance(dist_table, perm, min_route_dist, component_size);
const auto new_distance =
ReturnDistance(dist_table, node_order, min_route_dist, number_of_locations);
// we can use `<` instead of `<=` here, since all distances are `!=` INVALID_EDGE_WEIGHT
// In case we really sum up to invalid edge weight for all permutations, keeping the very
// first one is fine too.
if (new_distance < min_route_dist)
{
min_route_dist = new_distance;
route = perm;
route = node_order;
}
} while (std::next_permutation(std::begin(perm), std::end(perm)));
} while (std::next_permutation(std::begin(node_order), std::end(node_order)));
return route;
}
}
}
}
} // namespace trip
} // namespace engine
} // namespace osrm
#endif // TRIP_BRUTE_FORCE_HPP
+32 -65
View File
@@ -50,8 +50,11 @@ GetShortestRoundTrip(const NodeID new_loc,
const auto dist_to = dist_table(new_loc, *to_node);
const auto trip_dist = dist_from + dist_to - dist_table(*from_node, *to_node);
BOOST_ASSERT_MSG(dist_from != INVALID_EDGE_WEIGHT, "distance has invalid edge weight");
BOOST_ASSERT_MSG(dist_to != INVALID_EDGE_WEIGHT, "distance has invalid edge weight");
// If the edge_weight is very large (INVALID_EDGE_WEIGHT) then the algorithm will not choose
// this edge in final minimal path. So instead of computing all the permutations after this
// large edge, discard this edge right here and don't consider the path after this edge.
if (dist_from == INVALID_EDGE_WEIGHT || dist_to == INVALID_EDGE_WEIGHT)
continue;
// This is not neccessarily true:
// Lets say you have an edge (u, v) with duration 100. If you place a coordinate exactly in
// the middle of the segment yielding (u, v'), the adjusted duration will be 100 * 0.5 = 50.
@@ -72,18 +75,14 @@ GetShortestRoundTrip(const NodeID new_loc,
return std::make_pair(min_trip_distance, next_insert_point_candidate);
}
template <typename NodeIDIterator>
// given two initial start nodes, find a roundtrip route using the farthest insertion algorithm
std::vector<NodeID> FindRoute(const std::size_t &number_of_locations,
const std::size_t &component_size,
const NodeIDIterator &start,
const NodeIDIterator &end,
const util::DistTableWrapper<EdgeWeight> &dist_table,
const NodeID &start1,
const NodeID &start2)
{
BOOST_ASSERT_MSG(number_of_locations >= component_size,
"component size bigger than total number of locations");
BOOST_ASSERT_MSG(number_of_locations * number_of_locations == dist_table.size(),
"number_of_locations and dist_table size do not match");
std::vector<NodeID> route;
route.reserve(number_of_locations);
@@ -96,22 +95,21 @@ std::vector<NodeID> FindRoute(const std::size_t &number_of_locations,
route.push_back(start1);
route.push_back(start2);
// add all other nodes missing (two nodes are already in the initial start trip)
for (std::size_t j = 2; j < component_size; ++j)
// two nodes are already in the initial start trip, so we need to add all other nodes
for (std::size_t added_nodes = 2; added_nodes < number_of_locations; ++added_nodes)
{
auto farthest_distance = std::numeric_limits<int>::min();
auto next_node = -1;
NodeIDIter next_insert_point;
// find unvisited loc i that is the farthest away from all other visited locs
for (auto i = start; i != end; ++i)
// find unvisited node that is the farthest away from all other visited locs
for (std::size_t id = 0; id < number_of_locations; ++id)
{
// find the shortest distance from i to all visited nodes
if (!visited[*i])
if (!visited[id])
{
const auto insert_candidate =
GetShortestRoundTrip(*i, dist_table, number_of_locations, route);
GetShortestRoundTrip(id, dist_table, number_of_locations, route);
BOOST_ASSERT_MSG(insert_candidate.first != INVALID_EDGE_WEIGHT,
"shortest round trip is invalid");
@@ -121,7 +119,7 @@ std::vector<NodeID> FindRoute(const std::size_t &number_of_locations,
if (insert_candidate.first > farthest_distance)
{
farthest_distance = insert_candidate.first;
next_node = *i;
next_node = id;
next_insert_point = insert_candidate.second;
}
}
@@ -136,10 +134,7 @@ std::vector<NodeID> FindRoute(const std::size_t &number_of_locations,
return route;
}
template <typename NodeIDIterator>
std::vector<NodeID> FarthestInsertionTrip(const NodeIDIterator &start,
const NodeIDIterator &end,
const std::size_t number_of_locations,
std::vector<NodeID> FarthestInsertionTrip(const std::size_t number_of_locations,
const util::DistTableWrapper<EdgeWeight> &dist_table)
{
//////////////////////////////////////////////////////////////////////////////////////////////////
@@ -158,57 +153,29 @@ std::vector<NodeID> FarthestInsertionTrip(const NodeIDIterator &start,
// Guard against dist_table being empty therefore max_element returning the end iterator.
BOOST_ASSERT(dist_table.size() > 0);
const auto component_size = std::distance(start, end);
BOOST_ASSERT(component_size >= 0);
BOOST_ASSERT_MSG(number_of_locations * number_of_locations == dist_table.size(),
"number_of_locations and dist_table size do not match");
auto max_from = -1;
auto max_to = -1;
if (static_cast<std::size_t>(component_size) == number_of_locations)
{
// find the pair of location with the biggest distance and make the pair the initial start
// trip. Skipping over the very first element (0,0), we make sure not to end up with the
// same start/end in the special case where all entries are the same.
const auto index =
std::distance(std::begin(dist_table),
std::max_element(std::begin(dist_table) + 1, std::end(dist_table)));
max_from = index / number_of_locations;
max_to = index % number_of_locations;
}
else
{
auto max_dist = std::numeric_limits<EdgeWeight>::min();
for (auto x = start; x != end; ++x)
{
for (auto y = start; y != end; ++y)
{
// don't repeat coordinates
if (*x == *y)
continue;
const auto xy_dist = dist_table(*x, *y);
// SCC decomposition done correctly?
BOOST_ASSERT(xy_dist != INVALID_EDGE_WEIGHT);
if (xy_dist >= max_dist)
{
max_dist = xy_dist;
max_from = *x;
max_to = *y;
}
}
}
}
// find the pair of location with the biggest distance and make the pair the initial start
// trip. Skipping over the very first element (0,0), we make sure not to end up with the
// same start/end in the special case where all entries are the same.
const auto index_of_farthest_distance = std::distance(
std::begin(dist_table), std::max_element(std::begin(dist_table) + 1, std::end(dist_table)));
// distance table is a nxn matrix with the distance(u,v) in column u and row v
// but the distance table is stored in an 1D array of distances
// to get the actual (u,v), get the row by dividing and the column by computing modulo n
NodeID max_from = index_of_farthest_distance / number_of_locations;
NodeID max_to = index_of_farthest_distance % number_of_locations;
BOOST_ASSERT(max_from >= 0);
BOOST_ASSERT(max_to >= 0);
BOOST_ASSERT_MSG(static_cast<std::size_t>(max_from) < number_of_locations, "start node");
BOOST_ASSERT_MSG(static_cast<std::size_t>(max_to) < number_of_locations, "start node");
return FindRoute(number_of_locations, component_size, start, end, dist_table, max_from, max_to);
}
}
}
return FindRoute(number_of_locations, dist_table, max_from, max_to);
}
} // namespace trip
} // namespace engine
} // namespace osrm
#endif // TRIP_FARTHEST_INSERTION_HPP
+28 -1
View File
@@ -4,6 +4,7 @@
#include "server/api/route_parameters_grammar.hpp"
#include "engine/api/trip_parameters.hpp"
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
namespace osrm
@@ -15,6 +16,7 @@ namespace api
namespace
{
namespace ph = boost::phoenix;
namespace qi = boost::spirit::qi;
}
@@ -26,12 +28,37 @@ struct TripParametersGrammar final : public RouteParametersGrammar<Iterator, Sig
TripParametersGrammar() : BaseGrammar(root_rule)
{
roundtrip_rule =
qi::lit("roundtrip=") >
qi::bool_[ph::bind(&engine::api::TripParameters::roundtrip, qi::_r1) = qi::_1];
source_type.add("any", engine::api::TripParameters::SourceType::Any)(
"first", engine::api::TripParameters::SourceType::First);
destination_type.add("any", engine::api::TripParameters::DestinationType::Any)(
"last", engine::api::TripParameters::DestinationType::Last);
source_rule = qi::lit("source=") >
source_type[ph::bind(&engine::api::TripParameters::source, qi::_r1) = qi::_1];
destination_rule =
qi::lit("destination=") >
destination_type[ph::bind(&engine::api::TripParameters::destination, qi::_r1) = qi::_1];
root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") >
-('?' > (BaseGrammar::base_rule(qi::_r1)) % '&');
-('?' > (roundtrip_rule(qi::_r1) | source_rule(qi::_r1) |
destination_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1)) %
'&');
}
private:
qi::rule<Iterator, Signature> source_rule;
qi::rule<Iterator, Signature> destination_rule;
qi::rule<Iterator, Signature> roundtrip_rule;
qi::rule<Iterator, Signature> root_rule;
qi::symbols<char, engine::api::TripParameters::SourceType> source_type;
qi::symbols<char, engine::api::TripParameters::DestinationType> destination_type;
};
}
}
+12
View File
@@ -46,6 +46,18 @@ template <typename T> class DistTableWrapper
return table_[index];
}
void SetValue(NodeID from, NodeID to, EdgeWeight value)
{
BOOST_ASSERT_MSG(from < number_of_nodes_, "from ID is out of bound");
BOOST_ASSERT_MSG(to < number_of_nodes_, "to ID is out of bound");
const auto index = from * number_of_nodes_ + to;
BOOST_ASSERT_MSG(index < table_.size(), "index is out of bound");
table_[index] = value;
}
ConstIterator begin() const { return std::begin(table_); }
Iterator begin() { return std::begin(table_); }
+1 -2
View File
@@ -13,8 +13,7 @@ namespace util
{
// This Wrapper provides all methods that are needed for extractor::TarjanSCC, when the graph is
// given in a
// matrix representation (e.g. as output from a distance table call)
// given in a matrix representation (e.g. as output from a distance table call)
template <typename T> class MatrixGraphWrapper
{
+2
View File
@@ -60,6 +60,8 @@ using NameID = std::uint32_t;
using EdgeWeight = std::int32_t;
using TurnPenalty = std::int16_t; // turn penalty in 100ms units
static const std::size_t INVALID_INDEX = std::numeric_limits<std::size_t>::max();
using LaneID = std::uint8_t;
static const LaneID INVALID_LANEID = std::numeric_limits<LaneID>::max();
using LaneDataID = std::uint16_t;