A Python ecosystem offers numerous tools for the visualisation of data on a map. A lot of them depend on XYZ tiles, providing a base map layer, either from OpenStreetMap, satellite or other sources. The issue is that each package that offers XYZ support manages its own list of supported providers.
We have built
xyzservices package to support any Python library making use of XYZ tiles. I’ll try to explain the rationale why we did that, without going into the details of the package. If you want those details, check its documentation.
Let me quickly look at a few popular packages and their approach to tile management –
contextily brings contextual base maps to static
geopandas plots. It comes with a dedicated
contextily.providers module, which contains a hard-coded list of providers scraped from the list used by leaflet (as of version 1.1.0).
ipyleaflet brings leaflet support to Jupyter notebooks and comes with a bit more options than
folium. It has a very similar approach as
contextily does – it has a hard-coded list of about 37 providers in its
holoviews provides a Python interface to the Bokeh library and its list of supported base maps is also hard-coded.
A similar situation is in other packages like
Each package has to maintain the list of base maps, ensure that they all work, respond to users requiring more, update links… That is a lot of duplicated maintenance burden. We think it is avoidable.
All XYZ tile providers have a single lightweight home and a clean API supporting the rest of the ecosystem. All the other packages use the same resource, one which is tested and expanded by a single group of maintainers.
We have designed
xyzservices to be exactly that. It is a Python package that has no dependencies and only a single purpose – to collect and process metadata of tile providers.
We envisage a few potential use cases.
The first – packages like
geopandas will directly support
xyzservices.TileProvider object when specifying tiles. Nothing else is needed,
contextily will fetch the data it needs (final tile URL, an attribution, zoom and extent limits) from the object. In the code form:
import xyzservices.providers as xyz from contextily import add_basemap add_basemap(ax, source=xyz.CartoDB.Positron)
The second option is wrapping
xyzservices.providers into a custom API providing, for example, an interactive selection of tiles.
The third one is using different parts of a
TileProvider individually when passing the information. This option can be currently used, for example, with
import folium import xyzservices.providers as xyz tiles = xyz.CartoDB.Positron folium.Map( location=[53.4108, -2.9358], tiles=tiles.build_url(), attr=tiles.attribution, )
The last one is the most versatile. The
xyzservices comes with a JSON file used as a storage of all the metadata. The JSON is automatically installed to
share/xyzservices/providers.json where it is available for any other package without depending on
We hope to cooperate with maintainers of other existing packages and move most of the functionality around XYZ tiles that can be reused to
xyzservices. We think that it will:
- Remove the burden from individual developers. Any package will just implement an interface to Python or JSON API of
- Expand the list easy-to-use tiles for users.
xyzservicescurrently has over 200 providers, all of which should be available for users across the ecosystem, without the need to individually hard-code them in every package.
While this discussion started in May 2020 (thanks @darribas!), the initial version of the package is out now and installable from PyPI and conda-forge. We hope to have as many developers as possible on board to allow for the consolidation of the ecosystem in the future.