Skip to main content

System Architecture

MapToPoster is a Python-based command-line tool that generates minimalist map posters by fetching OpenStreetMap data and rendering it with matplotlib. The architecture follows a modular pipeline design.

High-Level Data Flow

1

CLI Argument Parsing

The entry point (create_map_poster.py:860-1051) uses argparse to collect user inputs:
  • Required: City and country names
  • Optional: Theme, distance, dimensions, coordinates override, custom fonts
  • Validation: Maximum dimensions (20 inches), theme availability checks
2

Coordinate Resolution

The get_coordinates() function (create_map_poster.py:319-370) converts city/country to lat/lon:
  • Uses Nominatim geocoding service via geopy
  • Implements rate limiting (1 second delay) to respect API policies
  • Caches results locally to avoid redundant requests
  • Falls back to manual coordinate override via --latitude/--longitude flags
3

OSM Data Fetching

Three parallel data fetch operations using OSMnx:Street Network (fetch_graph() at line 409-441):
  • Fetches all road types within specified radius
  • Uses network_type='all' to include all drivable roads
  • Applies distance compensation for viewport cropping
  • Cached via pickle for performance
Water Features (fetch_features() at line 444-479):
  • Tags: {"natural": ["water", "bay", "strait"], "waterway": "riverbank"}
  • Returns GeoDataFrame of water polygons
  • Rate-limited with 0.3s delay between requests
Parks & Green Spaces:
  • Tags: {"leisure": "park", "landuse": "grass"}
  • Returns GeoDataFrame of park polygons
4

Map Rendering

The create_poster() function (create_map_poster.py:482-772) orchestrates the rendering:
  • Creates matplotlib figure with theme background color
  • Projects all geographic data to metric CRS for accurate scaling
  • Renders layers in correct z-order
  • Applies gradient fades and typography
  • Exports to PNG (300 DPI), SVG, or PDF

Rendering Pipeline

Layer Composition

MapToPoster renders map elements in a specific z-order (bottom to top) to ensure proper visual hierarchy:
Background (create_map_poster.py:564-566):
  • Solid color fill using THEME['bg']
  • Applied to both figure and axes for seamless borders
Water Rendering (create_map_poster.py:574-582):
  • Filters to only Polygon and MultiPolygon geometries (excludes point features)
  • Projects to graph CRS for proper alignment
  • Rendered with THEME['water'] color at zorder=0.5
  • No edge color for clean appearance
Parks Rendering (create_map_poster.py:584-593):
  • Same polygon filtering as water features
  • Projected to match graph coordinate system
  • Uses THEME['parks'] color at zorder=0.8
  • Sits above water but below roads
Road Hierarchy (create_map_poster.py:595-612):
  • Roads styled by OSM highway tag using get_edge_colors_by_type() and get_edge_widths_by_type()
  • Rendered at zorder=2 (matplotlib default for ox.plot_graph)
  • Aspect ratio maintained via get_crop_limits() to prevent distortion
  • Node size set to 0 for clean minimalist aesthetic
Gradient Fades (create_gradient_fade() at line 214-252):
  • Top and bottom 25% of canvas get alpha-gradient overlay
  • Creates depth and focuses attention on center
  • Uses THEME['gradient_color'] (usually matches background)
  • Implemented via ax.imshow() with custom colormap
Typography (create_map_poster.py:683-753):
  • All text uses transform=ax.transAxes (0-1 normalized coordinates)
  • Font sizes scale dynamically based on poster dimensions
  • Latin script detection applies letter spacing for aesthetics
  • Coordinate formatting adjusts for hemisphere (N/S, E/W)

Core Functions Reference

Geocoding & Data Fetching

All fetching functions implement automatic caching using pickle serialization. Cache files are stored in cache/ directory with sanitized filenames based on parameters.

Road Styling & Hierarchy

OSM Highway Type Mapping:
The highway tag can be a list or string. Both functions handle this by taking the first element if it’s a list (create_map_poster.py:267-268, 299-300).

Rendering & Visual Effects

Typography & Internationalization

create_map_poster.py:114
Script Detection Logic:
  • Latin scripts (English, French, Spanish, etc.): Text is uppercased and letter-spaced (“P A R I S”)
  • Non-Latin scripts (Japanese, Arabic, Thai, Korean, Chinese): Natural spacing preserved (“東京”)
Implementation at create_map_poster.py:654-660:

Theme System

Theme Structure

Themes are JSON files stored in themes/ directory. Each theme defines a complete color palette:
themes/terracotta.json

Theme Loading

The load_theme() function (create_map_poster.py:177-207):
  • Reads JSON from themes/{theme_name}.json
  • Falls back to embedded terracotta theme if file not found
  • Returns dictionary accessible via global THEME variable
  • Prints theme name and description on load

Adding Custom Themes

1

Create JSON file

Add a new JSON file to themes/ directory with all required color keys.
2

Define color palette

All colors must be hex codes. Required keys:
  • bg, text, gradient_color
  • water, parks
  • road_motorway, road_primary, road_secondary, road_tertiary, road_residential, road_default
3

Use the theme

Reference by filename (without .json):

Font Management

The font_management.py module handles typography with support for Google Fonts and local font files.

Font Loading Pipeline

font_management.py:137
When no --font-family is specified, loads Roboto from fonts/ directory:
  • Roboto-Bold.ttffonts['bold']
  • Roboto-Regular.ttffonts['regular']
  • Roboto-Light.ttffonts['light']
Verifies all files exist before returning (font_management.py:158-168).
The download_google_font() function (font_management.py:17-134):Download Process:
  1. Queries Google Fonts API for font family with weights 300, 400, 700
  2. Parses CSS response to extract .woff2 or .ttf URLs
  3. Downloads font files to fonts/cache/ directory
  4. Maps weights to keys: 300→light, 400→regular, 700→bold
Caching:
  • Downloaded fonts cached in fonts/cache/{font_name_safe}_{weight}.woff2
  • Subsequent runs use cached files
  • Fallback to closest available weight if exact weight not found
Usage:

Typography Positioning

All text uses transform=ax.transAxes with normalized coordinates (0-1 range):
Dynamic Font Scaling (create_map_poster.py:618-681):
  • Base sizes defined at 12” reference width
  • Scales by min(height, width) / 12.0 for proper landscape/portrait support
  • City name font reduces for long names (>10 characters) to prevent truncation
  • Coordinate labels adjust format based on latitude/longitude signs

Caching System

Cache Implementation

MapToPoster uses pickle-based caching to avoid redundant OSM API calls:

Cached Data Types

Cache directory location can be customized via CACHE_DIR environment variable. Defaults to cache/ in project root.

Cache Invalidation

Cache files persist indefinitely unless manually deleted. To refresh data:

Performance Considerations

Large distance values can significantly impact performance:
  • < 20km: Fast downloads, reasonable memory usage
  • 20-30km: Slower, higher memory consumption
  • > 30km: Very slow, may hit API limits or memory issues
Recommendation: Use smallest distance that captures desired area. See Distance Guide.
The default network_type='all' includes all road types. For faster renders:
Other options: 'bike', 'walk' (see OSMnx documentation).
Reduce DPI for quick previews:
Cuts file size and render time by ~75%.
Implemented delays to respect service policies:
  • Nominatim geocoding: 1.0s delay (create_map_poster.py:334)
  • OSMnx graph fetch: 0.5s delay (create_map_poster.py:433)
  • OSMnx features fetch: 0.3s delay (create_map_poster.py:471)
Note: First run for a location is always slow due to API calls. Subsequent runs use cache and are near-instant.

Extension Points

Adding New Map Layers

To add a new layer (e.g., railways, buildings):
Don’t forget to:
  1. Add "railway": "#FF0000" to all theme JSON files
  2. Add fallback color in load_theme() default dict

Useful OSMnx Tag Patterns

See OSM Wiki for complete tag reference.

Custom Theme Properties

To add a new color property (e.g., for railways):
1

Update all theme JSON files

Add the new key to every theme in themes/:
2

Add fallback in load_theme()

Update the default theme dict at create_map_poster.py:186-200:
3

Use in rendering code

Reference via THEME['railway'] in your layer plotting code.

Dependencies

Core Python packages used: Full dependency list in pyproject.toml and requirements.txt.