4

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.

Freek
  • 1,097
  • 2
  • 12
  • 30

3 Answers3

2

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.

scai
  • 20,297
  • 4
  • 56
  • 72
  • The ways are sorted "almost" correctly, that is, when I just take the ways and use them as they are ordered in the element, (and disregard the order the relation presents them in), it also goes wrong but to a much higher degree. So I was under the impression that I was looking in the right direction at least... But I'll dig more into the proper way to sort ways. Of course I can do some estimating (i.e. manually find the nearest starting node in a way for each end node in a way), but I bet there is a better... way. – Freek Jul 14 '21 at 12:10
  • Hmm, perhaps I should approach this problem by plotting all ways separately and not even try to connect the lines... that would make this problem much easier indeed... Perhaps that would give problems when I want to offer the trail as a GPX file though. – Freek Jul 14 '21 at 12:17
  • Drawing ways separately is also a nice idea! – scai Jul 14 '21 at 12:54
  • I'll go for that for now. But I fear that a time will come when I want to offer the whole relation as a GPX file and we are back at this problem. But maybe I'll solve it before that and then I will report back here. – Freek Jul 14 '21 at 13:00
2

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.

Freek
  • 1,097
  • 2
  • 12
  • 30
1

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.

mcsoini
  • 6,280
  • 2
  • 15
  • 38
  • Is this "smartness" guaranteed? I tried with three parts of line, it worked but as guarantee, is there a source that is implemented? – Khaled Feb 18 '23 at 23:32
  • @Khaled Your question is unclear, please rephrase. – mcsoini Feb 19 '23 at 07:49
  • Say I pass the list of lines as follow 'multi_line = geometry.MultiLineString([line_c, line_b, line_a])' or another permutation, is the result always guaranteed the same due of smartness? in your example it woked as expected but what if list of lines is of 100 items or more, this smartness still works? – Khaled Feb 19 '23 at 10:27
  • @Khaled Please note that I'm not a shapely author. I would not expect the order of the individual linestrings to mattern, nor their number. It would be interesting to see how it behaves for more complex shapes, e.g. loops. To figure out whether this is a suitable solution for your use case, please give it a try using your own data, consult the [docs](https://shapely.readthedocs.io/en/latest/reference/shapely.MultiLineString.html), as well as the [sourcecode](https://github.com/shapely/shapely/blob/main/shapely/ops.py) – mcsoini Feb 19 '23 at 11:22