-
Notifications
You must be signed in to change notification settings - Fork 224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add function to load raster tile maps using contextily #2125
Changes from 32 commits
6f7a4f0
917604d
66f66cc
e99ed4d
3c437c0
c738375
30fd9a2
f913222
1270c3f
0455bb4
8b5317f
6142b40
5400710
8aa1c7a
bf1c007
26dd404
2959e58
241aec1
eeda973
cc86e7d
67b17fc
5a446fb
3dc8846
5c61779
d06e2c0
6b2d5cd
b1ad795
484b6c8
cf7ab34
8563ecf
aa2d5fb
d74f878
9f14d31
b02b50c
10af781
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,11 +54,14 @@ | |
|
||
# intersphinx configuration | ||
intersphinx_mapping = { | ||
"python": ("https://docs.python.org/3/", None), | ||
"contextily": ("https://contextily.readthedocs.io/en/stable/", None), | ||
"geopandas": ("https://geopandas.org/en/stable/", None), | ||
"numpy": ("https://numpy.org/doc/stable/", None), | ||
"python": ("https://docs.python.org/3/", None), | ||
"pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None), | ||
"rasterio": ("https://rasterio.readthedocs.io/en/stable/", None), | ||
"xarray": ("https://docs.xarray.dev/en/stable/", None), | ||
"xyzservices": ("https://xyzservices.readthedocs.io/en/stable", None), | ||
Comment on lines
+62
to
+64
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't worked with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These intersphinx links are added so that |
||
} | ||
|
||
# options for sphinx-copybutton | ||
|
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,140 @@ | ||||||||||
""" | ||||||||||
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 | ||||||||||
Optional. Level of detail. [Default is 'auto']. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, from the default it seems like that there is also string input possible.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. Yes, I did wonder if people actually knew what to use for There is a small complication in that not all tile sources supports zoom up to level 22, so it's a bit hard to just document something like "Use an integer between 0 and 22 (inclusive)". But let me try adding a better description of this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, more detail added in 9f14d31. Let me know if that reads ok! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree that the description of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks @weiji14 for expanding the docstring for |
||||||||||
|
||||||||||
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). | ||||||||||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
lonlat : bool | ||||||||||
Optional. If False, coordinates in ``region`` are assumed to be | ||||||||||
Spherical Mercator as opposed to longitude/latitude [Default is True]. | ||||||||||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
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]. | ||||||||||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
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]. | ||||||||||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
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 | ||||||||||
seisman marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
... ) | ||||||||||
>>> 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Off-topic: All Python packages are installed using
pip
, except thatgeopandas
is installed usingmamba
. What's the reason?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at #1290 (comment), I think it was to get compatible GDAL versions between
gmt
,geopandas
andfiona
at the time. We could try doingpip install --pre geopandas
, but best do it in a separate PR.