Exemplo n.º 1
0
Status NearestPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
                                    const api::NearestParameters &params,
                                    util::json::Object &json_result) const
{
    BOOST_ASSERT(params.IsValid());

    if (!CheckAlgorithms(params, algorithms, json_result))
        return Status::Error;

    const auto &facade = algorithms.GetFacade();

    if (max_results > 0 &&
        (boost::numeric_cast<std::int64_t>(params.number_of_results) > max_results))
    {
        return Error("TooBig",
                     "Number of results " + std::to_string(params.number_of_results) +
                         " is higher than current maximum (" + std::to_string(max_results) + ")",
                     json_result);
    }

    if (!CheckAllCoordinates(params.coordinates))
        return Error("InvalidOptions", "Coordinates are invalid", json_result);

    if (params.coordinates.size() != 1)
    {
        return Error("InvalidOptions", "Only one input coordinate is supported", json_result);
    }

    auto phantom_nodes = GetPhantomNodes(facade, params, params.number_of_results);

    if (phantom_nodes.front().size() == 0)
    {
        return Error("NoSegment", "Could not find a matching segments for coordinate", json_result);
    }
    BOOST_ASSERT(phantom_nodes.front().size() > 0);

    api::NearestAPI nearest_api(facade, params);
    nearest_api.MakeResponse(phantom_nodes, json_result);

    return Status::Ok;
}
Exemplo n.º 2
0
// given the node order in which to visit, compute the actual route (with geometry, travel time and
// so on) and return the result
InternalRouteResult TripPlugin::ComputeRoute(const RoutingAlgorithmsInterface &algorithms,
                                             const std::vector<PhantomNode> &snapped_phantoms,
                                             const std::vector<NodeID> &trip,
                                             const bool roundtrip) const
{
    InternalRouteResult min_route;
    // given the final trip, compute total duration and return the route and location permutation
    PhantomNodes viapoint;

    // computes a roundtrip from the nodes in trip
    for (auto node = trip.begin(); node < trip.end() - 1; ++node)
    {
        const auto from_node = *node;
        const auto to_node = *std::next(node);

        viapoint = PhantomNodes{snapped_phantoms[from_node], snapped_phantoms[to_node]};
        min_route.segment_end_coordinates.emplace_back(viapoint);
    }

    // return back to the first node if it is a round trip
    if (roundtrip)
    {
        viapoint = PhantomNodes{snapped_phantoms[trip.back()], snapped_phantoms[trip.front()]};
        min_route.segment_end_coordinates.emplace_back(viapoint);
        // trip comes out to be something like 0 1 4 3 2 0
        BOOST_ASSERT(min_route.segment_end_coordinates.size() == trip.size());
    }
    else
    {
        // trip comes out to be something like 0 1 4 3 2, so the sizes don't match
        BOOST_ASSERT(min_route.segment_end_coordinates.size() == trip.size() - 1);
    }

    min_route = algorithms.ShortestPathSearch(min_route.segment_end_coordinates, {false});
    BOOST_ASSERT_MSG(min_route.shortest_path_weight < INVALID_EDGE_WEIGHT, "unroutable route");
    return min_route;
}
Exemplo n.º 3
0
Status TripPlugin::HandleRequest(const datafacade::ContiguousInternalMemoryDataFacadeBase &facade,
                                 const RoutingAlgorithmsInterface &algorithms,
                                 const api::TripParameters &parameters,
                                 util::json::Object &json_result) const
{
    if (!algorithms.HasShortestPathSearch())
    {
        return Error("NotImplemented",
                     "Shortest path search is not implemented for the chosen search algorithm.",
                     json_result);
    }
    if (!algorithms.HasManyToManySearch())
    {
        return Error("NotImplemented",
                     "Many to many search is not implemented for the chosen search algorithm.",
                     json_result);
    }

    BOOST_ASSERT(parameters.IsValid());
    const auto number_of_locations = parameters.coordinates.size();

    std::size_t source_id = INVALID_INDEX;
    std::size_t destination_id = INVALID_INDEX;
    if (parameters.source == api::TripParameters::SourceType::First)
    {
        source_id = 0;
    }
    if (parameters.destination == api::TripParameters::DestinationType::Last)
    {
        BOOST_ASSERT(number_of_locations > 0);
        destination_id = number_of_locations - 1;
    }
    bool fixed_start = (source_id == 0);
    bool fixed_end = (destination_id == number_of_locations - 1);
    if (!IsSupportedParameterCombination(fixed_start, fixed_end, parameters.roundtrip))
    {
        return Error("NotImplemented", "This request is not supported", json_result);
    }

    // enforce maximum number of locations for performance reasons
    if (max_locations_trip > 0 && static_cast<int>(number_of_locations) > max_locations_trip)
    {
        return Error("TooBig", "Too many trip coordinates", json_result);
    }

    if (!CheckAllCoordinates(parameters.coordinates))
    {
        return Error("InvalidValue", "Invalid coordinate value.", json_result);
    }

    auto phantom_node_pairs = GetPhantomNodes(facade, parameters);
    if (phantom_node_pairs.size() != number_of_locations)
    {
        return Error("NoSegment",
                     std::string("Could not find a matching segment for coordinate ") +
                         std::to_string(phantom_node_pairs.size()),
                     json_result);
    }
    BOOST_ASSERT(phantom_node_pairs.size() == number_of_locations);

    if (fixed_start && fixed_end && (source_id >= parameters.coordinates.size() ||
                                     destination_id >= parameters.coordinates.size()))
    {
        return Error("InvalidValue", "Invalid source or destination value.", json_result);
    }

    auto snapped_phantoms = SnapPhantomNodes(phantom_node_pairs);

    BOOST_ASSERT(snapped_phantoms.size() == number_of_locations);

    // compute the duration table of all phantom nodes
    auto result_table = util::DistTableWrapper<EdgeWeight>(
        algorithms.ManyToManySearch(snapped_phantoms, {}, {}), number_of_locations);

    if (result_table.size() == 0)
    {
        return Status::Error;
    }

    const constexpr std::size_t BF_MAX_FEASABLE = 10;
    BOOST_ASSERT_MSG(result_table.size() == number_of_locations * number_of_locations,
                     "Distance Table has wrong size");

    if (!IsStronglyConnectedComponent(result_table))
    {
        return Error("NoTrips", "No trip visiting all destinations possible.", json_result);
    }

    if (fixed_start && fixed_end)
    {
        ManipulateTableForFSE(source_id, destination_id, result_table);
    }

    std::vector<NodeID> trip;
    trip.reserve(number_of_locations);
    // get an optimized order in which the destinations should be visited
    if (number_of_locations < BF_MAX_FEASABLE)
    {
        trip = trip::BruteForceTrip(number_of_locations, result_table);
    }
    else
    {
        trip = trip::FarthestInsertionTrip(number_of_locations, result_table);
    }

    // rotate result such that roundtrip starts at node with index 0
    // thist first if covers scenarios: !fixed_end || fixed_start || (fixed_start && fixed_end)
    if (!fixed_end || fixed_start)
    {
        auto desired_start_index = std::find(std::begin(trip), std::end(trip), 0);
        BOOST_ASSERT(desired_start_index != std::end(trip));
        std::rotate(std::begin(trip), desired_start_index, std::end(trip));
    }
    else if (fixed_end && !fixed_start && parameters.roundtrip)
    {
        auto desired_start_index = std::find(std::begin(trip), std::end(trip), destination_id);
        BOOST_ASSERT(desired_start_index != std::end(trip));
        std::rotate(std::begin(trip), desired_start_index, std::end(trip));
    }

    // get the route when visiting all destinations in optimized order
    InternalRouteResult route =
        ComputeRoute(algorithms, snapped_phantoms, trip, parameters.roundtrip);

    // get api response
    const std::vector<std::vector<NodeID>> trips = {trip};
    const std::vector<InternalRouteResult> routes = {route};
    api::TripAPI trip_api{facade, parameters};
    trip_api.MakeResponse(trips, routes, snapped_phantoms, json_result);

    return Status::Ok;
}
Exemplo n.º 4
0
Status MatchPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
                                  const api::MatchParameters &parameters,
                                  util::json::Object &json_result) const
{
    if (!algorithms.HasMapMatching())
    {
        return Error("NotImplemented",
                     "Map matching is not implemented for the chosen search algorithm.",
                     json_result);
    }

    if (!CheckAlgorithms(parameters, algorithms, json_result))
        return Status::Error;

    const auto &facade = algorithms.GetFacade();

    BOOST_ASSERT(parameters.IsValid());

    // enforce maximum number of locations for performance reasons
    if (max_locations_map_matching > 0 &&
        static_cast<int>(parameters.coordinates.size()) > max_locations_map_matching)
    {
        return Error("TooBig", "Too many trace coordinates", json_result);
    }

    if (!CheckAllCoordinates(parameters.coordinates))
    {
        return Error("InvalidValue", "Invalid coordinate value.", json_result);
    }

    if (max_radius_map_matching > 0 && std::any_of(parameters.radiuses.begin(),
                                                   parameters.radiuses.end(),
                                                   [&](const auto &radius) {
                                                       if (!radius)
                                                           return false;
                                                       return *radius > max_radius_map_matching;
                                                   }))
    {
        return Error("TooBig", "Radius search size is too large for map matching.", json_result);
    }

    // Check for same or increasing timestamps. Impl. note: Incontrast to `sort(first,
    // last, less_equal)` checking `greater` in reverse meets irreflexive requirements.
    const auto time_increases_monotonically = std::is_sorted(
        parameters.timestamps.rbegin(), parameters.timestamps.rend(), std::greater<>{});

    if (!time_increases_monotonically)
    {
        return Error(
            "InvalidValue", "Timestamps need to be monotonically increasing.", json_result);
    }

    SubMatchingList sub_matchings;
    api::tidy::Result tidied;
    if (parameters.tidy)
    {
        // Transparently tidy match parameters, do map matching on tidied parameters.
        // Then use the mapping to restore the original <-> tidied relationship.
        tidied = api::tidy::tidy(parameters);
    }
    else
    {
        tidied = api::tidy::keep_all(parameters);
    }

    // Error: first and last points should be waypoints
    if (!parameters.waypoints.empty() &&
        (tidied.parameters.waypoints[0] != 0 ||
         tidied.parameters.waypoints.back() != (tidied.parameters.coordinates.size() - 1)))
    {
        return Error("InvalidValue",
                     "First and last coordinates must be specified as waypoints.",
                     json_result);
    }

    // assuming radius is the standard deviation of a normal distribution
    // that models GPS noise (in this model), x3 should give us the correct
    // search radius with > 99% confidence
    std::vector<double> search_radiuses;
    if (tidied.parameters.radiuses.empty())
    {
        search_radiuses.resize(tidied.parameters.coordinates.size(),
                               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;
                           }

                       });
    }

    auto candidates_lists = GetPhantomNodesInRange(facade, tidied.parameters, search_radiuses);

    filterCandidates(tidied.parameters.coordinates, candidates_lists);
    if (std::all_of(candidates_lists.begin(),
                    candidates_lists.end(),
                    [](const std::vector<PhantomNodeWithDistance> &candidates) {
                        return candidates.empty();
                    }))
    {
        return Error("NoSegment",
                     std::string("Could not find a matching segment for any coordinate."),
                     json_result);
    }

    // call the actual map matching
    sub_matchings =
        algorithms.MapMatching(candidates_lists,
                               tidied.parameters.coordinates,
                               tidied.parameters.timestamps,
                               tidied.parameters.radiuses,
                               parameters.gaps == api::MatchParameters::GapsType::Split);

    if (sub_matchings.size() == 0)
    {
        return Error("NoMatch", "Could not match the trace.", json_result);
    }

    // trace was split, we don't support the waypoints parameter across multiple match objects
    if (sub_matchings.size() > 1 && !parameters.waypoints.empty())
    {
        return Error("NoMatch", "Could not match the trace with the given waypoints.", json_result);
    }

    // Error: Check if user-supplied waypoints can be found in the resulting matches
    {
        std::set<std::size_t> tidied_waypoints(tidied.parameters.waypoints.begin(),
                                               tidied.parameters.waypoints.end());
        for (const auto &sm : sub_matchings)
        {
            std::for_each(sm.indices.begin(),
                          sm.indices.end(),
                          [&tidied_waypoints](const auto index) { tidied_waypoints.erase(index); });
        }
        if (!tidied_waypoints.empty())
        {
            return Error(
                "NoMatch", "Requested waypoint parameter could not be matched.", json_result);
        }
    }

    // each sub_route will correspond to a MatchObject
    std::vector<InternalRouteResult> sub_routes(sub_matchings.size());
    for (auto index : util::irange<std::size_t>(0UL, sub_matchings.size()))
    {
        BOOST_ASSERT(sub_matchings[index].nodes.size() > 1);

        // FIXME we only run this to obtain the geometry
        // The clean way would be to get this directly from the map matching plugin
        PhantomNodes current_phantom_node_pair;
        for (unsigned i = 0; i < sub_matchings[index].nodes.size() - 1; ++i)
        {
            current_phantom_node_pair.source_phantom = sub_matchings[index].nodes[i];
            current_phantom_node_pair.target_phantom = sub_matchings[index].nodes[i + 1];
            BOOST_ASSERT(current_phantom_node_pair.source_phantom.IsValid());
            BOOST_ASSERT(current_phantom_node_pair.target_phantom.IsValid());
            sub_routes[index].segment_end_coordinates.emplace_back(current_phantom_node_pair);
        }
        // force uturns to be on
        // we split the phantom nodes anyway and only have bi-directional phantom nodes for
        // possible uturns
        sub_routes[index] =
            algorithms.ShortestPathSearch(sub_routes[index].segment_end_coordinates, {false});
        BOOST_ASSERT(sub_routes[index].shortest_path_weight != INVALID_EDGE_WEIGHT);
        if (!tidied.parameters.waypoints.empty())
        {
            std::vector<bool> waypoint_legs;
            waypoint_legs.reserve(sub_matchings[index].indices.size());
            for (unsigned i = 0, j = 0; i < sub_matchings[index].indices.size(); ++i)
            {
                auto current_wp = tidied.parameters.waypoints[j];
                if (current_wp == sub_matchings[index].indices[i])
                {
                    waypoint_legs.push_back(true);
                    ++j;
                }
                else
                {
                    waypoint_legs.push_back(false);
                }
            }
            sub_routes[index] = CollapseInternalRouteResult(sub_routes[index], waypoint_legs);
        }
    }

    api::MatchAPI match_api{facade, parameters, tidied};
    match_api.MakeResponse(sub_matchings, sub_routes, json_result);

    return Status::Ok;
}