rio-tiler 6.8.0__py3-none-any.whl → 7.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 +54 -18
- rio_tiler/io/base.py +330 -131
- rio_tiler/io/rasterio.py +36 -102
- rio_tiler/io/stac.py +35 -19
- rio_tiler/io/xarray.py +14 -115
- rio_tiler/models.py +16 -65
- rio_tiler/reader.py +3 -4
- rio_tiler/types.py +3 -2
- rio_tiler/utils.py +36 -12
- {rio_tiler-6.8.0.dist-info → rio_tiler-7.0.0.dist-info}/METADATA +13 -10
- {rio_tiler-6.8.0.dist-info → rio_tiler-7.0.0.dist-info}/RECORD +15 -15
- {rio_tiler-6.8.0.dist-info → rio_tiler-7.0.0.dist-info}/WHEEL +0 -0
- {rio_tiler-6.8.0.dist-info → rio_tiler-7.0.0.dist-info}/licenses/AUTHORS.txt +0 -0
- {rio_tiler-6.8.0.dist-info → rio_tiler-7.0.0.dist-info}/licenses/LICENSE +0 -0
rio_tiler/io/rasterio.py
CHANGED
|
@@ -19,7 +19,7 @@ from rasterio.io import DatasetReader, DatasetWriter, MemoryFile
|
|
|
19
19
|
from rasterio.rio.overview import get_maximum_overview_level
|
|
20
20
|
from rasterio.transform import from_bounds as transform_from_bounds
|
|
21
21
|
from rasterio.vrt import WarpedVRT
|
|
22
|
-
from rasterio.warp import
|
|
22
|
+
from rasterio.warp import transform_geom
|
|
23
23
|
from rasterio.windows import Window
|
|
24
24
|
from rasterio.windows import from_bounds as window_from_bounds
|
|
25
25
|
|
|
@@ -35,7 +35,12 @@ from rio_tiler.expression import parse_expression
|
|
|
35
35
|
from rio_tiler.io.base import BaseReader
|
|
36
36
|
from rio_tiler.models import BandStatistics, ImageData, Info, PointData
|
|
37
37
|
from rio_tiler.types import BBox, Indexes, NumType, RIOResampling
|
|
38
|
-
from rio_tiler.utils import
|
|
38
|
+
from rio_tiler.utils import (
|
|
39
|
+
CRS_to_uri,
|
|
40
|
+
_validate_shape_input,
|
|
41
|
+
has_alpha_band,
|
|
42
|
+
has_mask_band,
|
|
43
|
+
)
|
|
39
44
|
|
|
40
45
|
|
|
41
46
|
@attr.s
|
|
@@ -46,7 +51,6 @@ class Reader(BaseReader):
|
|
|
46
51
|
input (str): dataset path.
|
|
47
52
|
dataset (rasterio.io.DatasetReader or rasterio.io.DatasetWriter or rasterio.vrt.WarpedVRT, optional): Rasterio dataset.
|
|
48
53
|
tms (morecantile.TileMatrixSet, optional): TileMatrixSet grid definition. Defaults to `WebMercatorQuad`.
|
|
49
|
-
geographic_crs (rasterio.crs.CRS, optional): CRS to use as geographic coordinate system. Defaults to WGS84.
|
|
50
54
|
colormap (dict, optional): Overwrite internal colormap.
|
|
51
55
|
options (dict, optional): Options to forward to low-level reader methods.
|
|
52
56
|
|
|
@@ -75,16 +79,13 @@ class Reader(BaseReader):
|
|
|
75
79
|
)
|
|
76
80
|
|
|
77
81
|
tms: TileMatrixSet = attr.ib(default=WEB_MERCATOR_TMS)
|
|
78
|
-
geographic_crs: CRS = attr.ib(default=WGS84_CRS)
|
|
79
82
|
|
|
80
83
|
colormap: Dict = attr.ib(default=None)
|
|
81
84
|
|
|
82
85
|
options: reader.Options = attr.ib()
|
|
83
86
|
|
|
84
87
|
# Context Manager to handle rasterio open/close
|
|
85
|
-
_ctx_stack = attr.ib(init=False, factory=contextlib.ExitStack)
|
|
86
|
-
_minzoom: int = attr.ib(init=False, default=None)
|
|
87
|
-
_maxzoom: int = attr.ib(init=False, default=None)
|
|
88
|
+
_ctx_stack: contextlib.ExitStack = attr.ib(init=False, factory=contextlib.ExitStack)
|
|
88
89
|
|
|
89
90
|
@options.default
|
|
90
91
|
def _options_default(self):
|
|
@@ -121,6 +122,10 @@ class Reader(BaseReader):
|
|
|
121
122
|
self.bounds = tuple(self.dataset.bounds)
|
|
122
123
|
self.crs = self.dataset.crs
|
|
123
124
|
|
|
125
|
+
self.transform = self.dataset.transform
|
|
126
|
+
self.height = self.dataset.height
|
|
127
|
+
self.width = self.dataset.width
|
|
128
|
+
|
|
124
129
|
if self.colormap is None:
|
|
125
130
|
self._get_colormap()
|
|
126
131
|
|
|
@@ -140,85 +145,15 @@ class Reader(BaseReader):
|
|
|
140
145
|
"""Support using with Context Managers."""
|
|
141
146
|
self.close()
|
|
142
147
|
|
|
143
|
-
def _dst_geom_in_tms_crs(self):
|
|
144
|
-
"""Return dataset info in TMS projection."""
|
|
145
|
-
tms_crs = self.tms.rasterio_crs
|
|
146
|
-
if self.crs != tms_crs:
|
|
147
|
-
dst_affine, w, h = calculate_default_transform(
|
|
148
|
-
self.crs,
|
|
149
|
-
tms_crs,
|
|
150
|
-
self.dataset.width,
|
|
151
|
-
self.dataset.height,
|
|
152
|
-
*self.dataset.bounds,
|
|
153
|
-
)
|
|
154
|
-
else:
|
|
155
|
-
dst_affine = list(self.dataset.transform)
|
|
156
|
-
w = self.dataset.width
|
|
157
|
-
h = self.dataset.height
|
|
158
|
-
|
|
159
|
-
return dst_affine, w, h
|
|
160
|
-
|
|
161
|
-
def get_minzoom(self) -> int:
|
|
162
|
-
"""Define dataset minimum zoom level."""
|
|
163
|
-
if self._minzoom is None:
|
|
164
|
-
# We assume the TMS tilesize to be constant over all matrices
|
|
165
|
-
# ref: https://github.com/OSGeo/gdal/blob/dc38aa64d779ecc45e3cd15b1817b83216cf96b8/gdal/frmts/gtiff/cogdriver.cpp#L274
|
|
166
|
-
tilesize = self.tms.tileMatrices[0].tileWidth
|
|
167
|
-
|
|
168
|
-
try:
|
|
169
|
-
dst_affine, w, h = self._dst_geom_in_tms_crs()
|
|
170
|
-
|
|
171
|
-
# The minzoom is defined by the resolution of the maximum theoretical overview level
|
|
172
|
-
# We assume `tilesize`` is the smallest overview size
|
|
173
|
-
overview_level = get_maximum_overview_level(w, h, minsize=tilesize)
|
|
174
|
-
|
|
175
|
-
# Get the resolution of the overview
|
|
176
|
-
resolution = max(abs(dst_affine[0]), abs(dst_affine[4]))
|
|
177
|
-
ovr_resolution = resolution * (2**overview_level)
|
|
178
|
-
|
|
179
|
-
# Find what TMS matrix match the overview resolution
|
|
180
|
-
self._minzoom = self.tms.zoom_for_res(ovr_resolution)
|
|
181
|
-
|
|
182
|
-
except: # noqa
|
|
183
|
-
# if we can't get max zoom from the dataset we default to TMS maxzoom
|
|
184
|
-
warnings.warn(
|
|
185
|
-
"Cannot determine minzoom based on dataset information, will default to TMS minzoom.",
|
|
186
|
-
UserWarning,
|
|
187
|
-
)
|
|
188
|
-
self._minzoom = self.tms.minzoom
|
|
189
|
-
|
|
190
|
-
return self._minzoom
|
|
191
|
-
|
|
192
|
-
def get_maxzoom(self) -> int:
|
|
193
|
-
"""Define dataset maximum zoom level."""
|
|
194
|
-
if self._maxzoom is None:
|
|
195
|
-
try:
|
|
196
|
-
dst_affine, _, _ = self._dst_geom_in_tms_crs()
|
|
197
|
-
|
|
198
|
-
# The maxzoom is defined by finding the minimum difference between
|
|
199
|
-
# the raster resolution and the zoom level resolution
|
|
200
|
-
resolution = max(abs(dst_affine[0]), abs(dst_affine[4]))
|
|
201
|
-
self._maxzoom = self.tms.zoom_for_res(resolution)
|
|
202
|
-
|
|
203
|
-
except: # noqa
|
|
204
|
-
# if we can't get min/max zoom from the dataset we default to TMS maxzoom
|
|
205
|
-
warnings.warn(
|
|
206
|
-
"Cannot determine maxzoom based on dataset information, will default to TMS maxzoom.",
|
|
207
|
-
UserWarning,
|
|
208
|
-
)
|
|
209
|
-
self._maxzoom = self.tms.maxzoom
|
|
210
|
-
|
|
211
|
-
return self._maxzoom
|
|
212
|
-
|
|
213
148
|
@property
|
|
214
149
|
def minzoom(self):
|
|
215
150
|
"""Return dataset minzoom."""
|
|
216
|
-
return self.
|
|
151
|
+
return self._minzoom
|
|
217
152
|
|
|
218
153
|
@property
|
|
219
154
|
def maxzoom(self):
|
|
220
155
|
"""Return dataset maxzoom."""
|
|
221
|
-
return self.
|
|
156
|
+
return self._maxzoom
|
|
222
157
|
|
|
223
158
|
def _get_colormap(self):
|
|
224
159
|
"""Retrieve the internal colormap."""
|
|
@@ -245,9 +180,8 @@ class Reader(BaseReader):
|
|
|
245
180
|
nodata_type = "None"
|
|
246
181
|
|
|
247
182
|
meta = {
|
|
248
|
-
"bounds": self.
|
|
249
|
-
"
|
|
250
|
-
"maxzoom": self.maxzoom,
|
|
183
|
+
"bounds": self.bounds,
|
|
184
|
+
"crs": CRS_to_uri(self.crs) or self.crs.to_wkt(),
|
|
251
185
|
"band_metadata": [
|
|
252
186
|
(f"b{ix}", self.dataset.tags(ix)) for ix in self.dataset.indexes
|
|
253
187
|
],
|
|
@@ -346,13 +280,10 @@ class Reader(BaseReader):
|
|
|
346
280
|
f"Tile(x={tile_x}, y={tile_y}, z={tile_z}) is outside bounds"
|
|
347
281
|
)
|
|
348
282
|
|
|
349
|
-
tile_bounds = self.tms.xy_bounds(Tile(x=tile_x, y=tile_y, z=tile_z))
|
|
350
|
-
dst_crs = self.tms.rasterio_crs
|
|
351
|
-
|
|
352
283
|
return self.part(
|
|
353
|
-
|
|
354
|
-
dst_crs=
|
|
355
|
-
bounds_crs=
|
|
284
|
+
tuple(self.tms.xy_bounds(Tile(x=tile_x, y=tile_y, z=tile_z))),
|
|
285
|
+
dst_crs=self.tms.rasterio_crs,
|
|
286
|
+
bounds_crs=self.tms.rasterio_crs,
|
|
356
287
|
height=tilesize,
|
|
357
288
|
width=tilesize,
|
|
358
289
|
max_size=None,
|
|
@@ -664,8 +595,6 @@ class ImageReader(Reader):
|
|
|
664
595
|
tms: TileMatrixSet = attr.ib(init=False)
|
|
665
596
|
|
|
666
597
|
crs: CRS = attr.ib(init=False, default=None)
|
|
667
|
-
geographic_crs: CRS = attr.ib(init=False, default=None)
|
|
668
|
-
|
|
669
598
|
transform: Affine = attr.ib(init=False)
|
|
670
599
|
|
|
671
600
|
def __attrs_post_init__(self):
|
|
@@ -676,23 +605,30 @@ class ImageReader(Reader):
|
|
|
676
605
|
|
|
677
606
|
height, width = self.dataset.height, self.dataset.width
|
|
678
607
|
self.bounds = (0, height, width, 0)
|
|
679
|
-
self.transform = transform_from_bounds(*self.bounds, width=width, height=height)
|
|
680
|
-
|
|
681
608
|
self.tms = LocalTileMatrixSet(width=width, height=height)
|
|
682
|
-
self.
|
|
683
|
-
self.
|
|
609
|
+
self.transform = transform_from_bounds(*self.bounds, width=width, height=height)
|
|
610
|
+
self.height = height
|
|
611
|
+
self.width = width
|
|
684
612
|
|
|
685
613
|
if self.colormap is None:
|
|
686
614
|
self._get_colormap()
|
|
687
615
|
|
|
688
|
-
if min(
|
|
689
|
-
self.dataset.width, self.dataset.height
|
|
690
|
-
) > 512 and not self.dataset.overviews(1):
|
|
616
|
+
if min(width, height) > 512 and not self.dataset.overviews(1):
|
|
691
617
|
warnings.warn(
|
|
692
618
|
"The dataset has no Overviews. rio-tiler performances might be impacted.",
|
|
693
619
|
NoOverviewWarning,
|
|
694
620
|
)
|
|
695
621
|
|
|
622
|
+
@property
|
|
623
|
+
def minzoom(self):
|
|
624
|
+
"""Return dataset minzoom."""
|
|
625
|
+
return self.tms.minzoom
|
|
626
|
+
|
|
627
|
+
@property
|
|
628
|
+
def maxzoom(self):
|
|
629
|
+
"""Return dataset maxzoom."""
|
|
630
|
+
return self.tms.maxzoom
|
|
631
|
+
|
|
696
632
|
def tile( # type: ignore
|
|
697
633
|
self,
|
|
698
634
|
tile_x: int,
|
|
@@ -731,10 +667,8 @@ class ImageReader(Reader):
|
|
|
731
667
|
f"Tile {tile_z}/{tile_x}/{tile_y} is outside {self.input} bounds"
|
|
732
668
|
)
|
|
733
669
|
|
|
734
|
-
tile_bounds = self.tms.xy_bounds(Tile(x=tile_x, y=tile_y, z=tile_z))
|
|
735
|
-
|
|
736
670
|
return self.part(
|
|
737
|
-
|
|
671
|
+
tuple(self.tms.xy_bounds(Tile(x=tile_x, y=tile_y, z=tile_z))),
|
|
738
672
|
height=tilesize,
|
|
739
673
|
width=tilesize,
|
|
740
674
|
max_size=None,
|
|
@@ -822,8 +756,8 @@ class ImageReader(Reader):
|
|
|
822
756
|
"""Read a pixel value from an Image.
|
|
823
757
|
|
|
824
758
|
Args:
|
|
825
|
-
|
|
826
|
-
|
|
759
|
+
x (float): X coordinate.
|
|
760
|
+
y (float): Y coordinate.
|
|
827
761
|
indexes (sequence of int or int, optional): Band indexes.
|
|
828
762
|
expression (str, optional): rio-tiler expression (e.g. b1/b2+b3).
|
|
829
763
|
unscale (bool, optional): Apply 'scales' and 'offsets' on output data value. Defaults to `False`.
|
rio_tiler/io/stac.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
5
|
import warnings
|
|
6
|
-
from typing import Any, Dict, Iterator, Optional, Set, Tuple, Type, Union
|
|
6
|
+
from typing import Any, Dict, Iterator, Optional, Sequence, Set, Tuple, Type, Union
|
|
7
7
|
from urllib.parse import urlparse
|
|
8
8
|
|
|
9
9
|
import attr
|
|
@@ -13,7 +13,7 @@ import rasterio
|
|
|
13
13
|
from cachetools import LRUCache, cached
|
|
14
14
|
from cachetools.keys import hashkey
|
|
15
15
|
from morecantile import TileMatrixSet
|
|
16
|
-
from rasterio.
|
|
16
|
+
from rasterio.transform import array_bounds
|
|
17
17
|
|
|
18
18
|
from rio_tiler.constants import WEB_MERCATOR_TMS, WGS84_CRS
|
|
19
19
|
from rio_tiler.errors import InvalidAssetName, MissingAssets
|
|
@@ -175,7 +175,7 @@ def _to_pystac_item(item: Union[None, Dict, pystac.Item]) -> Union[None, pystac.
|
|
|
175
175
|
"""Attr converter to convert to Dict to pystac.Item
|
|
176
176
|
|
|
177
177
|
Args:
|
|
178
|
-
|
|
178
|
+
item (Union[Dict, pystac.Item]): STAC Item.
|
|
179
179
|
|
|
180
180
|
Returns
|
|
181
181
|
pystac.Item: pystac STAC item object.
|
|
@@ -197,11 +197,11 @@ class STACReader(MultiBaseReader):
|
|
|
197
197
|
tms (morecantile.TileMatrixSet, optional): TileMatrixSet grid definition. Defaults to `WebMercatorQuad`.
|
|
198
198
|
minzoom (int, optional): Set minzoom for the tiles.
|
|
199
199
|
maxzoom (int, optional): Set maxzoom for the tiles.
|
|
200
|
-
geographic_crs (rasterio.crs.CRS, optional): CRS to use as geographic coordinate system. Defaults to WGS84.
|
|
201
200
|
include_assets (set of string, optional): Only Include specific assets.
|
|
202
201
|
exclude_assets (set of string, optional): Exclude specific assets.
|
|
203
202
|
include_asset_types (set of string, optional): Only include some assets base on their type.
|
|
204
203
|
exclude_asset_types (set of string, optional): Exclude some assets base on their type.
|
|
204
|
+
default_assets (list of string, optional): Default assets to use if none are defined.
|
|
205
205
|
reader (rio_tiler.io.BaseReader, optional): rio-tiler Reader. Defaults to `rio_tiler.io.Reader`.
|
|
206
206
|
reader_options (dict, optional): Additional option to forward to the Reader. Defaults to `{}`.
|
|
207
207
|
fetch_options (dict, optional): Options to pass to `rio_tiler.io.stac.fetch` function fetching the STAC Items. Defaults to `{}`.
|
|
@@ -229,10 +229,8 @@ class STACReader(MultiBaseReader):
|
|
|
229
229
|
item: pystac.Item = attr.ib(default=None, converter=_to_pystac_item)
|
|
230
230
|
|
|
231
231
|
tms: TileMatrixSet = attr.ib(default=WEB_MERCATOR_TMS)
|
|
232
|
-
minzoom: int = attr.ib()
|
|
233
|
-
maxzoom: int = attr.ib()
|
|
234
|
-
|
|
235
|
-
geographic_crs: CRS = attr.ib(default=WGS84_CRS)
|
|
232
|
+
minzoom: int = attr.ib(default=None)
|
|
233
|
+
maxzoom: int = attr.ib(default=None)
|
|
236
234
|
|
|
237
235
|
include_assets: Optional[Set[str]] = attr.ib(default=None)
|
|
238
236
|
exclude_assets: Optional[Set[str]] = attr.ib(default=None)
|
|
@@ -240,12 +238,15 @@ class STACReader(MultiBaseReader):
|
|
|
240
238
|
include_asset_types: Set[str] = attr.ib(default=DEFAULT_VALID_TYPE)
|
|
241
239
|
exclude_asset_types: Optional[Set[str]] = attr.ib(default=None)
|
|
242
240
|
|
|
241
|
+
assets: Sequence[str] = attr.ib(init=False)
|
|
242
|
+
default_assets: Optional[Sequence[str]] = attr.ib(default=None)
|
|
243
|
+
|
|
243
244
|
reader: Type[BaseReader] = attr.ib(default=Reader)
|
|
244
245
|
reader_options: Dict = attr.ib(factory=dict)
|
|
245
246
|
|
|
246
247
|
fetch_options: Dict = attr.ib(factory=dict)
|
|
247
248
|
|
|
248
|
-
ctx:
|
|
249
|
+
ctx: rasterio.Env = attr.ib(default=rasterio.Env)
|
|
249
250
|
|
|
250
251
|
def __attrs_post_init__(self):
|
|
251
252
|
"""Fetch STAC Item and get list of valid assets."""
|
|
@@ -253,10 +254,25 @@ class STACReader(MultiBaseReader):
|
|
|
253
254
|
fetch(self.input, **self.fetch_options), self.input
|
|
254
255
|
)
|
|
255
256
|
|
|
256
|
-
|
|
257
|
-
self.bounds = self.item.bbox
|
|
257
|
+
self.bounds = tuple(self.item.bbox)
|
|
258
258
|
self.crs = WGS84_CRS
|
|
259
259
|
|
|
260
|
+
if hasattr(self.item, "ext") and self.item.ext.has("proj"):
|
|
261
|
+
if all(
|
|
262
|
+
[
|
|
263
|
+
self.item.ext.proj.transform,
|
|
264
|
+
self.item.ext.proj.shape,
|
|
265
|
+
self.item.ext.proj.crs_string,
|
|
266
|
+
]
|
|
267
|
+
):
|
|
268
|
+
self.height, self.width = self.item.ext.proj.shape
|
|
269
|
+
self.transform = self.item.ext.proj.transform
|
|
270
|
+
self.bounds = array_bounds(self.height, self.width, self.transform)
|
|
271
|
+
self.crs = rasterio.crs.CRS.from_string(self.item.ext.proj.crs_string)
|
|
272
|
+
|
|
273
|
+
self.minzoom = self.minzoom if self.minzoom is not None else self._minzoom
|
|
274
|
+
self.maxzoom = self.maxzoom if self.maxzoom is not None else self._maxzoom
|
|
275
|
+
|
|
260
276
|
self.assets = list(
|
|
261
277
|
_get_assets(
|
|
262
278
|
self.item,
|
|
@@ -269,13 +285,9 @@ class STACReader(MultiBaseReader):
|
|
|
269
285
|
if not self.assets:
|
|
270
286
|
raise MissingAssets("No valid asset found. Asset's media types not supported")
|
|
271
287
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
return self.
|
|
275
|
-
|
|
276
|
-
@maxzoom.default
|
|
277
|
-
def _maxzoom(self):
|
|
278
|
-
return self.tms.maxzoom
|
|
288
|
+
def _get_reader(self, asset_info: AssetInfo) -> Tuple[Type[BaseReader], Dict]:
|
|
289
|
+
"""Get Asset Reader."""
|
|
290
|
+
return self.reader, {}
|
|
279
291
|
|
|
280
292
|
def _parse_vrt_asset(self, asset: str) -> Tuple[str, Optional[str]]:
|
|
281
293
|
if asset.startswith("vrt://") and asset not in self.assets:
|
|
@@ -322,12 +334,16 @@ class STACReader(MultiBaseReader):
|
|
|
322
334
|
if alternate := extras["alternate"].get(STAC_ALTERNATE_KEY):
|
|
323
335
|
info["url"] = alternate["href"]
|
|
324
336
|
|
|
337
|
+
if asset_info.media_type:
|
|
338
|
+
info["media_type"] = asset_info.media_type
|
|
339
|
+
|
|
325
340
|
# https://github.com/stac-extensions/file
|
|
326
341
|
if head := extras.get("file:header_size"):
|
|
327
342
|
info["env"] = {"GDAL_INGESTED_BYTES_AT_OPEN": head}
|
|
328
343
|
|
|
329
344
|
# https://github.com/stac-extensions/raster
|
|
330
|
-
if
|
|
345
|
+
if extras.get("raster:bands") and not vrt_options:
|
|
346
|
+
bands = extras.get("raster:bands")
|
|
331
347
|
stats = [
|
|
332
348
|
(b["statistics"]["minimum"], b["statistics"]["maximum"])
|
|
333
349
|
for b in bands
|
rio_tiler/io/xarray.py
CHANGED
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import warnings
|
|
6
5
|
from typing import Any, Dict, List, Optional
|
|
7
6
|
|
|
8
7
|
import attr
|
|
9
8
|
from morecantile import Tile, TileMatrixSet
|
|
10
9
|
from rasterio.crs import CRS
|
|
11
10
|
from rasterio.enums import Resampling
|
|
12
|
-
from rasterio.rio.overview import get_maximum_overview_level
|
|
13
11
|
from rasterio.transform import from_bounds, rowcol
|
|
14
12
|
from rasterio.warp import calculate_default_transform
|
|
15
13
|
from rasterio.warp import transform as transform_coords
|
|
@@ -24,7 +22,7 @@ from rio_tiler.errors import (
|
|
|
24
22
|
from rio_tiler.io.base import BaseReader
|
|
25
23
|
from rio_tiler.models import BandStatistics, ImageData, Info, PointData
|
|
26
24
|
from rio_tiler.types import BBox, NoData, WarpResampling
|
|
27
|
-
from rio_tiler.utils import _validate_shape_input
|
|
25
|
+
from rio_tiler.utils import CRS_to_uri, _validate_shape_input
|
|
28
26
|
|
|
29
27
|
try:
|
|
30
28
|
import xarray
|
|
@@ -44,7 +42,6 @@ class XarrayReader(BaseReader):
|
|
|
44
42
|
Attributes:
|
|
45
43
|
dataset (xarray.DataArray): Xarray DataArray dataset.
|
|
46
44
|
tms (morecantile.TileMatrixSet, optional): TileMatrixSet grid definition. Defaults to `WebMercatorQuad`.
|
|
47
|
-
geographic_crs (rasterio.crs.CRS, optional): CRS to use as geographic coordinate system. Defaults to WGS84.
|
|
48
45
|
|
|
49
46
|
Examples:
|
|
50
47
|
>>> ds = xarray.open_dataset(
|
|
@@ -63,10 +60,6 @@ class XarrayReader(BaseReader):
|
|
|
63
60
|
input: xarray.DataArray = attr.ib()
|
|
64
61
|
|
|
65
62
|
tms: TileMatrixSet = attr.ib(default=WEB_MERCATOR_TMS)
|
|
66
|
-
geographic_crs: CRS = attr.ib(default=WGS84_CRS)
|
|
67
|
-
|
|
68
|
-
_minzoom: int = attr.ib(init=False, default=None)
|
|
69
|
-
_maxzoom: int = attr.ib(init=False, default=None)
|
|
70
63
|
|
|
71
64
|
_dims: List = attr.ib(init=False, factory=list)
|
|
72
65
|
|
|
@@ -84,100 +77,34 @@ class XarrayReader(BaseReader):
|
|
|
84
77
|
)
|
|
85
78
|
|
|
86
79
|
if self.crs == WGS84_CRS and (
|
|
87
|
-
self.bounds[0] < -180
|
|
88
|
-
or self.bounds[1] < -90
|
|
89
|
-
or self.bounds[2] > 180
|
|
90
|
-
or self.bounds[3] > 90
|
|
80
|
+
round(self.bounds[0]) < -180
|
|
81
|
+
or round(self.bounds[1]) < -90
|
|
82
|
+
or round(self.bounds[2]) > 180
|
|
83
|
+
or round(self.bounds[3]) > 90
|
|
91
84
|
):
|
|
92
85
|
raise InvalidGeographicBounds(
|
|
93
86
|
f"Invalid geographic bounds: {self.bounds}. Must be within (-180, -90, 180, 90)."
|
|
94
87
|
)
|
|
95
88
|
|
|
89
|
+
self.transform = self.input.rio.transform()
|
|
90
|
+
self.height = self.input.rio.height
|
|
91
|
+
self.width = self.input.rio.width
|
|
92
|
+
|
|
96
93
|
self._dims = [
|
|
97
94
|
d
|
|
98
95
|
for d in self.input.dims
|
|
99
96
|
if d not in [self.input.rio.x_dim, self.input.rio.y_dim]
|
|
100
97
|
]
|
|
101
98
|
|
|
102
|
-
def _dst_geom_in_tms_crs(self):
|
|
103
|
-
"""Return dataset info in TMS projection."""
|
|
104
|
-
tms_crs = self.tms.rasterio_crs
|
|
105
|
-
if self.crs != tms_crs:
|
|
106
|
-
dst_affine, w, h = calculate_default_transform(
|
|
107
|
-
self.crs,
|
|
108
|
-
tms_crs,
|
|
109
|
-
self.input.rio.width,
|
|
110
|
-
self.input.rio.height,
|
|
111
|
-
*self.bounds,
|
|
112
|
-
)
|
|
113
|
-
else:
|
|
114
|
-
dst_affine = list(self.input.rio.transform())
|
|
115
|
-
w = self.input.rio.width
|
|
116
|
-
h = self.input.rio.height
|
|
117
|
-
|
|
118
|
-
return dst_affine, w, h
|
|
119
|
-
|
|
120
|
-
def get_minzoom(self) -> int:
|
|
121
|
-
"""Define dataset minimum zoom level."""
|
|
122
|
-
if self._minzoom is None:
|
|
123
|
-
# We assume the TMS tilesize to be constant over all matrices
|
|
124
|
-
# ref: https://github.com/OSGeo/gdal/blob/dc38aa64d779ecc45e3cd15b1817b83216cf96b8/gdal/frmts/gtiff/cogdriver.cpp#L274
|
|
125
|
-
tilesize = self.tms.tileMatrices[0].tileWidth
|
|
126
|
-
|
|
127
|
-
try:
|
|
128
|
-
dst_affine, w, h = self._dst_geom_in_tms_crs()
|
|
129
|
-
|
|
130
|
-
# The minzoom is defined by the resolution of the maximum theoretical overview level
|
|
131
|
-
# We assume `tilesize`` is the smallest overview size
|
|
132
|
-
overview_level = get_maximum_overview_level(w, h, minsize=tilesize)
|
|
133
|
-
|
|
134
|
-
# Get the resolution of the overview
|
|
135
|
-
resolution = max(abs(dst_affine[0]), abs(dst_affine[4]))
|
|
136
|
-
ovr_resolution = resolution * (2**overview_level)
|
|
137
|
-
|
|
138
|
-
# Find what TMS matrix match the overview resolution
|
|
139
|
-
self._minzoom = self.tms.zoom_for_res(ovr_resolution)
|
|
140
|
-
|
|
141
|
-
except: # noqa
|
|
142
|
-
# if we can't get min/max zoom from the dataset we default to TMS maxzoom
|
|
143
|
-
warnings.warn(
|
|
144
|
-
"Cannot determine maxzoom based on dataset information, will default to TMS maxzoom.",
|
|
145
|
-
UserWarning,
|
|
146
|
-
)
|
|
147
|
-
self._minzoom = self.tms.maxzoom
|
|
148
|
-
|
|
149
|
-
return self._minzoom
|
|
150
|
-
|
|
151
|
-
def get_maxzoom(self) -> int:
|
|
152
|
-
"""Define dataset maximum zoom level."""
|
|
153
|
-
if self._maxzoom is None:
|
|
154
|
-
try:
|
|
155
|
-
dst_affine, _, _ = self._dst_geom_in_tms_crs()
|
|
156
|
-
|
|
157
|
-
# The maxzoom is defined by finding the minimum difference between
|
|
158
|
-
# the raster resolution and the zoom level resolution
|
|
159
|
-
resolution = max(abs(dst_affine[0]), abs(dst_affine[4]))
|
|
160
|
-
self._maxzoom = self.tms.zoom_for_res(resolution)
|
|
161
|
-
|
|
162
|
-
except: # noqa
|
|
163
|
-
# if we can't get min/max zoom from the dataset we default to TMS maxzoom
|
|
164
|
-
warnings.warn(
|
|
165
|
-
"Cannot determine maxzoom based on dataset information, will default to TMS maxzoom.",
|
|
166
|
-
UserWarning,
|
|
167
|
-
)
|
|
168
|
-
self._maxzoom = self.tms.maxzoom
|
|
169
|
-
|
|
170
|
-
return self._maxzoom
|
|
171
|
-
|
|
172
99
|
@property
|
|
173
100
|
def minzoom(self):
|
|
174
101
|
"""Return dataset minzoom."""
|
|
175
|
-
return self.
|
|
102
|
+
return self._minzoom
|
|
176
103
|
|
|
177
104
|
@property
|
|
178
105
|
def maxzoom(self):
|
|
179
106
|
"""Return dataset maxzoom."""
|
|
180
|
-
return self.
|
|
107
|
+
return self._maxzoom
|
|
181
108
|
|
|
182
109
|
@property
|
|
183
110
|
def band_names(self) -> List[str]:
|
|
@@ -190,9 +117,8 @@ class XarrayReader(BaseReader):
|
|
|
190
117
|
metadata = [band.attrs for d in self._dims for band in self.input[d]]
|
|
191
118
|
|
|
192
119
|
meta = {
|
|
193
|
-
"bounds": self.
|
|
194
|
-
"
|
|
195
|
-
"maxzoom": self.maxzoom,
|
|
120
|
+
"bounds": self.bounds,
|
|
121
|
+
"crs": CRS_to_uri(self.crs),
|
|
196
122
|
"band_metadata": [(f"b{ix}", v) for ix, v in enumerate(metadata, 1)],
|
|
197
123
|
"band_descriptions": [(f"b{ix}", v) for ix, v in enumerate(bands, 1)],
|
|
198
124
|
"dtype": str(self.input.dtype),
|
|
@@ -223,7 +149,6 @@ class XarrayReader(BaseReader):
|
|
|
223
149
|
tile_y: int,
|
|
224
150
|
tile_z: int,
|
|
225
151
|
tilesize: int = 256,
|
|
226
|
-
resampling_method: Optional[WarpResampling] = None,
|
|
227
152
|
reproject_method: WarpResampling = "nearest",
|
|
228
153
|
auto_expand: bool = True,
|
|
229
154
|
nodata: Optional[NoData] = None,
|
|
@@ -235,7 +160,6 @@ class XarrayReader(BaseReader):
|
|
|
235
160
|
tile_y (int): Tile's vertical index.
|
|
236
161
|
tile_z (int): Tile's zoom level index.
|
|
237
162
|
tilesize (int, optional): Output image size. Defaults to `256`.
|
|
238
|
-
resampling_method (WarpResampling, optional): **DEPRECATED**, WarpKernel resampling algorithm. Defaults to `nearest`.
|
|
239
163
|
reproject_method (WarpResampling, optional): WarpKernel resampling algorithm. Defaults to `nearest`.
|
|
240
164
|
auto_expand (boolean, optional): When True, rioxarray's clip_box will expand clip search if only 1D raster found with clip. When False, will throw `OneDimensionalRaster` error if only 1 x or y data point is found. Defaults to True.
|
|
241
165
|
nodata (int or float, optional): Overwrite dataset internal nodata value.
|
|
@@ -244,13 +168,6 @@ class XarrayReader(BaseReader):
|
|
|
244
168
|
rio_tiler.models.ImageData: ImageData instance with data, mask and tile spatial info.
|
|
245
169
|
|
|
246
170
|
"""
|
|
247
|
-
if resampling_method:
|
|
248
|
-
warnings.warn(
|
|
249
|
-
"`resampling_method` is deprecated and will be removed in rio-tiler 7.0, please use `reproject_method`",
|
|
250
|
-
DeprecationWarning,
|
|
251
|
-
)
|
|
252
|
-
reproject_method = resampling_method
|
|
253
|
-
|
|
254
171
|
if not self.tile_exists(tile_x, tile_y, tile_z):
|
|
255
172
|
raise TileOutsideBounds(
|
|
256
173
|
f"Tile(x={tile_x}, y={tile_y}, z={tile_z}) is outside bounds"
|
|
@@ -260,7 +177,7 @@ class XarrayReader(BaseReader):
|
|
|
260
177
|
if nodata is not None:
|
|
261
178
|
ds = ds.rio.write_nodata(nodata)
|
|
262
179
|
|
|
263
|
-
tile_bounds = self.tms.xy_bounds(Tile(x=tile_x, y=tile_y, z=tile_z))
|
|
180
|
+
tile_bounds = tuple(self.tms.xy_bounds(Tile(x=tile_x, y=tile_y, z=tile_z)))
|
|
264
181
|
dst_crs = self.tms.rasterio_crs
|
|
265
182
|
|
|
266
183
|
# Create source array by clipping the xarray dataset to extent of the tile.
|
|
@@ -299,7 +216,6 @@ class XarrayReader(BaseReader):
|
|
|
299
216
|
bbox: BBox,
|
|
300
217
|
dst_crs: Optional[CRS] = None,
|
|
301
218
|
bounds_crs: CRS = WGS84_CRS,
|
|
302
|
-
resampling_method: Optional[WarpResampling] = None,
|
|
303
219
|
reproject_method: WarpResampling = "nearest",
|
|
304
220
|
auto_expand: bool = True,
|
|
305
221
|
nodata: Optional[NoData] = None,
|
|
@@ -310,7 +226,6 @@ class XarrayReader(BaseReader):
|
|
|
310
226
|
bbox (tuple): Output bounds (left, bottom, right, top) in target crs ("dst_crs").
|
|
311
227
|
dst_crs (rasterio.crs.CRS, optional): Overwrite target coordinate reference system.
|
|
312
228
|
bounds_crs (rasterio.crs.CRS, optional): Bounds Coordinate Reference System. Defaults to `epsg:4326`.
|
|
313
|
-
resampling_method (WarpResampling, optional): **DEPRECATED**, WarpKernel resampling algorithm. Defaults to `nearest`.
|
|
314
229
|
reproject_method (WarpResampling, optional): WarpKernel resampling algorithm. Defaults to `nearest`.
|
|
315
230
|
auto_expand (boolean, optional): When True, rioxarray's clip_box will expand clip search if only 1D raster found with clip. When False, will throw `OneDimensionalRaster` error if only 1 x or y data point is found. Defaults to True.
|
|
316
231
|
nodata (int or float, optional): Overwrite dataset internal nodata value.
|
|
@@ -319,13 +234,6 @@ class XarrayReader(BaseReader):
|
|
|
319
234
|
rio_tiler.models.ImageData: ImageData instance with data, mask and input spatial info.
|
|
320
235
|
|
|
321
236
|
"""
|
|
322
|
-
if resampling_method:
|
|
323
|
-
warnings.warn(
|
|
324
|
-
"`resampling_method` is deprecated and will be removed in rio-tiler 7.0, please use `reproject_method`",
|
|
325
|
-
DeprecationWarning,
|
|
326
|
-
)
|
|
327
|
-
reproject_method = resampling_method
|
|
328
|
-
|
|
329
237
|
dst_crs = dst_crs or bounds_crs
|
|
330
238
|
|
|
331
239
|
ds = self.input
|
|
@@ -438,7 +346,6 @@ class XarrayReader(BaseReader):
|
|
|
438
346
|
shape: Dict,
|
|
439
347
|
dst_crs: Optional[CRS] = None,
|
|
440
348
|
shape_crs: CRS = WGS84_CRS,
|
|
441
|
-
resampling_method: Optional[WarpResampling] = None,
|
|
442
349
|
reproject_method: WarpResampling = "nearest",
|
|
443
350
|
nodata: Optional[NoData] = None,
|
|
444
351
|
) -> ImageData:
|
|
@@ -448,7 +355,6 @@ class XarrayReader(BaseReader):
|
|
|
448
355
|
shape (dict): Valid GeoJSON feature.
|
|
449
356
|
dst_crs (rasterio.crs.CRS, optional): Overwrite target coordinate reference system.
|
|
450
357
|
shape_crs (rasterio.crs.CRS, optional): Input geojson coordinate reference system. Defaults to `epsg:4326`.
|
|
451
|
-
resampling_method (WarpResampling, optional): **DEPRECATED**, WarpKernel resampling algorithm. Defaults to `nearest`.
|
|
452
358
|
reproject_method (WarpResampling, optional): WarpKernel resampling algorithm. Defaults to `nearest`.
|
|
453
359
|
nodata (int or float, optional): Overwrite dataset internal nodata value.
|
|
454
360
|
|
|
@@ -456,13 +362,6 @@ class XarrayReader(BaseReader):
|
|
|
456
362
|
rio_tiler.models.ImageData: ImageData instance with data, mask and input spatial info.
|
|
457
363
|
|
|
458
364
|
"""
|
|
459
|
-
if resampling_method:
|
|
460
|
-
warnings.warn(
|
|
461
|
-
"`resampling_method` is deprecated and will be removed in rio-tiler 7.0, please use `reproject_method`",
|
|
462
|
-
DeprecationWarning,
|
|
463
|
-
)
|
|
464
|
-
reproject_method = resampling_method
|
|
465
|
-
|
|
466
365
|
if not dst_crs:
|
|
467
366
|
dst_crs = shape_crs
|
|
468
367
|
|