PostGIS + GeoDjango


10 marca 2013


Przykład wykorzystania przestrzennej bazy danych oraz frameworka GeoDjango.

W poprzednim wpisie przedstawiłem, jak utworzyć przestrzenną bazę danych pozwalającą gromadzić dane geograficzne. W niniejszym zaprezentuję prosty przykład połączenia naszej bazy z Django a bardziej szczegółowo to ujmując: z modułem GeoDjango.

GeoDjango to moduł Django, dzięki, któremu framework ten staje się frameworkiem geograficznym umożliwiającym szybkie tworzenie webowych systemów GIS. Z GeoDjango możemy korzystać po instalacji Django, ponieważ moduł ten jest cześcią frameworka.

Poniższy przykład został zrealizowany w Django 1.4.5 oraz bazie Postgres z rozszerzeniem PostGIS 2.0.3.

Pracę rozpoczynamy od stworzenia bazy danych.

createdb urzedy;
psql urzedy;
CREATE EXTENSION postgis;

Tworzymy projekt oraz aplikację w Django:

django-admin startproject urzedy
cd urzedy/
./manage.py startapp urzedy_skarbowe

W pliku settings.py ustawiamy parametry połą‚czenia z bazą… danych. Warto zwrócić uwagę, a konfigurację sterownika bazy danych. Nie wykorzystuje on standardowego połączenia przez "django.db.backends". 

...
DATABASES = {
    'default': {
        'ENGINE': 'django.contrib.gis.db.backends.postgis', 
        'NAME': 'urzedy',                     
        'USER': 'greg',                     
        'PASSWORD': 'greg',                 
        'HOST': 'localhost',                      
        'PORT': '',                     
    }
}
... 

Dodajemy… aplikację "urzedy_skarbowe", django.contrib.gis oraz włą…czamy panel admina w sekcji INSTALLED_APPS

...
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Uncomment the next line to enable the admin:
    'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    # 'django.contrib.admindocs',
    'django.contrib.gis',
    'urzedy_skarbowe',
)
...

W pliku "urls.py" aktywujemy routing dla panelu administracyjny:

from django.conf.urls import patterns, include, url

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', 'urzedy.views.home', name='home'),
    # url(r'^urzedy/', include('urzedy.foo.urls')),

    # Uncomment the admin/doc line below to enable admin documentation:
    # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),

    # Uncomment the next line to enable the admin:
    url(r'^admin/', include(admin.site.urls)),
)

Stwórzmy model "UrzedySkarbowe" w pliku "models.py" w katalogu aplikacji "urzedy_skarbowe". Tworząc model musimy pamiętać o zmianie modułu z, którego importujemy klasę "models.Model", ponieważ domyślnie Django w modelach dziedziczy po klasie pochodzącej z modułu "django.db":

# -*- coding: utf-8 -*-
from django.contrib.gis.db import models

class UrzedySkarbowe(models.Model):
    nazwa = models.CharField(max_length=50)
    
    polozenie = models.PointField()
    objects = models.GeoManager()

    def __unicode__(self):
        return self.nazwa
    
    class Meta:
        ordering = ['nazwa']
        verbose_name = 'urzą…d skarbowy'
        verbose_name_plural = 'Urzę™dy skarbowe'

Tworzymy plik "admin.py" i rejestrujemy nasz model:

from django.contrib.gis import admin
from models import UrzedySkarbowe
from urzedy import settings

admin.site.register(UrzedySkarbowe, admin.OSMGeoAdmin) 

Podobnie jak to było w przypadku tworzenia modelu także w pliku "admin.py" musimy zaimportować odpowiedni moduł ("django.contrib.gis") z, którego będziemy wykorzystywali zmienną "admin". 

W funkcji register jako drugi parametr podaliśmy klasę, która jest odpowiedzialna za wczytanie projektora generowanych map. GeoDjango do generowania map wykorzystuje "OpenLayers" - darmową bibliotekę JavaScript, która pozwala tworzyć dynamicznie generowane mapy na stronach internetowych.

W przykładzie dodaliśmy klasę "OSMGeoAdmin", która do generowania mapy korzysta z zasobów "OpenStreetMap" i mapy zawierają więcej szczegółów podczas powiększania. Możemy skorzystać także z klasy "GeoModelAdmin" gdy chcemy korzystać wyłacznie z "OpenLayers". 

Kolejnym krokiem jest synchronizacja naszego projektu z bazą danych. Przechodzimy do konsoli i tworzymy tabele w bazie danych:

./manage.py syncdb

Warto podczas wykonywania skryptu zdefiniować‡ administratora, za pomocą którego będziemy uwierzytelniali się w panelu Django. 
Podczas synchronizacji, może wystąpić błąd:

Failed to install index for urzedy_skarbowe.UrzedySkarbowe model: BŁĄ„D: 
klasa operatora "gist_geometry_ops" nie istnieje dla metody dostę™pu "gist"

Framework nie był w stanie utworzyć‡ index-u w tabeli (błą…d nie wystę™puje gdy wykonujemy te same czynności z PostGIS 1.5). Aby naprawić błą…d musimy ręcznie utworzyć‡ index w bazie danych. Aby wyświetlić zapytania generowane przez "syncdb" używamy polecenia:

./manage.py sqlall urzedy_skarbowe

W wyniku wywołania komendy otrzymujemy zapytania SQL.

BEGIN;
CREATE TABLE "urzedy_skarbowe_urzedyskarbowe" (
    "id" serial NOT NULL PRIMARY KEY,
    "nazwa" varchar(50) NOT NULL
)
;
SELECT AddGeometryColumn('urzedy_skarbowe_urzedyskarbowe', 
                         'polozenie', 
                         4326, 
                         'POINT', 
                         2);
ALTER TABLE "urzedy_skarbowe_urzedyskarbowe" 
            ALTER "polozenie" SET NOT NULL;
CREATE INDEX "urzedy_skarbowe_urzedyskarbowe_polozenie_id" 
             ON "urzedy_skarbowe_urzedyskarbowe" 
             USING GIST ( "polozenie" GIST_GEOMETRY_OPS );
COMMIT;

Musimy z powyższych zapytań„ wykonać‡ na naszej bazie danych wyłą…cznie zapytania przy czym z zapytania "CREATE INDEX ..." usuniemy ciąg znaków "GIST_GEOMETRY_OPS": 

SELECT AddGeometryColumn('urzedy_skarbowe_urzedyskarbowe', 
                         'polozenie', 
                         4326, 
                         'POINT', 
                         2);
ALTER TABLE "urzedy_skarbowe_urzedyskarbowe" 
            ALTER "polozenie" SET NOT NULL;
CREATE INDEX "urzedy_skarbowe_urzedyskarbowe_polozenie_id" 
             ON "urzedy_skarbowe_urzedyskarbowe" 
             USING GIST ( "polozenie");

Po wykonaniu zapytań pozostaje nam tylko uruchomienie serwera Django.

./manage.py runserver

Co zawiera blog?

Na blogu umieszczam wpisy dotyczące mojej pracy, zainteresowań. Głowna tematyka to programowanie oraz recenzje płyt oraz książek.

The Avalanches - Since I Left You


Od zawsze fascynowała mnie muzyka tworzona przy użyciu "czarnych krążków" ...

Arkusze i xlwt


Przykład użycia biblioteki xlwt (scalanie, zwijanie wierszy, zamrażanie wierszy i ...

Nadchodzi nowe


Opis dokonanych zmian w aplikacji oraz zapowiedź przyszłych modyfikacji oraz ...

Własne znaczniki w szablonach


Przykład tworzenia znaczników i ich użycia w systemie szablonów Django. ...

The Prodigy - The Day Is My Enemy Remixed


Co sądzę o płycie z remixami i bonusowymi utworami?

Coldcut ‎– Let Us Play!


Magiczna mieszanina