Spaces:
Running
Running
| # City Map Poster Generator | |
| Generate beautiful, minimalist map posters for any city in the world. | |
| <img src="posters/singapore_neon_cyberpunk_20260108_184503.png" width="250"> | |
| <img src="posters/dubai_midnight_blue_20260108_174920.png" width="250"> | |
| ## Examples | |
| | Country | City | Theme | Poster | | |
| |:------------:|:--------------:|:---------------:|:------:| | |
| | USA | San Francisco | sunset | <img src="posters/san_francisco_sunset_20260108_184122.png" width="250"> | | |
| | Spain | Barcelona | warm_beige | <img src="posters/barcelona_warm_beige_20260108_172924.png" width="250"> | | |
| | Italy | Venice | blueprint | <img src="posters/venice_blueprint_20260108_165527.png" width="250"> | | |
| | Japan | Tokyo | japanese_ink | <img src="posters/tokyo_japanese_ink_20260108_165830.png" width="250"> | | |
| | India | Mumbai | contrast_zones | <img src="posters/mumbai_contrast_zones_20260108_170325.png" width="250"> | | |
| | Morocco | Marrakech | terracotta | <img src="posters/marrakech_terracotta_20260108_180821.png" width="250"> | | |
| | Singapore | Singapore | neon_cyberpunk | <img src="posters/singapore_neon_cyberpunk_20260108_184503.png" width="250"> | | |
| | Australia | Melbourne | forest | <img src="posters/melbourne_forest_20260108_181459.png" width="250"> | | |
| | UAE | Dubai | midnight_blue | <img src="posters/dubai_midnight_blue_20260108_174920.png" width="250"> | | |
| ## Installation | |
| ```bash | |
| pip install -r requirements.txt | |
| ``` | |
| ## Usage | |
| ```bash | |
| python create_map_poster.py --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 | |
| ```bash | |
| # Iconic grid patterns | |
| python create_map_poster.py -c "New York" -C "USA" -t noir -d 12000 # Manhattan grid | |
| python create_map_poster.py -c "Barcelona" -C "Spain" -t warm_beige -d 8000 # Eixample district | |
| # Waterfront & canals | |
| python create_map_poster.py -c "Venice" -C "Italy" -t blueprint -d 4000 # Canal network | |
| python create_map_poster.py -c "Amsterdam" -C "Netherlands" -t ocean -d 6000 # Concentric canals | |
| python create_map_poster.py -c "Dubai" -C "UAE" -t midnight_blue -d 15000 # Palm & coastline | |
| # Radial patterns | |
| python create_map_poster.py -c "Paris" -C "France" -t pastel_dream -d 10000 # Haussmann boulevards | |
| python create_map_poster.py -c "Moscow" -C "Russia" -t noir -d 12000 # Ring roads | |
| # Organic old cities | |
| python create_map_poster.py -c "Tokyo" -C "Japan" -t japanese_ink -d 15000 # Dense organic streets | |
| python create_map_poster.py -c "Marrakech" -C "Morocco" -t terracotta -d 5000 # Medina maze | |
| python create_map_poster.py -c "Rome" -C "Italy" -t warm_beige -d 8000 # Ancient layout | |
| # Coastal cities | |
| python create_map_poster.py -c "San Francisco" -C "USA" -t sunset -d 10000 # Peninsula grid | |
| python create_map_poster.py -c "Sydney" -C "Australia" -t ocean -d 12000 # Harbor city | |
| python create_map_poster.py -c "Mumbai" -C "India" -t contrast_zones -d 18000 # Coastal peninsula | |
| # River cities | |
| python create_map_poster.py -c "London" -C "UK" -t noir -d 15000 # Thames curves | |
| python create_map_poster.py -c "Budapest" -C "Hungary" -t copper_patina -d 8000 # Danube split | |
| # List available themes | |
| 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: | |
| ```json | |
| { | |
| "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 | |
| βββ 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 | |
| ```python | |
| # 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):** | |
| ```python | |
| # 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 | |
| ```python | |
| # 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 | |