Library / SDK
chrieke/prettymapp avatar
chrieke/prettymapp

prettymapp pins streamlit in its Dockerfile and leaves the extra unpinned

🖼️ Create beautiful maps from OpenStreetMap data in a streamlit webapp

2,825 stars453 forksPythonMIT

At a glance

What is it?
prettymapp is a Streamlit webapp and Python package that renders OpenStreetMap data as styled maps, forked from prettymaps and trimmed for speed. Its Docker image pins two Streamlit packages the manifest leaves floating, installs a third the manifest never mentions, and its test target reformats your code before running it.
Who is it for?
prettymapp is a good fit if you want a small, readable plotting path over OpenStreetMap geometry and do not need the configuration surface the original prettymaps offers, since this rewrite says outright that it drops the more complex options for speed and that it is only partially tested. Three things to check first.
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 89 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 October 4, 2026, and from our analysis. They are not legal advice.

Editorial analysis

It is partially tested, and the readme says so in one line

The project is a rewrite of prettymaps by marceloprates, and the credit is unambiguous: all credit for the original idea, designs, and implementation goes to him. What the rewrite changed is stated just as plainly. It focuses on speed and on configuration adapted to the webapp interface. It drops more complex configuration options in favour of improved speed, reduced code complexity, and simplified configuration interfaces. And then one sentence that most projects would leave out: it is partially tested. That is the maintainer's own assessment of the test suite, and it belongs in your evaluation rather than at the end of a footnote. The test suite does exist, with pylint and mypy wired into pytest, but the coverage is described as partial.

An address and a radius become a DataFrame, then a Plot

The library path is four calls. get_aoi takes an address string and a radius, with a rectangular flag that switches the area between a circle and a bounding box. get_osm_geometries turns that area into a frame of geometries, optionally filtered by a landcover class selector. A Plot object takes the frame, the area bounds, and a draw settings dictionary, and plot_all returns a matplotlib figure you save. Four styles ship by default, named Peach, Auburn, Citrus, and Flannel. There is also a path that skips the network entirely, reading an exported OSM XML file such as one downloaded from openstreetmap.org and using the frame's own total bounds as the extent.

The landcover selector takes booleans or explicit subclass lists

Customisation happens through dictionaries rather than a theme object, and the mechanism is worth reading because it is unusually direct. You copy the defaults and edit them. The landcover selector decides which OpenStreetMap subclasses get drawn, and each key accepts either shape: a boolean, where False drops all building subclasses and True includes all leisure subclasses, or an explicit list, where a single named subclass can be selected on its own. That is the layer most people want to trim first, since building footprints dominate a dense city. The visual layer works the same way, with a cmap, an edge colour, a line width, and a z order per layer, and the Plot class takes shape and contour width on top of that. In practice that looks like this:

python
from prettymapp.settings import LANDCOVER_CLASSES

custom_lc_classes = LANDCOVER_CLASSES.copy()
custom_lc_classes["urban"] = {"building": False} # drops all building subclasses
custom_lc_classes["grassland"] = {
    "leisure": True,  # Include all leisure subclasses
    "natural": ["island"],  # Selects only specific natural subclasses
}

df = get_osm_geometries(aoi=aoi, landcover_classes=custom_lc_classes)

The image pins Streamlit and the manifest does not

Three Streamlit related dependencies live in the Docker build, and they are not in the manifest. The install line is pip3 install with the local package, then streamlit pinned to an exact version, then streamlit-image-select pinned to another, then pyogrio with no version at all. The manifest's optional dependency group for the webapp names streamlit and streamlit-image-select with no constraints whatsoever. So the same two packages resolve differently depending on whether you took the image or the extra, and the image is the only place with a reproducible answer. The Dockerfile is explicit about why it installs from the local source rather than from the index, with a comment saying the app and the library must never drift apart, and the tree does carry a Dockerfile alongside a committed lock file for the other route.

pyogrio is in the image and in no manifest

pyogrio is the library geopandas reaches for when it needs an actual OGR backend to read vector data, and it appears once in this repository: on the Docker install line. It is not in the base dependencies, which are matplotlib, osmnx, pandas, geopandas, and shapely, and it is not in the streamlit extra either. So a developer who follows the PyPI route with pip install prettymapp, or the local route with uv sync and the extra, gets geopandas without the driver being named as a requirement, and only finds out at the point where a data source needs reading. This is the kind of gap that a package-data entry can hide too, since the only data file declared for shipping is the marker font.

make test formats the repository before it tests anything

The test target starts with the formatter, not with the tests. Its first line runs the formatter across the whole repository, and only the second line runs pytest, with pylint and mypy both enabled as pytest plugins through their own wrappers, a project pylint configuration file passed by name, mypy told to ignore missing imports, and a durations report. There is a second target with a bracket suffix that repeats the same shape and adds a runlive flag while raising the durations count, which is the opt-in path for tests that touch the network. Running the default target therefore rewrites your working tree as a side effect, and any diff you see afterwards may be the formatter rather than your own edit.

twine upload skips any version that already exists

The release path is four targets and the middle one is worth reading closely. The package target deletes the dist directory, builds, and then runs twine check over whatever landed there. The upload target then runs twine upload with a skip-existing flag and the same glob. That flag means a version already present on the index is passed over without an error, so re-running the upload after a mistaken version bump looks like it worked. There is no guard that compares the built version against what you meant to publish, and no dry run in the Makefile. Note also that twine appears both as a development dependency and as something invoked through an ephemeral runner, so the two invocations do not necessarily use the same twine.

The changelog URL is the repository URL with a word appended

The project URLs block has two entries and one of them does not resolve to anything. The homepage points at the repository, which is correct. The changelog entry points at the same repository path with the word changelog appended and no file, no anchor, and no blob segment, while the actual changelog is a file called CHANGELOG.md sitting in the root. So the one piece of project metadata a reader would use to find release notes sends them to a 404. The container has a smaller version of the same habit: it exposes port 8501 and binds the server to 0.0.0.0, which is what a hosted Streamlit deployment needs and what makes a locally run container reachable from the rest of your network.

Editorial conclusion

prettymapp is a good fit if you want a small, readable plotting path over OpenStreetMap geometry and do not need the configuration surface the original prettymaps offers, since this rewrite says outright that it drops the more complex options for speed and that it is only partially tested. Three things to check first. If you deploy the container, know that Streamlit is pinned to one version inside the image while the manifest extra floats, so the two routes can diverge. If you extend it, read the test target before running it, since it reformats the repository on the way to the test suite. And if you cite a version, check PyPI yourself, because the upload target skips any version that already exists rather than failing.

Frequently asked questions

Is prettymapp tested?

Partially, and the readme says so directly. It describes the project as partially tested while also listing speed, reduced code complexity and simplified configuration as the reasons it dropped options from prettymaps. The test suite exists and runs pylint and mypy through pytest, with a separate target that adds a live run flag for tests that reach the network.

Can I use prettymapp without the web app?

Yes. The package installs on its own with pip install prettymapp and requires Python 3.11 or later. You can build a map from an address and a radius, or from an exported OSM XML file, then hand the geometries to a Plot object with a style dictionary and save the matplotlib figure.

How do I change which OpenStreetMap layers prettymapp draws?

By copying and editing the landcover class dictionary and passing it to the geometry call. Each key takes either a boolean, where False drops every subclass of that group and True includes them all, or a list naming specific subclasses. The same copy and edit pattern applies to the visual layer, where each entry holds a colour map, an edge colour, a line width and a z order.

Does prettymapp need OpenStreetMap to be free or licensed a certain way?

The repository says nothing about OpenStreetMap licensing, funding, or terms. What it does state is where the data comes from: it reads geometry through osmnx, it can read OSM XML exported from openstreetmap.org, and it credits the original prettymaps project by marceloprates as the basis for the whole design. Any question about the underlying data source has to be answered from OpenStreetMap itself.

Which Streamlit version does prettymapp use?

It depends on the route. The Docker image installs streamlit at an exact pinned version along with a pinned streamlit-image-select, while the manifest's streamlit extra names both packages with no version constraint at all. The image also installs pyogrio, which appears in no manifest entry, so the container and a pip install of the extra are not the same dependency set.

Official sources

  1. chrieke/prettymapp on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/chrieke-prettymapp.svg)](https://hysenlabs.com/projects/chrieke-prettymapp)