maptoposter / ORIGINAL_README.md
fffiloni's picture
Migrated from GitHub
82103f2 verified
# 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