PostGIS básico para datos geoespaciales

Avanzado
SQL
SQL
Actualizado: 19/04/2026

PostGIS convierte a PostgreSQL en el motor SIG (Sistema de Información Geográfica) más utilizado del mundo, por delante de soluciones específicas como SQL Server Spatial u Oracle Spatial. Cualquier aplicación que trabaje con coordenadas (mapas, flotas, IoT, dispositivos móviles, catastro, agricultura) acaba apoyándose en él. Esta lección cubre los conceptos esenciales para empezar.

Habilitar PostGIS

PostGIS es una extensión que normalmente se instala como paquete del sistema operativo o ya viene preinstalada en proveedores cloud. Activarla es una sentencia:

CREATE EXTENSION IF NOT EXISTS postgis;

SELECT postgis_full_version();

Esto registra los tipos geometry y geography, cientos de funciones ST_* y los operadores espaciales. PostGIS añade además la tabla spatial_ref_sys con miles de sistemas de referencia espacial (SRID) conocidos.

Geometry frente a Geography

PostGIS ofrece dos tipos principales que conviene distinguir:

  • geometry: trata las coordenadas como un plano cartesiano. Más rápido pero las distancias en metros son incorrectas si los datos están en latitud/longitud sin proyectar.
  • geography: trata las coordenadas como puntos sobre la esfera (WGS84). Las distancias se calculan en metros reales, pero las operaciones son más caras.

| Aspecto | geometry | geography | |---------|------------|-------------| | Coordenadas | Cualquier sistema | Solo SRID 4326 (WGS84) | | Unidad de distancia | Depende del SRID | Metros | | Velocidad | Mayor | Menor | | Funciones disponibles | Catálogo completo | Subconjunto | | Casos de uso | Datos proyectados (UTM) | Lat/lon de GPS, móvil |

Regla práctica: si tus datos vienen como latitud/longitud GPS, usa geography. Si los importas en proyección local (UTM, ETRS89), usa geometry con el SRID correspondiente.

Crear tablas geoespaciales

CREATE TABLE tiendas (
    id BIGSERIAL PRIMARY KEY,
    nombre TEXT NOT NULL,
    ubicacion GEOGRAPHY(POINT, 4326) NOT NULL,
    creada_en TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE rutas (
    id BIGSERIAL PRIMARY KEY,
    nombre TEXT,
    trazado GEOGRAPHY(LINESTRING, 4326) NOT NULL
);

CREATE TABLE zonas (
    id BIGSERIAL PRIMARY KEY,
    nombre TEXT,
    poligono GEOGRAPHY(POLYGON, 4326) NOT NULL
);

La sintaxis GEOGRAPHY(POINT, 4326) declara el subtipo (POINT, LINESTRING, POLYGON, MULTIPOINT, etc.) y el SRID (4326 = WGS84).

Insertar geometrías

Las funciones de construcción más usadas son ST_MakePoint y ST_GeomFromText:

-- Punto a partir de longitud y latitud (en ese orden!)
INSERT INTO tiendas (nombre, ubicacion) VALUES
('Tienda Madrid', ST_MakePoint(-3.7038, 40.4168)::GEOGRAPHY),
('Tienda Barcelona', ST_MakePoint(2.16992, 41.3879)::GEOGRAPHY),
('Tienda Sevilla', ST_MakePoint(-5.9845, 37.3891)::GEOGRAPHY);

-- Linea a partir de WKT (Well-Known Text)
INSERT INTO rutas (nombre, trazado) VALUES
('Ruta MAD-BCN',
 ST_GeomFromText('LINESTRING(-3.7038 40.4168, 2.16992 41.3879)', 4326)::GEOGRAPHY);

-- Poligono
INSERT INTO zonas (nombre, poligono) VALUES
('Centro Madrid',
 ST_GeomFromText('POLYGON((-3.72 40.41, -3.69 40.41, -3.69 40.43, -3.72 40.43, -3.72 40.41))',
                 4326)::GEOGRAPHY);

Atención al orden: PostGIS usa (longitud, latitud), no (latitud, longitud). Es la convención de OGC y de GeoJSON, contraria a la intuición de la mayoría de hispanohablantes.

Distancias y puntos cercanos

ST_Distance devuelve la distancia entre dos geografías. Para geography la unidad es siempre metros:

SELECT
    ST_Distance(
        ST_MakePoint(-3.7038, 40.4168)::GEOGRAPHY,
        ST_MakePoint(2.16992, 41.3879)::GEOGRAPHY
    ) AS metros_madrid_barcelona;
-- 504000 metros aproximadamente

Para encontrar puntos cercanos se usa ST_DWithin, que comprueba si dos geografías están dentro de una distancia especificada y aprovecha el índice GiST:

-- Tiendas a menos de 500 km de un punto dado
SELECT nombre, ST_Distance(ubicacion,
    ST_MakePoint(-3.7038, 40.4168)::GEOGRAPHY) / 1000 AS km
FROM tiendas
WHERE ST_DWithin(
    ubicacion,
    ST_MakePoint(-3.7038, 40.4168)::GEOGRAPHY,
    500000  -- metros
)
ORDER BY km;

ST_DWithin es la función a usar para "dame los puntos en un radio". ST_Distance solo en el SELECT, no en WHERE; usar ST_Distance(...) < x impide aprovechar el índice.

Indexación GiST

Sin índice, las consultas espaciales hacen sequential scan. PostGIS usa índices GiST sobre columnas geográficas:

CREATE INDEX idx_tiendas_ubicacion ON tiendas USING GIST (ubicacion);
CREATE INDEX idx_rutas_trazado ON rutas USING GIST (trazado);
CREATE INDEX idx_zonas_poligono ON zonas USING GIST (poligono);

EXPLAIN ANALYZE
SELECT * FROM tiendas
WHERE ST_DWithin(ubicacion, ST_MakePoint(-3.7, 40.4)::GEOGRAPHY, 10000);
-- Index Scan using idx_tiendas_ubicacion

El índice GiST acelera no solo ST_DWithin, sino también ST_Intersects, ST_Contains, ST_Crosses y otros operadores topológicos.

Pertenencia y intersección

Para saber si un punto cae dentro de una zona se usa ST_Within o ST_Contains:

SELECT z.nombre AS zona, t.nombre AS tienda
FROM tiendas t, zonas z
WHERE ST_Within(t.ubicacion::GEOMETRY, z.poligono::GEOMETRY);

-- Cuantas tiendas hay en cada zona
SELECT z.nombre, COUNT(t.id) AS num_tiendas
FROM zonas z
LEFT JOIN tiendas t ON ST_Contains(z.poligono::GEOMETRY, t.ubicacion::GEOMETRY)
GROUP BY z.nombre;

ST_Intersects es más permisivo: devuelve true si dos geometrías comparten cualquier punto, incluyendo bordes y solapamientos parciales.

Buffer y áreas de influencia

ST_Buffer crea una zona alrededor de un punto, línea o polígono. Útil para áreas de cobertura, zonas de seguridad o rutas con margen:

-- Crear un buffer de 1 km alrededor de cada tienda
SELECT
    nombre,
    ST_Buffer(ubicacion, 1000) AS zona_1km
FROM tiendas;

-- Tiendas cuya zona de cobertura incluye un punto dado
SELECT nombre
FROM tiendas
WHERE ST_DWithin(ubicacion, ST_MakePoint(-3.7, 40.4)::GEOGRAPHY, 1000);

ST_Buffer sobre geography es lento. Para análisis masivos suele ser mejor proyectar a un sistema local (UTM, por ejemplo) y trabajar con geometry.

Transformaciones de proyección

Para convertir entre sistemas de referencia se usa ST_Transform:

-- De WGS84 (4326) a UTM zona 30N (25830 para España)
SELECT
    nombre,
    ST_Transform(ubicacion::GEOMETRY, 25830) AS utm_30n
FROM tiendas;

Esta operación es esencial cuando importas datos de organismos oficiales que vienen en proyecciones nacionales y necesitas combinarlos con datos GPS.

Casos de uso típicos

PostGIS aparece en multitud de escenarios reales:

  • Logística y reparto: encontrar el punto más cercano a una dirección, calcular rutas óptimas con extensiones como pgRouting.
  • Apps móviles: filtrar lugares en un radio del usuario.
  • Catastro y urbanismo: cruzar parcelas con normativa.
  • Agricultura de precisión: zonas de fertilización por características del suelo.
  • IoT y telemetría: trayectos de flotas, geofencing.
  • Análisis demográfico: superponer datos censales con ventas.

Combinación con consultas SQL clásicas

La fortaleza real de PostGIS es que las operaciones espaciales se mezclan con SQL normal. Una consulta típica podría ser:

SELECT
    t.nombre AS tienda,
    COUNT(p.id) AS pedidos_cercanos,
    SUM(p.total) AS facturacion
FROM tiendas t
LEFT JOIN pedidos p ON ST_DWithin(
    t.ubicacion,
    p.direccion_entrega,
    5000
)
WHERE p.creado_en >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY t.id, t.nombre
ORDER BY facturacion DESC NULLS LAST;

Esta query devuelve la facturación de los últimos 30 días por tienda, contando solo pedidos a menos de 5 km de cada tienda. Mezcla agregación, filtros temporales y geoespaciales en una sola consulta.

flowchart LR
    A[Cliente movil GPS] --> B[Coordenada lat lon]
    B --> C[ST_DWithin contra<br/>tiendas con indice GiST]
    C --> D[Top 5 mas cercanas]
    D --> E[Filtro por horario,<br/>stock, valoracion en SQL]
    E --> F[Resultado al usuario]

PostGIS es la diferencia entre un proyecto que escala con datos geoespaciales reales y uno que tiene que mantener un sistema separado para esos datos. Las funciones presentadas aquí cubren el 80 % de los casos de uso; el catálogo completo supera las 500 funciones para análisis avanzado.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, SQL es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de SQL

Explora más contenido relacionado con SQL y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Habilitar PostGIS y crear columnas geometry y geography. Insertar puntos, líneas y polígonos con ST_MakePoint y ST_GeomFromText. Calcular distancias con ST_Distance y ST_DWithin para puntos cercanos. Intersectar geometrías con ST_Intersects y ST_Within. Crear índices GiST para acelerar consultas espaciales. Distinguir cuándo usar geometry frente a geography.

Cursos que incluyen esta lección

Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje