-
Notifications
You must be signed in to change notification settings - Fork 12
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
Simplify the early stages of creating a StreetNetwork
from OSM data
#127
Comments
Thanks for writing this up! I'm totally in favor.
I really like this idea! I think I've tried to get external tools to do that clipping before, but never gotten exactly the right thing working. Currently we tend to assume something like One consequence of rethinking the logic at the OSM level is treating very long roads or railways equivalently. Currently we special case railways that start and end outside the boundary but pass somewhere inside. No reason we shouldn't treat roads the same. (Old traffic sim assumptions leaking through were the original reason.)
In https://github.com/a-b-street/osm2streets/tree/osm_tags I'm getting rid of I agree this would very nicely clean up current hacks around
That's sort of the intention behind the raw map editor, but in its current form, it's overly complex because of the old way that IDs used to be managed. The refactor you describe will make building this kind of app much, much easier. |
Yeah, the Because the OSM data model allows splitting ways wherever the mapper wants to, I think any "out of bounds" data should be ignored consistently, so it would be really cool for
Yes, I'd like to see the clipping be exhaustive at the OSM level, doing proper intersection checks for every line segment, though moving the clipping logic out of
I like the idea of
Even before we get there, thinking about how |
We have some support for that today. Areas get more complex, because they're often relations with some of the members missing in the complete-ways extract.
This is the case that osm2streets/streets_reader/src/clip.rs Line 33 in 117e8fb
Hmm, maintaining a two-way mapping sounds tougher. Right now each |
I had a go at starting this idea, because our current haphazard handling of map edge geometry is complicating the geometry refactor. A few problems... Tests: We don't have an explicit boundary for any of the tests. I think we need to go back and record one, because we otherwise assume a bounding box covering everything in the OSM input. It'd be good to do this anyway, so we can make obnoxious cases like
Currently This gets a bit tricky with areas. We already have I'm leaning towards a different API... after reading a I think the complication of clipping at the document level is introducing geometry that doesn't come from OSM. |
Also has the side effect of getting rid of handling for tiny roundabouts, because it's hard to reason about...
Something started in a branch; it's much simpler. Two more questions raised:
I'll open a few PRs to start pushing this through. |
Also plumb through the boundary when actually calling osm2streets.
Also has the side effect of getting rid of handling for tiny roundabouts, because it's hard to reason about...
Also has the side effect of getting rid of handling for tiny roundabouts, because it's hard to reason about...
Also plumb through the boundary when actually calling osm2streets.
Also has the side effect of getting rid of handling for tiny roundabouts. 3 tests break, with clipped roads appearing on the wrong side of the boundary.
…f the PolyLine are in-bounds. #127
Also has the side effect of getting rid of handling for tiny roundabouts. 3 tests break, with clipped roads appearing on the wrong side of the boundary.
…f the PolyLine are in-bounds. #127
Also has the side effect of getting rid of handling for tiny roundabouts. 3 tests break, with clipped roads appearing on the wrong side of the boundary.
…f the PolyLine are in-bounds. #127
The reality is that roads can have multiple way IDs and intersections multiple node IDs. The idea of remembering an OSM segment (way, node -> node) doesn't handle all cases. Any callers trying to do this need to deal with the real complexity of multiple OSM Ids. Also get rid of merge_osm_ways, an old debugging hack for A/B Street. If we want something like this again, we can set internal_junction_road earlier. Bunch of test diffs from picking up OSM IDs now.
I had to back away a bit more from the idea of clipping an OSM document being blind to the types of things being clipped. When loop roads like https://www.openstreetmap.org/way/128464171 are split by a boundary, it was breaking previously. The previous way was also throwing out some geometry for large multipolygon relations that crossed the boundary. We can revisit generic clipping again, but we need osm2areas or something with tests first. :) A/B Street importer is my proxy for that in the meantime. |
- traffic signals should never wind up on map edges - multipolygons and OSM document-level clipping are still strange
The start of main
osm_to_street_network
function is a complicated tangle of stateful modifications of theStreetNetwork
with plenty of comments hinting at dependencies between seemingly unrelated steps. The delegation of responsibilities and relationship betweenStreetNetwork::blank
,extract_osm
,split_up_roads
andclip_map
is not clear, with mutations happening to theStreetNetwork
all over the place.osm2streets/streets_reader/src/lib.rs
Lines 102 to 115 in 3d64f99
I think we should improve the public APIs of
StreetNetwork
andOsmExtract
so that they help explain the ordering constraints and delegation of responsibilities. I'll share my ideas about that here, which requires a diversion into clipping and theStreetNetwork
to OSM mapping.Clipping the Map
Clipping is an interesting problem because the interpretation of the map features is influenced by nearby features, that's the whole reason osm2streets needs to exist. I think that
StreetNetwork
ought to treat its features fundamentally as areas, not centerpoints and centerlines. (That's why I'm advocating for geometry calculations to move up earlier in the process.) If that is the case, then precisely clipping aStreetNetwork
has to involve cuttingRoad
s andIntersection
s in half. This is not something that we should support at the representation level, because the representation still needs to use points and centerlines as reference for the distinct features (intersections and roads) that make up the areas.In fact, it is an unavoidable problem that we can't accurately represent anything that is close to the edge of the raw data we have. We should acknowledge that fact and build it into our understanding and the API. There should be a buffer border of some distance around the area that we have data for that we consider to be nonsense output. Probably about half the width of a very wide road.
When doing the OSM to Streets conversion, I think we should trim the raw OSM to the boundary polygon in
OsmExtract
, before adding it intoStreetNetwork
. The trimming operation is better defined in the domain of the OSM data itself, because the OSM data is points and lines. There should be no clipping operation done onStreetsNetwork
representation data, instead we could clip the end-result geometry precisely to the inside edge of the buffer boundary, or output that boundary and let the consumer do it.Mapping Between
StreetNetwork
and OSMA major usecase for
StreetNetwork
is empowering tools for authoring OSM data, so it will be useful to be able to get from someStreetNetwork
feature back to OSM feature that defines it. From our recent discussions, it seems like it will be useful for transformations to have access back to the defining OSM too. That way they can use whatever weird properties of the OSM data they want, without us having to represent it first class within theStreetNetwork
features.The takeaway here is that
StreetNetwork
should store a full copy of the original OSM data (at least until the user wants to discard it) and maintain a mapping. Doing so makes it easier to reason about adding in additional details at later steps, likeuse_barrier_nodes
anduse_crossing_nodes
does currently.The Refactor
Ok, enough waffling about motivations, my proposal is to use private methods and "constructor" methods to delineate the responsibilities, and use public methods to communicate what is safe to do without worrying about internal data dependencies.
OsmExtract
, with the invariant that it only stores data within the boundary. Don't be scared about discarding data just outside boundary, we already have no idea what features we can't see beyond the bounds of the raw data we do have, so lets be clear about our cut-off point.OsmExtract::new(doc: osm::Document, boundary: Option<Vec<LonLat>>) -> Self
which doesn't depend on anythingStreetNetwork
. It can clip the raw OSM features as it reads them fromdoc
.StreetNetwork::from_osm(osm: OsmExtract, config: MapConfig) -> Self
which doessplit_up_roads
internally, and whatever else is necessary to return aStreetNetwork
that is ready to have its public API called.sort_roads
andupdate_movements
private again, and work towards all transformations doing their work using the publicStreetNetwork
API, instead of direct mutation.Because
StreetNetwork::insert_road
exists the implication is that it handles some internal data dependency so the caller doesn't have to.split_up_roads
should be private toStreetNetwork
because it wants to be more efficient than usinginsert_road
and is willing to understand the data dependency in order to do it safely. Transformations should use the public API because they should be non-vital and safe. The only danger of skipping them or calling them in the wrong order should be the missed opportunity for an easy improvement.The idea is that an interactive editor could use
StreetNetwork::new(bounds: GPSBounds, config: MapConfig)
to create a blank map for the user, and useinsert_intersection
andinsert_road
to build up a map based on user input. Then transformations would be buttons available to "tidy up" the map. They can be clicked in any order as many times as the user likes.The text was updated successfully, but these errors were encountered: