9
9
import pandas as pd
10
10
import rasterio as rio
11
11
from PIL import Image
12
- from cartopy .io .img_tiles import _merge_tiles as merge_tiles
13
12
from rasterio .transform import from_origin
14
13
from . import tile_providers as sources
15
14
@@ -146,6 +145,7 @@ def bounds2img(w, s, e, n, zoom='auto',
146
145
if zoom == 'auto' :
147
146
zoom = _calculate_zoom (w , e , s , n )
148
147
tiles = []
148
+ arrays = []
149
149
for t in mt .tiles (w , s , e , n , [zoom ]):
150
150
x , y , z = t .x , t .y , t .z
151
151
tile_url = url .replace ('tileX' , str (x )).replace ('tileY' , str (y )).replace ('tileZ' , str (z ))
@@ -155,17 +155,15 @@ def bounds2img(w, s, e, n, zoom='auto',
155
155
image = Image .open (image_stream ).convert ('RGB' )
156
156
image = np .asarray (image )
157
157
# ---
158
- wt , st , et , nt = mt .bounds (t )
159
- xr = np .linspace (wt , et , image .shape [0 ])
160
- yr = np .linspace (st , nt , image .shape [1 ])
161
- tiles .append ([image , xr , yr , 'lower' ])
162
- merged , extent = merge_tiles (tiles )[:2 ]
158
+ tiles .append (t )
159
+ arrays .append (image )
160
+ merged , extent = _merge_tiles (tiles , arrays )
163
161
# lon/lat extent --> Spheric Mercator
164
- minX , maxX , minY , maxY = extent
165
- w , s = mt .xy (minX , minY )
166
- e , n = mt .xy (maxX , maxY )
167
- extent = w , e , s , n
168
- return merged [:: - 1 ] , extent
162
+ west , south , east , north = extent
163
+ left , bottom = mt .xy (west , south )
164
+ right , top = mt .xy (east , north )
165
+ extent = left , right , bottom , top
166
+ return merged , extent
169
167
170
168
171
169
def _retryer (tile_url , wait , max_retries ):
@@ -332,3 +330,50 @@ def _calculate_zoom(w, s, e, n):
332
330
zoom_lat = np .ceil (np .log2 (360 * 2. / lat_length ))
333
331
zoom = np .max ([zoom_lon , zoom_lat ])
334
332
return int (zoom )
333
+
334
+
335
+ def _merge_tiles (tiles , arrays ):
336
+ """
337
+ Merge a set of tiles into a single array.
338
+
339
+ Parameters
340
+ ---------
341
+ tiles : list of mercantile.Tile objects
342
+ The tiles to merge.
343
+ arrays : list of numpy arrays
344
+ The corresponding arrays (image pixels) of the tiles. This list
345
+ has the same length and order as the `tiles` argument.
346
+
347
+ Returns
348
+ -------
349
+ img : np.ndarray
350
+ Merged arrays.
351
+ extent : tuple
352
+ Bounding box [west, south, east, north] of the returned image
353
+ in long/lat.
354
+ """
355
+ # create (n_tiles x 2) array with column for x and y coordinates
356
+ tile_xys = np .array ([(t .x , t .y ) for t in tiles ])
357
+
358
+ # get indices starting at zero
359
+ indices = tile_xys - tile_xys .min (axis = 0 )
360
+
361
+ # the shape of individual tile images
362
+ h , w , d = arrays [0 ].shape
363
+
364
+ # number of rows and columns in the merged tile
365
+ n_x , n_y = (indices + 1 ).max (axis = 0 )
366
+
367
+ # empty merged tiles array to be filled in
368
+ img = np .zeros ((h * n_y , w * n_x , d ), dtype = np .uint8 )
369
+
370
+ for ind , arr in zip (indices , arrays ):
371
+ x , y = ind
372
+ img [y * h :(y + 1 )* h , x * w :(x + 1 )* w , :] = arr
373
+
374
+ bounds = np .array ([mt .bounds (t ) for t in tiles ])
375
+ west , south , east , north = (
376
+ min (bounds [:, 0 ]), min (bounds [:, 1 ]),
377
+ max (bounds [:, 2 ]), max (bounds [:, 3 ]))
378
+
379
+ return img , (west , south , east , north )
0 commit comments