Mientras diseñaba el backend que en algún momento dará soporte a la sincronización de Caladero, llegué a una pregunta que cualquiera que trabaje con datos geográficos en un servidor termina haciéndose: ¿basta con guardar latitud y longitud como dos columnas numéricas normales, o hace falta algo más especializado como PostGIS? La respuesta corta es que depende de qué preguntas se le vayan a hacer a esos datos, y quiero explicar por qué.

Qué es PostGIS exactamente

PostGIS es una extensión de PostgreSQL que añade tipos de datos geográficos y geométricos nativos, junto con un conjunto extenso de funciones para trabajar con ellos: calcular distancias reales entre puntos, comprobar si un punto está dentro de un área determinada, encontrar todos los puntos dentro de un radio concreto, o calcular intersecciones entre formas geométricas. No es una base de datos distinta; es PostgreSQL, la base de datos relacional habitual, con capacidades espaciales añadidas mediante esta extensión.

Por qué dos columnas numéricas no son suficientes

Guardar latitud y longitud como dos columnas DOUBLE PRECISION funciona perfectamente para almacenar el dato y para mostrarlo en un mapa. El problema aparece en cuanto se necesita hacer una pregunta espacial mínimamente compleja. Un ejemplo típico: "dame todos los puntos guardados en un radio de cinco kilómetros alrededor de esta ubicación".

Sin PostGIS, esa pregunta obliga a calcular manualmente, para cada fila, la distancia entre dos coordenadas usando una fórmula como la de Haversine (que tiene en cuenta la curvatura de la Tierra), y hacerlo dentro de una consulta SQL normal es incómodo, propenso a errores de precisión, y sobre todo, no puede aprovechar ningún índice espacial, así que la consulta tiene que recorrer todas las filas de la tabla una por una para calcular la distancia de cada una. Con una tabla de cientos de miles de puntos, eso deja de ser viable en tiempos de respuesta razonables.

Cómo se ve esta consulta con PostGIS

Con PostGIS, la misma pregunta se convierte en una consulta espacial nativa, que además puede beneficiarse de un índice especializado (GIST) pensado exactamente para este tipo de operación:

SELECT id, nombre
FROM puntos_guardados
WHERE ST_DWithin(
    ubicacion,
    ST_SetSRID(ST_MakePoint(-3.7038, 40.4168), 4326)::geography,
    5000
);

Esta consulta devuelve todos los puntos guardados dentro de un radio de cinco mil metros (el último parámetro, en metros cuando se trabaja con el tipo geography) alrededor de unas coordenadas concretas. La función ST_DWithin está diseñada precisamente para aprovechar el índice espacial de la columna ubicacion, lo que hace que la consulta siga siendo rápida incluso con una tabla muy grande, algo que la versión manual con dos columnas numéricas no puede conseguir sin trabajo adicional considerable.

El campo SRID: un detalle que hay que entender bien

El número 4326 en el ejemplo anterior no es arbitrario: es el identificador del sistema de referencia de coordenadas WGS84, el estándar habitual para coordenadas de latitud y longitud tal y como las entrega el GPS de cualquier móvil. PostGIS obliga a ser explícito sobre qué sistema de referencia se está usando en cada geometría, precisamente porque mezclar sistemas de referencia distintos sin darse cuenta es una fuente de errores de cálculo difíciles de detectar a simple vista: los números de la coordenada pueden parecer razonables y sin embargo estar interpretados con el sistema de referencia equivocado.

Al principio de trabajar con PostGIS, este fue el concepto que más me costó interiorizar, porque en el uso diario con apps móviles uno se acostumbra a pensar en "latitud y longitud" sin más, y PostGIS obliga a ser explícito sobre en qué sistema de coordenadas se está trabajando en cada operación.

Otras preguntas espaciales que resuelve de forma nativa

Más allá de la búsqueda por radio, PostGIS resuelve de forma directa otras preguntas que serían muy costosas de programar a mano:

  • ¿Este punto está dentro de esta zona? Útil, por ejemplo, para comprobar si un punto guardado cae dentro de los límites de un área protegida o de una zona con restricciones de pesca, usando ST_Contains o ST_Within.
  • ¿Cuál es el punto guardado más cercano a esta ubicación? Con un índice espacial, ORDER BY ubicacion <-> punto_referencia LIMIT 1 encuentra el vecino más cercano de forma eficiente, sin calcular la distancia a todos los puntos de la tabla.
  • ¿Qué puntos caen dentro de la vista actual del mapa? Una consulta con ST_Intersects contra un rectángulo que representa el área visible del mapa permite cargar solo los puntos relevantes para lo que el usuario está viendo, en vez de traer toda la tabla y filtrar en el propio dispositivo.

Cuándo NO hace falta todavía PostGIS

Es importante no caer en el extremo contrario de meter PostGIS "porque es lo correcto" en cualquier proyecto que toque coordenadas. Si un proyecto solo necesita guardar una ubicación y mostrarla en un mapa, sin hacer nunca búsquedas por proximidad, filtros espaciales complejos ni cálculos de distancia real, dos columnas numéricas simples siguen siendo perfectamente razonables, y añadir una extensión y un modelo de datos más complejo sin necesidad real solo añade complejidad de mantenimiento sin ningún beneficio a cambio.

En Caladero, mientras el proyecto se mantiene en desarrollo con almacenamiento principalmente local en el dispositivo (como explico en el artículo sobre desarrollo local-first), no tengo todavía una necesidad urgente de PostGIS en producción. La decisión de incorporarlo llegará cuando la sincronización con un backend centralizado y las búsquedas por proximidad entre usuarios sean funcionalidades reales, no antes.

Errores que he visto cometer, y que yo mismo estuve a punto de cometer

El error más habitual es empezar guardando coordenadas como columnas numéricas simples y, cuando llega el momento de añadir búsquedas por proximidad, intentar resolverlas con consultas SQL manuales cada vez más complejas en vez de migrar a un tipo de dato geográfico adecuado desde el principio. Esa deuda técnica se paga después con consultas lentas y difíciles de mantener, justo cuando el volumen de datos empieza a crecer y el rendimiento empieza a importar de verdad.

Otro error es no crear el índice espacial (GIST) sobre la columna geográfica después de migrar a PostGIS, lo que hace que las consultas espaciales sigan siendo lentas aunque técnicamente ya se estén usando las funciones correctas. El índice no es opcional si se quiere aprovechar de verdad la ventaja de rendimiento que ofrece PostGIS frente al cálculo manual.

Conclusión

PostGIS no es una pieza obligatoria en cualquier proyecto que toque coordenadas geográficas, pero se vuelve prácticamente imprescindible en cuanto aparecen preguntas espaciales reales: búsquedas por proximidad, comprobaciones de pertenencia a un área, o cálculos de distancia sobre volúmenes de datos que ya no caben en un simple bucle manual. Para un proyecto como Caladero, cuya utilidad depende en buena medida de responder preguntas de este tipo sobre puntos geográficos, adoptarlo en el backend cuando llegue el momento de la sincronización entre dispositivos parece la decisión razonable, en vez de posponerlo hasta que el rendimiento ya sea un problema visible.