Python Cartographic Library

Version: 0.7
Author: Sean Gillies
Revision: 1.3
Date: 2004-12-11

Introduction

Python is rapidly becoming the language of choice for GIS programming. At the fore of this movement have been AVPython, OpenEV, and Python bindings for GDAL and OGR. On their heels, ESRI now distributes Python with Arc 9 and other commercial vendors are following suit.

I have been dabbling in Python and GIS since 2000. During all this time I have primarily used MapServer

http://mapserver.gis.umn.edu

for making maps. Since 2003 I have been the lead developer on the Python, Perl, and Java bindings for MapServer known as "mapscript". In that time I have added a ton of new features, eliminated leaks, and made mapscript safe for threaded use. For me, however, mapscript remains cumbersome and awkward to use with Python, and will always be so. I've been itching for something better for more than a year.

I've resigned myself to loving mapscript for what it is and will be scratching my itch by moving on to a new and more Python-friendly module for cartographic programming. The objectives of this Python cartographic library are:

Objectives

Mapping capabilities

PCL will match the features of MapServer by using that software as one of its possible mapping engines. It is the only engine available now, but could be joined by any number of others.

Explicit programming

Usage of MapServer's mapscript is very much like map file scripting: set the values of many interdependent parameters and hit the "go" button. Now, I'm not against run-time app configuration in the style of MapServer's map file, but I'd like a more explicit library for programming with an API that is fully separated from the run-time configuration.

Well known objects

The PCL classes will be modeled after the Open Geospatial Consortium's SLD specification and the GeoAPI 1.0

http://geoapi.sourceforge.net/

These have an advantage over the MapServer classes in that they have been through a fair amount of refactoring, whereas the MapServer classes tend to be intertwined and poorly normalized. The disadvantage of SLD and the GeoAPI is that they are very verbose. The Python Cartographic Library will simplify wherever it is possible for maximum usability. We dispense with all the getters and setters of the GeoAPI LineSymbolizer, for example, and use a more natural Python idiom.

The major advantage of using these domain objects is that service applications developed using PCL will find it very easy to describe their capabilities to consumers.

Design

I'm trying to walk a fine line between good and excessive design. PCL will rely on built-in types as much as possible and minimize the number of classes. Strokes, Fills, and Fonts, for example, are implemented as dictionaries. I'm also not going overboard on the UML. Currently there are two class diagrams for PCL focusing on the data and mapping modules. I'm using OmniGraffle on OS X to produce these.

See the design index (design/index.txt) for details

API Documentation

The Python code is well documented and javadoc-like API documentation is prepared using epydoc.

HOWTO Documentation

The brand new querying capabilities are discussed in the query howto.

Applications

Cartographic Objects for Zope

ZCO is the first application using the cartographic library.

Usage

Presently, ZCO or the PCL unit tests, particularly testrenderer.py, are the best examples of PCL usage.

Here is an example from testrenderer, rendering from a raster layer with no specified style:

from cartography import data, mapping, spatial

# path to MapServer 4.4 testing GeoTIFF file

path = os.path.join(TESTPATH, 'raster.tif')

# create a featurestore, pop into a layer, and make a renderer
# to render the layer into a map image

store = data.DiskFeatureStore(path)
srs = spatial.SpatialReference(epsg=4326)
layer = mapping.Layer(store, 'raster', srs)
mapper = mapping.MapRenderer('MAPSERVER')

# Define the spatial view and dimensions for output image
# The view is a box with upper left corner at -2.0 E 54.0 N
# and lower right corner at 2.0 E 50.0 N, image dimesion
# will be 200 pixels wide by 200 pixels high

view = mapping.View(srs, ul=spatial.Point(-2.0, 54.0),
                         lr=spatial.Point(2.0, 50.0))
size = [200, 200]

# Render the layer to a JPEG map image

image = mapper.render([(layer, None)], view, 'image/jpeg', size)

# Save to a file on disk

f = open('testRasterLayerMap.jpg', 'wb')
f.write(image.getdata())
f.close()

The output:

testRasterLayerMap.jpg

Future Development

If the Python Cartographic Module interests you, please contact me by email <sgillies at frii dot com>. I am always open to suggestions. If you think that you'd like to contribute to development, we can start to discuss the details of your contribution.

Does this mean I am ending my contributions to and support for MapServer? Absolutely not. My work on MapServer continues.