3
3
:class:`xarray.DataArray`.
4
4
"""
5
5
6
- import contextlib
7
6
from collections .abc import Sequence
8
7
from typing import Literal
9
8
10
9
from packaging .version import Version
11
10
12
11
try :
13
12
import contextily
13
+ from rasterio .crs import CRS
14
14
from xyzservices import TileProvider
15
15
16
16
_HAS_CONTEXTILY = True
17
17
except ImportError :
18
+ CRS = None
18
19
TileProvider = None
19
20
_HAS_CONTEXTILY = False
20
21
21
- with contextlib .suppress (ImportError ):
22
- # rioxarray is needed to register the rio accessor
22
+ try :
23
23
import rioxarray # noqa: F401
24
24
25
+ _HAS_RIOXARRAY = True
26
+ except ImportError :
27
+ _HAS_RIOXARRAY = False
28
+
25
29
import numpy as np
26
30
import xarray as xr
27
31
@@ -33,6 +37,7 @@ def load_tile_map(
33
37
zoom : int | Literal ["auto" ] = "auto" ,
34
38
source : TileProvider | str | None = None ,
35
39
lonlat : bool = True ,
40
+ crs : str | CRS = "EPSG:3857" ,
36
41
wait : int = 0 ,
37
42
max_retries : int = 2 ,
38
43
zoom_adjust : int | None = None ,
@@ -42,7 +47,8 @@ def load_tile_map(
42
47
43
48
The tiles that compose the map are merged and georeferenced into an
44
49
:class:`xarray.DataArray` image with 3 bands (RGB). Note that the returned image is
45
- in a Spherical Mercator (EPSG:3857) coordinate reference system.
50
+ in a Spherical Mercator (EPSG:3857) coordinate reference system (CRS) by default,
51
+ but can be customized using the ``crs`` parameter.
46
52
47
53
Parameters
48
54
----------
@@ -80,6 +86,10 @@ def load_tile_map(
80
86
lonlat
81
87
If ``False``, coordinates in ``region`` are assumed to be Spherical Mercator as
82
88
opposed to longitude/latitude.
89
+ crs
90
+ Coordinate reference system (CRS) of the returned :class:`xarray.DataArray`
91
+ image. Default is ``"EPSG:3857"`` (i.e., Spherical Mercator). The CRS can be in
92
+ either string or :class:`rasterio.crs.CRS` format.
83
93
wait
84
94
If the tile API is rate-limited, the number of seconds to wait between a failed
85
95
request and the next try.
@@ -128,6 +138,10 @@ def load_tile_map(
128
138
... raster.rio.crs.to_string()
129
139
'EPSG:3857'
130
140
"""
141
+ # The CRS of the source tile provider. If the source is a TileProvider object, use
142
+ # its crs attribute if available. Otherwise, default to EPSG:3857.
143
+ _source_crs = getattr (source , "crs" , "EPSG:3857" )
144
+
131
145
if not _HAS_CONTEXTILY :
132
146
msg = (
133
147
"Package `contextily` is required to be installed to use this function. "
@@ -136,28 +150,34 @@ def load_tile_map(
136
150
)
137
151
raise ImportError (msg )
138
152
139
- contextily_kwargs = {}
153
+ if crs != _source_crs and not _HAS_RIOXARRAY :
154
+ msg = (
155
+ f"Package `rioxarray` is required if CRS is not '{ _source_crs } '. "
156
+ "Please use `python -m pip install rioxarray` or "
157
+ "`mamba install -c conda-forge rioxarray` to install the package."
158
+ )
159
+ raise ImportError (msg )
160
+
161
+ # Keyword arguments for contextily.bounds2img
162
+ contextily_kwargs = {
163
+ "zoom" : zoom ,
164
+ "source" : source ,
165
+ "ll" : lonlat ,
166
+ "wait" : wait ,
167
+ "max_retries" : max_retries ,
168
+ }
140
169
if zoom_adjust is not None :
141
- contextily_kwargs ["zoom_adjust" ] = zoom_adjust
142
170
if Version (contextily .__version__ ) < Version ("1.5.0" ):
143
171
msg = (
144
172
"The `zoom_adjust` parameter requires `contextily>=1.5.0` to work. "
145
173
"Please upgrade contextily, or manually set the `zoom` level instead."
146
174
)
147
- raise TypeError (msg )
175
+ raise ValueError (msg )
176
+ contextily_kwargs ["zoom_adjust" ] = zoom_adjust
148
177
149
178
west , east , south , north = region
150
179
image , extent = contextily .bounds2img (
151
- w = west ,
152
- s = south ,
153
- e = east ,
154
- n = north ,
155
- zoom = zoom ,
156
- source = source ,
157
- ll = lonlat ,
158
- wait = wait ,
159
- max_retries = max_retries ,
160
- ** contextily_kwargs ,
180
+ w = west , s = south , e = east , n = north , ** contextily_kwargs
161
181
)
162
182
163
183
# Turn RGBA img from channel-last to channel-first and get 3-band RGB only
@@ -176,8 +196,12 @@ def load_tile_map(
176
196
dims = ("band" , "y" , "x" ),
177
197
)
178
198
179
- # If rioxarray is installed, set the coordinate reference system
199
+ # If rioxarray is installed, set the coordinate reference system.
180
200
if hasattr (dataarray , "rio" ):
181
- dataarray = dataarray .rio .write_crs (input_crs = "EPSG:3857" )
201
+ dataarray = dataarray .rio .write_crs (input_crs = _source_crs )
202
+
203
+ # Reproject raster image from the source CRS to the specified CRS.
204
+ if crs != _source_crs :
205
+ dataarray = dataarray .rio .reproject (dst_crs = crs )
182
206
183
207
return dataarray
0 commit comments