Skip to content

Commit

Permalink
Support for custom colormaps (#223)
Browse files Browse the repository at this point in the history
* Handle serialization and deserialization of Colormap and lists of colors.

* Add List[str]

* Truthy check

* Linting

* Add validation test

* Handle colormaps instances

* Fix truthy issue

---------

Co-authored-by: Bane Sullivan <[email protected]>
  • Loading branch information
lopezvoliver and banesullivan authored Oct 4, 2024
1 parent 33838b3 commit 130087c
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 5 deletions.
18 changes: 15 additions & 3 deletions localtileserver/client.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from collections.abc import Iterable
import json
import logging
import pathlib
from typing import List, Optional, Union

from matplotlib.colors import Colormap, ListedColormap
import rasterio
import requests
from rio_tiler.io import Reader
Expand Down Expand Up @@ -416,7 +418,7 @@ def create_url(self, path: str, client: bool = False):
def get_tile_url(
self,
indexes: Optional[List[int]] = None,
colormap: Optional[str] = None,
colormap: Optional[Union[str, Colormap, List[str]]] = None,
vmin: Optional[Union[float, List[float]]] = None,
vmax: Optional[Union[float, List[float]]] = None,
nodata: Optional[Union[int, float]] = None,
Expand Down Expand Up @@ -446,8 +448,18 @@ def get_tile_url(
if indexes is not None:
params["indexes"] = indexes
if colormap is not None:
# make sure palette is valid
palette_valid_or_raise(colormap)
if isinstance(colormap, ListedColormap):
colormap = json.dumps([c for c in colormap.colors])
elif isinstance(colormap, Colormap):
colormap = json.dumps(
{k: tuple(v.tolist()) for k, v in enumerate(colormap(range(256), 1, 1))}
)
elif isinstance(colormap, list):
colormap = json.dumps(colormap)
else:
# make sure palette is valid
palette_valid_or_raise(colormap)

params["colormap"] = colormap
if vmin is not None:
if isinstance(vmin, Iterable) and not isinstance(indexes, Iterable):
Expand Down
20 changes: 19 additions & 1 deletion localtileserver/tiler/handler.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""Methods for working with images."""
import json
import pathlib
from typing import Dict, List, Optional, Tuple, Union

from matplotlib.colors import Colormap, LinearSegmentedColormap, ListedColormap
import numpy as np
import rasterio
from rasterio.enums import ColorInterp
Expand Down Expand Up @@ -150,7 +152,23 @@ def _render_image(
colormap: Optional[str] = None,
img_format: str = "PNG",
):
colormap = cmap.get(colormap) if colormap else None
if colormap in cmap.list():
colormap = cmap.get(colormap)
elif isinstance(colormap, ListedColormap):
c = LinearSegmentedColormap.from_list("", colormap.colors, N=256)
colormap = {k: tuple(v) for k, v in enumerate(c(range(256), 1, 1))}
elif isinstance(colormap, Colormap):
colormap = {k: tuple(v) for k, v in enumerate(colormap(range(256), 1, 1))}
elif colormap:
c = json.loads(colormap)
if isinstance(c, list):
c = LinearSegmentedColormap.from_list("", c, N=256)
colormap = {k: tuple(v) for k, v in enumerate(c(range(256), 1, 1))}
else:
colormap = {}
for key, value in c.items():
colormap[int(key)] = tuple(value)

if (
not colormap
and len(indexes) == 1
Expand Down
3 changes: 2 additions & 1 deletion localtileserver/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing import List, Optional, Union
import warnings

from matplotlib.colors import Colormap
import rasterio

from localtileserver.client import TileClient, get_or_create_tile_client
Expand All @@ -23,7 +24,7 @@ def get_leaflet_tile_layer(
port: Union[int, str] = "default",
debug: bool = False,
indexes: Optional[List[int]] = None,
colormap: Optional[str] = None,
colormap: Optional[Union[str, Colormap, List[str]]] = None,
vmin: Optional[Union[float, List[float]]] = None,
vmax: Optional[Union[float, List[float]]] = None,
nodata: Optional[Union[int, float]] = None,
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions tests/test_rendering.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from matplotlib.colors import ListedColormap
import pytest

from .utilities import get_content
Expand Down Expand Up @@ -56,6 +57,24 @@ def test_tile_colormap(bahamas, compare, colormap, indexes):
compare(direct_content)


@pytest.mark.parametrize(
"colormap,indexes",
[
(ListedColormap(["red", "blue"]), None),
(ListedColormap(["blue", "green"]), 2),
],
)
def test_custom_colormap(bahamas, compare, colormap, indexes):
# Get a tile over the REST API
tile_url = bahamas.get_tile_url(colormap=colormap, indexes=indexes).format(z=8, x=72, y=110)
rest_content = get_content(tile_url)
# Get tile directly
direct_content = bahamas.tile(z=8, x=72, y=110, colormap=colormap, indexes=indexes)
# Make sure they are the same
assert rest_content == direct_content
compare(direct_content)


@pytest.mark.parametrize("vmin", [100, [100, 200, 250]])
def test_tile_vmin(bahamas, compare, vmin):
url = bahamas.get_tile_url(
Expand Down

0 comments on commit 130087c

Please sign in to comment.