Este artículo pretende mostrar de qué manera se puede realizar un join espacial en PostGIS, una de las operaciones más habituales y que cualquier profesional GIS debe conocer y dominar.
El empleo de esta técnica es tremendamente útil al trabajar en PostGIS o en cualquier software GIS de escritorio. Se trata de un proceso utilizado de forma habitual ya que cubre una necesidad básica, que es requerida de forma reiterada en muchos proyectos y tareas vinculadas al análisis de datos espaciales.
Como veremos, existen distintas maneras de llevarlo a cabo, tanto por el modo de construir la consulta SQL como por la función espacial a utilizar.
¿Qué es un join espacial?
Un join espacial o unión espacial (en inglés spatial join) es una operación que se basa en la combinación de dos o más conjuntos de datos espaciales mediante su relación espacial.
A diferencia de un join tradicional o alfanumérico que utiliza claves o identificadores comunes entre tablas para relacionarlas, un join espacial utiliza una o varias funciones espaciales para determinar la relación espacial entre sus objetos. Estas funciones espaciales incluyen operaciones como intersección, contención, proximidad, cruce, contacto, entre otras.
El join espacial permite permite recuperar información alfanumérica (atributos) y gráfica (geometría) de los objetos de una o varias tablas que mantienen cierta condición o relación espacial respecto a los objetos de otra.
Por ejemplo, podemos relacionar los datos de una capa poligonal representando los espacios naturales de un país con los datos de una capa de puntos de conatos de incendio ocurridos en la última década. Con el join espacial, podemos obtener una relación de todos los conatos de incendio ocurridos dentro de un parque natural. Incluso, podríamos agregarlos por cada uno de los distintos parques y establecer estadísticas de ocurrencia por todo el período, por un año concreto de la serie, etcétera.
Veamos qué opciones existen para realizar un join espacial y qué podemos obtener con ellas.
¿Cómo realizar un join espacial en PostGIS?
Lo primero es asegurarse de que ambas tablas tienen sus respectivas geometrías debidamente georreferenciadas al mismo sistema de coordenadas. Evidentemente, ambas tablas deben tener una columna con geometría.
Para ello, podemos usar la siguiente expresión sobre ambas tablas.
En primer lugar, sobre la primera tabla geométrica:
select st_srid(geom) from tabla_1
Y a continuación, sobre la segunda tabla geométrica:
select st_srid(geom) from tabla_2
Sin una correcta georreferenciación de los objetos gráficos al mismo sistema de referencia no podremos establecer la relación en el espacio y por lo tanto no será posible realizar la operación de join espacial satisfactoriamente.
También es interesante asegurarse de que ambas tablas tienen índices espaciales creados. De lo contrario, especialmente si son tablas con muchos registros, las operaciones espaciales pueden resultar lentas.
Una vez asegurados estos puntos, podremos llevar a cabo la operación de join espacial.
Join espacial en PostGIS con la cláusula JOIN
La forma habitual de llevar a cabo una operación de join espacial en PostGIS es mediante una cláusula JOIN de una tabla sobre otra.
Un ejemplo de consulta SQL de dicha operación sería el siguiente:
SELECT *
FROM tabla_1
JOIN tabla_2
ON ST_Intersects(tabla_1.geom, tabla_2.geom);
En este ejemplo estaríamos realizando un join espacial de los atributos de la tabla_2 sobre la tabla_1, siempre y cuando los objetos de la tabla_2 cumplan la condición de intersección con los objetos de la tabla_1.
Como resultado, obtendríamos una tabla con los atributos de los objetos de la tabla_1 seguidos de los atributos de los objetos de la tabla_2 con los que haya intersecado.
La función ST_Intersects() de PostGIS devuelve verdadero si dos geometrías se intersecan, es decir, si comparten algún punto o línea en común.
Mediante este tipo de operación podemos obtener registros repetidos de ambas tablas, dado que los objetos de la primera pueden intersecar con uno o más objetos de la segunda, y viceversa.
Funciones espaciales disponibles en PostGIS
Además de la función ST_Intersects(), también existen otras variaciones con operaciones o funciones espaciales de PostGIS como pueden ser las siguientes.
La función ST_Contains() devuelve verdadero si una geometría contiene completamente a otra:
SELECT *
FROM tabla_1
JOIN tabla_2
ON ST_Contains(tabla_1.geom, tabla_2.geom);
Por otro lado, la función ST_Touches() devuelve verdadero si dos geometrías se tocan en algún punto:
SELECT *
FROM tabla_1
JOIN tabla_2
ON ST_Touches(tabla_1.geom, tabla_2.geom);
Exsite también la función ST_Overlaps() que devuelve verdadero si dos geometrías se superponen parcialmente.
SELECT *
FROM tabla_1
JOIN tabla_2
ON ST_Overlaps(tabla_1.geom, tabla_2.geom);
También encontramos ST_Crosses(), función que devuelve verdadero si dos geometrías se cruzan, es decir, si tienen algún punto en común pero no comparten ningún segmento o línea por completo.
SELECT *
FROM tabla_1
JOIN tabla_2
ON ST_Crosses(tabla_1.geom, tabla_2.geom);
Por otro lado, la función ST_Within() devuelve verdadero si una geometría está completamente dentro de otra geometría
SELECT *
FROM tabla_1
JOIN tabla_2
ON ST_Within(tabla_1.geom, tabla_2.geom);
Finalmente, la función ST_DWithin() devuelve verdadero si dos geometrías están dentro de una distancia determinada:
SELECT *
FROM tabla_1
JOIN tabla_2
ON ST_DWithin(tabla_1.geom, tabla_2.geom, 50);
En este caso, la función devuelve verdadero si la distancia entre las geometrías es menor a 50 unidades espaciales.
Distintos tipos de JOIN a utilizar
Es importante tener en cuenta los distintos tipos de JOIN que existen en lenguaje SQL y qué resultados ofrece cada uno de ellos a la hora de aplicarlos a una operación espacial de estas características.
En la siguiente imagen se muestra un cuadro resumen muy claro e intuitivo acerca de qué join SQL utilizar para cada caso, según los registros que se deseen obtener con la operación:
Los distintos tipos de JOIN existentes en SQL son:
- Inner Join: devuelve solamente las filas que tienen valores coincidentes en ambas tablas que se están uniendo.
- Left Join (también conocido como Left Outer Join): devuelve todas las filas de la tabla izquierda y las filas correspondientes de la tabla derecha. Si no hay correspondencia en la tabla derecha, devuelve valores nulos.
- Right Join (también conocido como Right Outer Join): devuelve todas las filas de la tabla derecha y las filas correspondientes de la tabla izquierda. Si no hay correspondencia en la tabla izquierda, devuelve valores nulos.
- Full Join (también conocido como Full Outer Join): devuelve todas las filas de ambas tablas. Si no hay correspondencia en una tabla, devuelve valores nulos.
- Cross Join (también conocido como Cartesian Product Join): devuelve todas las combinaciones posibles entre las filas de ambas tablas.
De hecho, lo más habitual en una operación de tipo join espacial es utilizar el LEFT JOIN preferentemente. Si usamos un JOIN simple o INNER JOIN estaríamos retornando únicamente los casos en los que efectivamente se cumpla dicha condición espacial. No obstante, tomando como ejemplo el caso de la capa de puntos incendios y polígonos con parques naturales, nos podríamos plantear la necesidad de retornar todos los incendios independientemente de su relación espacial con la capa de parques y, en caso de intersecar con un parque natural, obtener los atributos del parque.
En este caso usaríamos un LEFT JOIN de la tabla de parques sobre la tabla de incendios:
SELECT *
FROM incendios
LEFT JOIN parques
ON ST_Intersects(indencios.geom, parques.geom);
De este modo obtendremos la totalidad de los registros de la primera tabla (incendios) y los atributos de su correspondiente parque en caso de que efectivamente cumpla la condición espacial de intersección indicada en el join espacial.
Así pues, con el uso del LEFT JOIN obtendremos dos tipos de registros en el resultado:
- Los incendios que sí intersequen con un parque contendrán los atributos del incendio seguidos de los atributos con valores del parque con el que hayan intersecado.
- Los incendios que no intersequen con un parque contendrán los atributos del incendio seguidos de los atributos vacíos (NULL) ya que no habrá intersecado con ningún parque.
Negación de un JOIN espacial
Con negación nos referimos a obtener el resultado de una operación que retorne valor booleano falso en vez de verdadero. Es decir, en este caso, nos planteamos cómo podemos obtener los objetos de una tabla que no cumplan la condición espacial indicada.
Siguiendo con el ejemplo de los incendios y los parques naturales, nos preguntamos ¿Cómo obtenemos todos aquellos incendios que no se originaron dentro de un parque natural?
En este caso, tan sólo sería necesario añadir un filtro WHERE sobre la operación LEFT JOIN anterior. Ese filtro deberá indicar que sólo deseamos obtener los resultados que no recuperen valores del parque (ya que los incendios no están ubicados dentro), es decir, que tengan valor nulo.
SELECT *
FROM incendios
LEFT JOIN parques
ON ST_Intersects(indencios.geom, parques.geom)
WHERE parques.id IS NULL;
Así pues, estaremos obteniendo únicamente todos los registros de la primera tabla (incendios) que no intersequen con la segunda (parques).
Otra opción para obtener todos los registros de una tabla que no tienen ningún tipo de relación espacial con los objetos de otra tabla es mediante la operación ST_Disjoint().
SELECT *
FROM incendios
JOIN parques
ON ST_Disjoint(indencios.geom, parques.geom);
Join espacial con la cláusula WHERE
También es posible realizar un join espacial con una cláusula WHERE debidamente utilizada, a pesar de que en realidad se está utilizando un filtro con una condición espacial como operación de análisis.
SELECT *
FROM tabla_1, tabla_2
WHERE ST_Intersects(tabla_1.geom, tabla_2.geom);
En esta consulta se cruzan las tablas tabla_1 y tabla_2 en la cláusula FROM, y se utiliza la función ST_Intersects() en la cláusula WHERE para determinar qué registros coinciden espacialmente entre ambas tablas.
Con esta consulta SQL estaríamos obteniendo los atributos de los objetos de ambas tablas que cumplen la condición espacial de intersección.
Este método es mucho más restrictivo, ya que al utilizar un filtro WHERE sólo estaríamos recuperando la información de aquellos casos en los que sí se cumple la condición, no pudiendo introducir el «juego» que los distintos tipos de JOIN ofrecen.
De hecho, la condición WHERE empleada en este ejemplo podría asimilarse a la operación mediante un JOIN simple o INNER JOIN espacial:
SELECT *
FROM tabla_1
INNER JOIN tabla_2
ON ST_Intersects(tabla_1.geom, tabla_2.geom);
A pesar de que esta opción es léxicamente más sencilla, siendo puristas es más correcto la aproximación mediante el uso de la cláusula JOIN (del tipo que sea) con la operación espacial deseada.
Practicar con los distintos tipos de JOIN espaciales existentes es la mejor manera de entender qué resultados ofrecen, ayudando a saber qué tipo de operación y join utilizar en cada caso.
Esperamos que el artículo haya resultado útil y práctico, y ayude a comprender las distintas opciones posibles que se pueden utilizar a la hora de llevar a cabo este tipo de operaciones tan recurrentes en el análisis espacial en PostGIS.