Transform any city into a stunning, print-ready map poster.
A full-stack web application for creating high-resolution map posters of cities worldwide. Built with React, FastAPI, and powered by OpenStreetMap data.
- 18 Themes — From minimalist noir to vibrant neon cyberpunk, with full custom color override support
- Real-Time SVG Preview — Watch your map render layer-by-layer; switch themes and colors instantly without re-fetching data
- Global Coverage — Generate maps for any city in the world using OpenStreetMap data via OSMnx
- 4 Formats — Vertical Poster (3:4), Horizontal Poster (4:3), Desktop Wallpaper (16:9), Phone Wallpaper (9:16) — all at 300 DPI
- Layer Controls — Toggle motorways, primary/secondary/tertiary roads, water, ocean, and parks independently
- Smart Caching — Two-level cache (raw OSM data + rendered preview) with optional Supabase cloud backup
- History — Automatically saves SVG thumbnails and metadata with one-click restoration
Map2Poster runs as two processes: a Python backend (FastAPI) that fetches OSM data and generates maps, and a React frontend (Vite) that provides the UI. Both run locally with no external services required.
- Python 3.8+ (3.11 recommended)
- Node.js 16+ (18+ recommended)
- pip (Python package manager)
- GDAL/GEOS/PROJ system libraries (required by GeoPandas/Shapely)
macOS (via Homebrew):
brew install gdal geos projUbuntu/Debian:
sudo apt-get install -y libgdal-dev libgeos-dev libproj-dev gcc g++Windows: Install via OSGeo4W or use WSL with the Ubuntu instructions above.
-
Clone the repository
git clone https://github.com/ScottySalvo/Map2Poster.git cd Map2Poster -
Install Python dependencies
pip install -r requirements.txt
-
Install frontend dependencies
cd frontend npm install cd ..
You need two terminals — one for the backend, one for the frontend.
Terminal 1 — Backend:
python api.pyThe API server starts at http://localhost:8000. You should see:
⚠️ API: Cloud storage unavailable: ...
INFO: Uvicorn running on http://0.0.0.0:8080
The cloud storage warning is normal — it just means Supabase isn't configured, so caching uses local disk only.
Terminal 2 — Frontend:
cd frontend
npm run devThe frontend starts at http://localhost:5173. Open this URL in your browser.
The frontend automatically connects to http://localhost:8000 for the API (configured in frontend/src/config.js).
Enter a city and country, click Create Preview, and your map will generate. No API keys, no accounts, no external services needed. All data comes from the free OpenStreetMap Overpass API.
A note on generation speed: Map generation speed is largely dependent on the Overpass API (OpenStreetMap's public data server), which processes and returns road, water, park, and coastline data. The application itself is optimized — data is fetched in parallel, geometry processing is batched and vectorized, and a two-level cache ensures repeat requests are near-instant — but the Overpass API response time is the primary bottleneck and is outside the application's control.
By default, generated maps and cache are stored locally in posters/ and cache/ directories. For persistence across deploys (or if self-hosting on ephemeral infrastructure), you can optionally connect Supabase:
- Create a free Supabase project
- Create two storage buckets:
map-cacheandposters - Copy
.env.exampleto.envand fill in your credentials:cp .env.example .env
SUPABASE_URL=https://your-project-id.supabase.co SUPABASE_KEY=your-anon-key-here
- Restart the backend — you should see
☁️ API: Cloud storage enabled
For deploying to the internet (not just local use):
A Dockerfile and railway.json are included. Set these environment variables:
| Variable | Description | Example |
|---|---|---|
FRONTEND_URL |
Your frontend URL (for CORS) | https://your-app.vercel.app |
CUSTOM_DOMAIN |
Custom domain if applicable | https://your-domain.com |
SUPABASE_URL |
Supabase project URL | https://xxx.supabase.co |
SUPABASE_KEY |
Supabase anon key | eyJ... |
Set this build-time environment variable:
| Variable | Description | Example |
|---|---|---|
VITE_API_URL |
Your backend URL | https://your-backend.up.railway.app |
The frontend is a standard Vite/React app. Build with npm run build in the frontend/ directory.
Map2Poster/
├── api.py # FastAPI server
├── create_map_poster.py # Map generation engine
├── supabase_storage.py # Cloud storage adapter (optional)
├── requirements.txt
├── Dockerfile
├── railway.json # Railway deployment config
├── .env.example # Environment variable template
├── themes/ # 18 theme JSON files
├── fonts/ # Custom fonts for poster text
├── data/ # Natural Earth ocean shapefile
├── frontend/
│ ├── src/
│ │ ├── App.jsx # Main application
│ │ ├── App.css # Styles
│ │ ├── config.js # API URL configuration
│ │ ├── components/
│ │ │ ├── MapPreviewSVG.jsx # SVG map renderer
│ │ │ ├── MobileLayout.jsx # Mobile-responsive layout
│ │ │ ├── HistoryModal.jsx # History browser
│ │ │ └── FormatSelector.jsx # Format picker
│ │ └── main.jsx
│ └── package.json
├── posters/ # Generated output (auto-created)
└── cache/ # Local disk cache (auto-created)
| Method | Endpoint | Description |
|---|---|---|
| POST | /start-preview |
Start async preview generation |
| POST | /start-generate |
Start async high-res poster generation |
| POST | /start-restore |
Restore a map from history |
| GET | /job-status/{id} |
Poll job progress |
| GET | /themes |
List available themes |
| GET | /history |
List saved previews |
| DELETE | /history/{filename} |
Delete a history entry |
| POST | /purge-cache |
Clear all caches and storage |
- Geocode city name to coordinates (Nominatim)
- Fetch roads, water, parks, ocean data in parallel (OSMnx/Overpass API)
- Process ocean coastlines via polygonization, subtract ocean from parks
- Transform geometries to SVG paths using vectorized NumPy operations
- Render preview as JSON with SVG path data (frontend renders via React SVG)
- Generate high-res PNG via Matplotlib at 300 DPI (on demand)
- Cache at both raw data and rendered output levels
Autumn, Blueprint, Contrast Zones, Copper Patina, Crimson Noir, Feature Based, Forest, Gradient Roads, Japanese Ink, Midnight Blue, Monochrome Blue, Neon Cyberpunk, Noir, Ocean, Pastel Dream, Sunset, Terracotta, Warm Beige
This project is open source and community-driven. Contributions are welcome!
- Pull requests: Fork the repo, make your changes, and submit a PR
- Theme contributions: Add a new JSON file to
themes/following the existing format
This project began as a Python CLI script by Ankur Gupta and has been rebuilt into a full-stack web application.
| Original | Map2Poster | |
|---|---|---|
| Interface | CLI | Web app |
| Preview | None | Real-time SVG |
| Formats | 1 | 4 |
| Themes | 1 | 18 |
| Caching | None | Two-level + cloud |
Built upon foundational work by Ankur Gupta who created the original map poster generation script.
Created by Scotty Salvo.
Map data from OpenStreetMap contributors.
MIT License — see LICENSE for details.
If you find this project useful, consider buying me a coffee:



