Create a custom tile server

cog_worker can be used to create a custom tile server that executes complex analyses on the fly.

1. Install dependencies

We’ll set up a simple server with fastapi. We also use morecantile to enable custom tile matrix sets, though using the default Manager parameters will give you a standard web mercator tiler.

[11]:
# if necessary
#!pip3 install --quiet morecantile
#!pip3 install --quiet fastapi[all]

2. Extend the Manager class

Extend the cog_worker.Manager class to add a tiling function.

[12]:
from typing import Iterable, Mapping, Tuple, Any
from cog_worker import Manager, WorkerFunction, BoundingBox
from rasterio.crs import CRS
import morecantile

class TileManager(Manager):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.tms = morecantile.TileMatrixSet.custom(
            self._proj_bounds, CRS.from_user_input(self.proj)
        )

    def tile(
        self,
        f: WorkerFunction,
        f_args: Iterable = None,
        f_kwargs: Mapping = None,
        z: int = 0,
        x: int = 0,
        y: int = 0,
        tilesize: int = 256,
        **kwargs
    ) -> Tuple[Any, BoundingBox]:
        """Execute a function for the scale and bounds of a TMS tile.

        The tile method supports non-global and non-mercator tiling schemes via
        Morecantile. To generate standard web tiles, instantiate the Manager
        with the default parameters.

        Args:
            f (:obj:`cog_worker.types.WorkerFunction`): The function to execute. The function will
                recieve a cog_worker.worker.Worker as its first argument.
            f_args (list): Additional arguments to pass to the function.
            f_kwargs (dict): Additional keyword arguments to pass to the function.
            bounds (BoundingBox): The region to analize (default: self.bounds)
            max_size (int): The maximum size (width or height) in pixels to compute, ignoring any buffer
                (default: 1024px). Automatically reduces the scale of analysis to fit within `max_size`.
            **kwargs: Additional keyword arguments to overload the Manager's properties.
                (buffer).

        Returns:
            A tuple containing the return value of the function and the bounding
            box of the executed analysis in the target projection.
        """
        # get the bounds of the tile
        proj_bounds = self.tms.xy_bounds(x, y, z)
        left, bottom, right, top = proj_bounds

        # set the scale to equal the tilesize
        size = max(right - left, top - bottom)
        scale = size / tilesize

        kwargs.update(
            {
                "proj_bounds": proj_bounds,
                "scale": scale,
            }
        )

        # execute the analysis as normal
        return self.execute(f, f_args, f_kwargs, **kwargs)

3. Create a FastAPI app

Define an endpoint that executes a WorkerFunction for a tile.

[13]:
from fastapi import FastAPI, Response
from rio_tiler.utils import render

app = FastAPI()

def read_cog(worker):
    return worker.read('example-cog.tif')

functions = {
    'read': read_cog
}

@app.get("/tile/{function}/{z}/{x}/{y}.png")
async def tile(
    function: str,
    z: int,
    x: int,
    y: int
):
    f = functions[function]
    arr, bbox = TileManager().tile(f, z=z, x=x, y=y)
    img = render(arr)
    return Response(img, media_type="image/png")

4. Run the app

  1. Copy the code to a main.py.

  2. Run the app with uvicorn main:app --reload

  3. Check the endpoint at http://localhost:8000/tile/read/0/0/0.png 🎉

Learn more about building a FastAPI app at https://fastapi.tiangolo.com.