Michael Minn (http://michaelminn.com)
September 1, 2009
Describes use of MMQGIS, a set of Python vector map layer plugins for Quantum GIS
MMQGIS is a set of Python plugins for manipulating vector map layers in Quantum GIS (QGIS), an open source geographic information system. While not offering a complete or profound set of new capabilities, MMQGIS does offer some useful features missing from native QGIS or common plugin sets available as of this writing.
This document also includes a very basic introduction to writing plugins for QGIS using Python.
References are occasionally made to fTools, a set of advanced spatial analysis plugins for QGIS that you will likely find useful if you use QGIS for anything other than the most trivial mapping applications. The package is free and small and installation is highly recommended.
MMQGIS is free software and is offered without guarantee or warranty. You can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License (GPL v2) as published by the Free Software Foundation (www.gnu.org). Bug reports or suggestions are welcome at the e-mail address above, but I cannot promise a prompt or mutually satisfactory resolution.
Install QGIS and the associated packages for using Python plugins. On Fedora distros, those packages are:
Python Qt PyQt PyQt-devel PyQt4-devel QGIS QGIS-python
On Debian distros, the packages are:
qgis python-qgis python-qgis-common pyqt-tools pyqt4-dev-tools
Download the current version of MMQGIS HERE.
Unpack the zip file and move the mmqgis directory of files to the mmqgis plugin directory:
unzip mmqgis.zip mv mmqgis ~/.qgis/python/plugins
Optionally, you may install MMQGIS in the shared system plugin directory if want to share the installation with other users on a multiuser system or simply do not want extra files clogging your /home directory
Enable MMQGIS by starting qgis and using the manage plugins utility (Plugins -> Manage Plugins). The utility pops up a list box of all available plugins and you should select the checkbox beside MMQGIS. After clicking OK, an MMQGIS option should be available on the PLUGINS dropdownn menu.
Python Library Problem: On some distros (as of this writing), the QGIS python library (qgis-python) is not installed in a way that makes it accessible to QGIS, so the plugins might not be visible from the Plugins dropdown. This can be fixed by setting up a symbolic link from the expected library name to the version of the library that is installed. (reference):
sudo ln -s /usr/lib/libqgispython.so.1.0 /usr/lib/libqgispython.so
v2009.08.28 - Initial public release
v2009.09.01 - Added merge layers feature
![]() |
![]() |
QGIS lacks a native feature for displaying a lat/long grid, so the MMQGIS grid feature creates a line shapefile containing grid lines. The grid location is specified with an X/Y center origin, the width/height of the area to cover with gridlines, and the horizontal/vertical spacing of the gridlines.
The defaults when the dialog is brought up are set to cover the full extent of the map layer that is active when the plugin is invoked. The grid is saved to a shapefile specified in the "Output Shapefile" form field and a checkbox is provided to specify whether to add the grid as a new layer to the existing map.
fTools also has a vector grid feature (Tools -> Research Tools -> Vector grid), although the fTools gridline placement is specified based on the outer edges (min/max x/y) rather than from a central point. Specifying from a central point may be useful when drawing a grid relative to some specific location.
![]() |
![]() |
QGis does not permit individual formatting or placement of dynamically generated feature lables. It is possible to add attribute columns to the feature for setting various "data defined" formatting and positioning parameters, although these can be a bit cumbersome to use if you simply want to drag a few lables to new positions or delete some existing labels.
The create label layer feature provides a way to work around this deficiency by creating a point layer with unique attribute values from an existing vector layer. Those points can then be labeled and moved at will.
The QGis labeling mechanism also does not have a feature for conveniently eliminating duplicate labels when multiple features are used to represent a single geographic entity (such as multiple segments of a single street). The create label layer feature overcomes this problem by creating points only for unique label names. The attributes of each point are the attributes for the first feature encountered in the source table, although the placement of the point is determined by a coordinate average of the centers of all features which have that label.
The create label layer feature has a single dialog box that permits specification of the source vector layer, the attribute that should be used for determining unique label text, and a box for the output shapefile. The label layer will be automatically added to the map, but the user will need to adjust the properties of the layer to set the attribute that is used for labeling. Additional points can be added to support multiline labels.
If some labels need to be individually tweaked for characteristics other than placement (such as formatting, rotation, font size, bolding, etc.), attribute columns will need to be added to the point layer that can be used to specify data defined label characteristics.
Note that if the source data does not have a problem with numerous redundant labels, a better approach than using this tool may be to simply add additional attribute columns to the source data for data defined positioning, formatting, etc. Use of this tool breaks the dynamic linkage between data and labels, so that labels will have to be manually adjusted to reflect changes to the underlying source data.
It is possible to perform a similar task using the fTools polygon centroids tool. However, that tool does not work with line or point layers and does not deal with the problem of redundant labels on multi-feature entites.
![]() |
![]() |
The gridify feature permits simplification of points, lines and polygons in a shapefile by aligning all vertices to a specified grid and then removing redundant points. This makes it possible to significantly improve display refresh time when working with shapefiles that are more detailed than is necessary for the final map. However, the gridification process can result in some odd artifacts when viewed at higher resolutions, particularly in dealing with coves and other appendages along the edges of larger polygons.
The alignment grid is specified with horizontal and vertical spacing. Defaults are based on 0.5% of the extent of the target layer. The gridified shapes are saved to a shapefile specified in the "Output Shapefile" form field and a checkbox is provided to specify whether to add the shapes as a new layer to the existing map.
The export attributes feature saves attributes from a map layer to a .csv file, which can then be viewed or edited. This can be helpful when you have some use for the attribute data without the asssociated geographic data. When dealing with data sets that have large numbers of rows or columns, viewing or searching exported data can be simpler or faster than the QGIS open attribute table, which can be unacceptably slow with large data sets.
A multiple-selection list box on the dialog permits selection of the attribute columns to export to a .csv file specified at the bottom of the form.
The join attributes feature permits import of attributes from a .csv file based on a join using a "key" field that is present both in the .csv file and in the attribute table of the map layer to which the data is being joined. The key is specified by selection boxes for CSV File Field and Join Layer Attribute and do not have to have the same name. The join is similar to an SQL join and if there are multiple occurances of a key in the CSV or map layer, there will be multiple combinations of the data in the output shapefile.
A box is also provided to specify the file where unmatched records from the .csv file are saved for further analysis.
The merge layers feature merges features from multiple layers into a single shapefile and adds the merged shapefile to the project. One or more layers are selected from the "Select Source Layers" dialog list box and an output shapefile name is specified in the "Output Shapefile" dialog field.
Merged layers must all be the same geometry type (point, polygon, etc.). If the source layers have different attribute fields (distinguished by name and type), the merged file will contain a set of all different fields from the source layers with NULL values inserted when a source layer does not have a specific output field.
Shapefile attributes are stored in DBase format files (.dbf), which can be veiwed and edited in external programs like OpenOffice. However, the ordering of the data in the .dbf file may not be optimal, but if the .dbf data is reordered without reordering the associated shapes, the data will be associated with the wrong shapes, making it useless.
The sort feature permits sorting of attribute data and associated shapefile data to maintain alignment. Sorting is based on a single attribute column and the result of the sort is saved to a shapefile specified in the Output Shapefile box. A checkbox is also available for specifying whether the sorted shapefile should be added to the map.
![]() |
![]() |
The street address geocoding feature requires a layer with street centerline features and attributes indicating the range of addresses associated with each feature. For each feature there is a from x/y and to x/y (in map coordinates), a range of addresses on the left side, and a range of addresses on the right side.
Shapefiles created as ESRI "address locators" can be used with this geocoder, but newer version file geodatabases and older Access geodatabases cannot be used because these proprietary formats are not supported by QGIS. MapInfo tables are supported and can be used if these are provided as an alternative. An example of a street centerline shapefile is the New York City Department of City Planning's DCPLION.
Addresses (along with other attributes) are read in from a .csv file. Addresses should be in a single attribute field (number and street together) that can selected once an input CSV file is selected. The attributes columns from the street centerline layer can also be selected, although when a layer is selected, the dialog will attempt to find columns with appropriate names. The "Building Setback" indicates how far the geocoded points should be set away from the street centerline (i.e. how far buildings are from the middle of the street, in map units).
To improve searching, street names are internally modified with common abbreviations such as "st" for "street" and "w" for "west". This add fuzziness to the search process that may result in unexpected results. This address handling may be augmented or refined in future releases.
Output is a new point shapefile and a .csv file listing which addresses were not matched.
![]() |
![]() |
A Voronoi diagram is a collection of polygons, each surrounding individual points and representing the area around each point that is closer to that point than any other. The shapes are named after Russian mathematician Georgy Fedoseevich Voronoi, who published a formal definition in 1908, but the concept of this type of polygon extends back to Descartes in the 17th century. Similar polygons were famously used by physician John Snow to trace the source of a London cholera epidemic in 1854 and Voronoi diagrams remain useful in GIS for analyzing areas of influence associated with individual points within a collection.
The Voronoi diagram feature requires a point layer and outputs a polygon shapefile with the option to add it to the map. The boundary of the Voronoi diagram is the min/max extent of the points in the source layer.
The algorithm used to calculate the edges and nodes starts with tangents at the midpoints of lines between each point. The closest tangent is assumed to be a border and intersections to the border are calculated to circle each border until back to the beginning. The algorithm is computationally intensive, but still seems to run fairly quickly on a reasonably small set of points.
Following are two simple of examples to demonstrate how to write a Python plugin for QGIS. Serious programming of such plugins requires knowledge of the Python scripting language, the Qt user interface framework API and the QGIS programming API. However, developing plugins is a nice (albeit time-consuming) way to learn to use all three.
A plugin consists of at least two files: an entry point module named __init__.py and a plugin class file.
The following is an example __init__.py file simplified from the plugin example given in the QGIS users manual. It contains six unclassed methods that give QGIS information about the plugin and a way to access the plugin class:
# -*- coding: utf-8 -*- from demoplug import demoplug def name(): return "Demo QGIS Plugin" def description(): return "A simple demo plugin to load shapefiles" def version(): return "2009.08.04" def qgisMinimumVersion(): return "1.0" def authorName(): return "Michael Minn" def classFactory(iface): return demoplug(iface)
The first five methods are for identification purposes. The "classFactory" method calls the constructor for the plugin class, in this case called "demoplug" and imported (at the top of the file) from the demoplug.py file. This file is as follows:
# -*- coding: utf-8 -*- # Import the PyQt and QGIS libraries from PyQt4.QtCore import * from PyQt4.QtGui import * from qgis.core import * import os.path class demoplug: def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface def initGui(self): # Create action to start plugin config and connect to the run method icon = QIcon(os.path.dirname(__file__) + "/demoplug.png") self.action = QAction(icon, "Add Vector Layer", self.iface.mainWindow()) QObject.connect(self.action, SIGNAL("activated()"), self.run) self.iface.addPluginToMenu("&demoplug", self.action) print 'Loaded demoplug' def unload(self): self.iface.removePluginMenu("&demoplug", self.action) print 'Unloaded demoplug' def run(self): fileName = QFileDialog.getOpenFileName(None, \ QString.fromLocal8Bit("Select a file:"),"", "*.shp *.gml") if fileName.isNull(): QMessageBox.information(None, "Cancel", "File selection canceled") else: self.iface.addVectorLayer(fileName, "myLayer", "ogr")
The initGUI() method is called to add a Plugin menu entry and unload() is called to remove it from the menu. The run() method (which is an action called when the plugin menu item is selected) prompts the user for a shapefile (via the standard Qt open filename dialog) and then adds it as a vector laayer to the current project.
Create these two files and move them to a subdirectory that you create in the QGIS plugin directory:
mkdir ~/.qgis/python/plugins/demoplug mv __init__.py demoplug.py ~/.qgis/python/plugins/demoplug
If you wish, you can also create a 24x24 pixel demoplug.png file with an icon you want to display in the menu to represent the plugin.
After starting QGIS, under the "Plugins" dropdown should be an entry to "Manage Plugins...". Scroll through the list to find demoplug and click the checkbox to enable it. This should place it in the menu so it can be run.
Most plugins use a custom dialog box to prompt the user for information needed to perform an operation.
Dialog boxes can be created with direct Qt API calls, although it is easier to use the Qt designer application to lay out the dialog and use the pyuic4 compiler to create the class that can be used by a Python plugin.
For this example, the output of the pyuic4 compiler saved in a third file, mmqgisqtwidgets.py:
# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'demoplugqtwidgets.ui' # # Created: Wed Aug 5 09:23:05 2009 # by: PyQt4 UI code generator 4.4.4 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui class Ui_demoplugqtwidgets(object): def setupUi(self, demoplugqtwidgets): demoplugqtwidgets.setObjectName("demoplugqtwidgets") demoplugqtwidgets.resize(384, 105) self.buttonBox = QtGui.QDialogButtonBox(demoplugqtwidgets) self.buttonBox.setGeometry(QtCore.QRect(110, 60, 160, 26)) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") self.layerName = QtGui.QLineEdit(demoplugqtwidgets) self.layerName.setGeometry(QtCore.QRect(40, 20, 301, 22)) self.layerName.setObjectName("layerName") self.retranslateUi(demoplugqtwidgets) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), demoplugqtwidgets.accept) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), demoplugqtwidgets.reject) QtCore.QMetaObject.connectSlotsByName(demoplugqtwidgets) def retranslateUi(self, demoplugqtwidgets): demoplugqtwidgets.setWindowTitle(QtGui.QApplication.translate("demoplugqtwidgets", \ "Dialog", None, QtGui.QApplication.UnicodeUTF8))
A demoplugqt.py class file is created to use this dialog:
# -*- coding: utf-8 -*- from PyQt4.QtCore import * from PyQt4.QtGui import * from qgis.core import * from demoplugqtwidgets import Ui_demoplugqtwidgets import os.path # --------------------------------------------- class demoplugqtdialog(QDialog, Ui_demoplugqtwidgets): def __init__(self, iface): QDialog.__init__(self) self.iface = iface self.setupUi(self) # --------------------------------------------- class demoplugqt: def __init__(self, iface): self.iface = iface def initGui(self): icon = QIcon(os.path.dirname(__file__) + "/demoplugqt.png") self.action = QAction(icon, "Rename Layer", self.iface.mainWindow()) QObject.connect(self.action, SIGNAL("activated()"), self.run) self.iface.addPluginToMenu("&demoplug", self.action) print 'Loaded demoplugqt' def unload(self): self.iface.removePluginMenu("&demoplug", self.action) print 'Unloaded demoplugqt' def run(self): print "Running dialog" layer = self.iface.activeLayer() if layer == None: QMessageBox.information(self.iface.mainWindow(), "Rename Layer", \ "Project has no active layers") return dialog = demoplugqtdialog(self.iface) dialog.exec_() newname = str(dialog.layerName.displayText()).strip() if newname > "": layer.setLayerName(newname)
A __init__.py file is created with info on the new plugin:
# -*- coding: utf-8 -*- from demoplugqt import demoplugqt def name(): return "Demo QGIS Plugin (with Qt dialog)" def description(): return "A simple demo plugin to rename layers" def version(): return "2009.08.05" def qgisMinimumVersion(): return "1.0" def authorName(): return "Michael Minn" def classFactory(iface): return demoplugqt(iface)
Finally, create a subdirectory in the plugin directory and move these three files into it.
mkdir ~/.qgis/python/plugins/demoplugqt mv __init__.py demoplugqt.py demoplugqtwidgets.py ~/.qgis/python/plugins/demoplugqt