perform zero-length segment removal
This commit is contained in:
parent
290101b8ce
commit
d40f777a87
@ -106,7 +106,8 @@ class RouteAPI : public BaseAPI
|
|||||||
* Using post-processing on basis of route-steps for a single leg at a time
|
* Using post-processing on basis of route-steps for a single leg at a time
|
||||||
* comes at the cost that we cannot count the correct exit for roundabouts.
|
* comes at the cost that we cannot count the correct exit for roundabouts.
|
||||||
* We can only emit the exit nr/intersections up to/starting at a part of the leg.
|
* We can only emit the exit nr/intersections up to/starting at a part of the leg.
|
||||||
* If a roundabout is not terminated in a leg, we will end up with a enter-roundabout
|
* If a roundabout is not terminated in a leg, we will end up with a
|
||||||
|
*enter-roundabout
|
||||||
* and exit-roundabout-nr where the exit nr is out of sync with the previous enter.
|
* and exit-roundabout-nr where the exit nr is out of sync with the previous enter.
|
||||||
*
|
*
|
||||||
* | S |
|
* | S |
|
||||||
@ -118,16 +119,22 @@ class RouteAPI : public BaseAPI
|
|||||||
* | |
|
* | |
|
||||||
* | |
|
* | |
|
||||||
*
|
*
|
||||||
* Coming from S via V to T, we end up with the legs S->V and V->T. V-T will say to take
|
* Coming from S via V to T, we end up with the legs S->V and V->T. V-T will say to
|
||||||
|
*take
|
||||||
* the second exit, even though counting from S it would be the third.
|
* the second exit, even though counting from S it would be the third.
|
||||||
* For S, we only emit `roundabout` without an exit number, showing that we enter a roundabout
|
* For S, we only emit `roundabout` without an exit number, showing that we enter a
|
||||||
|
*roundabout
|
||||||
* to find a via point.
|
* to find a via point.
|
||||||
* The same exit will be emitted, though, if we should start routing at S, making
|
* The same exit will be emitted, though, if we should start routing at S, making
|
||||||
* the overall response consistent.
|
* the overall response consistent.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
leg.steps = guidance::postProcess(std::move(steps));
|
leg.steps = guidance::postProcess(std::move(steps));
|
||||||
leg_geometry = guidance::resyncGeometry(std::move(leg_geometry),leg.steps);
|
guidance::trimShortSegments(leg.steps, leg_geometry);
|
||||||
|
leg.steps = guidance::assignRelativeLocations(std::move(leg.steps), leg_geometry,
|
||||||
|
phantoms.source_phantom,
|
||||||
|
phantoms.target_phantom);
|
||||||
|
leg_geometry = guidance::resyncGeometry(std::move(leg_geometry), leg.steps);
|
||||||
}
|
}
|
||||||
|
|
||||||
leg_geometries.push_back(std::move(leg_geometry));
|
leg_geometries.push_back(std::move(leg_geometry));
|
||||||
|
@ -62,25 +62,11 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
|
|||||||
std::size_t segment_index = 0;
|
std::size_t segment_index = 0;
|
||||||
BOOST_ASSERT(leg_geometry.locations.size() >= 2);
|
BOOST_ASSERT(leg_geometry.locations.size() >= 2);
|
||||||
|
|
||||||
// We report the relative position of source/target to the road only within a range that is
|
|
||||||
// sufficiently different but not full of the path
|
|
||||||
const constexpr double MINIMAL_RELATIVE_DISTANCE = 5., MAXIMAL_RELATIVE_DISTANCE = 300.;
|
|
||||||
const auto distance_to_start = util::coordinate_calculation::haversineDistance(
|
|
||||||
source_node.input_location, leg_geometry.locations[0]);
|
|
||||||
const auto initial_modifier =
|
|
||||||
distance_to_start >= MINIMAL_RELATIVE_DISTANCE &&
|
|
||||||
distance_to_start <= MAXIMAL_RELATIVE_DISTANCE
|
|
||||||
? angleToDirectionModifier(util::coordinate_calculation::computeAngle(
|
|
||||||
source_node.input_location, leg_geometry.locations[0], leg_geometry.locations[1]))
|
|
||||||
: extractor::guidance::DirectionModifier::UTurn;
|
|
||||||
|
|
||||||
if (leg_data.size() > 0)
|
if (leg_data.size() > 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
StepManeuver maneuver = detail::stepManeuverFromGeometry(
|
StepManeuver maneuver = detail::stepManeuverFromGeometry(
|
||||||
extractor::guidance::TurnInstruction{extractor::guidance::TurnType::NoTurn,
|
extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Depart, leg_geometry);
|
||||||
initial_modifier},
|
|
||||||
WaypointType::Depart, leg_geometry);
|
|
||||||
maneuver.location = source_node.location;
|
maneuver.location = source_node.location;
|
||||||
|
|
||||||
// PathData saves the information we need of the segment _before_ the turn,
|
// PathData saves the information we need of the segment _before_ the turn,
|
||||||
@ -134,9 +120,7 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
|
|||||||
// |---------| target_duration
|
// |---------| target_duration
|
||||||
|
|
||||||
StepManeuver maneuver = detail::stepManeuverFromGeometry(
|
StepManeuver maneuver = detail::stepManeuverFromGeometry(
|
||||||
extractor::guidance::TurnInstruction{extractor::guidance::TurnType::NoTurn,
|
extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Depart, leg_geometry);
|
||||||
initial_modifier},
|
|
||||||
WaypointType::Depart, leg_geometry);
|
|
||||||
int duration = target_duration - source_duration;
|
int duration = target_duration - source_duration;
|
||||||
BOOST_ASSERT(duration >= 0);
|
BOOST_ASSERT(duration >= 0);
|
||||||
|
|
||||||
@ -151,20 +135,9 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
|
|||||||
}
|
}
|
||||||
|
|
||||||
BOOST_ASSERT(segment_index == number_of_segments - 1);
|
BOOST_ASSERT(segment_index == number_of_segments - 1);
|
||||||
const auto distance_from_end = util::coordinate_calculation::haversineDistance(
|
|
||||||
target_node.input_location, leg_geometry.locations.back());
|
|
||||||
const auto final_modifier =
|
|
||||||
distance_from_end >= MINIMAL_RELATIVE_DISTANCE &&
|
|
||||||
distance_from_end <= MAXIMAL_RELATIVE_DISTANCE
|
|
||||||
? angleToDirectionModifier(util::coordinate_calculation::computeAngle(
|
|
||||||
leg_geometry.locations[leg_geometry.locations.size() - 2],
|
|
||||||
leg_geometry.locations[leg_geometry.locations.size() - 1],
|
|
||||||
target_node.input_location))
|
|
||||||
: extractor::guidance::DirectionModifier::UTurn;
|
|
||||||
// This step has length zero, the only reason we need it is the target location
|
// This step has length zero, the only reason we need it is the target location
|
||||||
auto final_maneuver = detail::stepManeuverFromGeometry(
|
auto final_maneuver = detail::stepManeuverFromGeometry(
|
||||||
extractor::guidance::TurnInstruction{extractor::guidance::TurnType::NoTurn, final_modifier},
|
extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Arrive, leg_geometry);
|
||||||
WaypointType::Arrive, leg_geometry);
|
|
||||||
steps.push_back(RouteStep{target_node.name_id,
|
steps.push_back(RouteStep{target_node.name_id,
|
||||||
facade.GetNameForID(target_node.name_id),
|
facade.GetNameForID(target_node.name_id),
|
||||||
ZERO_DURATION,
|
ZERO_DURATION,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#ifndef ENGINE_GUIDANCE_POST_PROCESSING_HPP
|
#ifndef ENGINE_GUIDANCE_POST_PROCESSING_HPP
|
||||||
#define ENGINE_GUIDANCE_POST_PROCESSING_HPP
|
#define ENGINE_GUIDANCE_POST_PROCESSING_HPP
|
||||||
|
|
||||||
|
#include "engine/phantom_node.hpp"
|
||||||
#include "engine/guidance/route_step.hpp"
|
#include "engine/guidance/route_step.hpp"
|
||||||
#include "engine/guidance/leg_geometry.hpp"
|
#include "engine/guidance/leg_geometry.hpp"
|
||||||
|
|
||||||
@ -16,6 +17,19 @@ namespace guidance
|
|||||||
// passed as none-reference to modify in-place and move out again
|
// passed as none-reference to modify in-place and move out again
|
||||||
std::vector<RouteStep> postProcess(std::vector<RouteStep> steps);
|
std::vector<RouteStep> postProcess(std::vector<RouteStep> steps);
|
||||||
|
|
||||||
|
// trim initial/final segment of very short length.
|
||||||
|
// This function uses in/out parameter passing to modify both steps and geometry in place.
|
||||||
|
// We use this method since both steps and geometry are closely coupled logically but
|
||||||
|
// are not coupled in the same way in the background. To avoid the additional overhead
|
||||||
|
// of introducing intermediate structions, we resolve to the in/out scheme at this point.
|
||||||
|
void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry);
|
||||||
|
|
||||||
|
// assign relative locations to depart/arrive instructions
|
||||||
|
std::vector<RouteStep> assignRelativeLocations(std::vector<RouteStep> steps,
|
||||||
|
const LegGeometry &geometry,
|
||||||
|
const PhantomNode &source_node,
|
||||||
|
const PhantomNode &target_node);
|
||||||
|
|
||||||
// postProcess will break the connection between the leg geometry
|
// postProcess will break the connection between the leg geometry
|
||||||
// for which a segment is supposed to represent exactly the coordinates
|
// for which a segment is supposed to represent exactly the coordinates
|
||||||
// between routing maneuvers and the route steps itself.
|
// between routing maneuvers and the route steps itself.
|
||||||
|
@ -22,7 +22,7 @@ StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instr
|
|||||||
|
|
||||||
double pre_turn_bearing = 0, post_turn_bearing = 0;
|
double pre_turn_bearing = 0, post_turn_bearing = 0;
|
||||||
Coordinate turn_coordinate;
|
Coordinate turn_coordinate;
|
||||||
if (waypoint_type == WaypointType::Arrive)
|
if (waypoint_type == WaypointType::Depart)
|
||||||
{
|
{
|
||||||
turn_coordinate = leg_geometry.locations.front();
|
turn_coordinate = leg_geometry.locations.front();
|
||||||
const auto post_turn_coordinate = *(leg_geometry.locations.begin() + 1);
|
const auto post_turn_coordinate = *(leg_geometry.locations.begin() + 1);
|
||||||
@ -31,7 +31,7 @@ StepManeuver stepManeuverFromGeometry(extractor::guidance::TurnInstruction instr
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(waypoint_type == WaypointType::Depart);
|
BOOST_ASSERT(waypoint_type == WaypointType::Arrive);
|
||||||
turn_coordinate = leg_geometry.locations.back();
|
turn_coordinate = leg_geometry.locations.back();
|
||||||
const auto pre_turn_coordinate = *(leg_geometry.locations.end() - 2);
|
const auto pre_turn_coordinate = *(leg_geometry.locations.end() - 2);
|
||||||
pre_turn_bearing =
|
pre_turn_bearing =
|
||||||
|
@ -2,12 +2,16 @@
|
|||||||
#include "extractor/guidance/turn_instruction.hpp"
|
#include "extractor/guidance/turn_instruction.hpp"
|
||||||
|
|
||||||
#include "engine/guidance/toolkit.hpp"
|
#include "engine/guidance/toolkit.hpp"
|
||||||
|
#include "engine/guidance/assemble_steps.hpp"
|
||||||
|
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
#include <boost/range/algorithm_ext/erase.hpp>
|
#include <boost/range/algorithm_ext/erase.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <cmath>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <limits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
using TurnInstruction = osrm::extractor::guidance::TurnInstruction;
|
using TurnInstruction = osrm::extractor::guidance::TurnInstruction;
|
||||||
@ -292,6 +296,131 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
|
|||||||
return steps;
|
return steps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
|
||||||
|
{
|
||||||
|
// Doing this step in post-processing provides a few challenges we cannot overcome.
|
||||||
|
// The removal of an initial step imposes some copy overhead in the steps, moving all later
|
||||||
|
// steps to the front.
|
||||||
|
// In addition, we cannot reduce the travel time that is accumulated at a different location.
|
||||||
|
// As a direct implication, we have to keep the time of the initial/final turns (which adds a
|
||||||
|
// few seconds of inaccuracy at both ends. This is acceptable, however, since the turn should
|
||||||
|
// usually not be as relevant.
|
||||||
|
|
||||||
|
if (steps.size() <= 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// if phantom node is located at the connection of two segments, either one can be selected as
|
||||||
|
// turn
|
||||||
|
//
|
||||||
|
// a --- b
|
||||||
|
// |
|
||||||
|
// c
|
||||||
|
//
|
||||||
|
// If a route from b to c is requested, both a--b and b--c could be selected as start segment.
|
||||||
|
// In case of a--b, we end up with an unwanted turn saying turn-right onto b-c.
|
||||||
|
// These cases start off with an initial segment which is of zero length.
|
||||||
|
// We have to be careful though, since routing that starts in a roundabout has a valid
|
||||||
|
// initial segment of length zero and we cannot delete the upcoming segment.
|
||||||
|
|
||||||
|
if (steps.front().distance <= std::numeric_limits<double>::epsilon() &&
|
||||||
|
!entersRoundabout((steps.begin() + 1)->maneuver.instruction))
|
||||||
|
{
|
||||||
|
// We have to adjust the first step both for its name and the bearings
|
||||||
|
const auto ¤t_depart = steps.front();
|
||||||
|
auto &designated_depart = *(steps.begin() + 1);
|
||||||
|
|
||||||
|
// FIXME this is required to be consistent with the route durations. The initial turn is not
|
||||||
|
// actually part of the route, though
|
||||||
|
designated_depart.duration += current_depart.duration;
|
||||||
|
|
||||||
|
geometry.locations.erase(geometry.locations.begin());
|
||||||
|
|
||||||
|
BOOST_ASSERT(geometry.segment_offsets[1] == 1);
|
||||||
|
// geometry offsets have to be adjusted. Move all offsets to the front and reduce by one.
|
||||||
|
std::transform(geometry.segment_offsets.begin() + 1, geometry.segment_offsets.end(),
|
||||||
|
geometry.segment_offsets.begin(), [](const std::size_t val)
|
||||||
|
{
|
||||||
|
return val - 1;
|
||||||
|
});
|
||||||
|
geometry.segment_offsets.pop_back();
|
||||||
|
|
||||||
|
// remove the initial distance value
|
||||||
|
geometry.segment_distances.erase(geometry.segment_distances.begin());
|
||||||
|
|
||||||
|
// update initial turn direction/bearings. Due to the duplicated first coordinate, the
|
||||||
|
// initial bearing is invalid
|
||||||
|
designated_depart.maneuver = detail::stepManeuverFromGeometry(
|
||||||
|
TurnInstruction::NO_TURN(), WaypointType::Depart, geometry);
|
||||||
|
|
||||||
|
// finally remove the initial (now duplicated move)
|
||||||
|
steps.erase(steps.begin());
|
||||||
|
|
||||||
|
// and update the leg geometry indices for the removed entry
|
||||||
|
std::for_each(steps.begin(), steps.end(), [](RouteStep &step)
|
||||||
|
{
|
||||||
|
--step.geometry_begin;
|
||||||
|
--step.geometry_end;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we still have enough segments
|
||||||
|
if (steps.size() <= 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto &next_to_last_step = *(steps.end() - 2);
|
||||||
|
// in the end, the situation with the roundabout cannot occur. As a result, we can remove all
|
||||||
|
// zero-length instructions
|
||||||
|
if (next_to_last_step.distance <= std::numeric_limits<double>::epsilon())
|
||||||
|
{
|
||||||
|
geometry.locations.pop_back();
|
||||||
|
geometry.segment_offsets.pop_back();
|
||||||
|
BOOST_ASSERT(geometry.segment_distances.back() < std::numeric_limits<double>::epsilon());
|
||||||
|
geometry.segment_distances.pop_back();
|
||||||
|
|
||||||
|
next_to_last_step.maneuver = detail::stepManeuverFromGeometry(
|
||||||
|
TurnInstruction::NO_TURN(), WaypointType::Arrive, geometry);
|
||||||
|
steps.pop_back();
|
||||||
|
// the geometry indices of the last step are already correct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign relative locations to depart/arrive instructions
|
||||||
|
std::vector<RouteStep> assignRelativeLocations(std::vector<RouteStep> steps,
|
||||||
|
const LegGeometry &leg_geometry,
|
||||||
|
const PhantomNode &source_node,
|
||||||
|
const PhantomNode &target_node)
|
||||||
|
{
|
||||||
|
// We report the relative position of source/target to the road only within a range that is
|
||||||
|
// sufficiently different but not full of the path
|
||||||
|
BOOST_ASSERT(steps.size() >= 2 );
|
||||||
|
BOOST_ASSERT(leg_geometry.locations.size() >= 2 );
|
||||||
|
const constexpr double MINIMAL_RELATIVE_DISTANCE = 5., MAXIMAL_RELATIVE_DISTANCE = 300.;
|
||||||
|
const auto distance_to_start = util::coordinate_calculation::haversineDistance(
|
||||||
|
source_node.input_location, leg_geometry.locations[0]);
|
||||||
|
const auto initial_modifier =
|
||||||
|
distance_to_start >= MINIMAL_RELATIVE_DISTANCE &&
|
||||||
|
distance_to_start <= MAXIMAL_RELATIVE_DISTANCE
|
||||||
|
? angleToDirectionModifier(util::coordinate_calculation::computeAngle(
|
||||||
|
source_node.input_location, leg_geometry.locations.at(0), leg_geometry.locations.at(1)))
|
||||||
|
: extractor::guidance::DirectionModifier::UTurn;
|
||||||
|
|
||||||
|
steps.front().maneuver.instruction.direction_modifier = initial_modifier;
|
||||||
|
|
||||||
|
const auto distance_from_end = util::coordinate_calculation::haversineDistance(
|
||||||
|
target_node.input_location, leg_geometry.locations.back());
|
||||||
|
const auto final_modifier =
|
||||||
|
distance_from_end >= MINIMAL_RELATIVE_DISTANCE &&
|
||||||
|
distance_from_end <= MAXIMAL_RELATIVE_DISTANCE
|
||||||
|
? angleToDirectionModifier(util::coordinate_calculation::computeAngle(
|
||||||
|
leg_geometry.locations.at(leg_geometry.locations.size() - 2),
|
||||||
|
leg_geometry.locations.at(leg_geometry.locations.size() - 1),
|
||||||
|
target_node.input_location))
|
||||||
|
: extractor::guidance::DirectionModifier::UTurn;
|
||||||
|
|
||||||
|
steps.back().maneuver.instruction.direction_modifier = final_modifier;
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
|
||||||
LegGeometry resyncGeometry(LegGeometry leg_geometry, const std::vector<RouteStep> &steps)
|
LegGeometry resyncGeometry(LegGeometry leg_geometry, const std::vector<RouteStep> &steps)
|
||||||
{
|
{
|
||||||
// The geometry uses an adjacency array-like structure for representation.
|
// The geometry uses an adjacency array-like structure for representation.
|
||||||
@ -308,7 +437,7 @@ LegGeometry resyncGeometry(LegGeometry leg_geometry, const std::vector<RouteStep
|
|||||||
leg_geometry.segment_offsets.push_back(step.geometry_end - 1);
|
leg_geometry.segment_offsets.push_back(step.geometry_end - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
//remove the data fromt the reached-target step again
|
// remove the data fromt the reached-target step again
|
||||||
leg_geometry.segment_offsets.pop_back();
|
leg_geometry.segment_offsets.pop_back();
|
||||||
leg_geometry.segment_distances.pop_back();
|
leg_geometry.segment_distances.pop_back();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user