Mapnik - Visualizador GIS para Python y C++

PythonArco

Mapnik es una librería que permite generar y manipular mapas obteniendo los datos de diferentes fuentes (archivos .shp, PostGIS, .tif). En esta receta se muestra como utilizar mapnik en Python.

Ingredientes

  • mapnik (por ahora nos vale con el paquete debian)
  • libmapnik0.5
  • mapnik-plugins
  • mapnik-utils
  • python-mapnik
  • postgis
  • postgresql
  • ogr2ogr (opcional)

Map

Mapnik permite crear y manejar mapas a partir de diferentes fuentes de datos. Esta librería se basa en el objeto Map que contiene varios atributos que permiten personalizarlo. Los atributos mas relevantes son:

  • background - Establece el color de fondo del mapa.
  • height - Ancho del mapa.
  • width - Alto del mapa.
  • style - Define cómo se visualizan de los distintos componentes del mapa.
  • layers - Capas que contiene el mapa. Es decir cada una de las fuentes de datos que queramos que muestre el mapa.

Además esta clase permite manejar el mapa por medio de diversos métodos.

  • pan - centra el mapa en la posicion indicada (x,y).
  • pan_and_zoom - centra el mapa en la posicion indicada y hace zoom sobre ella.
  • zoom_all - realiza el zoom a todas las capas, es decir, visualiza el mapa entero.
  • zoom_to_box - realiza el zoom a todas las capas en un determinado area.
  • envelope - obtiene el area de las capas del mapa.

También existen algunas funciones que no pertenecen a ningún objeto en concreto.

  • render_to_file(Map, archivoConExtension) - Renderiza el mapa a un archivo.
  • render(Map, CairoContext) - Gracias a este método se puede integrar con GTK. No disponible via paquete debian, solo bajando el .tgz y compilando.
  • save_map(Map, archivoConExtension) - Almacena el mapa en un archivo.

from mapnik import *
 
#Creacion del Mapa
width = 790
height = 590
map = Map(width,height,'+proj=latlong +datum=WGS84')
map.background = Color('white')

Style

Para visualizar el objeto Map se le debe asociar una serie de objetos Style que permiten definir como se debe representar cada elemento del mapa.

Un Objeto Style se compone de reglas (Rules) para elementos del mapa, siendo posible definir filtros para cada una de dichas reglas. Por ejemplo podemos crear dos Rules en las que en una los puntos que cumplen una determinada condición se vean de color rojo mientras que los demás se vean de otro color. Para ello se debe asociar a la regla un objeto Filter que realize el filtrado deseado.

Elementos del mapa:

  • PolygonSymbolizer - Establece el estilo de los poligonos.
  • LineSymbolizer - Establece el estilo de las lineas.
  • PointSymbolizer- Establece el estilo de los puntos.
  • RasterSymbolizer - Permite que se visualizen las imagenes raster.

#Creacion del Estilo del Mapa
styleShp = Style()
r = Rule()
r.symbols.append(PolygonSymbolizer(Color('#f2eff9')))
r.symbols.append(LineSymbolizer(Color('rgb(50%,50%,50%)'),0.1))
r.symbols.append(RasterSymbolizer())
styleShp.rules.append(r)
 
#Filter
# Acepta <, >, <>, not, or, and
# En teoria tambien puede filtrar por expresiones regulares
# pero tras probarlo no he obtenido resultados satisfactorios
# Ej.- [id].match('^ascensor')
 
style = Style()
rVerde = Rule()
rVerde.symbols.append(PointSymbolizer('img/puntoVerde.png','png',10,10))
rVerde.filter = Filter("not [id]='escalera0_up' or not [id]='escalera1_up'")
 
rRojo = Rule()
rRojo.symbols.append(PointSymbolizer('img/puntoRojo.png','png',10,10))
rRojo.filter = Filter("[id]='escalera0_up' or [id]='escalera1_up'")
 
style.rules.append(rRojo)
style.rules.append(rVerde)
 
map.append_style('styleShp',styleShp)
map.append_style('style',style)

Layers

Hasta el momento puede obtener datos de diferentes fuentes, para ello se crean
objetos Layer con la fuente de datos necesaria (datasource) a partir de varias clases.

  • Raster - Permite crear una layer a partir de imagenes (.tif). Puede ser util la herramienta ogr2ogr para convertir imagenes normales a imagenes con formato geográfico.
  • Shapefile - Crea una layer a partir de un archivo .shp de descripción de figuras geométricas.
  • PostGIS - Crea una layer a partir de la información de una tabla de una base de datos Postgres con campos PostGIS (geometry). Es posible realizar un pequeño filtrado de la tabla pero solo se puede trabajar con la tabla en cuestión nada de hacer joins ni comparaciones con campos de otras tablas.
  • Gdal - Todavia no lo he probado Smiling

#Layers del Mapa
#---------------
#A cada layer hay que asociarle:
# Una fuente de datos
# Un Style para representar la informacion
# Finalmente hay que asociarla al mapa
 
lyrImg = Layer('raster')
lyrImg.datasource = Raster(file='img/pbaja.tif',lox='0', loy='0', hix='50', hiy='50')
lyrImg.styles.append('styleShp')
 
lyrGeo = Layer('GIS')
lyrGeo.datasource = PostGIS(host='localhost',
                            user='mapnik',
                            password='mapnik',
                            dbname='mapnik',
                            table='mapnik')
lyrGeo.styles.append('style')
 
lyrShp = Layer('shape')
lyrShp.datasource = Shapefile(file='shp/salas')
lyrShp.styles.append('styleShp')
 
#El orden de aplicacion de las capas es relevante
map.layers.append(lyrImg)
map.layers.append(lyrShp)
map.layers.append(lyrGeo)
 
#Importante ajustar el zoom para ver el mapa
map.zoom_all()
 
render_to_file(map, "MapNik", "png")

Anexo - Crear una base de datos con soporte GIS

$ sudo su - postgres
$ createuser -s -P -e usuario
$ createdb -O dueño databasename
$ createlang plpgsql -d databasename
$ psql databasename -f /usr/share/postgresql-8.3-postgis/lwpostgis.sql
$ psql databasename -f /usr/share/postgresql-8.3-postgis/spatial ref sys.sql
$ psql -d databasename -f db-schema.sql

Ejemplo de esquema SQL.

CREATE TABLE mapnik (
    id    varchar(100) PRIMARY KEY,
);
 
SELECT AddGeometryColumn('mapnik', 'geom', -1, 'GEOMETRY', 3);
 
CREATE INDEX mapnik_idx ON mapnik USING GIST(geom);

Finalmente hay que poblar la base de datos:

INSERT INTO mapnik (id, geom)
VALUES ('escalera0_up', GeomFromEWKT('POINT(45 56 0)'));
 
INSERT INTO mapnik (id, geom)
VALUES ('escalera1_up', GeomFromEWKT('POINT(66 59 0)'));
 
INSERT INTO mapnik (id, geom)
VALUES ('entrada', GeomFromEWKT('POINT(32 45 0)'));
 
INSERT INTO mapnik (id, geom)
VALUES ('salida', GeomFromEWKT('POINT(30 43 0)'));

Referencias