a-b-street / osm2lanes

A common library and set of test cases for transforming OSM tags to lane specifications
https://a-b-street.github.io/osm2lanes/
Apache License 2.0
34 stars 2 forks source link

Lane Width, `width`, and Locale #79

Open droogmic opened 2 years ago

droogmic commented 2 years ago

Each (non-separator) lane on an OSM way must have a width.

See: https://wiki.openstreetmap.org/wiki/Key:width#Width_of_streets

In the unlikely event that all of the relevant width:carriageway, cycleway:<side>:width, parking:lane:<side>:width, shoulder:width, lanes:width are specified, we could have a trivial implementation. This is rarely the case, so we must assume incomplete information.

Locale

Locale (the region specific encoding of highway construction conventions) will be used to infer the width of each lane. As this is constant for a given road, we will treat this and the resulting widths as constant in our requirements and algorithms.

No Width

When no width is given, the locale is used to determine typical widths of the lanes in the road. If tags for certain lanes are provided (e.g. cycleway:<side>:width), the defaults can be replaced.

Total Width: width=* or width:carriageway=*

When a total width is given, we have the strict requirement to fill that width. In the simple case, we could use the same method as above, and then scale all the lanes by the same factor to reach this requirement. However (depending on the locale) it is reasonable to imagine situations where this is insufficient. For example, bus lanes are unlikely to expand beyond a maximum width, shrinking footpaths will take precedence over shrinking travel lanes, there is a minimum size for a parallel parking bay, etc.

One proposal could be to model the following parameters per locale:

Width {
    min_width: Option<Metre>,
    default_width: Option<Metre>,
    max_width: Option<Metre>,
}

The lanes are still scaled together to meed the width, but will staurate at their minimum or maximum. In situations where the width constraints cannot be met, an error may be thrown. As more tags are provided (e.g. cycleway:<side>:width), the calculation above can be further constrained.

Config

Consumers may not want osm2lanes to guess what the width should be, so this should be a config option.

Roundtrip

When a lane width has been inferred with no guarantee of correctness, it should be excluded from the proposed/normalized/roundtrip OSM tags.

This may require an internal representation of the data:

enum DataWithConfidence {
    Direct, // Stated in a tag
    Calculated, // Other tags, when combined, can be used to calculate this
    Locale, // Not known for sure, so based on locale
    Default, // Locale has no standard, so defaults are used
    Unknown, // Unknown
}

We ignore situations where the OSM tags are contradictory.

Implementation

The current implementation does a single direct assignment of the lanes (excluding separators) and all their parameters.

Options:

  1. "Simple" Assign the default width first, and as a post processing step apply the above algorithm to mutate the widths. This has the downside that information from other tags cannot be added on the preceding step

  2. "Builder" A LaneBuilder struct is passed around instead of a Lane, upon which properties like maximum and minimum widths may be set. At the end, a LaneBuilder.lane(self) is called to return the API Lane. This would allow for more future flexibility when building lanes across multiple "passes". Downside: this will be a bit of work to rewrite the current implementation.

tordans commented 2 years ago

Thanks for this great writeup.

I talked to @supaplexosm when I saw your PR (before reading here). He had a few thoughts that I wanted to share


I wonder, what good iterations on this topic might be…

For example, it could be easiest to start with just default/fallback values (per country?) and only respect the width values where they are explicitly attached to a lane (lane in the osm2lanes sense). That would be, only looking at width:lanes, parking…width, sidewalk…width, cycleway…width (plus maybe buffer=<widthvalue> (Example) and such).

That makes the math and matrix involved a lot easier, doesn't it?

dabreegster commented 2 years ago

Awesome writeup -- I'm generally in favor of everything you wrote.

When a lane width has been inferred with no guarantee of correctness, it should be excluded from the proposed/normalized/roundtrip OSM tags.

I like this explicit representation of provenance. Just to state it, a simpler alternative: when roundtripping, never output any width tags. Ignore them for the sake of the unit test. If lanes->tags is used as part of a future OSM editor, just retain the width tags already mapped, and only consider updating other tags. But -- I'm in favor of the enum.

That makes the math and matrix involved a lot easier, doesn't it?

I agree this could be a simpler first step

tordans commented 2 years ago

About the roundtrip-section:

In https://github.com/a-b-street/osm2lanes/issues/80 > "Highway lanes", I am wondering if the "Locale" and "Default" values need to have an option to be based on other tags. In that case the value of the highway tag.

dabreegster commented 2 years ago

I am wondering if the "Locale" and "Default" values need to have an option to be based on other tags. In that case the value of the highway tag.

I think so. highway=primary width is likely different than highway=residential, given the same number of lanes and no other info.

droogmic commented 2 years ago

Good discussions so far.

He considers width on a highway unreliable. ... Width between curbs / edges of the road without sidewalks, but with parked cars when they are on street ... My take is, to maybe not consider it at all since the effort to do so is high and the result, even after this high effort, is still likely wrong.

As an MVP, we could always ignore it and return a warning. In the future though, I think there are situations where a width can give information, and is not kerb to kerb. Example: a way with bridge=yes, sidewalk=both, and width=12. I think we can assume the width is that of the bridge, and the 4 lanes (foot, travel, travel, foot) fill that width, so 2m, 4m, 4m, 2m would be a good inference.

easiest to start with just default/fallback values (per country?) and only respect the width values where they are explicitl Agreed, let's start there.

But: I do want to attempt the refactor for implementation option 2, because I expect we will want the flexibility it provides in the future.

dabreegster commented 2 years ago

https://streetwidths.its.ucla.edu/data/ Slightly off-topic for where this issue wound up, but this is a possible source of locale-specific width estimates. It's unfortunate all the data collected isn't just upstreamed in OSM.

dabreegster commented 1 year ago

Some test cases: