地图到海报 – 创作你最喜欢的城市艺术
Map To Poster – Create Art of your favourite city

原始链接: https://github.com/originalankur/maptoposter

## 极简地图海报生成器 摘要 该工具使用Python和OSMnx、Matplotlib等库,生成世界各地城市的精美极简地图海报。用户指定 `--city` 和 `--country`,并可使用 `--theme`(17种可选,从蓝图到日落)和 `--distance`(地图半径,单位米,针对不同城市规模优化 – 4000-20000米)等选项自定义输出。 脚本通过OSMnx获取地图数据,通过Nominatim进行地理编码,然后使用Matplotlib渲染海报,叠加道路(根据OSM高速公路类型设置样式)、公园、水域和文本标签等元素。主题在JSON文件中定义,控制背景、道路和其他特征的颜色。 海报保存为PNG图像,存储在 `posters/` 目录中。代码设计具有可扩展性,地理编码、数据获取和渲染部分清晰明确,允许贡献者添加新的地图图层、主题或样式选项。性能提示包括缓存坐标和使用合适的网络类型以加快渲染速度。

一个名为“Map To Poster” (github.com/originalankur) 的新项目允许用户生成他们最喜欢的城市艺术作品。作者使用了 matplotlib,有人建议输出 SVG 格式代替默认的 PNG 格式。作者承认了这种可能性,并指出响应式设计问题导致图片在移动设备上隐藏——可以在桌面或通过 X (以前的 Twitter) 链接查看示例。 讨论还集中在旧金山示例中缺失的区域,这些区域被确定为公共公园(Presidio 和金门国家娱乐区)。作者提供了一个生成海报的命令行示例,建议对于旧金山大小的城市,细节级别 (-d) 在 10,000-14,000 之间。该项目收到了用户的积极反馈。
相关文章

原文

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

pip install -r requirements.txt
python create_map_poster.py --city <city> --country <country> [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
# 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 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)

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

Posters are saved to posters/ directory with format:

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

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"
}
map_poster/
├── create_map_poster.py          # Main script
├── themes/               # Theme JSON files
├── fonts/                # Roboto font files
├── posters/              # Generated posters
└── README.md

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

┌─────────────────┐     ┌──────────────┐     ┌─────────────────┐
│   CLI Parser    │────▶│  Geocoding   │────▶│  Data Fetching  │
│   (argparse)    │     │  (Nominatim) │     │    (OSMnx)      │
└─────────────────┘     └──────────────┘     └─────────────────┘
                                                     │
                        ┌──────────────┐             ▼
                        │    Output    │◀────┌─────────────────┐
                        │  (matplotlib)│     │   Rendering     │
                        └──────────────┘     │  (matplotlib)   │
                                             └─────────────────┘
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_linkThickest (1.2), darkest
trunk, primaryThick (1.0)
secondaryMedium (0.8)
tertiaryThin (0.6)
residential, living_streetThinnest (0.4), lightest

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

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)
# 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
  • 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
联系我们 contact @ memedata.com