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/__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 +70 -58
- 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.2.dist-info → rio_tiler-8.0.0.dist-info}/METADATA +12 -36
- {rio_tiler-7.9.2.dist-info → rio_tiler-8.0.0.dist-info}/RECORD +19 -17
- {rio_tiler-7.9.2.dist-info → rio_tiler-8.0.0.dist-info}/WHEEL +0 -0
- {rio_tiler-7.9.2.dist-info → rio_tiler-8.0.0.dist-info}/licenses/AUTHORS.txt +0 -0
- {rio_tiler-7.9.2.dist-info → rio_tiler-8.0.0.dist-info}/licenses/LICENSE +0 -0
rio_tiler/__init__.py
CHANGED
rio_tiler/colormap.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""rio-tiler colormap functions and classes."""
|
|
2
2
|
|
|
3
|
+
import itertools
|
|
3
4
|
import json
|
|
4
5
|
import os
|
|
5
6
|
import pathlib
|
|
@@ -166,9 +167,10 @@ def apply_discrete_cmap(
|
|
|
166
167
|
|
|
167
168
|
data = numpy.transpose(res, [2, 0, 1])
|
|
168
169
|
|
|
169
|
-
# If
|
|
170
|
+
# If colormap values are between 0-255
|
|
170
171
|
# we cast the output array to Uint8
|
|
171
|
-
|
|
172
|
+
cmap_v = list(itertools.chain(*colormap.values()))
|
|
173
|
+
if min(cmap_v) >= 0 and max(cmap_v) <= 255:
|
|
172
174
|
data = data.astype("uint8")
|
|
173
175
|
|
|
174
176
|
return data[:-1], data[-1]
|
|
@@ -206,9 +208,10 @@ def apply_intervals_cmap(
|
|
|
206
208
|
|
|
207
209
|
data = numpy.transpose(res, [2, 0, 1])
|
|
208
210
|
|
|
209
|
-
# If
|
|
211
|
+
# If colormap values are between 0-255
|
|
210
212
|
# we cast the output array to Uint8
|
|
211
|
-
|
|
213
|
+
cmap_v = list(itertools.chain(*[v for k, v in colormap]))
|
|
214
|
+
if min(cmap_v) >= 0 and max(cmap_v) <= 255:
|
|
212
215
|
data = data.astype("uint8")
|
|
213
216
|
|
|
214
217
|
return data[:-1], data[-1]
|
rio_tiler/errors.py
CHANGED
|
@@ -95,3 +95,11 @@ class InvalidGeographicBounds(RioTilerError):
|
|
|
95
95
|
|
|
96
96
|
class RioTilerExperimentalWarning(UserWarning):
|
|
97
97
|
"""A rio-tiler specific experimental functionality warning."""
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class MaxArraySizeError(RioTilerError):
|
|
101
|
+
"""Trying to load to many pixels in memory."""
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class NoAssetFoundError(RioTilerError):
|
|
105
|
+
"""No Asset found"""
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
"""rio_tiler experimental ZarrReaders."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import contextlib
|
|
6
|
+
from functools import cache
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Callable, Dict, List, Literal, Optional, Union
|
|
9
|
+
from urllib.parse import urlparse
|
|
10
|
+
|
|
11
|
+
import attr
|
|
12
|
+
from morecantile import TileMatrixSet
|
|
13
|
+
from rasterio.crs import CRS
|
|
14
|
+
|
|
15
|
+
from rio_tiler.constants import WEB_MERCATOR_TMS, WGS84_CRS
|
|
16
|
+
from rio_tiler.errors import InvalidGeographicBounds
|
|
17
|
+
from rio_tiler.io.base import BaseReader
|
|
18
|
+
from rio_tiler.io.xarray import XarrayReader
|
|
19
|
+
from rio_tiler.models import BandStatistics, ImageData, Info, PointData
|
|
20
|
+
from rio_tiler.types import BBox
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
import obstore
|
|
24
|
+
from zarr.storage import ObjectStore
|
|
25
|
+
except ImportError: # pragma: nocover
|
|
26
|
+
ObjectStore = None # type: ignore
|
|
27
|
+
obstore = None # type: ignore
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
import rioxarray
|
|
31
|
+
import xarray
|
|
32
|
+
except ImportError: # pragma: nocover
|
|
33
|
+
xarray = None # type: ignore
|
|
34
|
+
rioxarray = None # type: ignore
|
|
35
|
+
|
|
36
|
+
sel_methods = Literal["nearest", "pad", "ffill", "backfill", "bfill"]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@cache
|
|
40
|
+
def open_dataset(src_path: str, **kwargs: Any) -> xarray.Dataset:
|
|
41
|
+
"""Open Xarray dataset
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
src_path (str): dataset path.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
xarray.DataTree
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
parsed = urlparse(src_path)
|
|
51
|
+
if not parsed.scheme:
|
|
52
|
+
src_path = str(Path(src_path).resolve())
|
|
53
|
+
src_path = "file://" + src_path
|
|
54
|
+
store = obstore.store.from_url(src_path, **kwargs)
|
|
55
|
+
zarr_store = ObjectStore(store=store, read_only=True)
|
|
56
|
+
ds = xarray.open_dataset(
|
|
57
|
+
zarr_store,
|
|
58
|
+
decode_times=True,
|
|
59
|
+
decode_coords="all",
|
|
60
|
+
consolidated=True,
|
|
61
|
+
engine="zarr",
|
|
62
|
+
)
|
|
63
|
+
return ds
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@attr.s
|
|
67
|
+
class ZarrReader(BaseReader):
|
|
68
|
+
"""Zarr dataset Reader.
|
|
69
|
+
|
|
70
|
+
Attributes:
|
|
71
|
+
input (str): dataset path.
|
|
72
|
+
dataset (xarray.Dataset): Xarray dataset.
|
|
73
|
+
tms (morecantile.TileMatrixSet, optional): TileMatrixSet grid definition. Defaults to `WebMercatorQuad`.
|
|
74
|
+
opener (Callable): Xarray dataset opener. Defaults to `open_dataset`.
|
|
75
|
+
opener_options (dict): Options to forward to the opener callable.
|
|
76
|
+
|
|
77
|
+
Examples:
|
|
78
|
+
>>> with ZarrReader(
|
|
79
|
+
"s3://mur-sst/zarr-v1",
|
|
80
|
+
opener_options={
|
|
81
|
+
"skip_signature": True,
|
|
82
|
+
"region": "us-west-2",
|
|
83
|
+
}
|
|
84
|
+
) as src:
|
|
85
|
+
print(src)
|
|
86
|
+
print(src.variables)
|
|
87
|
+
img = src.tile(x, y, z, tmax)
|
|
88
|
+
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
input: str = attr.ib()
|
|
92
|
+
dataset: xarray.Dataset = attr.ib(default=None)
|
|
93
|
+
|
|
94
|
+
tms: TileMatrixSet = attr.ib(default=WEB_MERCATOR_TMS)
|
|
95
|
+
|
|
96
|
+
opener: Callable[..., xarray.Dataset] = attr.ib(default=open_dataset)
|
|
97
|
+
opener_options: Dict = attr.ib(factory=dict)
|
|
98
|
+
_ctx_stack: contextlib.ExitStack = attr.ib(init=False, factory=contextlib.ExitStack)
|
|
99
|
+
|
|
100
|
+
def __attrs_post_init__(self):
|
|
101
|
+
"""Set bounds and CRS."""
|
|
102
|
+
assert xarray is not None, "xarray must be installed to use XarrayReader"
|
|
103
|
+
assert rioxarray is not None, "rioxarray must be installed to use XarrayReader"
|
|
104
|
+
|
|
105
|
+
if not self.dataset:
|
|
106
|
+
self.dataset = self._ctx_stack.enter_context(
|
|
107
|
+
self.opener(self.input, **self.opener_options)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# NOTE: rioxarray returns **ordered** bounds in form of (minx, miny, maxx, maxx)
|
|
111
|
+
self.bounds = tuple(self.dataset.rio.bounds())
|
|
112
|
+
# Make sure we have a valid CRS
|
|
113
|
+
self.dataset = self.dataset.rio.write_crs(
|
|
114
|
+
self.dataset.rio.crs or "epsg:4326",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
self.crs = self.dataset.rio.crs
|
|
118
|
+
|
|
119
|
+
# adds half x/y resolution on each values
|
|
120
|
+
# https://github.com/corteva/rioxarray/issues/645#issuecomment-1461070634
|
|
121
|
+
xres, yres = map(abs, self.dataset.rio.resolution())
|
|
122
|
+
if self.crs == WGS84_CRS and (
|
|
123
|
+
self.bounds[0] + xres / 2 < -180
|
|
124
|
+
or self.bounds[1] + yres / 2 < -90
|
|
125
|
+
or self.bounds[2] - xres / 2 > 180
|
|
126
|
+
or self.bounds[3] - yres / 2 > 90
|
|
127
|
+
):
|
|
128
|
+
raise InvalidGeographicBounds(
|
|
129
|
+
f"Invalid geographic bounds: {self.bounds}. Must be within (-180, -90, 180, 90)."
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
self.transform = self.dataset.rio.transform()
|
|
133
|
+
self.height = self.dataset.rio.height
|
|
134
|
+
self.width = self.dataset.rio.width
|
|
135
|
+
|
|
136
|
+
def close(self):
|
|
137
|
+
"""Close xarray dataset."""
|
|
138
|
+
self._ctx_stack.close()
|
|
139
|
+
|
|
140
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
141
|
+
"""Support using with Context Managers."""
|
|
142
|
+
self.close()
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def variables(self) -> List[str]:
|
|
146
|
+
"""Return dataset variable names"""
|
|
147
|
+
return list(self.dataset.data_vars)
|
|
148
|
+
|
|
149
|
+
def _arrange_dims(self, da: xarray.DataArray) -> xarray.DataArray:
|
|
150
|
+
"""Arrange coordinates and time dimensions.
|
|
151
|
+
|
|
152
|
+
An rioxarray.exceptions.InvalidDimensionOrder error is raised if the coordinates are not in the correct order time, y, and x.
|
|
153
|
+
See: https://github.com/corteva/rioxarray/discussions/674
|
|
154
|
+
|
|
155
|
+
We conform to using x and y as the spatial dimension names..
|
|
156
|
+
|
|
157
|
+
"""
|
|
158
|
+
if "x" not in da.dims and "y" not in da.dims:
|
|
159
|
+
try:
|
|
160
|
+
latitude_var_name = next(
|
|
161
|
+
name
|
|
162
|
+
for name in ["lat", "latitude", "LAT", "LATITUDE", "Lat"]
|
|
163
|
+
if name in da.dims
|
|
164
|
+
)
|
|
165
|
+
longitude_var_name = next(
|
|
166
|
+
name
|
|
167
|
+
for name in ["lon", "longitude", "LON", "LONGITUDE", "Lon"]
|
|
168
|
+
if name in da.dims
|
|
169
|
+
)
|
|
170
|
+
except StopIteration as e:
|
|
171
|
+
raise ValueError(f"Couldn't find X/Y dimensions in {da.dims}") from e
|
|
172
|
+
|
|
173
|
+
da = da.rename({latitude_var_name: "y", longitude_var_name: "x"})
|
|
174
|
+
|
|
175
|
+
if "TIME" in da.dims:
|
|
176
|
+
da = da.rename({"TIME": "time"})
|
|
177
|
+
|
|
178
|
+
if extra_dims := [d for d in da.dims if d not in ["x", "y"]]:
|
|
179
|
+
da = da.transpose(*extra_dims, "y", "x")
|
|
180
|
+
else:
|
|
181
|
+
da = da.transpose("y", "x")
|
|
182
|
+
|
|
183
|
+
# If min/max values are stored in `valid_range` we add them in `valid_min/valid_max`
|
|
184
|
+
vmin, vmax = da.attrs.get("valid_min"), da.attrs.get("valid_max")
|
|
185
|
+
if "valid_range" in da.attrs and not (vmin is not None and vmax is not None):
|
|
186
|
+
valid_range = da.attrs.get("valid_range")
|
|
187
|
+
da.attrs.update({"valid_min": valid_range[0], "valid_max": valid_range[1]})
|
|
188
|
+
|
|
189
|
+
return da
|
|
190
|
+
|
|
191
|
+
def _get_variable(
|
|
192
|
+
self,
|
|
193
|
+
variable: str,
|
|
194
|
+
sel: Optional[List[str]] = None,
|
|
195
|
+
method: Optional[sel_methods] = None,
|
|
196
|
+
) -> xarray.DataArray:
|
|
197
|
+
"""Get DataArray from xarray Dataset."""
|
|
198
|
+
da = self.dataset[variable]
|
|
199
|
+
|
|
200
|
+
if sel:
|
|
201
|
+
_idx: Dict[str, List] = {}
|
|
202
|
+
for s in sel:
|
|
203
|
+
val: Union[str, slice]
|
|
204
|
+
dim, val = s.split("=")
|
|
205
|
+
|
|
206
|
+
# cast string to dtype of the dimension
|
|
207
|
+
if da[dim].dtype != "O":
|
|
208
|
+
val = da[dim].dtype.type(val)
|
|
209
|
+
|
|
210
|
+
if dim in _idx:
|
|
211
|
+
_idx[dim].append(val)
|
|
212
|
+
else:
|
|
213
|
+
_idx[dim] = [val]
|
|
214
|
+
|
|
215
|
+
sel_idx = {k: v[0] if len(v) < 2 else v for k, v in _idx.items()}
|
|
216
|
+
da = da.sel(sel_idx, method=method)
|
|
217
|
+
|
|
218
|
+
da = self._arrange_dims(da)
|
|
219
|
+
|
|
220
|
+
# Make sure we have a valid CRS
|
|
221
|
+
crs = da.rio.crs or "epsg:4326"
|
|
222
|
+
da = da.rio.write_crs(crs)
|
|
223
|
+
|
|
224
|
+
if crs == "epsg:4326" and (da.x > 180).any():
|
|
225
|
+
# Adjust the longitude coordinates to the -180 to 180 range
|
|
226
|
+
da = da.assign_coords(x=(da.x + 180) % 360 - 180)
|
|
227
|
+
|
|
228
|
+
# Sort the dataset by the updated longitude coordinates
|
|
229
|
+
da = da.sortby(da.x)
|
|
230
|
+
|
|
231
|
+
assert len(da.dims) in [
|
|
232
|
+
2,
|
|
233
|
+
3,
|
|
234
|
+
], "rio_tiler.io.xarray.DatasetReader can only work with 2D or 3D DataArray"
|
|
235
|
+
|
|
236
|
+
return da
|
|
237
|
+
|
|
238
|
+
def spatial_info( # type: ignore
|
|
239
|
+
self,
|
|
240
|
+
*,
|
|
241
|
+
variable: str,
|
|
242
|
+
sel: Optional[List[str]] = None,
|
|
243
|
+
method: Optional[sel_methods] = None,
|
|
244
|
+
):
|
|
245
|
+
"""Return xarray.DataArray info."""
|
|
246
|
+
with XarrayReader(
|
|
247
|
+
self._get_variable(variable, sel=sel, method=method),
|
|
248
|
+
) as da:
|
|
249
|
+
return {
|
|
250
|
+
"crs": da.crs,
|
|
251
|
+
"bounds": da.bounds,
|
|
252
|
+
"minzoom": da.minzoom,
|
|
253
|
+
"maxzoom": da.maxzoom,
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
def get_geographic_bounds( # type: ignore
|
|
257
|
+
self,
|
|
258
|
+
crs: CRS,
|
|
259
|
+
*,
|
|
260
|
+
variable: str,
|
|
261
|
+
sel: Optional[List[str]] = None,
|
|
262
|
+
method: Optional[sel_methods] = None,
|
|
263
|
+
) -> BBox:
|
|
264
|
+
"""Return Geographic Bounds for a Geographic CRS."""
|
|
265
|
+
with XarrayReader(
|
|
266
|
+
self._get_variable(variable, sel=sel, method=method),
|
|
267
|
+
) as da:
|
|
268
|
+
return da.get_geographic_bounds(crs)
|
|
269
|
+
|
|
270
|
+
def info( # type: ignore
|
|
271
|
+
self,
|
|
272
|
+
*,
|
|
273
|
+
variable: str,
|
|
274
|
+
sel: Optional[List[str]] = None,
|
|
275
|
+
method: Optional[sel_methods] = None,
|
|
276
|
+
) -> Info:
|
|
277
|
+
"""Return xarray.DataArray info."""
|
|
278
|
+
with XarrayReader(
|
|
279
|
+
self._get_variable(variable, sel=sel, method=method),
|
|
280
|
+
) as da:
|
|
281
|
+
return da.info()
|
|
282
|
+
|
|
283
|
+
def statistics( # type: ignore
|
|
284
|
+
self,
|
|
285
|
+
*args: Any,
|
|
286
|
+
variable: str,
|
|
287
|
+
sel: Optional[List[str]] = None,
|
|
288
|
+
method: Optional[sel_methods] = None,
|
|
289
|
+
**kwargs: Any,
|
|
290
|
+
) -> Dict[str, BandStatistics]:
|
|
291
|
+
"""Return statistics from a dataset."""
|
|
292
|
+
with XarrayReader(
|
|
293
|
+
self._get_variable(variable, sel=sel, method=method),
|
|
294
|
+
) as da:
|
|
295
|
+
return da.statistics(*args, **kwargs)
|
|
296
|
+
|
|
297
|
+
def tile( # type: ignore
|
|
298
|
+
self,
|
|
299
|
+
*args: Any,
|
|
300
|
+
variable: str,
|
|
301
|
+
sel: Optional[List[str]] = None,
|
|
302
|
+
method: Optional[sel_methods] = None,
|
|
303
|
+
**kwargs: Any,
|
|
304
|
+
) -> ImageData:
|
|
305
|
+
"""Read a Web Map tile from a dataset."""
|
|
306
|
+
with XarrayReader(
|
|
307
|
+
self._get_variable(variable, sel=sel, method=method),
|
|
308
|
+
tms=self.tms,
|
|
309
|
+
) as da:
|
|
310
|
+
return da.tile(*args, **kwargs)
|
|
311
|
+
|
|
312
|
+
def part( # type: ignore
|
|
313
|
+
self,
|
|
314
|
+
*args: Any,
|
|
315
|
+
variable: str,
|
|
316
|
+
sel: Optional[List[str]] = None,
|
|
317
|
+
method: Optional[sel_methods] = None,
|
|
318
|
+
**kwargs: Any,
|
|
319
|
+
) -> ImageData:
|
|
320
|
+
"""Read part of a dataset."""
|
|
321
|
+
with XarrayReader(
|
|
322
|
+
self._get_variable(variable, sel=sel, method=method),
|
|
323
|
+
) as da:
|
|
324
|
+
return da.part(*args, **kwargs)
|
|
325
|
+
|
|
326
|
+
def preview( # type: ignore
|
|
327
|
+
self,
|
|
328
|
+
*args: Any,
|
|
329
|
+
variable: str,
|
|
330
|
+
sel: Optional[List[str]] = None,
|
|
331
|
+
method: Optional[sel_methods] = None,
|
|
332
|
+
**kwargs: Any,
|
|
333
|
+
) -> ImageData:
|
|
334
|
+
"""Return a preview of a dataset."""
|
|
335
|
+
with XarrayReader(
|
|
336
|
+
self._get_variable(variable, sel=sel, method=method),
|
|
337
|
+
) as da:
|
|
338
|
+
return da.preview(*args, **kwargs)
|
|
339
|
+
|
|
340
|
+
def point( # type: ignore
|
|
341
|
+
self,
|
|
342
|
+
*args: Any,
|
|
343
|
+
variable: str,
|
|
344
|
+
sel: Optional[List[str]] = None,
|
|
345
|
+
method: Optional[sel_methods] = None,
|
|
346
|
+
**kwargs: Any,
|
|
347
|
+
) -> PointData:
|
|
348
|
+
"""Read a pixel value from a dataset."""
|
|
349
|
+
with XarrayReader(
|
|
350
|
+
self._get_variable(variable, sel=sel, method=method),
|
|
351
|
+
) as da:
|
|
352
|
+
return da.point(*args, **kwargs)
|
|
353
|
+
|
|
354
|
+
def feature( # type: ignore
|
|
355
|
+
self,
|
|
356
|
+
*args: Any,
|
|
357
|
+
variable: str,
|
|
358
|
+
sel: Optional[List[str]] = None,
|
|
359
|
+
method: Optional[sel_methods] = None,
|
|
360
|
+
**kwargs: Any,
|
|
361
|
+
) -> ImageData:
|
|
362
|
+
"""Read part of a dataset defined by a geojson feature."""
|
|
363
|
+
with XarrayReader(
|
|
364
|
+
self._get_variable(variable, sel=sel, method=method),
|
|
365
|
+
) as da:
|
|
366
|
+
return da.feature(*args, **kwargs)
|
rio_tiler/expression.py
CHANGED
|
@@ -10,7 +10,7 @@ from rio_tiler.errors import InvalidExpression
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def parse_expression(expression: str, cast: bool = True) -> Tuple:
|
|
13
|
-
"""Parse rio-tiler band math expression.
|
|
13
|
+
"""Parse rio-tiler band math expression and extract bands.
|
|
14
14
|
|
|
15
15
|
Args:
|
|
16
16
|
expression (str): band math/combination expression.
|
|
@@ -20,11 +20,11 @@ def parse_expression(expression: str, cast: bool = True) -> Tuple:
|
|
|
20
20
|
tuple: band names/indexes.
|
|
21
21
|
|
|
22
22
|
Examples:
|
|
23
|
-
>>> parse_expression("b1
|
|
24
|
-
(
|
|
23
|
+
>>> parse_expression("b1+b2")
|
|
24
|
+
(1, 2)
|
|
25
25
|
|
|
26
26
|
>>> parse_expression("B1/B2", cast=False)
|
|
27
|
-
(
|
|
27
|
+
('1', '2')
|
|
28
28
|
|
|
29
29
|
"""
|
|
30
30
|
bands = set(re.findall(r"\bb(?P<bands>[0-9A-Z]+)\b", expression, re.IGNORECASE))
|
|
@@ -47,8 +47,8 @@ def get_expression_blocks(expression: str) -> List[str]:
|
|
|
47
47
|
list: expression blocks (str).
|
|
48
48
|
|
|
49
49
|
Examples:
|
|
50
|
-
>>>
|
|
51
|
-
|
|
50
|
+
>>> get_expression_blocks("b1/b2;b2+b1")
|
|
51
|
+
['b1/b2', 'b2+b1']
|
|
52
52
|
|
|
53
53
|
"""
|
|
54
54
|
return [expr for expr in expression.split(";") if expr]
|
rio_tiler/io/base.py
CHANGED
|
@@ -620,8 +620,12 @@ class MultiBaseReader(SpatialMixin, metaclass=abc.ABCMeta):
|
|
|
620
620
|
"Can't use `asset_as_band` for multibands asset"
|
|
621
621
|
)
|
|
622
622
|
data.band_names = [asset]
|
|
623
|
+
data.band_descriptions = [asset]
|
|
623
624
|
else:
|
|
624
625
|
data.band_names = [f"{asset}_{n}" for n in data.band_names]
|
|
626
|
+
data.band_descriptions = [
|
|
627
|
+
f"{asset}_{n}" for n in data.band_descriptions
|
|
628
|
+
]
|
|
625
629
|
|
|
626
630
|
return data
|
|
627
631
|
|
|
@@ -711,8 +715,12 @@ class MultiBaseReader(SpatialMixin, metaclass=abc.ABCMeta):
|
|
|
711
715
|
"Can't use `asset_as_band` for multibands asset"
|
|
712
716
|
)
|
|
713
717
|
data.band_names = [asset]
|
|
718
|
+
data.band_descriptions = [asset]
|
|
714
719
|
else:
|
|
715
720
|
data.band_names = [f"{asset}_{n}" for n in data.band_names]
|
|
721
|
+
data.band_descriptions = [
|
|
722
|
+
f"{asset}_{n}" for n in data.band_descriptions
|
|
723
|
+
]
|
|
716
724
|
|
|
717
725
|
return data
|
|
718
726
|
|
|
@@ -800,8 +808,12 @@ class MultiBaseReader(SpatialMixin, metaclass=abc.ABCMeta):
|
|
|
800
808
|
"Can't use `asset_as_band` for multibands asset"
|
|
801
809
|
)
|
|
802
810
|
data.band_names = [asset]
|
|
811
|
+
data.band_descriptions = [asset]
|
|
803
812
|
else:
|
|
804
813
|
data.band_names = [f"{asset}_{n}" for n in data.band_names]
|
|
814
|
+
data.band_descriptions = [
|
|
815
|
+
f"{asset}_{n}" for n in data.band_descriptions
|
|
816
|
+
]
|
|
805
817
|
|
|
806
818
|
return data
|
|
807
819
|
|
|
@@ -887,8 +899,12 @@ class MultiBaseReader(SpatialMixin, metaclass=abc.ABCMeta):
|
|
|
887
899
|
"Can't use `asset_as_band` for multibands asset"
|
|
888
900
|
)
|
|
889
901
|
data.band_names = [asset]
|
|
902
|
+
data.band_descriptions = [asset]
|
|
890
903
|
else:
|
|
891
904
|
data.band_names = [f"{asset}_{n}" for n in data.band_names]
|
|
905
|
+
data.band_descriptions = [
|
|
906
|
+
f"{asset}_{n}" for n in data.band_descriptions
|
|
907
|
+
]
|
|
892
908
|
|
|
893
909
|
return data
|
|
894
910
|
|
|
@@ -978,8 +994,12 @@ class MultiBaseReader(SpatialMixin, metaclass=abc.ABCMeta):
|
|
|
978
994
|
"Can't use `asset_as_band` for multibands asset"
|
|
979
995
|
)
|
|
980
996
|
data.band_names = [asset]
|
|
997
|
+
data.band_descriptions = [asset]
|
|
981
998
|
else:
|
|
982
999
|
data.band_names = [f"{asset}_{n}" for n in data.band_names]
|
|
1000
|
+
data.band_descriptions = [
|
|
1001
|
+
f"{asset}_{n}" for n in data.band_descriptions
|
|
1002
|
+
]
|
|
983
1003
|
|
|
984
1004
|
return data
|
|
985
1005
|
|
rio_tiler/io/rasterio.py
CHANGED
|
@@ -637,7 +637,6 @@ class ImageReader(Reader):
|
|
|
637
637
|
tilesize: int = 256,
|
|
638
638
|
indexes: Optional[Indexes] = None,
|
|
639
639
|
expression: Optional[str] = None,
|
|
640
|
-
force_binary_mask: bool = True,
|
|
641
640
|
out_dtype: Optional[Union[str, numpy.dtype]] = None,
|
|
642
641
|
resampling_method: RIOResampling = "nearest",
|
|
643
642
|
unscale: bool = False,
|
|
@@ -654,7 +653,6 @@ class ImageReader(Reader):
|
|
|
654
653
|
tilesize (int, optional): Output image size. Defaults to `256`.
|
|
655
654
|
indexes (int or sequence of int, optional): Band indexes.
|
|
656
655
|
expression (str, optional): rio-tiler expression (e.g. b1/b2+b3).
|
|
657
|
-
force_binary_mask (bool, optional): Cast returned mask to binary values (0 or 255). Defaults to `True`.
|
|
658
656
|
resampling_method (RIOResampling, optional): RasterIO resampling algorithm. Defaults to `nearest`.
|
|
659
657
|
unscale (bool, optional): Apply 'scales' and 'offsets' on output data value. Defaults to `False`.
|
|
660
658
|
post_process (callable, optional): Function to apply on output data and mask values.
|
|
@@ -675,7 +673,6 @@ class ImageReader(Reader):
|
|
|
675
673
|
max_size=None,
|
|
676
674
|
indexes=indexes,
|
|
677
675
|
expression=expression,
|
|
678
|
-
force_binary_mask=force_binary_mask,
|
|
679
676
|
out_dtype=out_dtype,
|
|
680
677
|
resampling_method=resampling_method,
|
|
681
678
|
unscale=unscale,
|
|
@@ -690,7 +687,6 @@ class ImageReader(Reader):
|
|
|
690
687
|
max_size: Optional[int] = None,
|
|
691
688
|
height: Optional[int] = None,
|
|
692
689
|
width: Optional[int] = None,
|
|
693
|
-
force_binary_mask: bool = True,
|
|
694
690
|
out_dtype: Optional[Union[str, numpy.dtype]] = None,
|
|
695
691
|
resampling_method: RIOResampling = "nearest",
|
|
696
692
|
unscale: bool = False,
|
|
@@ -707,7 +703,6 @@ class ImageReader(Reader):
|
|
|
707
703
|
max_size (int, optional): Limit the size of the longest dimension of the dataset read, respecting bounds X/Y aspect ratio.
|
|
708
704
|
height (int, optional): Output height of the array.
|
|
709
705
|
width (int, optional): Output width of the array.
|
|
710
|
-
force_binary_mask (bool, optional): Cast returned mask to binary values (0 or 255). Defaults to `True`.
|
|
711
706
|
resampling_method (RIOResampling, optional): RasterIO resampling algorithm. Defaults to `nearest`.
|
|
712
707
|
unscale (bool, optional): Apply 'scales' and 'offsets' on output data value. Defaults to `False`.
|
|
713
708
|
post_process (callable, optional): Function to apply on output data and mask values.
|
|
@@ -733,7 +728,6 @@ class ImageReader(Reader):
|
|
|
733
728
|
width=width,
|
|
734
729
|
height=height,
|
|
735
730
|
indexes=indexes,
|
|
736
|
-
force_binary_mask=force_binary_mask,
|
|
737
731
|
out_dtype=out_dtype,
|
|
738
732
|
resampling_method=resampling_method,
|
|
739
733
|
unscale=unscale,
|
|
@@ -793,6 +787,7 @@ class ImageReader(Reader):
|
|
|
793
787
|
coordinates=self.dataset.xy(x, y),
|
|
794
788
|
crs=self.dataset.crs,
|
|
795
789
|
band_names=img.band_names,
|
|
790
|
+
band_descriptions=img.band_descriptions,
|
|
796
791
|
pixel_location=(x, y),
|
|
797
792
|
)
|
|
798
793
|
|
|
@@ -804,7 +799,6 @@ class ImageReader(Reader):
|
|
|
804
799
|
max_size: Optional[int] = None,
|
|
805
800
|
height: Optional[int] = None,
|
|
806
801
|
width: Optional[int] = None,
|
|
807
|
-
force_binary_mask: bool = True,
|
|
808
802
|
out_dtype: Optional[Union[str, numpy.dtype]] = None,
|
|
809
803
|
resampling_method: RIOResampling = "nearest",
|
|
810
804
|
unscale: bool = False,
|
|
@@ -824,7 +818,6 @@ class ImageReader(Reader):
|
|
|
824
818
|
max_size=max_size,
|
|
825
819
|
height=height,
|
|
826
820
|
width=width,
|
|
827
|
-
force_binary_mask=force_binary_mask,
|
|
828
821
|
out_dtype=out_dtype,
|
|
829
822
|
resampling_method=resampling_method,
|
|
830
823
|
unscale=unscale,
|