-
Notifications
You must be signed in to change notification settings - Fork 224
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add function to load raster tile maps using contextily (#2125)
New dataset function to load XYZ tiles! Uses contextily to retrieve the tiles based on a bounding box. Included an example doctest that shows how the map tiles can be loaded into an xarray.DataArray. Added a new section in the API docs and intersphinx mappings for contextily and xyzservices. * Use correct Spherical Mercator coordinates Can't assume that the input bounding box (which can be in longitude/latitude) is the same as the returned extent (which is always in EPSG:3857). * Change ll parameter to lonlat To fix pylint `C0103: Argument name "ll" doesn't conform to snake_case naming style (invalid-name)`. * Add contextily to CI build matrix and include it as optional dependency Let the Continuous Integration tests run with `contextily`, include it in pyproject.toml and environment.yml, and document it in `doc/install.rst` as an optional dependency. * Set default bounding box coordinates to be lonlat Bounding box coordinates are assumed to be longitude/latitude by default, rather than in Spherical Mercator. * Skip doctest when contextily is not installed Using the `__doctest_requires__` variable, see https://github.com/astropy/pytest-doctestplus/tree/v0.12.1#doctest-dependencies * Add intersphinx link for rasterio Also updated intersphinx link for xarray to new URL at https://docs.xarray.dev/en/stable * Document wait and max_retries parameters used in contextily.bounds2img * Use PyGMT's convention for default values in docstrings Modified from original contextily.bounds2img docstring to fit PyGMT's standards. Xref https://github.com/Generi cMappingTools/pygmt/pull/1182. * Rename load_map_tiles to load_tile_map Also create new dedicated section for load_tile_map in the API docs index. * Add zoom parameter and remove kwargs Wrap all of the parameters in contextily.bounds2img, and so can remove kwargs. Need to disable the pylint recommendation `R0914: Too many local variables`. * Add contextily to docs build CI requirements * Add contextily to pygmt.show_versions() dependency list * Document the three possible source options thoroughly Split out the three possible source options into bullet points. Link to https://contextily.readthedocs.io/en/latest/providers_deepdive.html, give an example OpenStreetMap URL, and link to https://contextily.readthedocs.io/en/stable/working_with_local_files.html. * Add more detail about the zoom level of detail --------- 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
3305bf2
commit f379164
Showing
13 changed files
with
180 additions
and
6 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
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,151 @@ | ||
""" | ||
Function to load raster tile maps from XYZ tile providers, and load as | ||
:class:`xarray.DataArray`. | ||
""" | ||
|
||
try: | ||
import contextily | ||
except ImportError: | ||
contextily = None | ||
|
||
import numpy as np | ||
import xarray as xr | ||
|
||
__doctest_requires__ = {("load_tile_map"): ["contextily"]} | ||
|
||
|
||
def load_tile_map(region, zoom="auto", source=None, lonlat=True, wait=0, max_retries=2): | ||
""" | ||
Load a georeferenced raster tile map from XYZ tile providers. | ||
The tiles that compose the map are merged and georeferenced into an | ||
:class:`xarray.DataArray` image with 3 bands (RGB). Note that the returned | ||
image is in a Spherical Mercator (EPSG:3857) coordinate reference system. | ||
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``]. | ||
Returns | ||
------- | ||
raster : xarray.DataArray | ||
Georeferenced 3-D data array of RGB values. | ||
Raises | ||
------ | ||
ModuleNotFoundError | ||
If ``contextily`` is not installed. Follow | ||
:doc:`install instructions for contextily <contextily:index>`, (e.g. | ||
via ``pip install contextily``) before using this function. | ||
Examples | ||
-------- | ||
>>> import contextily | ||
>>> from pygmt.datasets import load_tile_map | ||
>>> raster = load_tile_map( | ||
... region=[103.60, 104.06, 1.22, 1.49], # West, East, South, North | ||
... source=contextily.providers.Stamen.TerrainBackground, | ||
... lonlat=True, # bounding box coordinates are longitude/latitude | ||
... ) | ||
>>> raster.sizes | ||
Frozen({'band': 3, 'y': 1024, 'x': 1536}) | ||
>>> raster.coords | ||
Coordinates: | ||
* band (band) int64 0 1 2 | ||
* y (y) float64 1.663e+05 1.663e+05 1.663e+05 ... 1.272e+05 ... | ||
* x (x) float64 1.153e+07 1.153e+07 1.153e+07 ... 1.158e+07 ... | ||
""" | ||
# pylint: disable=too-many-locals | ||
if contextily is None: | ||
raise ModuleNotFoundError( | ||
"Package `contextily` is required to be installed to use this function. " | ||
"Please use `pip install contextily` or " | ||
"`conda install -c conda-forge contextily` " | ||
"to install the package." | ||
) | ||
|
||
west, east, south, north = region | ||
image, extent = contextily.bounds2img( | ||
w=west, | ||
s=south, | ||
e=east, | ||
n=north, | ||
zoom=zoom, | ||
source=source, | ||
ll=lonlat, | ||
wait=wait, | ||
max_retries=max_retries, | ||
) | ||
|
||
# Turn RGBA img from channel-last to channel-first and get 3-band RGB only | ||
_image = image.transpose(2, 0, 1) # Change image from (H, W, C) to (C, H, W) | ||
rgb_image = _image[0:3, :, :] # Get just RGB by dropping RGBA's alpha channel | ||
|
||
# Georeference RGB image into an xarray.DataArray | ||
left, right, bottom, top = extent | ||
dataarray = xr.DataArray( | ||
data=rgb_image, | ||
coords={ | ||
"band": [0, 1, 2], # Red, Green, Blue | ||
"y": np.linspace(start=top, stop=bottom, num=rgb_image.shape[1]), | ||
"x": np.linspace(start=left, stop=right, num=rgb_image.shape[2]), | ||
}, | ||
dims=("band", "y", "x"), | ||
) | ||
|
||
# If rioxarray is installed, set the coordinate reference system | ||
if hasattr(dataarray, "rio"): | ||
dataarray = dataarray.rio.write_crs(input_crs="EPSG:3857") | ||
|
||
return dataarray |
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