-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Remove safety normalizer #6782
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
base: dev-2.x
Are you sure you want to change the base?
Remove safety normalizer #6782
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## dev-2.x #6782 +/- ##
=============================================
+ Coverage 72.15% 72.18% +0.02%
- Complexity 19782 19848 +66
=============================================
Files 2151 2156 +5
Lines 79981 80097 +116
Branches 8061 8075 +14
=============================================
+ Hits 57713 57816 +103
- Misses 19422 19432 +10
- Partials 2846 2849 +3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
# Conflicts: # application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java # application/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/ElevationSnapshotTest.snap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing the safety normalizer in practice reduces the cost of cycling a bit. This effect could obviously be balanced by manually updating the bicycle safety factor values to be slightly higher, but I'm not sure if we want to do that. We could also adjust the default cycling reluctance to be higher but that would only affect those who use the default.
...cation/src/main/java/org/opentripplanner/graph_builder/module/osm/SafetyValueNormalizer.java
Outdated
Show resolved
Hide resolved
| * Further reading: <a href="https://github.com/opentripplanner/OpenTripPlanner/issues/4442">Issue 4442</a> | ||
| * <a href="https://github.com/opentripplanner/OpenTripPlanner/issues/6775">Issue 6775</a> | ||
| */ | ||
| class SafetyValueNormalizer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should rename this as this has now been repurposed. I tried to about a name for this but couldn't come up with one quickly. You can try to come up with a new name or we can decide in a dev meeting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should call this SafetyValueApplier?
application/src/main/java/org/opentripplanner/street/model/StreetLimitationParameters.java
Outdated
Show resolved
Hide resolved
| public EuclideanRemainingWeightHeuristic(Float maxCarSpeed) { | ||
| this.maxCarSpeed = maxCarSpeed != null ? maxCarSpeed : DEFAULT_MAX_CAR_SPEED; | ||
| public EuclideanRemainingWeightHeuristic( | ||
| StreetLimitationParametersService streetLimitationParametersService |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we should pass in this service or just the three parameters that are relevant here.
.../main/java/org/opentripplanner/street/search/strategy/EuclideanRemainingWeightHeuristic.java
Show resolved
Hide resolved
.../main/java/org/opentripplanner/street/search/strategy/EuclideanRemainingWeightHeuristic.java
Outdated
Show resolved
Hide resolved
.../main/java/org/opentripplanner/street/search/strategy/EuclideanRemainingWeightHeuristic.java
Outdated
Show resolved
Hide resolved
| StreetLimitationParametersService DEFAULT = new StreetLimitationParametersService() { | ||
| @Override | ||
| public float getMaxCarSpeed() { | ||
| return DEFAULT_MAX_CAR_SPEED; | ||
| } | ||
|
|
||
| @Override | ||
| public float getBestWalkSafety() { | ||
| return 1.0f; | ||
| } | ||
|
|
||
| @Override | ||
| public float getBestBikeSafety() { | ||
| return 1.0f; | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit of an unusual approach for constructing defaults. I'm not sure if we are ok with this, we can discuss it in a dev meeting.
...java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/BikeRentalSnapshotTest.snap
Outdated
Show resolved
Hide resolved
application/src/test/java/org/opentripplanner/street/model/_data/SimpleConcreteEdge.java
Show resolved
Hide resolved
# Conflicts: # application/src/main/java/org/opentripplanner/graph_builder/module/osm/SafetyValueNormalizer.java # application/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/ElevationSnapshotTest.snap
|
Commenting after a little discussion in last Tuesday's developer meeting. This PR and the referenced issue #4442 show that normalization could be removed, and how it could be removed while keeping heuristics admissible. But the question arises of why we would want to remove it, and whether the benefits are significant enough to motivate the surrounding changes. Are the reasons still the same as stated in #4442? Namely:
In that ticket, these are described as a "minor inconvenience" and a decision to not change the normalization or heuristics was documented there. In addition, @t2gran commented that he did not want to remove the normalization and found it simpler to reason about. It seems that a decision to make these changes would be dependent on overriding that past decision. Is there a reason removing normalization has become more pressing now? I see that there is some issue communicating with end users about the meaning, but might that be resolved by rewording the explanations? I also don't think these reluctance parameters were ever meant to be exposed directly to end users - we should make sure this is an intentional decision. |
|
The reasons stated in #4442 have become a serious problem for us when we are trying to introduce the safety feature to our users. All the bicycle journeys got inflated generalized cost resulting in the backend suggesting taking transport even when the user wants to cycle more, because there are a few ways with exceptionally low safety values such as 0.25 due to some combination of tags not foreseen when the mapper was written, resulting in normal roads forming the majority of the map inflated from 1 to 4. This normalizing has complicated the matter for both the people making the mapper, and the user needing to set an appropriate value, so it should be removed. The ability to set a custom reluctance factor on a sliding scale is the whole reason why Aubin exists nowadays. |
To clarify what I mean here: |
|
Yes. I may need to adjust the wording however the intention is the same. The problem with the normalizer here is that, if there exists even one single road in the map with safety value 0.25, all roads with default safety (i.e. 1.0) suddenly becomes 4.0 in practice, so the system thinks that all roads are dangerous and tells the user to take public transport instead, if the user wants to use a safe route. |
It's been a while since I used any safety factors so I wasn't understanding the problem at first. I now see the problem. Although the reluctance factors are multiplicative factors, so have the same relative effect on street routing independent of the cost of a "normal" road", those renormalized bike safety costs are being directly compared against (and combined with) transit costs that are in uninflated seconds of in-vehicle time. On some level it seems reasonably correct though. Bicycling on the best, safest bicycle path around is the reference point for a good safe bicycle ride, on par with riding inside a transit vehicle, and in a context where such paths exist, bicycling on normal roads with car traffic really is 4 times worse (in reality possibly much worse than that, a quick look at the literature makes me think a factor of 25 is reasonable). I am a little hesitant about the meaning of sub-1.0 factors for safe bike paths, with 1.0 factors for "normal" roads. Doesn't this imply that riding a bicycle on a car road is just as safe as riding inside a transit vehicle? Again, it's been a long time since I used these factors so I'm looking into it now to refresh my understanding in the interest of reviewing this PR. |
Well, all roads that are not bicycle paths completely separated from traffic are dangerous to bicyclists. Generally vastly more dangerous than being inside a metal transit vehicle. Do we want the safety model to align with people's (often incorrect) intuitive perception of safety, rather than measurable reality? People underestimate how extremely dangerous being in the same space with moving cars is. In the same way people are afraid of flying but vastly more likely to be killed crossing the street on the way to the airport. |
|
We can further discuss this later - when safety factor is 1.0 and bike reluctance is 1.0, shall we treat cycling on normal roads the same as taking transit (making cycling on safe streets preferable to transit), or cycling on safest streets the same as taking transit (making cycling on normal roads worse than taking transit). I can accept both options and would like the community to decide, as I can easily explain to the user. There is also a problem with the "safest streets" option which is supposed to exaggerate the safety even more than normal - it never works with the normalizer so in effect it is the same as "safe streets", and I have a follow up PR to fix this problem. |
|
An objective measure of safety factors is the death statistics. For example, if a death (someone getting killed) is 5x more likely on a certain class of road compared to riding transit for the same duration, the safety factor of that class of the road would be 5. The effect is that, if we choose a safe route, we are minimalising the likelihood of losing lives. If we want to adopt this, not only we still have to remove the normalizer, but we also need to introduce safety factors for car driving as well (because driving a car is also more deadly than taking transit). |
|
Summary of the discussion in the dev meeting @t2gran:
|
application/src/main/java/org/opentripplanner/graph_builder/module/osm/SafetyValueApplier.java
Show resolved
Hide resolved
| * Further reading: <a href="https://github.com/opentripplanner/OpenTripPlanner/issues/4442">Issue 4442</a> | ||
| * <a href="https://github.com/opentripplanner/OpenTripPlanner/issues/6775">Issue 6775</a> | ||
| */ | ||
| class SafetyValueNormalizer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should call this SafetyValueApplier?
application/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java
Show resolved
Hide resolved
|
|
||
| private double getCyclingCostPerDistance(BikePreferences preferences) { | ||
| return getCostPerDistance( | ||
| preferences.speed(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we also scale the speed based on the triangle preferences? It's a bit complex problem what we should and shouldn't take into account here since there is also the slope factor, and we don't really want to overestimate the minimum cost here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will be too complicated to take elevation into the account when calculating the heuristic. It may pose a problem when the journey is totally downhill.
If anyone knows how I can guess the minimum for a downhill value I am happy to implement it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The speed always affects the cost, it is not relevant to the triangle preference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again the original concept was: all configurable cost factors should only ever increase cost. So specifically: adding elevation data should only ever increase cost above the baseline, not decrease it. If you are applying cost-transforms that can decrease cost (yield fractional multiplers in [0, 1)), the minimum factor would need to be found and accounted for in the heuristic. Otherwise the heuristic is just not "admissible" and the algorithm is going to return suboptimal paths. Strictly speaking coasting downhill is easier than rolling on perfectly flat ground, but is it more important to model this "cost rebate" of coasting downhill, or to keep your heuristic admissible? Priorities will need to be chosen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think our strategy so fare has been to "keep your heuristic admissible" - I do not want to change the strategy, unless there is a very good reason - preferable analyzed and described in a document or issue summary.
# Conflicts: # application/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/ElevationSnapshotTest.snap
# Conflicts: # application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/DirectStreetRouter.java # application/src/main/java/org/opentripplanner/routing/impl/GraphPathFinder.java # application/src/test/java/org/opentripplanner/astar/AStarTest.java # application/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/BikeRentalSnapshotTest.snap # application/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/ElevationSnapshotTest.snap # application/src/test/java/org/opentripplanner/street/integration/BikeRentalTest.java
|
This was discussed at length in the developer meeting on 2025-10-14. Among those with any opinion on the subject, there is support for merging the changes in this PR. But it would need to be combined with additional changes that would adjust any defaults and hard-coded values, making the sequence of commits a pure refactor with no confusing side-effects from the perspective of people deploying OTP. This PR should not be merged into dev until those follow-up changes have also been thought through and approved. So probably two PRs that will be merged at once. |
It is impossible to give an accurate number after fixing this because the effect depends on the OSM used. This is why the normalizer was problematic as loading a smaller or larger OSM can result in the same area behave differently. Meanwhile I am mentioning #6767 here as it is the PR which changes the hardcoded values. |
|
I will need a migration guide, also it is important that this PR is not blocking us from using dev-2.x for a long time - a few days or a week is ok. But, for us it is important that when this is merged any follow up PRs witch affect the configuratio is also merged relativly soon after. The migration guide must be available before any of these PRs are merged. |
I have added documentation of how the safety values actually work in practice, which now become clear when the normalization is removed (they are applied as a multiplier on top of the reluctance). My aim is to change the values of the tag mappers in #6767 so that the configurations won't need to be changed for similar effect. In this case, it will create an impression that cycling on most urban roads (e.g. secondary or tertiary) is actually undesirable, and I believe this is what the community wants. |
7b5cf8b to
ad3842b
Compare
ad3842b to
9b738fd
Compare
Summary
This removes the safety normalizer. The normalizer class still exists but it now just collects the minimum safety values instead of changing the edge.
The heuristic function is also updated to take the reluctance and minimum safety into account, so it will still be admissible even with a low reluctance (e.g. if the user sets walk reluctance to 0.6).
As mentioned in #4442,
Our client app has the ability to submit custom reluctance values, and explained to the users what it is supposed to do. For example, our app has the following explanation:
The safety normalizer has resulted in cyclists complaining because it shows the cycling journeys much more stressful then they thought, even if it is on a safe route.
Issue
Fixes #6775
Unit tests
Added extensively
Documentation
Changelog
Bumping the serialization version id
Needed. The minimum safety values for walk and cycle are now stored in the graph.