-
Notifications
You must be signed in to change notification settings - Fork 2
Fix lane count calculation - use Scheme structs to avoid re-reading/re-interpreting tags #111
Conversation
Forward and backward lanes are independently guessed to be half of the lanes, but the tags on the website assume that providing `lanes` and `lanes:backward` is enough.
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.
Looks good, thanks for the work so far.
You are working on issue #72
Looks pretty idiomatic to me, but then again I am not expert. |
This was a bug, it should be fixed now. You should be able to run all the tests with just The roundtrip works by taking the output (only the output), and checking that you can get back to the same spec by going to tags and back again (round trip). Why not tags -> spec -> tags? Documentation is very welcome! |
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.
Thanks @droogmic for reviewing, feel free to take the lead on this. I may have some time over the weekend to catch up, but other stuff first...
Co-authored-by: droogmic <droogmic@gmail.com>
Incorporate lanes:both_ways and centre_turn_lanes. Add assumption that oneway=no, lanes=1 means lanes:both_ways=1
Agreed, the content and location of the warnings appears correct.
…On Sun, 27 Mar 2022, 07:52 Ben Ritter, ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In rust/osm2lanes/src/transform/tags_to_lanes/mod.rs
<#111 (comment)>:
> + .get("lanes:bus")
+ .and_then(|num| num.parse::<usize>().ok());
+
+ if oneway.into() {
+ // TODO support oneway=-1
+ if tagged_backward.is_some() {
+ warn!("lanes:backwards tag on a oneway road")
+ }
+ if tagged_both_ways.is_some() {
+ warn!("lanes:both_ways tag on a oneway road")
+ }
+ match (tagged_lanes, tagged_forward) {
+ (Some(l), None) => (l, 0),
+ (Some(l), Some(f)) => {
+ if l != f {
+ warn!("lanes tag does not agree with lanes:forward on oneway road")
This does seem like the right place to generate most of these warnings, so
I will upgrade all of the warn!s to instead feed into the RoadWarnings
vec.
—
Reply to this email directly, view it on GitHub
<#111 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACEL2Z6UD6DUGQ2WZS7ZFEDVB7ZRTANCNFSM5RTT6CFA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Reveals a bunch of failed tests...
…sway and lanes:{bus,psv}
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.
Looking good so far, surprisingly complex :)
// Always assume no center turn lane unless tagged, so we already know: | ||
let num_both_ways = tagged_both_ways.unwrap_or(tagged_center_turn_lanes.unwrap_or(0)); |
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.
Is centre_turn_lane=no
and lanes:both_ways=1
or centre_turn_lane=yes
and lanes:both_ways=0
valid.
} | ||
(Some(l), Some(f), None) => (f, num_both_ways, l - f - num_both_ways), | ||
(Some(l), None, Some(b)) => (l - b - num_both_ways, num_both_ways, b), | ||
(Some(1), None, None) => (0, 1, 0), |
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.
1 lane becomes a centre lane? seems fishy?
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 is a bit fishy, but I think it is because describing "how many lanes in each direction does a skinny allyway have" is fishy. I might tag them "lane_markings=no width=5", but we have to represent that in schema somehow.
fn driving_lane_directions( | ||
tags: &Tags, | ||
locale: &Locale, | ||
oneway: Oneway, | ||
warnings: &mut RoadWarnings, | ||
) -> (usize, usize, usize) { |
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.
can we test this in isolation, it is quite complex now :)
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 agree! (if this function ends up keeping all of its responsibilities.)
Does that mean a set of tests in tests.yaml, or rust tests?
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.
Rust tests I'd imagine, since the input and output are quite specialized here. The unit tests can be in this file, since it's private
I am finding more and more out about The problem with locking in a final guess on the lanes distribution at the startWe cannot assume or guess missing parts of the We need a way to narrow down our guess as we go. Random thoughts:
|
I agree with everything you say, but I also don't really know the best way forward.
correct
I agree, that we should maybe try this instead. The downside is that all the other logic needs to account for this new system, so it will be a painful refactor :(. A simplified version of the logic you have now should probably still exist, to populate a top level In any event, it sounds like you understand the problem. Now you just need to decide how hard you make the solution for yourself ;) |
I added you to the project, so hopefully I dont need to approve the CI anymore |
Probably doesn't compile, but close
…tructure. The code is a mess, gesturing generally at the idea, without compiling. I feel like the whole lot should mean close to what it actually says.
@@ -1054,6 +1055,8 @@ | |||
|
|||
- way_id: 389654080 | |||
mapillary: https://www.mapillary.com/app/?pKey=331760328316020 | |||
rust: | |||
expect_warnings: true # deprecated centre_turn_lane |
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.
good idea to comment like that, need to do that myself
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 can agree to this once you look at what I have done and can justify why this is better.
For example:
How do we extend this to include https://taginfo.openstreetmap.org/keys/taxi:lanes?
it feels a lot of the lane handling is hardcoded to bus and only bus?
sidewalk: "no" | ||
shoulder: "no" | ||
lanes: "3" | ||
lanes:forward: "2" |
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.
can this not be inferred from bus:lanes:forward
what happens when this is removed?
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 could, but lanes:forward
should be tagged in OSM anyway. Inferring it is being a bit fancy and I'm not trying to solve the entirety of lane tagging in this PR.
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.
but lanes:forward should be tagged in OSM anyway
it should but it often isn't, we shouldn't depend on perfect OSM tagging.
we can leave it in to simplify the implementation for now, but please add a comment describing why this data is redundant so that we can try to remove it later?
impl ToString for DrivingSide { | ||
fn to_string(&self) -> String { | ||
match self { | ||
Self::Right => String::from("right"), |
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.
not sure if important,
but I avoided implementing this to avoid confusing right hand drive with right hand travel.
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 am confused about the difference, so maybe it is important! Is right hand drive when the driver gets in the right hand door of the car? I suppose this should be Into<TagKey>
instead of ToString
or something?
@@ -11,6 +11,8 @@ pub use key::TagKey; | |||
mod osm; | |||
pub use osm::{Highway, HighwayType, Lifecycle, HIGHWAY, LIFECYCLE}; | |||
|
|||
use crate::transform::{RoadWarnings, TagsToLanesMsg}; |
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 feels strange including these in this mod.
I would expect tag to eventually become its own crate or similar, with no dependencies on the osm2lanes transformation logic.
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.
Agreed, get_parsed
is a convenience function for parsing a bunch of tags at the same time, and should probably live in transform
somewhere. It was suggested that it be moved in here, but maybe it should be a function, not a method: let l: Option<usize> = get_parsed_tag(tags, "lanes", &warnings)
; or a method on RoadWarnings
: let l: Option<usize> = warnings.parse_tag(tags, "lanes")
?
&self, | ||
key: &K, | ||
warnings: &mut RoadWarnings, | ||
) -> Option<T> { |
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.
Result<Option<T>, String>
where the Err is the value that cannot be parsed?
or
Result<Option<T>, E>
where E comes from the underlying FromStr
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 Option<Result> makes more sense?
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 would be Result<T, E>
(which would be converted to an Option
with ok()
most of the time, because the error is already added to the warnings
).
|
||
const LANES: TagKey = TagKey::from("lanes"); | ||
#[derive(Debug)] | ||
pub struct LanesScheme { |
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 name of the mod and this struct are unclear for me
in my PR I named this Counts
, as it isn't really defining a scheme?
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 didn't know what to name the mod. The struct is named LanesScheme
because it represents the lanes:*
tags (which together make up what I was calling "scheme").
|
||
const CENTRE_TURN_LANE: TagKey = TagKey::from("centre_turn_lane"); | ||
pub struct CentreTurnLaneScheme { | ||
pub present: Infer<bool>, |
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.
what does "present" mean in this context?
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 refers to the presence of such a lane: "is a centre turn lane present on this road?".
Is something like struct CentreTurnLaneScheme(Infer<bool>)
possible? I haven't used that language feature before.
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.
yes, that would be the correct construct.
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.
pub struct BuswayScheme { | ||
pub forward_side_direction: Infer<Option<Direction>>, | ||
pub backward_side_direction: Infer<Option<Direction>>, | ||
} |
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 don't think I found a case of a Backward
in forward_side_direction
or Forward
in backward_side_direction
nor would I expect it.
what would go wrong with BuswayScheme(Infer<(bool, bool)>)
to cover the existence of a forward and/or backward lane?
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.
second: this would be nice to add this as a standalone intermediate refactor PR, to make reviewing easier.
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 don't think I found a case of a Backward in forward_side_direction or Forward in backward_side_direction
nor would I expect it.
Given this, I agree that BuswayScheme(Infer<(bool, bool)>)
is better.
My intention is to achieve a couple of things:
These ideas would be further helped by:
|
Ok, but then I think we might be trying to do too much in one PR (which is where the difficulty is coming from). Either we fix the lane counting, or we fix the scheme parsing. Doing both at the same time is blocking quick progress :) |
kotlin is probably failing in the tests.yml file, so I suggest bisecting the changes you made there until kotlin is happy? |
my bad, the button for close is very close to the one with comment 😆 |
lanes: 4 | ||
lanes:backward: 1 |
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.
these are interpreted as integers, when they should be strings, that causes the kotlin tests to fail
Thanks for splitting into smaller PRs and pushing them through. Just checking in, was closing this PR intentional, or a side effect of the commit message there? Are any of the ideas in this PR still not merged in by one of the smaller PRs? |
A side-effect. I removed a lot of stuff from this PR that were not needed to make the tests pass @BudgieInWA will probably want to go through the final status to reintroduce some of the less critical ideas he had (I removed the Infer::Guessed variant for example), preferably as a TDD when they become needed. |
This is a great outcome. Getting these ideas our into the open is probably the appropriate amount of progress for now. |
I am submitting this draft PR to get input on the new approach to
driving_lane_directions
, rust style andtests.yml
. This PR is aiming towards#69#72.Are we happy with this overall structure for
driving_lane_directions
?bf5df9d shows the concise version, and dc5de27 shows how useful tag validation warnings can be added (thinking about #101 and how an editor would like to use
tags_to_lanes
).Any feedback on my rust style?
I want to fit into this project, more than anything else, but couldn't help trying out
is_some_and
and using lots ofunwrap_or
.How do the roundtrip tests work?
I couldn't figure it out just yet. Some guidance in the docs about writing and running
tests.yml
tests would be super awesome. (I figured outcargo test --feature tests
, which I'll add to the docs if I get there first.)