Skip to content

Commit

Permalink
Release/v7.0.0 (#750)
Browse files Browse the repository at this point in the history
* 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`
  • Loading branch information
vincentsarago authored Oct 21, 2024
1 parent 0ee8dfb commit 0900af9
Show file tree
Hide file tree
Showing 26 changed files with 1,624 additions and 2,429 deletions.
5 changes: 5 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,8 @@ repos:
additional_dependencies:
- types-attrs
- types-cachetools

- repo: https://github.com/nbQA-dev/nbQA
rev: 1.8.7
hooks:
- id: nbqa-black
8 changes: 7 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

# Unreleased
# 7.0.0 (2024-10-21)

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

Expand Down Expand Up @@ -79,6 +79,12 @@

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

* remove deprecated method and attributes

* `round` xarray dataset's bounds to avoid precision errors when checking for valid geographic bounding box

* fix `bounds` type information

# 6.7.0 (2024-09-05)

* raise `MissingCRS` or `InvalidGeographicBounds` errors when Xarray datasets have wrong geographic metadata
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,17 +169,17 @@ At the low level, `rio-tiler` is *just* a wrapper around the [rasterio](https://
You can install `rio-tiler` using pip

```bash
$ pip install -U pip
$ pip install -U rio-tiler
$ python -m pip install -U pip
$ python -m pip install -U rio-tiler
```

or install from source:

```bash
$ git clone https://github.com/cogeotiff/rio-tiler.git
$ cd rio-tiler
$ pip install -U pip
$ pip install -e .
$ python -m pip install -U pip
$ python -m pip install -e .
```

## Plugins
Expand Down
145 changes: 109 additions & 36 deletions docs/src/advanced/custom_readers.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@ Main `rio_tiler.io` Abstract Base Class.

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


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

#### Methods

- **tile_exists**: Check if a given tile (for the input TMS) intersect the dataset bounds.

##### Properties

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

##### Abstract Methods

Expand Down Expand Up @@ -64,6 +64,8 @@ from rio_tiler.io.base import MultiBaseReader
from rio_tiler.io import Reader, BaseReader
from rio_tiler.constants import WEB_MERCATOR_TMS
from rio_tiler.models import Info
from rio_tiler.types import AssetInfo
from rio_tiler.errors import InvalidAssetName

@attr.s
class AssetFileReader(MultiBaseReader):
Expand All @@ -86,19 +88,26 @@ class AssetFileReader(MultiBaseReader):
self.assets = sorted(
[p.stem.split("_")[1] for p in pathlib.Path(self.input).glob(f"*{self.prefix}*.tif")]
)
with self.reader(self._get_asset_url(self.assets[0])) as cog:
with self.reader(self._get_asset_info(self.assets[0])["url"]) as cog:
self.bounds = cog.bounds
self.crs = cog.crs

self.transform = cog.transform
self.height = cog.height
self.width = cog.width
if self.minzoom is None:
self.minzoom = cog.minzoom

if self.maxzoom is None:
self.maxzoom = cog.maxzoom

def _get_asset_url(self, band: str) -> str:
def _get_asset_info(self, asset: str) -> AssetInfo:
"""Validate band's name and return band's url."""
return os.path.join(self.input, f"{self.prefix}{band}.tif")
if asset not in self.assets:
raise InvalidAssetName(
f"'{asset}' is not valid, should be one of {self.assets}"
)

return AssetInfo(url=os.path.join(self.input, f"{self.prefix}{asset}.tif"))

# we have a directoty with "scene_b1.tif", "scene_b2.tif"
with AssetFileReader(input="my_dir/", prefix="scene_") as cr:
Expand All @@ -114,14 +123,44 @@ with AssetFileReader(input="my_dir/", prefix="scene_") as cr:
assert isinstance(info["band1"], Info)
print(info["band1"].model_dump_json(exclude_none=True))
>>> {
'bounds': [-11.979244865430259, 24.296321392464325, -10.874546803397614, 25.304623891542263],
'minzoom': 7,
'maxzoom': 9,
'band_metadata': [('b1', {})],
'band_descriptions': [('b1', '')],
'dtype': 'uint16',
'nodata_type': 'Nodata',
'colorinterp': ['gray']
"bounds": [
199980,
2690220,
309780,
2800020
],
"crs": "http://www.opengis.net/def/crs/EPSG/0/32629",
"band_metadata": [
[
"b1",
{}
]
],
"band_descriptions": [
[
"b1",
""
]
],
"dtype": "uint16",
"nodata_type": "Nodata",
"colorinterp": [
"gray"
],
"scales": [
1
],
"offsets": [
0
],
"driver": "GTiff",
"count": 1,
"width": 549,
"height": 549,
"overviews": [
2
],
"nodata_value": 0
}
img = cr.tile(238, 218, 9, assets=("band1", "band2"))

Expand Down Expand Up @@ -176,7 +215,9 @@ class BandFileReader(MultiBandReader):
with self.reader(self._get_band_url(self.bands[0])) as cog:
self.bounds = cog.bounds
self.crs = cog.crs

self.transform = cog.transform
self.height = cog.height
self.width = cog.width
if self.minzoom is None:
self.minzoom = cog.minzoom

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

print(cr.info(bands=("band1", "band2")).model_dump_json(exclude_none=True))
>>> {
'bounds': [-11.979244865430259, 24.296321392464325, -10.874546803397614, 25.304623891542263],
'minzoom': 7,
'maxzoom': 9,
'band_metadata': [('band1', {}), ('band2', {})],
'band_descriptions': [('band1', ''), ('band2', '')],
'dtype': 'uint16',
'nodata_type': 'Nodata',
'colorinterp': ['gray', 'gray']
"bounds": [
199980,
2690220,
309780,
2800020
],
"crs": "http://www.opengis.net/def/crs/EPSG/0/32629",
"band_metadata": [
[
"band1",
{}
],
[
"band2",
{}
]
],
"band_descriptions": [
[
"band1",
""
],
[
"band2",
""
]
],
"dtype": "uint16",
"nodata_type": "Nodata",
"colorinterp": [
"gray",
"gray"
]
}

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


```python
from typing import Any, Dict
from typing import Any, Dict, List

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

# We overwrite the abstract base class attribute definition and set default
minzoom: int = attr.ib(init=False, default=WEB_MERCATOR_TMS.minzoom)
maxzoom: int = attr.ib(init=False, default=WEB_MERCATOR_TMS.maxzoom)

def __attrs_post_init__(self):
# Set bounds and crs variable
self.bounds = self.input.bounds
self.crs = self.input.crs
self.transform = self.input.transform
self.height = self.input.height
self.width = self.input.width

@property
def minzoom(self):
"""Return dataset minzoom."""
return self._minzoom

@property
def maxzoom(self):
"""Return dataset maxzoom."""
return self._maxzoom

# implement all mandatory methods
def info(self) -> Info:
Expand Down Expand Up @@ -333,7 +408,7 @@ class SimpleReader(BaseReader):

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

data, mask = reader.part(
return reader.part(
self.input,
tile_bounds,
width=256,
Expand All @@ -342,9 +417,7 @@ class SimpleReader(BaseReader):
dst_crs=tms.rasterio_crs,
**kwargs,
)
return ImageData(
data, mask, bounds=tile_bounds, crs=tms.rasterio_crs
)


with rasterio.open("file.tif") as src:
with SimpleReader(src) as cog:
Expand Down
3 changes: 2 additions & 1 deletion docs/src/advanced/dynamic_tiler.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def tile(
"""Handle tile requests."""
with Reader(url) as cog:
img = cog.tile(x, y, z)

content = img.render(img_format="PNG", **img_profiles.get("png"))
return Response(content, media_type="image/png")

Expand All @@ -85,7 +86,7 @@ def tilejson(

with Reader(url) as cog:
return {
"bounds": cog.geographic_bounds,
"bounds": cog.get_geographic_bounds(cog.tms.rasterio_geographic_crs),
"minzoom": cog.minzoom,
"maxzoom": cog.maxzoom,
"name": os.path.basename(url),
Expand Down
4 changes: 4 additions & 0 deletions docs/src/colormap.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ data, mask = apply_cmap(data, cmap)
![](img/miscellaneous.png)
![](img/colormaps_for_oceanography.png)

### Automatically load custom colormap

User can set `COLORMAP_DIRECTORY` env variable to tell rio-tiler to search for `.npy` or `.json` files holding custom colormaps.

### References

- Matplotlib colormaps: <https://matplotlib.org/3.1.0/tutorials/colors/colormaps.html>
Expand Down
197 changes: 36 additions & 161 deletions docs/src/examples/Using-nonEarth-dataset.ipynb

Large diffs are not rendered by default.

Loading

0 comments on commit 0900af9

Please sign in to comment.