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

Suppress turns emitted while on ferry #3420

Merged
merged 3 commits into from Dec 23, 2016
Merged

Suppress turns emitted while on ferry #3420

merged 3 commits into from Dec 23, 2016

Conversation

karenzshea
Copy link
Contributor

@karenzshea karenzshea commented Dec 7, 2016

Issue

Fixes #3225

Tasklist

  • add regression / cucumber cases (see docs/testing.md)
  • review
  • adjust for comments

Requirements / Relations

cc @MoKob @daniel-j-h

@karenzshea karenzshea added this to the 5.6.0 milestone Dec 7, 2016
@karenzshea karenzshea requested a review from MoKob December 7, 2016 21:32
@karenzshea karenzshea removed the request for review from MoKob December 7, 2016 22:07
@emiltin
Copy link
Contributor

emiltin commented Dec 7, 2016

the same should probably apply to other types of routes, like trains.

@TheMarex
Copy link
Member

TheMarex commented Dec 7, 2016

Hm could we handle this in pre-processing instead of post-processing? Turning from a starting_point = false to another starting_point = false node should be a NoTurn.

/cc @MoKob

@MoKob
Copy link

MoKob commented Dec 8, 2016

@TheMarex you are correct. If we assume for ferries always to be staring_point == false. But we don't event need this assumption. We know the travel modes. So we can check TravelMode==Ferry in Preprocessing and not emit any instruction.

@daniel-j-h
Copy link
Member

Sorry my mistake here. I implemented lane removal from roundabouts in post-processing and was under the impression @karenzshea could do the same quick fix there, too.

Now as @MoKob today told me in the context of #2705 is that suppressed + use lane could still happen. The easier and correct solution is to not emit them in pre-processing only.

@karenzshea take a look at the changes in https://github.com/Project-OSRM/osrm-backend and how @MoKob handles not collapsing inside roundabouts and not assigning lanes there.

Maybe this is a good start.

@@ -51,6 +51,11 @@ TurnType::Enum IntersectionHandler::findBasicTurnType(const EdgeID via_edge,
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
const auto &out_data = node_based_graph.GetEdgeData(road.eid);

// suppress instructions on edges that the can't control anyway
if (!in_data.startpoint && !out_data.startpoint)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my view, we shouldn't abuse is startpoint here (and below). We want to filter out Ferry turns. As already stated in the discussion, we should use the TravelMode Ferry to do so. Someone might decide that starting on other roads is invalid, but we know that jumping ship is not possible. If someone disables motorways as start points, we might still want to give instructions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree here. This would mean that you need to add explicit handling based on mode for every mode you want to support. That means the probability that we need to find and adjust this piece of code in the future is very high, which I think is undesirable and duplicates logic from the profile.

Using startingpoint uses a concept that we already expose in the profiles: Flagging ways that should be threaded as a single 'unit' from a user perspective. The name might be good or bad but this is effectively what this does. I don't see any scenario where we would reasonably emit an instruction between edges of this type. Any division between steps is done by the routing engine for technical reasons not user intend.

I feel this will be easier to use from a user perspective and offer less surprises then explicitly needing to cycle back every time a user finds a use-case for having ferry-like edges.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem I see here is that what we snap to and what we consider as connections in forms of a turn are two entirely different concepts to me. Ferries are special in terms of that they often share ways in OSM data but don't allow switching from one item to another in between.

If we want to specify that snapping to is equivalent to does not allow turning, then you would be right. But as long as we don't make a distinction here, using startingpoint would not capture the issue we see in ferries.

The concept we need are ways that share segments but are distinct (right now we don't handle these correctly in all cases, since we can right now actually switch ferries in between). Ferries will need specific handling anyhow. The main question for me here is: do we really want to tie snapping to turn instructions?

/cc @daniel-j-h @danpat for additional oppinions

@MoKob
Copy link

MoKob commented Dec 9, 2016

Just a heads up. I've added a small test

@MoKob
Copy link

MoKob commented Dec 9, 2016

To explain the reasoning behind my test-case:

I have the feeling that the two locations currently modified in this PR might not catch all ferry cases. In my view it would be easier to do a dedicated ferry handler in the beginning that checks for allFerry and immediately returns all NoTurns.

@@ -53,4 +55,7 @@ const constexpr osrm::extractor::TravelMode TRAVEL_MODE_RIVER_UP = 10;
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_RIVER_DOWN = 11;
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_ROUTE = 12;

// travel modes for which navigation should be suppressed
const std::array<osrm::extractor::TravelMode, 2> constexpr SUPPRESS_MODE_LIST = {{TRAVEL_MODE_TRAIN, TRAVEL_MODE_FERRY}};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with using std::array here is the size has to be known at compile time. If you now add an additional mode you have to go through the code base and change all array<T, N> to array<T, N + 1>.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this could be solved using a type alias though. using SupressionModeArray = std::array<osrm::extractor::TravelMode, 2>;

MoKob
MoKob previously requested changes Dec 13, 2016
Given the profile "car"
Given a grid size of 20 meters

Scenario: Collapse Steps While On Ferry
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you maybe directly add the ascii-art to make the tests more readable to begin with? (here and on the other tests)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all the tests are rendered in ASCII now

| waypoints | route | turns | modes |
| f,j | land,sea,land,land | depart,notification right,turn left,arrive | driving,ferry,driving,driving |

Scenario: Collapse Steps While On Ferry
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we could remove this test. It looks like the only difference here to the test above is an additional coordinate on the ferry route, which should not influence any turn decisions.

| bc | primary | ferry | pennydog-island-ferry |

When I route I should get
| waypoints | route | turns | modes |
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

misaligned, also I miss my monkey island reference...

const TravelMode &out_mode,
const SuppressModeListT &SUPPRESS_MODE_LIST) const;

bool SuppressModeNavigation(const EdgeID &via_edge,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

via_edge/connected are unintuitive to me. how about from_edge and to_edge? or in_edge and out_edge?

{
const auto suppress_in_mode = std::find(begin(SUPPRESS_MODE_LIST), end(SUPPRESS_MODE_LIST), in_mode);
if (suppress_in_mode != end(SUPPRESS_MODE_LIST))
{
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor nitpick: the check for in_mode == out_mode is cheaper than the algorithm call. So doing it the other way around would be faster:

if( in_mode != out_mode )
 return false;

const auto suppress_in_mode = std::find(begin(SUPPRESS_MODE_LIST), end(SUPPRESS_MODE_LIST), in_mode);

return (suppress_in_mode != SUPPRESS_MODE_LIST.end();

In addition: using end like this requires the inclusion of iterator and a using std::end.
In this case, it's probably easier to simply call SUPPRESS_MODE_LIST.end().

@@ -37,13 +38,41 @@ IntersectionHandler::IntersectionHandler(const util::NodeBasedDynamicGraph &node
{
}

bool IntersectionHandler::SuppressModeNavigation(const EdgeID &via_edge,
const EdgeID &connected,
const SuppressModeListT &SUPPRESS_MODE_LIST) const
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it would be cleaner to not pass the mode list as a parameter.
Since you already define it globally in travel_mode.hpp, you are essentially shadowing the name.

Shadowing describes a situation where you use the same name (here SUPPRESS_MODE_LIST) for two variables that are available at the same time. (https://en.wikipedia.org/wiki/Variable_shadowing). Simply accessing the global variable would be cleaner, in my opinion, since I don't see a reason where we would require greater flexibility at some point.

TurnType::Enum IntersectionHandler::findBasicTurnType(const EdgeID via_edge,
const ConnectedRoad &road) const
{

const auto &in_data = node_based_graph.GetEdgeData(via_edge);
const auto &out_data = node_based_graph.GetEdgeData(road.eid);

const auto in_mode = node_based_graph.GetEdgeData(via_edge).travel_mode;
const auto out_mode = node_based_graph.GetEdgeData(road.eid).travel_mode;
if (SuppressModeNavigation(in_mode, out_mode, SUPPRESS_MODE_LIST))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per #3420 (comment), I've added a test. Using the three fallbacks is not sufficient to suppress all turns in ferries. I've added a test that still emits a turn on a ferry.

To fully solve this, I'd propose a dedicated handler (see previous comment). The handler should check all pairs of roads for same travel mode (e.g std::all_of(intersection,SuppressModeNavigation(first_mode,road_mode)) to check if it is applicable and simply assign NoTurn to all turns in its operator().

@karenzshea karenzshea changed the title Collapse turns emitted while on ferry Suppress turns emitted while on ferry Dec 13, 2016
return node_based_graph.GetEdgeData(way.eid).travel_mode == in_mode;
});

if (all_share_mode)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the reasoning for only suppressing when all outgoing edges share the mode.

The scenario I'm thinking of is a multi-stop ferry journey like this:

http://map.project-osrm.org/?z=12&center=48.908511%2C-123.166180&loc=49.035392%2C-123.086014&loc=48.782211%2C-123.181801&hl=en&alt=0

Should we suppress the instruction at the stopover point? I'm kind of torn.

What you've got should work great for ferry route splits in the middle of the ocean though. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally I'd been selectively suppressing only instructions onto ways that involved modes on the mode blacklist, but this failed a test added by @MoKob wherein there is an intersection with a new ferry line and a land-based way.

https://github.com/Project-OSRM/osrm-backend/pull/3420/files#diff-0bf26f655163cdd60bedcd61e763028dR35

The test is written such that the instruction at the stopover point is expected to be suppressed.

My thinking on this is that without a destination based instruction when the first ferry line is boarded, it's not exactly useful to emit an instruction at the "stopover" if there really is a stopover at all or just a change of direction? Do we have a way of knowing like in the test above whether or not a switch to a new ferry involves user action? I actually would kind of assume that switching to a new ferry line necessitates user action, but I defer to @MoKob who wrote the original test.

Copy link
Member

@danpat danpat Dec 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, ok. I guess there's a larger discussion to be had around route relations, ferries/trains changing name, stopovers, etc, but I think the logic here handles the egregious cases that the original ticket identified. 👍 on this approach for now.

namespace guidance
{

class SuppressModeHandler : public IntersectionHandler
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can make it SuppressModeHandler final : - all handler could be final except the base class IntersectionHandler

@@ -53,4 +56,7 @@ const constexpr osrm::extractor::TravelMode TRAVEL_MODE_RIVER_UP = 10;
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_RIVER_DOWN = 11;
const constexpr osrm::extractor::TravelMode TRAVEL_MODE_ROUTE = 12;

// travel modes for which navigation should be suppressed
const osrm::extractor::SuppressModeListT constexpr SUPPRESS_MODE_LIST = {{TRAVEL_MODE_TRAIN, TRAVEL_MODE_FERRY}};

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be in the SuppressModeHandler then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohh good thinking, this doesn't need to be a global anymore

@@ -44,6 +45,9 @@ TurnType::Enum IntersectionHandler::findBasicTurnType(const EdgeID via_edge,
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
const auto &out_data = node_based_graph.GetEdgeData(road.eid);

const auto in_mode = node_based_graph.GetEdgeData(via_edge).travel_mode;
const auto out_mode = node_based_graph.GetEdgeData(road.eid).travel_mode;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm? Dead code?

Intersection SuppressModeHandler::
operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection intersection) const
{
const auto in_mode = node_based_graph.GetEdgeData(intersection[0].eid).travel_mode;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the travel mode for the UTurn road?


// if all out ways share the same mode as the approach way, suppress all the turns
const auto all_share_mode =
std::all_of(begin(intersection) + 1, end(intersection), [this, &in_mode](ConnectedRoad way) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const auto& road

std::for_each(begin(intersection) + 1, end(intersection), [&](ConnectedRoad &out_way) {
out_way.instruction = {TurnType::Suppressed, getTurnDirection(out_way.angle)};
});
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you

auto first = begin(intersection) + 1;
auto last = end(intersection);

above you can re-use them here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

{
intersection = suppress_mode_handler(
node_prior_to_intersection, entering_via_edge, std::move(intersection));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your handler should come first, since it could influence others.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also if your handler fires you have to disable all other handlers. Otherwise it could be that e.g. your handler changes instructions to NoTurn and now the Sliproad Handler accidentally is getting confused with the NoTurn and classifies this as a Sliproad.

Make the handler come first and if it triggers skip all other handler.

@MoKob MoKob dismissed their stale review December 15, 2016 11:10

no specific reason

@daniel-j-h
Copy link
Member

Mason macOS build is failing due to std::abs issue already stumbled upon in #1000.

@springmeyer you looked into this back then, any ideas why it is failing only now on this branch and e.g. not on master? The file failing to compile is the same as on master after all.

Build is

https://travis-ci.org/Project-OSRM/osrm-backend/builds/183957985

and error is at

https://travis-ci.org/Project-OSRM/osrm-backend/jobs/183957988#L1179

@oxidase
Copy link
Contributor

oxidase commented Dec 21, 2016

@daniel-j-h just rebase is needed. It was "fixed" by b1aced3#diff-4ff77436d81ab7e8c0c026734a2d79cdR6
in #3409

@daniel-j-h
Copy link
Member

Rebased and refactored a bit. @MoKob could you have a final look over this.

Note: needs to be squashed before merging.

@daniel-j-h daniel-j-h dismissed their stale review December 21, 2016 11:34

Taking this over - dismissing my own review here.

@daniel-j-h daniel-j-h self-assigned this Dec 21, 2016
- Bugfixes
- Fix #3475 removed an invalid `exit` field from the `arrive` maneuver
- Guidance
- No longer emitting turns on ferries, if a ferry should use multiple docking locations
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm @MoKob, I just wanted to add one but it looks like you already added a changelog entry here - what's missing then?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah. I just added one, after I said it in chat. Sorry for not updating you. Other than that, if you are OK with my last changes, it should be good to go.

@daniel-j-h daniel-j-h merged commit b9b52cb into master Dec 23, 2016
@TheMarex TheMarex deleted the fairy-collapse branch December 23, 2016 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants