-
Notifications
You must be signed in to change notification settings - Fork 245
load_tile_map: Add the new parameter 'crs' to set the CRS of the returned dataarray #3554
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
Changes from 5 commits
3304104
cfb69d2
00e2605
3265240
7660e2c
421c167
8346af7
4ed799c
6733e19
c1d55b0
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 |
|---|---|---|
|
|
@@ -3,25 +3,29 @@ | |
| :class:`xarray.DataArray`. | ||
| """ | ||
|
|
||
| import contextlib | ||
| from collections.abc import Sequence | ||
| from typing import Literal | ||
|
|
||
| from packaging.version import Version | ||
|
|
||
| try: | ||
| import contextily | ||
| from rasterio.crs import CRS | ||
| from xyzservices import TileProvider | ||
|
|
||
| _HAS_CONTEXTILY = True | ||
| except ImportError: | ||
| CRS = None | ||
|
Comment on lines
+13
to
+18
Member
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 put the
Member
Author
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.
Member
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. Rasterio is also a dependency of |
||
| TileProvider = None | ||
| _HAS_CONTEXTILY = False | ||
|
|
||
| with contextlib.suppress(ImportError): | ||
| # rioxarray is needed to register the rio accessor | ||
| try: | ||
| import rioxarray # noqa: F401 | ||
|
|
||
| _HAS_RIOXARRAY = True | ||
| except ImportError: | ||
| _HAS_RIOXARRAY = False | ||
|
|
||
| import numpy as np | ||
| import xarray as xr | ||
|
|
||
|
|
@@ -33,6 +37,7 @@ def load_tile_map( | |
| zoom: int | Literal["auto"] = "auto", | ||
| source: TileProvider | str | None = None, | ||
| lonlat: bool = True, | ||
| crs: str | CRS = "EPSG:3857", | ||
| wait: int = 0, | ||
| max_retries: int = 2, | ||
| zoom_adjust: int | None = None, | ||
|
|
@@ -42,7 +47,8 @@ def load_tile_map( | |
|
|
||
| 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. | ||
| in a Spherical Mercator (EPSG:3857) coordinate reference system (CRS) by default, | ||
| but can be customized using the ``crs`` parameter. | ||
|
|
||
| Parameters | ||
| ---------- | ||
|
|
@@ -80,6 +86,10 @@ def load_tile_map( | |
| lonlat | ||
| If ``False``, coordinates in ``region`` are assumed to be Spherical Mercator as | ||
| opposed to longitude/latitude. | ||
| crs | ||
| Coordinate reference system (CRS) of the returned :class:`xarray.DataArray` | ||
| image. Default is ``"EPSG:3857"`` (i.e., Spherical Mercator). The CRS can be in | ||
| either string or :class:`rasterio.crs.CRS` format. | ||
| wait | ||
| If the tile API is rate-limited, the number of seconds to wait between a failed | ||
| request and the next try. | ||
|
|
@@ -128,6 +138,8 @@ def load_tile_map( | |
| ... raster.rio.crs.to_string() | ||
| 'EPSG:3857' | ||
| """ | ||
| _default_crs = "EPSG:3857" # The default CRS returned from contextily | ||
|
Member
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. At https://contextily.readthedocs.io/en/latest/reference.html#contextily.bounds2img, there is this sentence:
While tilemaps are usually served in EPSG:3857, I know that there are some tilemaps that can be served in a different projection system by default. I'm hesitant to use this default of EPSG:3857 here, unless we can be confident that contextily only supports EPSG:3857 tiles.
Member
Author
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 contexily source codes, I think contexitly always assumes that CRS is EPSG:3857, at least when writing the image to raster files
Member
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. That is for the Non-Web Mercator (EPSG:3857) XYZ tiles are not common, but they do exist, e.g. Openlayers allows configuring the projection system of the returned tiles:
Member
Author
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 meant this line: In bounds2raster, it first calls Looking at the xyzservices (https://github.com/geopandas/xyzservices/blob/c847f312d2e67a0a45ceae119fdcdf7cf60858b6/provider_sources/xyzservices-providers.json#L50), I can also see some providers that has the
Member
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 tried using import contextily
import xyzservices
import matplotlib.pyplot as plt
source = xyzservices.providers.MapTiler.Basic4326(
key="BwTZdryxoypHc4BnCZ4" # add an 'o' to the end
)
img, bounds = contextily.bounds2img(w=0, s=-80, e=150, n=80, ll=True, source=source)
# img.shape # (4096, 2048, 4)
plt.imshow(img[:, :, :3])produces The extent doesn't seem to be correct (it should be plotting from Greenwich to Asia), possibly a bug in
Member
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. Found geopandas/contextily#119, and had a closer look at the
Member
Author
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.
When
Member
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. How about checking
Member
Author
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. Done in 6733e19.
Please note that this line of code works for Edit: renamed Please note that, currently, the returned xarray.DataArray is still "EPSG:3857" even if the source CRS is not (i.e. we don't the reprojection). This may not be ideal. |
||
|
|
||
| if not _HAS_CONTEXTILY: | ||
| msg = ( | ||
| "Package `contextily` is required to be installed to use this function. " | ||
|
|
@@ -136,28 +148,33 @@ def load_tile_map( | |
| ) | ||
| raise ImportError(msg) | ||
|
|
||
| contextily_kwargs = {} | ||
| if zoom_adjust is not None: | ||
| contextily_kwargs["zoom_adjust"] = zoom_adjust | ||
| if Version(contextily.__version__) < Version("1.5.0"): | ||
| msg = ( | ||
| "The `zoom_adjust` parameter requires `contextily>=1.5.0` to work. " | ||
| "Please upgrade contextily, or manually set the `zoom` level instead." | ||
| ) | ||
| raise TypeError(msg) | ||
| if crs != _default_crs and not _HAS_RIOXARRAY: | ||
| msg = ( | ||
| f"Package `rioxarray` is required if CRS is not '{_default_crs}'. " | ||
| "Please use `python -m pip install rioxarray` or " | ||
| "`mamba install -c conda-forge rioxarray` to install the package." | ||
| ) | ||
| raise ImportError(msg) | ||
|
|
||
| if zoom_adjust is not None and Version(contextily.__version__) < Version("1.5.0"): | ||
| msg = ( | ||
| "The `zoom_adjust` parameter requires `contextily>=1.5.0` to work. " | ||
| "Please upgrade contextily, or manually set the `zoom` level instead." | ||
| ) | ||
| raise ValueError(msg) | ||
|
|
||
| # Keyword arguments for contextily.bounds2img | ||
| contextily_kwargs = { | ||
| "zoom": zoom, | ||
| "source": source, | ||
| "ll": lonlat, | ||
| "wait": wait, | ||
| "max_retries": max_retries, | ||
| "zoom_adjust": zoom_adjust, | ||
|
weiji14 marked this conversation as resolved.
Outdated
|
||
| } | ||
| 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, | ||
| **contextily_kwargs, | ||
| w=west, s=south, e=east, n=north, **contextily_kwargs | ||
| ) | ||
|
|
||
| # Turn RGBA img from channel-last to channel-first and get 3-band RGB only | ||
|
|
@@ -176,8 +193,12 @@ def load_tile_map( | |
| dims=("band", "y", "x"), | ||
| ) | ||
|
|
||
| # If rioxarray is installed, set the coordinate reference system | ||
| # If rioxarray is installed, set the coordinate reference system. | ||
|
seisman marked this conversation as resolved.
|
||
| if hasattr(dataarray, "rio"): | ||
| dataarray = dataarray.rio.write_crs(input_crs="EPSG:3857") | ||
| dataarray = dataarray.rio.write_crs(input_crs=_default_crs) | ||
|
|
||
| # Reproject raster image from EPSG:3857 to specified CRS, using rioxarray. | ||
| if crs != _default_crs: | ||
| dataarray = dataarray.rio.reproject(dst_crs=crs) | ||
|
seisman marked this conversation as resolved.
Outdated
|
||
|
|
||
| return dataarray | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,13 +9,9 @@ | |
| from pygmt.helpers import build_arg_list, fmt_docstring, kwargs_to_strings, use_alias | ||
|
|
||
| try: | ||
| import rioxarray # noqa: F401 | ||
| from xyzservices import TileProvider | ||
|
|
||
| _HAS_RIOXARRAY = True | ||
| except ImportError: | ||
| TileProvider = None | ||
| _HAS_RIOXARRAY = False | ||
|
|
||
|
|
||
| @fmt_docstring | ||
|
|
@@ -111,39 +107,21 @@ def tilemap( | |
|
|
||
| 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 | ||
| ``python -m pip install rioxarray``) before using this function. | ||
| """ | ||
| kwargs = self._preprocess(**kwargs) | ||
|
|
||
| if not _HAS_RIOXARRAY: | ||
|
Member
Author
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. The |
||
| msg = ( | ||
| "Package `rioxarray` is required to be installed to use this function. " | ||
| "Please use `python -m pip install rioxarray` or " | ||
| "`mamba install -c conda-forge rioxarray` to install the package." | ||
| ) | ||
| raise ImportError(msg) | ||
|
|
||
| raster = load_tile_map( | ||
| region=region, | ||
| zoom=zoom, | ||
| source=source, | ||
| lonlat=lonlat, | ||
| crs="OGC:CRS84" if lonlat is True else "EPSG:3857", | ||
|
Member
Author
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. If |
||
| wait=wait, | ||
| max_retries=max_retries, | ||
| zoom_adjust=zoom_adjust, | ||
| ) | ||
|
|
||
| # 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 | ||
| if lonlat: | ||
| 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 | ||
|
|
||

Uh oh!
There was an error while loading. Please reload this page.