1

I want to create a georeference png of the map canvas in QGIS using Python with:

mapCanvas().saveAsImage('filename.png')

I want to be able to change the resolution from the default of 96 dpi.

Is there a way to adjust this using the Python editor within QGIS?

1 Answer 1

2

I assume, you run your map render context in a standalone application. I'm not sure if you want to produce high resolution images or set only the DPI number.

If you want to produce high resolution images, you could set mapCanvas()mapSettings().setDevicePixelRatio(scaleFactor) before you call mapCanvas().saveAsImage('FILENAME.PNG'). The function triggers QgsMapSettings::updateDerived() and calculates the mapUnitsPerPixel and the new DPI number.

 # line 170
 // calculate the translation and scaling parameters
 const double mapUnitsPerPixelY = mExtent.height() / myHeight;
 const double mapUnitsPerPixelX = mExtent.width() / myWidth;
 mMapUnitsPerPixel = mapUnitsPerPixelY > mapUnitsPerPixelX ? mapUnitsPerPixelY : mapUnitsPerPixelX;
 ...
 # Line 192 
 ...
 // update the scale
 mScaleCalculator.setDpi( mDpi * mDevicePixelRatio );
 mScale = mScaleCalculator.calculate( mVisibleExtent, mSize.width() );

I have not tested this befor because, I personally try to avoid the rendering into an image context and use a the pdf printer context instead. In the printer context you have all properties to render your map into a well defined page. You can use imagemagick or ghostscript to create DPI correct and anti-aliased images later.

Example code snippet from one of my projects

#!/usr/bin/env python3
import sys
from qgis.core import *
from qgis.gui import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtXml import *

# ---------------------------------------------------
# Constants 
# ---------------------------------------------------
SHP_DATA     = 'ogr'
QGIS_PATH    = '/usr'
PAGE_WIDTH   = 300 # mm
PAGE_HEIGHT  = 300 # mm
MAP_WIDTH    = 130
MAP_HEIGHT   = 130
MAP_LEFT     = 25
MAP_TOP      = 10
MAP_RIGHT    = MAP_LEFT + MAP_WIDTH
MAP_BOTTOM   = MAP_TOP  + MAP_HEIGHT
GAP          = 40
LEG_TOP      = MAP_TOP
LEG_LEFT     = MAP_RIGHT + GAP

# -----------------------------------------------------
# Keys for the map frame
# -----------------------------------------------------
# Label inside or outside of the map 
GRID_ANNON_INMAP  = 0
GRID_ANNON_OUTMAP = 1
GRID_ANNON_NONE   = 2

# Keys for the grid annotation side
GRID_SIDE_RIGHT   = 0
GRID_SIDE_LEFT    = 1
GRID_SIDE_BOTTOM  = 2
GRID_SIDE_TOP     = 3

# Keys for the label direction
GRID_ANNON_DIR_0    = 0
GRID_ANNON_DIR_90   = 1
GRID_ANNON_DIR_180  = 2
GRID_ANNON_DIR_270  = 3

# Keys for the axis format
GRID_ANNON_FMT_DEC = 0 # Dezimalzahlen + N, E
GRID_ANNON_FMT_DM  = 1 # D°M'N und D°M'E 
GRID_ANNON_FMT_DMS = 2 # D°M'S"N und D°M'S"E 
GRID_ANNON_FMT_DEC_NONSWE  = 3 # Ohne Dezimalzahlen
GRID_ANNON_FMT_DM_NONSWE   = 4 # D°M'

# Gridd type
GRID_FRAME_PLAIN = 0 # einfache Linie
GRID_FRAME_ZEBRA = 1 # Kästchen

# -----------------------------------------------------
# Settings for the map frame
# -----------------------------------------------------
# Schrittweite der Label 10km 
GRID_FRAME_STEP_LABX = 1/3.0
GRID_FRAME_STEP_LABY = 1/6.0

# Fonts font for the map labels
GRID_FRAME_FONT = QFont('Arial',11,0)

# Precisiion of the labels
GRID_FRAME_PRECISION = 0

# Size of the map crosses 0 if you don't want
GRID_FRAME_CROSS_SIZE = 0

# Breite des Rahmens
GRID_FRAME_WIDTH = 1

# Colors for the frame 
GRID_COLOR_LIGHT = QColor('#ffffff')
GRID_COLOR_DARK  = QColor('#040404')
 
# ---------------------------------------------------
# Create a map
# ---------------------------------------------------
# @param page - Composer page 
# @param upper - Upper border of the sheet in mm
# @param left  - Left border of the sheet in mm
# @param lower - Lower border of the sheet in mm
# @param right - Right border of the sheet in mm
# @param extent - Extent of the map info world coordinates only
# @return Map with border config (..see also GRID_FRAME_*)
# ---------------------------------------------------
def createMap(page, upper, left, lower, right, extent):
    # Karte bauen
    print "Create ma sheet:  (%d, %d) (%d, %d) [mm]" % (upper, left, lower, right)
    print "    Size UTM:  (%2.0f,%2.0f) (%2.0f, %2.0f) [m]" \
      % (extent.xMinimum(), extent.yMinimum(), \
         extent.xMaximum(), extent.yMaximum())
    cmap = QgsComposerMap(page, upper, left, lower, right)
    # Kartenabdeckung eintragen
    cmap.setNewExtent(extent)
    # Create frame and cosses ----------------------
    cmap.setGridStyle(GRID_FRAME_ZEBRA)
    # Label font 
    cmap.setGridAnnotationFont(GRID_FRAME_FONT)
    # Size of the crosses
    cmap.setCrossLength(GRID_FRAME_CROSS_SIZE)
    # Intervalabstand setzten
    cmap.grid().setCrs(tk.epsgCrs(4326))
    cmap.setGridIntervalX(GRID_FRAME_STEP_LABX)
    cmap.setGridIntervalY(GRID_FRAME_STEP_LABY)
    # Label format
    cmap.setGridAnnotationFormat(GRID_ANNON_FMT_DM_NONSWE)
    # Precision of the label format
    cmap.setGridAnnotationPrecision(GRID_FRAME_PRECISION)
    # Ausrichtung des Labels fuer die ensprechende Achse
    cmap.setGridAnnotationDirection(GRID_ANNON_DIR_90,
                                    GRID_SIDE_LEFT)
    cmap.setGridAnnotationDirection(GRID_ANNON_DIR_90,
                                    GRID_SIDE_RIGHT)
    # Label per axis Labelposition
    cmap.setGridAnnotationPosition(GRID_ANNON_OUTMAP,
                                   GRID_SIDE_TOP)
    cmap.setGridAnnotationPosition(GRID_ANNON_OUTMAP,
                                   GRID_SIDE_RIGHT)
    cmap.setGridAnnotationPosition(GRID_ANNON_OUTMAP,
                                   GRID_SIDE_LEFT)
    cmap.setGridAnnotationPosition(GRID_ANNON_OUTMAP,
                                   GRID_SIDE_BOTTOM)
    # Activate annotation
    cmap.setShowGridAnnotation(1)
    # Frame style -----------------------------
    cmap.setGridFrameStyle(GRID_FRAME_ZEBRA)
    # Frame width
    cmap.setGridFrameWidth(GRID_FRAME_WIDTH)
    # Frame color (hell & dunkel)
    cmap.setGridFrameFillColor1(GRID_COLOR_LIGHT)
    cmap.setGridFrameFillColor2(GRID_COLOR_DARK)
    # Enable frame 
    cmap.setFrameEnabled(True)
    return cmap

# ------------------------------------------------------
# Start application ...
# ------------------------------------------------------
application = QgsApplication([], True)
QgsApplication.setPrefixPath(QGIS_PATH, True)
QgsApplication.initQgis()

# ------------------------------------------------------
# Service function print a page into a pdf
# ------------------------------------------------------
def writePdfFullPage(page, width, height, fileName='out.pdf'):
    print "Schreibe PDF: "+fileName
    printer = QPrinter()
    printer.setOutputFormat(QPrinter.PdfFormat)
    printer.setOutputFileName(fileName)
    printer.setPaperSize(QSizeF(width, height), QPrinter.Millimeter)
    printer.setFullPage(True)
    printer.setColorMode(QPrinter.Color)
    printer.setResolution(page.printResolution())

    pdfPainter = QPainter(printer)
    paperRectMM = printer.pageRect(QPrinter.Millimeter)
    paperRectPixel = printer.pageRect(QPrinter.DevicePixel)
    page.render(pdfPainter, paperRectPixel, paperRectMM)
    pdfPainter.end()

# ------------------------------------------------------
# Calculate the map extent
# ------------------------------------------------------
def calcMapExt(lyr, offs=0.01):
    result = lyr.extent()
    print "Map extent:", result.xMinimum(), result.yMinimum(), result.xMaximum(), result.yMaximum()
    result.setXMinimum(result.xMinimum()-offs)
    result.setYMinimum(result.yMinimum()-offs)
    result.setXMaximum(result.xMaximum()+offs)
    result.setYMaximum(result.yMaximum()+offs)
    print "Map extent:", result.xMinimum(), result.yMinimum(), result.xMaximum(), result.yMaximum()
    return result;

# ---------------------------------------------------
# Open a vector layer  
# ---------------------------------------------------
def openVectorLayer(uri, name):
    print "Open layer: %s " % (name)  
    layer = QgsVectorLayer(uri.uri(),name,SHP_DATA)
    if not layer.isValid():
          msg  = "Cannot open layer  '"+name+"'\n"
          msg += "File:     "+uri+"\n"
          msg += "Filter:   "+where+"\n"
          raise ValueError(msg)
    print "    %d Object(s)" % (layer.featureCount())  
    return layer

# ----------------------------------------------
# Create the legend
# ----------------------------------------------
def createLegend (page, layers,  x, y, title, \
                  titleFont = QFont('Arial',12,2), \
                  groupFont = QFont('Arial',11,1), 
                  itemFont  = QFont('Arial',10,1) ):
    print "Create legend: %s %d %d " % (title, x, y)
    cleg = QgsComposerLegend(page)
    cleg.setTitle(title)
    cleg.rstyle(QgsComposerLegendStyle.Symbol).setMargin(QgsComposerLegendStyle.Top, 1)
    cleg.setStyleFont(QgsComposerLegendStyle.Title, titleFont)
    cleg.setStyleFont(QgsComposerLegendStyle.Subgroup, groupFont)
    cleg.setStyleFont(QgsComposerLegendStyle.SymbolLabel, itemFont)
    cleg.setX(x)
    cleg.setY(y)
    cleg.model().setLayerSet(layers)
    return cleg

# ---------------------------------------------------
# Read some layers and add a renderer
# ---------------------------------------------------
# Region of interest 
lyrRoi     = openVectorLayer('roi.shp','roi')
symRoi = QgsSimpleLineSymbolLayerV2()
symRoi.setColor(QColor('#dd4433'))
symRoi.setWidth(1)
lyrRoi.rendererV2().symbols()[0].changeSymbolLayer(0, symRoi)

# Special protection area
lyrSPA = openVectorLayer('SPA.shp','SPA')
symSPAa = QgsSimpleLineSymbolLayerV2()
colSPA = QColor('#aaaaaa');
symSPAa.setColor(colSPA)
symSPAa.setWidth(0.5)
symSPAb = QgsLinePatternFillSymbolLayer()
symSPAb.setColor(colSPA)
symSPAb.setLineWidth(0.5)
symSPAb.setLineAngle(45.0)
symSPAb.setDistance(1.5);
lyrSPA.rendererV2().symbols()[0].changeSymbolLayer(0, symSPAa)
lyrSPA.rendererV2().symbols()[0].appendSymbolLayer(symSPAb)

# -------------------------------------------
# Open the render engine 
# -------------------------------------------
renderer = QgsMapRenderer()
mapCrs   = tk.epsgCrs(3395)
renderer.setDestinationCrs(mapCrs)
renderer.setProjectionsEnabled(True)

mapset = []

mapset.append(lyrRoi.id())
mapset.append(lyrContent.id())
...
renderer.setLayerSet(mapset)
mapExtent  = calcMapExt(lyrRoi);
mapTrfm    = QgsCoordinateTransform(lyrRoi.crs(), mapCrs)
mapExtent  = mapTrfm.transform(mapExtent)
renderer.setExtent(mapExtent)

# --------------------------------------------------
# Declare a page and store the PDF
# --------------------------------------------------   
page = QgsComposition(renderer)
page.setPlotStyle(QgsComposition.Print)

# Create the map...
cmap= createMap(page, MAP_LEFT, MAP_TOP, \
               MAP_RIGHT, MAP_BOTTOM, mapExtent)

...
# Legende
cleg = createLegend( page, mapset, \
                    LEG_LEFT, LEG_TOP, \
                    '')

writePdfFullPage(page,PAGE_WIDTH, PAGE_HEIGHT, \
 fileName='example.pdf')

print "OK."
QgsApplication.exitQgis()

# EOF
2
  • I am using it in QGIS python script editor. I am a little confused how to use the set device pixel command. I tested like this using both 1 and 100 and it had no effect on the output image: iface.mapCanvas().mapSettings().setDevicePixelRatio(1); iface.mapCanvas().saveAsImage('FILENAME.PNG').
    – JUNGLE
    Dec 9, 2021 at 22:36
  • OK have , as mentioned, used the code above only in a QGIS stand alone context. What is the code base to execute the task within QGIS?
    – huckfinn
    Dec 9, 2021 at 23:41

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.