One of the area’s that QGIS is constantly improving is the ‘Processing framework’, Formerly known as the sextante framework and written in java, it is rewritten in Python by one of the original authors Victor Olaya and made part of QGIS since about QGIS 2.0.
I think it is VERY usefull and in use a lot already, but not so much people are writing about this. In this blogpost I use it as a tool to run some pyqgis code, but Processing is much much more! Read about it in docs and manuals.
Recently there were some questions in the mailing list, which I thought would be fun to solve with a Processing script (instead of writing some lines of code in the python console, or creating a plugin).
Processing has it’s own Menu-item, and when you select the Toolbox item there you will have a full blown dialog in the left docking part of QGIS as seen in the screenshot above.
Processing has it’s own chapter in the QGIS manual.
But als the QGIS Training manual has a lot of information in the Processing chapter
Just zoom to this x,y point, with this crs (coordinate reference system)
When blogging on my corporate website about Mozilla Location Services, I needed a simple way to zoom to a position on the map given a lat lon coordinate returned by Mozilla when given some wifi access point BSSID’s.
It is a hidden feature that the you can edit the ‘Coordinate’ part of the statusbar at the bottom of QGIS to center the map to a certain coordinate (see 1 in image below. Funny thing is that the ‘Extents’ input is NOT editable)…
But that is only working in the crs of the map, while I wanted to use LatLon coordinates when looking to a map in our national CRS.
To try: in the Processing Toolbox widget, open the ‘Scripts’ node, and in that the ‘Tools’ node. There double click ‘Create new script’. An empty Script editor dialog will open in which you can copy paste the following code:
[sourcecode language=”python” wraplines=”false” collapse=”false” gutter=”false”]
##[QGISNL scripts]=group
##x=string
##y=string
##epsg=number 4326
# note for above: using type string, because
# it is harder to paste a number in a number field
from qgis.core import *
from qgis.gui import *
from qgis.utils import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
crsto = iface.mapCanvas().mapRenderer().destinationCrs()
crsfrom = QgsCoordinateReferenceSystem()
crsfrom.createFromId(epsg)
crsTransform = QgsCoordinateTransform(crsfrom, crsto)
point = QgsPoint(float(x), float(y))
geom = QgsGeometry.fromPoint(point)
geom.transform(crsTransform)
# a vertexmarker
m = QgsVertexMarker(iface.mapCanvas())
m.setCenter(geom.asPoint())
m.setIconSize(8)
m.setPenWidth(4)
#m.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X
#m.setColor(QColor(0,255,0))
# zoom to with center is actually setting a point rectangle and then zoom
center = geom.asPoint()
rect = QgsRectangle(center, center)
iface.mapCanvas().setExtent(rect)
iface.mapCanvas().zoomScale(794)
iface.mapCanvas().refresh()
# some magic to let the marker disappear 5 seconds
def timer_fired():
iface.mapCanvas().scene().removeItem(m)
timer.stop()
timer = QTimer()
timer.timeout.connect(timer_fired)
timer.setSingleShot(True)
timer.start(5000)
[/sourcecode]
You will see that the code in the script editor is nicely formatted with nice colors.
When you give it a name: for example ‘ZoomToPoint.py’ it will show up in the Scripts part of the Processing Toolbox.
You can always edit it again via right mouse click on the name and choose ‘Edit script’, OR run it via double clicking on it.
About the code: the first part (with the double # signs) are used as a sort of metadata for the dialog (every script get’s a dialog for ‘free’). The ‘[QGISNL scripts]=group’-part places it in a QGISNL group, the next lines determine the input values and types of those in the default dialog. As you can see I declare the x and y as strings and the epsg-code as number with the default value 4326 (= latlon). Why a string for a coordinate? Because the inputs in the dialog for strings are better suited for copy pasting from some text.
The rest is some pyqgis code which makes the map zoom to the x-y coordinate given, show a small marker for 5 seconds and that is it.
NOTE: processing has a lot more helping functions available which are very usefull for loading data etc etc. Have a look into the other available example scripts to see what is possible.
Open a local KML file with a GroundOverlay
Then there was the question on the QGiS User mailing list about opening a local KML file containing a link to a local image ( a so called GroundOverlay ) and how to open that one in QGIS.
Classic KML files, containing Features can be opened in QGIS as a vector file, but KML nowadays is a Google-whatever-we-need-call-it-KML format, and can contain all kind of data. These groundoverlay files actuall contain nothing more then a link or name of a raster images, and the bounding box/extent in which this raster is to be placed.
[sourcecode language=”xml” wraplines=”false” collapse=”false” gutter=”false”]
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<GroundOverlay>
<name>SMA</name>
<color>b5ffffff</color>
<Icon>
<href>sma_EUCLID_regionqc_201307.dat.gif</href>
<viewBoundScale>0.75</viewBoundScale>
</Icon>
<LatLonBox>
<north>72</north>
<south>35</south>
<east>33</east>
<west>-11</west>
</LatLonBox>
</GroundOverlay></kml>
[/sourcecode]
In QGIS and the geo world, we mostly work with world files, a small text file next to a raster which tell the application where to place the image in the world, and how much ‘world there is in one pixel’.
If you understand the concept, and have enough information, it is actually pretty easy to create those world files by hand (see the user mailing list link above for some more background).
To try this all yourself, download the example kml from the mailing list question (note that this zip already contains the gfw world file, but normally you would only have kml+raster file).
Then create a new script with the following python code:
[sourcecode language=”python” wraplines=”false” collapse=”false” gutter=”false”]
##[QGISNL scripts]=group
##Input_kml=vector
##Input_raster=raster
from qgis.core import *
from PyQt4.QtCore import *
# read extent from KML, by opening it with ogr
extentLayer = QgsVectorLayer(Input_kml, "extent", "ogr")
width = extentLayer.extent().width()
height = extentLayer.extent().height()
xmin = extentLayer.extent().xMinimum()
ymax = extentLayer.extent().yMaximum()
# we do NOT want the default behaviou: asking for a crs
# we want to set it to 4326, see
# http://gis.stackexchange.com/questions/27745/how-can-i-specify-the-crs-of-a-raster-layer-in-pyqgis
s = QSettings()
oldValidation = s.value( "/Projections/defaultBehaviour", "useGlobal" )
s.setValue( "/Projections/defaultBehaviour", "useGlobal" )
# open raster, but do not show it. Only to get the pixel size
image = QgsRasterLayer(Input_raster)
img_width = image.width()
img_height = image.height()
# now write the worldfile (todo: for gif: gfw, for tiff twf etc)
wf_name = Input_raster.replace(‘.gif’, ‘.gfw’)
f = open(wf_name, ‘w’)
f.write( unicode(width/img_width)+’\n’ )
f.write( unicode(0)+’\n’ )
f.write( unicode(0)+’\n’ )
f.write( unicode(-height/img_height)+’\n’ )
f.write( unicode(xmin)+’\n’ )
f.write( unicode(ymax)+’\n’ )
f.close()
# now open the raster again, and world file will be used
fileInfo = QFileInfo(Input_raster)
base_name = fileInfo.baseName()
raster = QgsRasterLayer(Input_raster, base_name)
# kml is always 4326
raster.setCrs( QgsCoordinateReferenceSystem(4326, QgsCoordinateReferenceSystem.EpsgCrsId) )
if raster.isValid():
QgsMapLayerRegistry.instance().addMapLayer(raster)
else:
iface.messageBar().pushMessage(‘Problem’, ‘Mmm, something went wrong…’, level=QgsMessageBar.CRITICAL, duration=5)
# change back to default action of asking for crs
s.setValue( "/Projections/defaultBehaviour", oldValidation )
[/sourcecode]
Now if all goes well, after unpacking the example zip, you should be able to point to the KML (needed for the extent) and the gif (needed to find out the size of the raster) and run the script to load the kml/raster:
Let’s stop now. There is just too much to tell about processing.
I invite you to read the documenation about it and with the above examples explore it a little bit more. Also have a look into other examples, there are even Github repositories available already containing all kind of handy user scripts.
We haven’t even talked about using all the different algorithms and functions from other libraries for which Processing is actually written (SAGA, GRASS and Orfeo). Or the Visual Models you can create with it. But I keep that for somebody else to talk about. Or another time maybe…
UPDATE (from an email of Victor Olaya, the main dev of processing):
– There is a crs data typ that you can use. Instead of ##epsg=number 4326, you can do authid=crs. In the authid variable, you will have the authId of the CRS, which the user can select from the usual CRS selector. No need to enter 4326 as default, since that’s already the default.
– You can use progress.setInfo(string) to show info to the user, instead of using the messageBar. Also, if there is an error, instead of showing info in the message bar, you should raise a GeoAlgorithmExecutionException(string), and Processing will take care of handling that correctly (don’t forget that the algorithm might run in the modeler, and is important to signal that it could not complete)
– Your first algorithm doesn’t look suitable for the modeler, so maybe it would be a good idea to add the ##nomodeler tag
Leave a comment
This is amazing! Thank you so much for putting this script together. However there is one problem when I try to run it. I’m running your script (and on your sample kml) exactly and I get a CRS error. I feel like I am making a very simple mistake. Could you help? I’ve pasted the error below:
”
An error has occured while executing Python code:
Traceback (most recent call last):
File “C:/Users/amy/.qgis2/python/plugins\processing\gui\AlgorithmDialog.py”, line 148, in accept
if checkCRS and not self.alg.checkInputCRS():
File “C:/Users/amy/.qgis2/python/plugins\processing\core\GeoAlgorithm.py”, line 403, in checkInputCRS
crs = dataobjects.getObject(item).crs()
AttributeError: ‘NoneType’ object has no attribute ‘crs’
Python version:
2.7.5 (default, May 15 2013, 22:44:16) [MSC v.1500 64 bit (AMD64)]
QGIS version:
2.8.2-Wien Wien, 1b929ef
Python path: [‘C:\\Users\\amy\\.qgis2\\python\\plugins\\GeoCoding\\libs’, ‘C:/Users/amy/.qgis2/python/plugins\\processing’, ‘C:/Users/amy/.qgis2/python/plugins\\LecoS’, ‘C:/PROGRA~1/QGISWI~1/apps/qgis/./python’, u’C:/Users/amy/.qgis2/python’, u’C:/Users/amy/.qgis2/python/plugins’, ‘C:/PROGRA~1/QGISWI~1/apps/qgis/./python/plugins’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\matplotlib-1.3.1-py2.7-win-amd64.egg’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\nose-1.3.3-py2.7.egg’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\tornado-4.0.1-py2.7-win-amd64.egg’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\backports.ssl_match_hostname-3.4.0.2-py2.7.egg’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\certifi-14.05.14-py2.7.egg’, ‘C:\\PROGRA~1\\QGISWI~1\\bin\\python27.zip’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\DLLs’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\plat-win’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\lib-tk’, ‘C:\\PROGRA~1\\QGISWI~1\\bin’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\PIL’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\jinja2-2.7.2-py2.7.egg’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\markupsafe-0.23-py2.7-win-amd64.egg’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\pytz-2012j-py2.7.egg’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\win32’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\win32\\lib’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\Pythonwin’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\Shapely-1.2.18-py2.7-win-amd64.egg’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\six-1.3.0-py2.7.egg’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\wx-2.8-msw-unicode’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\xlrd-0.9.2-py2.7.egg’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\Python27\\lib\\site-packages\\xlwt-0.7.5-py2.7.egg’, u’C:/Users/amy/.qgis2//python’, ‘C:\\Users\\amy\\.qgis2\\python\\plugins\\mmqgis/forms’, ‘C:\\Users\\amy\\.qgis2\\python\\plugins\\opeNoise\\tools’, ‘C:\\PROGRA~1\\QGISWI~1\\apps\\qgis\\python\\plugins\\fTools\\tools’, ‘C:\\Users\\amy\\.qgis2\\python\\plugins\\DigitizingTools\\tools’, ‘C:\\Users\\amy\\.qgis2\\python\\plugins\\qgis_etri\\ui/..’, ‘C:\\Users\\amy\\.qgis2\\python\\plugins\\qgis_etri\\mcda/..’, ‘C:\\Users\\amy\\.qgis2\\python\\plugins\\qgis_etri\\mcda/..’]
“
Hi Amy, I cannot reproduce your error. Nor in 2.10 or 2.8.
What is your setting in Options/CRS? I have ‘CRS for new layers’ to prompt me for a crs. So when I run the script I get asked what crs I want for the gif, and then it just works, even if I cancel that dialog…
did you try other kml’s?