Geodjango und PostGIS

What the GIS?

Das bekannteste Geo Informations System ist sicherlich das aus dem Hause Google. Aber Google Maps und dessen Konsorten waren nicht die Ersten, die auf die brilliante Idee gekommen sind, Ihre Welt zu vermessen und auf Karten zu manifestieren.

Dieses Bedürfnis der Menschheit geht bis in die Steinzeit zurück, wo schlaue Krieger die Wanderrouten ihrer Lieblingsbeuten in die Höhlenwände malten.

Das GIS von heute hat mittlerweile damit nur noch recht wenig zu tun. Durch Satellitenunterstützung sind wir in der Lage unsere Erde supergenau abzubilden und Open Source Communities stellen diese Daten sogar der Menschheit zur freien Verfügung.

GeoDjango

Bedient sich dieser Informationen und stellt dem Entwickler alles denkbare zur Verfügung, um mit diesen Daten möglichst einfach umgehen zu können.

Dabei bedient sich GeoDjango den Geofunkionalitäten, die moderne DBMS zur Verfügung stellen. Dazu gehören MySQL, Oracle und Postgres mit der PostGIS Erweiterung.

Meine Erfahrungen habe ich mit PostGIS gemacht und beziehe mich in Beispielen immer auf diese Datenbank.

Models

PostGIS erweiterte Datenbanken werden um einige “plpgsql”-Scripte verstärkt, die sich um die Umrechnung und Berechnung von Koordinaten kümmern. Ausserdem führt PostGIS einige neue Datentypen ein, wie den Typ “geometry”. Mit diesem Typ werden die Koordinateninformationen in der Datenbank gespeichert.

Ein Knackpunkt, der schwer zu verstehen war, ist die SRID (Spatial Reference System Identity). Defaultwert bei GeoDjango ist meistens 4326, was bedeutet, dass der Wert in Längen und Breitengraden gemessen sind.

Wenn man das alles in Metern haben will, muss man die Angaben umrechnen. Da die Längen und Breitengrade an jedem Platz der Welt unterschiedlich weit auseinander sind, muss umgerechnet werden. Für die meisten Deutschen Plätze ist die SRID 32632 hilfreich. Mehr Infos darüber gibt es bei Wikipedia.

Hier ein kleines Beispiel aus meiner Test Datenbank, die ich mit den Daten der freien GeoNames Datenbank gefüttert habe.

gisdb=# select name, latlon_point from geoname \
                       where name = 'Fulda' or name = 'Schlitz';

  name   |          latlon_point
---------+----------------------------------------------------------
 Schlitz | 0101000020E6100000FEFFFF7F3E1F2340B7877DED4A564940
 Fulda   | 0101000020E6100000FFFFFFFFB0592340D9BA78609B464940
(2 Zeilen)

Unter Zuhilfenahme dieser Koordinaten kann GeoDjango ziemlich coole Sachen bewerkstelligen.

Man kann nicht nur Punkte, sondern auch Linien und Polygone in die Datenbank packen.

Dazu stellt GeoDjango folgende ModelFields zur Verfügung.

  • PointField
  • LineStringField
  • PolygonField
  • MultiPointField
  • MultiLineStringField
  • MultiPolygonField
  • GeometryCollectionField

Wichtig ist, dass man nicht wie üblich, von django.db.models.Model ableitet, sondern von django.contrib.gis.db.models.Model. Auch der Manager in objects muss gegen den von GeoDjango ausgetauscht werden.

from django.contrib.gis.db import models

class Place(models.Model):
    name = models.IntegerField()
    street = models.CharField(max_length=255)
    city = models.CharField(max_length=255)
    latlong = models.PointField(srid=32632)

    objects = models.GeoManager()

Und jetzt kann es losgehen!

Funktionen

Mit dem latlong Feld lassen sich die herrlichsten Sachen anstellen.

Dazu gehören alle Operationen der Mengenlehre. Beinhaltet ein Objekt das andere. Besitzen sie eine Schnittmenge. Sind sie disjunkt oder deckungsgleich. Weiterhin auch noch mehr, wie “Ist es Links, Rechts, Oben oder Unten” bzw. “Schneidet es Links, Rechts, Oben oder Unten”. Für Linien gilt das Selbe. Schneiden sich die Linien, und wo. Berühren sie sich? etc. Weitere Infos beinhaltet die Database Dokumentation von GeoDjango

Distanzen lassen sich in ähnlicher Weise errechnen. Zumindest, wenn man nicht gerade auf MySQL arbeitet, denn dieses Feature ist nur mit PostGIS, Oracle und SpatiaLite (die SQlite Erweiterung) zugänglich.

Spannend wird das Spiel erst, wenn man Punkthaufen sucht. So ist es möglich, alle Punkte aus der Datenbank zu holen, die in einem bestimmen Umkreis um einen anderen Punkt liegen:

from django.contrib.gis.geos import *
from places import Cities
from django.contrib.gis.measure import D

pnt = fromstr('POINT(50.6741616118374, 9.561023712158)', srid=4326)
citites = Cities.objects.filter(point__distance_lte=(pnt, D(km=7)))

Dieser Schnipsel sucht alle Orte, die im Umkreis von 7 km rund um Schlitz (pnt). Alles Weitere hält wieder die GeoDjango Doku bereit.

So, das soll es erstmal für den Anfang gewesen sein. Ich wünsche viel Spass beim ausprobieren. Kurz noch, hier sind ein paar links, die für das Thema interessant sind:

Last but not least ein bisschen SQL, um die GeoNames Datenbank mit feinen PostGIS Koordinaten zu füttern. Danke an das Geodjango Forum

 SELECT AddGeometryColumn( 
                  'public', 
                  'geoname', 
                  'latlon_point',
                  4326, 
                  'POINT',
                  2 
          );
 UPDATE geoname 
   SET latlon_point = PointFromText(
             'POINT(' || longitude || ' ' || latitude || ')',4326
           );
 CREATE INDEX latlon_point_index
   ON geoname
   USING gist
   (latlon_point);
 ALTER TABLE geonameCLUSTER ON latlon_point_index;
Add post to: Delicious Reddit Slashdot Digg Technorati Google