rio-tiler 8.0.4__py3-none-any.whl → 9.0.0a1__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/rasterio.py CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  import contextlib
4
4
  import warnings
5
- from typing import Any, Callable, Dict, List, Optional, Sequence, Union, cast
5
+ from collections.abc import Callable
6
+ from typing import Any, cast
6
7
 
7
8
  import attr
8
9
  import numpy
@@ -34,7 +35,7 @@ from rio_tiler.errors import (
34
35
  from rio_tiler.expression import parse_expression
35
36
  from rio_tiler.io.base import BaseReader
36
37
  from rio_tiler.models import BandStatistics, ImageData, Info, PointData
37
- from rio_tiler.types import BBox, Indexes, NumType, RIOResampling
38
+ from rio_tiler.types import BBox, Indexes, RIOResampling
38
39
  from rio_tiler.utils import (
39
40
  CRS_to_uri,
40
41
  _validate_shape_input,
@@ -73,14 +74,14 @@ class Reader(BaseReader):
73
74
 
74
75
  """
75
76
 
76
- input: str = attr.ib()
77
- dataset: Union[DatasetReader, DatasetWriter, MemoryFile, WarpedVRT] = attr.ib(
77
+ input: str | None = attr.ib()
78
+ dataset: DatasetReader | DatasetWriter | MemoryFile | WarpedVRT | None = attr.ib(
78
79
  default=None
79
80
  )
80
81
 
81
82
  tms: TileMatrixSet = attr.ib(default=WEB_MERCATOR_TMS)
82
83
 
83
- colormap: Dict = attr.ib(default=None)
84
+ colormap: dict | None = attr.ib(default=None)
84
85
 
85
86
  options: reader.Options = attr.ib()
86
87
 
@@ -168,7 +169,7 @@ class Reader(BaseReader):
168
169
 
169
170
  def _get_descr(ix):
170
171
  """Return band description."""
171
- return self.dataset.descriptions[ix - 1] or ""
172
+ return self.dataset.descriptions[ix - 1] or f"b{ix}"
172
173
 
173
174
  if self.options.get("nodata", self.dataset.nodata) is not None:
174
175
  nodata_type = "Nodata"
@@ -214,14 +215,14 @@ class Reader(BaseReader):
214
215
  def statistics(
215
216
  self,
216
217
  categorical: bool = False,
217
- categories: Optional[List[float]] = None,
218
- percentiles: Optional[List[int]] = None,
219
- hist_options: Optional[Dict] = None,
218
+ categories: list[float] | None = None,
219
+ percentiles: list[int] | None = None,
220
+ hist_options: dict | None = None,
220
221
  max_size: int = 1024,
221
- indexes: Optional[Indexes] = None,
222
- expression: Optional[str] = None,
222
+ indexes: Indexes | None = None,
223
+ expression: str | None = None,
223
224
  **kwargs: Any,
224
- ) -> Dict[str, BandStatistics]:
225
+ ) -> dict[str, BandStatistics]:
225
226
  """Return bands statistics from a dataset.
226
227
 
227
228
  Args:
@@ -253,10 +254,10 @@ class Reader(BaseReader):
253
254
  tile_x: int,
254
255
  tile_y: int,
255
256
  tile_z: int,
256
- tilesize: int = 256,
257
- indexes: Optional[Indexes] = None,
258
- expression: Optional[str] = None,
259
- buffer: Optional[float] = None,
257
+ tilesize: int | None = None,
258
+ indexes: Indexes | None = None,
259
+ expression: str | None = None,
260
+ buffer: float | None = None,
260
261
  **kwargs: Any,
261
262
  ) -> ImageData:
262
263
  """Read a Web Map tile from a Dataset.
@@ -265,7 +266,7 @@ class Reader(BaseReader):
265
266
  tile_x (int): Tile's horizontal index.
266
267
  tile_y (int): Tile's vertical index.
267
268
  tile_z (int): Tile's zoom level index.
268
- tilesize (int, optional): Output image size. Defaults to `256`.
269
+ tilesize (int, optional): Output image size.
269
270
  indexes (int or sequence of int, optional): Band indexes.
270
271
  expression (str, optional): rio-tiler expression (e.g. b1/b2+b3).
271
272
  buffer (float, optional): Buffer on each side of the given tile. It must be a multiple of `0.5`. Output **tilesize** will be expanded to `tilesize + 2 * tile_buffer` (e.g 0.5 = 257x257, 1.0 = 258x258).
@@ -280,12 +281,18 @@ class Reader(BaseReader):
280
281
  f"Tile(x={tile_x}, y={tile_y}, z={tile_z}) is outside bounds"
281
282
  )
282
283
 
284
+ matrix = self.tms.matrix(tile_z)
285
+ bbox = cast(
286
+ BBox,
287
+ self.tms.xy_bounds(Tile(x=tile_x, y=tile_y, z=tile_z)),
288
+ )
289
+
283
290
  return self.part(
284
- cast(BBox, self.tms.xy_bounds(Tile(x=tile_x, y=tile_y, z=tile_z))),
291
+ bbox,
285
292
  dst_crs=self.tms.rasterio_crs,
286
293
  bounds_crs=self.tms.rasterio_crs,
287
- height=tilesize,
288
- width=tilesize,
294
+ height=tilesize or matrix.tileHeight,
295
+ width=tilesize or matrix.tileWidth,
289
296
  max_size=None,
290
297
  indexes=indexes,
291
298
  expression=expression,
@@ -296,14 +303,14 @@ class Reader(BaseReader):
296
303
  def part(
297
304
  self,
298
305
  bbox: BBox,
299
- dst_crs: Optional[CRS] = None,
306
+ dst_crs: CRS | None = None,
300
307
  bounds_crs: CRS = WGS84_CRS,
301
- indexes: Optional[Union[int, Sequence]] = None,
302
- expression: Optional[str] = None,
303
- max_size: Optional[int] = None,
304
- height: Optional[int] = None,
305
- width: Optional[int] = None,
306
- buffer: Optional[float] = None,
308
+ indexes: Indexes | None = None,
309
+ expression: str | None = None,
310
+ max_size: int | None = None,
311
+ height: int | None = None,
312
+ width: int | None = None,
313
+ buffer: float | None = None,
307
314
  **kwargs: Any,
308
315
  ) -> ImageData:
309
316
  """Read part of a Dataset.
@@ -359,11 +366,11 @@ class Reader(BaseReader):
359
366
 
360
367
  def preview(
361
368
  self,
362
- indexes: Optional[Indexes] = None,
363
- expression: Optional[str] = None,
369
+ indexes: Indexes | None = None,
370
+ expression: str | None = None,
364
371
  max_size: int = 1024,
365
- height: Optional[int] = None,
366
- width: Optional[int] = None,
372
+ height: int | None = None,
373
+ width: int | None = None,
367
374
  **kwargs: Any,
368
375
  ) -> ImageData:
369
376
  """Return a preview of a Dataset.
@@ -394,8 +401,8 @@ class Reader(BaseReader):
394
401
  lon: float,
395
402
  lat: float,
396
403
  coord_crs: CRS = WGS84_CRS,
397
- indexes: Optional[Indexes] = None,
398
- expression: Optional[str] = None,
404
+ indexes: Indexes | None = None,
405
+ expression: str | None = None,
399
406
  **kwargs: Any,
400
407
  ) -> PointData:
401
408
  """Read a pixel value from a Dataset.
@@ -435,15 +442,15 @@ class Reader(BaseReader):
435
442
 
436
443
  def feature(
437
444
  self,
438
- shape: Dict,
439
- dst_crs: Optional[CRS] = None,
445
+ shape: dict,
446
+ dst_crs: CRS | None = None,
440
447
  shape_crs: CRS = WGS84_CRS,
441
- indexes: Optional[Indexes] = None,
442
- expression: Optional[str] = None,
443
- max_size: Optional[int] = None,
444
- height: Optional[int] = None,
445
- width: Optional[int] = None,
446
- buffer: Optional[NumType] = None,
448
+ indexes: Indexes | None = None,
449
+ expression: str | None = None,
450
+ max_size: int | None = None,
451
+ height: int | None = None,
452
+ width: int | None = None,
453
+ buffer: float | None = None,
447
454
  **kwargs: Any,
448
455
  ) -> ImageData:
449
456
  """Read part of a Dataset defined by a geojson feature.
@@ -514,8 +521,8 @@ class Reader(BaseReader):
514
521
 
515
522
  def read(
516
523
  self,
517
- indexes: Optional[Indexes] = None,
518
- expression: Optional[str] = None,
524
+ indexes: Indexes | None = None,
525
+ expression: str | None = None,
519
526
  **kwargs: Any,
520
527
  ) -> ImageData:
521
528
  """Read the Dataset.
@@ -560,7 +567,7 @@ class LocalTileMatrixSet:
560
567
  minzoom: int = attr.ib(init=False, default=0)
561
568
  maxzoom: int = attr.ib(init=False)
562
569
 
563
- rasterio_crs: CRS = attr.ib(init=False, default=None)
570
+ rasterio_crs: CRS | None = attr.ib(init=False, default=None)
564
571
 
565
572
  @maxzoom.default
566
573
  def _maxzoom(self):
@@ -592,9 +599,9 @@ class LocalTileMatrixSet:
592
599
  class ImageReader(Reader):
593
600
  """Non Geo Image Reader"""
594
601
 
595
- tms: TileMatrixSet = attr.ib(init=False)
602
+ tms: LocalTileMatrixSet = attr.ib(init=False) # type: ignore[assignment]
596
603
 
597
- crs: CRS = attr.ib(init=False, default=None)
604
+ crs: CRS | None = attr.ib(init=False, default=None)
598
605
  transform: Affine = attr.ib(init=False)
599
606
 
600
607
  def __attrs_post_init__(self):
@@ -634,15 +641,14 @@ class ImageReader(Reader):
634
641
  tile_x: int,
635
642
  tile_y: int,
636
643
  tile_z: int,
637
- tilesize: int = 256,
638
- indexes: Optional[Indexes] = None,
639
- expression: Optional[str] = None,
640
- out_dtype: Optional[Union[str, numpy.dtype]] = None,
644
+ tilesize: int | None = None,
645
+ indexes: Indexes | None = None,
646
+ expression: str | None = None,
647
+ out_dtype: str | numpy.dtype | None = None,
641
648
  resampling_method: RIOResampling = "nearest",
642
649
  unscale: bool = False,
643
- post_process: Optional[
644
- Callable[[numpy.ma.MaskedArray], numpy.ma.MaskedArray]
645
- ] = None,
650
+ post_process: Callable[[numpy.ma.MaskedArray], numpy.ma.MaskedArray]
651
+ | None = None,
646
652
  ) -> ImageData:
647
653
  """Read a Web Map tile from an Image.
648
654
 
@@ -666,10 +672,15 @@ class ImageReader(Reader):
666
672
  f"Tile {tile_z}/{tile_x}/{tile_y} is outside {self.input} bounds"
667
673
  )
668
674
 
675
+ bbox = cast(
676
+ BBox,
677
+ self.tms.xy_bounds(Tile(x=tile_x, y=tile_y, z=tile_z)),
678
+ )
679
+
669
680
  return self.part(
670
- cast(BBox, self.tms.xy_bounds(Tile(x=tile_x, y=tile_y, z=tile_z))),
671
- height=tilesize,
672
- width=tilesize,
681
+ bbox,
682
+ height=tilesize or self.tms.tile_size,
683
+ width=tilesize or self.tms.tile_size,
673
684
  max_size=None,
674
685
  indexes=indexes,
675
686
  expression=expression,
@@ -682,17 +693,16 @@ class ImageReader(Reader):
682
693
  def part( # type: ignore
683
694
  self,
684
695
  bbox: BBox,
685
- indexes: Optional[Union[int, Sequence]] = None,
686
- expression: Optional[str] = None,
687
- max_size: Optional[int] = None,
688
- height: Optional[int] = None,
689
- width: Optional[int] = None,
690
- out_dtype: Optional[Union[str, numpy.dtype]] = None,
696
+ indexes: Indexes | None = None,
697
+ expression: str | None = None,
698
+ max_size: int | None = None,
699
+ height: int | None = None,
700
+ width: int | None = None,
701
+ out_dtype: str | numpy.dtype | None = None,
691
702
  resampling_method: RIOResampling = "nearest",
692
703
  unscale: bool = False,
693
- post_process: Optional[
694
- Callable[[numpy.ma.MaskedArray], numpy.ma.MaskedArray]
695
- ] = None,
704
+ post_process: Callable[[numpy.ma.MaskedArray], numpy.ma.MaskedArray]
705
+ | None = None,
696
706
  ) -> ImageData:
697
707
  """Read part of an Image.
698
708
 
@@ -744,14 +754,13 @@ class ImageReader(Reader):
744
754
  self,
745
755
  x: float,
746
756
  y: float,
747
- indexes: Optional[Indexes] = None,
748
- expression: Optional[str] = None,
749
- out_dtype: Optional[Union[str, numpy.dtype]] = None,
757
+ indexes: Indexes | None = None,
758
+ expression: str | None = None,
759
+ out_dtype: str | numpy.dtype | None = None,
750
760
  resampling_method: RIOResampling = "nearest",
751
761
  unscale: bool = False,
752
- post_process: Optional[
753
- Callable[[numpy.ma.MaskedArray], numpy.ma.MaskedArray]
754
- ] = None,
762
+ post_process: Callable[[numpy.ma.MaskedArray], numpy.ma.MaskedArray]
763
+ | None = None,
755
764
  ) -> PointData:
756
765
  """Read a pixel value from an Image.
757
766
 
@@ -793,18 +802,17 @@ class ImageReader(Reader):
793
802
 
794
803
  def feature( # type: ignore
795
804
  self,
796
- shape: Dict,
797
- indexes: Optional[Indexes] = None,
798
- expression: Optional[str] = None,
799
- max_size: Optional[int] = None,
800
- height: Optional[int] = None,
801
- width: Optional[int] = None,
802
- out_dtype: Optional[Union[str, numpy.dtype]] = None,
805
+ shape: dict,
806
+ indexes: Indexes | None = None,
807
+ expression: str | None = None,
808
+ max_size: int | None = None,
809
+ height: int | None = None,
810
+ width: int | None = None,
811
+ out_dtype: str | numpy.dtype | None = None,
803
812
  resampling_method: RIOResampling = "nearest",
804
813
  unscale: bool = False,
805
- post_process: Optional[
806
- Callable[[numpy.ma.MaskedArray], numpy.ma.MaskedArray]
807
- ] = None,
814
+ post_process: Callable[[numpy.ma.MaskedArray], numpy.ma.MaskedArray]
815
+ | None = None,
808
816
  ) -> ImageData:
809
817
  """Read part of an Image defined by a geojson feature."""
810
818
  bbox = featureBounds(shape)
rio_tiler/io/stac.py CHANGED
@@ -3,18 +3,8 @@
3
3
  import json
4
4
  import os
5
5
  import warnings
6
- from typing import (
7
- Any,
8
- Dict,
9
- Iterator,
10
- List,
11
- Optional,
12
- Sequence,
13
- Set,
14
- Tuple,
15
- Type,
16
- Union,
17
- )
6
+ from collections.abc import Iterator, Sequence
7
+ from typing import Any
18
8
  from urllib.parse import urlparse
19
9
 
20
10
  import attr
@@ -111,7 +101,7 @@ def aws_get_object(
111
101
  LRUCache(maxsize=512),
112
102
  key=lambda filepath, **kargs: hashkey(filepath, json.dumps(kargs)),
113
103
  )
114
- def fetch(filepath: str, **kwargs: Any) -> Dict:
104
+ def fetch(filepath: str, **kwargs: Any) -> dict:
115
105
  """Fetch STAC items.
116
106
 
117
107
  A LRU cache is set on top of this function.
@@ -142,11 +132,11 @@ def fetch(filepath: str, **kwargs: Any) -> Dict:
142
132
 
143
133
  def _get_assets(
144
134
  stac_item: pystac.Item,
145
- include: Optional[Set[str]] = None,
146
- exclude: Optional[Set[str]] = None,
147
- include_asset_types: Optional[Set[str]] = None,
148
- exclude_asset_types: Optional[Set[str]] = None,
149
- ) -> Iterator:
135
+ include: set[str] | None = None,
136
+ exclude: set[str] | None = None,
137
+ include_asset_types: set[str] | None = None,
138
+ exclude_asset_types: set[str] | None = None,
139
+ ) -> Iterator[str]:
150
140
  """Get valid asset list.
151
141
 
152
142
  Args:
@@ -183,7 +173,7 @@ def _get_assets(
183
173
  yield asset
184
174
 
185
175
 
186
- def _to_pystac_item(item: Union[None, Dict, pystac.Item]) -> Union[None, pystac.Item]:
176
+ def _to_pystac_item(item: dict | pystac.Item | None) -> pystac.Item | None:
187
177
  """Attr converter to convert to Dict to pystac.Item
188
178
 
189
179
  Args:
@@ -193,7 +183,7 @@ def _to_pystac_item(item: Union[None, Dict, pystac.Item]) -> Union[None, pystac.
193
183
  pystac.Item: pystac STAC item object.
194
184
 
195
185
  """
196
- if isinstance(item, Dict):
186
+ if isinstance(item, dict):
197
187
  return pystac.Item.from_dict(item)
198
188
 
199
189
  return item
@@ -237,26 +227,26 @@ class STACReader(MultiBaseReader):
237
227
 
238
228
  """
239
229
 
240
- input: str = attr.ib()
241
- item: pystac.Item = attr.ib(default=None, converter=_to_pystac_item)
230
+ input: str | None = attr.ib()
231
+ item: pystac.Item | None = attr.ib(default=None, converter=_to_pystac_item)
242
232
 
243
233
  tms: TileMatrixSet = attr.ib(default=WEB_MERCATOR_TMS)
244
- minzoom: int = attr.ib(default=None)
245
- maxzoom: int = attr.ib(default=None)
234
+ minzoom: int | None = attr.ib(default=None)
235
+ maxzoom: int | None = attr.ib(default=None)
246
236
 
247
- include_assets: Optional[Set[str]] = attr.ib(default=None)
248
- exclude_assets: Optional[Set[str]] = attr.ib(default=None)
237
+ include_assets: set[str] | None = attr.ib(default=None)
238
+ exclude_assets: set[str] | None = attr.ib(default=None)
249
239
 
250
- include_asset_types: Set[str] = attr.ib(default=DEFAULT_VALID_TYPE)
251
- exclude_asset_types: Optional[Set[str]] = attr.ib(default=None)
240
+ include_asset_types: set[str] = attr.ib(default=DEFAULT_VALID_TYPE)
241
+ exclude_asset_types: set[str] | None = attr.ib(default=None)
252
242
 
253
243
  assets: Sequence[str] = attr.ib(init=False)
254
- default_assets: Optional[Sequence[str]] = attr.ib(default=None)
244
+ default_assets: Sequence[str] | None = attr.ib(default=None)
255
245
 
256
- reader: Type[BaseReader] = attr.ib(default=Reader)
257
- reader_options: Dict = attr.ib(factory=dict)
246
+ reader: type[BaseReader] = attr.ib(default=Reader)
247
+ reader_options: dict = attr.ib(factory=dict)
258
248
 
259
- fetch_options: Dict = attr.ib(factory=dict)
249
+ fetch_options: dict = attr.ib(factory=dict)
260
250
 
261
251
  ctx: rasterio.Env = attr.ib(default=rasterio.Env)
262
252
 
@@ -289,7 +279,7 @@ class STACReader(MultiBaseReader):
289
279
  if not self.assets:
290
280
  raise MissingAssets("No valid asset found. Asset's media types not supported")
291
281
 
292
- def get_asset_list(self) -> List[str]:
282
+ def get_asset_list(self) -> list[str]:
293
283
  """Get valid asset list"""
294
284
  return list(
295
285
  _get_assets(
@@ -301,11 +291,11 @@ class STACReader(MultiBaseReader):
301
291
  )
302
292
  )
303
293
 
304
- def _get_reader(self, asset_info: AssetInfo) -> Tuple[Type[BaseReader], Dict]:
294
+ def _get_reader(self, asset_info: AssetInfo) -> tuple[type[BaseReader], dict]:
305
295
  """Get Asset Reader."""
306
296
  return self.reader, {}
307
297
 
308
- def _parse_vrt_asset(self, asset: str) -> Tuple[str, Optional[str]]:
298
+ def _parse_vrt_asset(self, asset: str) -> tuple[str, str | None]:
309
299
  if asset.startswith("vrt://") and asset not in self.assets:
310
300
  parsed = urlparse(asset)
311
301
  if not parsed.netloc:
@@ -333,33 +323,52 @@ class STACReader(MultiBaseReader):
333
323
 
334
324
  """
335
325
  asset, vrt_options = self._parse_vrt_asset(asset)
326
+
327
+ method_options: dict[str, Any] = {}
328
+
329
+ # NOTE: asset can be in form of
330
+ # "{asset_name}|some_option=some_value&another_option=another_value"
331
+ if "|" in asset:
332
+ asset, params = asset.split("|", 1)
333
+ # NOTE: Construct method options from params
334
+ if params:
335
+ for param in params.split("&"):
336
+ key, value = param.split("=", 1)
337
+ if key == "indexes":
338
+ method_options["indexes"] = list(map(int, value.split(",")))
339
+ elif key == "expression":
340
+ method_options["expression"] = value
341
+
336
342
  if asset not in self.assets:
337
343
  raise InvalidAssetName(
338
344
  f"'{asset}' is not valid, should be one of {self.assets}"
339
345
  )
340
346
 
347
+ asset_modified = "expression" in method_options or vrt_options
348
+
341
349
  asset_info = self.item.assets[asset]
342
350
  extras = asset_info.extra_fields
343
351
 
344
352
  info = AssetInfo(
345
353
  url=asset_info.get_absolute_href() or asset_info.href,
346
- metadata=extras if not vrt_options else None,
354
+ name=asset,
355
+ media_type=asset_info.media_type,
356
+ method_options=method_options,
347
357
  )
348
358
 
359
+ if not asset_modified:
360
+ info["metadata"] = extras
361
+
349
362
  if STAC_ALTERNATE_KEY and extras.get("alternate"):
350
363
  if alternate := extras["alternate"].get(STAC_ALTERNATE_KEY):
351
364
  info["url"] = alternate["href"]
352
365
 
353
- if asset_info.media_type:
354
- info["media_type"] = asset_info.media_type
355
-
356
366
  # https://github.com/stac-extensions/file
357
367
  if head := extras.get("file:header_size"):
358
368
  info["env"] = {"GDAL_INGESTED_BYTES_AT_OPEN": head}
359
369
 
360
370
  # https://github.com/stac-extensions/raster
361
- if extras.get("raster:bands") and not vrt_options:
362
- bands = extras.get("raster:bands")
371
+ if (bands := extras.get("raster:bands", [])) and not asset_modified:
363
372
  stats = [
364
373
  (b["statistics"]["minimum"], b["statistics"]["maximum"])
365
374
  for b in bands