From a088d487500382d2f331f05156199f722506fee4 Mon Sep 17 00:00:00 2001 From: Greg Date: Tue, 1 Oct 2024 17:07:34 +0200 Subject: [PATCH 1/9] Export tif to qgis first attempt --- .../jupytergis_qgis/qgis_loader.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py index d406ba76..8f6cdfa7 100644 --- a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py +++ b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py @@ -12,6 +12,7 @@ from PyQt5.QtGui import QColor from qgis.core import ( QgsApplication, + QgsColorRampShader, QgsCoordinateReferenceSystem, QgsDataSourceUri, QgsFillSymbol, @@ -22,9 +23,11 @@ QgsMarkerSymbol, QgsProject, QgsRasterLayer, + QgsRasterShader, QgsRectangle, QgsReferencedRectangle, QgsSettings, + QgsSingleBandPseudoColorRenderer, QgsVectorLayer, QgsVectorTileLayer, ) @@ -450,6 +453,37 @@ def build_uri(parameters: dict[str, str], source_type: str) -> str | None: url = "/vsicurl/" + parameters["urls"][0]["url"] map_layer = QgsRasterLayer(url, layer_name, "gdal") + # Create a color ramp shader + color_ramp_shader = QgsColorRampShader() + color_ramp_shader.setColorRampType(QgsColorRampShader.Interpolated) + + # Define color stops + color_stops = [ + QgsColorRampShader.ColorRampItem(0, QColor(255, 0, 0)), # Red + QgsColorRampShader.ColorRampItem(0.5, QColor(255, 255, 0)), # Yellow + QgsColorRampShader.ColorRampItem(1, QColor(0, 0, 255)), # Blue + ] + + color_ramp_shader.setColorRampItemList(color_stops) + + # Create a raster shader + raster_shader = QgsRasterShader() + raster_shader.setRasterShaderFunction(color_ramp_shader) + + # Create the renderer + renderer = QgsSingleBandPseudoColorRenderer( + map_layer.dataProvider(), + map_layer.bandCount() - 1, # Use the last band + raster_shader, + ) + + # Set minimum and maximum values + renderer.setClassificationMax(3000) + renderer.setClassificationMin(1000) + + # Apply the renderer to the layer + map_layer.setRenderer(renderer) + if map_layer is None: logs["warnings"].append( f"Layer {layer_id} not exported: enable to export layer type {layer_type}" From 10212a08c8b4453a625439cf52267bd157ba8bc9 Mon Sep 17 00:00:00 2001 From: Greg Date: Wed, 2 Oct 2024 13:48:50 +0200 Subject: [PATCH 2/9] Export interpolate tifs to qgis --- .../jupytergis_qgis/qgis_loader.py | 98 ++++++++++++++----- 1 file changed, 73 insertions(+), 25 deletions(-) diff --git a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py index 8f6cdfa7..a1563e26 100644 --- a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py +++ b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py @@ -72,31 +72,48 @@ def qgis_layer_to_jgis( layer_type = "WebGlLayer" source_type = "GeoTiffSource" - # Remove "/vsicurl/" from source - urls = [{"url": layer.source()[9:]}] - # Need to build layer color renderer = layer.renderer() shader = renderer.shader() shaderFunc = shader.rasterShaderFunction() colorList = shaderFunc.colorRampItemList() band = renderer.band() + source_min = renderer.classificationMin() + source_max = renderer.classificationMax() + + # Remove "/vsicurl/" from source + urls = [ + { + "url": layer.source()[9:], + "min": source_min, + "max": source_max, + } + ] colorRampTypeMap = {0: "interpolate", 1: "discrete", 2: "exact"} colorRampType = colorRampTypeMap[shaderFunc.colorRampType()] - # TODO: Only supports linear interpolation for now - if colorRampType == "interpolate": color = [ "interpolate", ["linear"], ["band", float(band)], + 0.0, + [0.0, 0.0, 0.0, 0.0], ] + for node in colorList: - color.append(node.value) + unscaled_val = (node.value * (1 - 0) - source_min * (1 - 0)) / ( + source_max - source_min + ) + color.append(unscaled_val) color.append( - [node.color.red(), node.color.green(), node.color.blue()] + [ + node.color.red(), + node.color.green(), + node.color.blue(), + float(node.color.alpha()) / 255, + ] ) if colorRampType == "discrete": @@ -107,7 +124,12 @@ def qgis_layer_to_jgis( for node in colorList[:-1]: color.append(["<=", ["band", float(band)], node.value]) color.append( - [node.color.red(), node.color.green(), node.color.blue()] + [ + node.color.red(), + node.color.green(), + node.color.blue(), + float(node.color.alpha()) / 255, + ] ) lastElement = colorList[-1] color.append( @@ -126,7 +148,12 @@ def qgis_layer_to_jgis( for node in colorList[:-1]: color.append(["==", ["band", float(band)], node.value]) color.append( - [node.color.red(), node.color.green(), node.color.blue()] + [ + node.color.red(), + node.color.green(), + node.color.blue(), + float(node.color.alpha()) / 255, + ] ) lastElement = colorList[-1] color.append( @@ -138,7 +165,7 @@ def qgis_layer_to_jgis( ) # TODO: Could probably look at RGB values to see what normalize should be - source_parameters.update(urls=urls, normalize=False, wrapX=True) + source_parameters.update(urls=urls, normalize=True, wrapX=True) layer_parameters.update(color=color) else: @@ -407,14 +434,14 @@ def build_uri(parameters: dict[str, str], source_type: str) -> str | None: return if layer_type == "RasterLayer" and source_type == "RasterSource": - parameters = source.get("parameters", {}) - uri = build_uri(parameters, "RasterSource") + source_parameters = source.get("parameters", {}) + uri = build_uri(source_parameters, "RasterSource") map_layer = QgsRasterLayer(uri, layer_name, "wms") if layer_type == "VectorTileLayer" and source_type == "VectorTileSource": - parameters = source.get("parameters", {}) + source_parameters = source.get("parameters", {}) color_params = layer["parameters"]["color"] - uri = build_uri(parameters, "VectorTileSource") + uri = build_uri(source_parameters, "VectorTileSource") map_layer = QgsVectorTileLayer(uri, layer_name) renderer = map_layer.renderer() @@ -448,23 +475,44 @@ def build_uri(parameters: dict[str, str], source_type: str) -> str | None: renderer.setStyles(parsed_styles) if layer_type == "WebGlLayer" and source_type == "GeoTiffSource": - parameters = source.get("parameters", {}) + source_parameters = source.get("parameters", {}) # TODO: Support sources with multiple URLs - url = "/vsicurl/" + parameters["urls"][0]["url"] + url = "/vsicurl/" + source_parameters["urls"][0]["url"] map_layer = QgsRasterLayer(url, layer_name, "gdal") + layer_colors = layer["parameters"]["color"] + selected_band = layer_colors[2][1] + source_min = source_parameters["urls"][0]["min"] + source_max = source_parameters["urls"][0]["max"] + # Create a color ramp shader color_ramp_shader = QgsColorRampShader() - color_ramp_shader.setColorRampType(QgsColorRampShader.Interpolated) + + if layer_colors[0] == "interpolate": + color_ramp_shader.setColorRampType(QgsColorRampShader.Interpolated) # Define color stops - color_stops = [ - QgsColorRampShader.ColorRampItem(0, QColor(255, 0, 0)), # Red - QgsColorRampShader.ColorRampItem(0.5, QColor(255, 255, 0)), # Yellow - QgsColorRampShader.ColorRampItem(1, QColor(0, 0, 255)), # Blue - ] + color_stops = [] + for index in range(5, len(layer_colors), 2): + scaled_value = (layer_colors[index] * (source_max - source_min)) / ( + 1 - 0 + ) + source_min + + colors = layer_colors[index + 1] + color_stops.append( + QgsColorRampShader.ColorRampItem( + scaled_value, + QColor( + int(colors[0]), + int(colors[1]), + int(colors[2]), + int(colors[3] * 255), + ), + ), + ) color_ramp_shader.setColorRampItemList(color_stops) + color_ramp_shader.setClip(True) # Create a raster shader raster_shader = QgsRasterShader() @@ -473,13 +521,13 @@ def build_uri(parameters: dict[str, str], source_type: str) -> str | None: # Create the renderer renderer = QgsSingleBandPseudoColorRenderer( map_layer.dataProvider(), - map_layer.bandCount() - 1, # Use the last band + int(selected_band), raster_shader, ) # Set minimum and maximum values - renderer.setClassificationMax(3000) - renderer.setClassificationMin(1000) + renderer.setClassificationMin(source_min) + renderer.setClassificationMax(source_max) # Apply the renderer to the layer map_layer.setRenderer(renderer) From b4899921b7fbe22ee2fe345a96362293a3c94dd6 Mon Sep 17 00:00:00 2001 From: Greg Date: Wed, 2 Oct 2024 16:57:53 +0200 Subject: [PATCH 3/9] Get discrete option to export --- .../jupytergis_qgis/qgis_loader.py | 83 +++++++++++++++---- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py index a1563e26..c7a76dac 100644 --- a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py +++ b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py @@ -117,12 +117,21 @@ def qgis_layer_to_jgis( ) if colorRampType == "discrete": + last_value = (source_max * (1 - 0) - source_min * (1 - 0)) / ( + source_max - source_min + ) color = [ "case", + ["==", ["band", 1.0], 0.0], + [0.0, 0.0, 0.0, 0.0], ] + # Last entry is used for the fallback value in jgis for node in colorList[:-1]: - color.append(["<=", ["band", float(band)], node.value]) + unscaled_val = (node.value * (1 - 0) - source_min * (1 - 0)) / ( + source_max - source_min + ) + color.append(["<=", ["band", float(band)], unscaled_val]) color.append( [ node.color.red(), @@ -131,12 +140,14 @@ def qgis_layer_to_jgis( float(node.color.alpha()) / 255, ] ) + lastElement = colorList[-1] color.append( [ lastElement.color.red(), lastElement.color.green(), lastElement.color.blue(), + float(node.color.alpha()) / 255, ] ) @@ -146,7 +157,11 @@ def qgis_layer_to_jgis( ] # Last entry is used for the fallback value in jgis for node in colorList[:-1]: - color.append(["==", ["band", float(band)], node.value]) + unscaled_val = (node.value * (1 - 0) - source_min * (1 - 0)) / ( + source_max - source_min + ) + + color.append(["==", ["band", float(band)], unscaled_val]) color.append( [ node.color.red(), @@ -481,32 +496,70 @@ def build_uri(parameters: dict[str, str], source_type: str) -> str | None: map_layer = QgsRasterLayer(url, layer_name, "gdal") layer_colors = layer["parameters"]["color"] - selected_band = layer_colors[2][1] source_min = source_parameters["urls"][0]["min"] source_max = source_parameters["urls"][0]["max"] # Create a color ramp shader color_ramp_shader = QgsColorRampShader() + color_stops = [] if layer_colors[0] == "interpolate": + selected_band = layer_colors[2][1] color_ramp_shader.setColorRampType(QgsColorRampShader.Interpolated) - # Define color stops - color_stops = [] - for index in range(5, len(layer_colors), 2): - scaled_value = (layer_colors[index] * (source_max - source_min)) / ( - 1 - 0 - ) + source_min + # Define color stops + for index in range(5, len(layer_colors), 2): + scaled_value = (layer_colors[index] * (source_max - source_min)) / ( + 1 - 0 + ) + source_min + + colors = layer_colors[index + 1] + color_stops.append( + QgsColorRampShader.ColorRampItem( + scaled_value, + QColor( + int(colors[0]), + int(colors[1]), + int(colors[2]), + int(colors[3] * 255), + ), + ), + ) - colors = layer_colors[index + 1] + if layer_colors[0] == "case": + selected_band = layer_colors[1][1][1] + # check logical operator to choose discrete or exact + op = layer_colors[3][0] + if op == "<=": + color_ramp_shader.setColorRampType(QgsColorRampShader.Discrete) + if op == "==": + color_ramp_shader.setColorRampType(QgsColorRampShader.Exact) + + # skip the last value, thats the fallback and not used by qgis + # skip the second to last because thats handles special + for index in range(3, len(layer_colors) - 1, 2): + val = layer_colors[index][2] + scaled_value = (val * (source_max - source_min)) / (1 - 0) + source_min + colors = layer_colors[index + 1] + color_stops.append( + QgsColorRampShader.ColorRampItem( + scaled_value, + QColor( + int(colors[0]), + int(colors[1]), + int(colors[2]), + int(colors[3] * 255), + ), + ), + ) color_stops.append( QgsColorRampShader.ColorRampItem( - scaled_value, + float("inf"), QColor( - int(colors[0]), - int(colors[1]), - int(colors[2]), - int(colors[3] * 255), + int(layer_colors[-2][0]), + int(layer_colors[-2][1]), + int(layer_colors[-2][2]), + int(layer_colors[-2][3] * 255), ), ), ) From b0ed769ac9165b8621023ab9d746415f9e450077 Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 7 Oct 2024 10:31:00 +0200 Subject: [PATCH 4/9] Better qgis -> jgis --- .../jupytergis_qgis/qgis_loader.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py index c7a76dac..851acb80 100644 --- a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py +++ b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py @@ -90,6 +90,8 @@ def qgis_layer_to_jgis( } ] + print("urls", urls) + colorRampTypeMap = {0: "interpolate", 1: "discrete", 2: "exact"} colorRampType = colorRampTypeMap[shaderFunc.colorRampType()] @@ -117,16 +119,13 @@ def qgis_layer_to_jgis( ) if colorRampType == "discrete": - last_value = (source_max * (1 - 0) - source_min * (1 - 0)) / ( - source_max - source_min - ) color = [ "case", ["==", ["band", 1.0], 0.0], [0.0, 0.0, 0.0, 0.0], ] - # Last entry is used for the fallback value in jgis + # Last entry is inf so handle differently for node in colorList[:-1]: unscaled_val = (node.value * (1 - 0) - source_min * (1 - 0)) / ( source_max - source_min @@ -142,6 +141,10 @@ def qgis_layer_to_jgis( ) lastElement = colorList[-1] + last_value = (source_max * (1 - 0) - source_min * (1 - 0)) / ( + source_max - source_min + ) + color.append(["<=", ["band", float(band)], last_value]) color.append( [ lastElement.color.red(), @@ -151,6 +154,9 @@ def qgis_layer_to_jgis( ] ) + # Fallback value for openlayers + color.append([0, 0, 0, 1]) + if colorRampType == "exact": color = [ "case", @@ -357,6 +363,7 @@ def qgis_layer_tree_to_jgis( def import_project_from_qgis(path: str | Path): + print("SANITY") if isinstance(path, Path): path = str(path) @@ -496,6 +503,7 @@ def build_uri(parameters: dict[str, str], source_type: str) -> str | None: map_layer = QgsRasterLayer(url, layer_name, "gdal") layer_colors = layer["parameters"]["color"] + print("source_parameters", source_parameters) source_min = source_parameters["urls"][0]["min"] source_max = source_parameters["urls"][0]["max"] @@ -642,6 +650,7 @@ def jgis_layer_group_to_qgis( def export_project_to_qgis( path: str | Path, virtual_file: dict[str, Any] ) -> dict[str, list[str]]: + print("SHOULD NOT HAPPEN") if not all(k in virtual_file for k in ["layers", "sources", "layerTree"]): return From 2f7645b7b5287005652395fafaedf0fde80f1664 Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 7 Oct 2024 11:06:18 +0200 Subject: [PATCH 5/9] Set up exact --- .../jupytergis_qgis/jupytergis_qgis/qgis_loader.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py index 851acb80..0deb3df6 100644 --- a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py +++ b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py @@ -90,8 +90,6 @@ def qgis_layer_to_jgis( } ] - print("urls", urls) - colorRampTypeMap = {0: "interpolate", 1: "discrete", 2: "exact"} colorRampType = colorRampTypeMap[shaderFunc.colorRampType()] @@ -363,7 +361,6 @@ def qgis_layer_tree_to_jgis( def import_project_from_qgis(path: str | Path): - print("SANITY") if isinstance(path, Path): path = str(path) @@ -503,7 +500,7 @@ def build_uri(parameters: dict[str, str], source_type: str) -> str | None: map_layer = QgsRasterLayer(url, layer_name, "gdal") layer_colors = layer["parameters"]["color"] - print("source_parameters", source_parameters) + source_min = source_parameters["urls"][0]["min"] source_max = source_parameters["urls"][0]["max"] @@ -544,8 +541,8 @@ def build_uri(parameters: dict[str, str], source_type: str) -> str | None: color_ramp_shader.setColorRampType(QgsColorRampShader.Exact) # skip the last value, thats the fallback and not used by qgis - # skip the second to last because thats handles special - for index in range(3, len(layer_colors) - 1, 2): + # skip the second to last pair because that needs special handling + for index in range(3, len(layer_colors) - 3, 2): val = layer_colors[index][2] scaled_value = (val * (source_max - source_min)) / (1 - 0) + source_min colors = layer_colors[index + 1] @@ -560,6 +557,8 @@ def build_uri(parameters: dict[str, str], source_type: str) -> str | None: ), ), ) + + # Final stop in qgis has inf value color_stops.append( QgsColorRampShader.ColorRampItem( float("inf"), @@ -650,7 +649,6 @@ def jgis_layer_group_to_qgis( def export_project_to_qgis( path: str | Path, virtual_file: dict[str, Any] ) -> dict[str, list[str]]: - print("SHOULD NOT HAPPEN") if not all(k in virtual_file for k in ["layers", "sources", "layerTree"]): return From 3f84567b0429501ebeb4cfd86e3f6cec887b8700 Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 7 Oct 2024 11:20:16 +0200 Subject: [PATCH 6/9] Refactor color band stuff --- .../jupytergis_qgis/qgis_loader.py | 107 +++++++----------- 1 file changed, 44 insertions(+), 63 deletions(-) diff --git a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py index 0deb3df6..b99e21c4 100644 --- a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py +++ b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py @@ -117,71 +117,10 @@ def qgis_layer_to_jgis( ) if colorRampType == "discrete": - color = [ - "case", - ["==", ["band", 1.0], 0.0], - [0.0, 0.0, 0.0, 0.0], - ] - - # Last entry is inf so handle differently - for node in colorList[:-1]: - unscaled_val = (node.value * (1 - 0) - source_min * (1 - 0)) / ( - source_max - source_min - ) - color.append(["<=", ["band", float(band)], unscaled_val]) - color.append( - [ - node.color.red(), - node.color.green(), - node.color.blue(), - float(node.color.alpha()) / 255, - ] - ) - - lastElement = colorList[-1] - last_value = (source_max * (1 - 0) - source_min * (1 - 0)) / ( - source_max - source_min - ) - color.append(["<=", ["band", float(band)], last_value]) - color.append( - [ - lastElement.color.red(), - lastElement.color.green(), - lastElement.color.blue(), - float(node.color.alpha()) / 255, - ] - ) - - # Fallback value for openlayers - color.append([0, 0, 0, 1]) + color = build_color_ramp("<=", colorList, band, source_min, source_max) if colorRampType == "exact": - color = [ - "case", - ] - # Last entry is used for the fallback value in jgis - for node in colorList[:-1]: - unscaled_val = (node.value * (1 - 0) - source_min * (1 - 0)) / ( - source_max - source_min - ) - - color.append(["==", ["band", float(band)], unscaled_val]) - color.append( - [ - node.color.red(), - node.color.green(), - node.color.blue(), - float(node.color.alpha()) / 255, - ] - ) - lastElement = colorList[-1] - color.append( - [ - lastElement.color.red(), - lastElement.color.green(), - lastElement.color.blue(), - ] - ) + color = build_color_ramp("==", colorList, band, source_min, source_max) # TODO: Could probably look at RGB values to see what normalize should be source_parameters.update(urls=urls, normalize=True, wrapX=True) @@ -330,6 +269,48 @@ def qgis_layer_to_jgis( return layer_id +def build_color_ramp(operator, colorList, band, source_min, source_max): + color = [ + "case", + ["==", ["band", 1.0], 0.0], + [0.0, 0.0, 0.0, 0.0], + ] + + # Last entry is inf so handle differently + for node in colorList[:-1]: + unscaled_val = (node.value * (1 - 0) - source_min * (1 - 0)) / ( + source_max - source_min + ) + color.append([operator, ["band", float(band)], unscaled_val]) + color.append( + [ + node.color.red(), + node.color.green(), + node.color.blue(), + float(node.color.alpha()) / 255, + ] + ) + + lastElement = colorList[-1] + last_value = (source_max * (1 - 0) - source_min * (1 - 0)) / ( + source_max - source_min + ) + color.append([operator, ["band", float(band)], last_value]) + color.append( + [ + lastElement.color.red(), + lastElement.color.green(), + lastElement.color.blue(), + float(node.color.alpha()) / 255, + ] + ) + + # Fallback value for openlayers + color.append([0, 0, 0, 1]) + + return color + + def qgis_layer_tree_to_jgis( node: QgsLayerTreeGroup, layer_tree: list | None = None, From aa7d828abda381df4cbc01a7b59610a064f54654 Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 7 Oct 2024 12:43:24 +0200 Subject: [PATCH 7/9] Fix final stop for exact --- .../jupytergis_qgis/qgis_loader.py | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py index b99e21c4..6300cd7e 100644 --- a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py +++ b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py @@ -117,10 +117,10 @@ def qgis_layer_to_jgis( ) if colorRampType == "discrete": - color = build_color_ramp("<=", colorList, band, source_min, source_max) + color = _build_color_ramp("<=", colorList, band, source_min, source_max) if colorRampType == "exact": - color = build_color_ramp("==", colorList, band, source_min, source_max) + color = _build_color_ramp("==", colorList, band, source_min, source_max) # TODO: Could probably look at RGB values to see what normalize should be source_parameters.update(urls=urls, normalize=True, wrapX=True) @@ -269,7 +269,7 @@ def qgis_layer_to_jgis( return layer_id -def build_color_ramp(operator, colorList, band, source_min, source_max): +def _build_color_ramp(operator, colorList, band, source_min, source_max): color = [ "case", ["==", ["band", 1.0], 0.0], @@ -516,14 +516,18 @@ def build_uri(parameters: dict[str, str], source_type: str) -> str | None: selected_band = layer_colors[1][1][1] # check logical operator to choose discrete or exact op = layer_colors[3][0] + + # skip the last value in both cases, thats the fallback and not used by qgis if op == "<=": + # skip the second to last pair because that needs special handling color_ramp_shader.setColorRampType(QgsColorRampShader.Discrete) + endIndex = len(layer_colors) - 3 if op == "==": color_ramp_shader.setColorRampType(QgsColorRampShader.Exact) + endIndex = len(layer_colors) - 1 - # skip the last value, thats the fallback and not used by qgis - # skip the second to last pair because that needs special handling - for index in range(3, len(layer_colors) - 3, 2): + # skip the first pair, that's for open layers to handle NoData values + for index in range(3, endIndex, 2): val = layer_colors[index][2] scaled_value = (val * (source_max - source_min)) / (1 - 0) + source_min colors = layer_colors[index + 1] @@ -539,18 +543,19 @@ def build_uri(parameters: dict[str, str], source_type: str) -> str | None: ), ) - # Final stop in qgis has inf value - color_stops.append( - QgsColorRampShader.ColorRampItem( - float("inf"), - QColor( - int(layer_colors[-2][0]), - int(layer_colors[-2][1]), - int(layer_colors[-2][2]), - int(layer_colors[-2][3] * 255), + # Final stop in qgis for discrete has inf value + if op == "<=": + color_stops.append( + QgsColorRampShader.ColorRampItem( + float("inf"), + QColor( + int(layer_colors[-2][0]), + int(layer_colors[-2][1]), + int(layer_colors[-2][2]), + int(layer_colors[-2][3] * 255), + ), ), - ), - ) + ) color_ramp_shader.setColorRampItemList(color_stops) color_ramp_shader.setClip(True) From 54fc9da05dd7d1605b8feed9d3b8b8342ffeca42 Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 7 Oct 2024 13:04:13 +0200 Subject: [PATCH 8/9] Make fallback transparent --- .../dialogs/components/symbology/SingleBandPseudoColor.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/base/src/dialogs/components/symbology/SingleBandPseudoColor.tsx b/packages/base/src/dialogs/components/symbology/SingleBandPseudoColor.tsx index 9891d291..e836cfbc 100644 --- a/packages/base/src/dialogs/components/symbology/SingleBandPseudoColor.tsx +++ b/packages/base/src/dialogs/components/symbology/SingleBandPseudoColor.tsx @@ -263,7 +263,7 @@ const SingleBandPseudoColor = ({ }); // fallback value - colorExpr.push([0, 0, 0, 1.0]); + colorExpr.push([0, 0, 0, 0.0]); break; } case 'exact': { @@ -283,7 +283,7 @@ const SingleBandPseudoColor = ({ }); // fallback value - colorExpr.push([0, 0, 0, 1.0]); + colorExpr.push([0, 0, 0, 0.0]); break; } } From a2fa0cb18fa1f403a86786f62dcfb6d5eb19136d Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 7 Oct 2024 13:08:30 +0200 Subject: [PATCH 9/9] Color ramp builder fix --- .../jupytergis_qgis/jupytergis_qgis/qgis_loader.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py index 6300cd7e..76c6b365 100644 --- a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py +++ b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py @@ -122,7 +122,6 @@ def qgis_layer_to_jgis( if colorRampType == "exact": color = _build_color_ramp("==", colorList, band, source_min, source_max) - # TODO: Could probably look at RGB values to see what normalize should be source_parameters.update(urls=urls, normalize=True, wrapX=True) layer_parameters.update(color=color) @@ -276,7 +275,7 @@ def _build_color_ramp(operator, colorList, band, source_min, source_max): [0.0, 0.0, 0.0, 0.0], ] - # Last entry is inf so handle differently + # Last entry is inf for discrete, so handle differently for node in colorList[:-1]: unscaled_val = (node.value * (1 - 0) - source_min * (1 - 0)) / ( source_max - source_min @@ -292,9 +291,11 @@ def _build_color_ramp(operator, colorList, band, source_min, source_max): ) lastElement = colorList[-1] - last_value = (source_max * (1 - 0) - source_min * (1 - 0)) / ( - source_max - source_min - ) + last_value = ( + source_max + if operator == "<=" + else lastElement.value * (1 - 0) - source_min * (1 - 0) + ) / (source_max - source_min) color.append([operator, ["band", float(band)], last_value]) color.append( [ @@ -306,7 +307,7 @@ def _build_color_ramp(operator, colorList, band, source_min, source_max): ) # Fallback value for openlayers - color.append([0, 0, 0, 1]) + color.append([0.0, 0.0, 0.0, 0.0]) return color