Skip to content
21 changes: 10 additions & 11 deletions examples/gallery/maps/tilemaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
Tile maps
=========

The :meth:`pygmt.Figure.tilemap` method allows to plot
tiles from a tile server or local file as a basemap or overlay.
The :meth:`pygmt.Figure.tilemap` method allows to plot tiles from a tile server or
local file as a basemap or overlay.
"""

# %%
Expand All @@ -15,24 +15,23 @@
fig.tilemap(
region=[-157.84, -157.8, 21.255, 21.285],
projection="M12c",
# Set level of details (0-22)
# Higher levels mean a zoom level closer to the Earth's
# surface with more tiles covering a smaller
# geographic area and thus more details and vice versa
# Please note, not all zoom levels are always available
# Set level of details (0-22).
# Higher levels mean a zoom level closer to the Earth's surface with more tiles
# covering a smaller geographic area and thus more details and vice versa.
# Please note, not all zoom levels are always available.
zoom=14,
# Use tiles from OpenStreetMap tile server
source="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

source="https://tile.openstreetmap.org/{z}/{x}/{y}.png",
frame=Axis(annot=True, tick=True, grid=True),
)

fig.show()

# %%
# It's also possible to use tiles provided via the
# `contextily <https://github.com/geopandas/contextily>`__
# library. See :doc:`Contextily providers <contextily:providers_deepdive>`
# for a list of possible tilemap options.
# `contextily <https://github.com/geopandas/contextily>`__ library. See
# :doc:`Contextily providers <contextily:providers_deepdive>` for a list of possible
# tilemap options.

fig = pygmt.Figure()
fig.tilemap(
Expand Down
31 changes: 30 additions & 1 deletion pygmt/datasets/tile_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
from collections.abc import Sequence
from typing import Literal

from packaging.version import Version
from pygmt._show_versions import __version__ as _pygmt_version
from pygmt.exceptions import GMTParameterError

try:
import contextily
from rasterio.crs import CRS
Expand Down Expand Up @@ -40,6 +44,7 @@ def load_tile_map(
wait: int = 0,
max_retries: int = 2,
zoom_adjust: int | None = None,
headers: dict[str, str] | None = None,
) -> xr.DataArray:
"""
Load a georeferenced raster tile map from XYZ tile providers.
Expand Down Expand Up @@ -75,7 +80,7 @@ def load_tile_map(
OpenStreetMap Humanitarian web tiles.
- A web tile provider in the form of a URL. The placeholders for the XYZ in the
URL need to be {x}, {y}, {z}, respectively. E.g.
``https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png``.
``https://tile.openstreetmap.org/{z}/{x}/{y}.png``.
- A local file path. The file is read with :doc:`rasterio <rasterio:index>` and
all bands are loaded into the basemap. See
:doc:`contextily:working_with_local_files`.
Expand All @@ -98,6 +103,14 @@ def load_tile_map(
zoom_adjust
The amount to adjust a chosen zoom level if it is chosen automatically. Values
outside of -1 to 1 are not recommended as they can lead to slow execution.
headers
HTTP headers to include with requests to the tile server. This can be useful
for authentication or to set a custom User-Agent. When supported by
``contextily`` (>=1.7.0), PyGMT sets a default ``User-Agent`` header like
``PyGMT/vX.Y.Z (+https://www.pygmt.org)``.

.. note::
Requires ``contextily>=1.7.0``.

Returns
-------
Expand Down Expand Up @@ -164,6 +177,22 @@ def load_tile_map(
"zoom_adjust": zoom_adjust,
}

# TODO(contextily>=1.7.0): Remove once contextily>=1.7.0 is required.
# The 'headers' parameter was added in contextily v1.7.0
if Version(contextily.__version__) < Version("1.7.0"):
if headers is not None:
raise GMTParameterError(
reason="The 'headers' parameter requires contextily>=1.7.0."
)
else:
# Set default HTTP headers.
default_ua = f"PyGMT/{_pygmt_version} (+https://www.pygmt.org)"
if headers is None:
headers = {"User-Agent": default_ua}
elif not any(key.lower() == "user-agent" for key in headers):
headers = {**headers, "User-Agent": default_ua}
contextily_kwargs["headers"] = headers
Comment thread
Copilot marked this conversation as resolved.
Comment on lines +190 to +194

west, east, south, north = region
image, extent = contextily.bounds2img(
w=west, s=south, e=east, n=north, **contextily_kwargs
Expand Down
12 changes: 11 additions & 1 deletion pygmt/src/tilemap.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def tilemap(
wait: int = 0,
max_retries: int = 2,
zoom_adjust: int | None = None,
headers: dict[str, str] | None = None,
monochrome: bool = False,
no_clip: bool = False,
projection: str | None = None,
Expand Down Expand Up @@ -89,7 +90,7 @@ def tilemap(
OpenStreetMap Humanitarian web tiles.
- A web tile provider in the form of a URL. The placeholders for the XYZ in the
URL need to be ``{x}``, ``{y}``, ``{z}``, respectively. E.g.
``https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png``.
``https://tile.openstreetmap.org/{z}/{x}/{y}.png``.
- A local file path. The file is read with :doc:`rasterio <rasterio:index>` and
all bands are loaded into the basemap. See
:doc:`contextily:working_with_local_files`.
Expand All @@ -108,6 +109,14 @@ def tilemap(
zoom_adjust
The amount to adjust a chosen zoom level if it is chosen automatically. Values
outside of -1 to 1 are not recommended as they can lead to slow execution.
headers
HTTP headers to include with requests to the tile server. This can be useful
for authentication or to set a custom User-Agent. When supported by
``contextily`` (>=1.7.0), PyGMT sets a default ``User-Agent`` header like
``PyGMT/vX.Y.Z (+https://www.pygmt.org)``.

Comment thread
seisman marked this conversation as resolved.
.. note::
Requires ``contextily>=1.7.0``.
kwargs : dict
Extra keyword arguments to pass to :meth:`pygmt.Figure.grdimage`.
"""
Expand All @@ -120,6 +129,7 @@ def tilemap(
wait=wait,
max_retries=max_retries,
zoom_adjust=zoom_adjust,
headers=headers,
)
if lonlat:
raster.gmt.gtype = GridType.GEOGRAPHIC
Expand Down
Loading