|
In this article, Christian Jauvin describes the Dracones Framework for MapServer, and the DraconesPH application that is based on this framework.
MapServer is a widely used open source GIS platform, especially useful in web environments. It is written in C, and has two primary modes of usage: through a CGI script, or in a more programmatic manner, via MapScript, a set bindings for many programming languages. Both methods are based on MapFiles, which contain specifications and parameters for a map (which can be based on a shapefile for instance), written in a declarative mini-language. The basic working of MapServer can be abstracted in an easy way: you give it a MapFile as its input, and it produces in return a (static) image of the resulting map (usually via a web server), which a client application is then free to manipulate in any useful way.
Since it's not MapServer's goal to provide all the particular services that may compose an application, a small ecosystem of frameworks and tools has emerged to meet many needs. In particular, there are frameworks covering many aspects of the web-mapping spectrum: from mapping widgets to ease user interaction, to more abstract building blocks that help in the creation of complex applications, etc. This is where Dracones is trying to position itself. Dracones was created with a web-mapping and public health surveillance tool creation grant that was obtained from GeoConnections, a governmental agency promoting and subsidizing the use of geospatial software in Canada. It was developed for Montreal's Direction de sante publique (Public Health Department), where it is still in use and being actively developed by Dr. David Buckeridge, a McGill University epidemiologist and computer scientist, and myself, as the only programmer and designer of the software. The software has had a steady evolution over the years: it first began as an experimental mapping and surveillance web application, meshing techniques and code from many sources and ideas, before it gradually began to take form as two distinct entities. First, a set of reusable components (written in JavaScript and Python) that would compose eventually a new framework for MapServer. Secondly, the separate application DraconesPH, which serves the purpose of demonstrating what is possible with these components. Here is a screenshot of DraconesPH in action: 
It may be that this reverse order of what could be seen perhaps as the "normal" path for software creation, that yields Dracones its particular flavor: first an application was iteratively created and tested in a "real" environment, and then only after a certain time, the idea of abstracting certain useful and carefully designed services and components into a more general framework naturally evolved from it. There are other interesting aspects, more specific to the DraconesPH companion application that can be mentioned: for instance it integrates with SaTScan, a space-time scan statistics application widely used in many fields where surveillance is involved, for which it acts as an interactive visualization module. DraconesPH also features a modern user interface, written in 100% JavaScript (with the Ext JS library), which is completely decoupled from the core mapping widget that is provided with the framework. How it worksDracones' working can be described from two points of view. Let's begin with the first, the client. In essence, the client is a JQuery-based, 100% JavaScript map widget and with an API that is similar to Google Maps (although not in elegance I fear, but rather interface-wise). Setting up a Dracones map widget in any web page is very easy, as it can be anchored to any existing element. The map widget constructor will specify certain core attributes, for example which map to display, and what layers to turn on and act upon. Out of the box, it will provide basic services like map navigation (panning and zooming), point/area selection and mouse hover items. To implement seamless Ajax-based panning, I experimented with a few ideas. I finally settled on a non-tiled one based on a single map that is refreshed when needed using a double-buffered image swapping technique.
Dracones is designed to act as a superficial layer over MapServer, trying to espouse and complement its design and features, rather than replacing or hiding them. If you define a simple shapefile-based mapfile like this one for instance: 1
2
3
4
5
6
7
8
9
10
|
MAP LAYER NAME "montreal" TYPE line DATA "montreal.shp" CLASSITEM "region_id" CLASS STYLE COLOR 232 232 232 OUTLINECOLOR 105 105 105 END END CLASS STYLE COLOR 50 150 255 OUTLINECOLOR 105 105 105 END END END END
|
The presence of a CLASSITEM informs Dracones that the corresponding layer must be selectable, while the second CLASS attribute will
specify the visual appearance of the selected items. This setting will automatically work, and it will be very easy to retrieve the selected items server-side, via the "selected" Python list attribute of the corresponding DLayer instance (a DLayer, as the name suggests, is a thin Dracones wrapper around a MapServer layer). Some other client services are more application-centric in nature, and include: an easy to use undo/redo mechanism, and a map image export function. Both will be typically bound to simple controls, typically buttons, external to the map widget. The server part of Dracones is written in 100% Python (there's a Python flavor for MapScript of course) and is composed of two main parts. The first is a set of classes wrapping some core MapServer concepts, like maps and layers, and adding some services, such as persistence through a session-based mechanism. The second part is the interface through which the communication with the client is performed. All the services provided by Dracones are implemented server-side using the same "skeleton", that can be best described with three simple Python calls: 1
2
3
4
5
6
7
8
9
10
11
12
|
def service(req): params, sess, dmap = beginDracones(req)
# Whatever map transformation you want to implement goes here..
json_out = endDracones(dmap)
# Whatever info to be returned to the client can be set in #the json_out variable..
return exitDracones(json_out)
|
A customized query is actually defined using this exact mechanism. Suppose for instance that I would like to select all the points (belonging to a given point layer) that are lying inside the set of currently selected regions (belonging to a regional layer):
1
2
3
4
5
6
7
8
9
10
11
|
def findPoints(req):
params, sess, dmap = beginDracones(req)
selected_regs = dmap.getDLayer('regions').selected dmap.getDLayer('points').queryByAttributes('region_id', selected_regs)
json_out = endDracones(dmap)
return exitDracones(json_out)
|
To bind such a customized query to any UI control, the corresponding client-side JavaScript code is also very straightforward:
1
2
3
4
5
6
7
8
|
jQuery(<control_id>).bind('click', function() { map.customRequest({ url: '/<script_name>/<function_name>', data: {/* any additional data */}, callback: function(json_in) { /* optional callback function */ } }); });
|
Similarly, new types of "action" can be defined, to override the "point" and "box" mouse behavior (triggered by using the mouse and the CTRL button). It is for instance very easy to replace the point selection mechanism by one that draws a symbol and attach a tooltip with customized text to it instead. Both these custom scenarios are explained in greater details in the tutorials found on the Dracones site. Here are some examples of mouse hover and click effects in action:
SummaryDracones should help in easing the introduction to MapServer programming, for which the learning phase can look daunting a priori. The organization of some common application-centric usage patterns it provides may be a modest but useful contribution in that direction, or at least such are my hopes.
Christian Jauvin is the main programmer/designer of the Dracones project. which was developed as a part of the McGill Surveillance Lab. Christian's MSc involved the statistical modeling of natural language, using
an ontology of word senses and concepts. Since then, he has developed a strong interest for artificial intelligence, open
source software and culture, GIS systems, high-level programming
languages and state of the art web programming techniques.
 |