domingo, agosto 30, 2009

Primeros pasos con geodjango

Este es un minitutorial "expreso" para probar geodjango en debian testing con postgres 8.3, postgis y django 1.1

Pues lo primero es instalar los "juguetes":

$ apt-get install postgis postgresql-8.3 postgresql-plpython-8.3 
$ apt-get install python2.5 python-psycopg2 python-django ipython 
$ apt-get install gdal-bin python-gdal  
Ahora debemos configurar un poco postgres asi:
Debemos volvernos root y luego el usuario postgres

$ su 
$ su postrgres
$ psql template1

Este úlrimo comando nos conecta a postgres como el usuario postgres (el root del servidor de bases de datos), luego configuramos una plantilla para postigs (esto lo hacemos una sola vez)

template1=# ALTER USER postgres WITH PASSWORD 'postgres';
ALTER ROLE
Template1=#  \q

Luego:

$ cd /tmp
$ wget http://geodjango.org/docs/create_template_postgis-debian.sh
$ bash create_template_postgis-debian.sh
Esto nos crea la plantilla template_postgis, ahora volvemos a postgres:

$ createdb -T template_postgis geodjango

Salimos de postgres y del usuario root.
Ahora creamos un projecto y la app:
$ djano-admin startproject geodjango
$ cd geodjango
$ python manage.py startapp world

Luego editamos el settings de nuestro proyecto con el editor de nuestra preferencia (sugerencia rapida gedit)

$ gedit settings.py &
Dejamos las siguientes variables así

DATABASE_ENGINE = 'postgresql_psycopg2' 
DATABASE_NAME = 'geodjango'             # Or path to database file if using sqlite3.
DATABASE_USER = 'postgres'             # Not used with sqlite3.
DATABASE_PASSWORD = 'postgres'         # Not used with sqlite3.
DATABASE_HOST = 'localhost'
LANGUAGE_CODE = 'es'
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.admin',
    'django.contrib.gis',
    'geodjango.world',
)

luego de guardar estos cambios:
$ mkdir world/data
$ cd world/data
$ wget http://thematicmapping.org/downloads/TM_WORLD_BORDERS-0.3.zip
$ unzip TM_WORLD_BORDERS-0.3.zip
$ cd ../..
 
Luego editamos otro archivo:
 
$ gedit world/models.py & 
 
from django.contrib.gis.db import models

class WorldBorders(models.Model):
    # Regular Django fields corresponding to the attributes in the
    # world borders shapefile.
    name = models.CharField(max_length=50)
    area = models.IntegerField()
    pop2005 = models.IntegerField('Population 2005')
    fips = models.CharField('FIPS Code', max_length=2)
    iso2 = models.CharField('2 Digit ISO', max_length=2)
    iso3 = models.CharField('3 Digit ISO', max_length=3)
    un = models.IntegerField('United Nations Code')
    region = models.IntegerField('Region Code')
    subregion = models.IntegerField('Sub-Region Code')
    lon = models.FloatField()
    lat = models.FloatField()

    # GeoDjango-specific: a geometry field (MultiPolygonField), and
    # overriding the default manager with a GeoManager instance.
    mpoly = models.MultiPolygonField()
    objects = models.GeoManager()

    # So the model is pluralized correctly in the admin.
    class Meta:
        verbose_name_plural = "World Borders"

    # Returns the string representation of the model.
    def __unicode__(self):
        return self.name
 
Luego vemos que todo vaya bien:
 
$ ./manage.py sqlall world 

Esto nos mostrara el sql de postgres para crear nuestro modelo:
 
BEGIN;
CREATE TABLE "world_worldborders" (
    "id" serial NOT NULL PRIMARY KEY,
    "name" varchar(50) NOT NULL,
    "area" integer NOT NULL,
    "pop2005" integer NOT NULL,
    "fips" varchar(2) NOT NULL,
    "iso2" varchar(2) NOT NULL,
    "iso3" varchar(3) NOT NULL,
    "un" integer NOT NULL,
    "region" integer NOT NULL,
    "subregion" integer NOT NULL,
    "lon" double precision NOT NULL,
    "lat" double precision NOT NULL
)
;
SELECT AddGeometryColumn('world_worldborders', 'mpoly', 4326, 'MULTIPOLYGON', 2);
ALTER TABLE "world_worldborders" ALTER "mpoly" SET NOT NULL;
CREATE INDEX "world_worldborders_mpoly_id" ON "world_worldborders" USING GIST ( "mpoly" GIST_GEOMETRY_OPS );
COMMIT;
 
 
Luego ejecutamos este sql:
$ ./manage.py syncdb
 
Lo que nos preguntara el usuario admin, su correo y la clave de acceso:
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table django_admin_log
Creating table world_worldborders

You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (Leave blank to use 'diegueus9'): <- Toma el usuario con el que estes haciendo esto
E-mail address:  <- Escribe tu correo
Password:
Password (again):
Superuser created successfully.
Installing custom SQL for world.WorldBorders model
Installing index for auth.Permission model
Installing index for auth.Message model
Installing index for admin.LogEntry model 

Luego creamos un scrip que cargará los datos de los archivos que descargamos:
$ gedit world/load.py
import os
from django.contrib.gis.utils import LayerMapping
from models import WorldBorders

world_mapping = {
    'fips' : 'FIPS',
    'iso2' : 'ISO2',
    'iso3' : 'ISO3',
    'un' : 'UN',
    'name' : 'NAME',
    'area' : 'AREA',
    'pop2005' : 'POP2005',
    'region' : 'REGION',
    'subregion' : 'SUBREGION',
    'lon' : 'LON',
    'lat' : 'LAT',
    'mpoly' : 'MULTIPOLYGON',
}

world_shp = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data/TM_WORLD_BORDERS-0.3.shp'))

def run(verbose=True):
    lm = LayerMapping(WorldBorders, world_shp, world_mapping,
                      transform=False, encoding='iso-8859-1')

    lm.save(strict=True, verbose=verbose)
 
Ahora lo que vamos a hacer es subir la info de las fronteras de los paises del mundo a nuestro modelo:
 
$ ./manage shell 
Python 2.5.4 (r254:67916, Feb 18 2009, 03:00:47)
Type "copyright", "credits" or "license" for more information.

IPython 0.9.1 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object'. ?object also works, ?? prints more.

In [1]: from world import load

In [2]: load.run()
...
...
...
Saved: Jersey
Saved: South Georgia South Sandwich Islands
Saved: Taiwan

In [3]: exit()
Do you really want to exit ([y]/n)? y
Ahora vamos a activar y usar nuestra interfaz automagica administrativa que nos da django:
$ gedit world/admin.py &
from django.contrib.gis import admin
from models import WorldBorders

class WorldBordersAdmin(admin.GeoModelAdmin):
    list_display = ['name', 'area', 'pop2005', 'lon', 'lat']
    search_fields = ['name']

admin.site.register(WorldBorders, WorldBordersAdmin)
 
Y luego editamos las urls:
$ gedit urls.py &
from django.conf.urls.defaults import *
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    (r'^admin/', include(admin.site.urls)),
)

Ahora ponemos a correr nuestro servidor de desarrollo:
$ ./manage runserver 9090
Validating models...
0 errors found

Django version 1.1, using settings 'geodjango.settings'
Development server is running at http://127.0.0.1:9090/
Quit the server with CONTROL-C.
Y en el navegador vamos a  http://127.0.0.1:9090/ veremos un error 404 como este:

Luego vamos a http://127.0.0.1:9090/admin y veremos esta interfaz:
alli ingresamos el usuario y la clave que configuramos cuando corrimos el comando syncdb y veremos esto:

Luego hacemos clic en World Borders y veremos algo así:

Allí podemos ordenar la lista por nombre  del pais (name), area (area), poblacion del 2005 (population 2005) o longitud y latitud del mismo. Ahora cerca al boton buscar ingresamos colombia y le damos buscar luego clic en colombia y veremos info y este mapa:

Y eso es todo, así tendremos nuestro "Hola mundo" con geodjango, django, postgres, postgis, etc...

2 comentarios:

Ariel Camino dijo...

Gracias por el tutorial che, lástima que no lo ví antes jaja, igual me ayudó con un pequeño detalle del admin (para especificar el list_display utilizando GeoModelAdmin).

Octaviano Cerna Alonso dijo...

Buen mini tuto lo probare...