8

I have the following function:

def createScreenshots(layer):
  counter = 0
  for feature in layer.getFeatures():
    counter += 1
    point = feature.geometry().asPoint()
    qgis.utils.iface.mapCanvas().setCenter(point)
    qgis.utils.iface.mapCanvas().refreshAllLayers()
    qgis.utils.iface.mapCanvas().saveAsImage("D:\\\\m\\testing\\" + str(counter) + ".png")

Screenshots are not in real coordinates, .pgw header is not in right coordinates, therefore resulting png images are shifted.

Question: How to wait for rendering of canvas after setCenter is called?

Marcel GJS
  • 473
  • 3
  • 13

3 Answers3

5

You can use QgsMapCanvas.mapCanvasRefreshed SIGNAL to only save the map when canvas has finished painting layers. After saving, zoom to the next feature, and so on.

layer = iface.activeLayer()
ids = layer.allFeatureIds()

def exportMap():
    global ids
    iface.mapCanvas().saveAsImage( u"/output/folder/{}.png".format( ids.pop() ) )
    if ids:
        setNextFeatureExtent()
    else: # We're done
        iface.mapCanvas().mapCanvasRefreshed.disconnect( exportMap )

def setNextFeatureExtent():
    iface.mapCanvas().zoomToFeatureIds( layer, [ids[-1]] )

iface.mapCanvas().mapCanvasRefreshed.connect( exportMap )
setNextFeatureExtent() # Let's start

You could easily adjust the code snippet above to use setExtent() or setCenter() methods, after which you would need to call mapCanvas.refresh() anyway.

Germán Carrillo
  • 36,307
  • 5
  • 123
  • 178
4
ids = None

def createScreenshot4(layer):
  global ids
  ids = layer.allFeatureIds()

  def exportMap():
    global ids
    qgis.utils.iface.mapCanvas().saveAsImage( "D:\\\\ma\\bo\\{}.png".format( ids.pop() ) )
    if ids:
        setNextFeatureExtent()
    else: # We're done
        qgis.utils.iface.mapCanvas().mapCanvasRefreshed.disconnect( exportMap )

  def setNextFeatureExtent():
    reqq = QgsFeatureRequest()
    reqq.setFilterFid(ids[-1])
    for feature in layer.getFeatures(reqq):
        point = feature.geometry().asPoint()
        qgis.utils.iface.mapCanvas().setCenter(point)
        qgis.utils.iface.mapCanvas().refreshAllLayers()


  qgis.utils.iface.mapCanvas().mapCanvasRefreshed.connect( exportMap )
  setNextFeatureExtent() # Let's start

Resulted code with setCenter and refreshAllLayers.

Marcel GJS
  • 473
  • 3
  • 13
  • Note: the default saveAsImage process gives non-georeferenced PNG files - no SRS in the PGW, or in the PNG itself. You can't change the SRS on PNG files using gdal_edit. The QGIS API docs show that there are two optional parameters that can be passed to iface.mapcanvas().saveAsimage("[filename]" , QPixmap,"[format]"); putting None for QPixmap gives a Python error (global name nullptr is not defined) for every saved image, but the images still save so meh to the error. Then for %f in *.tiff do gdal_edit -a_SRS EPSG:3857 %f ... result: fixed-resolution geoTIFFs. – GT. Jun 20 '17 at 22:30
  • Update : there's clearly something I don't understand about the QPixmap parameter. Everyone who's ever asked about it has set it to None seemingly without incident, but doing so in 2.18.9 will make a loop-based feature-saving script gag every 20 or so features (the Python error notification will change from an ignorable box at the top of the map canvas, to a process-interrupting popup). The .cpp file passes the input to *theQPixmap (at line 665), and if it exists sets the image to be painted to theQPixmap->toImage(), else mMap->contentImage().copy() (at line 673-688). – GT. Jun 20 '17 at 23:34
2

You may use a QTimer class, which allows waiting some milliseconds before running a specified function/instruction.

The line to use is something like this:

QTimer.singleShot(1000, saveMap)

where the first argument is expressed in milliseconds (1000 ms = 1 second) and the second argument is the function to call.

You may rewrite your original code in this way:

def createScreenshots(layer):
    counter = 0

    def saveMap():
        qgis.utils.iface.mapCanvas().saveAsImage("D:\\\\m\\testing\\" + str(counter) + ".png")

    for feature in layer.getFeatures():
        counter += 1
        point = feature.geometry().asPoint()
        qgis.utils.iface.mapCanvas().setCenter(point)
        qgis.utils.iface.mapCanvas().refreshAllLayers()
        QTimer.singleShot(1000, saveMap)

I think that 1000 milliseconds should be enough for reflecting the changes, otherwise you may adapt it to your specific case.

mgri
  • 16,159
  • 6
  • 47
  • 80
  • 1
    Thank you, but QGIS is setting center to the last feature and is making screenshot image. Other features are ignored. – Marcel GJS Mar 09 '17 at 15:54