-
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 7 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 |
---|---|---|
|
@@ -227,6 +227,7 @@ and store them in the GMT cache folder. | |
datasets.load_fractures_compilation | ||
datasets.load_hotspots | ||
datasets.load_japan_quakes | ||
datasets.load_map_tiles | ||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
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'm unsure about the name. Should it be called 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 was thinking about the 2nd part of #2115, and had an idea about making a 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. Are both "map tiles" and "tile maps" grammatically correct? 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 think both should be ok. At least I know there's Web Map Tile Service (WMTS) and Tile Map Service (TMS) 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'm starting to lean towards 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 prefer 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, renamed to |
||
datasets.load_mars_shape | ||
datasets.load_ocean_ridge_points | ||
datasets.load_sample_bathymetry | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
""" | ||
Function to load raster basemap tiles 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 | ||
|
||
|
||
def load_map_tiles(region, source=None, lonlat=False, **kwargs): | ||
""" | ||
Load a georeferenced raster basemap 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. | ||
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. Perhaps we should convert the returned image to longitude/latitude because it's more commonly used than the Spherical Mercator coordinate system, following https://contextily.readthedocs.io/en/latest/warping_guide.html. 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. Most tiles are originally served as Web Mercator (EPSG:3857), and reprojecting to latlon (EPSG:4326) would result in distortion. I'd prefer the reprojection to be a user controlled step (e.g. using 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.
What about having an option so that users can decide on the desired projection and don't have to learn the syntax of the 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. Maybe in a separate PR? 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.
Sounds good to me. |
||
|
||
Parameters | ||
---------- | ||
region : list | ||
The bounding box of the map in the form of a list [*xmin*, *xmax*, | ||
*ymin*, *ymax*]. These coordinates should be in Spherical Mercator | ||
(EPSG:3857) if ``lonlat=False``, or longitude/latitude if | ||
``lonlat=True``. | ||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
source : xyzservices.TileProvider or str | ||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
[Optional. Default: Stamen Terrain web tiles] The tile source: web tile | ||
provider or path to local file. The web tile provider can be in the | ||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
form of a :class:`xyzservices.TileProvider` object or a URL. The | ||
placeholders for the XYZ in the URL need to be {x}, {y}, {z}, | ||
respectively. For local file paths, the file is read with rasterio and | ||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
all bands are loaded into the basemap. IMPORTANT: tiles are assumed to | ||
be in the Spherical Mercator projection (EPSG:3857). | ||
|
||
lonlat : bool | ||
[Optional. Default: False]. If True, coordinates in ``region`` are | ||
assumed to be lon/lat as opposed to Spherical Mercator. | ||
|
||
kwargs : dict | ||
Extra keyword arguments to pass to :func:`contextily.bounds2img`. | ||
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. It seems like there are only a few 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. Using That said, the API does seem rather stable looking at https://github.com/geopandas/contextily/blame/v1.2.0/contextily/tile.py#L157-L195, so I could add 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. Does the 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. It seems the 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. Hmm yeah, 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.
Likely the My concern 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.
Fair point. I've added the |
||
|
||
Returns | ||
------- | ||
raster : xarray.DataArray | ||
Georefenced 3D data array of RGB value. | ||
|
||
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_map_tiles | ||
>>> raster = load_map_tiles( | ||
... 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 ... | ||
""" | ||
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" | ||
weiji14 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
|
||
west, east, south, north = region | ||
image, (left, right, bottom, top) = contextily.bounds2img( | ||
w=west, s=south, e=east, n=north, source=source, ll=lonlat, **kwargs | ||
) | ||
|
||
# Turn RGBA image from channel-last (H, W, C) to channel-first (C, H, W) | ||
# and get just RGB (3 band) by dropping RGBA's alpha channel | ||
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. Why drop the alpha channel? GMT supports GeoTiff files with alpha channel. 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 think the alpha channel is the same throughout (at least for the basemaps I tried before), so not very helpful. 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. Looking at the grdimage_img_c2s_with_intensity function, it can handle 4 bands with transparency. 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, but the function returns an That said, there are only a few XYZ tiles that I know of which have actual transparencies, e.g. the Stamen Toner Labels example at geopandas/contextily#114 (comment). Regular tiles like Stamen Terrain do have an alpha channel, but they don't really carry meaningful transparency information (i.e. they are all 0s or 1s). |
||
rgb_image = image.transpose(2, 0, 1)[0:3, :, :] | ||
|
||
# Georeference RGB image into an xarray.DataArray | ||
dataarray = xr.DataArray( | ||
data=rgb_image, | ||
coords=dict( | ||
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.