GeoDjango es un módulo contribuido incluido en Django que lo convierte en un marco web de clase mundial para geografía. GeoDjango busca hacer tan simple como sea posible crear aplicaciones web geográficas, como servicios basados en ubicación. Sus características incluyen:
Campos de modelo Django para geometrías y datos raster OGC.
Extensiones a la ORM de Django para consultar y manipular datos espaciales.
Interfaces de alto nivel, sueltas y en Python para operaciones y manipulación de geometría GIS y datos raster en diferentes formatos.
Edición de campos de geometría desde el administrador.
Este tutorial asume familiaridad con Django; por lo tanto, si eres nuevo en Django, por favor lee a través del tutorial regular para familiarizarte con Django antes.
Nota
GeoDjango tiene requisitos adicionales más allá de los que requiere Django – consulte la documentación de instalación :doc:` <install/index>` para obtener más detalles.
Esta es la traducción de los textos:
Nota
Pasa por las secciones del tutorial secuencialmente para instrucciones paso a paso.
Normalmente no es necesario realizar configuraciones especiales, así que puedes crear una base de datos como lo harías con cualquier otro proyecto. Ofrecemos algunas pautas para bases de datos seleccionadas:
instalar/postgis
instalar/spatialite
Utiliza el script estándar django-admin para crear un proyecto llamado geodjango:
$ django-admin startproject geodjango
Esto inicializará un nuevo proyecto. Ahora, crea una aplicación Django world dentro del proyecto geodjango:
$ cd geodjango
$ python manage.py startapp world
Los ajustes de configuración del proyecto geodjango se almacenan en el archivo geodjango/settings.py. Edita los ajustes de conexión a la base de datos para que coincidan con tu configuración:
DATABASES = {
"default": {
"ENGINE": "django.contrib.gis.db.backends.postgis",
"NAME": "geodjango",
"USER": "geo",
},
}
Además, modifica la configuración de INSTALLED_APPS para incluir django.contrib.admin, django.contrib.gis, y world (tu aplicación recién creada).
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.gis",
"world",
]
La información de fronteras mundiales está disponible en este archivo `zip`__. Crea un directorio data en la aplicación world, descarga los datos de fronteras mundiales y descomprímelo. En plataformas GNU/Linux, utiliza las siguientes órdenes:
$ mkdir world/data
$ cd world/data
$ wget https://web.archive.org/web/20231220150759/https://thematicmapping.org/downloads/TM_WORLD_BORDERS-0.3.zip
$ unzip TM_WORLD_BORDERS-0.3.zip
$ cd ../..
El archivo ZIP contiene una serie de archivos de datos colectivamente conocidos como un **`ESRI Shapefile`**_, uno de los formatos de datos geoespaciales más populares. Cuando se descomprime, el conjunto de datos de fronteras mundiales incluye archivos con las siguientes extensiones:
.shp: Almacena los datos de vectores para las geometrías de fronteras mundiales.
.shx: archivo de índice espacial para geometrías almacenadas en el .shp.
.dbf: Archivo de base de datos para almacenar datos no geométricos de atributos (por ejemplo, campos enteros y caracteres).
.prj: Contiene la información de referencia espacial para los datos geográficos almacenados en el archivo shapefile.
ogrinfo para examinar los datos espaciales¶La utilidad ogrinfo del GDAL permite examinar los metadatos de archivos shapefiles o otras fuentes de datos vectoriales:
$ ogrinfo world/data/TM_WORLD_BORDERS-0.3.shp
INFO: Open of `world/data/TM_WORLD_BORDERS-0.3.shp'
using driver `ESRI Shapefile' successful.
1: TM_WORLD_BORDERS-0.3 (Polygon)
ogrinfo nos dice que el archivo shapefile tiene una capa, y que esta capa contiene datos poligonales. Para obtener más información, especificaremos el nombre de la capa y utilizaremos la opción -so para obtener solo la información resumida importante:
$ ogrinfo -so world/data/TM_WORLD_BORDERS-0.3.shp TM_WORLD_BORDERS-0.3
INFO: Open of `world/data/TM_WORLD_BORDERS-0.3.shp'
using driver `ESRI Shapefile' successful.
Layer name: TM_WORLD_BORDERS-0.3
Metadata:
DBF_DATE_LAST_UPDATE=2008-07-30
Geometry: Polygon
Feature Count: 246
Extent: (-180.000000, -90.000000) - (180.000000, 83.623596)
Layer SRS WKT:
GEOGCRS["WGS 84",
DATUM["World Geodetic System 1984",
ELLIPSOID["WGS 84",6378137,298.257223563,
LENGTHUNIT["metre",1]]],
PRIMEM["Greenwich",0,
ANGLEUNIT["degree",0.0174532925199433]],
CS[ellipsoidal,2],
AXIS["latitude",north,
ORDER[1],
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["longitude",east,
ORDER[2],
ANGLEUNIT["degree",0.0174532925199433]],
ID["EPSG",4326]]
Data axis to CRS axis mapping: 2,1
FIPS: String (2.0)
ISO2: String (2.0)
ISO3: String (3.0)
UN: Integer (3.0)
NAME: String (50.0)
AREA: Integer (7.0)
POP2005: Integer64 (10.0)
REGION: Integer (3.0)
SUBREGION: Integer (3.0)
LON: Real (8.3)
LAT: Real (7.3)
Esta información resumida detallada nos dice el número de características en la capa (246), los límites geográficos de los datos, el sistema de referencia espacial («SRS WKT»), así como la información del tipo para cada campo de atributo. Por ejemplo, FIPS: Cadena (2.0) indica que el campo de caracteres FIPS tiene una longitud máxima de 2. De manera similar, LON: Real (8.3) es un campo flotante que almacena un máximo de 8 dígitos con tres decimales.
Ahora que has examinado tu conjunto de datos utilizando ogrinfo, crea un modelo GeoDjango para representar estos datos:
from django.contrib.gis.db import models
class WorldBorder(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, null=True)
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)
mpoly = models.MultiPolygonField()
# Returns the string representation of the model.
def __str__(self):
return self.name
Nota que el módulo models se importa desde django.contrib.gis.db.
El sistema de referencia espacial por defecto para los campos de geometría es WGS84 (lo que significa que el `SRID`__ es 4326) – en otras palabras, las coordenadas del campo están en pares de longitud y latitud en unidades de grados. Para utilizar un sistema de coordenadas diferente, establezca el SRID del campo de geometría con la argumento srid. Utiliza un entero que represente el código EPSG del sistema de coordenadas.
migrar.¶Después de definir tu modelo, debes sincronizarlo con la base de datos. Primero, crea una migración de base de datos:
$ python manage.py makemigrations
Migrations for 'world':
world/migrations/0001_initial.py:
+ Create model WorldBorder
Vamos a ver el SQL que generará la tabla para el modelo WorldBorder:
$ python manage.py sqlmigrate world 0001
Este comando debería producir la siguiente salida:
BEGIN;
--
-- Create model WorldBorder
--
CREATE TABLE "world_worldborder" (
"id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
"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
"mpoly" geometry(MULTIPOLYGON,4326) NOT NULL
)
;
CREATE INDEX "world_worldborder_mpoly_id" ON "world_worldborder" USING GIST ("mpoly");
COMMIT;
Si esto parece correcto, ejecuta migrar para crear esta tabla en la base de datos:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, world
Running migrations:
...
Applying world.0001_initial... OK
Esta sección mostrará cómo importar el archivo shapefile de fronteras mundiales en la base de datos a través de modelos GeoDjango utilizando la Utilidad de importación de datos de LayerMapping.
Hay muchas formas diferentes de importar datos en una base de datos espacial – además de las herramientas incluidas dentro de GeoDjango, también puedes utilizar las siguientes:
ogr2ogr: Una utilidad de línea de comandos incluida con GDAL que puede importar muchos formatos de datos vectoriales a bases de datos PostGIS, MySQL y Oracle.
shp2pgsql: Esta utilidad incluida con PostGIS importa archivos ESRI shapefiles en PostGIS.
Anteriormente, utilizaste ogrinfo para examinar el contenido del archivo shapefile de fronteras mundiales. GeoDjango también incluye una interfaz Pythonica a la poderosa biblioteca OGR de GDAL que puede trabajar con todos los fuentes de datos vectoriales que soporta OGR.
Primero, invoca la consola de Django:
$ python manage.py shell
Si descargaste los datos Fronteras del Mundo anteriormente en el tutorial, entonces puedes determinar su ruta utilizando Python’s pathlib.Path:
>>> from pathlib import Path
>>> import world
>>> world_shp = Path(world.__file__).resolve().parent / "data" / "TM_WORLD_BORDERS-0.3.shp"
Ahora, abre el archivo shapefile de fronteras mundiales utilizando la interfaz GeoDjango de DataSource :
>>> from django.contrib.gis.gdal import DataSource
>>> ds = DataSource(world_shp)
>>> print(ds)
/ ... /geodjango/world/data/TM_WORLD_BORDERS-0.3.shp (ESRI Shapefile)
Los objetos de fuente de datos pueden tener diferentes capas de características geoespaciales; sin embargo, los archivos shapefiles solo están permitidos para tener una sola capa:
>>> print(len(ds))
1
>>> lyr = ds[0]
>>> print(lyr)
TM_WORLD_BORDERS-0.3
Puedes ver el tipo de geometría de la capa y cuántas características contiene:
>>> print(lyr.geom_type)
Polygon
>>> print(len(lyr))
246
Nota
Desafortunadamente, el formato de datos del archivo shapefile no permite mayor especificidad con respecto a los tipos de geometrías. Este archivo shapefile, como muchos otros, incluye realmente geometrías MultiPolygon, no Polígonos. Es importante utilizar un campo de tipo más general en los modelos: un campo GeoDjango MultiPolygonField aceptará una geometría Polygon, pero un campo PolygonField no aceptará un tipo de geometría MultiPolygon. Esto es por qué el modelo WorldBorder definido anteriormente utiliza un campo MultiPolygonField.
La capa Layer puede tener también un sistema de referencia espacial asociado con ella. Si lo hace, la propiedad srs devolverá un objeto SpatialReference :
>>> srs = lyr.srs
>>> print(srs)
GEOGCS["WGS 84",
DATUM["WGS_1984",
SPHEROID["WGS 84",6378137,298.257223563,
AUTHORITY["EPSG","7030"]],
AUTHORITY["EPSG","6326"]],
PRIMEM["Greenwich",0,
AUTHORITY["EPSG","8901"]],
UNIT["degree",0.0174532925199433,
AUTHORITY["EPSG","9122"]],
AXIS["Latitude",NORTH],
AXIS["Longitude",EAST],
AUTHORITY["EPSG","4326"]]
>>> srs.proj # PROJ representation
'+proj=longlat +datum=WGS84 +no_defs'
Este archivo shapefile está en el popular sistema de referencia espacial WGS84 – a saber, los datos utilizan pares de longitudes y latitudes en unidades de grados.
Además, los archivos de forma también admiten campos de atributo que pueden contener datos adicionales. Aquí están los campos en la capa de fronteras del mundo:
>>> print(lyr.fields)
['FIPS', 'ISO2', 'ISO3', 'UN', 'NAME', 'AREA', 'POP2005', 'REGION', 'SUBREGION', 'LON', 'LAT']
El siguiente código te permitirá examinar los tipos OGR (por ejemplo, entero o cadena) asociados con cada uno de los campos:
>>> [fld.__name__ for fld in lyr.field_types]
['OFTString', 'OFTString', 'OFTString', 'OFTInteger', 'OFTString', 'OFTInteger', 'OFTInteger64', 'OFTInteger', 'OFTInteger', 'OFTReal', 'OFTReal']
Puedes iterar sobre cada característica en la capa y extraer información tanto de la geometría de la característica (accedida a través del atributo geom) como de los campos de atributos de la característica (cuyos valores se acceden a través del método get()):
>>> for feat in lyr:
... print(feat.get("NAME"), feat.geom.num_points)
...
Guernsey 18
Jersey 26
South Georgia South Sandwich Islands 338
Taiwan 363
Los objetos Layer pueden ser rebanados:
>>> lyr[0:2]
[<django.contrib.gis.gdal.feature.Feature object at 0x2f47690>, <django.contrib.gis.gdal.feature.Feature object at 0x2f47650>]
Y las características individuales pueden recuperarse por su ID de característica:
>>> feat = lyr[234]
>>> print(feat.get("NAME"))
San Marino
Las geometrías de fronteras pueden exportarse como WKT y GeoJSON:
>>> geom = feat.geom
>>> print(geom.wkt)
POLYGON ((12.415798 43.957954,12.450554 ...
>>> print(geom.json)
{ "type": "Polygon", "coordinates": [ [ [ 12.415798, 43.957954 ], [ 12.450554, 43.979721 ], ...
LayerMapping¶Para importar los datos, utiliza un LayerMapping en un script de Python. Crea un archivo llamado load.py dentro de la aplicación world, con el siguiente código:
from pathlib import Path
from django.contrib.gis.utils import LayerMapping
from .models import WorldBorder
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 = Path(__file__).resolve().parent / "data" / "TM_WORLD_BORDERS-0.3.shp"
def run(verbose=True):
lm = LayerMapping(WorldBorder, world_shp, world_mapping, transform=False)
lm.save(strict=True, verbose=verbose)
Un par de notas sobre lo que está sucediendo:
Cada clave en el diccionario world_mapping corresponde a un campo en el modelo WorldBorder. El valor es el nombre del campo del archivo de forma desde donde se cargará los datos.
La traducción de los textos es la siguiente:
La ruta al archivo shapefile no es absoluta – en otras palabras, si mueves la aplicación world (con subdirectorio data) a una ubicación diferente, el script seguirá funcionando.
La palabra clave transform está configurada como False porque los datos en el archivo shapefile no necesitan ser convertidos – ya están en WGS84 (SRID=4326).
Luego, invoca la consola de Django desde el directorio del proyecto geodjango:
$ python manage.py shell
A continuación, importa el módulo load, llama a la rutina run y observa cómo LayerMapping hace el trabajo:
>>> from world import load
>>> load.run()
ogrinspect¶Ahora que has visto cómo definir modelos geográficos y importar datos con la Utilidad de importación de datos de LayerMapping, es posible automatizar aún más este proceso mediante el uso del comando de administración ogrinspect. El comando ogrinspect inspecciona una fuente de datos vectorial apoyada por GDAL (por ejemplo, un archivo shapefile) y genera automáticamente la definición del modelo y el diccionario LayerMapping.
La utilización general del comando sigue este patrón:
$ python manage.py ogrinspect [options] <data_source> <model_name> [options]
data_source es la ruta a la fuente de datos apoyada por GDAL y model_name es el nombre a utilizar para el modelo. Se pueden utilizar opciones de línea de comandos para definir aún más cómo se genera el modelo.
Por ejemplo, el siguiente comando reproduce casi exactamente el modelo WorldBorder y el diccionario de mapeo creado anteriormente, automáticamente:
$ python manage.py ogrinspect world/data/TM_WORLD_BORDERS-0.3.shp WorldBorder \
--srid=4326 --mapping --multi
Los textos traducidos son:
La opción --srid=4326 establece el SRID para el campo geográfico.
La opción --mapping indica a ogrinspect que también genere un diccionario de mapeo para su uso con LayerMapping.
La opción --multi se especifica para que el campo geográfico sea un MultiPolygonField en lugar de solo un PolygonField.
El comando produce la siguiente salida, que puede copiarse directamente en el archivo models.py de una aplicación GeoDjango:
# This is an auto-generated Django model module created by ogrinspect.
from django.contrib.gis.db import models
class WorldBorder(models.Model):
fips = models.CharField(max_length=2)
iso2 = models.CharField(max_length=2)
iso3 = models.CharField(max_length=3)
un = models.IntegerField()
name = models.CharField(max_length=50)
area = models.IntegerField()
pop2005 = models.IntegerField()
region = models.IntegerField()
subregion = models.IntegerField()
lon = models.FloatField()
lat = models.FloatField()
geom = models.MultiPolygonField(srid=4326)
# Auto-generated `LayerMapping` dictionary for WorldBorder model
worldborders_mapping = {
"fips": "FIPS",
"iso2": "ISO2",
"iso3": "ISO3",
"un": "UN",
"name": "NAME",
"area": "AREA",
"pop2005": "POP2005",
"region": "REGION",
"subregion": "SUBREGION",
"lon": "LON",
"lat": "LAT",
"geom": "MULTIPOLYGON",
}
GeoDjango agrega consultas espaciales a la ORM de Django. Por ejemplo, puedes encontrar el país en la tabla WorldBorder que contiene un punto particular. Primero, arranca la consola de gestión:
$ python manage.py shell
Ahora, define un punto de interés [3]:
>>> pnt_wkt = "POINT(-95.3385 29.7245)"
La cadena pnt_wkt representa el punto a -95.3385 grados de longitud, 29.7245 grados de latitud. La geometría está en un formato conocido como Texto Conocido (WKT), un estándar emitido por la Open Geospatial Consortium (OGC). [4] Importa el modelo WorldBorder y realiza una consulta de contención utilizando pnt_wkt como parámetro:
>>> from world.models import WorldBorder
>>> WorldBorder.objects.filter(mpoly__contains=pnt_wkt)
<QuerySet [<WorldBorder: United States>]>
Aquí, recuperaste un conjunto de consultas con solo un modelo: la frontera de los Estados Unidos (exactamente lo que esperarías).
De manera similar, también puedes utilizar un objeto de geometría GEOS.
>>> from django.contrib.gis.geos import Point
>>> pnt = Point(12.4604, 43.9420)
>>> WorldBorder.objects.get(mpoly__intersects=pnt)
<WorldBorder: San Marino>
Las consultas contains y intersects son solo un subconjunto de las consultas disponibles – la documentación del GeoDjango Database API tiene más información.
Al realizar consultas espaciales, GeoDjango transforma automáticamente las geometrías si están en un sistema de coordenadas diferente. En el ejemplo siguiente, se expresarán las coordenadas en `EPSG SRID 32140`__, un sistema de coordenadas específico para el sur de Texas solo y con unidades de metros, no grados:
>>> from django.contrib.gis.geos import GEOSGeometry, Point
>>> pnt = Point(954158.1, 4215137.1, srid=32140)
Nota que pnt también puede construirse con EWKT, una forma «extendida» de WKT que incluye el SRID:
>>> pnt = GEOSGeometry("SRID=32140;POINT(954158.1 4215137.1)")
La ORM de GeoDjango envolverá automáticamente los valores de geometría en SQL de transformación, permitiendo al desarrollador trabajar a un nivel más alto de abstracción:
>>> qs = WorldBorder.objects.filter(mpoly__intersects=pnt)
>>> print(qs.query) # Generating the SQL
SELECT "world_worldborder"."id", "world_worldborder"."name", "world_worldborder"."area",
"world_worldborder"."pop2005", "world_worldborder"."fips", "world_worldborder"."iso2",
"world_worldborder"."iso3", "world_worldborder"."un", "world_worldborder"."region",
"world_worldborder"."subregion", "world_worldborder"."lon", "world_worldborder"."lat",
"world_worldborder"."mpoly" FROM "world_worldborder"
WHERE ST_Intersects("world_worldborder"."mpoly", ST_Transform(%s, 4326))
>>> qs # printing evaluates the queryset
<QuerySet [<WorldBorder: United States>]>
Consultas brutas
Al utilizar consultas brutas, debes envolver tus campos de geometría para que el valor del campo pueda ser reconocido por GEOS:
>>> from django.db import connection
>>> # or if you're querying a non-default database:
>>> from django.db import connections
>>> connection = connections["your_gis_db_alias"]
>>> City.objects.raw(
... "SELECT id, name, %s as point from myapp_city" % (connection.ops.select % "point")
... )
Solo debes utilizar consultas brutas cuando sepas exactamente lo que estás haciendo.
GeoDjango carga las geometrías en una representación textual estándarizada. Cuando se accede por primera vez al campo de geometría, GeoDjango crea un objeto GEOSGeometry, que expone funcionalidades poderosas, como propiedades de serialización para formatos geoespaciales populares:
>>> sm = WorldBorder.objects.get(name="San Marino")
>>> sm.mpoly
<MultiPolygon object at 0x24c6798>
>>> sm.mpoly.wkt # WKT
MULTIPOLYGON (((12.4157980000000006 43.9579540000000009, 12.4505540000000003 43.9797209999999978, ...
>>> sm.mpoly.wkb # WKB (as Python binary buffer)
<read-only buffer for 0x1fe2c70, size -1, offset 0 at 0x2564c40>
>>> sm.mpoly.geojson # GeoJSON
'{ "type": "MultiPolygon", "coordinates": [ [ [ [ 12.415798, 43.957954 ], [ 12.450554, 43.979721 ], ...
Esto incluye acceso a todas las operaciones geométricas avanzadas proporcionadas por la biblioteca GEOS:
>>> pnt = Point(12.4604, 43.9420)
>>> sm.mpoly.contains(pnt)
True
>>> pnt.contains(sm.mpoly)
False
GeoDjango también ofrece un conjunto de anotaciones geográficas para calcular distancias y varias otras operaciones (intersección, diferencia, etc.). Consulte la documentación de las funciones.
La aplicación administrativa de Django <ref/contrib/admin/index> admite la edición de campos de geometría.
La aplicación administrativa de Django permite a los usuarios crear y modificar geometrías en una mapa deslizante JavaScript (impulsado por OpenLayers).
Vamos directos al grano. Crea un archivo llamado admin.py dentro de la aplicación world con el siguiente código:
from django.contrib.gis import admin
from .models import WorldBorder
admin.site.register(WorldBorder, admin.ModelAdmin)
Edita tu archivo urls.py en la carpeta de aplicación geodjango como se indica a continuación:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
]
Crear un usuario administrador:
$ python manage.py createsuperuser
Luego, arranca el servidor de desarrollo de Django:
$ python manage.py runserver
Finalmente, navega hasta http://localhost:8000/admin/, y haz login con el usuario que acabas de crear. Navega a cualquier entrada de WorldBorder – los límites pueden ser editados haciendo clic en un polígono y arrastrando los vértices a la posición deseada.
Con la clase GISModelAdmin, GeoDjango utiliza una capa de OpenStreetMap en el panel de administración. Esto proporciona más contexto (incluyendo detalles de calle y vía) que el disponible con la clase ModelAdmin (que utiliza el conjunto de datos WMS Vector Map Level 0 alojado en OSGeo.
Los archivos de desplazamiento del datum PROJ deben estar instalados (consulte las instrucciones de instalación de PROJ <proj4> para más detalles).
Si cumplen con esta requisito, entonces utilicen la clase de opción GISModelAdmin en su archivo admin.py.
admin.site.register(WorldBorder, admin.GISModelAdmin)
Notas al pie
may 31, 2026