Skip to content

Car loops caused by turn restrictions - #3399

Merged
gmellemstrand merged 15 commits into
opentripplanner:dev-2.xfrom
mfdz:car-loops
Apr 14, 2021
Merged

gmellemstrand merged 15 commits into
opentripplanner:dev-2.xfrom
mfdz:car-loops

Conversation

@leonardehrenfried

@leonardehrenfried leonardehrenfried commented Apr 1, 2021 •

Copy link
Copy Markdown
Member

Summary

This problem has been discussed in the OTP2 dev meeting and is described in #3393.

Short recap: (Car) routes with required loops (example) lead to an error.

Our solution is to allow those loops close to the start or end of the route by adding a check in the dominance function. This is the actual change of the PR: 88f28d0

It also updates Junit to the latest version.

Alternative solutions

Both @flaktack and @gmellemstrand have made suggestions how the problem could be solved:

  • you could simply allow loops anywhere (but that will increase the search space)
  • you could allow only a certain length of loop, say 1 kilometer. however, it doesn't seem like the distance driven is currently tracked in State so that would need to be added.

Lets discuss in the next dev meeting.

Issue

Fixes #3393
Fixes #3402

Unit tests

The bulk of the PR actually consists of unit tests. It introduces a technique not used a lot in current OTP tests: It builds a graph from actual OSM data, runs queries on it and uses the resulting polyline for assertions.

For tricky situations like the one we are discussing here I find it hard to reproduce the issues by building up individual edges manually and therefore opted for this approach. It also helps to build up a corpus of routes that make it easier to tell if an algorithm change has unintended side effects.

The downside is that building the graph takes longer than the ordinary test setup: building the graph for a small city like Herrenberg takes around 3-4 seconds. We should discuss if this is too high a price to pay or if restructuring these tests (perhaps by running them in a separate integration test phase) is worth doing.

In order to achieve this I introduced an interface for the graph sources so that it's easier to specify the exact OSM and GTFS files in a test graph.

This PR also introduces a new helper tool to give you a nice error message when two polylines are not the same. It links you to a website where you can see the lines visualised on a map.

Code style

✅

Documentation

✅

Changelog

✅

@leonardehrenfried
leonardehrenfried requested a review from a team April 1, 2021 14:15
@leonardehrenfried leonardehrenfried changed the title WIP: Car loops WIP: Car loops caused by turn restrictions Apr 1, 2021
@leonardehrenfried leonardehrenfried changed the title WIP: Car loops caused by turn restrictions Car loops caused by turn restrictions Apr 1, 2021
}
}

public static Graph buildGraph(String path) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This code was unused.

return instance;
}

public GtfsContext getPortlandContext() {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This code was unused.

@leonardehrenfried
leonardehrenfried force-pushed the car-loops branch 2 times, most recently from 52bcbb2 to b9ae55f Compare April 1, 2021 15:44
@t2gran t2gran added this to the 2.1 milestone Apr 1, 2021
@leonardehrenfried
leonardehrenfried force-pushed the car-loops branch 2 times, most recently from a8d7c7b to 514d197 Compare April 6, 2021 12:55
@leonardehrenfried

Copy link
Copy Markdown
Member Author

After our conversation today at the OTP2 meeting I reworked this PR a little and have some results so share.

Test graph building speed

I reduced the size of the test graph by both using a smaller area and stripping out anything that is not a road. Building the graph is now reported to take 0 seconds, so presumably it's only a few hundred milliseconds. I would say @t2gran's concerns are addressed.

Performance impact

The good news is that I could generate a trip from Oslo to Trondheim which is a 480km trip.

Screenshot from 2021-04-06 14-45-38

Sadly the performance hit is quite significant: before the change it would take 2.5 seconds to calculate and after removing the turn restrictions check it takes around 4 seconds. 😿

I'm not sure if this is an acceptable slowdown.

@leonardehrenfried

Copy link
Copy Markdown
Member Author

I've taken a more scientific approach and benchmarked the various implementations more thoroughly.

To do this, I've used apache bench (ab) to run first 100 warm-up requests and then 10 requests to do the actual measuring.

Commands

Warm up

ab -n 100 -c 5 "http://localhost:8080/otp/routers/default/plan?fromPlace=59.9295600665038%2C10.767974853515625&toPlace=63.43331707559086%2C10.4205322265625&time=3%3A03pm&date=04-06-2021&mode=CAR&arriveBy=false&wheelchair=false&debugItineraryFilter=false&locale=en"

Measuring

ab -n 10 "http://localhost:8080/otp/routers/default/plan?fromPlace=59.9295600665038%2C10.767974853515625&toPlace=63.43331707559086%2C10.4205322265625&time=3%3A03pm&date=04-06-2021&mode=CAR&arriveBy=false&wheelchair=false&debugItineraryFilter=false&locale=en"

Results

No loops allowed (current dev-2.x)

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:  2168 2451 292.3   2358    2939
Waiting:     2167 2449 292.2   2357    2937
Total:       2168 2451 292.3   2358    2939

Loops are always allowed

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       0
Processing:  3395 4344 566.4   4494    5026
Waiting:     3392 4337 572.1   4489    5026
Total:       3395 4344 566.4   4494    5026

Only at start or end

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:  2001 2530 806.6   2349    4756
Waiting:     2000 2529 806.7   2348    4755
Total:       2001 2530 806.7   2349    4756

Lets discuss these numbers in the next dev meeting.

@leonardehrenfried
leonardehrenfried force-pushed the car-loops branch 8 times, most recently from 9f84369 to 1b0d737 Compare April 9, 2021 08:32
* The following variable determines how close you have to be to the start or the end point for it to be
* considered "close enough for a loop".
*/
if (a.backEdge != b.getBackEdge()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should there be a check for car mode here?

@leonardehrenfried leonardehrenfried Apr 9, 2021 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Well, technically turn restrictions also apply to cycling, however you can always get off and push the bike. Maybe this is a good idea.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

So I checked my test case and if I only allow the loop for cars the bike will take a route like this: https://leonard.io/polyline-visualiser/?base64=b3VxZ0h9bWN1QGdBRV1VfUJhQV1RfUBddUFzQFtTQW1ARWVAQVVHQkFIS0Y_SFNIR3hA

That's ok, I guess.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I see your point that it may be relevant to biking too. I will leave it up to you whether to include biking or. not, as you have worked more on bike routing than me

@gmellemstrand

Copy link
Copy Markdown
Contributor

I discussed this a little bit with @flaktack in the chat window of the last OTP meeting. I think for one-to-many searches we would need to check for loop edges for the entire search, as we do not know if we are close to one of the acceptable destinations.

@leonardehrenfried
leonardehrenfried force-pushed the car-loops branch 2 times, most recently from 415775c to bb6fef7 Compare April 9, 2021 10:58
@leonardehrenfried

Copy link
Copy Markdown
Member Author

I discussed this a little bit with @flaktack in the chat window of the last OTP meeting. I think for one-to-many searches we would need to check for loop edges for the entire search, as we do not know if we are close to one of the acceptable destinations.

How would you address this issue?

@leonardehrenfried

Copy link
Copy Markdown
Member Author

@t2gran I've made the testing part of the PR a lot less invasive which makes this a lot easier to review.

@abyrd I've used the method you suggested to compute the "closeness": 0a80bf0

I visualised the envelopes and they produce quite symmetric squares:

Screenshot from 2021-04-09 09-38-07

@leonardehrenfried
leonardehrenfried force-pushed the car-loops branch 2 times, most recently from 7d2a8f1 to c3438f9 Compare April 9, 2021 11:08
@gmellemstrand

Copy link
Copy Markdown
Contributor

I discussed this a little bit with @flaktack in the chat window of the last OTP meeting. I think for one-to-many searches we would need to check for loop edges for the entire search, as we do not know if we are close to one of the acceptable destinations.

How would you address this issue?

I think the isCloseToStartOrEnd should always return true if there are no from/to vertices (and document it on the method). I also see now that you are checking the RoutingRequest.from, when it would probably be more correct to check the RoutingContext.fromVertices and RoutingContext.toVertices. These are now used to search from/to from all stops within a station (which would be in quite close proximity, but there is really no guarantee that they will be).

This would require a check on all from/to vertices, to see whether they are inside the envelopes, but that should not be too expensive.

@t2gran t2gran left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is almost good to go from a technical point, I have not looked at the functionality.

The addition of the dev-doc is great.

Comment on lines +89 to +93
if (backEdgeHasTurnRestrictions(a)
/**
* {@link DominanceFunction.MAX_METERS_ROUTE_LOOPS}
*/
|| a.getBackMode() != TraverseMode.WALK && isCloseToStartOrEnd(a.getVertex(), a.getOptions())) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

JavaDoc inside expression?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think you commented on an outdated version, right?

@DisplayName("car routes can contain loops (traversing the same edge twice)")
public void shouldAllowLoopCausedByTurnRestrictions() {
var hindenburgStrUnderConstruction = ConstantsForTests.buildOsmGraph(
ConstantsForTests.HERRENBERG_HINDENBURG_STR_UNDER_CONSTRUCTION_OSM);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Egyptian: (), break end down.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I used the Intellij code style supplied by you to automatically format this file. Is this not the result you were expecting?

computePolyline(hindenburgStrUnderConstruction, gueltsteinerStr, aufDemGraben);

assertThatPolylinesAreEqual(
polyline, "ouqgH}mcu@gAE]U}BaA]Q}@]uAs@[SAm@Ee@AUEi@XEQkBQ?Bz@Dt@Dh@@TGBC@KBSHGx@");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

() break end down

var itineraries = GraphPathToItineraryMapper.mapItineraries(paths, request);
// make sure that we only get CAR legs
itineraries.forEach(
i -> i.legs.forEach(l -> Assertions.assertEquals(l.mode, TraverseMode.CAR)));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

()

@leonardehrenfried

Copy link
Copy Markdown
Member Author

@t2gran I've reimported the Intellij style but it didn't autoformat to Egyptian brackets so I did it manually.

@leonardehrenfried

Copy link
Copy Markdown
Member Author

@gmellemstrand I've changed the JavaDoc to take the limitations of this approach into account: 23ee8a6

Can you check that I understood everything ok?

t2gran
t2gran previously approved these changes Apr 13, 2021

@t2gran t2gran left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you for doing this. I reviewed the two last commits, which complete what we talked about in todays developer meeting.

@gmellemstrand

Copy link
Copy Markdown
Contributor

@gmellemstrand I've changed the JavaDoc to take the limitations of this approach into account: 23ee8a6

Can you check that I understood everything ok?

I think what you wrote is sufficient. It gets the point across that the check will have to be expanded if these limitations are not acceptable.

@gmellemstrand gmellemstrand left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since only documentation and merging adjustments have been done after @t2gran 's review, I will approve this.

@gmellemstrand
gmellemstrand merged commit 77d24b6 into opentripplanner:dev-2.x Apr 14, 2021
@hbruch
hbruch deleted the car-loops branch July 28, 2024 08:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Upgrade to JUnit 5 Car routes with required loops lead to no route results being returned

3 participants