As directly using a Openlayers plugin layer in a programatically created print composition is not possible (see the accepted answer), this approach can at least be used to create a stationary image file (jpg/png) from a desired Openlayers map extent. Maybe this is helpful to somebody somehow...
It is based on the basemap2Image script which I modified slightly to make it callable from within a PyQGIS script running in the Python console. To use it, you need the following helper functions.
Getting the edge coordinates of a vector layer extent:
###################################
# function to get coordinates of a layer extent
###################################
def getCoordinatesOfLayerExtent(layer):
print 'getting coordinates of layer extent'
layerRectangle = layer.extent()
coordinates = [layerRectangle.xMinimum(), layerRectangle.yMinimum(), layerRectangle.xMaximum(), layerRectangle.yMaximum()]
return coordinates
Call it by providing a QgsVectorLayer object, e.g.
vectorLayerCoordinates = getCoordinatesOfLayerExtent(myFavoriteVectorLayer)
Coordinates have to be passed to the script in EPSG:3857, so if your vector layer does not use that CRS by default, use the following function to transform the coordinates first:
###################################
# function to transform a set of coordinates from one CRS to another
###################################
def transformCoordinates(coordinates, fromCRS, toCRS):
print 'transforming coordinates between crs'
crsSrc = QgsCoordinateReferenceSystem(fromCRS)
crsDest = QgsCoordinateReferenceSystem(toCRS)
xform = QgsCoordinateTransform(crsSrc, crsDest)
# convert list of coordinates to QgsPoint objects
coordinatesAsPoints = [QgsPoint(coordinates[0], coordinates[1]), QgsPoint(coordinates[2], coordinates[3])]
# do transformation for each point
transformedCoordinatesAsPoints = [xform.transform(point) for point in coordinatesAsPoints]
# transform the QgsPoint objects back to a list of coordinates
transformedCoordinates = [transformedCoordinatesAsPoints[0].x(), transformedCoordinatesAsPoints[0].y(), transformedCoordinatesAsPoints[1].x(), transformedCoordinatesAsPoints[1].y()]
return transformedCoordinates
Call it by providing your list of coordinates, the origin CRS and the desired destination CRS, e.g.:
transformedCoordinates = transformCoordinates(coordinates, 4326, 3857)
Now create an empty file called __init__.py
next to your script (this is necessary for importing functions from other scripts within the same folder) and another one, looking like this (this is the modified basemap2Image script):
#!/usr/bin/env python
# Nov 30, 2012
# Angel Joyce Torres Ramirez
# joys.tower@gmail.com
# I am not responsible for any use you give to this program, I did self-study and study purposes
# License:
# BaseMap2Image is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
import sys
import signal
from qgis.core import *
from qgis.utils import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *
from functools import partial
def main(htmlDirectory, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8):
print arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8
def onLoadFinished(result):
global repaintEnd
global xMin
global yMin
global xMax
global yMax
global width
global height
global fileOut
global fileFormat
if not result:
print "Request failed"
web.close()
action = "map.zoomToExtent(new OpenLayers.Bounds("+xMin+", "+yMin+", "+xMax+", "+yMax+"), true);"
web.page().mainFrame().evaluateJavaScript(action)
repaintEnd = None
pauseReq()
img = QImage(web.page().mainFrame().contentsSize(), QImage.Format_ARGB32_Premultiplied)
imgPainter = QPainter(img)
web.page().mainFrame().render(imgPainter)
imgPainter.end()
img = img.scaled(width, height, Qt.KeepAspectRatio, Qt.SmoothTransformation )
img.save( fileOut, fileFormat)
print 'saving basemap'
web.close()
def pauseReq():
global repaintEnd
timerMax.start()
while not repaintEnd:
qApp.processEvents()
timerMax.stop()
def endTimer():
global repaintEnd
repaintEnd = True
def resolutions():
if olResolutions == None:
resVariant = web.page().mainFrame().evaluateJavaScript("map.layers[0].resolutions")
olResolutions = []
for res in resVariant.toList():
olResolutions.append(res.toDouble()[0])
return olResolutions
global layer
layer = arg1
global xMin
xMin = arg2
global yMin
yMin = arg3
global xMax
xMax = arg4
global yMax
yMax = arg5
global fileOut
fileOut = arg6
global fileFormat
fileFormat="png"
if fileOut.find(".") != -1:
fileFormat = fileOut.split('.')[1]
if fileFormat == "jpg":
fileFormat = "jpeg"
timeWait = 2000
global width
width = float(arg7)
global height
height= float(arg8)
web = QWebView()
timerMax = QTimer()
global repaintEnd
repaintEnd = None
olResolutions = None
timerMax.setSingleShot(True)
timerMax.setInterval(int(timeWait))
QObject.connect(timerMax, SIGNAL("timeout()"), endTimer)
web.setFixedSize(width,height)
pathUrl = "file:///%s/%s.html" % (htmlDirectory, layer)
web.connect(web, SIGNAL("loadFinished(bool)"), onLoadFinished)
web.load(QUrl(pathUrl))
web.show()
To call the main function of this script, you have to pass the following arguments:
htmlDirectory: path to the html
directory that was part of the github package (clone the repository or download as zip, then copy the html directory to a convenient location)
mapProvider: choose one from the html
directory to which a corresponding .html file exists, e.g. 'google_streets' or 'osm'
xMin: coordinates of layer extent in EPSG:3857
yMin: coordinates of layer extent in EPSG:3857
xMax: coordinates of layer extent in EPSG:3857
yMax: coordinates of layer extent in EPSG:3857
filename: path and filename of the output image file
width: width of the output image file
height: height of the output image file
Make sure to pass all arguments as strings since the script was originally developed to be used from the command line where no other types exist.
An examplary call could look something like this:
basemap2Image.main(r'c:\PyQGIS\html', 'osm', str(transformedCoordinates[0]), str(transformedCoordinates[1]), str(transformedCoordinates[2]), str(transformedCoordinates[3]), r'c:\PyQGIS\basemap.png', '1600', '1200')
Last but not least, in order to run other Python scripts from a running Python console script, you have to tell the console where to look for it. The easiest and most permanent way for me to do this was searching for the file console.py
in my QGIS folder and add the line sys.path.append('/path/to/basemap2Image script')
under the import section (restart your QGIS to make it aware of the change).
A lot of work for such a simple feature... however, when running this as described above, a window should pop up, showing you a worldmap of your desired Openlayers provider, then zooming to the given extent, save the content to the given filename and then close the window again. This image file could then be used as a basemap or whatever else in a print composition.
Best Answer
I have noticed that the open layers instance of google earth is very sketchy from time to time. You can try working with Bing Maps Aerial imagery, this works a bit more reliable on my side.
This is not an answer to your question, but it might be something worth to look into.
Goog luck!