3

I'm developing a plugin for QGIS called 'Mappia_Example' to allows the community to publish its maps online. But i need some help, i am trying to export a map in QGIS to PNG but its using aproximated colors instead of the ones defined on legend entries.

You can export a map using the interface by clicking: Project -> Import/Export -> Export as image. and the issue is the same.

The problem is that the legend has a value but the PNG file has other colors. I need a way to export the map using the color in legend, not approximated colors. I've already set the ResamplingMethod to 'Nearest' but without success.

How to export a tif map to PNG in QGIS, and it keep using the exact colors of legend entries? If someone can please help.

Examples of wrong pixel color values:

  1. Issue on a gray entry (178,178,178) drawn as (177,177,177):

Legend (178,178,178) gray as 'No Forest' contrast, brightness and saturation are 0: Legend (178,178,178) gray as 'No Forest' contrast, brightness and saturation are 0 Drawn as (177,177,177) pixel: Getting the RGB value of the gray rendered entry as (177,177,177)

  1. Issue on a yellow entry (255,178, 0) drawn as (250,225,5):

Another example of difference in a legend entry with color (255,178, 0) representing '43-48' interval. Legend color (255,178, 0) representing 43-48 Drawn as (250,225,5) pixel: This legend is drawn as (250,225,5)

A Working code that i made trying to export using pyQgis tested in 3.14 but should work from 3.4 version:

from qgis.core import (QgsCoordinateTransform, QgsCoordinateReferenceSystem, QgsProject)

#Configure the rendering settings for the WMS tiles.
def createLayerRenderSettings(layer, dest_crs, outputFormat):
    settings = QgsMapSettings()
    settings.setFlag(QgsMapSettings.Flag.Antialiasing, False)
    settings.setFlag(QgsMapSettings.Flag.UseRenderingOptimization, False)
    #settings.setFlag(QgsMapSettings.Flag.LosslessImageRendering, True) #not available yet 
    settings.setFlag(QgsMapSettings.Flag.UseAdvancedEffects, False)
    
    settings.setOutputImageFormat(outputFormat)
    settings.setDestinationCrs(dest_crs)
    settings.setLayers([layer])
    dpi = 256
    settings.setOutputDpi(dpi)
    color = QColor(0, 0, 0, 0)
    settings.setBackgroundColor(color)
    layer.resampleFilter().setZoomedInResampler(None)
    layer.resampleFilter().setZoomedOutResampler(None)
    layer.resampleFilter().setOn(False)
    return settings

def getMapExtent(layer, projection):
    mapExtent = layer.extent()
    projection.validate()
    layer.crs().validate()
    src_to_proj = QgsCoordinateTransform(layer.crs(), projection, QgsProject.instance())
    return src_to_proj.transformBoundingBox(mapExtent)

#Return the rendered map (QImage) for the metatile zoom level.
def renderMetatile(dest_crs, renderSettings, transformContext, sourceCrs, extents, width, height):
    wgs_to_dest = QgsCoordinateTransform(sourceCrs, dest_crs, transformContext)
    renderSettings.setExtent(wgs_to_dest.transformBoundingBox(extents)) #QgsRectangle(*extents)))
    size = QSize(width, height)
    renderSettings.setOutputSize(size)
    image = QImage(size, renderSettings.outputImageFormat())
    image.fill(Qt.transparent)
    painter = QPainter(image)
    job = QgsMapRendererCustomPainterJob(renderSettings, painter)
    job.renderSynchronously()
    painter.end()
    return image


layer = QgsProject.instance().mapLayersByName("phylogenetic_composition")[0]
dest_crs = QgsCoordinateReferenceSystem('EPSG:3857')
wgs_crs = QgsCoordinateReferenceSystem('EPSG:4326')
extent = getMapExtent(layer, wgs_crs)
outputFormat = QImage.Format_ARGB32
layerRenderSettings = createLayerRenderSettings(layer, dest_crs, outputFormat)
tileHeight = tileWidth = 256
mapRendered = renderMetatile(dest_crs, layerRenderSettings, layer.transformContext(), wgs_crs, extent, tileWidth, tileHeight)
quality = 100
mapRendered.save('C:\\tmp\\tile.png', 'PNG', quality)
#no success with paleted png either
mapRendered.convertToFormat(QImage.Format_Indexed8, Qt.ColorOnly | Qt.ThresholdDither | Qt.AvoidDither).save("C:/tmp/2a.png", "PNG", -1)

Example data:

  1. Can download the map i'm using: https://github.com/asfixia/Mappia_Example/releases/download/Map_Download/phylogenetic_composition.zip
  2. Or access the map "phylogenetic_composition" online in: https://maps.csr.ufmg.br/calculator/?map=&queryid=152&listRepository=Repository&storeurl=https://github.com/asfixia/Mappia_Example/

PS: I've tried setting the QgsMapSettings.Flag.LosslessImageRendering in QGIS 3.15 but the results still the same.

Edit: The exact style i am using: https://github.com/asfixia/Mappia_Example/releases/download/Map_Download/phylogenetic_composition.qml

Edit2: I had to change the "Min / Max " interpolation method to "discrete" so the colors are now exact. The fixed QML style can be downloaded at: https://github.com/asfixia/Mappia_Example/releases/download/Map_Download/phylogenetic_composition_fixed.qml

8
  • Related: gis.stackexchange.com/questions/243436/… ? Sep 3, 2020 at 2:42
  • No, the other topic is in fact about the gdal library and is dealing with null value and resampling issues. Mine is about the legend color and exporting png from qgis.
    – Asfixia
    Sep 3, 2020 at 3:59
  • I've seen this effect with palette color files, which PNG support, but I can't find any reference to say that the pixel type can be changed from the QGIS end, your code clearly exports as 32 bit ARGB, is there any other process that touches the raster on the other end that 'helps' by making your ARGB into 8 bit palette to reduce transmission size? Sep 3, 2020 at 4:47
  • Yes i could set the color table directly. But my goal here is to find a way to export the exact color as in legend entry. (the 8 bit palette i put here just for testing)
    – Asfixia
    Sep 3, 2020 at 7:02
  • 1
    The export layout works great for me, here is my question with a code snippet for testing: gis.stackexchange.com/questions/372451/… Sep 3, 2020 at 7:34

1 Answer 1

0

As @Asfixia mentioned in the comments, setting the interpolation method to Discrete in rasters Symbology parameters solved the problem:

enter image description here

3
  • Tested, but unfortunatelly no success too.
    – Asfixia
    Sep 3, 2020 at 15:17
  • Humm, in fact you got it working. But not because the Min/Max Values, but the interpolation method. The method discrete works, use the real color, but the linear change the color. Looks like the QGIS use this method for interpolation of the drawn colors! Please change the reason in your answer, to i accept it. I really appreciated your help! Thanks! :)
    – Asfixia
    Sep 3, 2020 at 15:57
  • @Asfixia Glad to help you. Answer edited. Sep 4, 2020 at 4:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.