fffiloni commited on
Commit
82103f2
·
verified ·
1 Parent(s): 123b843

Migrated from GitHub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,20 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ fonts/Roboto-Bold.ttf filter=lfs diff=lfs merge=lfs -text
37
+ fonts/Roboto-Light.ttf filter=lfs diff=lfs merge=lfs -text
38
+ fonts/Roboto-Regular.ttf filter=lfs diff=lfs merge=lfs -text
39
+ posters/barcelona_warm_beige_20260108_172924.png filter=lfs diff=lfs merge=lfs -text
40
+ posters/chicago_noir_20260108_173313.png filter=lfs diff=lfs merge=lfs -text
41
+ posters/dubai_midnight_blue_20260108_174920.png filter=lfs diff=lfs merge=lfs -text
42
+ posters/marrakech_terracotta_20260108_180821.png filter=lfs diff=lfs merge=lfs -text
43
+ posters/melbourne_forest_20260108_181459.png filter=lfs diff=lfs merge=lfs -text
44
+ posters/mumbai_contrast_zones_20260108_170325.png filter=lfs diff=lfs merge=lfs -text
45
+ posters/mumbai_contrast_zones_20260108_172010.png filter=lfs diff=lfs merge=lfs -text
46
+ posters/new_york_noir_20260108_164217.png filter=lfs diff=lfs merge=lfs -text
47
+ posters/new_york_noir_20260108_172453.png filter=lfs diff=lfs merge=lfs -text
48
+ posters/san_francisco_sunset_20260108_184122.png filter=lfs diff=lfs merge=lfs -text
49
+ posters/singapore_neon_cyberpunk_20260108_184503.png filter=lfs diff=lfs merge=lfs -text
50
+ posters/tokyo_japanese_ink_20260108_165830.png filter=lfs diff=lfs merge=lfs -text
51
+ posters/venice_blueprint_20260108_165527.png filter=lfs diff=lfs merge=lfs -text
52
+ posters/washington_blueprint_20260108_184314.png filter=lfs diff=lfs merge=lfs -text
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ankur Gupta
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
ORIGINAL_README.md ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # City Map Poster Generator
2
+
3
+ Generate beautiful, minimalist map posters for any city in the world.
4
+
5
+ <img src="posters/singapore_neon_cyberpunk_20260108_184503.png" width="250">
6
+ <img src="posters/dubai_midnight_blue_20260108_174920.png" width="250">
7
+
8
+ ## Examples
9
+
10
+
11
+ | Country | City | Theme | Poster |
12
+ |:------------:|:--------------:|:---------------:|:------:|
13
+ | USA | San Francisco | sunset | <img src="posters/san_francisco_sunset_20260108_184122.png" width="250"> |
14
+ | Spain | Barcelona | warm_beige | <img src="posters/barcelona_warm_beige_20260108_172924.png" width="250"> |
15
+ | Italy | Venice | blueprint | <img src="posters/venice_blueprint_20260108_165527.png" width="250"> |
16
+ | Japan | Tokyo | japanese_ink | <img src="posters/tokyo_japanese_ink_20260108_165830.png" width="250"> |
17
+ | India | Mumbai | contrast_zones | <img src="posters/mumbai_contrast_zones_20260108_170325.png" width="250"> |
18
+ | Morocco | Marrakech | terracotta | <img src="posters/marrakech_terracotta_20260108_180821.png" width="250"> |
19
+ | Singapore | Singapore | neon_cyberpunk | <img src="posters/singapore_neon_cyberpunk_20260108_184503.png" width="250"> |
20
+ | Australia | Melbourne | forest | <img src="posters/melbourne_forest_20260108_181459.png" width="250"> |
21
+ | UAE | Dubai | midnight_blue | <img src="posters/dubai_midnight_blue_20260108_174920.png" width="250"> |
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pip install -r requirements.txt
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ```bash
32
+ python create_map_poster.py --city <city> --country <country> [options]
33
+ ```
34
+
35
+ ### Options
36
+
37
+ | Option | Short | Description | Default |
38
+ |--------|-------|-------------|---------|
39
+ | `--city` | `-c` | City name | required |
40
+ | `--country` | `-C` | Country name | required |
41
+ | `--theme` | `-t` | Theme name | feature_based |
42
+ | `--distance` | `-d` | Map radius in meters | 29000 |
43
+ | `--list-themes` | | List all available themes | |
44
+
45
+ ### Examples
46
+
47
+ ```bash
48
+ # Iconic grid patterns
49
+ python create_map_poster.py -c "New York" -C "USA" -t noir -d 12000 # Manhattan grid
50
+ python create_map_poster.py -c "Barcelona" -C "Spain" -t warm_beige -d 8000 # Eixample district
51
+
52
+ # Waterfront & canals
53
+ python create_map_poster.py -c "Venice" -C "Italy" -t blueprint -d 4000 # Canal network
54
+ python create_map_poster.py -c "Amsterdam" -C "Netherlands" -t ocean -d 6000 # Concentric canals
55
+ python create_map_poster.py -c "Dubai" -C "UAE" -t midnight_blue -d 15000 # Palm & coastline
56
+
57
+ # Radial patterns
58
+ python create_map_poster.py -c "Paris" -C "France" -t pastel_dream -d 10000 # Haussmann boulevards
59
+ python create_map_poster.py -c "Moscow" -C "Russia" -t noir -d 12000 # Ring roads
60
+
61
+ # Organic old cities
62
+ python create_map_poster.py -c "Tokyo" -C "Japan" -t japanese_ink -d 15000 # Dense organic streets
63
+ python create_map_poster.py -c "Marrakech" -C "Morocco" -t terracotta -d 5000 # Medina maze
64
+ python create_map_poster.py -c "Rome" -C "Italy" -t warm_beige -d 8000 # Ancient layout
65
+
66
+ # Coastal cities
67
+ python create_map_poster.py -c "San Francisco" -C "USA" -t sunset -d 10000 # Peninsula grid
68
+ python create_map_poster.py -c "Sydney" -C "Australia" -t ocean -d 12000 # Harbor city
69
+ python create_map_poster.py -c "Mumbai" -C "India" -t contrast_zones -d 18000 # Coastal peninsula
70
+
71
+ # River cities
72
+ python create_map_poster.py -c "London" -C "UK" -t noir -d 15000 # Thames curves
73
+ python create_map_poster.py -c "Budapest" -C "Hungary" -t copper_patina -d 8000 # Danube split
74
+
75
+ # List available themes
76
+ python create_map_poster.py --list-themes
77
+ ```
78
+
79
+ ### Distance Guide
80
+
81
+ | Distance | Best for |
82
+ |----------|----------|
83
+ | 4000-6000m | Small/dense cities (Venice, Amsterdam center) |
84
+ | 8000-12000m | Medium cities, focused downtown (Paris, Barcelona) |
85
+ | 15000-20000m | Large metros, full city view (Tokyo, Mumbai) |
86
+
87
+ ## Themes
88
+
89
+ 17 themes available in `themes/` directory:
90
+
91
+ | Theme | Style |
92
+ |-------|-------|
93
+ | `feature_based` | Classic black & white with road hierarchy |
94
+ | `gradient_roads` | Smooth gradient shading |
95
+ | `contrast_zones` | High contrast urban density |
96
+ | `noir` | Pure black background, white roads |
97
+ | `midnight_blue` | Navy background with gold roads |
98
+ | `blueprint` | Architectural blueprint aesthetic |
99
+ | `neon_cyberpunk` | Dark with electric pink/cyan |
100
+ | `warm_beige` | Vintage sepia tones |
101
+ | `pastel_dream` | Soft muted pastels |
102
+ | `japanese_ink` | Minimalist ink wash style |
103
+ | `forest` | Deep greens and sage |
104
+ | `ocean` | Blues and teals for coastal cities |
105
+ | `terracotta` | Mediterranean warmth |
106
+ | `sunset` | Warm oranges and pinks |
107
+ | `autumn` | Seasonal burnt oranges and reds |
108
+ | `copper_patina` | Oxidized copper aesthetic |
109
+ | `monochrome_blue` | Single blue color family |
110
+
111
+ ## Output
112
+
113
+ Posters are saved to `posters/` directory with format:
114
+ ```
115
+ {city}_{theme}_{YYYYMMDD_HHMMSS}.png
116
+ ```
117
+
118
+ ## Adding Custom Themes
119
+
120
+ Create a JSON file in `themes/` directory:
121
+
122
+ ```json
123
+ {
124
+ "name": "My Theme",
125
+ "description": "Description of the theme",
126
+ "bg": "#FFFFFF",
127
+ "text": "#000000",
128
+ "gradient_color": "#FFFFFF",
129
+ "water": "#C0C0C0",
130
+ "parks": "#F0F0F0",
131
+ "road_motorway": "#0A0A0A",
132
+ "road_primary": "#1A1A1A",
133
+ "road_secondary": "#2A2A2A",
134
+ "road_tertiary": "#3A3A3A",
135
+ "road_residential": "#4A4A4A",
136
+ "road_default": "#3A3A3A"
137
+ }
138
+ ```
139
+
140
+ ## Project Structure
141
+
142
+ ```
143
+ map_poster/
144
+ ├── create_map_poster.py # Main script
145
+ ├── themes/ # Theme JSON files
146
+ ├── fonts/ # Roboto font files
147
+ ├── posters/ # Generated posters
148
+ └── README.md
149
+ ```
150
+
151
+ ## Hacker's Guide
152
+
153
+ Quick reference for contributors who want to extend or modify the script.
154
+
155
+ ### Architecture Overview
156
+
157
+ ```
158
+ ┌─────────────────┐ ┌──────────────┐ ┌─────────────────┐
159
+ │ CLI Parser │────▶│ Geocoding │────▶│ Data Fetching │
160
+ │ (argparse) │ │ (Nominatim) │ │ (OSMnx) │
161
+ └─────────────────┘ └──────────────┘ └─────────────────┘
162
+
163
+ ┌──────────────┐ ▼
164
+ │ Output │◀────┌─────────────────┐
165
+ │ (matplotlib)│ │ Rendering │
166
+ └──────────────┘ │ (matplotlib) │
167
+ └─────────────────┘
168
+ ```
169
+
170
+ ### Key Functions
171
+
172
+ | Function | Purpose | Modify when... |
173
+ |----------|---------|----------------|
174
+ | `get_coordinates()` | City → lat/lon via Nominatim | Switching geocoding provider |
175
+ | `create_poster()` | Main rendering pipeline | Adding new map layers |
176
+ | `get_edge_colors_by_type()` | Road color by OSM highway tag | Changing road styling |
177
+ | `get_edge_widths_by_type()` | Road width by importance | Adjusting line weights |
178
+ | `create_gradient_fade()` | Top/bottom fade effect | Modifying gradient overlay |
179
+ | `load_theme()` | JSON theme → dict | Adding new theme properties |
180
+
181
+ ### Rendering Layers (z-order)
182
+
183
+ ```
184
+ z=11 Text labels (city, country, coords)
185
+ z=10 Gradient fades (top & bottom)
186
+ z=3 Roads (via ox.plot_graph)
187
+ z=2 Parks (green polygons)
188
+ z=1 Water (blue polygons)
189
+ z=0 Background color
190
+ ```
191
+
192
+ ### OSM Highway Types → Road Hierarchy
193
+
194
+ ```python
195
+ # In get_edge_colors_by_type() and get_edge_widths_by_type()
196
+ motorway, motorway_link → Thickest (1.2), darkest
197
+ trunk, primary → Thick (1.0)
198
+ secondary → Medium (0.8)
199
+ tertiary → Thin (0.6)
200
+ residential, living_street → Thinnest (0.4), lightest
201
+ ```
202
+
203
+ ### Adding New Features
204
+
205
+ **New map layer (e.g., railways):**
206
+ ```python
207
+ # In create_poster(), after parks fetch:
208
+ try:
209
+ railways = ox.features_from_point(point, tags={'railway': 'rail'}, dist=dist)
210
+ except:
211
+ railways = None
212
+
213
+ # Then plot before roads:
214
+ if railways is not None and not railways.empty:
215
+ railways.plot(ax=ax, color=THEME['railway'], linewidth=0.5, zorder=2.5)
216
+ ```
217
+
218
+ **New theme property:**
219
+ 1. Add to theme JSON: `"railway": "#FF0000"`
220
+ 2. Use in code: `THEME['railway']`
221
+ 3. Add fallback in `load_theme()` default dict
222
+
223
+ ### Typography Positioning
224
+
225
+ All text uses `transform=ax.transAxes` (0-1 normalized coordinates):
226
+ ```
227
+ y=0.14 City name (spaced letters)
228
+ y=0.125 Decorative line
229
+ y=0.10 Country name
230
+ y=0.07 Coordinates
231
+ y=0.02 Attribution (bottom-right)
232
+ ```
233
+
234
+ ### Useful OSMnx Patterns
235
+
236
+ ```python
237
+ # Get all buildings
238
+ buildings = ox.features_from_point(point, tags={'building': True}, dist=dist)
239
+
240
+ # Get specific amenities
241
+ cafes = ox.features_from_point(point, tags={'amenity': 'cafe'}, dist=dist)
242
+
243
+ # Different network types
244
+ G = ox.graph_from_point(point, dist=dist, network_type='drive') # roads only
245
+ G = ox.graph_from_point(point, dist=dist, network_type='bike') # bike paths
246
+ G = ox.graph_from_point(point, dist=dist, network_type='walk') # pedestrian
247
+ ```
248
+
249
+ ### Performance Tips
250
+
251
+ - Large `dist` values (>20km) = slow downloads + memory heavy
252
+ - Cache coordinates locally to avoid Nominatim rate limits
253
+ - Use `network_type='drive'` instead of `'all'` for faster renders
254
+ - Reduce `dpi` from 300 to 150 for quick previews
create_map_poster.py ADDED
@@ -0,0 +1,471 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import osmnx as ox
2
+ import matplotlib.pyplot as plt
3
+ from matplotlib.font_manager import FontProperties
4
+ import matplotlib.colors as mcolors
5
+ import numpy as np
6
+ from geopy.geocoders import Nominatim
7
+ from tqdm import tqdm
8
+ import time
9
+ import json
10
+ import os
11
+ from datetime import datetime
12
+ import argparse
13
+
14
+ THEMES_DIR = "themes"
15
+ FONTS_DIR = "fonts"
16
+ POSTERS_DIR = "posters"
17
+
18
+ def load_fonts():
19
+ """
20
+ Load Roboto fonts from the fonts directory.
21
+ Returns dict with font paths for different weights.
22
+ """
23
+ fonts = {
24
+ 'bold': os.path.join(FONTS_DIR, 'Roboto-Bold.ttf'),
25
+ 'regular': os.path.join(FONTS_DIR, 'Roboto-Regular.ttf'),
26
+ 'light': os.path.join(FONTS_DIR, 'Roboto-Light.ttf')
27
+ }
28
+
29
+ # Verify fonts exist
30
+ for weight, path in fonts.items():
31
+ if not os.path.exists(path):
32
+ print(f"⚠ Font not found: {path}")
33
+ return None
34
+
35
+ return fonts
36
+
37
+ FONTS = load_fonts()
38
+
39
+ def generate_output_filename(city, theme_name):
40
+ """
41
+ Generate unique output filename with city, theme, and datetime.
42
+ """
43
+ if not os.path.exists(POSTERS_DIR):
44
+ os.makedirs(POSTERS_DIR)
45
+
46
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
47
+ city_slug = city.lower().replace(' ', '_')
48
+ filename = f"{city_slug}_{theme_name}_{timestamp}.png"
49
+ return os.path.join(POSTERS_DIR, filename)
50
+
51
+ def get_available_themes():
52
+ """
53
+ Scans the themes directory and returns a list of available theme names.
54
+ """
55
+ if not os.path.exists(THEMES_DIR):
56
+ os.makedirs(THEMES_DIR)
57
+ return []
58
+
59
+ themes = []
60
+ for file in sorted(os.listdir(THEMES_DIR)):
61
+ if file.endswith('.json'):
62
+ theme_name = file[:-5] # Remove .json extension
63
+ themes.append(theme_name)
64
+ return themes
65
+
66
+ def load_theme(theme_name="feature_based"):
67
+ """
68
+ Load theme from JSON file in themes directory.
69
+ """
70
+ theme_file = os.path.join(THEMES_DIR, f"{theme_name}.json")
71
+
72
+ if not os.path.exists(theme_file):
73
+ print(f"⚠ Theme file '{theme_file}' not found. Using default feature_based theme.")
74
+ # Fallback to embedded default theme
75
+ return {
76
+ "name": "Feature-Based Shading",
77
+ "bg": "#FFFFFF",
78
+ "text": "#000000",
79
+ "gradient_color": "#FFFFFF",
80
+ "water": "#C0C0C0",
81
+ "parks": "#F0F0F0",
82
+ "road_motorway": "#0A0A0A",
83
+ "road_primary": "#1A1A1A",
84
+ "road_secondary": "#2A2A2A",
85
+ "road_tertiary": "#3A3A3A",
86
+ "road_residential": "#4A4A4A",
87
+ "road_default": "#3A3A3A"
88
+ }
89
+
90
+ with open(theme_file, 'r') as f:
91
+ theme = json.load(f)
92
+ print(f"✓ Loaded theme: {theme.get('name', theme_name)}")
93
+ if 'description' in theme:
94
+ print(f" {theme['description']}")
95
+ return theme
96
+
97
+ # Load theme (can be changed via command line or input)
98
+ THEME = None # Will be loaded later
99
+
100
+ def create_gradient_fade(ax, color, location='bottom', zorder=10):
101
+ """
102
+ Creates a fade effect at the top or bottom of the map.
103
+ """
104
+ vals = np.linspace(0, 1, 256).reshape(-1, 1)
105
+ gradient = np.hstack((vals, vals))
106
+
107
+ rgb = mcolors.to_rgb(color)
108
+ my_colors = np.zeros((256, 4))
109
+ my_colors[:, 0] = rgb[0]
110
+ my_colors[:, 1] = rgb[1]
111
+ my_colors[:, 2] = rgb[2]
112
+
113
+ if location == 'bottom':
114
+ my_colors[:, 3] = np.linspace(1, 0, 256)
115
+ extent_y_start = 0
116
+ extent_y_end = 0.25
117
+ else:
118
+ my_colors[:, 3] = np.linspace(0, 1, 256)
119
+ extent_y_start = 0.75
120
+ extent_y_end = 1.0
121
+
122
+ custom_cmap = mcolors.ListedColormap(my_colors)
123
+
124
+ xlim = ax.get_xlim()
125
+ ylim = ax.get_ylim()
126
+ y_range = ylim[1] - ylim[0]
127
+
128
+ y_bottom = ylim[0] + y_range * extent_y_start
129
+ y_top = ylim[0] + y_range * extent_y_end
130
+
131
+ ax.imshow(gradient, extent=[xlim[0], xlim[1], y_bottom, y_top],
132
+ aspect='auto', cmap=custom_cmap, zorder=zorder, origin='lower')
133
+
134
+ def get_edge_colors_by_type(G):
135
+ """
136
+ Assigns colors to edges based on road type hierarchy.
137
+ Returns a list of colors corresponding to each edge in the graph.
138
+ """
139
+ edge_colors = []
140
+
141
+ for u, v, data in G.edges(data=True):
142
+ # Get the highway type (can be a list or string)
143
+ highway = data.get('highway', 'unclassified')
144
+
145
+ # Handle list of highway types (take the first one)
146
+ if isinstance(highway, list):
147
+ highway = highway[0] if highway else 'unclassified'
148
+
149
+ # Assign color based on road type
150
+ if highway in ['motorway', 'motorway_link']:
151
+ color = THEME['road_motorway']
152
+ elif highway in ['trunk', 'trunk_link', 'primary', 'primary_link']:
153
+ color = THEME['road_primary']
154
+ elif highway in ['secondary', 'secondary_link']:
155
+ color = THEME['road_secondary']
156
+ elif highway in ['tertiary', 'tertiary_link']:
157
+ color = THEME['road_tertiary']
158
+ elif highway in ['residential', 'living_street', 'unclassified']:
159
+ color = THEME['road_residential']
160
+ else:
161
+ color = THEME['road_default']
162
+
163
+ edge_colors.append(color)
164
+
165
+ return edge_colors
166
+
167
+ def get_edge_widths_by_type(G):
168
+ """
169
+ Assigns line widths to edges based on road type.
170
+ Major roads get thicker lines.
171
+ """
172
+ edge_widths = []
173
+
174
+ for u, v, data in G.edges(data=True):
175
+ highway = data.get('highway', 'unclassified')
176
+
177
+ if isinstance(highway, list):
178
+ highway = highway[0] if highway else 'unclassified'
179
+
180
+ # Assign width based on road importance
181
+ if highway in ['motorway', 'motorway_link']:
182
+ width = 1.2
183
+ elif highway in ['trunk', 'trunk_link', 'primary', 'primary_link']:
184
+ width = 1.0
185
+ elif highway in ['secondary', 'secondary_link']:
186
+ width = 0.8
187
+ elif highway in ['tertiary', 'tertiary_link']:
188
+ width = 0.6
189
+ else:
190
+ width = 0.4
191
+
192
+ edge_widths.append(width)
193
+
194
+ return edge_widths
195
+
196
+ def get_coordinates(city, country):
197
+ """
198
+ Fetches coordinates for a given city and country using geopy.
199
+ Includes rate limiting to be respectful to the geocoding service.
200
+ """
201
+ print("Looking up coordinates...")
202
+ geolocator = Nominatim(user_agent="city_map_poster")
203
+
204
+ # Add a small delay to respect Nominatim's usage policy
205
+ time.sleep(1)
206
+
207
+ location = geolocator.geocode(f"{city}, {country}")
208
+
209
+ if location:
210
+ print(f"✓ Found: {location.address}")
211
+ print(f"✓ Coordinates: {location.latitude}, {location.longitude}")
212
+ return (location.latitude, location.longitude)
213
+ else:
214
+ raise ValueError(f"Could not find coordinates for {city}, {country}")
215
+
216
+ def create_poster(city, country, point, dist, output_file):
217
+ print(f"\nGenerating map for {city}, {country}...")
218
+
219
+ # Progress bar for data fetching
220
+ with tqdm(total=3, desc="Fetching map data", unit="step", bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt}') as pbar:
221
+ # 1. Fetch Street Network
222
+ pbar.set_description("Downloading street network")
223
+ G = ox.graph_from_point(point, dist=dist, dist_type='bbox', network_type='all')
224
+ pbar.update(1)
225
+ time.sleep(0.5) # Rate limit between requests
226
+
227
+ # 2. Fetch Water Features
228
+ pbar.set_description("Downloading water features")
229
+ try:
230
+ water = ox.features_from_point(point, tags={'natural': 'water', 'waterway': 'riverbank'}, dist=dist)
231
+ except:
232
+ water = None
233
+ pbar.update(1)
234
+ time.sleep(0.3)
235
+
236
+ # 3. Fetch Parks
237
+ pbar.set_description("Downloading parks/green spaces")
238
+ try:
239
+ parks = ox.features_from_point(point, tags={'leisure': 'park', 'landuse': 'grass'}, dist=dist)
240
+ except:
241
+ parks = None
242
+ pbar.update(1)
243
+
244
+ print("✓ All data downloaded successfully!")
245
+
246
+ # 2. Setup Plot
247
+ print("Rendering map...")
248
+ fig, ax = plt.subplots(figsize=(12, 16), facecolor=THEME['bg'])
249
+ ax.set_facecolor(THEME['bg'])
250
+ ax.set_position([0, 0, 1, 1])
251
+
252
+ # 3. Plot Layers
253
+ # Layer 1: Polygons
254
+ if water is not None and not water.empty:
255
+ water.plot(ax=ax, facecolor=THEME['water'], edgecolor='none', zorder=1)
256
+ if parks is not None and not parks.empty:
257
+ parks.plot(ax=ax, facecolor=THEME['parks'], edgecolor='none', zorder=2)
258
+
259
+ # Layer 2: Roads with hierarchy coloring
260
+ print("Applying road hierarchy colors...")
261
+ edge_colors = get_edge_colors_by_type(G)
262
+ edge_widths = get_edge_widths_by_type(G)
263
+
264
+ ox.plot_graph(
265
+ G, ax=ax, bgcolor=THEME['bg'],
266
+ node_size=0,
267
+ edge_color=edge_colors,
268
+ edge_linewidth=edge_widths,
269
+ show=False, close=False
270
+ )
271
+
272
+ # Layer 3: Gradients (Top and Bottom)
273
+ create_gradient_fade(ax, THEME['gradient_color'], location='bottom', zorder=10)
274
+ create_gradient_fade(ax, THEME['gradient_color'], location='top', zorder=10)
275
+
276
+ # 4. Typography using Roboto font
277
+ if FONTS:
278
+ font_main = FontProperties(fname=FONTS['bold'], size=60)
279
+ font_top = FontProperties(fname=FONTS['bold'], size=40)
280
+ font_sub = FontProperties(fname=FONTS['light'], size=22)
281
+ font_coords = FontProperties(fname=FONTS['regular'], size=14)
282
+ else:
283
+ # Fallback to system fonts
284
+ font_main = FontProperties(family='monospace', weight='bold', size=60)
285
+ font_top = FontProperties(family='monospace', weight='bold', size=40)
286
+ font_sub = FontProperties(family='monospace', weight='normal', size=22)
287
+ font_coords = FontProperties(family='monospace', size=14)
288
+
289
+ spaced_city = " ".join(list(city.upper()))
290
+
291
+ # --- BOTTOM TEXT ---
292
+ ax.text(0.5, 0.14, spaced_city, transform=ax.transAxes,
293
+ color=THEME['text'], ha='center', fontproperties=font_main, zorder=11)
294
+
295
+ ax.text(0.5, 0.10, country.upper(), transform=ax.transAxes,
296
+ color=THEME['text'], ha='center', fontproperties=font_sub, zorder=11)
297
+
298
+ lat, lon = point
299
+ coords = f"{lat:.4f}° N / {lon:.4f}° E" if lat >= 0 else f"{abs(lat):.4f}° S / {lon:.4f}° E"
300
+ if lon < 0:
301
+ coords = coords.replace("E", "W")
302
+
303
+ ax.text(0.5, 0.07, coords, transform=ax.transAxes,
304
+ color=THEME['text'], alpha=0.7, ha='center', fontproperties=font_coords, zorder=11)
305
+
306
+ ax.plot([0.4, 0.6], [0.125, 0.125], transform=ax.transAxes,
307
+ color=THEME['text'], linewidth=1, zorder=11)
308
+
309
+ # --- ATTRIBUTION (bottom right) ---
310
+ if FONTS:
311
+ font_attr = FontProperties(fname=FONTS['light'], size=8)
312
+ else:
313
+ font_attr = FontProperties(family='monospace', size=8)
314
+
315
+ ax.text(0.98, 0.02, "© OpenStreetMap contributors", transform=ax.transAxes,
316
+ color=THEME['text'], alpha=0.5, ha='right', va='bottom',
317
+ fontproperties=font_attr, zorder=11)
318
+
319
+ # 5. Save
320
+ print(f"Saving to {output_file}...")
321
+ plt.savefig(output_file, dpi=300, facecolor=THEME['bg'])
322
+ plt.close()
323
+ print(f"✓ Done! Poster saved as {output_file}")
324
+
325
+ def print_examples():
326
+ """Print usage examples."""
327
+ print("""
328
+ City Map Poster Generator
329
+ =========================
330
+
331
+ Usage:
332
+ python create_map_poster.py --city <city> --country <country> [options]
333
+
334
+ Examples:
335
+ # Iconic grid patterns
336
+ python create_map_poster.py -c "New York" -C "USA" -t noir -d 12000 # Manhattan grid
337
+ python create_map_poster.py -c "Barcelona" -C "Spain" -t warm_beige -d 8000 # Eixample district grid
338
+
339
+ # Waterfront & canals
340
+ python create_map_poster.py -c "Venice" -C "Italy" -t blueprint -d 4000 # Canal network
341
+ python create_map_poster.py -c "Amsterdam" -C "Netherlands" -t ocean -d 6000 # Concentric canals
342
+ python create_map_poster.py -c "Dubai" -C "UAE" -t midnight_blue -d 15000 # Palm & coastline
343
+
344
+ # Radial patterns
345
+ python create_map_poster.py -c "Paris" -C "France" -t pastel_dream -d 10000 # Haussmann boulevards
346
+ python create_map_poster.py -c "Moscow" -C "Russia" -t noir -d 12000 # Ring roads
347
+
348
+ # Organic old cities
349
+ python create_map_poster.py -c "Tokyo" -C "Japan" -t japanese_ink -d 15000 # Dense organic streets
350
+ python create_map_poster.py -c "Marrakech" -C "Morocco" -t terracotta -d 5000 # Medina maze
351
+ python create_map_poster.py -c "Rome" -C "Italy" -t warm_beige -d 8000 # Ancient street layout
352
+
353
+ # Coastal cities
354
+ python create_map_poster.py -c "San Francisco" -C "USA" -t sunset -d 10000 # Peninsula grid
355
+ python create_map_poster.py -c "Sydney" -C "Australia" -t ocean -d 12000 # Harbor city
356
+ python create_map_poster.py -c "Mumbai" -C "India" -t contrast_zones -d 18000 # Coastal peninsula
357
+
358
+ # River cities
359
+ python create_map_poster.py -c "London" -C "UK" -t noir -d 15000 # Thames curves
360
+ python create_map_poster.py -c "Budapest" -C "Hungary" -t copper_patina -d 8000 # Danube split
361
+
362
+ # List themes
363
+ python create_map_poster.py --list-themes
364
+
365
+ Options:
366
+ --city, -c City name (required)
367
+ --country, -C Country name (required)
368
+ --theme, -t Theme name (default: feature_based)
369
+ --distance, -d Map radius in meters (default: 29000)
370
+ --list-themes List all available themes
371
+
372
+ Distance guide:
373
+ 4000-6000m Small/dense cities (Venice, Amsterdam old center)
374
+ 8000-12000m Medium cities, focused downtown (Paris, Barcelona)
375
+ 15000-20000m Large metros, full city view (Tokyo, Mumbai)
376
+
377
+ Available themes can be found in the 'themes/' directory.
378
+ Generated posters are saved to 'posters/' directory.
379
+ """)
380
+
381
+ def list_themes():
382
+ """List all available themes with descriptions."""
383
+ available_themes = get_available_themes()
384
+ if not available_themes:
385
+ print("No themes found in 'themes/' directory.")
386
+ return
387
+
388
+ print("\nAvailable Themes:")
389
+ print("-" * 60)
390
+ for theme_name in available_themes:
391
+ theme_path = os.path.join(THEMES_DIR, f"{theme_name}.json")
392
+ try:
393
+ with open(theme_path, 'r') as f:
394
+ theme_data = json.load(f)
395
+ display_name = theme_data.get('name', theme_name)
396
+ description = theme_data.get('description', '')
397
+ except:
398
+ display_name = theme_name
399
+ description = ''
400
+ print(f" {theme_name}")
401
+ print(f" {display_name}")
402
+ if description:
403
+ print(f" {description}")
404
+ print()
405
+
406
+ if __name__ == "__main__":
407
+ parser = argparse.ArgumentParser(
408
+ description="Generate beautiful map posters for any city",
409
+ formatter_class=argparse.RawDescriptionHelpFormatter,
410
+ epilog="""
411
+ Examples:
412
+ python create_map_poster.py --city "New York" --country "USA"
413
+ python create_map_poster.py --city Tokyo --country Japan --theme midnight_blue
414
+ python create_map_poster.py --city Paris --country France --theme noir --distance 15000
415
+ python create_map_poster.py --list-themes
416
+ """
417
+ )
418
+
419
+ parser.add_argument('--city', '-c', type=str, help='City name')
420
+ parser.add_argument('--country', '-C', type=str, help='Country name')
421
+ parser.add_argument('--theme', '-t', type=str, default='feature_based', help='Theme name (default: feature_based)')
422
+ parser.add_argument('--distance', '-d', type=int, default=29000, help='Map radius in meters (default: 29000)')
423
+ parser.add_argument('--list-themes', action='store_true', help='List all available themes')
424
+
425
+ args = parser.parse_args()
426
+
427
+ # If no arguments provided, show examples
428
+ if len(os.sys.argv) == 1:
429
+ print_examples()
430
+ os.sys.exit(0)
431
+
432
+ # List themes if requested
433
+ if args.list_themes:
434
+ list_themes()
435
+ os.sys.exit(0)
436
+
437
+ # Validate required arguments
438
+ if not args.city or not args.country:
439
+ print("Error: --city and --country are required.\n")
440
+ print_examples()
441
+ os.sys.exit(1)
442
+
443
+ # Validate theme exists
444
+ available_themes = get_available_themes()
445
+ if args.theme not in available_themes:
446
+ print(f"Error: Theme '{args.theme}' not found.")
447
+ print(f"Available themes: {', '.join(available_themes)}")
448
+ os.sys.exit(1)
449
+
450
+ print("=" * 50)
451
+ print("City Map Poster Generator")
452
+ print("=" * 50)
453
+
454
+ # Load theme
455
+ THEME = load_theme(args.theme)
456
+
457
+ # Get coordinates and generate poster
458
+ try:
459
+ coords = get_coordinates(args.city, args.country)
460
+ output_file = generate_output_filename(args.city, args.theme)
461
+ create_poster(args.city, args.country, coords, args.distance, output_file)
462
+
463
+ print("\n" + "=" * 50)
464
+ print("✓ Poster generation complete!")
465
+ print("=" * 50)
466
+
467
+ except Exception as e:
468
+ print(f"\n✗ Error: {e}")
469
+ import traceback
470
+ traceback.print_exc()
471
+ os.sys.exit(1)
fonts/Roboto-Bold.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:61f89f8db49261c2f6106e8dccc35df7b2f7ed909020db40a3fc905e95f99334
3
+ size 514260
fonts/Roboto-Light.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:028f843b1990ba46e2a1c4ef1b82729c4da9a946b0d9d8dbf59e623d1095e454
3
+ size 518580
fonts/Roboto-Regular.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:56a45233d29f11b4dfb86d248e921939d115778f87325e7ae8cc108383d6664d
3
+ size 515100
posters/barcelona_warm_beige_20260108_172924.png ADDED

Git LFS Details

  • SHA256: c5f0ae43bfa98162bf6540b72da406d95dc838a1c4849c6ea50e2731bbad3eae
  • Pointer size: 132 Bytes
  • Size of remote file: 7.41 MB
posters/chicago_noir_20260108_173313.png ADDED

Git LFS Details

  • SHA256: bac37ba9b1dcb28828ca13356480c1257cf7367a1b05d24ab9763ea66f26b15d
  • Pointer size: 132 Bytes
  • Size of remote file: 6.09 MB
posters/dubai_midnight_blue_20260108_174920.png ADDED

Git LFS Details

  • SHA256: 8ce9a0659b4b4de629063baa4be85225f70aca70abc91705b6dcc3768d42481f
  • Pointer size: 132 Bytes
  • Size of remote file: 9.07 MB
posters/marrakech_terracotta_20260108_180821.png ADDED

Git LFS Details

  • SHA256: 22b7b2aa83c0d07de1b5ac87b73b5697983cb81209d0c09eb3940566c8725a60
  • Pointer size: 132 Bytes
  • Size of remote file: 5.23 MB
posters/melbourne_forest_20260108_181459.png ADDED

Git LFS Details

  • SHA256: 4b0ce5736aecf5d9dee762d9f3141001069ccb6ac0e58718b14be99b57237bcd
  • Pointer size: 133 Bytes
  • Size of remote file: 11.6 MB
posters/mumbai_contrast_zones_20260108_170325.png ADDED

Git LFS Details

  • SHA256: 5a049ed4badd9ca4ba7a225cf9da36a07907b072d8c75839a67a38f036d4fa59
  • Pointer size: 132 Bytes
  • Size of remote file: 5.49 MB
posters/mumbai_contrast_zones_20260108_172010.png ADDED

Git LFS Details

  • SHA256: c01ccd407737ae06c412e9da3c121161febead749fb387526b900496d93b9639
  • Pointer size: 132 Bytes
  • Size of remote file: 5.49 MB
posters/new_york_noir_20260108_164217.png ADDED

Git LFS Details

  • SHA256: 83e96986416448a1a072ae9fb081f60da96171eb1121ef839a2dd14ad2b188a6
  • Pointer size: 132 Bytes
  • Size of remote file: 9.22 MB
posters/new_york_noir_20260108_172453.png ADDED

Git LFS Details

  • SHA256: d28560ab4dedc4d6d4bbdd5cc46a07a48aeae1db2219d0c9cc9c2cb11e634ac6
  • Pointer size: 132 Bytes
  • Size of remote file: 9.22 MB
posters/san_francisco_sunset_20260108_184122.png ADDED

Git LFS Details

  • SHA256: 5093b7e22923f3781787712f7e3c76fc287d21ca00b0b2fa0278e69b5e7a7c46
  • Pointer size: 132 Bytes
  • Size of remote file: 4.73 MB
posters/singapore_neon_cyberpunk_20260108_184503.png ADDED

Git LFS Details

  • SHA256: 05ffe35bac43113901589fc02697e6f0a8c3931b46b5a95832952f83dc607b2c
  • Pointer size: 133 Bytes
  • Size of remote file: 11.1 MB
posters/tokyo_japanese_ink_20260108_165830.png ADDED

Git LFS Details

  • SHA256: 585ebd27173fba554e8e910e89c58a5a2f4c49b4cf1afaf0924ef8b05c78cff6
  • Pointer size: 133 Bytes
  • Size of remote file: 16.6 MB
posters/venice_blueprint_20260108_165527.png ADDED

Git LFS Details

  • SHA256: ceaa4bd6787a7f1276464b9dd9554b7063537840be999cb6fd95d75d072bde8f
  • Pointer size: 132 Bytes
  • Size of remote file: 3.24 MB
posters/washington_blueprint_20260108_184314.png ADDED

Git LFS Details

  • SHA256: c2c046b2737b961e096497a2ea984e5a210c0b209d631895251084c8b8a8b4f0
  • Pointer size: 133 Bytes
  • Size of remote file: 11.6 MB
requirements.txt ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ certifi==2026.1.4
2
+ charset-normalizer==3.4.4
3
+ contourpy==1.3.3
4
+ cycler==0.12.1
5
+ fonttools==4.61.1
6
+ geographiclib==2.1
7
+ geopandas==1.1.2
8
+ geopy==2.4.1
9
+ idna==3.11
10
+ kiwisolver==1.4.9
11
+ matplotlib==3.10.8
12
+ networkx==3.6.1
13
+ numpy==2.4.0
14
+ osmnx==2.0.7
15
+ packaging==25.0
16
+ pandas==2.3.3
17
+ pillow==12.1.0
18
+ pyogrio==0.12.1
19
+ pyparsing==3.3.1
20
+ pyproj==3.7.2
21
+ python-dateutil==2.9.0.post0
22
+ pytz==2025.2
23
+ requests==2.32.5
24
+ scipy==1.16.3
25
+ shapely==2.1.2
26
+ six==1.17.0
27
+ tqdm==4.67.1
28
+ tzdata==2025.3
29
+ urllib3==2.6.3
themes/autumn.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Autumn",
3
+ "description": "Burnt oranges, deep reds, golden yellows - seasonal warmth",
4
+ "bg": "#FBF7F0",
5
+ "text": "#8B4513",
6
+ "gradient_color": "#FBF7F0",
7
+ "water": "#D8CFC0",
8
+ "parks": "#E8E0D0",
9
+ "road_motorway": "#8B2500",
10
+ "road_primary": "#B8450A",
11
+ "road_secondary": "#CC7A30",
12
+ "road_tertiary": "#D9A050",
13
+ "road_residential": "#E8C888",
14
+ "road_default": "#CC7A30"
15
+ }
themes/blueprint.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Blueprint",
3
+ "description": "Classic architectural blueprint - technical drawing aesthetic",
4
+ "bg": "#1A3A5C",
5
+ "text": "#E8F4FF",
6
+ "gradient_color": "#1A3A5C",
7
+ "water": "#0F2840",
8
+ "parks": "#1E4570",
9
+ "road_motorway": "#E8F4FF",
10
+ "road_primary": "#C5DCF0",
11
+ "road_secondary": "#9FC5E8",
12
+ "road_tertiary": "#7BAED4",
13
+ "road_residential": "#5A96C0",
14
+ "road_default": "#7BAED4"
15
+ }
themes/contrast_zones.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Contrast Zones",
3
+ "description": "Strong contrast showing urban density - darker in center, lighter at edges",
4
+ "bg": "#FFFFFF",
5
+ "text": "#000000",
6
+ "gradient_color": "#FFFFFF",
7
+ "water": "#B0B0B0",
8
+ "parks": "#ECECEC",
9
+ "road_motorway": "#000000",
10
+ "road_primary": "#0F0F0F",
11
+ "road_secondary": "#252525",
12
+ "road_tertiary": "#404040",
13
+ "road_residential": "#5A5A5A",
14
+ "road_default": "#404040"
15
+ }
themes/copper_patina.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Copper Patina",
3
+ "description": "Oxidized copper aesthetic - teal-green patina with copper accents",
4
+ "bg": "#E8F0F0",
5
+ "text": "#2A5A5A",
6
+ "gradient_color": "#E8F0F0",
7
+ "water": "#C0D8D8",
8
+ "parks": "#D8E8E0",
9
+ "road_motorway": "#B87333",
10
+ "road_primary": "#5A8A8A",
11
+ "road_secondary": "#6B9E9E",
12
+ "road_tertiary": "#88B4B4",
13
+ "road_residential": "#A8CCCC",
14
+ "road_default": "#88B4B4"
15
+ }
themes/feature_based.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Feature-Based Shading",
3
+ "description": "Different shades for different road types and features with clear hierarchy",
4
+ "bg": "#FFFFFF",
5
+ "text": "#000000",
6
+ "gradient_color": "#FFFFFF",
7
+ "water": "#C0C0C0",
8
+ "parks": "#F0F0F0",
9
+ "road_motorway": "#0A0A0A",
10
+ "road_primary": "#1A1A1A",
11
+ "road_secondary": "#2A2A2A",
12
+ "road_tertiary": "#3A3A3A",
13
+ "road_residential": "#4A4A4A",
14
+ "road_default": "#3A3A3A"
15
+ }
themes/forest.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Forest",
3
+ "description": "Deep greens and sage tones - organic botanical aesthetic",
4
+ "bg": "#F0F4F0",
5
+ "text": "#2D4A3E",
6
+ "gradient_color": "#F0F4F0",
7
+ "water": "#B8D4D4",
8
+ "parks": "#D4E8D4",
9
+ "road_motorway": "#2D4A3E",
10
+ "road_primary": "#3D6B55",
11
+ "road_secondary": "#5A8A70",
12
+ "road_tertiary": "#7AAA90",
13
+ "road_residential": "#A0C8B0",
14
+ "road_default": "#7AAA90"
15
+ }
themes/gradient_roads.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Gradient Roads",
3
+ "description": "Smooth gradient from dark center to light edges with subtle features",
4
+ "bg": "#FFFFFF",
5
+ "text": "#000000",
6
+ "gradient_color": "#FFFFFF",
7
+ "water": "#D5D5D5",
8
+ "parks": "#EFEFEF",
9
+ "road_motorway": "#050505",
10
+ "road_primary": "#151515",
11
+ "road_secondary": "#2A2A2A",
12
+ "road_tertiary": "#404040",
13
+ "road_residential": "#555555",
14
+ "road_default": "#404040"
15
+ }
themes/japanese_ink.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Japanese Ink",
3
+ "description": "Traditional ink wash inspired - minimalist with subtle red accent",
4
+ "bg": "#FAF8F5",
5
+ "text": "#2C2C2C",
6
+ "gradient_color": "#FAF8F5",
7
+ "water": "#E8E4E0",
8
+ "parks": "#F0EDE8",
9
+ "road_motorway": "#8B2500",
10
+ "road_primary": "#4A4A4A",
11
+ "road_secondary": "#6A6A6A",
12
+ "road_tertiary": "#909090",
13
+ "road_residential": "#B8B8B8",
14
+ "road_default": "#909090"
15
+ }
themes/midnight_blue.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Midnight Blue",
3
+ "description": "Deep navy background with gold/copper roads - luxury atlas aesthetic",
4
+ "bg": "#0A1628",
5
+ "text": "#D4AF37",
6
+ "gradient_color": "#0A1628",
7
+ "water": "#061020",
8
+ "parks": "#0F2235",
9
+ "road_motorway": "#D4AF37",
10
+ "road_primary": "#C9A227",
11
+ "road_secondary": "#A8893A",
12
+ "road_tertiary": "#8B7355",
13
+ "road_residential": "#6B5B4F",
14
+ "road_default": "#8B7355"
15
+ }
themes/monochrome_blue.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Monochrome Blue",
3
+ "description": "Single blue color family with varying saturation - clean and cohesive",
4
+ "bg": "#F5F8FA",
5
+ "text": "#1A3A5C",
6
+ "gradient_color": "#F5F8FA",
7
+ "water": "#D0E0F0",
8
+ "parks": "#E0EAF2",
9
+ "road_motorway": "#1A3A5C",
10
+ "road_primary": "#2A5580",
11
+ "road_secondary": "#4A7AA8",
12
+ "road_tertiary": "#7AA0C8",
13
+ "road_residential": "#A8C4E0",
14
+ "road_default": "#4A7AA8"
15
+ }
themes/neon_cyberpunk.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Neon Cyberpunk",
3
+ "description": "Dark background with electric pink/cyan - bold night city vibes",
4
+ "bg": "#0D0D1A",
5
+ "text": "#00FFFF",
6
+ "gradient_color": "#0D0D1A",
7
+ "water": "#0A0A15",
8
+ "parks": "#151525",
9
+ "road_motorway": "#FF00FF",
10
+ "road_primary": "#00FFFF",
11
+ "road_secondary": "#00C8C8",
12
+ "road_tertiary": "#0098A0",
13
+ "road_residential": "#006870",
14
+ "road_default": "#0098A0"
15
+ }
themes/noir.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Noir",
3
+ "description": "Pure black background with white/gray roads - modern gallery aesthetic",
4
+ "bg": "#000000",
5
+ "text": "#FFFFFF",
6
+ "gradient_color": "#000000",
7
+ "water": "#0A0A0A",
8
+ "parks": "#111111",
9
+ "road_motorway": "#FFFFFF",
10
+ "road_primary": "#E0E0E0",
11
+ "road_secondary": "#B0B0B0",
12
+ "road_tertiary": "#808080",
13
+ "road_residential": "#505050",
14
+ "road_default": "#808080"
15
+ }
themes/ocean.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Ocean",
3
+ "description": "Various blues and teals - perfect for coastal cities",
4
+ "bg": "#F0F8FA",
5
+ "text": "#1A5F7A",
6
+ "gradient_color": "#F0F8FA",
7
+ "water": "#B8D8E8",
8
+ "parks": "#D8EAE8",
9
+ "road_motorway": "#1A5F7A",
10
+ "road_primary": "#2A7A9A",
11
+ "road_secondary": "#4A9AB8",
12
+ "road_tertiary": "#70B8D0",
13
+ "road_residential": "#A0D0E0",
14
+ "road_default": "#4A9AB8"
15
+ }
themes/pastel_dream.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Pastel Dream",
3
+ "description": "Soft muted pastels with dusty blues and mauves - dreamy artistic aesthetic",
4
+ "bg": "#FAF7F2",
5
+ "text": "#5D5A6D",
6
+ "gradient_color": "#FAF7F2",
7
+ "water": "#D4E4ED",
8
+ "parks": "#E8EDE4",
9
+ "road_motorway": "#7B8794",
10
+ "road_primary": "#9BA4B0",
11
+ "road_secondary": "#B5AEBB",
12
+ "road_tertiary": "#C9C0C9",
13
+ "road_residential": "#D8D2D8",
14
+ "road_default": "#C9C0C9"
15
+ }
themes/sunset.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Sunset",
3
+ "description": "Warm oranges and pinks on soft peach - dreamy golden hour aesthetic",
4
+ "bg": "#FDF5F0",
5
+ "text": "#C45C3E",
6
+ "gradient_color": "#FDF5F0",
7
+ "water": "#F0D8D0",
8
+ "parks": "#F8E8E0",
9
+ "road_motorway": "#C45C3E",
10
+ "road_primary": "#D87A5A",
11
+ "road_secondary": "#E8A088",
12
+ "road_tertiary": "#F0B8A8",
13
+ "road_residential": "#F5D0C8",
14
+ "road_default": "#E8A088"
15
+ }
themes/terracotta.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Terracotta",
3
+ "description": "Mediterranean warmth - burnt orange and clay tones on cream",
4
+ "bg": "#F5EDE4",
5
+ "text": "#8B4513",
6
+ "gradient_color": "#F5EDE4",
7
+ "water": "#A8C4C4",
8
+ "parks": "#E8E0D0",
9
+ "road_motorway": "#A0522D",
10
+ "road_primary": "#B8653A",
11
+ "road_secondary": "#C9846A",
12
+ "road_tertiary": "#D9A08A",
13
+ "road_residential": "#E5C4B0",
14
+ "road_default": "#D9A08A"
15
+ }
themes/warm_beige.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Warm Beige",
3
+ "description": "Earthy warm neutrals with sepia tones - vintage map aesthetic",
4
+ "bg": "#F5F0E8",
5
+ "text": "#6B5B4F",
6
+ "gradient_color": "#F5F0E8",
7
+ "water": "#DDD5C8",
8
+ "parks": "#E8E4D8",
9
+ "road_motorway": "#8B7355",
10
+ "road_primary": "#A08B70",
11
+ "road_secondary": "#B5A48E",
12
+ "road_tertiary": "#C9BBAA",
13
+ "road_residential": "#D9CFC2",
14
+ "road_default": "#C9BBAA"
15
+ }