From e9829bb3e47f613ce66904c43cb976e7cb777a0d Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Fri, 10 May 2024 15:23:35 +0200 Subject: [PATCH] adjust rescale and colormap render params --- tests/fixtures/catalog.json | 175 ++++++++++++++++++++++++++++++++++++ tests/test_render.py | 53 +++++++++++ titiler/stacapi/factory.py | 44 +++++---- 3 files changed, 256 insertions(+), 16 deletions(-) create mode 100644 tests/fixtures/catalog.json create mode 100644 tests/test_render.py diff --git a/tests/fixtures/catalog.json b/tests/fixtures/catalog.json new file mode 100644 index 0000000..4f2cb24 --- /dev/null +++ b/tests/fixtures/catalog.json @@ -0,0 +1,175 @@ +{ + "collections": [ + { + "id": "MAXAR_BayofBengal_Cyclone_Mocha_May_23", + "type": "Collection", + "links": [ + { + "rel": "items", + "type": "application/geo+json", + "href": "https://stac.endpoint.io/collections/MAXAR_BayofBengal_Cyclone_Mocha_May_23/items" + }, + { + "rel": "parent", + "type": "application/json", + "href": "https://stac.endpoint.io/" + }, + { + "rel": "root", + "type": "application/json", + "href": "https://stac.endpoint.io/" + }, + { + "rel": "self", + "type": "application/json", + "href": "https://stac.endpoint.io/collections/MAXAR_BayofBengal_Cyclone_Mocha_May_23" + } + ], + "title": "Bay of Bengal Cyclone Mocha 2023", + "extent": { + "spatial": { + "bbox": [ + [ + 91.831615, + 19.982078842323997, + 92.97426268500965, + 21.666101 + ], + [ + 92.567815, + 20.18811887678192, + 92.74417544237298, + 20.62968532404085 + ], + [ + 92.72278776887262, + 20.104801, + 92.893524, + 20.630214 + ], + [ + 92.75855246040959, + 19.982078842323997, + 92.89682495377032, + 20.514473160464657 + ], + [ + 92.84253515935835, + 19.984656587012033, + 92.97426268500965, + 20.514418665444474 + ], + [ + 91.831615, + 21.518411, + 91.957078, + 21.666101 + ] + ] + }, + "temporal": { + "interval": [ + [ + "2023-01-03T04:30:17Z", + "2023-05-22T04:35:25Z" + ] + ] + } + }, + "license": "CC-BY-NC-4.0", + "renders": { + "visual": { + "title": "Visual Image", + "assets": [ + "visual" + ], + "asset_bidx": "visual|1,2,3", + "minmax_zoom": [ + 8, + 22 + ], + "tilematrixsets": { + "WebMercatorQuad": [ + 8, + 22 + ] + } + }, + "color": { + "title": "Colored Image", + "assets": [ + "visual" + ], + "asset_bidx": "visual|1", + "colormap": { + "1": [0, 0, 0, 255], + "1000": [255, 255, 255, 255] + } + }, + "visualr": { + "title": "Rescaled Image", + "assets": [ + "visual" + ], + "asset_bidx": "visual|1", + "rescale": [ + [0, 100] + ] + } + }, + "description": "Maxar OpenData | Cyclone Mocha, a category five cyclone with 130 mph winds and torrential rain, hit parts of Myanmar and Bangladesh, forcing mass evacuations ahead of the storm. The cyclone, one of the most powerful to hit the region in the last decade, made landfall on Sunday, May 14, 2023, near Sittwe in Myanmar's Rakhine state. Rain and a storm surge caused widespread flooding in low-lying areas. The United National Office Coordination of Humanitarian Affairs stated that there had been extensive damage among already vulnerable communities and that communications with the affected areas have been difficult.", + "item_assets": { + "visual": { + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "roles": [ + "visual" + ], + "title": "Visual Image" + }, + "data-mask": { + "type": "application/geopackage+sqlite3", + "roles": [ + "data-mask" + ], + "title": "Data Mask" + }, + "ms_analytic": { + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "roles": [ + "data" + ], + "title": "Multispectral Image" + }, + "pan_analytic": { + "type": "image/tiff; application=geotiff; profile=cloud-optimized", + "roles": [ + "data" + ], + "title": "Panchromatic Image" + } + }, + "stac_version": "1.0.0", + "stac_extensions": [ + "https://stac-extensions.github.io/item-assets/v1.0.0/schema.json", + "https://stac-extensions.github.io/render/v1.0.0/schema.json" + ] + } + ], + "links": [ + { + "rel": "root", + "type": "application/json", + "href": "https://stac.endpoint.io/" + }, + { + "rel": "parent", + "type": "application/json", + "href": "https://stac.endpoint.io/" + }, + { + "rel": "self", + "type": "application/json", + "href": "https://stac.endpoint.io/collections" + } + ] +} diff --git a/tests/test_render.py b/tests/test_render.py new file mode 100644 index 0000000..379962d --- /dev/null +++ b/tests/test_render.py @@ -0,0 +1,53 @@ +"""test render extension.""" + +import json +import os +from unittest.mock import patch + +import pystac + +from titiler.core import dependencies +from titiler.stacapi.factory import get_dependency_params, get_layer_from_collections + +catalog_json = os.path.join(os.path.dirname(__file__), "fixtures", "catalog.json") + + +@patch("titiler.stacapi.factory.Client") +def test_render(client): + """test STAC items endpoints.""" + + with open(catalog_json, "r") as f: + collections = [ + pystac.Collection.from_dict(c) for c in json.loads(f.read())["collections"] + ] + client.open.return_value.get_collections.return_value = collections + + collections_render = get_layer_from_collections( + "https://something.stac", None, None + ) + assert len(collections_render) == 3 + + visual = collections_render["MAXAR_BayofBengal_Cyclone_Mocha_May_23_visual"] + assert visual["bbox"] + assert visual["tilematrixsets"]["WebMercatorQuad"] + assert visual["time"] + assert visual["render"]["asset_bidx"] + + color = collections_render["MAXAR_BayofBengal_Cyclone_Mocha_May_23_color"]["render"] + assert isinstance(color["colormap"], str) + + cmap = get_dependency_params( + dependency=dependencies.ColorMapParams, + query_params=color, + ) + assert cmap + + visualr = collections_render["MAXAR_BayofBengal_Cyclone_Mocha_May_23_visualr"][ + "render" + ] + assert isinstance(visualr["rescale"][0], str) + rescale = get_dependency_params( + dependency=dependencies.RescalingParams, + query_params=visualr, + ) + assert rescale diff --git a/titiler/stacapi/factory.py b/titiler/stacapi/factory.py index 8aa367c..3c9d92b 100644 --- a/titiler/stacapi/factory.py +++ b/titiler/stacapi/factory.py @@ -552,7 +552,7 @@ class WMTSMediaType(str, Enum): TTLCache(maxsize=cache_config.maxsize, ttl=cache_config.ttl), key=lambda url, headers, supported_tms: hashkey(url, json.dumps(headers)), ) -def get_layer_from_collections( +def get_layer_from_collections( # noqa: C901 url: str, headers: Optional[Dict] = None, supported_tms: Optional[TileMatrixSets] = None, @@ -647,9 +647,29 @@ def get_layer_from_collections( for x in range(0, (end_date - start_date).days + 1) ] - # TODO: - # special encoding for ColorMaps render = layer["render"] or {} + + # special encoding for rescale + # Per Specification, the rescale entry is a 2d array in form of `[[min, max], [min,max]]` + # We need to convert this to `['{min},{max}', '{min},{max}']` for titiler dependency + if rescale := render.pop("rescale", None): + rescales = [] + for r in rescale: + if not isinstance(r, str): + rescales.append(",".join(map(str, r))) + else: + rescales.append(r) + + render["rescale"] = rescales + + # special encoding for ColorMaps + # Per Specification, the colormap is a JSON object. TiTiler dependency expects a string encoded dict + if colormap := render.pop("colormap", None): + if not isinstance(colormap, str): + colormap = json.dumps(colormap) + + render["colormap"] = colormap + qs = urlencode( [(k, v) for k, v in render.items() if v is not None], doseq=True, @@ -810,19 +830,11 @@ def get_tile( # noqa: C901 ): image = post_process(image) - if "rescale" in query_params: - rescales = [] - for r in query_params["rescale"]: - if not isinstance(r, str): - rescales.append(",".join(map(str, r))) - else: - rescales.append(r) - - if rescale := get_dependency_params( - dependency=self.rescale_dependency, - query_params={"rescale": rescales}, - ): - image.rescale(rescale) + if rescale := get_dependency_params( + dependency=self.rescale_dependency, + query_params=query_params, + ): + image.rescale(rescale) if color_formula := get_dependency_params( dependency=self.color_formula_dependency,