MapToPoster / README.md
Arnab Dey
add gradio app
6577b7a

A newer version of the Gradio SDK is available: 6.17.3

Upgrade
metadata
title: MapToPoster
sdk: gradio
sdk_version: 6.3.0
python_version: '3.11'
app_file: app.py
license: mit

City Map Poster Generator

Generate beautiful, minimalist map posters for any city in the world.

Examples

Country City Theme Poster
USA San Francisco sunset
Spain Barcelona warm_beige
Italy Venice blueprint
Japan Tokyo japanese_ink
India Mumbai contrast_zones
Morocco Marrakech terracotta
Singapore Singapore neon_cyberpunk
Australia Melbourne forest
UAE Dubai midnight_blue

Installation

# Recommended: uv
# Install uv: https://docs.astral.sh/uv/
# Requires Python 3.11+
uv sync

If you prefer pip:

pip install -r requirements.txt

Hugging Face Space (Gradio)

This repo includes a Gradio Space entrypoint in app.py.

Local run:

pip install -r requirements.txt
python app.py

Notes for Spaces:

  • The UI caps radius to 2–20km and queues requests (1 at a time) to reduce load on public Nominatim/Overpass services.
  • If geocoding starts failing due to rate limits, set MAPTOP_POSTER_USER_AGENT to a unique value (ideally with a contact).
  • OSMnx caching is enabled under /tmp/osmnx_cache (override with OSMNX_CACHE_DIR).

Usage

# With uv (recommended)
uv run python create_map_poster.py --city <city> --country <country> [options]

# Or via the installed script entrypoint
uv run maptoposter --city <city> --country <country> [options]

Options

Option Short Description Default
--city -c City name required
--country -C Country name required
--theme -t Theme name feature_based
--distance -d Map radius in meters 29000
--list-themes List all available themes

Examples

# Iconic grid patterns
uv run python create_map_poster.py -c "New York" -C "USA" -t noir -d 12000           # Manhattan grid
uv run python create_map_poster.py -c "Barcelona" -C "Spain" -t warm_beige -d 8000   # Eixample district

# Waterfront & canals
uv run python create_map_poster.py -c "Venice" -C "Italy" -t blueprint -d 4000       # Canal network
uv run python create_map_poster.py -c "Amsterdam" -C "Netherlands" -t ocean -d 6000  # Concentric canals
uv run python create_map_poster.py -c "Dubai" -C "UAE" -t midnight_blue -d 15000     # Palm & coastline

# Radial patterns
uv run python create_map_poster.py -c "Paris" -C "France" -t pastel_dream -d 10000   # Haussmann boulevards
uv run python create_map_poster.py -c "Moscow" -C "Russia" -t noir -d 12000          # Ring roads

# Organic old cities
uv run python create_map_poster.py -c "Tokyo" -C "Japan" -t japanese_ink -d 15000    # Dense organic streets
uv run python create_map_poster.py -c "Marrakech" -C "Morocco" -t terracotta -d 5000 # Medina maze
uv run python create_map_poster.py -c "Rome" -C "Italy" -t warm_beige -d 8000        # Ancient layout

# Coastal cities
uv run python create_map_poster.py -c "San Francisco" -C "USA" -t sunset -d 10000    # Peninsula grid
uv run python create_map_poster.py -c "Sydney" -C "Australia" -t ocean -d 12000      # Harbor city
uv run python create_map_poster.py -c "Mumbai" -C "India" -t contrast_zones -d 18000 # Coastal peninsula

# River cities
uv run python create_map_poster.py -c "London" -C "UK" -t noir -d 15000              # Thames curves
uv run python create_map_poster.py -c "Budapest" -C "Hungary" -t copper_patina -d 8000  # Danube split

# List available themes
uv run python create_map_poster.py --list-themes

Distance Guide

Distance Best for
4000-6000m Small/dense cities (Venice, Amsterdam center)
8000-12000m Medium cities, focused downtown (Paris, Barcelona)
15000-20000m Large metros, full city view (Tokyo, Mumbai)

Themes

17 themes available in themes/ directory:

Theme Style
feature_based Classic black & white with road hierarchy
gradient_roads Smooth gradient shading
contrast_zones High contrast urban density
noir Pure black background, white roads
midnight_blue Navy background with gold roads
blueprint Architectural blueprint aesthetic
neon_cyberpunk Dark with electric pink/cyan
warm_beige Vintage sepia tones
pastel_dream Soft muted pastels
japanese_ink Minimalist ink wash style
forest Deep greens and sage
ocean Blues and teals for coastal cities
terracotta Mediterranean warmth
sunset Warm oranges and pinks
autumn Seasonal burnt oranges and reds
copper_patina Oxidized copper aesthetic
monochrome_blue Single blue color family

Output

Posters are saved to posters/ directory with format:

{city}_{theme}_{YYYYMMDD_HHMMSS}.png

Adding Custom Themes

Create a JSON file in themes/ directory:

{
  "name": "My Theme",
  "description": "Description of the theme",
  "bg": "#FFFFFF",
  "text": "#000000",
  "gradient_color": "#FFFFFF",
  "water": "#C0C0C0",
  "parks": "#F0F0F0",
  "road_motorway": "#0A0A0A",
  "road_primary": "#1A1A1A",
  "road_secondary": "#2A2A2A",
  "road_tertiary": "#3A3A3A",
  "road_residential": "#4A4A4A",
  "road_default": "#3A3A3A"
}

Project Structure

map_poster/
β”œβ”€β”€ create_map_poster.py          # Main script
β”œβ”€β”€ pyproject.toml                # Project metadata (uv)
β”œβ”€β”€ uv.lock                       # Locked dependencies (uv)
β”œβ”€β”€ themes/               # Theme JSON files
β”œβ”€β”€ fonts/                # Roboto font files
β”œβ”€β”€ posters/              # Generated posters
└── README.md

Hacker's Guide

Quick reference for contributors who want to extend or modify the script.

Architecture Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   CLI Parser    │────▢│  Geocoding   │────▢│  Data Fetching  β”‚
β”‚   (argparse)    β”‚     β”‚  (Nominatim) β”‚     β”‚    (OSMnx)      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                     β”‚
                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”             β–Ό
                        β”‚    Output    β”‚β—€β”€β”€β”€β”€β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                        β”‚  (matplotlib)β”‚     β”‚   Rendering     β”‚
                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚  (matplotlib)   β”‚
                                             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Functions

Function Purpose Modify when...
get_coordinates() City β†’ lat/lon via Nominatim Switching geocoding provider
create_poster() Main rendering pipeline Adding new map layers
get_edge_colors_by_type() Road color by OSM highway tag Changing road styling
get_edge_widths_by_type() Road width by importance Adjusting line weights
create_gradient_fade() Top/bottom fade effect Modifying gradient overlay
load_theme() JSON theme β†’ dict Adding new theme properties

Rendering Layers (z-order)

z=11  Text labels (city, country, coords)
z=10  Gradient fades (top & bottom)
z=3   Roads (via ox.plot_graph)
z=2   Parks (green polygons)
z=1   Water (blue polygons)
z=0   Background color

OSM Highway Types β†’ Road Hierarchy

# In get_edge_colors_by_type() and get_edge_widths_by_type()
motorway, motorway_link     β†’ Thickest (1.2), darkest
trunk, primary              β†’ Thick (1.0)
secondary                   β†’ Medium (0.8)
tertiary                    β†’ Thin (0.6)
residential, living_street  β†’ Thinnest (0.4), lightest

Adding New Features

New map layer (e.g., railways):

# In create_poster(), after parks fetch:
try:
    railways = ox.features_from_point(point, tags={'railway': 'rail'}, dist=dist)
except:
    railways = None

# Then plot before roads:
if railways is not None and not railways.empty:
    railways.plot(ax=ax, color=THEME['railway'], linewidth=0.5, zorder=2.5)

New theme property:

  1. Add to theme JSON: "railway": "#FF0000"
  2. Use in code: THEME['railway']
  3. Add fallback in load_theme() default dict

Typography Positioning

All text uses transform=ax.transAxes (0-1 normalized coordinates):

y=0.14  City name (spaced letters)
y=0.125 Decorative line
y=0.10  Country name
y=0.07  Coordinates
y=0.02  Attribution (bottom-right)

Useful OSMnx Patterns

# Get all buildings
buildings = ox.features_from_point(point, tags={'building': True}, dist=dist)

# Get specific amenities
cafes = ox.features_from_point(point, tags={'amenity': 'cafe'}, dist=dist)

# Different network types
G = ox.graph_from_point(point, dist=dist, network_type='drive')  # roads only
G = ox.graph_from_point(point, dist=dist, network_type='bike')   # bike paths
G = ox.graph_from_point(point, dist=dist, network_type='walk')   # pedestrian

Performance Tips

  • Large dist values (>20km) = slow downloads + memory heavy
  • Cache coordinates locally to avoid Nominatim rate limits
  • Use network_type='drive' instead of 'all' for faster renders
  • Reduce dpi from 300 to 150 for quick previews