Skip to content

Commit 2bb09b3

Browse files
Feature/refactor band names (#824)
* revert band-name change and add band-descriptions * update docs
1 parent deb5f3b commit 2bb09b3

File tree

12 files changed

+226
-90
lines changed

12 files changed

+226
-90
lines changed

CHANGES.md

Lines changed: 55 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,80 @@
33

44
# 8.0.0
55

6-
* use **description** as band name (instead of `b{ix}`) in `Reader`'s outputs **breaking change**
6+
* add **band_descriptions** in ImageData and PointData objects
77

88
```python
9-
# before
109
with Reader("tests/fixtures/cog_tags.tif") as src:
1110
info = src.info()
1211
img = src.preview()
13-
stats = src.statistics()
1412

1513
print(info.band_descriptions)
1614
>> [('b1', 'Green')]
1715

1816
print(img.band_names)
1917
>> ['b1']
2018

21-
print(list(stats))
22-
>> ['b1']
23-
24-
# now
25-
with Reader("tests/fixtures/cog_tags.tif") as src:
26-
info = src.info()
27-
img = src.preview()
28-
stats = src.statistics()
29-
30-
print(info.band_descriptions)
19+
print(img.band_descriptions)
3120
>> [('b1', 'Green')]
21+
```
3222

33-
print(img.band_names)
34-
>> ['Green']
35-
36-
print(list(stats))
37-
>> ['Green']
38-
39-
# Band without description
40-
with Reader("tests/fixtures/cog.tif") as src:
41-
info = src.info()
42-
stats = src.statistics()
43-
img = src.preview()
23+
* use `b{idx}` as band names in `XarrayReader` result (instead of band descriptions) **breaking change**
4424

45-
print(info.band_descriptions)
46-
>> [('b1', '')]
25+
```python
26+
arr = numpy.arange(0.0, 33 * 35 * 2, dtype="float32").reshape(2, 33, 35)
27+
data = xarray.DataArray(
28+
arr,
29+
dims=("time", "y", "x"),
30+
coords={
31+
"x": numpy.arange(-170, 180, 10),
32+
"y": numpy.arange(-80, 85, 5),
33+
"time": [datetime(2022, 1, 1), datetime(2022, 1, 2)],
34+
},
35+
)
36+
data.attrs.update({"valid_min": arr.min(), "valid_max": arr.max()})
37+
data.rio.write_crs("epsg:4326", inplace=True)
38+
39+
# Before
40+
with XarrayReader(data) as dst:
41+
assert info.band_metadata == [("b1", {}), ("b2", {})]
42+
assert info.band_descriptions == [
43+
("b1", "2022-01-01T00:00:00.000000000"),
44+
("b2", "2022-01-02T00:00:00.000000000"),
45+
]
46+
47+
stats = dst.statistics()
48+
assert list(stats) == [
49+
"2022-01-01T00:00:00.000000000",
50+
"2022-01-02T00:00:00.000000000",
51+
]
52+
53+
img = dst.tile(0, 0, 0)
54+
assert img.band_names == [
55+
"2022-01-01T00:00:00.000000000",
56+
"2022-01-02T00:00:00.000000000",
57+
]
4758

48-
print(list(stats))
49-
>> ['b1']
59+
# Now
60+
with XarrayReader(data) as dst:
61+
stats = dst.statistics()
62+
assert list(stats) == [
63+
"b1",
64+
"b2",
65+
]
5066

51-
print(list(stats))
52-
>> ['b1']
67+
img = dst.tile(0, 0, 0)
68+
assert img.band_names == [
69+
"b1",
70+
"b2",
71+
]
72+
assert img.band_descriptions == [
73+
"2022-01-01T00:00:00.000000000",
74+
"2022-01-02T00:00:00.000000000",
75+
]
5376
```
5477

78+
* rename `XarrayReader.band_names` -> `XarrayReader.band_descriptions` **breaking change**
79+
5580
* only cast data to `uint8` if colormap values are of type `uint8`
5681

5782
* add `alpha_mask` attribute to `ImageData` class

docs/src/models.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ This class has helper methods like `render` which forward internal data and mask
1212
- **bounds**: bounds of the data ([rasterio.coords.BoundingBox](https://github.com/rasterio/rasterio/blob/main/rasterio/coords.py#L8), optional)
1313
- **crs**: coordinate reference system for the data ([rasterio.crs.CRS](https://github.com/rasterio/rasterio/blob/main/rasterio/crs.py#L21), optional)
1414
- **metadata**: additional metadata (dict, optional)
15-
- **band_names**: image band's names
15+
- **band_names**: image band's names (e.g `["b1", "b2"]`)
16+
- **band_descriptions**: image band's descriptions (e.g `["Green", "Red"]`)
1617
- **dataset_statistics**: Dataset's min/max values (list of (min,max), optional)
1718
- **cutline_mask**: array representing the mask for `feature` methods
1819
- **alpha_nask**: array reprsenting the alpha mask (allowing partial transparency)
@@ -353,7 +354,8 @@ Note: Starting with `rio-tiler==2.1`, when the output datatype is not valid for
353354
- **coordinates**: Coordinates of the point (Tuple[float, float], optional)
354355
- **crs**: coordinate reference system for the data ([rasterio.crs.CRS](https://github.com/rasterio/rasterio/blob/master/rasterio/crs.py#L21), optional)
355356
- **metadata**: additional metadata (dict, optional)
356-
- **band_names**: values band's names
357+
- **band_names**: image band's names (e.g `["b1", "b2"]`)
358+
- **band_descriptions**: image band's descriptions (e.g `["Green", "Red"]`)
357359
- **pixel_location**: X, Y coordinates in raster space (Tuple[float, float], optional)
358360

359361
```python

docs/src/readers.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,10 +1209,10 @@ EPSG:4326
12091209
# stats will be in form or {"band": BandStatistics(), ...}
12101210
print(stats)
12111211
>>> {
1212-
'2022-01-01T00:00:00.000000000': BandStatistics(...),
1212+
'b1': BandStatistics(...),
12131213
}
12141214

1215-
print(stats["2022-01-01T00:00:00.000000000"].model_dump())
1215+
print(stats["b1"].model_dump())
12161216
>>> {
12171217
"min": 1,
12181218
"max": 7872,

rio_tiler/io/base.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,8 +620,12 @@ def _reader(asset: str, *args: Any, **kwargs: Any) -> ImageData:
620620
"Can't use `asset_as_band` for multibands asset"
621621
)
622622
data.band_names = [asset]
623+
data.band_descriptions = [asset]
623624
else:
624625
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+
]
625629

626630
return data
627631

@@ -711,8 +715,12 @@ def _reader(asset: str, *args: Any, **kwargs: Any) -> ImageData:
711715
"Can't use `asset_as_band` for multibands asset"
712716
)
713717
data.band_names = [asset]
718+
data.band_descriptions = [asset]
714719
else:
715720
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+
]
716724

717725
return data
718726

@@ -800,8 +808,12 @@ def _reader(asset: str, **kwargs: Any) -> ImageData:
800808
"Can't use `asset_as_band` for multibands asset"
801809
)
802810
data.band_names = [asset]
811+
data.band_descriptions = [asset]
803812
else:
804813
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+
]
805817

806818
return data
807819

@@ -887,8 +899,12 @@ def _reader(asset: str, *args: Any, **kwargs: Any) -> PointData:
887899
"Can't use `asset_as_band` for multibands asset"
888900
)
889901
data.band_names = [asset]
902+
data.band_descriptions = [asset]
890903
else:
891904
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+
]
892908

893909
return data
894910

@@ -978,8 +994,12 @@ def _reader(asset: str, *args: Any, **kwargs: Any) -> ImageData:
978994
"Can't use `asset_as_band` for multibands asset"
979995
)
980996
data.band_names = [asset]
997+
data.band_descriptions = [asset]
981998
else:
982999
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+
]
9831003

9841004
return data
9851005

rio_tiler/io/rasterio.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,7 @@ def point( # type: ignore
787787
coordinates=self.dataset.xy(x, y),
788788
crs=self.dataset.crs,
789789
band_names=img.band_names,
790+
band_descriptions=img.band_descriptions,
790791
pixel_location=(x, y),
791792
)
792793

rio_tiler/io/xarray.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def maxzoom(self):
130130
return self._maxzoom
131131

132132
@property
133-
def band_names(self) -> List[str]:
133+
def band_descriptions(self) -> List[str]:
134134
"""
135135
Return list of `band descriptions` in DataArray.
136136
@@ -152,7 +152,7 @@ def band_names(self) -> List[str]:
152152
if coords_name:
153153
return [str(self.input.coords[coords_name[0]].data)]
154154

155-
return [self.input.name or "array"]
155+
return [self.input.name or ""]
156156

157157
return [str(band) for d in self._dims for band in self.input[d].values]
158158

@@ -165,7 +165,7 @@ def info(self) -> Info:
165165
"crs": CRS_to_uri(self.crs) or self.crs.to_wkt(),
166166
"band_metadata": [(f"b{ix}", v) for ix, v in enumerate(metadata, 1)],
167167
"band_descriptions": [
168-
(f"b{ix}", v) for ix, v in enumerate(self.band_names, 1)
168+
(f"b{ix}", v) for ix, v in enumerate(self.band_descriptions, 1)
169169
],
170170
"dtype": str(self.input.dtype),
171171
"nodata_type": "Nodata" if self.input.rio.nodata is not None else "None",
@@ -184,10 +184,12 @@ def info(self) -> Info:
184184

185185
def _sel_indexes(
186186
self, indexes: Optional[Indexes] = None
187-
) -> Tuple[xarray.DataArray, List[str]]:
187+
) -> Tuple[xarray.DataArray, List[str], List[str]]:
188188
"""Select `band` indexes in DataArray."""
189189
da = self.input
190-
band_names = self.band_names
190+
band_descriptions = self.band_descriptions
191+
band_names = [f"b{ix + 1}" for ix in range(self.input.rio.count)]
192+
191193
if indexes := cast_to_sequence(indexes):
192194
assert all(v > 0 for v in indexes), "Indexes value must be >= 1"
193195
if da.ndim == 2:
@@ -196,13 +198,15 @@ def _sel_indexes(
196198
f"Invalid indexes {indexes} for array of shape {da.shape}"
197199
)
198200

199-
return da, band_names
201+
return da, band_names, band_descriptions
200202

201203
indexes = [idx - 1 for idx in indexes]
204+
202205
da = da[indexes]
203-
band_names = [self.band_names[idx] for idx in indexes]
206+
band_descriptions = [band_descriptions[idx] for idx in indexes]
207+
band_names = [band_names[idx] for idx in indexes]
204208

205-
return da, band_names
209+
return da, band_names, band_descriptions
206210

207211
def statistics(
208212
self,
@@ -217,7 +221,7 @@ def statistics(
217221
"""Return statistics from a dataset."""
218222
hist_options = hist_options or {}
219223

220-
da, band_names = self._sel_indexes(indexes)
224+
da, band_names, _ = self._sel_indexes(indexes)
221225

222226
if nodata is not None:
223227
da = da.rio.write_nodata(nodata)
@@ -268,7 +272,7 @@ def tile(
268272
f"Tile(x={tile_x}, y={tile_y}, z={tile_z}) is outside bounds"
269273
)
270274

271-
da, band_names = self._sel_indexes(indexes)
275+
da, band_names, band_descriptions = self._sel_indexes(indexes)
272276

273277
if nodata is not None:
274278
da = da.rio.write_nodata(nodata)
@@ -312,6 +316,7 @@ def tile(
312316
crs=dst_crs,
313317
dataset_statistics=stats,
314318
band_names=band_names,
319+
band_descriptions=band_descriptions,
315320
nodata=da.rio.nodata,
316321
)
317322

@@ -357,7 +362,7 @@ def part(
357362

358363
dst_crs = dst_crs or bounds_crs
359364

360-
da, band_names = self._sel_indexes(indexes)
365+
da, band_names, band_descriptions = self._sel_indexes(indexes)
361366

362367
if nodata is not None:
363368
da = da.rio.write_nodata(nodata)
@@ -406,6 +411,7 @@ def part(
406411
crs=da.rio.crs,
407412
dataset_statistics=stats,
408413
band_names=band_names,
414+
band_descriptions=band_descriptions,
409415
nodata=da.rio.nodata,
410416
)
411417

@@ -457,7 +463,7 @@ def preview(
457463
UserWarning,
458464
)
459465

460-
da, band_names = self._sel_indexes(indexes)
466+
da, band_names, band_descriptions = self._sel_indexes(indexes)
461467

462468
if nodata is not None:
463469
da = da.rio.write_nodata(nodata)
@@ -500,6 +506,7 @@ def preview(
500506
crs=da.rio.crs,
501507
dataset_statistics=stats,
502508
band_names=band_names,
509+
band_descriptions=band_descriptions,
503510
nodata=da.rio.nodata,
504511
)
505512

@@ -547,7 +554,7 @@ def point(
547554
):
548555
raise PointOutsideBounds("Point is outside dataset bounds")
549556

550-
da, band_names = self._sel_indexes(indexes)
557+
da, band_names, band_descriptions = self._sel_indexes(indexes)
551558

552559
if nodata is not None:
553560
da = da.rio.write_nodata(nodata)
@@ -568,6 +575,7 @@ def point(
568575
coordinates=(lon, lat),
569576
crs=coord_crs,
570577
band_names=band_names,
578+
band_descriptions=band_descriptions,
571579
pixel_location=(x, y),
572580
nodata=da.rio.nodata,
573581
)

0 commit comments

Comments
 (0)