rio-tiler 7.9.2__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/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, Optional, Tuple
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 band_names(self) -> List[str]:
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 "array"]
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.band_names, 1)
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, indexes: Optional[Indexes] = None
190
- ) -> Tuple[xarray.DataArray, List[str]]:
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
- band_names = self.band_names
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
- band_names = [self.band_names[idx] for idx in indexes]
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: Optional[List[float]] = None,
214
- percentiles: Optional[List[int]] = None,
215
- hist_options: Optional[Dict] = None,
216
- nodata: Optional[NoData] = None,
217
- indexes: Optional[Indexes] = None,
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: Optional[NoData] = None,
250
- indexes: Optional[Indexes] = None,
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: Optional[CRS] = None,
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: Optional[NoData] = None,
299
- indexes: Optional[Indexes] = None,
300
- max_size: Optional[int] = None,
301
- height: Optional[int] = None,
302
- width: Optional[int] = None,
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())
@@ -374,7 +389,7 @@ class XarrayReader(BaseReader):
374
389
  src_bounds[1] = max(src_bounds[1], -85.06)
375
390
  src_bounds[3] = min(src_bounds[3], 85.06)
376
391
 
377
- # South->North
392
+ # North->South
378
393
  if src_transform.e > 0:
379
394
  src_bounds = [src_bounds[0], src_bounds[3], src_bounds[2], src_bounds[1]]
380
395
  # West->East
@@ -422,45 +437,33 @@ class XarrayReader(BaseReader):
422
437
  )
423
438
 
424
439
  arr = da.to_masked_array()
440
+ arr.mask |= arr.data == da.rio.nodata
425
441
  if out_dtype:
426
442
  arr = arr.astype(out_dtype)
427
- arr.mask |= arr.data == da.rio.nodata
428
443
 
429
444
  output_bounds = da.rio._unordered_bounds()
430
445
  if output_bounds[1] > output_bounds[3] and da.rio.transform().e > 0:
431
446
  yaxis = self.input.dims.index(self.input.rio.y_dim)
432
447
  arr = numpy.flip(arr, axis=yaxis)
433
448
 
434
- img = ImageData(
449
+ return ImageData(
435
450
  arr,
436
451
  bounds=bbox,
437
452
  crs=da.rio.crs,
438
453
  dataset_statistics=stats,
439
454
  band_names=band_names,
455
+ band_descriptions=band_descriptions,
456
+ nodata=da.rio.nodata,
440
457
  )
441
458
 
442
- output_height = height or img.height
443
- output_width = width or img.width
444
- if max_size and not (width and height):
445
- output_height, output_width = _get_width_height(
446
- max_size, img.height, img.width
447
- )
448
-
449
- if output_height != img.height or output_width != img.width:
450
- img = img.resize(
451
- output_height, output_width, resampling_method=resampling_method
452
- )
453
-
454
- return img
455
-
456
- def preview(
459
+ def preview( # noqa: C901
457
460
  self,
458
461
  max_size: int = 1024,
459
- height: Optional[int] = None,
460
- width: Optional[int] = None,
461
- nodata: Optional[NoData] = None,
462
- indexes: Optional[Indexes] = None,
463
- dst_crs: Optional[CRS] = None,
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,
464
467
  reproject_method: WarpResampling = "nearest",
465
468
  resampling_method: RIOResampling = "nearest",
466
469
  out_dtype: str | numpy.dtype | None = None,
@@ -488,7 +491,12 @@ class XarrayReader(BaseReader):
488
491
  )
489
492
  max_size = None
490
493
 
491
- 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
+ )
492
500
 
493
501
  if nodata is not None:
494
502
  da = da.rio.write_nodata(nodata)
@@ -574,6 +582,8 @@ class XarrayReader(BaseReader):
574
582
  crs=da.rio.crs,
575
583
  dataset_statistics=stats,
576
584
  band_names=band_names,
585
+ band_descriptions=band_descriptions,
586
+ nodata=da.rio.nodata,
577
587
  )
578
588
 
579
589
  if max_size:
@@ -596,8 +606,8 @@ class XarrayReader(BaseReader):
596
606
  lon: float,
597
607
  lat: float,
598
608
  coord_crs: CRS = WGS84_CRS,
599
- nodata: Optional[NoData] = None,
600
- indexes: Optional[Indexes] = None,
609
+ nodata: NoData | None = None,
610
+ indexes: Indexes | None = None,
601
611
  out_dtype: str | numpy.dtype | None = None,
602
612
  **kwargs: Any,
603
613
  ) -> PointData:
@@ -621,7 +631,7 @@ class XarrayReader(BaseReader):
621
631
  ):
622
632
  raise PointOutsideBounds("Point is outside dataset bounds")
623
633
 
624
- da, band_names = self._sel_indexes(indexes)
634
+ da, band_names, band_descriptions = self._sel_indexes(indexes)
625
635
 
626
636
  if nodata is not None:
627
637
  da = da.rio.write_nodata(nodata)
@@ -642,21 +652,23 @@ class XarrayReader(BaseReader):
642
652
  coordinates=(lon, lat),
643
653
  crs=coord_crs,
644
654
  band_names=band_names,
655
+ band_descriptions=band_descriptions,
645
656
  pixel_location=(x, y),
657
+ nodata=da.rio.nodata,
646
658
  )
647
659
 
648
660
  def feature(
649
661
  self,
650
662
  shape: Dict,
651
- dst_crs: Optional[CRS] = None,
663
+ dst_crs: CRS | None = None,
652
664
  shape_crs: CRS = WGS84_CRS,
653
665
  reproject_method: WarpResampling = "nearest",
654
666
  auto_expand: bool = True,
655
- nodata: Optional[NoData] = None,
656
- indexes: Optional[Indexes] = None,
657
- max_size: Optional[int] = None,
658
- height: Optional[int] = None,
659
- width: Optional[int] = None,
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,
660
672
  resampling_method: RIOResampling = "nearest",
661
673
  out_dtype: str | numpy.dtype | None = None,
662
674
  **kwargs: Any,