Monday, November 3, 2008

GIS with Haskell 1

It's time to bite the bullet and do some GIS Haskelling.

My first project is to develop a simple map server in Haskell. Here are the ingredients:
  • PostgresSQL + PostGIS
  • Some data to put into the database. For this I sourced some Australian suburb boundaries.
  • A library for manipulation GIS geometry, GEOS. In particular this provides functions to parse WKT strings from the database into geometry structures.
  • Haskell CGI package
  • Haskell bindings to the GEOS library.
  • Extension to HaXML adding SVG combinators.
The definition of the map is specified using a set of combinators resulting in a DSL looking a lot like the MapFile format of MapServer.

The following gives a map showing the suburbs centred on the city of Melbourne.
ex0 = map (connection "host=localhost user=postgres password=postgres dbname=australia"
`u` size 700 700
`u` extents 144.897079467773 (-37.8575096130371) 0.16821284479996734 0.1410504416999956
`u` layer ( table "suburbs"
`u` geometry "the_geom"
`u` klass ( style ( outlinecolour 255 0 0 1
`u` colour 100 255 100 1))
))

The resulting SVG file when viewed looks like:
The components that make up the map definition:
  • connection - supplies the database connection parameters,
  • size - is the size of the SVG output to be generated.
  • extents - is the extents of the map in world coordinates.
  • layer - this defines a layer. The table property defines the database table to use, the geometry property defines the column name to use. The klass parameter defines the style to use for drawing the geometry.
Layers are the key to this. Basic layers associate a geometry from the database with a style to be used when drawing the geometry.

A slightly more complex example with two layers is the following:
ex1 = map (connection "host=localhost user=postgres password=postgres dbname=australia"
`u` size 700 700
`u` extents 144.897079467773 (-37.8575096130371) 0.16821284479996734 0.1410504416999956
`u` layer ( table "suburbs"
`u` geometry "the_geom"
`u` labelitem "name_2006"
`u` klass ( style ( outlinecolour 255 0 0 1
`u` colour 100 255 100 1))
`u` label (colour 255 255 0 1))
`u` layer (table "suburbs"
`u` geometry "geomunion(the_geom)"
`u` klass ( style ( outlinecolour 0 0 0 1 `u` width 4)))
)

This is the same as before but with a new layer that provides a thick border around the edge of all the suburb boundaries:

Next steps are to source some population data, to colour code the suburbs depending on population and to include a legend.

About Me

Melbourne, Australia
I work for GE in Melbourne Australia. All views do not necessarily represent GE.