Creating a Standalone GIS Application

In this example we are going to begin to build a standalone GIS application using Python and the QGIS libraries. We'll start simple by creating an application to load and display a shapefile. In later posts we'll enhance the application to provide some map tools and other features.


To build the application we need the following tools installed:
  • QGIS 1.0
  • PyQt4
  • Qt 4.3 or higher and the development tools (Designer)
  • Python 2.3 or better

Testing the Environment

Before getting started we need to test our development environment to make sure everything is in place. First we need to make sure Python can find the QGIS bindings.
From a terminal or command shell on Linux or OS X:
export PYTHONPATH=[path to]/share/qgis/python 
From a cmd shell on Windows:
set PYTHONPATH=[path to QGIS]\python 
If you are using the OsGeo4W install of QGIS, all you need to do is start the OsGeo4w shell. No setting of the PYTHONPATH is required.
Start Python from a shell and test the import of the needed components:
mizpah:~ gsherman$ python
Python 2.5.1 (r251:54869, Apr 18 2007, 22:08:04)
[GCC 4.0.1 (Apple Computer, Inc. build 5367)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import PyQt4.QtCore
>>> import PyQt4.QtGui
>>> import qgis.core
>>> import qgis.gui
If there are any errors you may have to set the path to the QGIS libraries using the LD_LIBRARY_PATH on OS X and Linux.
Now we are ready to get started.

Creating the Main Window

First create a directory for developing the application and change to it.
Now run Qt Designer. The New Form dialog should appear. If it doesn't, choose New Form... from the File menu.
We want to create a Main Window so choose it from the templates/forms list. Click "Create" to create the window and then resize it as you see fit.
Now we need to add a frame to hold our map control. Find the Frame widget in the list of widgets (under Containers) and drag it to the main window.
The last thing is to tell Designer that we want a grid layout. Click outside the frame you just added to select the main window area. Now click on the "Lay Out in a Grid" tool. in the toolbar. When you do, the frame will expand to fill your entire main window.
We are done in Designer. Save the form as shapeviewer_gui.ui in the project directory you created earlier.
In order to use the form we just created, we need to compile it using the user interface compiler. Open a shell and change to your project directory. Then compile the form:
pyuic4 -o shapeviewer_gui.py shapeviewer_gui.ui 
Now we have a Python script (shapeviewer_gui.py) that implements our form.

Creating the ShapeViewer Class

Now we need to create the class that will do the work of loading and displaying a shapefile.
We begin with some imports:

1 from PyQt4.QtCore import *
2 from PyQt4.QtGui import *
3 from qgis.core import *
4 from qgis.gui import *
5 import sys
6 import os

We need the PyQt and QGIS imports, as well as the Python sys and os modules.
In order to use our form, we need to import it next:

7 # Import our GUI
8 from shapeviewer_gui import Ui_MainWindow

To make things simple in our script, we are going to use an environment variable to locate the QGIS install:

10 # Environment variable QGISHOME must be set to the install directory
11 # before running the application
12 qgis_prefix = os.getenv("QGISHOME")

We'll set the environment variable later, right before we run the application.
Now the class definition---we'll call it ShapeViewer. ShapeViewer must inherit from two classes: QMainWindow and Ui_MainWindow:

14 class ShapeViewer(QMainWindow, Ui_MainWindow):
16 def __init__(self):
17 QMainWindow.__init__(self)
19 # Required by Qt4 to initialize the UI
20 self.setupUi(self)
22 # Set the title for the app
23 self.setWindowTitle("ShapeViewer")
25 # Create the map canvas
26 self.canvas = QgsMapCanvas()
27 self.canvas.useImageToRender(False)
28 self.canvas.show()
30 # Lay our widgets out in the main window using a
31 # vertical box layout
32 self.layout = QVBoxLayout(self.frame)
33 self.layout.addWidget(self.canvas)
35 # layout is set - open a layer
36 # Add an OGR layer to the map
37 file = QFileDialog.getOpenFileName(self,
38 "Open Shapefile", ".", "Shapefiles (*.shp)")
39 fileInfo = QFileInfo(file)
41 # Add the layer
42 layer = QgsVectorLayer(file, fileInfo.fileName(), "ogr")
44 if not layer.isValid():
45 return
47 # Change the color of the layer to gray
48 #symbols = layer.renderer().symbols()
49 #ymbol = symbols[0]
50 #ymbol.setFillColor(QColor.fromRgb(192,192,192))
52 # Add layer to the registry
53 QgsMapLayerRegistry.instance().addMapLayer(layer);
55 # Set extent to the extent of our layer
56 self.canvas.setExtent(layer.extent())
58 # Set up the map canvas layer set
59 cl = QgsMapCanvasLayer(layer)
60 layers = [cl]
61 self.canvas.setLayerSet(layers)

Let's take a look at the class definition in a bit more detail. In line 14 we declare the class and indicate that it inherits from both QMainWindow and Ui_MainWindow. Next is the __init__ function (line 16) which is called whenever you create an instance of ShapeViewer.
This is where the real work begins. The first thing we need to do is call init on the parent QMainWindow class to make sure things are initialized properly (line 17). In order to initialize our GUI, we call setupUi(self) in line 20. As you may have guessed by now, Ui_MainWindow is the class we created with Designer and pyuic4; and yes, setupUi is a function in our Ui_MainWindow class. If you like, you can change the name of the GUI class to something more appropriate when you create it in Designer by setting the objectName in the propery window.
In line 23 we set the title of the window to "ShapeViewer" using the setWindowTitle method.
Next we create the map canvas and set it to visible by calling its show() method (lines 25-28).
Eventually we'll want to add a toolbar to our application so we create a vertical box layout on our main window frame (line 32) and then add our map canvas onto the vertical box layout in line 33. That completes the setup of the layout for the application.
To add a layer to the application we are going to immediately open a file dialog from which a layer can be selected. Normally you would assign this to a menu and/or toolbar item. In lines 35-39 we open a file dialog and get the file name selected by the user. In line 39 we create a QFileInfo object from the full path of the file. We'll use this to get just the file name from the path and use it as name of our added layer in the application.
The layer is created in line 42, by passing the full path name (file), the file name (fileInfo.file), and the name of the data provider, in this case "ogr". The last thing to do is make sure the layer is valid and if it's not, we just exit the application using a return in lines 44-45. Of course a real application would handle this differently.
To get the layer ready for display, we add it to the QGIS layer registry in line 53. The registry keeps track of all loaded layers---if we don't add our layer to the registry we won't see it. We also need to set the extent of the map canvas to the extent of our layer (line 56). The last bit of housekeeping is to create the actual map canvas layer and add it to the layer set for the canvas (lines 58-61). Now we have a map canvas configured with one map canvas layer and ready for display.

Setting Up to Run

Next we need to setup the application so we can run it. In a real application, you would likely separate the following code from the class definition by putting it in a separate Python script.
Here is the application setup code:

63 def main(argv):
64 # create Qt application
65 app = QApplication(argv)
67 # Initialize qgis libraries
68 QgsApplication.setPrefixPath(qgis_prefix, True)
69 QgsApplication.initQgis()
71 # create main window
72 wnd = ShapeViewer()
73 # Move the app window to upper left
74 wnd.move(100,100)
75 wnd.show()
77 # run!
78 retval = app.exec_()
80 # exit
81 QgsApplication.exitQgis()
82 sys.exit(retval)
85 if __name__ == "__main__":
86 main(sys.argv)

Lines 63-82 setup the main method and contain the code needed initialize and run our application. In line 65 we create a QApplication (a Qt class). In lines 68 and 69 we setup the QgsApplication object by setting the path to the QGIS installation and initializing QGIS.
Next we need to establish the main window for our application. In our case this is our ShapeViewer class. Lines 71-75 create the main window and move it a bit from the upper left of the screen, then show it with wnd.show().
Lastly we enter the event loop by calling app.exec() in line 78. This starts the application and continues until the main window is closed. When closed lines 81 and 82 are executed to tidy things up and make a clean exit.
The last line (85) is a Python mechanism to allow our code to behave either as a module or as a script that can be executed. Since our source file contains the ShapeViewer class definition with methods and the main method, nothing would happen if we tried to execute it without having lines 85-86 in place. Well actually some things happen but nothing that we can see. When we run ShapeViewer.py as a script, "__name__" is set to "__main__" by the interpreter, thus causing the script to execute our main method. If you import ShapeViewer.py as a module in another script or interactively, "__name__" is set to the name of the module, i.e. "ShapeViewer".

Running the Application

On Linux or Mac OS X you need to set the QGISHOME directory to the location where QGIS is installed. On Linux this will be the directory containing the bin, lib, and share subdirectories of the QGIS install (possible locations include /usr and /usr/local):
export QGISHOME=/usr/local
On Mac OS X, a likely location (depending on where you installed QGIS) will be:
export QGISHOME=/Applications/Qgis.app/Contents/MacOS
On Windows, set it to the top level of the QGIS install:
If using the OSGeo4W install of QGIS, use:
Once QGISHOME is set, we need to set the PYTHONPATH as well. On Linux or OS X:
export PYTHONPATH=$QGISHOME/share/qgis/python
On Windows:
set PYTHONPATH=%QGISHOME%\share\qgis\python
If using the OSGeo4W install of QGIS, start an OSGeo4W shell---you don't need to set the PYTHONPATH
(To make things easier, you could set these environment variables for your operating system in a script and run the application)
To run the application after your environment is set use:
python ShapeViewer.py
When we run the application and choose the world_borders shapefile (available from this site) we get:

Tweaking the Result

The result, although impressive, is not very attractive. The black background of the map canvas coupled with the random blue chosen by QGIS for the countries is rather ugly.
We can fix the background by adding the following to our code, right after we create the map canvas (line 26):
#Set the background color to white
This sets the background color to white using the setCanvasColor method of the map canvas object. Running the application again gives us:

This is a much better result. You can also change the color of the layer but we'll leave that for another time, or for you to sort out if you wish.

Panning and Zooming

Although we didn't create any map navigation tools, with the QGIS map canvas we get that for free. To zoom the map, roll the mouse wheel in or out while holding down the space bar. To pan the map, hold down the space bar and move the mouse.

Source Code

The source code for the ShapeViewer application can be downloaded here:


This is a simple example of what you can do with Qt and the QGIS python bindings. Very sophisticated, custom applications can be created using these tools.
NOTE: The indentation of the Python code shown in this post may not be correct. Downloading the source code is the best way to examine the code.

from Gary Sherman Blogs: http://desktopgisbook.com/Creating_a_Standalone_GIS_Application_1

1 comment: