#!/usr/bin/env python # coding: utf-8 # In[3]: # Install prettymaps using pip: get_ipython().system('pip install prettymaps') # # prettymaps # # A minimal Python library to draw customized maps from [OpenStreetMap](https://www.openstreetmap.org/#map=12/11.0733/106.3078) created using the [osmnx](https://github.com/gboeing/osmnx), [matplotlib](https://matplotlib.org/), [shapely](https://shapely.readthedocs.io/en/stable/index.html) and [vsketch](https://github.com/abey79/vsketch) packages. # # ![](https://github.com/marceloprates/prettymaps/raw/main/prints/heerhugowaard.png) # # # This work is [licensed](LICENSE) under a GNU Affero General Public License v3.0 (you can make commercial use, distribute and modify this project, but must **disclose** the source code with the license and copyright notice) # # ## Note about crediting and NFTs: # - Please keep the printed message on the figures crediting my repository and OpenStreetMap ([mandatory by their license](https://www.openstreetmap.org/copyright)). # - I am personally **against** NFTs for their [environmental impact](https://earth.org/nfts-environmental-impact/), the fact that they're a [giant money-laundering pyramid scheme](https://twitter.com/smdiehl/status/1445795667826208770) and the structural incentives they create for [theft](https://twitter.com/NFTtheft) in the open source and generative art communities. # - **I do not authorize in any way this project to be used for selling NFTs**, although I cannot legally enforce it. **Respect the creator**. # - The [AeternaCivitas](https://magiceden.io/marketplace/aeterna_civitas) and [geoartnft](https://www.geo-nft.com/) projects have used this work to sell NFTs and refused to credit it. See how they reacted after being exposed: [AeternaCivitas](etc/NFT_theft_AeternaCivitas.jpg), [geoartnft](etc/NFT_theft_geoart.jpg). # - **I have closed my other generative art projects on Github and won't be sharing new ones as open source to protect me from the NFT community**. # # Buy Me a Coffee at ko-fi.com # # ## As seen on [Hacker News](https://web.archive.org/web/20210825160918/https://news.ycombinator.com/news): # ![](https://github.com/marceloprates/prettymaps/raw/main/prints/hackernews-prettymaps.png) # # ## [prettymaps subreddit](https://www.reddit.com/r/prettymaps_/) # ## [Google Colaboratory Demo](https://colab.research.google.com/github/marceloprates/prettymaps/blob/master/notebooks/examples.ipynb) # # Installation # # OBS. I'm trying to solve a dependency issue with [vsketch](https://vsketch.readthedocs.io/en/latest/install.html) before publishing prettymaps v0.1.3, so, for now, please install directly from GitHub. # ### Install locally: # Install prettymaps with: # # ``` # pip install git+https://github.com/marceloprates/prettymaps # ``` # ### Install on Google Colaboratory: # # Install prettymaps with: # # ``` # !pip install -e "git+https://github.com/marceloprates/prettymaps#egg=prettymaps" # ``` # # Then **restart the runtime** (Runtime -> Restart Runtime) before importing prettymaps # # Tutorial # Plotting with prettymaps is very simple. Run: # ```python # prettymaps.plot(your_query) # ``` # # **your_query** can be: # 1. An address (Example: "Porto Alegre"), # 2. Latitude / Longitude coordinates (Example: (-30.0324999, -51.2303767)) # 3. A custom boundary in GeoDataFrame format # In[1]: import prettymaps plot = prettymaps.plot('Stad van de Zon, Heerhugowaard, Netherlands') # You can also choose from different "presets" (parameter combinations saved in JSON files) # # See below an example using the "minimal" preset # In[2]: plot = prettymaps.plot( 'Stad van de Zon, Heerhugowaard, Netherlands', preset = 'minimal' ) # Run # # ```python # prettymaps.presets() # ``` # # to list all available presets: # In[3]: prettymaps.presets() # To examine a specific preset, run: # In[4]: prettymaps.preset('default') # # Insted of using the default configuration you can customize several parameters. The most important are: # # - layers: A dictionary of OpenStreetMap layers to fetch. # - Keys: layer names (arbitrary) # - Values: dicts representing OpenStreetMap queries # - style: Matplotlib style parameters # - Keys: layer names (the same as before) # - Values: dicts representing Matplotlib style parameters # # ```python # plot = prettymaps.plot( # # Your query. Example: "Porto Alegre" or (-30.0324999, -51.2303767) (GPS coords) # your_query, # # Dict of OpenStreetMap Layers to plot. Example: # # {'building': {'tags': {'building': True}}, 'water': {'tags': {'natural': 'water'}}} # # Check the /presets folder for more examples # layers, # # Dict of style parameters for matplotlib. Example: # # {'building': {'palette': ['#f00','#0f0','#00f'], 'edge_color': '#333'}} # style, # # Preset to load. Options include: # # ['default', 'minimal', 'macao', 'tijuca'] # preset, # # Save current parameters to a preset file. # # Example: "my-preset" will save to "presets/my-preset.json" # save_preset, # # Whether to update loaded preset with additional provided parameters. Boolean # update_preset, # # Plot with circular boundary. Boolean # circle, # # Plot area radius. Float # radius, # # Dilate the boundary by this amount. Float # dilate # ) # ``` # # **plot** is a python dataclass containing: # # ```python # @dataclass # class Plot: # # A dictionary of GeoDataFrames (one for each plot layer) # geodataframes: Dict[str, gp.GeoDataFrame] # # A matplotlib figure # fig: matplotlib.figure.Figure # # A matplotlib axis object # ax: matplotlib.axes.Axes # ``` # # Here's an example of running prettymaps.plot() with customized parameters: # In[5]: plot = prettymaps.plot( 'Praça Ferreira do Amaral, Macau', circle = True, radius = 1100, layers = { "green": { "tags": { "landuse": "grass", "natural": ["island", "wood"], "leisure": "park" } }, "forest": { "tags": { "landuse": "forest" } }, "water": { "tags": { "natural": ["water", "bay"] } }, "parking": { "tags": { "amenity": "parking", "highway": "pedestrian", "man_made": "pier" } }, "streets": { "width": { "motorway": 5, "trunk": 5, "primary": 4.5, "secondary": 4, "tertiary": 3.5, "residential": 3, } }, "building": { "tags": {"building": True}, }, }, style = { "background": { "fc": "#F2F4CB", "ec": "#dadbc1", "hatch": "ooo...", }, "perimeter": { "fc": "#F2F4CB", "ec": "#dadbc1", "lw": 0, "hatch": "ooo...", }, "green": { "fc": "#D0F1BF", "ec": "#2F3737", "lw": 1, }, "forest": { "fc": "#64B96A", "ec": "#2F3737", "lw": 1, }, "water": { "fc": "#a1e3ff", "ec": "#2F3737", "hatch": "ooo...", "hatch_c": "#85c9e6", "lw": 1, }, "parking": { "fc": "#F2F4CB", "ec": "#2F3737", "lw": 1, }, "streets": { "fc": "#2F3737", "ec": "#475657", "alpha": 1, "lw": 0, }, "building": { "palette": [ "#FFC857", "#E9724C", "#C5283D" ], "ec": "#2F3737", "lw": 0.5, } } ) # In order to plot an entire region and not just a rectangular or circular area, set # # ```python # radius = False # ``` # In[6]: plot = prettymaps.plot( 'Bom Fim, Porto Alegre, Brasil', radius = False, ) # You can access layers's GeoDataFrames directly like this: # In[7]: # Run prettymaps in show = False mode (we're only interested in obtaining the GeoDataFrames) plot = prettymaps.plot('Centro Histórico, Porto Alegre', show = False) plot.geodataframes['building'] # Search a building by name and display it: # In[8]: plot.geodataframes['building'][ plot.geodataframes['building'].name == 'Catedral Metropolitana Nossa Senhora Mãe de Deus' ].geometry[0] # Plot mosaic of building footprints # In[9]: import numpy as np import osmnx as ox from matplotlib import pyplot as plt from matplotlib.font_manager import FontProperties # Run prettymaps in show = False mode (we're only interested in obtaining the GeoDataFrames) plot = prettymaps.plot('Porto Alegre', show = False) # Get list of buildings from plot's geodataframes dict buildings = plot.geodataframes['building'] # Project from lat / long buildings = ox.project_gdf(buildings) buildings = [b for b in buildings.geometry if b.area > 0] # Draw Matplotlib mosaic of n x n building footprints n = 6 fig,axes = plt.subplots(n,n, figsize = (7,6)) # Set background color fig.patch.set_facecolor('#5cc0eb') # Figure title fig.suptitle( 'Buildings of Porto Alegre', size = 25, color = '#fff' ) # Draw each building footprint on a separate axis for ax,building in zip(np.concatenate(axes),buildings): ax.plot(*building.exterior.xy, c = '#ffffff') ax.autoscale(); ax.axis('off'); ax.axis('equal') # Access plot.ax or plot.fig to add new elements to the matplotlib plot: # In[10]: from matplotlib.font_manager import FontProperties plot = prettymaps.plot( (41.39491,2.17557), preset = 'barcelona', ) # Change background color plot.fig.patch.set_facecolor('#F2F4CB') # Add title plot.ax.set_title( 'Barcelona', size = 50 ) plt.show() # Use **plotter** mode to export a pen plotter-compatible SVG (thanks to abey79's amazing [vsketch](https://github.com/abey79/vsketch) library) # In[11]: plot = prettymaps.plot( (41.39491,2.17557), mode = 'plotter', layers = dict(perimeter = {}), preset = 'barcelona-plotter', scale_x = .6, scale_y = -.6, ) # Some other examples # In[12]: plot = prettymaps.plot( # City name 'Barra da Tijuca', dilate = 0, figsize = (22,10), preset = 'tijuca', ) # In[13]: plot = prettymaps.plot( 'Stad van de Zon, Heerhugowaard, Netherlands', preset = 'heerhugowaard', ) # Use prettymaps.create_preset() to create a preset: # In[14]: prettymaps.create_preset( "my-preset", layers = { "building": { "tags": { "building": True, "leisure": [ "track", "pitch" ] } }, "streets": { "width": { "trunk": 6, "primary": 6, "secondary": 5, "tertiary": 4, "residential": 3.5, "pedestrian": 3, "footway": 3, "path": 3 } }, }, style = { "perimeter": { "fill": False, "lw": 0, "zorder": 0 }, "streets": { "fc": "#F1E6D0", "ec": "#2F3737", "lw": 1.5, "zorder": 3 }, "building": { "palette": [ "#fff" ], "ec": "#2F3737", "lw": 1, "zorder": 4 } } ) prettymaps.preset('my-preset') # Use prettymaps.delete_preset() to delete presets: # In[15]: # Show presets before deletion print('Before deletion:') display(prettymaps.presets()) # Delete 'my-preset' prettymaps.delete_preset('my-preset') # Show presets after deletion print('After deletion:') display(prettymaps.presets()) # Use **prettymaps.multiplot** and **prettymaps.Subplot** to draw multiple regions on the same canvas # In[16]: # Draw several regions on the same canvas prettymaps.multiplot( prettymaps.Subplot( 'Cidade Baixa, Porto Alegre', style={'building': {'palette': ['#49392C', '#E1F2FE', '#98D2EB']}} ), prettymaps.Subplot( 'Bom Fim, Porto Alegre', style={'building': {'palette': ['#BA2D0B', '#D5F2E3', '#73BA9B', '#F79D5C']}} ), prettymaps.Subplot( 'Farroupilha, Porto Alegre', style={'building': {'palette': ['#EEE4E1', '#E7D8C9', '#E6BEAE']}} ), # Load a global preset preset='cb-bf-f', # Figure size figsize=(12, 12) )