From 4b595229127fa99411059b8882a5a3ef34e0e978 Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Sat, 21 Oct 2023 14:43:34 +0200 Subject: [PATCH 1/2] add reproject method and padding options --- CHANGES.md | 8 ++++ src/titiler/core/titiler/core/dependencies.py | 42 ++++++++++++++++--- src/titiler/core/titiler/core/factory.py | 17 ++++---- src/titiler/mosaic/titiler/mosaic/factory.py | 16 ++++--- 4 files changed, 64 insertions(+), 19 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0506e2558..cb9184dfb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,9 +2,17 @@ ## Unreleased +### titiler.core + +* add `dependencies.TileParams` dependency with `buffer` and `padding` options +* add `tile_dependency` attribute in `TilerFactory` class (defaults to `TileParams`) +* add `reproject` (alias to `reproject_method`) option in `DatasetParams` dependency + ### titiler.mosaic * Change `HTTP_404_NOT_FOUND` to `HTTP_204_NO_CONTENT` when no asset is found or tile is empty (author @simouel, https://github.com/developmentseed/titiler/pull/713) +* add `tile_dependency` attribute in `MosaicTilerFactory` class (defaults to `TileParams`) + ### cdk application diff --git a/src/titiler/core/titiler/core/dependencies.py b/src/titiler/core/titiler/core/dependencies.py index 2f2ae07d7..eda7c87a6 100644 --- a/src/titiler/core/titiler/core/dependencies.py +++ b/src/titiler/core/titiler/core/dependencies.py @@ -11,7 +11,7 @@ from rio_tiler.colormap import cmap as default_cmap from rio_tiler.colormap import parse_color from rio_tiler.errors import MissingAssets, MissingBands -from rio_tiler.types import RIOResampling +from rio_tiler.types import RIOResampling, WarpResampling from typing_extensions import Annotated @@ -352,17 +352,24 @@ class DatasetParams(DefaultDependency): ), ] = None unscale: Annotated[ - Optional[bool], + bool, Query( title="Apply internal Scale/Offset", - description="Apply internal Scale/Offset", + description="Apply internal Scale/Offset. Defaults to `False`.", ), ] = False resampling_method: Annotated[ RIOResampling, Query( alias="resampling", - description="Resampling method.", + description="RasterIO resampling algorithm. Defaults to `nearest`.", + ), + ] = "nearest" + reproject_method: Annotated[ + WarpResampling, + Query( + alias="reproject", + description="WarpKernel resampling algorithm (only used when doing re-projection). Defaults to `nearest`.", ), ] = "nearest" @@ -370,7 +377,7 @@ def __post_init__(self): """Post Init.""" if self.nodata is not None: self.nodata = numpy.nan if self.nodata == "nan" else float(self.nodata) - self.resampling_method = self.resampling_method + self.unscale = bool(self.unscale) @dataclass @@ -381,7 +388,7 @@ class ImageRenderingParams(DefaultDependency): bool, Query( alias="return_mask", - description="Add mask to the output data.", + description="Add mask to the output data. Defaults to `True`", ), ] = True @@ -559,3 +566,26 @@ def ColorFormulaParams( ) -> Optional[str]: """ColorFormula Parameter.""" return color_formula + + +@dataclass +class TileParams(DefaultDependency): + """Tile options.""" + + buffer: Annotated[ + Optional[float], + Query( + gt=0, + title="Tile buffer.", + description="Buffer on each side of the given tile. It must be a multiple of `0.5`. Output **tilesize** will be expanded to `tilesize + 2 * buffer` (e.g 0.5 = 257x257, 1.0 = 258x258).", + ), + ] = None + + padding: Annotated[ + Optional[int], + Query( + gt=0, + title="Tile padding.", + description="Padding to apply to each tile edge. Helps reduce resampling artefacts along edges. Defaults to `0`.", + ), + ] = None diff --git a/src/titiler/core/titiler/core/factory.py b/src/titiler/core/titiler/core/factory.py index a56dbe77b..ff628029c 100644 --- a/src/titiler/core/titiler/core/factory.py +++ b/src/titiler/core/titiler/core/factory.py @@ -37,7 +37,6 @@ BandsExprParamsOptional, BandsParams, BidxExprParams, - BufferParams, ColorFormulaParams, ColorMapParams, CoordCRSParams, @@ -52,6 +51,7 @@ RescaleType, RescalingParams, StatisticsParams, + TileParams, ) from titiler.core.models.mapbox import TileJSON from titiler.core.models.OGC import TileMatrixSetList @@ -137,7 +137,7 @@ class BaseTilerFactory(metaclass=abc.ABCMeta): # Path Dependency path_dependency: Callable[..., Any] = DatasetPathParams - # Rasterio Dataset Options (nodata, unscale, resampling) + # Rasterio Dataset Options (nodata, unscale, resampling, reproject) dataset_dependency: Type[DefaultDependency] = DatasetParams # Indexes/Expression Dependencies @@ -278,6 +278,9 @@ class TilerFactory(BaseTilerFactory): img_preview_dependency: Type[DefaultDependency] = PreviewParams img_part_dependency: Type[DefaultDependency] = PartFeatureParams + # Tile/Tilejson/WMTS Dependencies + tile_dependency: Type[DefaultDependency] = TileParams + # Add/Remove some endpoints add_preview: bool = True add_part: bool = True @@ -537,7 +540,7 @@ def tile( src_path=Depends(self.path_dependency), layer_params=Depends(self.layer_dependency), dataset_params=Depends(self.dataset_dependency), - buffer=Depends(BufferParams), + tile_params=Depends(self.tile_dependency), post_process=Depends(self.process_dependency), rescale=Depends(self.rescale_dependency), color_formula=Depends(self.color_formula_dependency), @@ -555,7 +558,7 @@ def tile( y, z, tilesize=scale * 256, - buffer=buffer, + **tile_params, **layer_params, **dataset_params, ) @@ -623,7 +626,7 @@ def tilejson( ] = None, layer_params=Depends(self.layer_dependency), dataset_params=Depends(self.dataset_dependency), - buffer=Depends(BufferParams), + tile_params=Depends(self.tile_dependency), post_process=Depends(self.process_dependency), rescale=Depends(self.rescale_dependency), color_formula=Depends(self.color_formula_dependency), @@ -703,7 +706,7 @@ def map_viewer( ] = None, layer_params=Depends(self.layer_dependency), dataset_params=Depends(self.dataset_dependency), - buffer=Depends(BufferParams), + tile_params=Depends(self.tile_dependency), post_process=Depends(self.process_dependency), rescale=Depends(self.rescale_dependency), color_formula=Depends(self.color_formula_dependency), @@ -765,7 +768,7 @@ def wmts( ] = None, layer_params=Depends(self.layer_dependency), dataset_params=Depends(self.dataset_dependency), - buffer=Depends(BufferParams), + tile_params=Depends(self.tile_dependency), post_process=Depends(self.process_dependency), rescale=Depends(self.rescale_dependency), color_formula=Depends(self.color_formula_dependency), diff --git a/src/titiler/mosaic/titiler/mosaic/factory.py b/src/titiler/mosaic/titiler/mosaic/factory.py index fad9104ea..60cab4d89 100644 --- a/src/titiler/mosaic/titiler/mosaic/factory.py +++ b/src/titiler/mosaic/titiler/mosaic/factory.py @@ -24,7 +24,7 @@ from starlette.responses import HTMLResponse, Response from typing_extensions import Annotated -from titiler.core.dependencies import BufferParams, CoordCRSParams, DefaultDependency +from titiler.core.dependencies import CoordCRSParams, DefaultDependency, TileParams from titiler.core.factory import BaseTilerFactory, img_endpoint_params from titiler.core.models.mapbox import TileJSON from titiler.core.resources.enums import ImageType, MediaType, OptionalHeader @@ -65,6 +65,9 @@ class MosaicTilerFactory(BaseTilerFactory): pixel_selection_dependency: Callable[..., MosaicMethodBase] = PixelSelectionParams + # Tile/Tilejson/WMTS Dependencies + tile_dependency: Type[DefaultDependency] = TileParams + supported_tms: TileMatrixSets = morecantile_tms default_tms: str = "WebMercatorQuad" @@ -266,7 +269,7 @@ def tile( layer_params=Depends(self.layer_dependency), dataset_params=Depends(self.dataset_dependency), pixel_selection=Depends(self.pixel_selection_dependency), - buffer=Depends(BufferParams), + tile_params=Depends(self.tile_dependency), post_process=Depends(self.process_dependency), rescale=Depends(self.rescale_dependency), color_formula=Depends(self.color_formula_dependency), @@ -313,7 +316,7 @@ def tile( pixel_selection=pixel_selection, tilesize=scale * 256, threads=threads, - buffer=buffer, + **tile_params, **layer_params, **dataset_params, ) @@ -385,7 +388,7 @@ def tilejson( layer_params=Depends(self.layer_dependency), dataset_params=Depends(self.dataset_dependency), pixel_selection=Depends(self.pixel_selection_dependency), - buffer=Depends(BufferParams), + tile_params=Depends(self.tile_dependency), post_process=Depends(self.process_dependency), rescale=Depends(self.rescale_dependency), color_formula=Depends(self.color_formula_dependency), @@ -478,7 +481,8 @@ def map_viewer( layer_params=Depends(self.layer_dependency), dataset_params=Depends(self.dataset_dependency), pixel_selection=Depends(self.pixel_selection_dependency), - buffer=Depends(BufferParams), + tile_params=Depends(self.tile_dependency), + post_process=Depends(self.process_dependency), rescale=Depends(self.rescale_dependency), color_formula=Depends(self.color_formula_dependency), colormap=Depends(self.colormap_dependency), @@ -541,7 +545,7 @@ def wmts( layer_params=Depends(self.layer_dependency), dataset_params=Depends(self.dataset_dependency), pixel_selection=Depends(self.pixel_selection_dependency), - buffer=Depends(BufferParams), + tile_params=Depends(self.tile_dependency), post_process=Depends(self.process_dependency), rescale=Depends(self.rescale_dependency), color_formula=Depends(self.color_formula_dependency), From 4fb7f3f1290994bd3731d709daa1d6346898c600 Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Mon, 23 Oct 2023 23:39:54 +0200 Subject: [PATCH 2/2] update docs --- docs/mkdocs.yml | 12 +- docs/src/advanced/dependencies.md | 1017 +++++++++++++++++----- docs/src/advanced/tiler_factories.md | 117 ++- docs/src/endpoints/cog.md | 12 +- docs/src/endpoints/stac.md | 9 +- docs/src/external_links.md | 20 +- src/titiler/core/titiler/core/factory.py | 17 +- 7 files changed, 946 insertions(+), 258 deletions(-) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index da7fb85bc..b662f4a1c 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -37,6 +37,12 @@ nav: - Rendering: "advanced/rendering.md" # - APIRoute and environment variables: "advanced/APIRoute_and_environment_variables.md" + - Endpoints documentation: + - /cog: "endpoints/cog.md" + - /stac: "endpoints/stac.md" + - /mosaicjson: "endpoints/mosaic.md" + - /tileMatrixSets: "endpoints/tms.md" + - Examples: - Create dynamic tilers with TiTiler: - Minimal COG Tiler: "examples/code/mini_cog_tiler.md" @@ -78,12 +84,6 @@ nav: - factory: api/titiler/mosaic/factory.md - errors: api/titiler/mosaic/errors.md - - titiler.application: - - /cog: "endpoints/cog.md" - - /stac: "endpoints/stac.md" - - /mosaicjson: "endpoints/mosaic.md" - - /tileMatrixSets: "endpoints/tms.md" - - Deployment: - Amazon Web Services: - Intro: "deployment/aws/intro.md" diff --git a/docs/src/advanced/dependencies.md b/docs/src/advanced/dependencies.md index c70dd89fe..b7837522f 100644 --- a/docs/src/advanced/dependencies.md +++ b/docs/src/advanced/dependencies.md @@ -5,31 +5,17 @@ In titiler `Factories`, we use the dependencies to define the inputs for each en Example: ```python -# Custom Dependency - from dataclasses import dataclass -from typing import Optional - from fastapi import Depends, FastAPI, Query from titiler.core.dependencies import DefaultDependency - -from rio_tiler.io import COGReader +from typing_extensions import Annotated +from rio_tiler.io import Reader @dataclass class ImageParams(DefaultDependency): - """Common Preview/Crop parameters.""" - - max_size: Optional[int] = Query( - 1024, description="Maximum image size to read onto." - ) - height: Optional[int] = Query(None, description="Force output image height.") - width: Optional[int] = Query(None, description="Force output image width.") - - def __post_init__(self): - """Post Init.""" - if self.width and self.height: - self.max_size = None - + max_size: Annotated[ + int, Query(description="Maximum image size to read onto.") + ] = 1024 app = FastAPI() @@ -39,15 +25,10 @@ def preview( url: str = Query(..., description="data set URL"), params: ImageParams = Depends(), ): - - with COGReader(url) as cog: + with Reader(url) as cog: img = cog.preview(**params) # classes built with `DefaultDependency` can be unpacked # or - img = cog.preview( - max_size=params.max_size, - height=params.height, - width=params.width, - ) + img = cog.preview(max_size=params.max_size) ... ``` @@ -57,236 +38,717 @@ def preview( Using `titiler.core.dependencies.DefaultDependency`, we can `unpack` the class as if it was a dictionary, which helps with customization. +#### AssetsParams -## Factories Dependencies +Define `assets`. -The `factories` allow users to set multiple default dependencies. Here is the list of common dependencies and their **default** values. +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **assets** | Query (str) | No | None -### BaseTilerFactory +
-#### path_dependency +```python +@dataclass +class AssetsParams(DefaultDependency): + """Assets parameters.""" -Set dataset path (url). + assets: List[str] = Query( + None, + title="Asset names", + description="Asset's names.", + examples={ + "one-asset": { + "description": "Return results for asset `data`.", + "value": ["data"], + }, + "multi-assets": { + "description": "Return results for assets `data` and `cog`.", + "value": ["data", "cog"], + }, + }, + ) +``` + +
+ + +#### AssetsBidxParams + +Define `assets` with option of `per-asset` expression with `asset_expression` option. + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **assets** | Query (str) | No | None +| **asset_indexes** | Query (str) | No | None +| **asset_expression** | Query (str) | No | False + +
```python -def DatasetPathParams( - url: str = Query(..., description="Dataset URL") -) -> str: - """Create dataset path from args""" - return url +@dataclass +class AssetsBidxParams(AssetsParams): + """Assets, Asset's band Indexes and Asset's band Expression parameters.""" + + asset_indexes: Annotated[ + Optional[Sequence[str]], + Query( + title="Per asset band indexes", + description="Per asset band indexes", + alias="asset_bidx", + examples={ + "one-asset": { + "description": "Return indexes 1,2,3 of asset `data`.", + "value": ["data|1;2;3"], + }, + "multi-assets": { + "description": "Return indexes 1,2,3 of asset `data` and indexes 1 of asset `cog`", + "value": ["data|1;2;3", "cog|1"], + }, + }, + ), + ] = None + + asset_expression: Annotated[ + Optional[Sequence[str]], + Query( + title="Per asset band expression", + description="Per asset band expression", + examples={ + "one-asset": { + "description": "Return results for expression `b1*b2+b3` of asset `data`.", + "value": ["data|b1*b2+b3"], + }, + "multi-assets": { + "description": "Return results for expressions `b1*b2+b3` for asset `data` and `b1+b3` for asset `cog`.", + "value": ["data|b1*b2+b3", "cog|b1+b3"], + }, + }, + ), + ] = None + + def __post_init__(self): + """Post Init.""" + if self.asset_indexes: + self.asset_indexes: Dict[str, Sequence[int]] = { # type: ignore + idx.split("|")[0]: list(map(int, idx.split("|")[1].split(","))) + for idx in self.asset_indexes + } + + if self.asset_expression: + self.asset_expression: Dict[str, str] = { # type: ignore + idx.split("|")[0]: idx.split("|")[1] for idx in self.asset_expression + } ``` -#### layer_dependency +
+ +#### AssetsBidxExprParams + +Define `assets`. + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **assets** | Query (str) | No\* | None +| **expression** | Query (str) | No\* | None +| **asset_indexes** | Query (str) | No | None +| **asset_as_band** | Query (bool) | No | False + +\* `assets` or `expression` is required. -Define band indexes or expression +
```python @dataclass -class BidxParams(DefaultDependency): - """Band Indexes parameters.""" +class AssetsBidxExprParams(AssetsParams): + """Assets, Expression and Asset's band Indexes parameters.""" + + expression: Annotated[ + Optional[str], + Query( + title="Band Math expression", + description="Band math expression between assets", + examples={ + "simple": { + "description": "Return results of expression between assets.", + "value": "asset1_b1 + asset2_b1 / asset3_b1", + }, + }, + ), + ] = None + + asset_indexes: Annotated[ + Optional[Sequence[str]], + Query( + title="Per asset band indexes", + description="Per asset band indexes (coma separated indexes)", + alias="asset_bidx", + examples={ + "one-asset": { + "description": "Return indexes 1,2,3 of asset `data`.", + "value": ["data|1,2,3"], + }, + "multi-assets": { + "description": "Return indexes 1,2,3 of asset `data` and indexes 1 of asset `cog`", + "value": ["data|1,2,3", "cog|1"], + }, + }, + ), + ] = None - indexes: Optional[List[int]] = Query( - None, - title="Band indexes", - alias="bidx", - description="Dataset band indexes", - examples={"one-band": {"value": [1]}, "multi-bands": {"value": [1, 2, 3]}}, - ) + asset_as_band: Annotated[ + Optional[bool], + Query( + title="Consider asset as a 1 band dataset", + description="Asset as Band", + ), + ] = None + + def __post_init__(self): + """Post Init.""" + if not self.assets and not self.expression: + raise MissingAssets( + "assets must be defined either via expression or assets options." + ) + + if self.asset_indexes: + self.asset_indexes: Dict[str, Sequence[int]] = { # type: ignore + idx.split("|")[0]: list(map(int, idx.split("|")[1].split(","))) + for idx in self.asset_indexes + } +``` + +
+ +#### AssetsBidxExprParamsOptional + +Define `assets`. Without requirement on assets nor expression. + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **assets** | Query (str) | No | None +| **expression** | Query (str) | No | None +| **asset_indexes** | Query (str) | No | None +| **asset_as_band** | Query (bool) | No | False + +
+```python @dataclass -class ExpressionParams(DefaultDependency): - """Expression parameters.""" +class AssetsBidxExprParamsOptional(AssetsBidxExprParams): + """Assets, Expression and Asset's band Indexes parameters but with no requirement.""" + + def __post_init__(self): + """Post Init.""" + if self.asset_indexes: + self.asset_indexes: Dict[str, Sequence[int]] = { # type: ignore + idx.split("|")[0]: list(map(int, idx.split("|")[1].split(","))) + for idx in self.asset_indexes + } +``` + +
+ + +#### BandsParams - expression: Optional[str] = Query( +Define `bands`. + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **bands** | Query (str) | No | None + +
+ +```python +@dataclass +class BandsParams(DefaultDependency): + """Band names parameters.""" + + bands: List[str] = Query( None, - title="Band Math expression", - description="rio-tiler's band math expression", + title="Band names", + description="Band's names.", examples={ - "simple": {"description": "Simple band math.", "value": "b1/b2"}, + "one-band": { + "description": "Return results for band `B01`.", + "value": ["B01"], + }, "multi-bands": { - "description": "Semicolon (;) delimited expressions (band1: b1/b2, band2: b2+b3).", - "value": "b1/b2;b2+b3", + "description": "Return results for bands `B01` and `B02`.", + "value": ["B01", "B02"], }, }, ) +``` + +
+ +#### BandsExprParams + +Define `bands`. + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **bands** | Query (str) | No\* | None +| **expression** | Query (str) | No\* | None + +\* `bands` or `expression` is required. + +
+ +```python @dataclass -class BidxExprParams(ExpressionParams, BidxParams): - """Band Indexes and Expression parameters.""" +class BandsExprParamsOptional(ExpressionParams, BandsParams): + """Optional Band names and Expression parameters.""" pass ``` -#### dataset_dependency +
+ +#### BandsExprParamsOptional -Overwrite nodata value, apply rescaling or change default resampling. +Define `bands`. + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **bands** | Query (str) | No | None +| **expression** | Query (str) | No | None + +
```python @dataclass -class DatasetParams(DefaultDependency): - """Low level WarpedVRT Optional parameters.""" +class BandsExprParamsOptional(ExpressionParams, BandsParams): + """Optional Band names and Expression parameters.""" - nodata: Optional[Union[str, int, float]] = Query( - None, title="Nodata value", description="Overwrite internal Nodata value" - ) - unscale: Optional[bool] = Query( - False, - title="Apply internal Scale/Offset", - description="Apply internal Scale/Offset", - ) - resampling_method: ResamplingName = Query( - ResamplingName.nearest, # type: ignore - alias="resampling", - description="Resampling method.", - ) + pass +``` - def __post_init__(self): - """Post Init.""" - if self.nodata is not None: - self.nodata = numpy.nan if self.nodata == "nan" else float(self.nodata) - self.resampling_method = self.resampling_method.value # type: ignore +
+ +#### `BidxParams` + +Define band indexes. + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **bidx** | Query (int) | No | None + +
+ +```python +@dataclass +class BidxParams(DefaultDependency): + """Band Indexes parameters.""" + + indexes: Annotated[ + Optional[List[int]], + Query( + title="Band indexes", + alias="bidx", + description="Dataset band indexes", + examples={"one-band": {"value": [1]}, "multi-bands": {"value": [1, 2, 3]}}, + ), + ] = None ``` -#### render_dependency +
+ +#### `ExpressionParams` -Image rendering options. +Define band expression. + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **expression** | Query (str) | No | None + + +
```python @dataclass -class ImageRenderingParams(DefaultDependency): - """Image Rendering options.""" +class ExpressionParams(DefaultDependency): + """Expression parameters.""" - add_mask: bool = Query( - True, alias="return_mask", description="Add mask to the output data." - ) + expression: Annotated[ + Optional[str], + Query( + title="Band Math expression", + description="rio-tiler's band math expression", + examples={ + "simple": {"description": "Simple band math.", "value": "b1/b2"}, + "multi-bands": { + "description": "Semicolon (;) delimited expressions (band1: b1/b2, band2: b2+b3).", + "value": "b1/b2;b2+b3", + }, + }, + ), + ] = None +``` + +
+ +#### `BidxExprParams` + +Define band indexes or expression. + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **bidx** | Query (int) | No | None +| **expression** | Query (str) | No | None + +
+ +```python +@dataclass +class BidxExprParams(ExpressionParams, BidxParams): + """Band Indexes and Expression parameters.""" + + pass ``` -#### colormap_dependency +
+ +#### `ColorFormulaParams` + +Color Formula option (see https://github.com/vincentsarago/color-operations). + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **color_formula** | Query (str) | No | None -Colormap options. +
```python +def ColorFormulaParams( + color_formula: Annotated[ + Optional[str], + Query( + title="Color Formula", + description="rio-color formula (info: https://github.com/mapbox/rio-color)", + ), + ] = None, +) -> Optional[str]: + """ColorFormula Parameter.""" + return color_formula +``` + +
+ +#### `ColorMapParams` + +Colormap options. See [titiler.core.dependencies](https://github.com/developmentseed/titiler/blob/e46c35c8927b207f08443a274544901eb9ef3914/src/titiler/core/titiler/core/dependencies.py#L18-L54). + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **colormap_name** | Query (str) | No | None +| **colormap** | Query (encoded json) | No | None + +
+ +```python +cmap = {} + def ColorMapParams( - colormap_name: ColorMapName = Query(None, description="Colormap name"), - colormap: str = Query(None, description="JSON encoded custom Colormap"), -) -> Optional[Union[Dict, Sequence]]: - """Colormap Dependency.""" + colormap_name: Annotated[ # type: ignore + Literal[tuple(cmap.list())], + Query(description="Colormap name"), + ] = None, + colormap: Annotated[ + Optional[str], Query(description="JSON encoded custom Colormap") + ] = None, +): if colormap_name: - return cmap.get(colormap_name.value) + return cmap.get(colormap_name) if colormap: try: - return json.loads( + c = json.loads( colormap, - object_hook=lambda x: {int(k): parse_color(v) for k, v in x.items()}, + object_hook=lambda x: { + int(k): parse_color(v) for k, v in x.items() + }, ) - except json.JSONDecodeError: + + # Make sure to match colormap type + if isinstance(c, Sequence): + c = [(tuple(inter), parse_color(v)) for (inter, v) in c] + + return c + except json.JSONDecodeError as e: raise HTTPException( status_code=400, detail="Could not parse the colormap value." - ) + ) from e return None ``` -#### reader_dependency +
-Additional reader options. Defaults to `DefaultDependency` (empty). +#### CoordCRSParams +Define input Coordinate Reference System. -#### Other Attributes +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **crs** | Query (str) | No | None -##### Supported TMS -The TMS dependency sets the available TMS for a tile endpoint. +
```python -# Allow all morecantile TMS -from morecantile import tms as default_tms +def CoordCRSParams( + crs: Annotated[ + Optional[str], + Query( + alias="coord_crs", + description="Coordinate Reference System of the input coords. Default to `epsg:4326`.", + ), + ] = None, +) -> Optional[CRS]: + """Coordinate Reference System Coordinates Param.""" + if crs: + return CRS.from_user_input(crs) + + return None +``` -tiler = TilerFactory(supported_tms=default_tms) +
+#### `DatasetParams` -# Restrict the TMS to `WebMercatorQuad` only -from morecantile import tms -from morecantile.defaults import TileMatrixSets +Overwrite `nodata` value, apply `rescaling` and change the `I/O` or `Warp` resamplings. -# Construct a TileMatrixSets object with only the `WebMercatorQuad` tms -default_tms = TileMatrixSets({"WebMercatorQuad": tms.get("WebMercatorQuad")}) -tiler = TilerFactory(supported_tms=default_tms) +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **nodata** | Query (str, int, float) | No | None +| **unscale** | Query (bool) | No | False +| **resampling** | Query (str) | No | 'nearest' +| **reproject** | Query (str) | No | 'nearest' + +
+ +```python +@dataclass +class DatasetParams(DefaultDependency): + """Low level WarpedVRT Optional parameters.""" + + nodata: Annotated[ + Optional[Union[str, int, float]], + Query( + title="Nodata value", + description="Overwrite internal Nodata value", + ), + ] = None + unscale: Annotated[ + bool, + Query( + title="Apply internal Scale/Offset", + description="Apply internal Scale/Offset. Defaults to `False`.", + ), + ] = False + resampling_method: Annotated[ + RIOResampling, + Query( + alias="resampling", + description="RasterIO resampling algorithm. Defaults to `nearest`.", + ), + ] = "nearest" + reproject_method: Annotated[ + WarpResampling, + Query( + alias="reproject", + description="WarpKernel resampling algorithm (only used when doing re-projection). Defaults to `nearest`.", + ), + ] = "nearest" + + def __post_init__(self): + """Post Init.""" + if self.nodata is not None: + self.nodata = numpy.nan if self.nodata == "nan" else float(self.nodata) + self.unscale = bool(self.unscale) ``` -##### Default TMS +
+ +#### `DatasetPathParams` + +Set dataset path. + +| Name | Type | Required | Default +| ------ | ----------|--------------------- |-------------- +| **url** | Query (str) | :warning: **Yes** :warning: | - -Set the default's TMS Identifier (default to `WebMercatorQuad`). + +
```python -# Create a Tile with it's default TMS being `WGS1984Quad` -tiler = TilerFactory(default_tms="WGS1984Quad") +def DatasetPathParams( + url: Annotated[str, Query(description="Dataset URL")] +) -> str: + """Create dataset path from args""" + return url ``` -### TilerFactory +
-The `TilerFactory` inherits dependency from `BaseTilerFactory`. -#### metadata_dependency +#### DstCRSParams -`rio_tiler.io.BaseReader.metadata()` methods options. +Define output Coordinate Reference System. + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **crs** | Query (str) | No | None + + +
+ +```python +def DstCRSParams( + crs: Annotated[ + Optional[str], + Query( + alias="dst_crs", + description="Output Coordinate Reference System.", + ), + ] = None, +) -> Optional[CRS]: + """Coordinate Reference System Coordinates Param.""" + if crs: + return CRS.from_user_input(crs) + + return None +``` + +
+ +#### HistogramParams + +Define *numpy*'s histogram options. + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **histogram_bins** | Query (encoded list of Number) | No | 10 +| **histogram_range** | Query (encoded list of Number) | No | None + +
```python @dataclass -class MetadataParams(DefaultDependency): - """Common Metadada parameters.""" +class HistogramParams(DefaultDependency): + """Numpy Histogram options.""" + + bins: Annotated[ + Optional[str], + Query( + alias="histogram_bins", + title="Histogram bins.", + description=""" +Defines the number of equal-width bins in the given range (10, by default). + +If bins is a sequence (comma `,` delimited values), it defines a monotonically increasing array of bin edges, including the rightmost edge, allowing for non-uniform bin widths. + +link: https://numpy.org/doc/stable/reference/generated/numpy.histogram.html + """, + examples={ + "simple": { + "description": "Defines the number of equal-width bins", + "value": 8, + }, + "array": { + "description": "Defines custom bin edges (comma `,` delimited values)", + "value": "0,100,200,300", + }, + }, + ), + ] = None - # Required params - pmin: float = Query(2.0, description="Minimum percentile") - pmax: float = Query(98.0, description="Maximum percentile") + range: Annotated[ + Optional[str], + Query( + alias="histogram_range", + title="Histogram range", + description=""" +Comma `,` delimited range of the bins. - # Optional params - max_size: Optional[int] = Query( - None, description="Maximum image size to read onto." - ) - histogram_bins: Optional[int] = Query(None, description="Histogram bins.") - histogram_range: Optional[str] = Query( - None, description="comma (',') delimited Min,Max histogram bounds" - ) - bounds: Optional[str] = Query( - None, - descriptions="comma (',') delimited Bounding box coordinates from which to calculate image statistics.", - ) +The lower and upper range of the bins. If not provided, range is simply (a.min(), a.max()). + +Values outside the range are ignored. The first element of the range must be less than or equal to the second. +range affects the automatic bin computation as well. + +link: https://numpy.org/doc/stable/reference/generated/numpy.histogram.html + """, + examples="0,1000", + ), + ] = None def __post_init__(self): """Post Init.""" - if self.max_size is not None: - self.kwargs["max_size"] = self.max_size - - if self.bounds: - self.kwargs["bounds"] = tuple(map(float, self.bounds.split(","))) - - hist_options = {} - if self.histogram_bins: - hist_options.update(dict(bins=self.histogram_bins)) - if self.histogram_range: - hist_options.update( - dict(range=list(map(float, self.histogram_range.split(",")))) - ) - if hist_options: - self.kwargs["hist_options"] = hist_options + if self.bins: + bins = self.bins.split(",") + if len(bins) == 1: + self.bins = int(bins[0]) # type: ignore + else: + self.bins = list(map(float, bins)) # type: ignore + else: + self.bins = 10 + + if self.range: + self.range = list(map(float, self.range.split(","))) # type: ignore ``` -#### img_preview_dependency +
+ +#### `ImageRenderingParams` + +Control output image rendering options. -Used in Statistics/Preview to define size of the output image. +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **return_mask** | Query (bool) | No | False + +
```python @dataclass -class PreviewParams(DefaultDependency): - """Common Preview parameters.""" +class ImageRenderingParams(DefaultDependency): + """Image Rendering options.""" - max_size: Optional[int] = Query( - 1024, description="Maximum image size to read onto." - ) - height: Optional[int] = Query(None, description="Force output image height.") - width: Optional[int] = Query(None, description="Force output image width.") + add_mask: Annotated[ + bool, + Query( + alias="return_mask", + description="Add mask to the output data. Defaults to `True`", + ), + ] = True +``` + +
+ +#### PartFeatureParams + +Same as `PreviewParams` but without default `max_size`. + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **max_size** | Query (int) | No | None +| **height** | Query (int) | No | None +| **width** | Query (int) | No | None + +
+ +```python +@dataclass +class PartFeatureParams(DefaultDependency): + """Common parameters for bbox and feature.""" + + max_size: Annotated[Optional[int], "Maximum image size to read onto."] = None + height: Annotated[Optional[int], "Force output image height."] = None + width: Annotated[Optional[int], "Force output image width."] = None def __post_init__(self): """Post Init.""" @@ -294,84 +756,215 @@ class PreviewParams(DefaultDependency): self.max_size = None ``` -#### img_part_dependency +
+ +#### PixelSelectionParams + +In `titiler.mosaic`, define pixel-selection method to apply. -Same as `PreviewParams` but without default `max_size`. Used in `/bbox`, `/feature` and `/statistics [POST]` endpoints. +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **pixel_selection** | Query (str) | No | 'first' + + +
+ +```python +def PixelSelectionParams( + pixel_selection: Annotated[ # type: ignore + Literal[tuple([e.name for e in PixelSelectionMethod])], + Query(description="Pixel selection method."), + ] = "first", +) -> MosaicMethodBase: + """ + Returns the mosaic method used to combine datasets together. + """ + return PixelSelectionMethod[pixel_selection].value() +``` + +
+ +#### PreviewParams + +Define image output size. + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **max_size** | Query (int) | No | 1024 +| **height** | Query (int) | No | None +| **width** | Query (int) | No | None + +
```python @dataclass -class PartFeatureParams(PreviewParams): - """Common parameters for bbox and feature.""" +class PreviewParams(DefaultDependency): + """Common Preview parameters.""" - max_size: Annotated[Optional[int], "Maximum image size to read onto."] = None + max_size: Annotated[int, "Maximum image size to read onto."] = 1024 height: Annotated[Optional[int], "Force output image height."] = None width: Annotated[Optional[int], "Force output image width."] = None + + def __post_init__(self): + """Post Init.""" + if self.width and self.height: + self.max_size = None ``` -### MultiBaseTilerFactory +
-The `MultiBaseTilerFactory` inherits dependency from `TilerFactory` and `BaseTilerFactory`. +#### `RescalingParams` -#### assets_dependency +Set Min/Max values to rescale from, to 0 -> 255. -Define `assets`. +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **rescale** | Query (str, comma delimited Numer) | No | None + +
```python -@dataclass -class AssetsParams(DefaultDependency): - """Assets parameters.""" +def RescalingParams( + rescale: Annotated[ + Optional[List[str]], + Query( + title="Min/Max data Rescaling", + description="comma (',') delimited Min,Max range. Can set multiple time for multiple bands.", + examples=["0,2000", "0,1000", "0,10000"], # band 1 # band 2 # band 3 + ), + ] = None, +) -> Optional[RescaleType]: + """Min/Max data Rescaling""" + if rescale: + return [tuple(map(float, r.replace(" ", "").split(","))) for r in rescale] - assets: List[str] = Query( - None, - title="Asset names", - description="Asset's names.", - examples={ - "one-asset": { - "description": "Return results for asset `data`.", - "value": ["data"], - }, - "multi-assets": { - "description": "Return results for assets `data` and `cog`.", - "value": ["data", "cog"], - }, - }, - ) + return None ``` -### MultiBandTilerFactory +
-The `MultiBaseTilerFactory` inherits dependency from `TilerFactory` and `BaseTilerFactory`. +#### StatisticsParams -#### bands_dependency +Define options for *rio-tiler*'s statistics method. -Define `bands`. +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **categorical** | Query (bool) | No | False +| **categories** | Query (list of Number) | No | None +| **p** | Query (list of Number) | No | [2, 98] + +
```python @dataclass -class BandsParams(DefaultDependency): - """Band names parameters.""" +class StatisticsParams(DefaultDependency): + """Statistics options.""" + + categorical: Annotated[ + bool, + Query(description="Return statistics for categorical dataset."), + ] = False + categories: Annotated[ + Optional[List[Union[float, int]]], + Query( + alias="c", + title="Pixels values for categories.", + description="List of values for which to report counts.", + examples=[1, 2, 3], + ), + ] = None + percentiles: Annotated[ + Optional[List[int]], + Query( + alias="p", + title="Percentile values", + description="List of percentile values (default to [2, 98]).", + examples=[2, 5, 95, 98], + ), + ] = None - bands: List[str] = Query( - None, - title="Band names", - description="Band's names.", - examples={ - "one-band": { - "description": "Return results for band `B01`.", - "value": ["B01"], - }, - "multi-bands": { - "description": "Return results for bands `B01` and `B02`.", - "value": ["B01", "B02"], - }, - }, - ) + def __post_init__(self): + """Set percentiles default.""" + if not self.percentiles: + self.percentiles = [2, 98] ``` -### MosaicTilerFactory +
-The `MultiBaseTilerFactory` inherits dependency from `BaseTilerFactory`. +#### TileParams + +Defile `buffer` and `padding` to apply at tile creation. + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **buffer** | Query (float) | No | None +| **padding** | Query (int) | No | None + +
+ +```python +@dataclass +class TileParams(DefaultDependency): + """Tile options.""" + + buffer: Annotated[ + Optional[float], + Query( + gt=0, + title="Tile buffer.", + description="Buffer on each side of the given tile. It must be a multiple of `0.5`. Output **tilesize** will be expanded to `tilesize + 2 * buffer` (e.g 0.5 = 257x257, 1.0 = 258x258).", + ), + ] = None + + padding: Annotated[ + Optional[int], + Query( + gt=0, + title="Tile padding.", + description="Padding to apply to each tile edge. Helps reduce resampling artefacts along edges. Defaults to `0`.", + ), + ] = None +``` + +
+ +#### `algorithm.dependency` + +Control which `algorithm` to apply to the data. + +See [titiler.core.algorithm](https://github.com/developmentseed/titiler/blob/e46c35c8927b207f08443a274544901eb9ef3914/src/titiler/core/titiler/core/algorithm/__init__.py#L54-L79). + +| Name | Type | Required | Default +| ------ | ----------|----------|-------------- +| **algorithm** | Query (str) | No | None +| **algorithm_params** | Query (encoded json) | No | None + +
+ +```python +algorithms = {} + +def post_process( + algorithm: Annotated[ + Literal[tuple(algorithms.keys())], + Query(description="Algorithm name"), + ] = None, + algorithm_params: Annotated[ + Optional[str], + Query(description="Algorithm parameter"), + ] = None, +) -> Optional[BaseAlgorithm]: + """Data Post-Processing options.""" + kwargs = json.loads(algorithm_params) if algorithm_params else {} + if algorithm: + try: + return algorithms.get(algorithm)(**kwargs) + + except ValidationError as e: + raise HTTPException(status_code=400, detail=str(e)) from e + + return None +``` -#### backend_dependency +
-Additional backend options. Defaults to `DefaultDependency` (empty). diff --git a/docs/src/advanced/tiler_factories.md b/docs/src/advanced/tiler_factories.md index 896c59a3b..129a23ff8 100644 --- a/docs/src/advanced/tiler_factories.md +++ b/docs/src/advanced/tiler_factories.md @@ -1,16 +1,65 @@ Tiler factories are helper functions that let users create a FastAPI router (`fastapi.APIRouter`) with a minimal set of endpoints. -### `titiler.core.factory.TilerFactory` +### BaseTilerFactory + +All **Factories** are built from an [abstract based class](https://docs.python.org/3/library/abc.html) which is used to define commons attributes and utility functions shared between all factories. + +#### Methods + +- **register_routes**: Abstract method which needs to be define by each factories. +- **url_for**: Method to construct endpoint URL +- **add_route_dependencies**: Add dependencies to routes. + +#### Attributes + +- **reader**: Dataset Reader **required**. +- **router**: FastAPI router. Defaults to `fastapi.APIRouter`. +- **path_dependency**: Dependency to use to define the dataset url. Defaults to `titiler.core.dependencies.DatasetPathParams`. +- **layer_dependency**: Dependency to define band indexes or expression. Defaults to `titiler.core.dependencies.BidxExprParams`. +- **dataset_dependency**: Dependency to overwrite `nodata` value, apply `rescaling` and change the `I/O` or `Warp` resamplings. Defaults to `titiler.core.dependencies.DatasetParams`. +- **process_dependency**: Dependency to control which `algorithm` to apply to the data. Defaults to `titiler.core.algorithm.algorithms.dependency`. +- **rescale_dependency**: Dependency to set Min/Max values to rescale from, to 0 -> 255. Defaults to `titiler.core.dependencies.RescalingParams`. +- **color_formula_dependency**: Dependency to define the Color Formula. Defaults to `titiler.core.dependencies.ColorFormulaParams`. +- **colormap_dependency**: Dependency to define the Colormap options. Defaults to `titiler.core.dependencies.ColorMapParams` +- **render_dependency**: Dependency to control output image rendering options. Defaults to `titiler.core.dependencies.ImageRenderingParams` +- **environment_dependency**: Dependency to defile GDAL environment at runtime. Default to `lambda: {}`. + +- **supported_tms**: +- **default_tms**: Set default `TileMatrixSet` identifier to use. Defaults to `WebMercatorQuad`. +- **router_prefix**: Set prefix to all factory's endpoint. Defaults to `""`. +- **optional_headers**: List of `OptionalHeader` which endpoints could add (if implemented). Defaults to `[]`. +- **route_dependencies**: Additional routes dependencies to add after routes creations. Defaults to `[]`. +- **extension**: TiTiler extensions to register after endpoints creations. Defaults to `[]`. +- **templates**: *Jinja2* templates to use in endpoints. Defaults to `titiler.core.factory.DEFAULT_TEMPLATES` + + +## TilerFactory + +Factory meant to create endpoints for single dataset using [*rio-tiler*'s `Reader`](https://cogeotiff.github.io/rio-tiler/readers/#rio_tileriorasterioreader). + +#### Attributes + +- **reader**: Dataset Reader. Defaults to `Reader`. +- **stats_dependency**: Dependency to define options for *rio-tiler*'s statistics method used in `/statistics` endpoints. Defaults to `titiler.core.dependencies.StatisticsParams`. +- **histogram_dependency**: Dependency to define *numpy*'s histogram options used in `/statistics` endpoints. Defaults to `titiler.core.dependencies.HistogramParams`. +- **img_preview_dependency**: Dependency to define image size for `/preview` and `/statistics` endpoints. Defaults to `titiler.core.dependencies.PreviewParams`. +- **img_part_dependency**: Dependency to define image size for `/bbox` and `/feature` endpoints. Defaults to `titiler.core.dependencies.PartFeatureParams`. +- **tile_dependency**: Dependency to defile `buffer` and `padding` to apply at tile creation. Defaults to `titiler.core.dependencies.TileParams`. +- **add_preview**: . Defaults to `True` +- **add_part**: . Defaults to `True` +- **add_viewer**: . Defaults to `True` + +#### Endpoints ```python from fastapi import FastAPI from titiler.core.factory import TilerFactory -app = FastAPI(description="A lightweight Cloud Optimized GeoTIFF tile server") +app = FastAPI() cog = TilerFactory() -app.include_router(cog.router, tags=["Cloud Optimized GeoTIFF"]) +app.include_router(cog.router ``` | Method | URL | Output | Description @@ -30,19 +79,28 @@ app.include_router(cog.router, tags=["Cloud Optimized GeoTIFF"]) | `GET` | `/map` | HTML | return a simple map viewer | `GET` | `[/{tileMatrixSetId}]/map` | HTML | return a simple map viewer -### `titiler.core.factory.MultiBaseTilerFactory` -Custom `TilerFactory` to be used with `rio_tiler.io.MultiBaseReader` type readers. +## MultiBaseTilerFactory + +Custom `TilerFactory` to be used with [`rio_tiler.io.MultiBaseReader`](http://127.0.0.1:8000/titiler/advanced/tiler_factories/#titilercorefactorymultibasetilerfactory) type readers (e.g `rio_tiler.io.STACReader`). + +#### Attributes + +- **layer_dependency**: Dependency to define assets or expression. Defaults to `titiler.core.dependencies.AssetsBidxExprParams`. +- **assets_dependency**: Dependency to define assets to be used. Defaults to `titiler.core.dependencies.AssetsParams`. + +#### Endpoints ```python from fastapi import FastAPI -from rio_tiler.io import STACReader # rio_tiler.io.STACReader is a MultiBaseReader +# rio_tiler.io.STACReader is a MultiBaseReader +from rio_tiler.io import STACReader from titiler.core.factory import MultiBaseTilerFactory -app = FastAPI(description="A lightweight STAC tile server") -cog = MultiBaseTilerFactory(reader=STACReader) -app.include_router(cog.router, tags=["STAC"]) +app = FastAPI() +stac = MultiBaseTilerFactory(reader=STACReader) +app.include_router(stac.router]) ``` | Method | URL | Output | Description @@ -63,25 +121,37 @@ app.include_router(cog.router, tags=["STAC"]) | `POST` | `/feature[/{width}x{height}][.{format}]` | image/bin | create an image from a geojson feature intersecting assets (**Optional**) | `GET` | `[/{tileMatrixSetId}]/map` | HTML | return a simple map viewer -### `titiler.core.factory.MultiBandTilerFactory` +## MultiBandTilerFactory + +Custom `TilerFactory` to be used with [`rio_tiler.io.MultiBandReader`](https://cogeotiff.github.io/rio-tiler/advanced/custom_readers/#multibasereader) type readers. + +#### Attributes -Custom `TilerFactory` to be used with `rio_tiler.io.MultiBandReader` type readers. +- **layer_dependency**: Dependency to define assets or expression. Defaults to `titiler.core.dependencies.BandsExprParams`. +- **bands_dependency**: Dependency to define bands to be used. Defaults to `titiler.core.dependencies.BandsParams`. + +#### Endpoints ```python from fastapi import FastAPI, Query -from rio_tiler_pds.landsat.aws import LandsatC2Reader # rio_tiler_pds.landsat.aws.LandsatC2Reader is a MultiBandReader - +# rio_tiler_pds.landsat.aws.LandsatC2Reader is a MultiBandReader +from rio_tiler_pds.landsat.aws import LandsatC2Reader from titiler.core.factory import MultiBandTilerFactory -def SceneIDParams(sceneid: str = Query(..., description="Landsat Scene ID")) -> str: +def SceneIDParams( + sceneid: Annotated[ + str, + Query(description="Landsat Scene ID") + ] +) -> str: """Use `sceneid` in query instead of url.""" return sceneid -app = FastAPI(description="A lightweight Landsat Collection 2 tile server") -cog = MultiBandTilerFactory(reader=LandsatC2Reader, path_dependency=SceneIDParams) -app.include_router(cog.router, tags=["Landsat"]) +app = FastAPI() +landsat = MultiBandTilerFactory(reader=LandsatC2Reader, path_dependency=SceneIDParams) +app.include_router(landsat.router) ``` | Method | URL | Output | Description @@ -101,8 +171,17 @@ app.include_router(cog.router, tags=["Landsat"]) | `POST` | `/feature[/{width}x{height}][.{format}]` | image/bin | create an image from a geojson feature | `GET` | `[/{tileMatrixSetId}]/map` | HTML | return a simple map viewer -### `titiler.mosaic.factory.MosaicTilerFactory` +## MosaicTilerFactory + + +In `titiler.mosaic.factory`, custom `TilerFactory` + +#### Attributes + +- **layer_dependency**: Dependency to define assets or expression. Defaults to `titiler.core.dependencies.BandsExprParams`. +- **bands_dependency**: Dependency to define bands to be used. Defaults to `titiler.core.dependencies.BandsParams`. +#### Endpoints | Method | URL | Output | Description | ------ | --------------------------------------------------------------- |--------------------------------------------------- |-------------- @@ -122,7 +201,7 @@ app.include_router(cog.router, tags=["Landsat"]) !!! Important - **Factories** are built around [`rio_tiler.io.BaseReader`](https://cogeotiff.github.io/rio-tiler/advanced/custom_readers/), which defines basic methods to access datasets (e.g COG or STAC). The default reader is `COGReader` for `TilerFactory` and `MosaicBackend` for `MosaicTilerFactory`. + **Factories** are built around [`rio_tiler.io.BaseReader`](https://cogeotiff.github.io/rio-tiler/advanced/custom_readers/), which defines basic methods to access datasets (e.g COG or STAC). The default reader is `Reader` for `TilerFactory` and `MosaicBackend` for `MosaicTilerFactory`. Factories classes use [dependencies injection](dependencies.md) to define most of the endpoint options. diff --git a/docs/src/endpoints/cog.md b/docs/src/endpoints/cog.md index da7cca295..de9967c2a 100644 --- a/docs/src/endpoints/cog.md +++ b/docs/src/endpoints/cog.md @@ -52,8 +52,8 @@ The `/cog` routes are based on `titiler.core.factory.TilerFactory` but with `cog - **colormap** (str): JSON encoded custom Colormap. - **colormap_name** (str): rio-tiler color map name. - **return_mask** (bool): Add mask to the output data. Default is True. - - **buffer** (float): Add buffer on each side of the tile (e.g 0.5 = 257x257, 1.0 = 258x258). - - **algorithm** (str): Custom algorithm name (e.g `hillshade`). + - **buffer** (float): Buffer on each side of the given tile. It must be a multiple of `0.5`. Output **tilesize** will be expanded to `tilesize + 2 * buffer` (e.g 0.5 = 257x257, 1.0 = 258x258). + - **padding** (int): Padding to apply to each tile edge. Helps reduce resampling artefacts along edges. Defaults to `0`. - **algorithm** (str): Custom algorithm name (e.g `hillshade`). - **algorithm_params** (str): JSON encoded algorithm parameters. Example: @@ -217,8 +217,8 @@ Example: - **colormap** (str): JSON encoded custom Colormap. - **colormap_name** (str): rio-tiler color map name. - **return_mask** (bool): Add mask to the output data. Default is True. - - **buffer** (float): Add buffer on each side of the tile (e.g 0.5 = 257x257, 1.0 = 258x258). - - **algorithm** (str): Custom algorithm name (e.g `hillshade`). + - **buffer** (float): Buffer on each side of the given tile. It must be a multiple of `0.5`. Output **tilesize** will be expanded to `tilesize + 2 * buffer` (e.g 0.5 = 257x257, 1.0 = 258x258). + - **padding** (int): Padding to apply to each tile edge. Helps reduce resampling artefacts along edges. Defaults to `0`. - **algorithm** (str): Custom algorithm name (e.g `hillshade`). - **algorithm_params** (str): JSON encoded algorithm parameters. Example: @@ -251,8 +251,8 @@ Example: - **colormap** (str): JSON encoded custom Colormap. - **colormap_name** (str): rio-tiler color map name. - **return_mask** (bool): Add mask to the output data. Default is True. - - **buffer** (float): Add buffer on each side of the tile (e.g 0.5 = 257x257, 1.0 = 258x258). - - **algorithm** (str): Custom algorithm name (e.g `hillshade`). + - **buffer** (float): Buffer on each side of the given tile. It must be a multiple of `0.5`. Output **tilesize** will be expanded to `tilesize + 2 * buffer` (e.g 0.5 = 257x257, 1.0 = 258x258). + - **padding** (int): Padding to apply to each tile edge. Helps reduce resampling artefacts along edges. Defaults to `0`. - **algorithm** (str): Custom algorithm name (e.g `hillshade`). - **algorithm_params** (str): JSON encoded algorithm parameters. Example: diff --git a/docs/src/endpoints/stac.md b/docs/src/endpoints/stac.md index bc5c33658..f1a21ce1d 100644 --- a/docs/src/endpoints/stac.md +++ b/docs/src/endpoints/stac.md @@ -54,7 +54,8 @@ The `/stac` routes are based on `titiler.core.factory.MultiBaseTilerFactory` but - **colormap** (str): JSON encoded custom Colormap. - **colormap_name** (str): rio-tiler color map name. - **return_mask** (bool): Add mask to the output data. Default is True. - - **buffer** (float): Add buffer on each side of the tile (e.g 0.5 = 257x257, 1.0 = 258x258). + - **buffer** (float): Buffer on each side of the given tile. It must be a multiple of `0.5`. Output **tilesize** will be expanded to `tilesize + 2 * buffer` (e.g 0.5 = 257x257, 1.0 = 258x258). + - **padding** (int): Padding to apply to each tile edge. Helps reduce resampling artefacts along edges. Defaults to `0`. - **algorithm** (str): Custom algorithm name (e.g `hillshade`). - **algorithm_params** (str): JSON encoded algorithm parameters. @@ -237,7 +238,8 @@ Example: - **colormap** (str): JSON encoded custom Colormap. - **colormap_name** (str): rio-tiler color map name. - **return_mask** (bool): Add mask to the output data. Default is True. - - **buffer** (float): Add buffer on each side of the tile (e.g 0.5 = 257x257, 1.0 = 258x258). + - **buffer** (float): Buffer on each side of the given tile. It must be a multiple of `0.5`. Output **tilesize** will be expanded to `tilesize + 2 * buffer` (e.g 0.5 = 257x257, 1.0 = 258x258). + - **padding** (int): Padding to apply to each tile edge. Helps reduce resampling artefacts along edges. Defaults to `0`. - **algorithm** (str): Custom algorithm name (e.g `hillshade`). - **algorithm_params** (str): JSON encoded algorithm parameters. @@ -275,7 +277,8 @@ Example: - **colormap** (str): JSON encoded custom Colormap. - **colormap_name** (str): rio-tiler color map name. - **return_mask** (bool): Add mask to the output data. Default is True. - - **buffer** (float): Add buffer on each side of the tile (e.g 0.5 = 257x257, 1.0 = 258x258). + - **buffer** (float): Buffer on each side of the given tile. It must be a multiple of `0.5`. Output **tilesize** will be expanded to `tilesize + 2 * buffer` (e.g 0.5 = 257x257, 1.0 = 258x258). + - **padding** (int): Padding to apply to each tile edge. Helps reduce resampling artefacts along edges. Defaults to `0`. - **algorithm** (str): Custom algorithm name (e.g `hillshade`). - **algorithm_params** (str): JSON encoded algorithm parameters. diff --git a/docs/src/external_links.md b/docs/src/external_links.md index 9cd60516a..68629b6f4 100644 --- a/docs/src/external_links.md +++ b/docs/src/external_links.md @@ -13,24 +13,38 @@ * David McCracken's Blog on [Plotly Dash Interactive Mapping - Dash Leaflet & TiTiler](https://www.pywram.com/t/blog-plotly-dash-interactive-mapping-dash-leaflet-titiler/287) +## TiTiler extensions/plugins + +* [stac-utils/titiler-pgstac](https://github.com/stac-utils/titiler-pgstac): TiTiler extension which connects to a PgSTAC database to create dynamic mosaics based on search queries. + +* [developmentseed/titiler-xarray](https://github.com/developmentseed/titiler-xarray): TiTiler extension for xarray + +* [developmentseed/titiler-images](https://github.com/developmentseed/titiler-images): TiTiler demo application for Sentinel-2 Digital Twin dataset + + ## Projects / Demo using TiTiler * ESA Charter Mapper [geobrowser](https://docs.charter.uat.esaportal.eu/webPortal/geobrowser/titiler/) * [developmentseed/titiler-digitaltwin](https://github.com/developmentseed/titiler-digitaltwin): TiTiler demo application for Sentinel-2 Digital Twin dataset -* [developmentseed/titiler-pds](https://github.com/developmentseed/titiler-pds): TiTiler demo application for Sentinel-2 and Landsat-8 AWS Public Datasets - * [developmentseed/titiler-mvt](https://github.com/developmentseed/titiler-mvt): TiTiler demo application to create Mapbox Vector Tiles from COG +* [developmentseed/titiler-pds](https://github.com/developmentseed/titiler-pds): TiTiler demo application for Sentinel-2 and Landsat-8 AWS Public Datasets + * [stac-utils/stac-fastapi](https://github.com/stac-utils/stac-fastapi): STAC API implementation with FastAPI. * [c-core-labs/stac-api](https://github.com/c-core-labs/stac-api): STAC compliant API implementation (built from stac-fastapi) -* [lambgeo/titiler-layer](https://github.com/lambgeo/titiler-layer): TiTiler Lambda layers for easy deployment on AWS +* [developmentseed/titiler-lambda-layer](https://github.com/developmentseed/titiler-lambda-layer): TiTiler Lambda layers for easy deployment on AWS * [Terradue/Stars](https://github.com/Terradue/Stars): Spatio Temporal Asset Runtime Services +* [developmentseed/rio-viz](https://github.com/developmentseed/rio-viz): Visualize Cloud Optimized GeoTIFF in browser + +* [developmentseed/pearl-backend](https://github.com/developmentseed/pearl-backend): PEARL (Planetary Computer Land Cover Mapping) Platform API and Infrastructure + +* [microsoft/planetary-computer-apis](https://github.com/microsoft/planetary-computer-apis): Microsoft Planetary Computer APIs ## Conferences / presentations / videos diff --git a/src/titiler/core/titiler/core/factory.py b/src/titiler/core/titiler/core/factory.py index ff628029c..e87822879 100644 --- a/src/titiler/core/titiler/core/factory.py +++ b/src/titiler/core/titiler/core/factory.py @@ -137,24 +137,23 @@ class BaseTilerFactory(metaclass=abc.ABCMeta): # Path Dependency path_dependency: Callable[..., Any] = DatasetPathParams - # Rasterio Dataset Options (nodata, unscale, resampling, reproject) - dataset_dependency: Type[DefaultDependency] = DatasetParams - # Indexes/Expression Dependencies layer_dependency: Type[DefaultDependency] = BidxExprParams - # Image rendering Dependencies - render_dependency: Type[DefaultDependency] = ImageRenderingParams - colormap_dependency: Callable[..., Optional[ColorMapType]] = ColorMapParams - color_formula_dependency: Callable[..., Optional[str]] = ColorFormulaParams - - rescale_dependency: Callable[..., Optional[RescaleType]] = RescalingParams + # Rasterio Dataset Options (nodata, unscale, resampling, reproject) + dataset_dependency: Type[DefaultDependency] = DatasetParams # Post Processing Dependencies (algorithm) process_dependency: Callable[ ..., Optional[BaseAlgorithm] ] = available_algorithms.dependency + # Image rendering Dependencies + rescale_dependency: Callable[..., Optional[RescaleType]] = RescalingParams + color_formula_dependency: Callable[..., Optional[str]] = ColorFormulaParams + colormap_dependency: Callable[..., Optional[ColorMapType]] = ColorMapParams + render_dependency: Type[DefaultDependency] = ImageRenderingParams + # Reader dependency reader_dependency: Type[DefaultDependency] = DefaultDependency