marceloprates / prettymaps

A small set of Python functions to draw pretty maps from OpenStreetMap data. Based on osmnx, matplotlib and shapely libraries.
GNU Affero General Public License v3.0
11.04k stars 516 forks source link

water issues -- rivers and other features often incomplete #26

Open acegallagher opened 2 years ago

acegallagher commented 2 years ago

Hi there. Good luck with the attention, it's not always a blessing!

I've done quite a bit of poking around in the code and I'm willing to help debug the problems with filling in rivers if you could guide me a bit. Below are the maps from my hometown, one using prettymaps and the other an OSM screenshot. There are several water features that miss being infilled. Do you understand why that might be and could you point me in the right direction?

Best

kremmling

kremmling_osm

hawtkey commented 2 years ago

the Colorado river natural: water has an outline attribute but Muddy creek waterway: river does not, it's just a line feature like a road. my guess is the problem lies with including waterway with streets in fetch.py I tried adding river and stream to streets: width: in one of the examples but couldn't get the creek to draw. I also had trouble drawing railway for what I think is the same reason. Perhaps waterway and railway need separate concerns?

aireybairey commented 2 years ago

I've been doing a lot of experimenting with railways and waterways and the following works for me:

'railway': {'custom_filter': '["railway"~"rail|light_rail|subway"]', 'dilate': 2000}
'waterway': {'custom_filter': '["waterway"~"river|stream|canal"]', 'dilate': 200}

This is in addition to whatever you are already using for the river, and must use the exact names 'railway' and 'waterway' for the layer names. I've not worked out how to vary the line widths of these yet, but this is a start. This is what it looked like when I did a quick test: image Hope that helps! And if you find a way of changing the the line widths for waterways and railways please let me know.

EDIT: You can include a width value in the railway and waterway layer defintions like so:

'railway': {'custom_filter': '["railway"~"rail|light_rail|subway"]', 'dilate': 2000, 'width': 10}
'waterway': {'custom_filter': '["waterway"~"river|stream|canal"]', 'dilate': 200, 'width': 10}

The code to use the widths in dict form doesn't seem to work for these layers, so you are limited to 1 width per layer at the moment. I've not delved into fetch.py too much but I don't think it would be too difficult to either fix the width dict function or to create additional railway / waterway layers and add them to the list of layers in fetch.py that uses the get_streets function.

acegallagher commented 2 years ago

@aireybairey This is a really informative reply. Thank you for this, I really appreciate it. I have a few questions for you:

1) Would you be willing to share the color dicts you used for your map there? You have excellent tastes.

2) I'll dig into this more, but where did you find the dict of available layer names? i.e. where in the code are the layers returned by OSM turned into a dictionary? I tried to find it but got a bit lost. Maybe this weekend I'll modify the functions you're talking about and create a pull request, looks simple enough.

Thanks again for taking the time to write your reply.

aireybairey commented 2 years ago

@acegallagher Glad to be of some help.

I took a lot of inspiration for my colour scheme from a map I've got on my wall, so can't claim much credit for the art style. These are the layers and colours I've been using besides dark for roads / railroad, and light for the background:

'recreation': {'tags': {'landuse': ['grass', 'recreation_ground', 'forest'], 'leisure': True, 'natural': ['wood', 'scrub']}, 'dilate': dilate},
'industrial': {'tags': {'landuse': 'industrial'}, 'dilate': dilate},
'water': {'tags': {'natural': ['water', 'bay'], 'place': 'sea', 'waterway': 'river'}, 'dilate': dilate},
'recreation': {'fc': '#F1C419', 'ec': '#475657', 'alpha': 1, 'lw': 0, 'zorder': 2},
'industrial': {'fc': '#DF2B51', 'ec': '#475657', 'alpha': 1, 'lw': 0, 'zorder': 2},
'water': {'fc': '#08A095', 'ec': '#475657', 'alpha': 1, 'lw': 0, 'zorder': 2},

I'm not entirely sure what you are asking for point 2, I think because my own knowledge of this is still pretty flaky and I maybe didn't explain too well before. On line 127 of fetch.py you have the following code, which chooses which layers are considered as streets which is why the naming of the layers is important:

if layer in ['streets', 'railway', 'waterway']:
        return get_streets(**kwargs, layer = layer)

So if the user-defined layer has a name matching one of those 3 it goes to the get_streets function to extract geometries from OSM assuming that they are street-like items. And it otherwise goes to the get_geometries function for all other layer names.

Sorry if that doesn't answer your question, I've spent more time just messing around with this than actually understanding it!