basemap: plotting on map projections inside matplotlib
Plot on map projections (with coastlines and political boundaries) using matplotlib
At a glance
- What is it?
- basemap adds map projections, coastlines and political boundaries to matplotlib, with prebuilt wheels on PyPI and separate data packages for the map assets. It is a vector plotting library for figures rather than an interactive map, which is both its strength and the reason it fits a specific kind of work.
- Who is it for?
- Adopt basemap if you need a publication quality vector map inside a matplotlib figure, with coastlines and political boundaries that match the styling of the rest of your plot, and you are prepared to manage a data package and a bundled GEOS library. Do not adopt it for interactive maps, tile based cartography or production web mapping, because it draws into a figure rather than a map view.
- 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 172 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 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A plotting library, not a map viewer
basemap is described in one line as plotting on map projections, with coastlines and political boundaries, using matplotlib. That phrasing sets expectations precisely, because it is the difference between this library and everything else in the same general area.
A basemap object is added to a matplotlib axes, and then you draw features onto it: coastlines, borders, filled land areas, and whatever data points your analysis produced. The output is a matplotlib figure, so it composes with the rest of your plotting code, saves through the usual savefig path, and carries the fonts and styling you already set up.
There is no map viewport, no panning, no tile fetching and no interaction. The tool does not render for the web. It produces a picture, and the picture is the point.
That makes the audience specific. Researchers and engineers producing figures for a paper, a report or a slide deck need coastlines that match the projection of their data and that render as vector paths rather than as a background image. The project sits inside the matplotlib organisation, and the documentation lives at matplotlib.org/basemap/, with example scripts in the doc/examples directory of the repository, plus an FAQ and the matplotlib-users mailing list for questions.
Two names appear as contacts, Ben Root and Víctor Molina García, and the acknowledgements list in the README is long, which reflects a library that has been worked on by many people over a long period.
The data packages are a separate decision
The installation is one command, because precompiled binary wheels for Windows, GNU/Linux and MacOS are available on PyPI:
python -m pip install basemapThe README also points to the basemap installation instructions in the documentation for specific details on installing through conda or from source.
What that command does not do is bring the map assets, and that is the part worth understanding before you start. The package depends on basemap-data, which carries the basic data assets supporting the essential functionality. Separately, there is an optional dependency on basemap-data-hires with the high resolution assets, which you install manually:
python -m pip install basemap-data-hiresSo the choice of resolution is a second install decision, not a runtime flag. A figure for a paper at print resolution and a figure for a slide at screen resolution have different needs, and the split lets you carry only the data you actually use.
There is a third optional dependency, OWSLib, which is needed for the Basemap method Basemap.wmsimage. That is the hook for pulling map imagery from an OGC web map service, and it is the one feature in the package that pulls in an outside network dependency.
GEOS arrives inside the wheels under its own licence
The licensing arrangement has two parts, and the README separates them explicitly.
The library itself is licensed under the terms of the MIT license, with the text in the LICENSE file. The GEOS dynamic library bundled with the package wheels is provided under the terms of the LGPL-2.1-only license, as given in a separate LICENSE.geos file.
That split matters more than it looks for a library that is MIT. The point of the MIT grant is that a user can take the code and do what they like with it, and a bundled LGPL component narrows that for the binary you install. The LGPL terms for a dynamically linked library are usually workable in practice, but this is exactly the kind of detail that a distribution or a legal review will ask about, and the project has done the right thing by stating it plainly and shipping the licence text alongside.
The build setup backs up the picture. setup.py contains a get_geos_install_prefix helper that returns the GEOS installation prefix or None if it is not found. It first looks for a GEOS_DIR environment variable, and otherwise searches a list of conventional prefixes, including the user's home directory variants, /usr/local, /usr, /opt/local, /opt and /sw. It then looks for a library file named with a geos_c pattern, with the expected extension per platform: dll on Windows and Cygwin, dylib on macOS and the shared object pattern elsewhere. That is what lets the wheels bundle a working GEOS without the user installing one.
Building from source means Cython and a pinned numpy
For anyone building from source rather than using a wheel, the build metadata sets the constraints.
The build backend is setuptools, and the build requirements are setuptools between 67.0 and 81.0, wheel between 0.40 and 0.46, cython between 3.0 and 3.2, and numpy pinned per interpreter: numpy == 2.1.0 when the Python version is 3.13, and numpy == 2.0.0 for Python 3.12 and below. That numpy pin is worth noticing, because it means a build against a newer numpy will not happen without changing the build requirements.
The version of the package itself is not hard coded in the metadata. setup.py reads it out of the source tree: get_version opens src/<package>/__init__.py and applies a regular expression to find the __version__ assignment. This is the usual pattern for keeping the version in one place, and it means the value comes from the code rather than from a duplicated constant.
The rest of the packaging is visible in the tree. There is a MANIFEST.in for the source distribution, a .pylintrc for lint configuration, a custom sdist command, find_namespace_packages for the package discovery, and setuptools Extension objects for the compiled parts.
The test configuration is also strict in a useful way. The pytest options turn warnings into errors with filterwarnings set to error, so a warning that would otherwise scroll past becomes a failing test, and the coverage configuration maps the src directory against installed site-packages paths so that a measured run reports on the sources rather than on the copies.
The fillcontinents bug and the drawlsmask workaround
The README has a known bugs section with one entry, and it is worth understanding because it will bite anyone drawing land masses.
The Basemap.fillcontinents method does not always do the right thing. The reason given is that matplotlib always tries to fill the inside of a polygon, and under certain situations what is the inside of a coastline polygon can be ambiguous, so the outside may be filled instead of the inside. In other words, on some projections the fill can invert, and you get an ocean where the land should be.
Two workarounds are offered. You can change the map projection region slightly, which changes which side of the polygon matplotlib decides is the inside, or you can mask the land areas with the Basemap.drawlsmask method instead of filling the coastline polygons. The README says the second is illustrated in the ortho_demo.py example.
This is a nice illustration of how basemap relates to matplotlib. The ambiguity is matplotlib's polygon filling behaviour meeting a projection where the inside of a coastline is not a simple matter of winding order. Basemap does not paper over it, it documents it and offers a different drawing call that avoids the problem entirely.
Where basemap fits against tile based mapping
It is worth being explicit about the alternative, because the choice is really about output medium rather than about features.
The alternative approach to putting a map in a web page is to embed a tile based mapping library, which requests raster tiles from a tile service as the user moves around, and which brings coastlines, labels, roads and hillshading with it as a ready made cartographic product. The difference in approach is where the map data comes from and what the artefact is.
With basemap, the map is a picture you compose. Coastlines come from the data packages as vector geometry, get transformed into your chosen projection, and are drawn with matplotlib's own styling, so they sit next to your data points with the same fonts, line widths and colour scheme. There is no network dependency at render time and no tile service to pay for or to rate limit. The cost is that you assemble the cartography yourself, label placement is your problem, and there is no interactive behaviour at all because the output is a static figure.
So the decision is between a publication figure and an application. If you are writing a paper or building a report pipeline where figures are regenerated when the data changes, that difference is exactly what you want.
Where basemap is the wrong tool
Three boundaries are clear.
The first is interactivity and the web. If you need a map a user can pan and zoom, or click on for a tooltip, or that updates as data changes, a figure canvas is the wrong substrate. Nothing in the library is oriented towards a browser view state, and the output path is a saved image.
The second is cartographic quality. Basemap gives you geometry and lets matplotlib draw it. It does not give you a designed basemap with place labels, terrain shading or administrative boundaries at a publication standard, and the data packages are described in terms of coastlines and political boundaries rather than as a styled cartographic product.
The third is the dependency surface. Even with prebuilt wheels there is a GEOS shared library bundled under its own licence, a separate data package for the assets, an optional high resolution data package, and an optional OWSLib for the web map service method. That is more moving parts than a pure Python plotting library usually carries, and a team that simply wants to draw a heat map over a coastline will feel each of them.
On upkeep, the repository is not archived and the last push was on 2026-05-04, with the newest release v2.0.0 dated 2025-06-13. The default branch is develop rather than main, so the head of the repository is ahead of the last tagged version, which is worth remembering if you pin versions in a reproducible environment.
Editorial conclusion
Adopt basemap if you need a publication quality vector map inside a matplotlib figure, with coastlines and political boundaries that match the styling of the rest of your plot, and you are prepared to manage a data package and a bundled GEOS library. Do not adopt it for interactive maps, tile based cartography or production web mapping, because it draws into a figure rather than a map view. Verify three things first: which data package you need, since basemap-data carries the essential assets while basemap-data-hires is optional and separate; the licence boundary, since the library is MIT but the GEOS dynamic library bundled in the wheels is under LGPL-2.1-only and sits in its own LICENSE.geos file; and whether the fillcontinents behaviour matters for your projection, since the README documents that it can fill the outside of a coastline polygon and points to drawlsmask as the workaround. The last push was on 2026-05-04 and the newest release is v2.0.0 dated 2025-06-13.
Frequently asked questions
How do I install basemap?
Precompiled binary wheels for Windows, GNU/Linux and MacOS are on PyPI, so python -m pip install basemap is enough for most cases. For conda or a source build, the README points to the basemap installation instructions in the documentation.
What are basemap-data and basemap-data-hires?
The package depends on basemap-data, which carries the basic data assets supporting the essential functionality, and optionally on basemap-data-hires with the high resolution assets, installed with python -m pip install basemap-data-hires.
Why does filling continents sometimes fill the wrong side?
The README documents it as a known bug: matplotlib always tries to fill the inside of a polygon, and under certain situations what is the inside of a coastline polygon can be ambiguous, so the outside may be filled instead. The workarounds are to change the projection region slightly or to mask land areas with Basemap.drawlsmask instead of filling the coastline polygons.
What licence applies to basemap and its bundled GEOS library?
The library is under the MIT license in the LICENSE file, while the GEOS dynamic library bundled with the package wheels is provided under the LGPL-2.1-only license as given in LICENSE.geos.
What does Basemap.wmsimage need?
That method depends optionally on OWSLib, which is not installed by default. It is the feature for pulling map imagery from an OGC web map service rather than drawing from the bundled coastline data.
Official sources
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.
[](https://hysenlabs.com/projects/matplotlib-basemap)Was this useful?