Skip to content

Commit 0900af9

Browse files
Release/v7.0.0 (#750)
* update changelog * Bump version: 6.7.0 → 7.0.0 * remove deprecate method and update docs * allow bench to fail * fix type information for `bounds`
1 parent 0ee8dfb commit 0900af9

26 files changed

+1624
-2429
lines changed

.pre-commit-config.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,8 @@ repos:
2727
additional_dependencies:
2828
- types-attrs
2929
- types-cachetools
30+
31+
- repo: https://github.com/nbQA-dev/nbQA
32+
rev: 1.8.7
33+
hooks:
34+
- id: nbqa-black

CHANGES.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
# Unreleased
2+
# 7.0.0 (2024-10-21)
33

44
* Enable dynamic definition of Asset **reader** in `MultiBaseReader` (https://github.com/cogeotiff/rio-tiler/pull/711/, https://github.com/cogeotiff/rio-tiler/pull/728)
55

@@ -79,6 +79,12 @@
7979

8080
* update `morecantile` dependency to allow `6.x` version
8181

82+
* remove deprecated method and attributes
83+
84+
* `round` xarray dataset's bounds to avoid precision errors when checking for valid geographic bounding box
85+
86+
* fix `bounds` type information
87+
8288
# 6.7.0 (2024-09-05)
8389

8490
* raise `MissingCRS` or `InvalidGeographicBounds` errors when Xarray datasets have wrong geographic metadata

README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -169,17 +169,17 @@ At the low level, `rio-tiler` is *just* a wrapper around the [rasterio](https://
169169
You can install `rio-tiler` using pip
170170

171171
```bash
172-
$ pip install -U pip
173-
$ pip install -U rio-tiler
172+
$ python -m pip install -U pip
173+
$ python -m pip install -U rio-tiler
174174
```
175175

176176
or install from source:
177177

178178
```bash
179179
$ git clone https://github.com/cogeotiff/rio-tiler.git
180180
$ cd rio-tiler
181-
$ pip install -U pip
182-
$ pip install -e .
181+
$ python -m pip install -U pip
182+
$ python -m pip install -e .
183183
```
184184

185185
## Plugins

docs/src/advanced/custom_readers.md

+109-36
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,18 @@ Main `rio_tiler.io` Abstract Base Class.
1818

1919
- **bounds**: Dataset's bounding box. Not in the `__init__` method.
2020
- **crs**: dataset's crs. Not in the `__init__` method.
21-
- **geographic_crs**: CRS to use as geographic coordinate system. Defaults to WGS84. Not in the `__init__` method.
21+
- **transform**: dataset's Affine transform. Not in the `__init__` method.
22+
- **height**: dataset's height. Not in the `__init__` method.
23+
- **width**: dataset's width. Not in the `__init__` method.
24+
2225

2326
!!! important
2427
BaseClass Arguments outside the `__init__` method and without default value **HAVE TO** be set in the `__attrs_post_init__` step.
2528

2629
#### Methods
2730

28-
- **tile_exists**: Check if a given tile (for the input TMS) intersect the dataset bounds.
29-
30-
##### Properties
31-
32-
- **geographic_bounds**: dataset's bounds in WGS84 crs (calculated from `self.bounds` and `self.crs`).
31+
- **tile_exists(tile_x: int, tile_y: int, tile_z: int)**: Check if a given tile (for the input TMS) intersect the dataset bounds.
32+
- **get_geographic_bounds(crs: rasterio.crs.CRS)**: dataset's bounds in Geographic CRS (calculated from `self.bounds` and `self.crs`).
3333

3434
##### Abstract Methods
3535

@@ -64,6 +64,8 @@ from rio_tiler.io.base import MultiBaseReader
6464
from rio_tiler.io import Reader, BaseReader
6565
from rio_tiler.constants import WEB_MERCATOR_TMS
6666
from rio_tiler.models import Info
67+
from rio_tiler.types import AssetInfo
68+
from rio_tiler.errors import InvalidAssetName
6769

6870
@attr.s
6971
class AssetFileReader(MultiBaseReader):
@@ -86,19 +88,26 @@ class AssetFileReader(MultiBaseReader):
8688
self.assets = sorted(
8789
[p.stem.split("_")[1] for p in pathlib.Path(self.input).glob(f"*{self.prefix}*.tif")]
8890
)
89-
with self.reader(self._get_asset_url(self.assets[0])) as cog:
91+
with self.reader(self._get_asset_info(self.assets[0])["url"]) as cog:
9092
self.bounds = cog.bounds
9193
self.crs = cog.crs
92-
94+
self.transform = cog.transform
95+
self.height = cog.height
96+
self.width = cog.width
9397
if self.minzoom is None:
9498
self.minzoom = cog.minzoom
9599

96100
if self.maxzoom is None:
97101
self.maxzoom = cog.maxzoom
98102

99-
def _get_asset_url(self, band: str) -> str:
103+
def _get_asset_info(self, asset: str) -> AssetInfo:
100104
"""Validate band's name and return band's url."""
101-
return os.path.join(self.input, f"{self.prefix}{band}.tif")
105+
if asset not in self.assets:
106+
raise InvalidAssetName(
107+
f"'{asset}' is not valid, should be one of {self.assets}"
108+
)
109+
110+
return AssetInfo(url=os.path.join(self.input, f"{self.prefix}{asset}.tif"))
102111

103112
# we have a directoty with "scene_b1.tif", "scene_b2.tif"
104113
with AssetFileReader(input="my_dir/", prefix="scene_") as cr:
@@ -114,14 +123,44 @@ with AssetFileReader(input="my_dir/", prefix="scene_") as cr:
114123
assert isinstance(info["band1"], Info)
115124
print(info["band1"].model_dump_json(exclude_none=True))
116125
>>> {
117-
'bounds': [-11.979244865430259, 24.296321392464325, -10.874546803397614, 25.304623891542263],
118-
'minzoom': 7,
119-
'maxzoom': 9,
120-
'band_metadata': [('b1', {})],
121-
'band_descriptions': [('b1', '')],
122-
'dtype': 'uint16',
123-
'nodata_type': 'Nodata',
124-
'colorinterp': ['gray']
126+
"bounds": [
127+
199980,
128+
2690220,
129+
309780,
130+
2800020
131+
],
132+
"crs": "http://www.opengis.net/def/crs/EPSG/0/32629",
133+
"band_metadata": [
134+
[
135+
"b1",
136+
{}
137+
]
138+
],
139+
"band_descriptions": [
140+
[
141+
"b1",
142+
""
143+
]
144+
],
145+
"dtype": "uint16",
146+
"nodata_type": "Nodata",
147+
"colorinterp": [
148+
"gray"
149+
],
150+
"scales": [
151+
1
152+
],
153+
"offsets": [
154+
0
155+
],
156+
"driver": "GTiff",
157+
"count": 1,
158+
"width": 549,
159+
"height": 549,
160+
"overviews": [
161+
2
162+
],
163+
"nodata_value": 0
125164
}
126165
img = cr.tile(238, 218, 9, assets=("band1", "band2"))
127166

@@ -176,7 +215,9 @@ class BandFileReader(MultiBandReader):
176215
with self.reader(self._get_band_url(self.bands[0])) as cog:
177216
self.bounds = cog.bounds
178217
self.crs = cog.crs
179-
218+
self.transform = cog.transform
219+
self.height = cog.height
220+
self.width = cog.width
180221
if self.minzoom is None:
181222
self.minzoom = cog.minzoom
182223

@@ -195,14 +236,39 @@ with BandFileReader(input="my_dir/", prefix="scene_") as cr:
195236

196237
print(cr.info(bands=("band1", "band2")).model_dump_json(exclude_none=True))
197238
>>> {
198-
'bounds': [-11.979244865430259, 24.296321392464325, -10.874546803397614, 25.304623891542263],
199-
'minzoom': 7,
200-
'maxzoom': 9,
201-
'band_metadata': [('band1', {}), ('band2', {})],
202-
'band_descriptions': [('band1', ''), ('band2', '')],
203-
'dtype': 'uint16',
204-
'nodata_type': 'Nodata',
205-
'colorinterp': ['gray', 'gray']
239+
"bounds": [
240+
199980,
241+
2690220,
242+
309780,
243+
2800020
244+
],
245+
"crs": "http://www.opengis.net/def/crs/EPSG/0/32629",
246+
"band_metadata": [
247+
[
248+
"band1",
249+
{}
250+
],
251+
[
252+
"band2",
253+
{}
254+
]
255+
],
256+
"band_descriptions": [
257+
[
258+
"band1",
259+
""
260+
],
261+
[
262+
"band2",
263+
""
264+
]
265+
],
266+
"dtype": "uint16",
267+
"nodata_type": "Nodata",
268+
"colorinterp": [
269+
"gray",
270+
"gray"
271+
]
206272
}
207273

208274
img = cr.tile(238, 218, 9, bands=("band1", "band2"))
@@ -278,7 +344,7 @@ In this `CustomSTACReader`, we are using a custom path `schema` in form of `{ite
278344

279345

280346
```python
281-
from typing import Any, Dict
347+
from typing import Any, Dict, List
282348

283349
import attr
284350
import rasterio
@@ -297,14 +363,23 @@ class SimpleReader(BaseReader):
297363
# We force tms to be outside the class __init__
298364
tms: TileMatrixSet = attr.ib(init=False, default=WEB_MERCATOR_TMS)
299365

300-
# We overwrite the abstract base class attribute definition and set default
301-
minzoom: int = attr.ib(init=False, default=WEB_MERCATOR_TMS.minzoom)
302-
maxzoom: int = attr.ib(init=False, default=WEB_MERCATOR_TMS.maxzoom)
303-
304366
def __attrs_post_init__(self):
305367
# Set bounds and crs variable
306368
self.bounds = self.input.bounds
307369
self.crs = self.input.crs
370+
self.transform = self.input.transform
371+
self.height = self.input.height
372+
self.width = self.input.width
373+
374+
@property
375+
def minzoom(self):
376+
"""Return dataset minzoom."""
377+
return self._minzoom
378+
379+
@property
380+
def maxzoom(self):
381+
"""Return dataset maxzoom."""
382+
return self._maxzoom
308383

309384
# implement all mandatory methods
310385
def info(self) -> Info:
@@ -333,7 +408,7 @@ class SimpleReader(BaseReader):
333408

334409
tile_bounds = self.tms.xy_bounds(Tile(x=tile_x, y=tile_y, z=tile_z))
335410

336-
data, mask = reader.part(
411+
return reader.part(
337412
self.input,
338413
tile_bounds,
339414
width=256,
@@ -342,9 +417,7 @@ class SimpleReader(BaseReader):
342417
dst_crs=tms.rasterio_crs,
343418
**kwargs,
344419
)
345-
return ImageData(
346-
data, mask, bounds=tile_bounds, crs=tms.rasterio_crs
347-
)
420+
348421

349422
with rasterio.open("file.tif") as src:
350423
with SimpleReader(src) as cog:

docs/src/advanced/dynamic_tiler.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def tile(
7070
"""Handle tile requests."""
7171
with Reader(url) as cog:
7272
img = cog.tile(x, y, z)
73+
7374
content = img.render(img_format="PNG", **img_profiles.get("png"))
7475
return Response(content, media_type="image/png")
7576

@@ -85,7 +86,7 @@ def tilejson(
8586

8687
with Reader(url) as cog:
8788
return {
88-
"bounds": cog.geographic_bounds,
89+
"bounds": cog.get_geographic_bounds(cog.tms.rasterio_geographic_crs),
8990
"minzoom": cog.minzoom,
9091
"maxzoom": cog.maxzoom,
9192
"name": os.path.basename(url),

docs/src/colormap.md

+4
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ data, mask = apply_cmap(data, cmap)
135135
![](img/miscellaneous.png)
136136
![](img/colormaps_for_oceanography.png)
137137

138+
### Automatically load custom colormap
139+
140+
User can set `COLORMAP_DIRECTORY` env variable to tell rio-tiler to search for `.npy` or `.json` files holding custom colormaps.
141+
138142
### References
139143

140144
- Matplotlib colormaps: <https://matplotlib.org/3.1.0/tutorials/colors/colormaps.html>

docs/src/examples/Using-nonEarth-dataset.ipynb

+36-161
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)