Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add MultiAssetsReader base class and refactor STACReader #225

Merged
merged 13 commits into from
Aug 20, 2020
11 changes: 11 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
NEXT (TBD)
------------------
- allow setting default kwargs in COGReader __init__ (#227)
- allow `vrt_options` in COGReader.point
- add `rio_tiler.io.base.MultiAssetsReader` class (#225)
- refactor `rio_tiler.io.stac.STACReader` to use MultiAssetsReader (#225)
- add `rio_tiler.task` submodule to share tools for handling rio-tiler's future tasks.

Breaking Changes:
- replace dataclass wiht attr to support more flexible class definition (see #225)

2.0b6 (2020-08-04)
------------------
- add `utils.create_cutline` helper (#218)
Expand Down
122 changes: 98 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ class COGReader:
with COGReader(src_path) as cog:
cog.tile(...)

# Set global options
with COGReader(src_path, unscale=True, nodata=0) as cog:
cog.tile(...)

with rasterio.open(src_path) as src_dst:
with WarpedVRT(src_dst, ...) as vrt_dst:
with COGReader(None, dataset=vrt_dst) as cog:
Expand Down Expand Up @@ -149,8 +153,6 @@ class COGReader:
COG center + minzoom
colormap: dict
COG internal colormap.
info: dict
General information about the COG (datatype, indexes, ...)

Methods
-------
Expand All @@ -162,6 +164,8 @@ class COGReader:
Read preview of the COG.
point((10, 10), indexes=1)
Read a point value from the COG.
info: dict
General information about the COG (datatype, indexes, ...)
vincentsarago marked this conversation as resolved.
Show resolved Hide resolved
stats(pmin=5, pmax=95)
Get Raster statistics.
meta(pmin=5, pmax=95)
Expand All @@ -180,28 +184,6 @@ class COGReader:
- **bounds**: Return the dataset bounds in WGS84
- **center**: Return the center of the dataset + minzoom
- **spatial_info**: Return the bounds, center and zoom infos
- **info**: Return simple metadata about the dataset

```python
with COGReader("myfile.tif") as cog:
print(cog.info())
{
"bounds": [-119.05915661478785, 13.102845359730287, -84.91821332299578, 33.995073647795806],
"center": [-101.98868496889182, 23.548959503763047, 3],
"minzoom": 3,
"maxzoom": 12,
"band_metadata": [[1, {}]],
"band_descriptions": [[1,"band1"]],
"dtype": "int8",
"colorinterp": ["palette"],
"nodata_type": "Nodata",
"colormap": {
"0": [0, 0, 0, 0],
"1": [0, 61, 0, 255],
...
}
}
```

#### Methods

Expand Down Expand Up @@ -275,6 +257,29 @@ with COGReader("myfile.tif") as cog:
[3, 4]
```

- **info()**: Return simple metadata about the dataset

```python
with COGReader("myfile.tif") as cog:
print(cog.info())
{
"bounds": [-119.05915661478785, 13.102845359730287, -84.91821332299578, 33.995073647795806],
"center": [-101.98868496889182, 23.548959503763047, 3],
"minzoom": 3,
"maxzoom": 12,
"band_metadata": [[1, {}]],
"band_descriptions": [[1,"band1"]],
"dtype": "int8",
"colorinterp": ["palette"],
"nodata_type": "Nodata",
"colormap": {
"0": [0, 0, 0, 0],
"1": [0, 61, 0, 255],
...
}
}
```

- **stats()**: Return image statistics (Min/Max/Stdev)

```python
Expand Down Expand Up @@ -329,6 +334,25 @@ with COGReader("myfile.tif") as cog:
}
```

##### Global Options

COGReader accept several options which will be forwarded to the `rio_tiler.reader._read` function (low level function accessing the data):
- `nodata`: Overwrite the nodata value (or set in not present)
vincentsarago marked this conversation as resolved.
Show resolved Hide resolved
- `unscale`: Apply internal rescaling factors
- `vrt_options`: Pass WarpedVRT Option (see: https://gdal.org/api/gdalwarp_cpp.html?highlight=vrt#_CPPv415GDALWarpOptions)

Note: Those options could already be passed on each `method` call.

```python
with COGReader("my_cog.tif", nodata=0) as cog:
tile, mask = cog.tile(1, 1, 1)

# is equivalent to

with COGReader("my_cog.tif") as cog:
tile, mask = cog.tile(1, 1, 1, nodata=0)
```

### STACReader

In rio-tiler v2, we added a `rio_tiler.io.STACReader` to allow tile/metadata fetching of assets withing a STAC item. The STACReader objects has the same properties/methods as the COGReader.
Expand Down Expand Up @@ -370,6 +394,8 @@ print(tile.shape)
> (1, 256, 256)
```

Note: STACReader is based on `rio_tiler.io.base.MultiAssetsReader` class.

## Working with multiple assets

#### Mosaic
Expand Down Expand Up @@ -418,6 +444,54 @@ data, mask = multi_part(assets, bbox, ...)
data, mask = multi_preview(assets, ...)
```

You can also use `rio_tiler.io.base.MultiAssetsReader` to build a custom asset reader:

```python
import attr
from rio_tiler.io.base import MultiAssetsReader
from rio_tiler.io import COGReader, BaseReader


@attr.s
vincentsarago marked this conversation as resolved.
Show resolved Hide resolved
class CustomReader(MultiAssetsReader):

directory: str = attr.ib() # required arg
reader: Type[BaseReader] = attr.ib(default=COGReader) # the default reader is COGReader

def __enter__(self):
# List files in directory
dirs = os.listdir(self.directory)

# get list of tifs
tiff = [f for f in dirs if f.endswith(".tif")]

# create list of assets names - REQUIRED
self.assets = [os.path.basename(f).split(".")[0] for f in tiff]

# `self.bounds` needs to be set! - REQUIRED
with self.reader(tiff[0]) as cog:
self.bounds = cog.bounds

return self

def _get_asset_url(self, asset: str) -> str:
"""Validate asset names and return asset's url."""
if asset not in self.assets:
raise InvalidAssetName(f"{asset} is not valid")

return os.path.join(self.directory, f"{asset}.tif")

# we have a directoty with "b1.tif", "b2.tif", "b3.tif"
with CustomReader("my_dir/") as cr:
print(cr.assets)
tile, mask = cr.tile(x, y, z, assets="b1")

> ["b1", "b2", "b3"]

print(tile.shape)
> (3, 256, 256)
```

#### Reading asset with a GeoJSON Polygon

Natively rio-tiler support mostly `bbox` reading. Using GDALWarpVRT **Cutline** option, it's possible to read a dataset for a given polygon.
Expand Down
13 changes: 7 additions & 6 deletions docs/colormap.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Colormaps

Rio-tiler includes many colormaps, some derived from Matplotlib and some custom
ones that are commonly-used with raster data.
ones that are commonly used with raster data.

You can load a colormap with `rio_tiler.colormap.get_colormap`, and then pass it
to `rio_tiler.utils.render`:
Expand All @@ -23,11 +23,12 @@ render(tile, mask, colormap=colormap)
![](img/qualitative.png)
![](img/miscellaneous.png)

### Refs
- Matplotlib: https://matplotlib.org/3.1.0/tutorials/colors/colormaps.html
- cfastie: http://publiclab.org/notes/cfastie/08-26-2014/new-ndvi-colormap
- rplumbo: https://github.com/cogeotiff/rio-tiler/pull/90
- schwarzwald: http://soliton.vm.bytemark.co.uk/pub/cpt-city/wkp/schwarzwald/tn/wiki-schwarzwald-cont.png.index.html
### References

- Matplotlib colormaps: <https://matplotlib.org/3.1.0/tutorials/colors/colormaps.html>
- `cfastie`: <http://publiclab.org/notes/cfastie/08-26-2014/new-ndvi-colormap>
- `rplumbo`: <https://github.com/cogeotiff/rio-tiler/pull/90>
- `schwarzwald`: <http://soliton.vm.bytemark.co.uk/pub/cpt-city/wkp/schwarzwald/tn/wiki-schwarzwald-cont.png.index.html>

### Update images for new colormaps

Expand Down
51 changes: 35 additions & 16 deletions docs/dynamic_tiler.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,39 @@
# Create a dynamic tiler
# Dynamic Tiling

rio-tiler aim to be a lightweight plugin for rasterio with sole goal is to read `Mercator Tile` from raster dataset.
`rio-tiler` aims to be a lightweight plugin for `rasterio` whose sole goal is to
read a Mercator Tile from a raster dataset.

If you can read `Tile` you can then create a **Dynamic Tile Server** to display raster tiles on a web map.
Given that `rio-tiler` allows for simple, efficient reading of tiles, you can
then leverage `rio-tiler` to create a **dynamic tile server** to display raster
tiles on a web map.

There are couple tile servers built on top of rio-tiler:
- [cogeo-tiler](https://github.com/developmentseed/cogeo-tiler)
- [titiler](https://github.com/developmentseed/titiler)
- [rio-viz](https://github.com/developmentseed/rio-viz)
There are couple tile servers built on top of rio-tiler:

- [`titiler`](https://github.com/developmentseed/titiler)
- [`cogeo-tiler`](https://github.com/developmentseed/cogeo-tiler)
- [`cogeo-mosaic-tiler`](https://github.com/developmentseed/cogeo-mosaic-tiler)
- [`rio-viz`](https://github.com/developmentseed/rio-viz)

## Application

To built a simple API we can use [FastAPI](https://github.com/tiangolo/fastapi). The
## Example Application

To build a simple dynamic tiling application, we can use
[FastAPI](https://github.com/tiangolo/fastapi). Note that `titiler` uses
`FastAPI` internally, so you might consider using `titiler` instead of making
your own API.

### Requirements
- rio-tiler ~= 2.0b
- fastapi
- uvicorn

`app.py`
- `rio-tiler ~= 2.0b`
- `fastapi`
- `uvicorn`

Install with

```bash
pip install fastapi uvicorn 'rio-tiler~=2.0b'
```

### `app.py`

```python
"""rio-tiler tile server."""
Expand Down Expand Up @@ -157,6 +170,12 @@ def tilejson(
return tjson
```

## Launch
## Launch Example

Use `uvicorn` to launch the application. Note that `app:app` tells `uvicorn` to
call the `app` function within `app.py`, so you must be in the same directory as
`app.py`.

`uvicorn app:app --reload`
```
uvicorn app:app --reload
```
59 changes: 33 additions & 26 deletions docs/mosaic.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@

## Mosaic
## Using `rio-tiler` with Mosaics

![](https://user-images.githubusercontent.com/10407788/57467798-30505800-7251-11e9-9bde-6f50801dc851.png)

The goal of rio_tiler.mosaic submodule is to create tiles from multiple observations.
The `rio-tiler-mosaic` library has been moved into `rio-tiler`. The goal of the
`rio_tiler.mosaic` module is to create a mercator tile from _multiple_
observations. This is useful when a source image doesn't fill the entire
mercator tile of interest.

Often when creating a mercator tile from multiple assets, there will be portions
of overlap where a pixel could be chosen from multiple datasets. To handle this,
the `rio-tiler.mosaic` module provides _pixel selection methods_ which define
how to handle these cases for each pixel:

Because user might want to choose which pixel goes on top of the tile, this plugin comes with 5 differents `pixel selection` algorithms:
- **First**: takes the first pixel received
- **Highest**: loop though all the assets and return the highest value
- **First**: select value from the first non-missing asset
- **Highest**: loop though all the assets and return the highest value
- **Lowest**: loop though all the assets and return the lowest value
- **Mean**: compute the mean value of the whole stack
- **Median**: compute the median value of the whole stack
Expand All @@ -17,10 +23,11 @@ Because user might want to choose which pixel goes on top of the tile, this plug
`rio_tiler.mosaic.mosaic_reader(assets, tiler, *args* pixel_selection=None, chunk_size=None, Threads=10, **kwargs)`

Inputs:

- assets : list, tuple of rio-tiler compatible assets (url or sceneid)
- tiler: Rio-tiler's tiler function (e.g rio_tiler.landsat8.tile)
- tiler: Callable that returns a tuple of `numpy.array` (e.g `tile, mask = rio_tiler.reader.tile(x, y, z, **kwargs)`)
- *args: tiler specific arguments.
- pixel_selection : optional **pixel selection** algorithm (default: "first").
- pixel_selection : optional **pixel selection** algorithm (default: "first").
- chunk_size: optional, control the number of asset to process per loop.
- **kwargs: tiler specific keyword arguments.

Expand Down Expand Up @@ -69,7 +76,7 @@ mosaic_reader(

### The `MosaicMethod` interface

the `rio_tiler.mosaic.methods.base.MosaicMethodBase` class defines an abstract
the `rio_tiler.mosaic.methods.base.MosaicMethodBase` class defines an abstract
interface for all `pixel selection` methods allowed by `rio_tiler.mosaic.mosaic_reader`. its methods and properties are:

- `is_done`: property, returns a boolean indicating if the process is done filling the tile
Expand All @@ -87,16 +94,16 @@ The rules for writing your own `pixel selection algorithm` class are as follows:

See [rio_tiler.mosaic.methods.defaults](/rio_tiler/mosaic/methods/defaults.py) classes for examples.

#### Smart Multi-Threading
#### Smart Multi-Threading

When dealing with an important number of image, you might not want to process the whole stack, especially if the pixel selection method stops when the tile is filled. To allow better optimization, `rio_tiler.mosaic.mosaic_reader` is fetching the tiles in parallel (threads) but to limit the number of files we also embeded the fetching in a loop (creating 2 level of processing):
When dealing with an important number of image, you might not want to process the whole stack, especially if the pixel selection method stops when the tile is filled. To allow better optimization, `rio_tiler.mosaic.mosaic_reader` is fetching the tiles in parallel (threads) but to limit the number of files we also embeded the fetching in a loop (creating 2 level of processing):

```python
assets = ["1.tif", "2.tif", "3.tif", "4.tif", "5.tif", "6.tif"]

# 1st level loop - Creates chuncks of assets
for chunks in _chunks(assets, chunk_size):

# 2nd level loop - Uses threads for process each `chunck`
with futures.ThreadPoolExecutor(max_workers=max_threads) as executor:
future_tasks = [(executor.submit(_tiler, asset), asset) for asset in chunks]
Expand All @@ -111,25 +118,25 @@ In some case, threading can slow down your application. You can set threads to `
Benchmark:
```
--------------------------------- benchmark '1images': 6 tests ---------------------------------
Name (time in ms) Min Max Mean Median
Name (time in ms) Min Max Mean Median
------------------------------------------------------------------------------------------------
1images-0threads 64.3108 (1.0) 66.9192 (1.0) 65.0202 (1.0) 64.9370 (1.0)
1images-4threads 69.0893 (1.07) 70.9919 (1.06) 69.6718 (1.07) 69.5102 (1.07)
1images-1threads 69.4884 (1.08) 71.8967 (1.07) 70.0853 (1.08) 69.9804 (1.08)
1images-5threads 69.5552 (1.08) 75.5498 (1.13) 71.7882 (1.10) 70.9849 (1.09)
1images-3threads 69.7684 (1.08) 74.4098 (1.11) 70.6282 (1.09) 70.2353 (1.08)
1images-2threads 69.9258 (1.09) 73.8798 (1.10) 70.8861 (1.09) 70.3682 (1.08)
1images-0threads 64.3108 (1.0) 66.9192 (1.0) 65.0202 (1.0) 64.9370 (1.0)
1images-4threads 69.0893 (1.07) 70.9919 (1.06) 69.6718 (1.07) 69.5102 (1.07)
1images-1threads 69.4884 (1.08) 71.8967 (1.07) 70.0853 (1.08) 69.9804 (1.08)
1images-5threads 69.5552 (1.08) 75.5498 (1.13) 71.7882 (1.10) 70.9849 (1.09)
1images-3threads 69.7684 (1.08) 74.4098 (1.11) 70.6282 (1.09) 70.2353 (1.08)
1images-2threads 69.9258 (1.09) 73.8798 (1.10) 70.8861 (1.09) 70.3682 (1.08)
------------------------------------------------------------------------------------------------

----------------------------------- benchmark '5images': 6 tests -----------------------------------
Name (time in ms) Min Max Mean Median
Name (time in ms) Min Max Mean Median
----------------------------------------------------------------------------------------------------
5images-5threads 104.1609 (1.0) 123.4442 (1.0) 110.4130 (1.0) 110.0683 (1.0)
5images-4threads 160.0952 (1.54) 170.7994 (1.38) 163.6062 (1.48) 161.8923 (1.47)
5images-3threads 161.2354 (1.55) 172.0363 (1.39) 165.1222 (1.50) 164.6513 (1.50)
5images-2threads 214.2413 (2.06) 220.7737 (1.79) 217.7740 (1.97) 217.9166 (1.98)
5images-0threads 228.2062 (2.19) 242.9397 (1.97) 231.9848 (2.10) 229.2843 (2.08)
5images-1threads 248.6630 (2.39) 251.8809 (2.04) 250.5195 (2.27) 251.2667 (2.28)
5images-5threads 104.1609 (1.0) 123.4442 (1.0) 110.4130 (1.0) 110.0683 (1.0)
5images-4threads 160.0952 (1.54) 170.7994 (1.38) 163.6062 (1.48) 161.8923 (1.47)
5images-3threads 161.2354 (1.55) 172.0363 (1.39) 165.1222 (1.50) 164.6513 (1.50)
5images-2threads 214.2413 (2.06) 220.7737 (1.79) 217.7740 (1.97) 217.9166 (1.98)
5images-0threads 228.2062 (2.19) 242.9397 (1.97) 231.9848 (2.10) 229.2843 (2.08)
5images-1threads 248.6630 (2.39) 251.8809 (2.04) 250.5195 (2.27) 251.2667 (2.28)
----------------------------------------------------------------------------------------------------
```
ref: https://github.com/cogeotiff/rio-tiler/issues/207#issuecomment-665958838
Expand Down
Loading