Link meet de teoria: https://meet.google.com/auc-jmas-veo
Link meet de practica: https://meet.google.com/wcm-echz-byj
Link meet de teoria: https://meet.google.com/auc-jmas-veo
Link meet de practica: https://meet.google.com/wcm-echz-byj
Agenda
Fechas, entregas y avisos importantes de la materia en un solo lugar.
MES
Todos los eventos · Noviembre
Modo estudio
Elegí uno o varios bancos de preguntas y practicá en modo práctica (con explicaciones) o examen.
Practicá con bancos de preguntas en modo práctica (con explicaciones) o examen.
Recursos
Vista interactiva
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
Vista interactiva
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
Vamos a trabajar en un plano cartesiano artificial, con coordenadas simples. No son coordenadas reales, no son latitud/longitud y no vamos a usar SRID 4326. La idea es dibujar lo que creemos que va a pasar, ejecutar SQL, mirar el resultado y, cuando convenga, verlo en QGIS.
Crear una base para la práctica:
CREATE DATABASE bdg_practica;
Conectarse a esa base y activar PostGIS:
CREATE EXTENSION postgis;
Comprobar que está disponible:
SELECT PostGIS_Version();
PostGIS agrega tipos de datos espaciales y funciones para trabajar con geometrías dentro de PostgreSQL.
CREATE TABLE puntos (
id SERIAL PRIMARY KEY,
descripcion VARCHAR(50),
geom geometry(Point)
);
La columna geom guarda geometrías. En esta tabla sólo vamos a guardar puntos.
Insertamos el origen:
INSERT INTO puntos (descripcion, geom)
VALUES (
'Origen',
ST_GeomFromText('POINT(0 0)')
);
ST_GeomFromText crea una geometría a partir de WKT.
Consultar la tabla:
SELECT *
FROM puntos;
Ver la geometría en formato legible:
SELECT
id,
descripcion,
ST_AsText(geom) AS wkt
FROM puntos;
Resultado esperado:
1 | Origen | POINT(0 0)
ST_AsText muestra la geometría como texto WKT.
Antes de ejecutar:
¿Qué dimensión tiene un punto?
SELECT
descripcion,
ST_GeometryType(geom) AS tipo,
ST_Dimension(geom) AS dimension,
ST_IsEmpty(geom) AS esta_vacia,
ST_X(geom) AS x,
ST_Y(geom) AS y
FROM puntos
WHERE descripcion = 'Origen';
Resultado esperado:
Origen | ST_Point | 0 | false | 0 | 0
Un punto tiene dimensión 0. ST_X y ST_Y devuelven sus coordenadas.
Vamos a armar este plano:
Y+10
|
Y+5
|
X-10 -- X-5 -- Origen -- X+5 -- X+10
|
Y-5
|
Y-10
Insertar los puntos:
INSERT INTO puntos (descripcion, geom)
VALUES
('X-5', ST_GeomFromText('POINT(-5 0)')),
('X-10', ST_GeomFromText('POINT(-10 0)')),
('X+5', ST_GeomFromText('POINT(5 0)')),
('X+10', ST_GeomFromText('POINT(10 0)')),
('Y-5', ST_GeomFromText('POINT(0 -5)')),
('Y-10', ST_GeomFromText('POINT(0 -10)')),
('Y+5', ST_GeomFromText('POINT(0 5)')),
('Y+10', ST_GeomFromText('POINT(0 10)'));
Ver los puntos:
SELECT
descripcion,
ST_AsText(geom) AS wkt
FROM puntos
ORDER BY id;
Abrir QGIS, cargar la tabla puntos, activar etiquetas con descripcion y hacer zoom a la capa.
Antes de ejecutar:
¿Cuánto vale la distancia entre
(0,0)y(5,0)?
SELECT ST_Distance(
ST_GeomFromText('POINT(0 0)'),
ST_GeomFromText('POINT(5 0)')
) AS distancia;
Resultado esperado:
5
Ahora una diagonal:
SELECT ST_Distance(
ST_GeomFromText('POINT(0 0)'),
ST_GeomFromText('POINT(5 5)')
) AS distancia;
Resultado esperado aproximado:
7.0710678118654755
En este plano las unidades son unidades abstractas. Todavía no son metros ni grados.
Medir la distancia desde cada punto hasta (15,15):
SELECT
descripcion,
ST_AsText(geom) AS punto,
ST_Distance(
geom,
ST_GeomFromText('POINT(15 15)')
) AS distancia
FROM puntos
ORDER BY distancia;
Antes de mirar el resultado, pensar cuál debería quedar más cerca.
Ahora medir la distancia de cada punto con cada uno de los otros puntos:
SELECT
p1.descripcion AS punto_1,
ST_AsText(p1.geom) AS geom_1,
p2.descripcion AS punto_2,
ST_AsText(p2.geom) AS geom_2,
ST_Distance(p1.geom, p2.geom) AS distancia
FROM puntos p1
JOIN puntos p2
ON p1.id < p2.id
ORDER BY p1.id, p2.id;
La condición p1.id < p2.id evita dos cosas:
Por ejemplo, si ya aparece Origen contra X+5, no necesitamos también X+5 contra Origen.
Creamos una tabla para guardar líneas.
CREATE TABLE lineas (
id SERIAL PRIMARY KEY,
descripcion VARCHAR(50),
geom geometry(LineString)
);
Primero insertamos los ejes X e Y.
INSERT INTO lineas (descripcion, geom)
VALUES
('Eje X', ST_GeomFromText('LINESTRING(-15 0, 15 0)')),
('Eje Y', ST_GeomFromText('LINESTRING(0 -15, 0 15)'));
Los ejes se dibujan más largos que los puntos de referencia para que funcionen visualmente como ejes del plano, no como segmentos que terminan justo en -10 y 10.
Ver las líneas:
SELECT
descripcion,
ST_AsText(geom) AS wkt
FROM lineas
ORDER BY id;
Cargar lineas en QGIS.
Crear una vista con el plano base:
CREATE OR REPLACE VIEW vista_plano_base AS
SELECT
'Punto: ' || descripcion AS descripcion,
geom::geometry AS geom
FROM puntos
WHERE descripcion IN (
'Origen',
'X-5', 'X-10', 'X+5', 'X+10',
'Y-5', 'Y-10', 'Y+5', 'Y+10'
)
UNION ALL
SELECT
descripcion,
geom::geometry AS geom
FROM lineas
WHERE descripcion IN ('Eje X', 'Eje Y');
Esta vista junta los puntos de referencia iniciales y los dos ejes. Sirve para abrir el resultado en el visor geométrico o en QGIS y tener contexto visual.
SELECT
descripcion,
geom
FROM vista_plano_base;
Ahora agregamos una línea oblicua, como si fuera la gráfica de una función lineal sobre nuestro plano.
Antes de ejecutar:
Dibujá aproximadamente una recta que pase por
(-3,-10)y(8,10). ¿Corta al eje X? ¿Corta al eje Y?
INSERT INTO lineas (descripcion, geom)
VALUES (
'Oblicua',
ST_GeomFromText('LINESTRING(-3 -10, 8 10)')
);
Volver a consultar:
SELECT
descripcion,
ST_AsText(geom) AS wkt
FROM lineas
ORDER BY id;
Actualizar la capa lineas en QGIS o volver a cargarla para ver la nueva línea.
SELECT
descripcion,
ST_Length(geom) AS longitud,
ST_NumPoints(geom) AS cantidad_puntos,
ST_AsText(ST_StartPoint(geom)) AS inicio,
ST_AsText(ST_EndPoint(geom)) AS fin
FROM lineas
ORDER BY id;
ST_Length mide la línea. ST_StartPoint y ST_EndPoint devuelven geometrías.
Ver los vértices:
SELECT
descripcion,
ST_AsText(ST_PointN(geom, 1)) AS primer_punto,
ST_AsText(ST_PointN(geom, 2)) AS segundo_punto
FROM lineas
ORDER BY id;
ST_PointN recupera el punto número N de una línea.
Antes de ejecutar:
¿La línea oblicua corta al eje X?
SELECT ST_Intersects(l1.geom, l2.geom) AS se_intersectan
FROM lineas l1
JOIN lineas l2
ON l1.descripcion = 'Oblicua'
AND l2.descripcion = 'Eje X';
Resultado esperado:
true
Probar todas las líneas contra todas:
SELECT
l1.descripcion AS linea_1,
l2.descripcion AS linea_2,
ST_Intersects(l1.geom, l2.geom) AS intersects,
ST_Disjoint(l1.geom, l2.geom) AS disjoint,
ST_Touches(l1.geom, l2.geom) AS touches,
ST_Crosses(l1.geom, l2.geom) AS crosses,
ST_Equals(l1.geom, l2.geom) AS equals
FROM lineas l1
JOIN lineas l2
ON l1.id < l2.id
ORDER BY l1.id, l2.id;
Estas funciones devuelven true o false.
Mini machete:
| Función | Pregunta que responde |
|---|---|
ST_Intersects | ¿Tienen algún punto en común? |
ST_Disjoint | ¿No tienen ningún punto en común? |
ST_Touches | ¿Se tocan en el borde, sin cruzarse por dentro? |
ST_Crosses | ¿Una geometría cruza a la otra? |
ST_Equals | ¿Representan exactamente la misma geometría? |
Ojo: ST_Disjoint es lo contrario de ST_Intersects. Si una da true, la otra debería dar false.
Primero preguntamos si se intersectan:
SELECT ST_Intersects(l1.geom, l2.geom) AS se_intersectan
FROM lineas l1
JOIN lineas l2
ON l1.descripcion = 'Oblicua'
AND l2.descripcion = 'Eje X';
Eso devuelve un booleano.
Ahora preguntamos dónde se intersectan:
SELECT
ST_AsText(ST_Intersection(l1.geom, l2.geom)) AS interseccion
FROM lineas l1
JOIN lineas l2
ON l1.descripcion = 'Oblicua'
AND l2.descripcion = 'Eje X';
Resultado esperado:
POINT(2.5 0)
Para verlo con contexto, consultar el plano base junto con el punto de intersección:
SELECT
descripcion,
geom
FROM vista_plano_base
UNION ALL
SELECT
'Intersección oblicua / eje X' AS descripcion,
ST_Intersection(l1.geom, l2.geom)::geometry AS geom
FROM lineas l1
JOIN lineas l2
ON l1.descripcion = 'Oblicua'
AND l2.descripcion = 'Eje X'
UNION ALL
SELECT
descripcion,
geom
FROM lineas
WHERE descripcion = 'Oblicua';
En el visor geométrico ahora deberían verse:
POINT(2.5 0).Ese punto debería aparecer exactamente sobre el eje X y también sobre la línea oblicua. La consulta usa UNION ALL para mostrar todas esas geometrías juntas sin perderlas como objetos separados.
| Función | Devuelve |
|---|---|
ST_Intersects | true / false |
ST_Intersection | una geometría |
Esta diferencia es clave.
Antes de ejecutar:
¿Qué forma esperás al hacer un buffer de 2 unidades alrededor del origen?
CREATE TABLE buffers (
id SERIAL PRIMARY KEY,
descripcion VARCHAR(80),
geom geometry
);
INSERT INTO buffers (descripcion, geom)
SELECT
'Buffer de 2 unidades alrededor del origen',
ST_Buffer(geom, 2)
FROM puntos
WHERE descripcion = 'Origen';
Ver el tipo de geometría generado:
SELECT
descripcion,
ST_GeometryType(geom) AS tipo
FROM buffers;
ST_Buffer genera una nueva geometría.
Ahora un buffer alrededor del eje X:
INSERT INTO buffers (descripcion, geom)
SELECT
'Buffer de 1 unidad alrededor del eje X',
ST_Buffer(geom, 1)
FROM lineas
WHERE descripcion = 'Eje X';
Para visualizar las geometrías originales junto con sus buffers:
SELECT
'Original: ' || descripcion AS descripcion,
geom
FROM puntos
WHERE descripcion = 'Origen'
UNION ALL
SELECT
'Original: ' || descripcion AS descripcion,
geom
FROM lineas
WHERE descripcion = 'Eje X'
UNION ALL
SELECT
descripcion,
geom
FROM buffers
ORDER BY descripcion;
En el visor geométrico deberían verse el punto Origen, el Eje X y los dos buffers generados. La idea es comparar cada geometría original con la zona que produce ST_Buffer.
Cargar buffers en QGIS y usar transparencia para ver las capas anteriores.
CREATE TABLE poligonos (
id SERIAL PRIMARY KEY,
descripcion VARCHAR(50),
geom geometry(Polygon)
);
Dibujar tres polígonos:
INSERT INTO poligonos (descripcion, geom)
VALUES
('A', ST_GeomFromText('POLYGON((0 0, 6 0, 6 6, 0 6, 0 0))')),
('B', ST_GeomFromText('POLYGON((4 2, 10 2, 10 8, 4 8, 4 2))')),
('C', ST_GeomFromText('POLYGON((1 1, 2 1, 2 2, 1 2, 1 1))'));
Verlos:
SELECT
descripcion,
ST_AsText(geom) AS wkt
FROM poligonos
ORDER BY descripcion;
Cargar poligonos en QGIS con relleno transparente.
Antes de ejecutar:
¿A contiene a C?
SELECT ST_Contains(a.geom, c.geom) AS a_contiene_c
FROM poligonos a
JOIN poligonos c
ON a.descripcion = 'A'
AND c.descripcion = 'C';
Resultado esperado:
true
Ahora la relación inversa:
SELECT ST_Within(c.geom, a.geom) AS c_dentro_de_a
FROM poligonos c
JOIN poligonos a
ON c.descripcion = 'C'
AND a.descripcion = 'A';
Resultado esperado:
true
Ahora comparamos A contra B y A contra C en una sola consulta:
SELECT
ST_Overlaps(a.geom, b.geom) AS a_b_se_superponen,
ST_Intersects(a.geom, b.geom) AS a_b_se_intersectan,
ST_Disjoint(a.geom, b.geom) AS a_b_son_disjuntos,
ST_Overlaps(a.geom, c.geom) AS a_c_se_superponen,
ST_Intersects(a.geom, c.geom) AS a_c_se_intersectan,
ST_Contains(a.geom, c.geom) AS a_contiene_c
FROM poligonos a
JOIN poligonos b
ON b.descripcion = 'B'
JOIN poligonos c
ON c.descripcion = 'C'
WHERE a.descripcion = 'A';
Resultado esperado:
true | true | false | false | true | true
A y B se superponen parcialmente. A y C no se superponen parcialmente: C está completamente dentro de A.
Agregamos un polígono D que toca a A por un borde.
INSERT INTO poligonos (descripcion, geom)
VALUES (
'D',
ST_GeomFromText('POLYGON((6 0, 9 0, 9 3, 6 3, 6 0))')
);
Antes de ejecutar:
¿D se superpone con A o sólo lo toca?
SELECT
ST_Touches(a.geom, d.geom) AS toca,
ST_Intersects(a.geom, d.geom) AS intersecta,
ST_Overlaps(a.geom, d.geom) AS se_superpone
FROM poligonos a
JOIN poligonos d
ON a.descripcion = 'A'
AND d.descripcion = 'D';
Resultado esperado:
true | true | false
Ahora probamos D contra B:
SELECT
ST_Touches(d.geom, b.geom) AS toca,
ST_Intersects(d.geom, b.geom) AS intersecta,
ST_Overlaps(d.geom, b.geom) AS se_superpone
FROM poligonos d
JOIN poligonos b
ON d.descripcion = 'D'
AND b.descripcion = 'B';
Resultado esperado:
false | true | true
D y B no sólo se tocan: comparten una región. Por eso ST_Overlaps da true.
Si dos geometrías se tocan, también se intersectan. Pero tocar no es lo mismo que superponerse.
CREATE TABLE resultados_poligonales (
id SERIAL PRIMARY KEY,
descripcion VARCHAR(80),
geom geometry
);
Antes de ejecutar, sombrear la zona común entre A y B.
INSERT INTO resultados_poligonales (descripcion, geom)
SELECT
'Intersección entre A y B',
ST_Intersection(a.geom, b.geom)
FROM poligonos a
JOIN poligonos b
ON a.descripcion = 'A'
AND b.descripcion = 'B';
SELECT
descripcion,
ST_AsText(geom) AS wkt
FROM resultados_poligonales
WHERE descripcion = 'Intersección entre A y B';
Antes de ejecutar, sombrear toda la región cubierta por A o por B.
INSERT INTO resultados_poligonales (descripcion, geom)
SELECT
'Unión entre A y B',
ST_Union(a.geom, b.geom)
FROM poligonos a
JOIN poligonos b
ON a.descripcion = 'A'
AND b.descripcion = 'B';
SELECT
descripcion,
ST_GeometryType(geom) AS tipo,
ST_AsText(geom) AS wkt
FROM resultados_poligonales
WHERE descripcion = 'Unión entre A y B';
Antes de ejecutar, sombrear la parte de A que no está cubierta por B.
INSERT INTO resultados_poligonales (descripcion, geom)
SELECT
'Diferencia A menos B',
ST_Difference(a.geom, b.geom)
FROM poligonos a
JOIN poligonos b
ON a.descripcion = 'A'
AND b.descripcion = 'B';
SELECT
descripcion,
ST_AsText(geom) AS wkt
FROM resultados_poligonales
WHERE descripcion = 'Diferencia A menos B';
Cargar resultados_poligonales en QGIS y comparar con A y B.
Antes usamos UNION ALL para mostrar varias geometrías juntas sin fusionarlas. Ahora usamos ST_Union para unir espacialmente todas las geometrías de vista_plano_base en una sola geometría.
SELECT
'Plano base unido' AS descripcion,
ST_Union(geom) AS geom
FROM vista_plano_base;
Ahora intersectamos la línea oblicua contra ese plano base unido y lo mostramos todo junto:
SELECT
descripcion,
geom
FROM vista_plano_base
UNION ALL
SELECT
descripcion,
geom
FROM lineas
WHERE descripcion = 'Oblicua'
UNION ALL
SELECT
'Intersección oblicua / plano base unido' AS descripcion,
ST_Intersection(l.geom, plano.geom)::geometry AS geom
FROM lineas l
CROSS JOIN (
SELECT ST_Union(geom) AS geom
FROM vista_plano_base
) plano
WHERE l.descripcion = 'Oblicua';
Resultado esperado: deberían verse el plano base, la oblicua y dos puntos de intersección, porque la oblicua corta al eje X y también al eje Y.
Para leerlos como texto:
SELECT
ST_AsText(
ST_Intersection(l.geom, plano.geom)
) AS interseccion
FROM lineas l
CROSS JOIN (
SELECT ST_Union(geom) AS geom
FROM vista_plano_base
) plano
WHERE l.descripcion = 'Oblicua';
La diferencia importante:
| Operación | Qué hace |
|---|---|
UNION ALL | Junta filas en una consulta |
ST_Union | Une geometrías espacialmente |
Volvemos a los puntos.
Antes de ejecutar:
¿Cuál sería el polígono convexo más chico que contiene todos los puntos?
SELECT
ST_AsText(
ST_ConvexHull(
ST_Collect(geom)
)
) AS convex_hull
FROM puntos;
ST_Collect agrupa los puntos. ST_ConvexHull calcula el polígono convexo que los envuelve.
Guardar el resultado:
INSERT INTO resultados_poligonales (descripcion, geom)
SELECT
'Convex hull de los puntos',
ST_ConvexHull(ST_Collect(geom))
FROM puntos;
Verlo en QGIS.
ST_Envelope devuelve el rectángulo mínimo alineado a los ejes que contiene una geometría.
Antes de ejecutar:
Si los puntos llegan hasta
-10y10en los ejes, ¿qué rectángulo esperás obtener?
SELECT
ST_AsText(
ST_Envelope(
ST_Collect(geom)
)
) AS envelope
FROM puntos;
Guardar el resultado:
INSERT INTO resultados_poligonales (descripcion, geom)
SELECT
'Envelope de los puntos',
ST_Envelope(ST_Collect(geom))
FROM puntos;
Ver juntos Convex hull y Envelope:
SELECT
descripcion,
geom
FROM resultados_poligonales
WHERE descripcion IN (
'Convex hull de los puntos',
'Envelope de los puntos'
);
El Convex Hull se ajusta a la forma convexa más chica. El Envelope siempre devuelve un rectángulo alineado a los ejes.
Una línea puede tener muchos vértices y seguir siendo una sola LINESTRING.
INSERT INTO lineas (descripcion, geom)
VALUES (
'Línea con varios vértices',
ST_GeomFromText(
'LINESTRING(1 1, 2 5, 3 4, 4 8, 5 9, 6 3, 7 5, 8 7, 9 6, 10 10)'
)
);
SELECT
descripcion,
ST_GeometryType(geom) AS tipo,
ST_NumPoints(geom) AS vertices,
ST_AsText(geom) AS wkt
FROM lineas
WHERE descripcion = 'Línea con varios vértices';
Aunque tenga muchos puntos, sigue siendo una única línea.
Ahora creamos una geometría múltiple:
CREATE TABLE multilines (
id SERIAL PRIMARY KEY,
descripcion VARCHAR(50),
geom geometry(MultiLineString)
);
INSERT INTO multilines (descripcion, geom)
VALUES (
'Dos líneas independientes',
ST_GeomFromText('MULTILINESTRING((0 0, 5 5), (0 5, 5 0))')
);
SELECT
descripcion,
ST_GeometryType(geom) AS tipo,
ST_AsText(geom) AS wkt
FROM multilines;
MULTILINESTRING contiene varias líneas independientes.
Vista interactiva
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
Vista interactiva
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
Archivo de Drive
Abrir en DriveArchivo de Drive
Abrir en DriveVista interactiva
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
By ChatGPT 5.5 High
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
Apariencia alternativa
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
by Claude Fable 5
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
Vista interactiva
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
Ya viste todos los apuntes disponibles.
Link meet de teoria: https://meet.google.com/auc-jmas-veo
Link meet de practica: https://meet.google.com/wcm-echz-byj
Link meet de teoria: https://meet.google.com/auc-jmas-veo
Link meet de practica: https://meet.google.com/wcm-echz-byj
Agenda
Fechas, entregas y avisos importantes de la materia en un solo lugar.
MES
Todos los eventos · Noviembre
Modo estudio
Elegí uno o varios bancos de preguntas y practicá en modo práctica (con explicaciones) o examen.
Practicá con bancos de preguntas en modo práctica (con explicaciones) o examen.
Recursos
Vista interactiva
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
Vista interactiva
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
Vamos a trabajar en un plano cartesiano artificial, con coordenadas simples. No son coordenadas reales, no son latitud/longitud y no vamos a usar SRID 4326. La idea es dibujar lo que creemos que va a pasar, ejecutar SQL, mirar el resultado y, cuando convenga, verlo en QGIS.
Crear una base para la práctica:
CREATE DATABASE bdg_practica;
Conectarse a esa base y activar PostGIS:
CREATE EXTENSION postgis;
Comprobar que está disponible:
SELECT PostGIS_Version();
PostGIS agrega tipos de datos espaciales y funciones para trabajar con geometrías dentro de PostgreSQL.
CREATE TABLE puntos (
id SERIAL PRIMARY KEY,
descripcion VARCHAR(50),
geom geometry(Point)
);
La columna geom guarda geometrías. En esta tabla sólo vamos a guardar puntos.
Insertamos el origen:
INSERT INTO puntos (descripcion, geom)
VALUES (
'Origen',
ST_GeomFromText('POINT(0 0)')
);
ST_GeomFromText crea una geometría a partir de WKT.
Consultar la tabla:
SELECT *
FROM puntos;
Ver la geometría en formato legible:
SELECT
id,
descripcion,
ST_AsText(geom) AS wkt
FROM puntos;
Resultado esperado:
1 | Origen | POINT(0 0)
ST_AsText muestra la geometría como texto WKT.
Antes de ejecutar:
¿Qué dimensión tiene un punto?
SELECT
descripcion,
ST_GeometryType(geom) AS tipo,
ST_Dimension(geom) AS dimension,
ST_IsEmpty(geom) AS esta_vacia,
ST_X(geom) AS x,
ST_Y(geom) AS y
FROM puntos
WHERE descripcion = 'Origen';
Resultado esperado:
Origen | ST_Point | 0 | false | 0 | 0
Un punto tiene dimensión 0. ST_X y ST_Y devuelven sus coordenadas.
Vamos a armar este plano:
Y+10
|
Y+5
|
X-10 -- X-5 -- Origen -- X+5 -- X+10
|
Y-5
|
Y-10
Insertar los puntos:
INSERT INTO puntos (descripcion, geom)
VALUES
('X-5', ST_GeomFromText('POINT(-5 0)')),
('X-10', ST_GeomFromText('POINT(-10 0)')),
('X+5', ST_GeomFromText('POINT(5 0)')),
('X+10', ST_GeomFromText('POINT(10 0)')),
('Y-5', ST_GeomFromText('POINT(0 -5)')),
('Y-10', ST_GeomFromText('POINT(0 -10)')),
('Y+5', ST_GeomFromText('POINT(0 5)')),
('Y+10', ST_GeomFromText('POINT(0 10)'));
Ver los puntos:
SELECT
descripcion,
ST_AsText(geom) AS wkt
FROM puntos
ORDER BY id;
Abrir QGIS, cargar la tabla puntos, activar etiquetas con descripcion y hacer zoom a la capa.
Antes de ejecutar:
¿Cuánto vale la distancia entre
(0,0)y(5,0)?
SELECT ST_Distance(
ST_GeomFromText('POINT(0 0)'),
ST_GeomFromText('POINT(5 0)')
) AS distancia;
Resultado esperado:
5
Ahora una diagonal:
SELECT ST_Distance(
ST_GeomFromText('POINT(0 0)'),
ST_GeomFromText('POINT(5 5)')
) AS distancia;
Resultado esperado aproximado:
7.0710678118654755
En este plano las unidades son unidades abstractas. Todavía no son metros ni grados.
Medir la distancia desde cada punto hasta (15,15):
SELECT
descripcion,
ST_AsText(geom) AS punto,
ST_Distance(
geom,
ST_GeomFromText('POINT(15 15)')
) AS distancia
FROM puntos
ORDER BY distancia;
Antes de mirar el resultado, pensar cuál debería quedar más cerca.
Ahora medir la distancia de cada punto con cada uno de los otros puntos:
SELECT
p1.descripcion AS punto_1,
ST_AsText(p1.geom) AS geom_1,
p2.descripcion AS punto_2,
ST_AsText(p2.geom) AS geom_2,
ST_Distance(p1.geom, p2.geom) AS distancia
FROM puntos p1
JOIN puntos p2
ON p1.id < p2.id
ORDER BY p1.id, p2.id;
La condición p1.id < p2.id evita dos cosas:
Por ejemplo, si ya aparece Origen contra X+5, no necesitamos también X+5 contra Origen.
Creamos una tabla para guardar líneas.
CREATE TABLE lineas (
id SERIAL PRIMARY KEY,
descripcion VARCHAR(50),
geom geometry(LineString)
);
Primero insertamos los ejes X e Y.
INSERT INTO lineas (descripcion, geom)
VALUES
('Eje X', ST_GeomFromText('LINESTRING(-15 0, 15 0)')),
('Eje Y', ST_GeomFromText('LINESTRING(0 -15, 0 15)'));
Los ejes se dibujan más largos que los puntos de referencia para que funcionen visualmente como ejes del plano, no como segmentos que terminan justo en -10 y 10.
Ver las líneas:
SELECT
descripcion,
ST_AsText(geom) AS wkt
FROM lineas
ORDER BY id;
Cargar lineas en QGIS.
Crear una vista con el plano base:
CREATE OR REPLACE VIEW vista_plano_base AS
SELECT
'Punto: ' || descripcion AS descripcion,
geom::geometry AS geom
FROM puntos
WHERE descripcion IN (
'Origen',
'X-5', 'X-10', 'X+5', 'X+10',
'Y-5', 'Y-10', 'Y+5', 'Y+10'
)
UNION ALL
SELECT
descripcion,
geom::geometry AS geom
FROM lineas
WHERE descripcion IN ('Eje X', 'Eje Y');
Esta vista junta los puntos de referencia iniciales y los dos ejes. Sirve para abrir el resultado en el visor geométrico o en QGIS y tener contexto visual.
SELECT
descripcion,
geom
FROM vista_plano_base;
Ahora agregamos una línea oblicua, como si fuera la gráfica de una función lineal sobre nuestro plano.
Antes de ejecutar:
Dibujá aproximadamente una recta que pase por
(-3,-10)y(8,10). ¿Corta al eje X? ¿Corta al eje Y?
INSERT INTO lineas (descripcion, geom)
VALUES (
'Oblicua',
ST_GeomFromText('LINESTRING(-3 -10, 8 10)')
);
Volver a consultar:
SELECT
descripcion,
ST_AsText(geom) AS wkt
FROM lineas
ORDER BY id;
Actualizar la capa lineas en QGIS o volver a cargarla para ver la nueva línea.
SELECT
descripcion,
ST_Length(geom) AS longitud,
ST_NumPoints(geom) AS cantidad_puntos,
ST_AsText(ST_StartPoint(geom)) AS inicio,
ST_AsText(ST_EndPoint(geom)) AS fin
FROM lineas
ORDER BY id;
ST_Length mide la línea. ST_StartPoint y ST_EndPoint devuelven geometrías.
Ver los vértices:
SELECT
descripcion,
ST_AsText(ST_PointN(geom, 1)) AS primer_punto,
ST_AsText(ST_PointN(geom, 2)) AS segundo_punto
FROM lineas
ORDER BY id;
ST_PointN recupera el punto número N de una línea.
Antes de ejecutar:
¿La línea oblicua corta al eje X?
SELECT ST_Intersects(l1.geom, l2.geom) AS se_intersectan
FROM lineas l1
JOIN lineas l2
ON l1.descripcion = 'Oblicua'
AND l2.descripcion = 'Eje X';
Resultado esperado:
true
Probar todas las líneas contra todas:
SELECT
l1.descripcion AS linea_1,
l2.descripcion AS linea_2,
ST_Intersects(l1.geom, l2.geom) AS intersects,
ST_Disjoint(l1.geom, l2.geom) AS disjoint,
ST_Touches(l1.geom, l2.geom) AS touches,
ST_Crosses(l1.geom, l2.geom) AS crosses,
ST_Equals(l1.geom, l2.geom) AS equals
FROM lineas l1
JOIN lineas l2
ON l1.id < l2.id
ORDER BY l1.id, l2.id;
Estas funciones devuelven true o false.
Mini machete:
| Función | Pregunta que responde |
|---|---|
ST_Intersects | ¿Tienen algún punto en común? |
ST_Disjoint | ¿No tienen ningún punto en común? |
ST_Touches | ¿Se tocan en el borde, sin cruzarse por dentro? |
ST_Crosses | ¿Una geometría cruza a la otra? |
ST_Equals | ¿Representan exactamente la misma geometría? |
Ojo: ST_Disjoint es lo contrario de ST_Intersects. Si una da true, la otra debería dar false.
Primero preguntamos si se intersectan:
SELECT ST_Intersects(l1.geom, l2.geom) AS se_intersectan
FROM lineas l1
JOIN lineas l2
ON l1.descripcion = 'Oblicua'
AND l2.descripcion = 'Eje X';
Eso devuelve un booleano.
Ahora preguntamos dónde se intersectan:
SELECT
ST_AsText(ST_Intersection(l1.geom, l2.geom)) AS interseccion
FROM lineas l1
JOIN lineas l2
ON l1.descripcion = 'Oblicua'
AND l2.descripcion = 'Eje X';
Resultado esperado:
POINT(2.5 0)
Para verlo con contexto, consultar el plano base junto con el punto de intersección:
SELECT
descripcion,
geom
FROM vista_plano_base
UNION ALL
SELECT
'Intersección oblicua / eje X' AS descripcion,
ST_Intersection(l1.geom, l2.geom)::geometry AS geom
FROM lineas l1
JOIN lineas l2
ON l1.descripcion = 'Oblicua'
AND l2.descripcion = 'Eje X'
UNION ALL
SELECT
descripcion,
geom
FROM lineas
WHERE descripcion = 'Oblicua';
En el visor geométrico ahora deberían verse:
POINT(2.5 0).Ese punto debería aparecer exactamente sobre el eje X y también sobre la línea oblicua. La consulta usa UNION ALL para mostrar todas esas geometrías juntas sin perderlas como objetos separados.
| Función | Devuelve |
|---|---|
ST_Intersects | true / false |
ST_Intersection | una geometría |
Esta diferencia es clave.
Antes de ejecutar:
¿Qué forma esperás al hacer un buffer de 2 unidades alrededor del origen?
CREATE TABLE buffers (
id SERIAL PRIMARY KEY,
descripcion VARCHAR(80),
geom geometry
);
INSERT INTO buffers (descripcion, geom)
SELECT
'Buffer de 2 unidades alrededor del origen',
ST_Buffer(geom, 2)
FROM puntos
WHERE descripcion = 'Origen';
Ver el tipo de geometría generado:
SELECT
descripcion,
ST_GeometryType(geom) AS tipo
FROM buffers;
ST_Buffer genera una nueva geometría.
Ahora un buffer alrededor del eje X:
INSERT INTO buffers (descripcion, geom)
SELECT
'Buffer de 1 unidad alrededor del eje X',
ST_Buffer(geom, 1)
FROM lineas
WHERE descripcion = 'Eje X';
Para visualizar las geometrías originales junto con sus buffers:
SELECT
'Original: ' || descripcion AS descripcion,
geom
FROM puntos
WHERE descripcion = 'Origen'
UNION ALL
SELECT
'Original: ' || descripcion AS descripcion,
geom
FROM lineas
WHERE descripcion = 'Eje X'
UNION ALL
SELECT
descripcion,
geom
FROM buffers
ORDER BY descripcion;
En el visor geométrico deberían verse el punto Origen, el Eje X y los dos buffers generados. La idea es comparar cada geometría original con la zona que produce ST_Buffer.
Cargar buffers en QGIS y usar transparencia para ver las capas anteriores.
CREATE TABLE poligonos (
id SERIAL PRIMARY KEY,
descripcion VARCHAR(50),
geom geometry(Polygon)
);
Dibujar tres polígonos:
INSERT INTO poligonos (descripcion, geom)
VALUES
('A', ST_GeomFromText('POLYGON((0 0, 6 0, 6 6, 0 6, 0 0))')),
('B', ST_GeomFromText('POLYGON((4 2, 10 2, 10 8, 4 8, 4 2))')),
('C', ST_GeomFromText('POLYGON((1 1, 2 1, 2 2, 1 2, 1 1))'));
Verlos:
SELECT
descripcion,
ST_AsText(geom) AS wkt
FROM poligonos
ORDER BY descripcion;
Cargar poligonos en QGIS con relleno transparente.
Antes de ejecutar:
¿A contiene a C?
SELECT ST_Contains(a.geom, c.geom) AS a_contiene_c
FROM poligonos a
JOIN poligonos c
ON a.descripcion = 'A'
AND c.descripcion = 'C';
Resultado esperado:
true
Ahora la relación inversa:
SELECT ST_Within(c.geom, a.geom) AS c_dentro_de_a
FROM poligonos c
JOIN poligonos a
ON c.descripcion = 'C'
AND a.descripcion = 'A';
Resultado esperado:
true
Ahora comparamos A contra B y A contra C en una sola consulta:
SELECT
ST_Overlaps(a.geom, b.geom) AS a_b_se_superponen,
ST_Intersects(a.geom, b.geom) AS a_b_se_intersectan,
ST_Disjoint(a.geom, b.geom) AS a_b_son_disjuntos,
ST_Overlaps(a.geom, c.geom) AS a_c_se_superponen,
ST_Intersects(a.geom, c.geom) AS a_c_se_intersectan,
ST_Contains(a.geom, c.geom) AS a_contiene_c
FROM poligonos a
JOIN poligonos b
ON b.descripcion = 'B'
JOIN poligonos c
ON c.descripcion = 'C'
WHERE a.descripcion = 'A';
Resultado esperado:
true | true | false | false | true | true
A y B se superponen parcialmente. A y C no se superponen parcialmente: C está completamente dentro de A.
Agregamos un polígono D que toca a A por un borde.
INSERT INTO poligonos (descripcion, geom)
VALUES (
'D',
ST_GeomFromText('POLYGON((6 0, 9 0, 9 3, 6 3, 6 0))')
);
Antes de ejecutar:
¿D se superpone con A o sólo lo toca?
SELECT
ST_Touches(a.geom, d.geom) AS toca,
ST_Intersects(a.geom, d.geom) AS intersecta,
ST_Overlaps(a.geom, d.geom) AS se_superpone
FROM poligonos a
JOIN poligonos d
ON a.descripcion = 'A'
AND d.descripcion = 'D';
Resultado esperado:
true | true | false
Ahora probamos D contra B:
SELECT
ST_Touches(d.geom, b.geom) AS toca,
ST_Intersects(d.geom, b.geom) AS intersecta,
ST_Overlaps(d.geom, b.geom) AS se_superpone
FROM poligonos d
JOIN poligonos b
ON d.descripcion = 'D'
AND b.descripcion = 'B';
Resultado esperado:
false | true | true
D y B no sólo se tocan: comparten una región. Por eso ST_Overlaps da true.
Si dos geometrías se tocan, también se intersectan. Pero tocar no es lo mismo que superponerse.
CREATE TABLE resultados_poligonales (
id SERIAL PRIMARY KEY,
descripcion VARCHAR(80),
geom geometry
);
Antes de ejecutar, sombrear la zona común entre A y B.
INSERT INTO resultados_poligonales (descripcion, geom)
SELECT
'Intersección entre A y B',
ST_Intersection(a.geom, b.geom)
FROM poligonos a
JOIN poligonos b
ON a.descripcion = 'A'
AND b.descripcion = 'B';
SELECT
descripcion,
ST_AsText(geom) AS wkt
FROM resultados_poligonales
WHERE descripcion = 'Intersección entre A y B';
Antes de ejecutar, sombrear toda la región cubierta por A o por B.
INSERT INTO resultados_poligonales (descripcion, geom)
SELECT
'Unión entre A y B',
ST_Union(a.geom, b.geom)
FROM poligonos a
JOIN poligonos b
ON a.descripcion = 'A'
AND b.descripcion = 'B';
SELECT
descripcion,
ST_GeometryType(geom) AS tipo,
ST_AsText(geom) AS wkt
FROM resultados_poligonales
WHERE descripcion = 'Unión entre A y B';
Antes de ejecutar, sombrear la parte de A que no está cubierta por B.
INSERT INTO resultados_poligonales (descripcion, geom)
SELECT
'Diferencia A menos B',
ST_Difference(a.geom, b.geom)
FROM poligonos a
JOIN poligonos b
ON a.descripcion = 'A'
AND b.descripcion = 'B';
SELECT
descripcion,
ST_AsText(geom) AS wkt
FROM resultados_poligonales
WHERE descripcion = 'Diferencia A menos B';
Cargar resultados_poligonales en QGIS y comparar con A y B.
Antes usamos UNION ALL para mostrar varias geometrías juntas sin fusionarlas. Ahora usamos ST_Union para unir espacialmente todas las geometrías de vista_plano_base en una sola geometría.
SELECT
'Plano base unido' AS descripcion,
ST_Union(geom) AS geom
FROM vista_plano_base;
Ahora intersectamos la línea oblicua contra ese plano base unido y lo mostramos todo junto:
SELECT
descripcion,
geom
FROM vista_plano_base
UNION ALL
SELECT
descripcion,
geom
FROM lineas
WHERE descripcion = 'Oblicua'
UNION ALL
SELECT
'Intersección oblicua / plano base unido' AS descripcion,
ST_Intersection(l.geom, plano.geom)::geometry AS geom
FROM lineas l
CROSS JOIN (
SELECT ST_Union(geom) AS geom
FROM vista_plano_base
) plano
WHERE l.descripcion = 'Oblicua';
Resultado esperado: deberían verse el plano base, la oblicua y dos puntos de intersección, porque la oblicua corta al eje X y también al eje Y.
Para leerlos como texto:
SELECT
ST_AsText(
ST_Intersection(l.geom, plano.geom)
) AS interseccion
FROM lineas l
CROSS JOIN (
SELECT ST_Union(geom) AS geom
FROM vista_plano_base
) plano
WHERE l.descripcion = 'Oblicua';
La diferencia importante:
| Operación | Qué hace |
|---|---|
UNION ALL | Junta filas en una consulta |
ST_Union | Une geometrías espacialmente |
Volvemos a los puntos.
Antes de ejecutar:
¿Cuál sería el polígono convexo más chico que contiene todos los puntos?
SELECT
ST_AsText(
ST_ConvexHull(
ST_Collect(geom)
)
) AS convex_hull
FROM puntos;
ST_Collect agrupa los puntos. ST_ConvexHull calcula el polígono convexo que los envuelve.
Guardar el resultado:
INSERT INTO resultados_poligonales (descripcion, geom)
SELECT
'Convex hull de los puntos',
ST_ConvexHull(ST_Collect(geom))
FROM puntos;
Verlo en QGIS.
ST_Envelope devuelve el rectángulo mínimo alineado a los ejes que contiene una geometría.
Antes de ejecutar:
Si los puntos llegan hasta
-10y10en los ejes, ¿qué rectángulo esperás obtener?
SELECT
ST_AsText(
ST_Envelope(
ST_Collect(geom)
)
) AS envelope
FROM puntos;
Guardar el resultado:
INSERT INTO resultados_poligonales (descripcion, geom)
SELECT
'Envelope de los puntos',
ST_Envelope(ST_Collect(geom))
FROM puntos;
Ver juntos Convex hull y Envelope:
SELECT
descripcion,
geom
FROM resultados_poligonales
WHERE descripcion IN (
'Convex hull de los puntos',
'Envelope de los puntos'
);
El Convex Hull se ajusta a la forma convexa más chica. El Envelope siempre devuelve un rectángulo alineado a los ejes.
Una línea puede tener muchos vértices y seguir siendo una sola LINESTRING.
INSERT INTO lineas (descripcion, geom)
VALUES (
'Línea con varios vértices',
ST_GeomFromText(
'LINESTRING(1 1, 2 5, 3 4, 4 8, 5 9, 6 3, 7 5, 8 7, 9 6, 10 10)'
)
);
SELECT
descripcion,
ST_GeometryType(geom) AS tipo,
ST_NumPoints(geom) AS vertices,
ST_AsText(geom) AS wkt
FROM lineas
WHERE descripcion = 'Línea con varios vértices';
Aunque tenga muchos puntos, sigue siendo una única línea.
Ahora creamos una geometría múltiple:
CREATE TABLE multilines (
id SERIAL PRIMARY KEY,
descripcion VARCHAR(50),
geom geometry(MultiLineString)
);
INSERT INTO multilines (descripcion, geom)
VALUES (
'Dos líneas independientes',
ST_GeomFromText('MULTILINESTRING((0 0, 5 5), (0 5, 5 0))')
);
SELECT
descripcion,
ST_GeometryType(geom) AS tipo,
ST_AsText(geom) AS wkt
FROM multilines;
MULTILINESTRING contiene varias líneas independientes.
Vista interactiva
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
Vista interactiva
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
Archivo de Drive
Abrir en DriveArchivo de Drive
Abrir en DriveVista interactiva
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
By ChatGPT 5.5 High
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
Apariencia alternativa
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
by Claude Fable 5
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
Vista interactiva
Abrir apunteApunte interactivo
Tocá para abrir la vista previa.
Ya viste todos los apuntes disponibles.
Resumen
Resumen