Skip to content

Commit 059916c

Browse files
add Custom Colormap querystring option (#262)
* add Custom Colormap querystring option * make sure colormap is valid on tilejson response * switch to rio-tiler functions * update changelog and ui
1 parent a8ff424 commit 059916c

File tree

9 files changed

+91
-27
lines changed

9 files changed

+91
-27
lines changed

CHANGES.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Release Notes
22

3-
## 0.2.0 (TBD)
3+
## 0.2.0 (2021-03-09)
44

55
* adapt for cogeo-mosaic `3.0.0rc2` and add `backend_options` attribute in MosaicTilerFactory (https://github.com/developmentseed/titiler/pull/247)
66
* update FastAPI requirements
@@ -15,6 +15,21 @@
1515
* renamed `MimeType` to `MediaType` (https://github.com/developmentseed/titiler/pull/258)
1616
* add `ColorMapParams` dependency to ease the creation of custom colormap dependency (https://github.com/developmentseed/titiler/pull/252)
1717
* renamed `PathParams` to `DatasetPathParams` and also made it a simple callable (https://github.com/developmentseed/titiler/pull/260)
18+
* renamed `colormap` query-parameter to `colormap_name` (https://github.com/developmentseed/titiler/pull/262)
19+
```
20+
# before
21+
/cog/preview.png?colormap=viridis
22+
23+
# now
24+
/cog/preview.png?colormap_name=viridis
25+
```
26+
27+
* use `colormap` query-parameter to pass custom colormap (https://github.com/developmentseed/titiler/pull/262)
28+
```
29+
/cog/preview.png?colormap={"0": "#FFFF00FF", ...}
30+
```
31+
32+
1833
1934
## 0.1.0 (2021-02-17)
2035

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"python-dotenv",
1818
"rasterio",
1919
"rio-cogeo>=2.1,<2.2",
20-
"rio-tiler>=2.0.1,<2.1",
20+
"rio-tiler>=2.0.4,<2.1",
2121
"uvicorn[standard]>=0.12.0,<0.14.0",
2222
# Additional requirements for python 3.6
2323
"dataclasses;python_version<'3.7'",

tests/routes/test_cog.py

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
"""test /COG endpoints."""
2+
3+
import json
24
import os
35
from io import BytesIO
46
from unittest.mock import patch
7+
from urllib.parse import parse_qsl, urlencode, urlparse
58

69
import numpy
710
import pytest
@@ -202,19 +205,37 @@ def test_tile(rio, app):
202205
assert response.headers["content-type"] == "image/png"
203206

204207
response = app.get(
205-
"/cog/tiles/8/84/47?url=https://myurl.com/cog.tif&nodata=0&rescale=0,1000&color_map=viridis"
208+
"/cog/tiles/8/84/47?url=https://myurl.com/cog.tif&nodata=0&rescale=0,1000&colormap_name=viridis"
209+
)
210+
assert response.status_code == 200
211+
assert response.headers["content-type"] == "image/png"
212+
213+
response = app.get(
214+
"/cog/tiles/8/53/50.png?url=https://myurl.com/above_cog.tif&bidx=1&colormap_name=above"
206215
)
207216
assert response.status_code == 200
208217
assert response.headers["content-type"] == "image/png"
209218

219+
cmap = urlencode(
220+
{
221+
"colormap": json.dumps(
222+
{
223+
"1": [58, 102, 24, 255],
224+
"2": [100, 177, 41],
225+
"3": "#b1b129",
226+
"4": "#ddcb9aFF",
227+
}
228+
)
229+
}
230+
)
210231
response = app.get(
211-
"/cog/tiles/8/53/50.png?url=https://myurl.com/above_cog.tif&bidx=1&color_map=above"
232+
f"/cog/tiles/8/53/50.png?url=https://myurl.com/above_cog.tif&bidx=1&{cmap}"
212233
)
213234
assert response.status_code == 200
214235
assert response.headers["content-type"] == "image/png"
215236

216237
response = app.get(
217-
"/cog/tiles/8/53/50.png?url=https://myurl.com/above_cog.tif&bidx=1&color_map=above&resampling_method=somethingwrong"
238+
"/cog/tiles/8/53/50.png?url=https://myurl.com/above_cog.tif&bidx=1&colormap_name=above&resampling_method=somethingwrong"
218239
)
219240
assert response.status_code == 422
220241

@@ -241,6 +262,7 @@ def test_tilejson(rio, app):
241262
assert body["tilejson"] == "2.2.0"
242263
assert body["version"] == "1.0.0"
243264
assert body["scheme"] == "xyz"
265+
assert body["name"] == "cog.tif"
244266
assert len(body["tiles"]) == 1
245267
assert body["tiles"][0].startswith(
246268
"http://testserver/cog/tiles/WebMercatorQuad/{z}/{x}/{y}@1x?url=https"
@@ -259,6 +281,24 @@ def test_tilejson(rio, app):
259281
"http://testserver/cog/tiles/WebMercatorQuad/{z}/{x}/{y}@2x.png?url=https"
260282
)
261283

284+
cmap_dict = {
285+
"1": [58, 102, 24, 255],
286+
"2": [100, 177, 41],
287+
"3": "#b1b129",
288+
"4": "#ddcb9aFF",
289+
}
290+
cmap = urlencode({"colormap": json.dumps(cmap_dict)})
291+
response = app.get(
292+
f"/cog/tilejson.json?url=https://myurl.com/above_cog.tif&bidx=1&{cmap}"
293+
)
294+
assert response.status_code == 200
295+
body = response.json()
296+
assert body["tiles"][0].startswith(
297+
"http://testserver/cog/tiles/WebMercatorQuad/{z}/{x}/{y}@1x?url=https"
298+
)
299+
query = dict(parse_qsl(urlparse(body["tiles"][0]).query))
300+
assert json.loads(query["colormap"]) == cmap_dict
301+
262302

263303
@patch("rio_tiler.io.cogeo.rasterio")
264304
def test_preview(rio, app):

tests/routes/test_mosaic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def test_tile(app):
161161
params={
162162
"url": MOSAICJSON_FILE,
163163
"rescale": "0,1000",
164-
"color_map": "viridis",
164+
"colormap_name": "viridis",
165165
"bidx": 1,
166166
},
167167
)

tests/test_CustomCmap.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,18 @@
1919
"cmap1": {6: (4, 5, 6, 255)},
2020
}
2121
cmap = ColorMaps(data=cmap_values)
22-
ColorMapNames = Enum( # type: ignore
23-
"ColorMapNames", [(a, a) for a in sorted(cmap.list())]
22+
ColorMapName = Enum( # type: ignore
23+
"ColorMapName", [(a, a) for a in sorted(cmap.list())]
2424
)
2525

2626

2727
def ColorMapParams(
28-
color_map: ColorMapNames = Query(None, description="Colormap name",)
28+
colormap_name: ColorMapName = Query(None, description="Colormap name"),
2929
) -> Optional[Dict]:
3030
"""Colormap Dependency."""
31-
if color_map:
32-
return cmap.get(color_map.value)
31+
if colormap_name:
32+
return cmap.get(colormap_name.value)
33+
3334
return None
3435

3536

@@ -41,7 +42,7 @@ def test_CustomCmap():
4142
client = TestClient(app)
4243

4344
response = client.get(
44-
f"/preview.npy?url={DATA_DIR}/above_cog.tif&bidx=1&color_map=cmap1"
45+
f"/preview.npy?url={DATA_DIR}/above_cog.tif&bidx=1&colormap_name=cmap1"
4546
)
4647
assert response.status_code == 200
4748
assert response.headers["content-type"] == "application/x-binary"
@@ -51,6 +52,6 @@ def test_CustomCmap():
5152
assert 6 in data[2]
5253

5354
response = client.get(
54-
f"/preview.npy?url={DATA_DIR}/above_cog.tif&bidx=1&color_map=another_cmap"
55+
f"/preview.npy?url={DATA_DIR}/above_cog.tif&bidx=1&colormap_name=another_cmap"
5556
)
5657
assert response.status_code == 422

titiler/dependencies.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Common dependency."""
22

3+
import json
34
import re
45
from dataclasses import dataclass, field
56
from enum import Enum
@@ -9,7 +10,7 @@
910
from morecantile import tms
1011
from morecantile.models import TileMatrixSet
1112
from rasterio.enums import Resampling
12-
from rio_tiler.colormap import cmap
13+
from rio_tiler.colormap import cmap, parse_color
1314
from rio_tiler.errors import MissingAssets, MissingBands
1415

1516
from .custom import cmap as custom_colormap
@@ -69,11 +70,19 @@ def TMSParams(
6970

7071

7172
def ColorMapParams(
72-
color_map: ColorMapName = Query(None, description="Colormap name",)
73+
colormap_name: ColorMapName = Query(None, description="Colormap name"),
74+
colormap: str = Query(None, description="JSON encoded custom Colormap"),
7375
) -> Optional[Dict]:
7476
"""Colormap Dependency."""
75-
if color_map:
76-
return cmap.get(color_map.value)
77+
if colormap_name:
78+
return cmap.get(colormap_name.value)
79+
80+
if colormap:
81+
return json.loads(
82+
colormap,
83+
object_hook=lambda x: {int(k): parse_color(v) for k, v in x.items()},
84+
)
85+
7786
return None
7887

7988

titiler/endpoints/factory.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import os
55
from dataclasses import dataclass, field
66
from typing import Any, Callable, Dict, List, Optional, Type
7-
from urllib.parse import urlencode
7+
from urllib.parse import urlencode, urlparse
88

99
import rasterio
1010
from cogeo_mosaic.backends import BaseBackend, MosaicBackend
@@ -325,7 +325,6 @@ def tile(
325325
**kwargs,
326326
)
327327
dst_colormap = getattr(src_dst, "colormap", None)
328-
329328
timings.append(("dataread", round(t.elapsed * 1000, 2)))
330329

331330
if not format:
@@ -423,7 +422,7 @@ def tilejson(
423422
"center": tuple(center),
424423
"minzoom": minzoom if minzoom is not None else src_dst.minzoom,
425424
"maxzoom": maxzoom if maxzoom is not None else src_dst.maxzoom,
426-
"name": os.path.basename(src_path),
425+
"name": urlparse(src_path).path.lstrip("/") or "cogeotif",
427426
"tiles": [tiles_url],
428427
}
429428

@@ -590,7 +589,7 @@ def preview(
590589
**dataset_params.kwargs,
591590
**kwargs,
592591
)
593-
colormap = colormap or getattr(src_dst, "colormap", None)
592+
dst_colormap = getattr(src_dst, "colormap", None)
594593
timings.append(("dataread", round(t.elapsed * 1000, 2)))
595594

596595
if not format:
@@ -607,7 +606,7 @@ def preview(
607606
content = image.render(
608607
add_mask=render_params.return_mask,
609608
img_format=format.driver,
610-
colormap=colormap,
609+
colormap=colormap or dst_colormap,
611610
**format.profile,
612611
**render_params.kwargs,
613612
)
@@ -661,7 +660,7 @@ def part(
661660
**dataset_params.kwargs,
662661
**kwargs,
663662
)
664-
colormap = colormap or getattr(src_dst, "colormap", None)
663+
dst_colormap = getattr(src_dst, "colormap", None)
665664
timings.append(("dataread", round(t.elapsed * 1000, 2)))
666665

667666
with utils.Timer() as t:
@@ -675,7 +674,7 @@ def part(
675674
content = image.render(
676675
add_mask=render_params.return_mask,
677676
img_format=format.driver,
678-
colormap=colormap,
677+
colormap=colormap or dst_colormap,
679678
**format.profile,
680679
**render_params.kwargs,
681680
)
@@ -1219,7 +1218,7 @@ def tilejson(
12191218
"center": tuple(center),
12201219
"minzoom": minzoom if minzoom is not None else src_dst.minzoom,
12211220
"maxzoom": maxzoom if maxzoom is not None else src_dst.maxzoom,
1222-
"name": os.path.basename(src_path),
1221+
"name": urlparse(src_path).path.lstrip("/") or "mosaic",
12231222
"tiles": [tiles_url],
12241223
}
12251224

titiler/templates/cog_index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@
412412
params.rescale = `${minV},${maxV}`
413413
}
414414
const cmap = document.getElementById('colormap-selector')[document.getElementById('colormap-selector').selectedIndex]
415-
if (cmap.value !== 'b&w') params.color_map = cmap.value
415+
if (cmap.value !== 'b&w') params.colormap_name = cmap.value
416416

417417
const url_params = Object.keys(params).map(i => `${i}=${params[i]}`).join('&')
418418
let url = `${tilejson_endpoint}?${url_params}`

titiler/templates/stac_index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@
408408
}
409409

410410
const cmap = document.getElementById('colormap-selector')[document.getElementById('colormap-selector').selectedIndex]
411-
if (cmap.value !== 'b&w') params.color_map = cmap.value
411+
if (cmap.value !== 'b&w') params.colormap_name = cmap.value
412412

413413
const url_params = Object.keys(params).map(i => `${i}=${params[i]}`).join('&')
414414
let url = `${tilejson_endpoint}?${url_params}`

0 commit comments

Comments
 (0)