Skip to content

Commit 130087c

Browse files
Support for custom colormaps (#223)
* 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 <banesullivan@gmail.com>
1 parent 33838b3 commit 130087c

6 files changed

Lines changed: 55 additions & 5 deletions

File tree

localtileserver/client.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from collections.abc import Iterable
2+
import json
23
import logging
34
import pathlib
45
from typing import List, Optional, Union
56

7+
from matplotlib.colors import Colormap, ListedColormap
68
import rasterio
79
import requests
810
from rio_tiler.io import Reader
@@ -416,7 +418,7 @@ def create_url(self, path: str, client: bool = False):
416418
def get_tile_url(
417419
self,
418420
indexes: Optional[List[int]] = None,
419-
colormap: Optional[str] = None,
421+
colormap: Optional[Union[str, Colormap, List[str]]] = None,
420422
vmin: Optional[Union[float, List[float]]] = None,
421423
vmax: Optional[Union[float, List[float]]] = None,
422424
nodata: Optional[Union[int, float]] = None,
@@ -446,8 +448,18 @@ def get_tile_url(
446448
if indexes is not None:
447449
params["indexes"] = indexes
448450
if colormap is not None:
449-
# make sure palette is valid
450-
palette_valid_or_raise(colormap)
451+
if isinstance(colormap, ListedColormap):
452+
colormap = json.dumps([c for c in colormap.colors])
453+
elif isinstance(colormap, Colormap):
454+
colormap = json.dumps(
455+
{k: tuple(v.tolist()) for k, v in enumerate(colormap(range(256), 1, 1))}
456+
)
457+
elif isinstance(colormap, list):
458+
colormap = json.dumps(colormap)
459+
else:
460+
# make sure palette is valid
461+
palette_valid_or_raise(colormap)
462+
451463
params["colormap"] = colormap
452464
if vmin is not None:
453465
if isinstance(vmin, Iterable) and not isinstance(indexes, Iterable):

localtileserver/tiler/handler.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
"""Methods for working with images."""
2+
import json
23
import pathlib
34
from typing import Dict, List, Optional, Tuple, Union
45

6+
from matplotlib.colors import Colormap, LinearSegmentedColormap, ListedColormap
57
import numpy as np
68
import rasterio
79
from rasterio.enums import ColorInterp
@@ -150,7 +152,23 @@ def _render_image(
150152
colormap: Optional[str] = None,
151153
img_format: str = "PNG",
152154
):
153-
colormap = cmap.get(colormap) if colormap else None
155+
if colormap in cmap.list():
156+
colormap = cmap.get(colormap)
157+
elif isinstance(colormap, ListedColormap):
158+
c = LinearSegmentedColormap.from_list("", colormap.colors, N=256)
159+
colormap = {k: tuple(v) for k, v in enumerate(c(range(256), 1, 1))}
160+
elif isinstance(colormap, Colormap):
161+
colormap = {k: tuple(v) for k, v in enumerate(colormap(range(256), 1, 1))}
162+
elif colormap:
163+
c = json.loads(colormap)
164+
if isinstance(c, list):
165+
c = LinearSegmentedColormap.from_list("", c, N=256)
166+
colormap = {k: tuple(v) for k, v in enumerate(c(range(256), 1, 1))}
167+
else:
168+
colormap = {}
169+
for key, value in c.items():
170+
colormap[int(key)] = tuple(value)
171+
154172
if (
155173
not colormap
156174
and len(indexes) == 1

localtileserver/widgets.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import List, Optional, Union
44
import warnings
55

6+
from matplotlib.colors import Colormap
67
import rasterio
78

89
from localtileserver.client import TileClient, get_or_create_tile_client
@@ -23,7 +24,7 @@ def get_leaflet_tile_layer(
2324
port: Union[int, str] = "default",
2425
debug: bool = False,
2526
indexes: Optional[List[int]] = None,
26-
colormap: Optional[str] = None,
27+
colormap: Optional[Union[str, Colormap, List[str]]] = None,
2728
vmin: Optional[Union[float, List[float]]] = None,
2829
vmax: Optional[Union[float, List[float]]] = None,
2930
nodata: Optional[Union[int, float]] = None,
52.3 KB
Loading
50.3 KB
Loading

tests/test_rendering.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from matplotlib.colors import ListedColormap
12
import pytest
23

34
from .utilities import get_content
@@ -56,6 +57,24 @@ def test_tile_colormap(bahamas, compare, colormap, indexes):
5657
compare(direct_content)
5758

5859

60+
@pytest.mark.parametrize(
61+
"colormap,indexes",
62+
[
63+
(ListedColormap(["red", "blue"]), None),
64+
(ListedColormap(["blue", "green"]), 2),
65+
],
66+
)
67+
def test_custom_colormap(bahamas, compare, colormap, indexes):
68+
# Get a tile over the REST API
69+
tile_url = bahamas.get_tile_url(colormap=colormap, indexes=indexes).format(z=8, x=72, y=110)
70+
rest_content = get_content(tile_url)
71+
# Get tile directly
72+
direct_content = bahamas.tile(z=8, x=72, y=110, colormap=colormap, indexes=indexes)
73+
# Make sure they are the same
74+
assert rest_content == direct_content
75+
compare(direct_content)
76+
77+
5978
@pytest.mark.parametrize("vmin", [100, [100, 200, 250]])
6079
def test_tile_vmin(bahamas, compare, vmin):
6180
url = bahamas.get_tile_url(

0 commit comments

Comments
 (0)