Open-source project
originalankur/maptoposter avatar
originalankur/maptoposter

originalankur/maptoposter: generate minimalist city map posters from the command line

Project brief: Transform your favorite cities into beautiful, minimalist designs. MapToPoster lets you create and export visually striking map posters with code.

14,163 stars1,257 forksPythonMIT

At a glance

What is it?
maptoposter is a Python script that turns a city name into a print-ready map poster using OpenStreetMap data and 17 built-in themes. It suits anyone who wants a reproducible, scriptable poster and is comfortable running Python locally.
Who is it for?
Adopt maptoposter if you want reproducible, scriptable map posters and can run Python 3.11 or newer with network access to OpenStreetMap, Nominatim geocoding and Google Fonts. Do not adopt it if you need a hosted web app, an interactive editor or offline operation, because the README describes none of those.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 55 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What maptoposter actually produces, and who ends up using it

maptoposter is a single-purpose generator. You give it a city and a country, and it writes a PNG poster into a posters/ directory with a filename in the form {city}_{theme}_{YYYYMMDD_HHMMSS}.png. The repository name is maptoposter and the entry point is create_map_poster.py, so the whole tool is one script plus supporting modules such as font_management.py and the themes/ directory.

The intended audience is narrow in a useful way. This is for people who want a poster of a specific place and would rather re-run a command than open a design tool: developers making wall art, people producing social media images at exact pixel sizes, and anyone who wants the same poster regenerated later with a different theme. The README's example table spans San Francisco, Barcelona, Venice, Tokyo, Mumbai, Marrakech, Singapore, Melbourne, Dubai and Seattle, which shows the tool is meant to work anywhere OpenStreetMap has coverage.

It is not a map viewer and not a GIS analysis package. There is no query language, no layer control, no interactive pan or zoom. The output is a static image, and the only knobs are the city, the country, the theme, the radius and the canvas size.

How the pipeline works: geocoding, OSM data, matplotlib rendering

The dependency list in pyproject.toml tells you most of the architecture. geopy and geographiclib handle geocoding, so the --city and --country strings are resolved to coordinates rather than being matched against a bundled place database. osmnx and geopandas pull and shape the street network, pyproj and shapely handle projection and geometry, and matplotlib with contourpy and pillow does the drawing. networkx is present, which fits the street graph that osmnx returns.

So the data flow is: name in, coordinates out, street graph fetched from OpenStreetMap over the network, projected into a local coordinate system, then drawn as lines and fills onto a matplotlib canvas at the requested size. The --distance flag sets the map radius in meters, which means the poster is a square-ish window centered on the geocoded point, not an administrative boundary. That is why the README's distance guide pairs 4000-6000 m with dense cities like Venice and Amsterdam, and 15000-20000 m with large metros like Tokyo and Mumbai.

Themes are data, not code. The README shows a theme as a JSON file in themes/ with keys such as name, description, bg and text, and states there are 17 of them. That design means adding a palette does not require touching the Python. Fonts are a separate path: font_management.py downloads Google Fonts families on demand and caches them in fonts/cache/, which is why the multilingual examples can pass --font-family "Noto Sans JP" and expect the glyphs to exist for 東京 and 日本.

Installing maptoposter and rendering your first poster

The README recommends uv and says the first run installs dependencies automatically. If you have uv available, this single command creates the environment and generates a poster for Paris with the default terracotta theme. Expect a progress bar while dependencies resolve and OSM data downloads, then a PNG path printed or written under posters/.

bash
uv run ./create_map_poster.py --city "Paris" --country "France"

The README also documents syncing locked versions first, which is the more reproducible route because uv.lock pins the full dependency set.

bash
uv sync --locked
uv run ./create_map_poster.py --city "Paris" --country "France"

If you prefer a plain virtual environment, the README gives the pip path. Note that the project requires Python 3.11 or newer according to pyproject.toml, so an older interpreter will fail before any map work starts.

bash
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Once installed, the useful first move is to list the themes rather than guess a name, then generate with an explicit theme and distance. The README's example uses noir at 12000 m for New York, which is the Manhattan grid case.

bash
python create_map_poster.py --list-themes
python create_map_poster.py -c "New York" -C "USA" -t noir -d 12000

For a non-Latin script, add the display flags and a Google Fonts family. The README's Japanese example is the clearest template, and it also names the japanese_ink theme, which suits the ink-wash palette.

bash
python create_map_poster.py -c "Tokyo" -C "Japan" -dc "東京" -dC "日本" --font-family "Noto Sans JP" -t japanese_ink

If you need a specific pixel size, the README's resolution table maps inches at 300 DPI to targets. An A4 print is 8.3 by 11.7 inches, and the width and height flags cap at 20 inches each.

bash
python create_map_poster.py -c "Barcelona" -C "Spain" -t warm_beige -d 8000 -W 8.3 -H 11.7

To compare every palette without editing the command, the README documents an --all-themes flag that generates posters for all available themes in one run.

Where maptoposter gets in the way

The most obvious constraint is that everything depends on the network. Geocoding goes through geopy, map geometry comes from OpenStreetMap, and fonts are fetched from Google Fonts. The README does not describe an offline mode, a local tile source or a bundled data snapshot, so a machine behind a restrictive proxy will fail at the geocoding or download step rather than degrade gracefully. There is also no documented rate-limit handling, which matters if you loop over many cities in a script.

The second constraint is framing. Because the poster is a radius around a point, not a boundary, the default 18000 m will either crop a small city's interesting core or swallow a large metro's suburbs. You can override the center with --latitude and --longitude, which lets you nudge the frame, but there is no documented way to fit an arbitrary polygon or follow a coastline.

The third is that the project has no releases. The repository has no tags listed and no release notes, so version 0.2.0 in pyproject.toml is the only version marker you get. There is no changelog entry to read here beyond the file's existence, and no upgrade path is described. If you need a supported, versioned artifact, this is not that. It is a script you run from a checkout.

How maptoposter differs from QGIS and from hosted poster makers

The closest general-purpose alternative is QGIS. QGIS is an interactive desktop GIS with a print layout engine, layer styling, projections and export to many formats. The difference in approach is that QGIS gives you a canvas and a project file, while maptoposter gives you a command and a theme name. With QGIS you can hand-tune label placement, add a legend, and control every layer; with maptoposter you get the street network rendered in a palette and nothing else. If your poster needs annotations, multiple data layers or a specific cartographic standard, QGIS is the right tool and maptoposter is not.

The other alternative is a hosted poster maker in a browser. Those tend to offer a preview, a pan and zoom control, and an export button, at the cost of depending on someone else's service and having no reproducible command. maptoposter inverts that: no preview, no editor, but the exact same command produces the same poster later, and the themes are files you can copy and edit. The README's custom theme example is a JSON object with keys like name, description, bg and text, so a new palette is a small file drop rather than a feature request.

Licence, dependencies and what an upgrade costs you

maptoposter is MIT licensed, both in the repository's LICENSE file and in the pyproject.toml license field. MIT is permissive, so using the generated posters and modifying the script are both within the stated terms. That is the extent of what can be said here; read the LICENSE file itself before relying on it for anything commercial.

The upgrade cost is mostly dependency weight. The pinned set includes geopandas, osmnx, matplotlib, numpy, scipy, shapely, pyproj and pyogrio, all at exact versions. That is a heavy scientific Python stack for a tool whose output is a PNG, and exact pins mean security updates arrive only when the maintainer bumps them. The pyproject.toml and requirements.txt are not identical: requirements.txt adds flake8, mccabe, pycodestyle, pyflakes and types-requests, which are lint and type stubs rather than runtime needs.

Because there are no releases, there is no changelog to consult before pulling. Upgrading means pulling the default branch and re-running uv sync --locked, and the lock file is the only thing standing between you and a surprise from a transitive dependency. If you have already generated posters you care about, the output filenames include a timestamp, so old files are not overwritten by a new run with the same city and theme.

Editorial conclusion

Adopt maptoposter if you want reproducible, scriptable map posters and can run Python 3.11 or newer with network access to OpenStreetMap, Nominatim geocoding and Google Fonts. Do not adopt it if you need a hosted web app, an interactive editor or offline operation, because the README describes none of those. Before committing, verify the licence terms in LICENSE, check that your city geocodes correctly, and test whether the default 18000 m distance frames the area you actually want.

Frequently asked questions

how to use maptoposter

Install it with uv or pip and run create_map_poster.py with a city and country, for example python create_map_poster.py -c "Paris" -C "France". Add -t for a theme, -d for the radius in meters, and -W and -H for the canvas size in inches. The README also documents --list-themes and --all-themes for working with the 17 available palettes.

What do cartographers use to make maps?

This question is about general cartographic practice rather than maptoposter, and the repository does not describe professional cartography workflows. What can be said from the repository is that maptoposter itself relies on OpenStreetMap data, osmnx and geopandas for geometry, and matplotlib for rendering.

What is the purpose of the poster?

In maptoposter the poster is the output artifact: a PNG written to the posters/ directory with a filename in the form {city}_{theme}_{YYYYMMDD_HHMMSS}.png. The README's resolution table suggests intended uses such as Instagram posts, mobile and HD wallpapers, and A4 print at 300 DPI.

Official sources

  1. Official README
  2. Project repository
Community notes

Community notes