-
Notifications
You must be signed in to change notification settings - Fork 213
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Figure.tilemap to plot XYZ tile maps (#2394)
Adding the tilemap method for plotting XYZ tile maps. This is a wrapper around `pygmt.datasets.load_tile_map` and `pygmt.Figure.grdimage`. Aliases from `grdimage` have been copied over, and docstring for parameters from `load_tile_map` have been copied over too. * Add rioxarray to CI build matrix and include it as optional dependency Let the Continuous Integration tests run with `rioxarray`, include it in pyproject.toml and environment.yml, and document it in `doc/install.rst` as an optional dependency. * Reproject and plot map in lonlat coordinates by default Given a region in lonlat coordinates, ensure that the output figure is also plotted in lonlat instead of Web Mercator. * Ensure that plot is clipped to bounding box region when no_clip is False Pass the region to the grdimage call so that the plot extent is the same as the bounding box region. Set `no_clip=True` to prevent the clipping from happening (i.e., the plot will extend out from the region of interest). Added a unit test checking results for both no_clip True/False, and updated some previous baseline images that have changed slightly. * Web Mercator to Spherical Mercator and remove verbose statement * Use rio.set_crs instead of rio.write_crs So that the `spatial_ref` CF coordinate won't be set, which would result in an int32 variable on Windows but int64 on Linux/macOS. --------- Co-authored-by: Dongdong Tian <[email protected]> Co-authored-by: Michael Grund <[email protected]> Co-authored-by: Yvonne Fröhlich <[email protected]>
- Loading branch information
1 parent
9af83e5
commit fad08a6
Showing
19 changed files
with
247 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -523,6 +523,7 @@ def _repr_html_(self): | |
subplot, | ||
ternary, | ||
text, | ||
tilemap, | ||
timestamp, | ||
velo, | ||
wiggle, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
""" | ||
tilemap - Plot XYZ tile maps. | ||
""" | ||
from pygmt.clib import Session | ||
from pygmt.datasets.tile_map import load_tile_map | ||
from pygmt.helpers import ( | ||
GMTTempFile, | ||
build_arg_string, | ||
fmt_docstring, | ||
kwargs_to_strings, | ||
use_alias, | ||
) | ||
|
||
try: | ||
import rioxarray | ||
except ImportError: | ||
rioxarray = None | ||
|
||
|
||
@fmt_docstring | ||
@use_alias( | ||
B="frame", | ||
E="dpi", | ||
I="shading", | ||
J="projection", | ||
M="monochrome", | ||
N="no_clip", | ||
Q="nan_transparent", | ||
# R="region", | ||
V="verbose", | ||
c="panel", | ||
p="perspective", | ||
t="transparency", | ||
) | ||
@kwargs_to_strings(c="sequence_comma", p="sequence") # R="sequence", | ||
def tilemap( | ||
self, region, zoom="auto", source=None, lonlat=True, wait=0, max_retries=2, **kwargs | ||
): | ||
r""" | ||
Plots an XYZ tile map. | ||
This method loads XYZ tile maps from a tile server or local file using | ||
:func:`pygmt.datasets.load_tile_map` into a georeferenced form, and plots | ||
the tiles as a basemap or overlay using :meth:`pygmt.Figure.grdimage`. | ||
**Note**: By default, standard web map tiles served in a Spherical Mercator | ||
(EPSG:3857) Cartesian format will be reprojected to a geographic coordinate | ||
reference system (OGC:WGS84) and plotted with longitude/latitude bounds | ||
when ``lonlat=True``. If reprojection is not desired, please set | ||
``lonlat=False`` and provide Spherical Mercator (EPSG:3857) coordinates to | ||
the ``region`` parameter. | ||
{aliases} | ||
Parameters | ||
---------- | ||
region : list | ||
The bounding box of the map in the form of a list [*xmin*, *xmax*, | ||
*ymin*, *ymax*]. These coordinates should be in longitude/latitude if | ||
``lonlat=True`` or Spherical Mercator (EPSG:3857) if ``lonlat=False``. | ||
zoom : int or str | ||
Optional. Level of detail. Higher levels (e.g. ``22``) mean a zoom | ||
level closer to the Earth's surface, with more tiles covering a smaller | ||
geographical area and thus more detail. Lower levels (e.g. ``0``) mean | ||
a zoom level further from the Earth's surface, with less tiles covering | ||
a larger geographical area and thus less detail [Default is | ||
``"auto"`` to automatically determine the zoom level based on the | ||
bounding box region extent]. | ||
**Note**: The maximum possible zoom level may be smaller than ``22``, | ||
and depends on what is supported by the chosen web tile provider | ||
source. | ||
source : xyzservices.TileProvider or str | ||
Optional. The tile source: web tile provider or path to a local file. | ||
Provide either: | ||
- A web tile provider in the form of a | ||
:class:`xyzservices.TileProvider` object. See | ||
:doc:`Contextily providers <contextily:providers_deepdive>` for a | ||
list of tile providers [Default is | ||
``xyzservices.providers.Stamen.Terrain``, i.e. Stamen Terrain 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``. | ||
- 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`. | ||
IMPORTANT: Tiles are assumed to be in the Spherical Mercator projection | ||
(EPSG:3857). | ||
lonlat : bool | ||
Optional. If ``False``, coordinates in ``region`` are assumed to be | ||
Spherical Mercator as opposed to longitude/latitude [Default is | ||
``True``]. | ||
wait : int | ||
Optional. If the tile API is rate-limited, the number of seconds to | ||
wait between a failed request and the next try [Default is ``0``]. | ||
max_retries : int | ||
Optional. Total number of rejected requests allowed before contextily | ||
will stop trying to fetch more tiles from a rate-limited API [Default | ||
is ``2``]. | ||
kwargs : dict | ||
Extra keyword arguments to pass to :meth:`pygmt.Figure.grdimage`. | ||
Raises | ||
------ | ||
ImportError | ||
If ``rioxarray`` is not installed. Follow | ||
:doc:`install instructions for rioxarray <rioxarray:installation>`, | ||
(e.g. via ``pip install rioxarray``) before using this function. | ||
""" | ||
kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access | ||
|
||
if rioxarray is None: | ||
raise ImportError( | ||
"Package `rioxarray` is required to be installed to use this function. " | ||
"Please use `pip install rioxarray` or " | ||
"`mamba install -c conda-forge rioxarray` " | ||
"to install the package." | ||
) | ||
|
||
raster = load_tile_map( | ||
region=region, | ||
zoom=zoom, | ||
source=source, | ||
lonlat=lonlat, | ||
wait=wait, | ||
max_retries=max_retries, | ||
) | ||
|
||
# Reproject raster from Spherical Mercator (EPSG:3857) to | ||
# lonlat (OGC:CRS84) if bounding box region was provided in lonlat | ||
if lonlat and raster.rio.crs == "EPSG:3857": | ||
raster = raster.rio.reproject(dst_crs="OGC:CRS84") | ||
raster.gmt.gtype = 1 # set to geographic type | ||
|
||
# Only set region if no_clip is None or False, so that plot is clipped to | ||
# exact bounding box region | ||
if kwargs.get("N") in [None, False]: | ||
kwargs["R"] = "/".join(str(coordinate) for coordinate in region) | ||
|
||
with GMTTempFile(suffix=".tif") as tmpfile: | ||
raster.rio.to_raster(raster_path=tmpfile.name) | ||
with Session() as lib: | ||
lib.call_module( | ||
module="grdimage", args=build_arg_string(kwargs, infile=tmpfile.name) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
outs: | ||
- md5: 9317080021b0ce6f3b9ea6d17feece00 | ||
size: 23275 | ||
path: test_tilemap_no_clip_False.png |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
outs: | ||
- md5: 83e6119b2351f9d472ca7e3cc45388c3 | ||
size: 60984 | ||
path: test_tilemap_no_clip_True.png |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
outs: | ||
- md5: 3de0555d86aca49b92425c8d5272a934 | ||
size: 59286 | ||
path: test_tilemap_ogc_wgs84.png |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
outs: | ||
- md5: a76d9a9a1890d6b1345305eaea598bc3 | ||
size: 122195 | ||
path: test_tilemap_web_mercator.png |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
""" | ||
Tests Figure.tilemap. | ||
""" | ||
import pytest | ||
from pygmt import Figure | ||
|
||
contextily = pytest.importorskip("contextily") | ||
rioxarray = pytest.importorskip("rioxarray") | ||
|
||
|
||
@pytest.mark.mpl_image_compare | ||
def test_tilemap_web_mercator(): | ||
""" | ||
Create a tilemap plot in Spherical Mercator projection (EPSG:3857). | ||
""" | ||
fig = Figure() | ||
fig.tilemap( | ||
region=[-20000000.0, 20000000.0, -20000000.0, 20000000.0], | ||
zoom=0, | ||
lonlat=False, | ||
frame="afg", | ||
) | ||
return fig | ||
|
||
|
||
@pytest.mark.mpl_image_compare | ||
def test_tilemap_ogc_wgs84(): | ||
""" | ||
Create a tilemap plot using longitude/latitude coordinates (OGC:WGS84), | ||
centred on the international date line. | ||
""" | ||
fig = Figure() | ||
fig.tilemap( | ||
region=[-180.0, 180.0, -90, 90], zoom=0, frame="afg", projection="R180/5c" | ||
) | ||
return fig | ||
|
||
|
||
@pytest.mark.mpl_image_compare | ||
@pytest.mark.parametrize("no_clip", [False, True]) | ||
def test_tilemap_no_clip(no_clip): | ||
""" | ||
Create a tilemap plot clipped to the Southern Hemisphere when no_clip is | ||
False, but for the whole globe when no_clip is True. | ||
""" | ||
fig = Figure() | ||
fig.tilemap( | ||
region=[-180.0, 180.0, -90, 0.6886], | ||
zoom=0, | ||
frame="afg", | ||
projection="H180/5c", | ||
no_clip=no_clip, | ||
) | ||
return fig |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters