rio-tiler 7.9.1__py3-none-any.whl → 8.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rio_tiler/__init__.py +1 -1
- rio_tiler/colormap.py +7 -4
- rio_tiler/errors.py +8 -0
- rio_tiler/experimental/zarr.py +366 -0
- rio_tiler/expression.py +6 -6
- rio_tiler/io/base.py +20 -0
- rio_tiler/io/rasterio.py +1 -8
- rio_tiler/io/xarray.py +74 -61
- rio_tiler/models.py +223 -66
- rio_tiler/mosaic/backend.py +242 -0
- rio_tiler/mosaic/reader.py +6 -0
- rio_tiler/reader.py +31 -32
- rio_tiler/tasks.py +16 -0
- rio_tiler/utils.py +29 -1
- {rio_tiler-7.9.1.dist-info → rio_tiler-8.0.0.dist-info}/METADATA +12 -36
- {rio_tiler-7.9.1.dist-info → rio_tiler-8.0.0.dist-info}/RECORD +19 -17
- {rio_tiler-7.9.1.dist-info → rio_tiler-8.0.0.dist-info}/WHEEL +0 -0
- {rio_tiler-7.9.1.dist-info → rio_tiler-8.0.0.dist-info}/licenses/AUTHORS.txt +0 -0
- {rio_tiler-7.9.1.dist-info → rio_tiler-8.0.0.dist-info}/licenses/LICENSE +0 -0
rio_tiler/io/xarray.py
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import math
|
|
6
|
+
import os
|
|
6
7
|
import warnings
|
|
7
|
-
from typing import Any, Dict, List,
|
|
8
|
+
from typing import Any, Dict, List, Tuple
|
|
8
9
|
|
|
9
10
|
import attr
|
|
10
11
|
import numpy
|
|
@@ -23,6 +24,7 @@ from rasterio.warp import transform_bounds, transform_geom
|
|
|
23
24
|
from rio_tiler.constants import WEB_MERCATOR_CRS, WEB_MERCATOR_TMS, WGS84_CRS
|
|
24
25
|
from rio_tiler.errors import (
|
|
25
26
|
InvalidGeographicBounds,
|
|
27
|
+
MaxArraySizeError,
|
|
26
28
|
MissingCRS,
|
|
27
29
|
PointOutsideBounds,
|
|
28
30
|
TileOutsideBounds,
|
|
@@ -50,6 +52,9 @@ except ImportError: # pragma: nocover
|
|
|
50
52
|
rioxarray = None # type: ignore
|
|
51
53
|
|
|
52
54
|
|
|
55
|
+
MAX_ARRAY_SIZE = os.environ.get("RIO_TILER_MAX_ARRAY_SIZE", 1_000_000_000) # 1Gb
|
|
56
|
+
|
|
57
|
+
|
|
53
58
|
@attr.s
|
|
54
59
|
class XarrayReader(BaseReader):
|
|
55
60
|
"""Xarray Reader.
|
|
@@ -133,7 +138,7 @@ class XarrayReader(BaseReader):
|
|
|
133
138
|
return self._maxzoom
|
|
134
139
|
|
|
135
140
|
@property
|
|
136
|
-
def
|
|
141
|
+
def band_descriptions(self) -> List[str]:
|
|
137
142
|
"""
|
|
138
143
|
Return list of `band descriptions` in DataArray.
|
|
139
144
|
|
|
@@ -155,7 +160,7 @@ class XarrayReader(BaseReader):
|
|
|
155
160
|
if coords_name:
|
|
156
161
|
return [str(self.input.coords[coords_name[0]].data)]
|
|
157
162
|
|
|
158
|
-
return [self.input.name or "
|
|
163
|
+
return [self.input.name or ""]
|
|
159
164
|
|
|
160
165
|
return [str(band) for d in self._dims for band in self.input[d].values]
|
|
161
166
|
|
|
@@ -168,7 +173,7 @@ class XarrayReader(BaseReader):
|
|
|
168
173
|
"crs": CRS_to_uri(self.crs) or self.crs.to_wkt(),
|
|
169
174
|
"band_metadata": [(f"b{ix}", v) for ix, v in enumerate(metadata, 1)],
|
|
170
175
|
"band_descriptions": [
|
|
171
|
-
(f"b{ix}", v) for ix, v in enumerate(self.
|
|
176
|
+
(f"b{ix}", v) for ix, v in enumerate(self.band_descriptions, 1)
|
|
172
177
|
],
|
|
173
178
|
"dtype": str(self.input.dtype),
|
|
174
179
|
"nodata_type": "Nodata" if self.input.rio.nodata is not None else "None",
|
|
@@ -186,11 +191,14 @@ class XarrayReader(BaseReader):
|
|
|
186
191
|
return Info(**meta)
|
|
187
192
|
|
|
188
193
|
def _sel_indexes(
|
|
189
|
-
self,
|
|
190
|
-
|
|
194
|
+
self,
|
|
195
|
+
indexes: Indexes | None = None,
|
|
196
|
+
) -> Tuple[xarray.DataArray, List[str], List[str]]:
|
|
191
197
|
"""Select `band` indexes in DataArray."""
|
|
192
198
|
da = self.input
|
|
193
|
-
|
|
199
|
+
band_descriptions = self.band_descriptions
|
|
200
|
+
band_names = [f"b{ix + 1}" for ix in range(self.input.rio.count)]
|
|
201
|
+
|
|
194
202
|
if indexes := cast_to_sequence(indexes):
|
|
195
203
|
assert all(v > 0 for v in indexes), "Indexes value must be >= 1"
|
|
196
204
|
if da.ndim == 2:
|
|
@@ -199,28 +207,30 @@ class XarrayReader(BaseReader):
|
|
|
199
207
|
f"Invalid indexes {indexes} for array of shape {da.shape}"
|
|
200
208
|
)
|
|
201
209
|
|
|
202
|
-
return da, band_names
|
|
210
|
+
return da, band_names, band_descriptions
|
|
203
211
|
|
|
204
212
|
indexes = [idx - 1 for idx in indexes]
|
|
213
|
+
|
|
205
214
|
da = da[indexes]
|
|
206
|
-
|
|
215
|
+
band_descriptions = [band_descriptions[idx] for idx in indexes]
|
|
216
|
+
band_names = [band_names[idx] for idx in indexes]
|
|
207
217
|
|
|
208
|
-
return da, band_names
|
|
218
|
+
return da, band_names, band_descriptions
|
|
209
219
|
|
|
210
220
|
def statistics(
|
|
211
221
|
self,
|
|
212
222
|
categorical: bool = False,
|
|
213
|
-
categories:
|
|
214
|
-
percentiles:
|
|
215
|
-
hist_options:
|
|
216
|
-
nodata:
|
|
217
|
-
indexes:
|
|
223
|
+
categories: List[float] | None = None,
|
|
224
|
+
percentiles: List[int] | None = None,
|
|
225
|
+
hist_options: Dict | None = None,
|
|
226
|
+
nodata: NoData | None = None,
|
|
227
|
+
indexes: Indexes | None = None,
|
|
218
228
|
**kwargs: Any,
|
|
219
229
|
) -> Dict[str, BandStatistics]:
|
|
220
230
|
"""Return statistics from a dataset."""
|
|
221
231
|
hist_options = hist_options or {}
|
|
222
232
|
|
|
223
|
-
da, band_names = self._sel_indexes(indexes)
|
|
233
|
+
da, band_names, _ = self._sel_indexes(indexes)
|
|
224
234
|
|
|
225
235
|
if nodata is not None:
|
|
226
236
|
da = da.rio.write_nodata(nodata)
|
|
@@ -246,8 +256,8 @@ class XarrayReader(BaseReader):
|
|
|
246
256
|
tilesize: int = 256,
|
|
247
257
|
reproject_method: WarpResampling = "nearest",
|
|
248
258
|
auto_expand: bool = True,
|
|
249
|
-
nodata:
|
|
250
|
-
indexes:
|
|
259
|
+
nodata: NoData | None = None,
|
|
260
|
+
indexes: Indexes | None = None,
|
|
251
261
|
out_dtype: str | numpy.dtype | None = None,
|
|
252
262
|
**kwargs: Any,
|
|
253
263
|
) -> ImageData:
|
|
@@ -291,15 +301,15 @@ class XarrayReader(BaseReader):
|
|
|
291
301
|
def part( # noqa: C901
|
|
292
302
|
self,
|
|
293
303
|
bbox: BBox,
|
|
294
|
-
dst_crs:
|
|
304
|
+
dst_crs: CRS | None = None,
|
|
295
305
|
bounds_crs: CRS = WGS84_CRS,
|
|
296
306
|
reproject_method: WarpResampling = "nearest",
|
|
297
307
|
auto_expand: bool = True,
|
|
298
|
-
nodata:
|
|
299
|
-
indexes:
|
|
300
|
-
max_size:
|
|
301
|
-
height:
|
|
302
|
-
width:
|
|
308
|
+
nodata: NoData | None = None,
|
|
309
|
+
indexes: Indexes | None = None,
|
|
310
|
+
max_size: int | None = None,
|
|
311
|
+
height: int | None = None,
|
|
312
|
+
width: int | None = None,
|
|
303
313
|
resampling_method: RIOResampling = "nearest",
|
|
304
314
|
out_dtype: str | numpy.dtype | None = None,
|
|
305
315
|
**kwargs: Any,
|
|
@@ -331,7 +341,7 @@ class XarrayReader(BaseReader):
|
|
|
331
341
|
|
|
332
342
|
dst_crs = dst_crs or bounds_crs
|
|
333
343
|
|
|
334
|
-
da, band_names = self._sel_indexes(indexes)
|
|
344
|
+
da, band_names, band_descriptions = self._sel_indexes(indexes)
|
|
335
345
|
|
|
336
346
|
if nodata is not None:
|
|
337
347
|
da = da.rio.write_nodata(nodata)
|
|
@@ -348,6 +358,11 @@ class XarrayReader(BaseReader):
|
|
|
348
358
|
auto_expand=auto_expand,
|
|
349
359
|
)
|
|
350
360
|
|
|
361
|
+
if da.nbytes > MAX_ARRAY_SIZE:
|
|
362
|
+
raise MaxArraySizeError(
|
|
363
|
+
f"Maximum array limit {MAX_ARRAY_SIZE} reached, trying to put DataArray of {da.shape} in memory."
|
|
364
|
+
)
|
|
365
|
+
|
|
351
366
|
src_width = da.rio.width
|
|
352
367
|
src_height = da.rio.height
|
|
353
368
|
src_bounds = list(da.rio.bounds())
|
|
@@ -373,17 +388,18 @@ class XarrayReader(BaseReader):
|
|
|
373
388
|
)
|
|
374
389
|
src_bounds[1] = max(src_bounds[1], -85.06)
|
|
375
390
|
src_bounds[3] = min(src_bounds[3], 85.06)
|
|
376
|
-
w = windows.from_bounds(*src_bounds, transform=src_transform)
|
|
377
|
-
src_height = round(w.height)
|
|
378
|
-
src_width = round(w.width)
|
|
379
391
|
|
|
380
|
-
# South
|
|
392
|
+
# North->South
|
|
381
393
|
if src_transform.e > 0:
|
|
382
394
|
src_bounds = [src_bounds[0], src_bounds[3], src_bounds[2], src_bounds[1]]
|
|
383
395
|
# West->East
|
|
384
396
|
if src_transform.a < 0:
|
|
385
397
|
src_bounds = [src_bounds[2], src_bounds[1], src_bounds[1], src_bounds[3]]
|
|
386
398
|
|
|
399
|
+
w = windows.from_bounds(*src_bounds, transform=src_transform)
|
|
400
|
+
src_height = round(w.height)
|
|
401
|
+
src_width = round(w.width)
|
|
402
|
+
|
|
387
403
|
if dst_crs != self.crs:
|
|
388
404
|
# transform of the reprojected dataset
|
|
389
405
|
dst_transform, _, _ = calculate_default_transform(
|
|
@@ -421,45 +437,33 @@ class XarrayReader(BaseReader):
|
|
|
421
437
|
)
|
|
422
438
|
|
|
423
439
|
arr = da.to_masked_array()
|
|
440
|
+
arr.mask |= arr.data == da.rio.nodata
|
|
424
441
|
if out_dtype:
|
|
425
442
|
arr = arr.astype(out_dtype)
|
|
426
|
-
arr.mask |= arr.data == da.rio.nodata
|
|
427
443
|
|
|
428
444
|
output_bounds = da.rio._unordered_bounds()
|
|
429
445
|
if output_bounds[1] > output_bounds[3] and da.rio.transform().e > 0:
|
|
430
446
|
yaxis = self.input.dims.index(self.input.rio.y_dim)
|
|
431
447
|
arr = numpy.flip(arr, axis=yaxis)
|
|
432
448
|
|
|
433
|
-
|
|
449
|
+
return ImageData(
|
|
434
450
|
arr,
|
|
435
451
|
bounds=bbox,
|
|
436
452
|
crs=da.rio.crs,
|
|
437
453
|
dataset_statistics=stats,
|
|
438
454
|
band_names=band_names,
|
|
455
|
+
band_descriptions=band_descriptions,
|
|
456
|
+
nodata=da.rio.nodata,
|
|
439
457
|
)
|
|
440
458
|
|
|
441
|
-
|
|
442
|
-
output_width = width or img.width
|
|
443
|
-
if max_size and not (width and height):
|
|
444
|
-
output_height, output_width = _get_width_height(
|
|
445
|
-
max_size, img.height, img.width
|
|
446
|
-
)
|
|
447
|
-
|
|
448
|
-
if output_height != img.height or output_width != img.width:
|
|
449
|
-
img = img.resize(
|
|
450
|
-
output_height, output_width, resampling_method=resampling_method
|
|
451
|
-
)
|
|
452
|
-
|
|
453
|
-
return img
|
|
454
|
-
|
|
455
|
-
def preview(
|
|
459
|
+
def preview( # noqa: C901
|
|
456
460
|
self,
|
|
457
461
|
max_size: int = 1024,
|
|
458
|
-
height:
|
|
459
|
-
width:
|
|
460
|
-
nodata:
|
|
461
|
-
indexes:
|
|
462
|
-
dst_crs:
|
|
462
|
+
height: int | None = None,
|
|
463
|
+
width: int | None = None,
|
|
464
|
+
nodata: NoData | None = None,
|
|
465
|
+
indexes: Indexes | None = None,
|
|
466
|
+
dst_crs: CRS | None = None,
|
|
463
467
|
reproject_method: WarpResampling = "nearest",
|
|
464
468
|
resampling_method: RIOResampling = "nearest",
|
|
465
469
|
out_dtype: str | numpy.dtype | None = None,
|
|
@@ -487,7 +491,12 @@ class XarrayReader(BaseReader):
|
|
|
487
491
|
)
|
|
488
492
|
max_size = None
|
|
489
493
|
|
|
490
|
-
da, band_names = self._sel_indexes(indexes)
|
|
494
|
+
da, band_names, band_descriptions = self._sel_indexes(indexes)
|
|
495
|
+
|
|
496
|
+
if da.nbytes > MAX_ARRAY_SIZE:
|
|
497
|
+
raise MaxArraySizeError(
|
|
498
|
+
f"Maximum array limit {MAX_ARRAY_SIZE} reached, trying to put DataArray of {da.shape} in memory."
|
|
499
|
+
)
|
|
491
500
|
|
|
492
501
|
if nodata is not None:
|
|
493
502
|
da = da.rio.write_nodata(nodata)
|
|
@@ -573,6 +582,8 @@ class XarrayReader(BaseReader):
|
|
|
573
582
|
crs=da.rio.crs,
|
|
574
583
|
dataset_statistics=stats,
|
|
575
584
|
band_names=band_names,
|
|
585
|
+
band_descriptions=band_descriptions,
|
|
586
|
+
nodata=da.rio.nodata,
|
|
576
587
|
)
|
|
577
588
|
|
|
578
589
|
if max_size:
|
|
@@ -595,8 +606,8 @@ class XarrayReader(BaseReader):
|
|
|
595
606
|
lon: float,
|
|
596
607
|
lat: float,
|
|
597
608
|
coord_crs: CRS = WGS84_CRS,
|
|
598
|
-
nodata:
|
|
599
|
-
indexes:
|
|
609
|
+
nodata: NoData | None = None,
|
|
610
|
+
indexes: Indexes | None = None,
|
|
600
611
|
out_dtype: str | numpy.dtype | None = None,
|
|
601
612
|
**kwargs: Any,
|
|
602
613
|
) -> PointData:
|
|
@@ -620,7 +631,7 @@ class XarrayReader(BaseReader):
|
|
|
620
631
|
):
|
|
621
632
|
raise PointOutsideBounds("Point is outside dataset bounds")
|
|
622
633
|
|
|
623
|
-
da, band_names = self._sel_indexes(indexes)
|
|
634
|
+
da, band_names, band_descriptions = self._sel_indexes(indexes)
|
|
624
635
|
|
|
625
636
|
if nodata is not None:
|
|
626
637
|
da = da.rio.write_nodata(nodata)
|
|
@@ -641,21 +652,23 @@ class XarrayReader(BaseReader):
|
|
|
641
652
|
coordinates=(lon, lat),
|
|
642
653
|
crs=coord_crs,
|
|
643
654
|
band_names=band_names,
|
|
655
|
+
band_descriptions=band_descriptions,
|
|
644
656
|
pixel_location=(x, y),
|
|
657
|
+
nodata=da.rio.nodata,
|
|
645
658
|
)
|
|
646
659
|
|
|
647
660
|
def feature(
|
|
648
661
|
self,
|
|
649
662
|
shape: Dict,
|
|
650
|
-
dst_crs:
|
|
663
|
+
dst_crs: CRS | None = None,
|
|
651
664
|
shape_crs: CRS = WGS84_CRS,
|
|
652
665
|
reproject_method: WarpResampling = "nearest",
|
|
653
666
|
auto_expand: bool = True,
|
|
654
|
-
nodata:
|
|
655
|
-
indexes:
|
|
656
|
-
max_size:
|
|
657
|
-
height:
|
|
658
|
-
width:
|
|
667
|
+
nodata: NoData | None = None,
|
|
668
|
+
indexes: Indexes | None = None,
|
|
669
|
+
max_size: int | None = None,
|
|
670
|
+
height: int | None = None,
|
|
671
|
+
width: int | None = None,
|
|
659
672
|
resampling_method: RIOResampling = "nearest",
|
|
660
673
|
out_dtype: str | numpy.dtype | None = None,
|
|
661
674
|
**kwargs: Any,
|