Plotting OpenStreetMap relations does not generate continous lines

Viewed 259

All,

I have been working on an index of all MTB trails worldwide. I'm a Python person so for all steps involved I try to use Python modules.

I was able to grab relations from the OSM overpass API like this:

from OSMPythonTools.overpass import Overpass
overpass = Overpass()

def fetch_relation_coords(relation): 
    rel = overpass.query('rel(%s); (._;>;); out;' % relation)
    return rel

rel = fetch_relation_coords("6750628")

I'm choosing this particular relation (6750628) because it is one of several that is resulting in discontinuous (or otherwise erroneous) plots.

I process the "rel" object to get a pandas.DataFrame like this:

elements = pd.DataFrame(rel.toJSON()['elements'])

"elements" looks like this: elements dataframe

The Elements pandas.DataFrame contains rows of the types "relation" (1 in this case), several of the type "way" and many of the type "node". It was my understanding that I would use the "relation" row, "members" column to extract the order of the ways (which point to the nodes), and use that order to make a list of the latitudes and longitudes of the nodes (for later use in leaflet), in the correct order, that is, the order that leads to continuous path on a map.

However, that is not the case. For this particular relation, I end up with the following plot:

plot of latitudes and longitudes of OSM relation 6750628

If we compare that with the way the relation is displayed on openstreetmap.org itself, we see that it goes wrong (focus on the middle, eastern part of the trail). I have many examples of this happening, although there are also a lot of relations that do display correctly.

So I was wondering, what am I missing? Are there nodes with tags that need to be ignored? I already tried several things, including leaving out nodes with any tags, this does not help. Somewhere my processing is wrong but I don't understand where.

3 Answers

You need to sort the ways inside the relation yourself. Only a few relation types require sorted members, for example some route relations such as route=bus and route=tram. Others may have sorted members, such as route=hiking, route=bicycle etc., but they don't require them. Various other relations, such as boundary relations (type=boundary), usually don't have sorted members.

I'm pretty sure there are already various tools for sorting relation members, obviously this includes the openstreetmap.org website where this relation is shown correctly. Unfortunately I'm not able to point you to these tools but I guess a little bit research will reveal others.

If I opt to just plot the different way on top of each other, I indeed get a continuous plot (index contains the indexes for all nodes per way):

All ways plotted separately

In the Database I would have preferred to have the nodes sorted anyway because I could use them to make a GPX file on the fly. But I guess I did answer my own question with this approach, thank you @scai for tipping me into this direction.

You could have a look at shapely.ops.linemerge, which seems to be smart enough to chain multiple linestrings even if the directions are inconsistent. For example (adapted from here):

from shapely import geometry, ops

line_a = geometry.LineString([[0,0], [1,1]])
line_b = geometry.LineString([[1,0], [2,5], [1,1]]) # <- switch direction
line_c = geometry.LineString([[1,0], [2,0]])

multi_line = geometry.MultiLineString([line_a, line_b, line_c])

merged_line = ops.linemerge(multi_line)
print(merged_line)  
# output:
LINESTRING (0 0, 1 1, 2 5, 1 0, 2 0)

Then you just need to make sure that the endpoints match exactly.

Related