Skip to content

Commit

Permalink
add **render_params.kwargs ito pass custom render params (#259)
Browse files Browse the repository at this point in the history
* add `**render_params.kwargs` ito pass custom render params

* update changelog
  • Loading branch information
vincentsarago authored Mar 5, 2021
1 parent 989d1b4 commit 272d62d
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* adapt for cogeo-mosaic `3.0.0rc2` and add `backend_options` attribute in MosaicTilerFactory (https://github.com/developmentseed/titiler/pull/247)
* update FastAPI requirements
* update minimal python version to 3.6
* add `**render_params.kwargs` to pass custom render params in `image.render` method (https://github.com/developmentseed/titiler/pull/259)

**breaking change**

Expand Down
38 changes: 28 additions & 10 deletions docs/concepts/dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,55 @@ In titiler `Factories`, we use the dependencies to define the inputs for each en

Example:
```python
# Custom Dependency
@dataclass
class ImageParams:
"""Common Image parameters."""
"""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.")

kwargs: Dict = field(init=False, default_factory=dict)

def __post_init__(self):
"""Post Init."""
super().__post_init__()

if self.width and self.height:
self.max_size = None

if self.width is not None:
self.kwargs["width"] = self.width

if self.height is not None:
self.kwargs["height"] = self.height

@router.get(r"/preview.png")
if self.max_size is not None:
self.kwargs["max_size"] = self.max_size


# Simple preview endpoint
@router.get("/preview.png")
def preview(
url: str = Query(..., description="data set URL"), params: ImageParams = Depends(),
url: str = Query(..., description="data set URL"),
params: ImageParams = Depends(),
):

with COGReader(url) as cog:
data, mask = cog.preview(
max_size=params.max_size,
width=params.width,
height=params.height,
)
img = cog.preview(**params.kwargs)
...
```

!!! important

In the example above, we create a custom `ImageParams` dependency which will then be injected to the `preview` endpoint to add **max_size**, **height** and **width** querystring parameters. As for most of `titiler` dependencies, the `ImageParams` class will host the input querystrings to a `kwargs` dictionary, which will then be used to pass the options to the `cog.preview()` methods.

Note: when calling the `cog.preview()` method, we use the `**` operator to pass the dictionary key/value pairs as keyword arguments.


### TiTiler Dependencies

The `factories` allow users to set multiple default dependencies. Here is the list of common dependencies and their default values:

* **path_dependency**: Set dataset path (url).
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@ def parse_img(content: bytes) -> Dict[Any, Any]:
"""Read tile image and return metadata."""
with MemoryFile(content) as mem:
with mem.open() as dst:
return dst.meta
return dst.profile
63 changes: 63 additions & 0 deletions tests/test_CustomRender.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# """Test TiTiler Custom Render Params."""

from dataclasses import dataclass
from typing import Optional, Union

import numpy

from titiler.dependencies import RenderParams
from titiler.endpoints import factory

from .conftest import DATA_DIR, parse_img

from fastapi import FastAPI, Query

from starlette.testclient import TestClient


@dataclass
class CustomRenderParams(RenderParams):

output_nodata: Optional[Union[str, int, float]] = Query(
None, title="Tiff Ouptut Nodata value",
)
output_compression: Optional[str] = Query(
None, title="Tiff compression schema",
)

def __post_init__(self):
super().__post_init__()
if self.output_nodata is not None:
self.kwargs["nodata"] = (
numpy.nan if self.output_nodata == "nan" else float(self.output_nodata)
)
if self.output_compression is not None:
self.kwargs["compress"] = self.output_compression


def test_CustomRender():
"""Test Custom Render Params dependency."""
app = FastAPI()
cog = factory.TilerFactory(render_dependency=CustomRenderParams)
app.include_router(cog.router)
client = TestClient(app)

response = client.get(f"/tiles/8/87/48.tif?url={DATA_DIR}/cog.tif")
assert response.status_code == 200
assert response.headers["content-type"] == "image/tiff; application=geotiff"
meta = parse_img(response.content)
assert meta["driver"] == "GTiff"
assert meta["nodata"] is None
assert meta["count"] == 2
assert not meta.get("compress")

response = client.get(
f"/tiles/8/87/48.tif?url={DATA_DIR}/cog.tif&return_mask=false&output_nodata=0&output_compression=deflate"
)
assert response.status_code == 200
assert response.headers["content-type"] == "image/tiff; application=geotiff"
meta = parse_img(response.content)
assert meta["driver"] == "GTiff"
assert meta["nodata"] == 0
assert meta["count"] == 1
assert meta["compress"] == "deflate"
2 changes: 1 addition & 1 deletion titiler/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def TMSParams(
class DefaultDependency:
"""Dependency Base Class"""

kwargs: dict = field(init=False, default_factory=dict)
kwargs: Dict = field(init=False, default_factory=dict)


@dataclass
Expand Down
11 changes: 7 additions & 4 deletions titiler/endpoints/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,9 +320,8 @@ def tile(
**dataset_params.kwargs,
**kwargs,
)
colormap = render_params.colormap or getattr(
src_dst, "colormap", None
)
dst_colormap = getattr(src_dst, "colormap", None)

timings.append(("dataread", round(t.elapsed * 1000, 2)))

if not format:
Expand All @@ -339,8 +338,9 @@ def tile(
content = image.render(
add_mask=render_params.return_mask,
img_format=format.driver,
colormap=colormap,
colormap=render_params.colormap or dst_colormap,
**format.profile,
**render_params.kwargs,
)
timings.append(("format", round(t.elapsed * 1000, 2)))

Expand Down Expand Up @@ -608,6 +608,7 @@ def preview(
img_format=format.driver,
colormap=colormap,
**format.profile,
**render_params.kwargs,
)
timings.append(("format", round(t.elapsed * 1000, 2)))

Expand Down Expand Up @@ -676,6 +677,7 @@ def part(
img_format=format.driver,
colormap=colormap,
**format.profile,
**render_params.kwargs,
)
timings.append(("format", round(t.elapsed * 1000, 2)))

Expand Down Expand Up @@ -1132,6 +1134,7 @@ def tile(
img_format=format.driver,
colormap=render_params.colormap,
**format.profile,
**render_params.kwargs,
)
timings.append(("format", round(t.elapsed * 1000, 2)))

Expand Down

0 comments on commit 272d62d

Please sign in to comment.