def testSingleBandPseudoColorRenderer_Exact(self): # get min and max of the band to renderer bandNo = 3 stats = self.raster_layer.dataProvider().bandStatistics(bandNo, QgsRasterBandStats.Min | QgsRasterBandStats.Max) minValue = stats.minimumValue maxValue = stats.maximumValue # create shader for the renderer shader = QgsRasterShader(minValue, maxValue) colorRampShaderFcn = QgsColorRampShader(minValue, maxValue) colorRampShaderFcn.setColorRampType(QgsColorRampShader.Exact) colorRampShaderFcn.setClassificationMode(QgsColorRampShader.Continuous) colorRampShaderFcn.setClip(True) items = [] for index in range(10): items.append(QgsColorRampShader.ColorRampItem(index, QColor('#{0:02d}{0:02d}{0:02d}'.format(index)), "{}".format(index))) colorRampShaderFcn.setColorRampItemList(items) shader.setRasterShaderFunction(colorRampShaderFcn) # create instance to test rasterRenderer = QgsSingleBandPseudoColorRenderer(self.raster_layer.dataProvider(), bandNo, shader) self.raster_layer.setRenderer(rasterRenderer) # do test dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '{}'.format(bandNo)) # check ColorMapEntry classes colorMap = root.elementsByTagName('sld:ColorMap') colorMap = colorMap.item(0).toElement() self.assertFalse(colorMap.isNull()) self.assertEqual(colorMap.attribute('type'), 'values') self.assertFalse(colorMap.hasAttribute('extendend')) colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(colorMapEntries.count(), 10) for index in range(colorMapEntries.count()): colorMapEntry = colorMapEntries.at(index).toElement() self.assertEqual(colorMapEntry.attribute('quantity'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('label'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('opacity'), '') self.assertEqual(colorMapEntry.attribute('color'), '#{0:02d}{0:02d}{0:02d}'.format(index)) # add check that is set ColoMap extended="true" if colormap is bigger that 255 entries # !NOTE! can't reuse previous shader => segmentation fault shader = QgsRasterShader(minValue, maxValue) colorRampShaderFcn = QgsColorRampShader(minValue, maxValue) colorRampShaderFcn.setColorRampType(QgsColorRampShader.Exact) colorRampShaderFcn.setClassificationMode(QgsColorRampShader.Continuous) colorRampShaderFcn.setClip(True) items = [] for index in range(255): items.append( QgsColorRampShader.ColorRampItem(index, QColor.fromHsv(index, 255, 255, 255), "{}".format(index))) colorRampShaderFcn.setColorRampItemList(items) shader.setRasterShaderFunction(colorRampShaderFcn) # create instance to test rasterRenderer = QgsSingleBandPseudoColorRenderer(self.raster_layer.dataProvider(), bandNo, shader)
def testSingleBandPseudoColorRenderer_Exact(self): # get min and max of the band to renderer bandNo = 3 stats = self.raster_layer.dataProvider().bandStatistics(bandNo, QgsRasterBandStats.Min | QgsRasterBandStats.Max) minValue = stats.minimumValue maxValue = stats.maximumValue # create shader for the renderer shader = QgsRasterShader(minValue, maxValue) colorRampShaderFcn = QgsColorRampShader(minValue, maxValue) colorRampShaderFcn.setColorRampType(QgsColorRampShader.Exact) colorRampShaderFcn.setClassificationMode(QgsColorRampShader.Continuous) colorRampShaderFcn.setClip(True) items = [] for index in range(10): items.append(QgsColorRampShader.ColorRampItem(index, QColor('#{0:02d}{0:02d}{0:02d}'.format(index)), "{}".format(index))) colorRampShaderFcn.setColorRampItemList(items) shader.setRasterShaderFunction(colorRampShaderFcn) # create instance to test rasterRenderer = QgsSingleBandPseudoColorRenderer(self.raster_layer.dataProvider(), bandNo, shader) self.raster_layer.setRenderer(rasterRenderer) # do test dom, root = self.rendererToSld(self.raster_layer.renderer()) self.assertNoOpacity(root) self.assertChannelBand(root, 'sld:GrayChannel', '{}'.format(bandNo)) # check ColorMapEntry classes colorMap = root.elementsByTagName('sld:ColorMap') colorMap = colorMap.item(0).toElement() self.assertFalse(colorMap.isNull()) self.assertEqual(colorMap.attribute('type'), 'values') self.assertFalse(colorMap.hasAttribute('extendend')) colorMapEntries = colorMap.elementsByTagName('sld:ColorMapEntry') self.assertEqual(colorMapEntries.count(), 10) for index in range(colorMapEntries.count()): colorMapEntry = colorMapEntries.at(index).toElement() self.assertEqual(colorMapEntry.attribute('quantity'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('label'), '{}'.format(index)) self.assertEqual(colorMapEntry.attribute('opacity'), '') self.assertEqual(colorMapEntry.attribute('color'), '#{0:02d}{0:02d}{0:02d}'.format(index)) # add check that is set ColoMap extended="true" if colormap is bigger that 255 entries # !NOTE! can't reuse previous shader => segmentation fault shader = QgsRasterShader(minValue, maxValue) colorRampShaderFcn = QgsColorRampShader(minValue, maxValue) colorRampShaderFcn.setColorRampType(QgsColorRampShader.Exact) colorRampShaderFcn.setClassificationMode(QgsColorRampShader.Continuous) colorRampShaderFcn.setClip(True) items = [] for index in range(255): items.append(QgsColorRampShader.ColorRampItem(index, QColor.fromHsv(index, 255, 255, 255), "{}".format(index))) colorRampShaderFcn.setColorRampItemList(items) shader.setRasterShaderFunction(colorRampShaderFcn) # create instance to test rasterRenderer = QgsSingleBandPseudoColorRenderer(self.raster_layer.dataProvider(), bandNo, shader)
def rat_classify(raster_layer, band, rat, criteria, ramp=None, feedback=QgsRasterBlockFeedback()) -> list: """Classify a raster. Note: cannot use a custom shader function QgsColorRampShader subclass because it's lost in the clone stage of the renderer. :param raster_layer: the raster layer to classify :type raster_layer: QgsRasterLayer :param band: band number (1-based) :type band: int :param rat: the RAT data :type rat: dict :param criteria: key of the RAT to be used for labels :type criteria: str :param ramp: optional color ramp, defaults to QgsRandomColorRamp() :type ramp: QgsColorRamp, optional :param feedback: QGIS feedback object, defaults to QgsRasterBlockFeedback() :type feedback: QgsRasterBlockFeedback, optional :return: unique row indexes for legend items (1-based) :rtype: list """ has_color = rat.has_color labels = rat.data[criteria] label_colors = {} unique_indexes = [] # QGIS >= 3.18 for first element label if Qgis.QGIS_VERSION_INT >= 31800: base_legend_row_index = 1 else: base_legend_row_index = 0 if rat.thematic_type == gdal.GRTT_THEMATIC: # Use paletted rat_log('Using paletted renderer') value_column_name = rat.field_name(gdal.GFU_MinMax) values = rat.data[value_column_name] is_integer = isinstance(values[0], int) if ramp is None: ramp = QgsRandomColorRamp() classes = QgsPalettedRasterRenderer.classDataFromRaster( raster_layer.dataProvider(), band, ramp, feedback) row_index = base_legend_row_index for klass in classes: value = int(klass.value) if is_integer else klass.value try: index = values.index(value) except ValueError: # NODATA rat_log( f'Value {value} not found in RAT, assuming NODATA', Qgis.Warning) data_provider = raster_layer.dataProvider() if not data_provider.userNoDataValuesContains(band, value): nodata = data_provider.userNoDataValues(band) nodata_value = QgsRasterRange(value, value) nodata.append(nodata_value) data_provider.setUserNoDataValue(band, nodata) continue klass.label = str(labels[index]) if klass.label not in label_colors: unique_indexes.append(row_index) if has_color: label_colors[klass.label] = rat.data[RAT_COLOR_HEADER_NAME][index] else: label_colors[klass.label] = klass.color klass.color = label_colors[klass.label] row_index += 1 renderer = QgsPalettedRasterRenderer( raster_layer.dataProvider(), band, classes) else: # ranges rat_log('Using singleband pseudocolor renderer') min_value_column = rat.field_name(gdal.GFU_Min) max_value_column = rat.field_name(gdal.GFU_Max) # Collect unique values and colors from criteria row_index = base_legend_row_index unique_labels = [] for index in range(len(labels)): label = labels[index] if label not in unique_labels: unique_labels.append(label) unique_indexes.append(row_index) # Collect color if has_color: label_colors[label] = rat.data[RAT_COLOR_HEADER_NAME][index] row_index += 1 # Assign colors from random ramp if not has_color: ramp = QgsRandomColorRamp() ramp.setTotalColorCount(len(unique_labels)) i = 0 for index in unique_indexes: label_colors[labels[index]] = ramp.color(ramp.value(i)) i += 1 # Create values for the ramp # Collect colors for all classes colors = [] for label in labels: colors.append(label_colors[label]) ramp = QgsPresetSchemeColorRamp(colors) minValue = min(rat.data[min_value_column]) maxValue = max(rat.data[max_value_column]) assert minValue < maxValue, "Min Value must be lower than Max Value" shader = QgsRasterShader(minValue, maxValue) colorRampShaderFcn = QgsColorRampShader( minValue, maxValue, ramp) colorRampShaderFcn.setClip(True) colorRampShaderFcn.setColorRampType(QgsColorRampShader.Discrete) items = [] row = 0 for label in labels: items.append(QgsColorRampShader.ColorRampItem( rat.data[max_value_column][row], label_colors[label], label)) row += 1 colorRampShaderFcn.setColorRampItemList(items) try: # for older QGIS colorRampShaderFcn.legendSettings().setUseContinuousLegend(False) except AttributeError: rat_log( 'QgsColorRampShader.legendSettings().setUseContinuousLegend() is not supported on ths QGIS version.', Qgis.Warning) shader.setRasterShaderFunction(colorRampShaderFcn) renderer = QgsSingleBandPseudoColorRenderer( raster_layer.dataProvider(), band, shader) raster_layer.setRenderer(renderer) raster_layer.triggerRepaint() return unique_indexes