Context geo tiles in Python
contextily
providers
One of the benefits of contextily is that it interfaces with the popular XYZ tiling standard. This means that any tiles provided through this format can be accessed. To make your life easier, contextily comes with a set of providers already included that makes including their maps much more straightforward and does not require you know any of the pesky details of the interface. These are all stored in the contextily.providers submodule, which also defines the specification contextily uses to store these so you can mimic it with an alternative provider that may not be built-in. This notebook will take a look at the submodule, illustrating how it works, what’s available and how you can expand it if needed.
contextily.providers
[1]:
import contextily as ctx import matplotlib.pyplot as plt
By default, if the user does not specify anything in this respect, the Stamen Terrain background is used:
[2]:
# we are using an extent around Mexico for the examples extent = (-12600000, -10300000, 1800000, 3800000)
[3]:
fig, ax = plt.subplots(figsize=(8, 8)) ax.axis(extent) ctx.add_basemap(ax)
The default can be modified by specifying a different provider with the source keyword.
source
For example, to use Stamen’s Toner Lite theme:
[4]:
fig, ax = plt.subplots(figsize=(8, 8)) ax.axis(extent) ctx.add_basemap(ax, source=ctx.providers.Stamen.TonerLite)
Or the main theme by OpenStreetMap:
[5]:
fig, ax = plt.subplots(figsize=(8, 8)) ax.axis(extent) ctx.add_basemap(ax, source=ctx.providers.OpenStreetMap.Mapnik)
The provider objects being passed to add_basemap are stored as dictionaries (with attribute access for easy access), so we can explore them in Python (or using tab completion in an interactive session!):
add_basemap
[6]:
ctx.providers.keys()
dict_keys(['OpenStreetMap', 'OpenSeaMap', 'OpenPtMap', 'OpenTopoMap', 'OpenRailwayMap', 'OpenFireMap', 'SafeCast', 'Thunderforest', 'OpenMapSurfer', 'Hydda', 'MapBox', 'Stamen', 'Esri', 'OpenWeatherMap', 'HERE', 'FreeMapSK', 'MtbMap', 'CartoDB', 'HikeBike', 'BasemapAT', 'nlmaps', 'NASAGIBS', 'NLS', 'JusticeMap', 'Wikimedia', 'GeoportailFrance', 'OneMapSG'])
Most of those providers have themselves multiple options. For example, OpenStreetMap provides multiple background styles:
[7]:
ctx.providers.OpenStreetMap.keys()
dict_keys(['Mapnik', 'DE', 'CH', 'France', 'HOT', 'BZH'])
Looking at a single one of those options, for example “Mapnik” (the default OpenStreetMap background), we can see this is a TileProvider object, which behaves as a dict:
TileProvider
[8]:
type(ctx.providers.OpenStreetMap.Mapnik)
contextily._providers.TileProvider
We can explore its contents:
[9]:
ctx.providers.OpenStreetMap.Mapnik
{'url': 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 'max_zoom': 19, 'attribution': '(C) OpenStreetMap contributors', 'name': 'OpenStreetMap.Mapnik'}
A “provider object” is then a dictionary with a few required entries, such as the url (required, with {z}/{x}/{y} to be replaced), with some additional metadata information (optional). These can be the attribution text to be used on the plot (attribution), or the maximum zoom level available for these tiles (max_zoom). When this information is available, contextily is smart enough to access and use it automatically.
{z}/{x}/{y}
attribution
max_zoom
The providers definitions that are shipped with contextily in the contextily.providers submodule, are coming from the `leaflet-providers <https://github.com/leaflet-extras/leaflet-providers>`__ package, an extension to javascript mapping library Leaflet that contains configurations for various free tile providers. Thus, all providers that are listed on their preview (http://leaflet-extras.github.io/leaflet-providers/preview/) should also be available in contextily.
`leaflet-providers
Some providers require additional information, such as an API access key. This can be specified by calling the provider object. Any keyword specified will override the default value in the provider object.
For example, the OpenWeatherMap requires an API key:
[10]:
ctx.providers.OpenWeatherMap.Clouds
{'url': 'http://{s}.tile.openweathermap.org/map/{variant}/{z}/{x}/{y}.png?appid={apiKey}', 'max_zoom': 19, 'attribution': 'Map data (C) OpenWeatherMap', 'apiKey': '<insert your api key here>', 'opacity': 0.5, 'variant': 'clouds', 'name': 'OpenWeatherMap.Clouds'}
We can specify this API key by calling the object:
[11]:
ctx.providers.OpenWeatherMap.Clouds(apiKey="my-private-api-key")
{'url': 'http://{s}.tile.openweathermap.org/map/{variant}/{z}/{x}/{y}.png?appid={apiKey}', 'max_zoom': 19, 'attribution': 'Map data (C) OpenWeatherMap', 'apiKey': 'my-private-api-key', 'opacity': 0.5, 'variant': 'clouds', 'name': 'OpenWeatherMap.Clouds'}
This can then be specified where a source is expected. For example:
ctx.add_basemap(ax, source=ctx.providers.OpenWeatherMap.Clouds(apiKey="my-private-api-key"))
Since the provider definition is basically nothing more than an url with the {z}/{x}/{y} format, you can also specify such an url manually, in case the provider of your choice is not available in contextily (the provider is expected to follow the standard XYZ tile protocol):
[12]:
fig, ax = plt.subplots(figsize=(8, 8)) ax.axis(extent) # using the url from `ctx.providers.OpenStreetMap.HOT` as example ctx.add_basemap(ax, source='https://a.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png')
In addition, you could also construct a dictionary with the "url" key and additional information yourself, for example to provide a default attribution text for all your plots.
"url"
Let us create a flat dictionary of the built-in providers:
[13]:
providers = {} def get_providers(provider): if "url" in provider: providers[provider['name']] = provider else: for prov in provider.values(): get_providers(prov) get_providers(ctx.providers)
This results in quite a few of them already available:
[14]:
len(providers)
141
For this illustration, let’s single out the following ones:
[15]:
selection = ['OpenStreetMap.Mapnik', 'OpenTopoMap', 'Stamen.Toner', 'Stamen.TonerLite', 'Stamen.Terrain', 'Stamen.TerrainBackground', 'Stamen.Watercolor', 'NASAGIBS.ViirsEarthAtNight2012', 'CartoDB.Positron', 'CartoDB.Voyager' ]
[19]:
fig, axs = plt.subplots(2, 5, figsize=(25, 10)) axs = axs.flatten() for name, ax in zip(selection, axs): ax.axis(extent) ctx.add_basemap(ax, source=providers[name]) ax.set_title(name) ax.set_axis_off() plt.show() fig.savefig("tiles.png", bbox_inches="tight")