Cuando construís una búsqueda por proximidad contra millones de puntos GPS, tarde o temprano os topáis con el mismo muro: un escaneo de distancias por fuerza bruta es demasiado lento, y las coordenadas lat/lng en bruto no indexan bien. Los sistemas de cuadrícula espacial resuelven esto ajustando las coordenadas a celdas precalculadas — búsquedas rápidas, agregación limpia e identificadores compartibles. Geohash lleva haciendo esto desde 2008. H3 popularizó los hexágonos cuando Uber lo publicó como código abierto en 2018. S2 es lo que usa Google Maps internamente. Los tres no son intercambiables, y las diferencias que parecen académicas en un prototipo se vuelven decisivas cuando vuestros datos cruzan una frontera de celda o abarcan varios continentes.
¿Qué son los sistemas de indexación de cuadrícula espacial?
Los sistemas de indexación de cuadrícula espacial dividen la superficie terrestre en celdas discretas y asignan a cada una un identificador único. En lugar de almacenar coordenadas en bruto, se almacena un ID de celda — lo que permite búsquedas rápidas, consultas de rango y agregación de datos simplemente comparando prefijos de cadena o valores enteros. La contrapartida principal es entre la uniformidad de las celdas, la simplicidad de implementación y el rendimiento de las consultas.
Los tres sistemas surgieron de culturas de ingeniería distintas. Geohash fue inventado por Gustavo Niemeyer en 2008 como un sistema de geocodificación de dominio público — es el más simple por diseño. S2 fue desarrollado en Google alrededor de 2011 como librería de geometría para la infraestructura de cartografía interna. H3 fue publicado como código abierto por Uber en 2018 para resolver el emparejamiento conductor/pasajero a escala de ciudad.
Las formas de sus celdas reflejan estos orígenes: Geohash produce rectángulos, S2 produce cuadriláteros esféricos (aproximadamente cuadrados sobre una esfera), y H3 produce hexágonos (con 12 pentágonos para cerrar la esfera).
Cómo funciona cada sistema
El enfoque de codificación de cada sistema determina tanto sus fortalezas como sus puntos de fallo en producción.
H3 proyecta la Tierra sobre un icosaedro regular (un poliedro de 20 caras) y subdivide cada cara en hexágonos. El resultado son 15 niveles de resolución donde cada celda se subdivide en aproximadamente 7 hijas. Los IDs de celda son enteros de 64 bits. La forma hexagonal significa que cada celda tiene exactamente 6 vecinos que comparten arista — sin ambigüedad por esquinas compartidas.
Geohash intercala las representaciones binarias de los bits de latitud y longitud, y luego codifica el resultado como una cadena Base32. Por eso las cadenas Geohash son legibles por humanos: u4pruydqqvj identifica de forma única un parche de 3 m × 3 m en el centro de París. La precisión se controla mediante la longitud de la cadena (1–12 caracteres). La propiedad de prefijo — u4pru siempre está dentro de u4pr — hace que las consultas de rango sean trivialmente sencillas.
S2 despliega la superficie terrestre sobre las seis caras de un cubo, aplica una curva de Hilbert de relleno de espacio en cada cara y luego mapea las posiciones a enteros de 64 bits. La curva de Hilbert preserva la localidad espacial: puntos cercanos en la Tierra producen enteros cercanos. S2 tiene 31 niveles de resolución y admite cobertura poligonal arbitraria mediante S2RegionCoverer, que aproxima cualquier forma con el número mínimo de celdas necesarias.
Precisión y cobertura — Tabla comparativa
Los tres sistemas usan escalas de precisión incompatibles — las 15 resoluciones de H3, las 12 longitudes de carácter de Geohash y los 31 niveles de S2 no se corresponden entre sí. La tabla siguiente los alinea por área de celda aproximada en tres escalas prácticas: agregación a nivel de ciudad, enrutamiento por barrio y precisión a nivel de edificio.
La reducción en grados de longitud sigue una curva coseno y se vuelve severa más rápido de lo que la mayoría de los desarrolladores esperan. A 30° de latitud (Los Ángeles, El Cairo, Shanghái), un grado de longitud abarca 96,5 km frente a los 110,6 km de un grado de latitud — un déficit este-oeste del 14,9 %. A 60° (Estocolmo, Anchorage, San Petersburgo), la relación alcanza 2:1: un grado de longitud solo tiene 55,7 km de ancho. Una celda Geohash de 6 caracteres que cubre ~0,74 km² en el ecuador cubre solo ~0,37 km² a 60°N. Si estáis construyendo un servicio global con umbrales de proximidad uniformes (p. ej., "en un radio de 500 metros"), Geohash con un nivel de precisión fijo dará resultados inconsistentes según la latitud — necesitaríais usar hashes más largos a latitudes bajas y más cortos cerca de los polos para obtener una cobertura real equivalente, lo que complica la lógica de vuestras consultas. H3 y S2 compensan ambos la distorsión esférica: H3 en resolución 9 varía menos del 3 % en área en todas las latitudes.
| Escala | Resolución H3 | Área celda H3 | Longitud Geohash | Área celda Geohash | Nivel S2 | Área celda S2 |
|---|---|---|---|---|---|---|
| Ciudad | 5 | ~253 km² | 4 chars | ~780 km² | 10 | ~325 km² |
| Barrio | 9 | ~0,11 km² | 6 chars | ~0,74 km² | 15 | ~0,32 km² |
| Edificio | 11 | ~0,0022 km² | 7 chars | ~0,023 km² | 18 | ~0,005 km² |
Búsqueda de vecinos y efectos de frontera
Las consultas de vecinos son la operación más habitual en un índice espacial: "dada esta celda, encontrad todas las adyacentes". Los tres sistemas abordan esto con ergonomías muy distintas.
H3 tiene el modelo de vecindad más limpio. Cada hexágono tiene exactamente 6 vecinos que comparten arista — sin vecinos de esquina, sin ambigüedad. La función gridDisk(cell, k) devuelve todas las celdas dentro de k anillos en una sola llamada. Un disco k=1 devuelve 7 celdas (incluido el centro); k=2 devuelve 19. Dado que los hexágonos tienen la misma longitud de arista en todos los lados, las consultas de distancia usando el recuento de anillos son geométricamente consistentes.
Geohash tiene 8 vecinos (4 aristas + 4 esquinas). Para la mayoría de las celdas esto funciona bien. El problema conocido está en las fronteras de cuadrícula: dos celdas que son geográficamente adyacentes pueden tener cadenas Geohash que no comparten ningún prefijo. Por ejemplo, las celdas que atraviesan el Meridiano Cero (longitud 0°) o el ecuador pueden tener hashes completamente distintos. El síntoma clásico: una consulta devuelve resultados a un lado de una frontera pero silenciosamente omite puntos justo al otro lado.
Un ejemplo real bien documentado: La Roche-Chalais, en el suroeste de Francia, tiene el prefijo Geohash u000, mientras que Pomerol — una localidad a solo 30 km de distancia — tiene el prefijo ezzz. Estas dos ubicaciones cercanas no comparten ningún prefijo común a ninguna longitud porque una frontera de celda principal pasa directamente entre ellas. Una consulta de proximidad basada en prefijos para "puntos cercanos a La Roche-Chalais" devolvería silenciosamente cero resultados de Pomerol. El mismo problema ocurre en el Meridiano de Greenwich (longitud 0°): dos puntos GPS a 10 metros de distancia en lados opuestos del Meridiano Cero obtienen hashes que comienzan por e (lado oeste) y s (lado este) — sin prefijo compartido, sin rango de índice compartido. La solución estándar es consultar siempre la celda objetivo más sus 8 vecinos y luego filtrar por distancia Haversine real. El comando GEOSEARCH de Redis gestiona esto internamente, que es una razón por la que el soporte nativo de la base de datos es preferible a implementar vuestra propia lógica de proximidad Geohash.
S2 usa una curva de Hilbert que proporciona sólidas garantías de localidad — los puntos cercanos en la Tierra se mapean a enteros cercanos en el índice. Sin embargo, "cercano en el espacio de Hilbert" y "espacialmente adyacente" no siempre son lo mismo. Las seis caras del cubo introducen costuras, y calcular vecinos a través de los límites entre caras requiere un manejo cuidadoso. S2CellId::EdgeNeighbors() lo gestiona correctamente, pero implica más trabajo de implementación que gridDisk de H3.
Si vuestros datos abarcan fronteras geográficas — costas, fronteras nacionales, la línea de cambio de fecha — la topología hexagonal uniforme de H3 es la opción más segura por defecto. Los sistemas de cuadrícula rectangular como Geohash tienen costuras en longitudes y latitudes predecibles, y depurar un fallo de frontera en producción es muy desagradable.
Cuándo usar cada sistema
Si ya usáis Redis o Elasticsearch, usad Geohash — la búsqueda por proximidad funciona de serie sin código adicional. Si necesitáis consistencia global o visualización hexagonal, H3. Recurrid a S2 solo si necesitáis indexar polígonos arbitrarios o estáis trabajando junto a infraestructura existente de Google. La tabla cubre los escenarios más comunes:
| Caso de uso | Recomendado | Motivo |
|---|---|---|
| Comandos Redis GEO / geo_point de Elasticsearch | Geohash | Soporte nativo integrado, configuración cero |
| Mapas de calor de demanda en transporte/delivery | H3 | Tamaño de celda consistente + visualización hexagonal |
| Agregación de datos globales (clima, telemetría) | H3 | Elimina la distorsión por latitud polar |
| Indexación de regiones poligonales arbitrarias | S2 | S2RegionCoverer aproxima cualquier forma |
| Sistemas de cuadrícula para videojuegos (Pokémon GO lo usa) | S2 | La elección original de infraestructura de Niantic |
| Prototipos rápidos y geofencing simple | Geohash | Curva de aprendizaje mínima, IDs legibles por humanos |
| Análisis multirresolución (zoom in/out) | H3 | Jerarquía padre/hijo limpia |
| PostGIS / bases de datos espaciales | Geohash | Soporte estándar del ecosistema GIS |
La madurez del ecosistema suele determinar la elección antes que la geometría. Geohash está integrado en Redis desde 2015 (GEOADD, GEORADIUS), y el tipo de campo geo_point de Elasticsearch usa Geohash internamente para las agregaciones de cuadrícula. Si vuestro stack ya incluye alguno de los dos, la integración con Geohash requiere casi ningún trabajo adicional.
H3 cuenta con librerías sólidas en Python (h3) y JavaScript (h3-js), un ecosistema creciente de herramientas de visualización (H3HexagonLayer de Deck.gl) y soporte de primera clase en la extensión espacial de DuckDB. S2 tiene las operaciones de geometría más potentes pero el soporte en JavaScript más débil — la librería principal es C++, con ports a otros lenguajes mantenidos por la comunidad.
Trabajar con H3, Geohash y S2 en código
Los tres sistemas tienen librerías en JavaScript y Python. Los siguientes ejemplos cubren las tres operaciones que usaréis con más frecuencia: codificar una coordenada, encontrar vecinos y obtener el polígono de límite de la celda. Para una comparación detallada de h3-js y ngeohash como paquetes npm — incluyendo tamaños de bundle, benchmarks de codificación y problemas comunes en producción — consultad nuestra guía h3-js vs ngeohash.
Dos advertencias sobre las librerías que conviene conocer antes de ponerlas en producción:
Precisión de enteros en ngeohash: las funciones encode_int / decode_int están limitadas a 52 bits en JavaScript (rango estándar de entero seguro para float64 según IEEE 754). Para la mayoría de los casos de uso esto es suficiente, pero si establecéis bitDepth por encima de 52, los resultados pierden precisión silenciosamente. Usad la API de cadenas (encode / decode) salvo que tengáis una razón específica para trabajar con enteros, y pasad siempre el mismo valor de bitDepth de forma consistente tanto en encode como en decode.
Representación de IDs de celda en h3-js: los IDs de celda H3 son enteros de 64 bits, que superan el rango de entero seguro de JavaScript (2^53 − 1). La librería h3-js los devuelve como cadenas por defecto (p. ej., "8928308280fffff"). Un error habitual en producción: si vuestra API serializa los IDs H3 como números JSON en lugar de cadenas, algunos parsers JSON redondearán silenciosamente el valor y corromperán el ID de la celda. Tratad siempre los IDs H3 como cadenas en toda vuestra pila — en el esquema de base de datos, las respuestas de la API y el código frontend.
JavaScript
import { latLngToCell, gridDisk, cellToBoundary } from "h3-js";
import ngeohash from "ngeohash";
// --- H3 ---
// Encode coordinate to H3 cell (resolution 9 ≈ 0.1 km²)
const h3Cell = latLngToCell(37.7749, -122.4194, 9);
// → "8928308280fffff"
// Get all cells within 1 ring (7 cells including center)
const h3Neighbors = gridDisk(h3Cell, 1);
// Get cell boundary polygon (array of [lat, lng] pairs)
const h3Boundary = cellToBoundary(h3Cell);
// --- Geohash ---
// Encode coordinate to Geohash (precision 6 ≈ 0.74 km²)
const hash = ngeohash.encode(37.7749, -122.4194, 6);
// → "9q8yyk"
// Decode back to coordinate
const { latitude, longitude } = ngeohash.decode(hash);
// Get 8 neighbors (N, NE, E, SE, S, SW, W, NW)
const geohashNeighbors = ngeohash.neighbors(hash);
// Get cell bounding box [minLat, minLon, maxLat, maxLon]
const bbox = ngeohash.decode_bbox(hash);
Python
import h3
import geohash # pip install python-geohash
# --- H3 ---
cell = h3.latlng_to_cell(37.7749, -122.4194, 9)
neighbors = h3.grid_disk(cell, 1)
boundary = h3.cell_to_boundary(cell) # list of (lat, lng) tuples
# --- Geohash ---
hash_str = geohash.encode(37.7749, -122.4194, precision=6)
decoded_lat, decoded_lng = geohash.decode(hash_str)
neighbors = geohash.neighbors(hash_str) # dict with N/NE/E/SE/S/SW/W/NW keys
S2 en JavaScript requiere un port comunitario (paquete npm s2-geometry) que va por detrás de la librería C++. Para trabajo serio con S2 en entornos JS, el enfoque más práctico es llamar a un servicio Python/Java o usar Cloudflare Workers con WASM. En Python, la librería s2sphere ofrece una interfaz limpia:
import s2sphere
# Encode coordinate to S2 cell (level 15 ≈ 0.32 km²)
latlng = s2sphere.LatLng.from_degrees(37.7749, -122.4194)
cell_id = s2sphere.CellId.from_lat_lng(latlng).parent(15)
# Get 4 edge neighbors
neighbors = cell_id.edge_neighbors()
# Cover an arbitrary polygon with S2 cells
region_coverer = s2sphere.RegionCoverer()
region_coverer.min_level = 10
region_coverer.max_level = 15
covering = region_coverer.get_covering(some_s2_region)
Podéis experimentar con la codificación Geohash y H3 directamente en el navegador usando la herramienta GeoHash de KunYu — sin necesidad de instalar ninguna librería.
FAQ
¿Es H3 mejor que Geohash?
Para proyectos nuevos sin restricciones de infraestructura existentes, H3 es la mejor opción por defecto: tamaños de celda uniformes, consultas de vecinos más limpias y sin errores de frontera. Geohash gana cuando necesitáis los comandos GEO de Redis o las agregaciones de Elasticsearch de serie, o cuando los IDs legibles por humanos importan — las cadenas Geohash son fáciles de leer en los logs; los IDs H3 como 8928308280fffff no lo son.
¿Admite Redis H3?
Redis no tiene soporte nativo para H3. Los comandos GEO integrados (GEOADD, GEORADIUS, GEOSEARCH) usan Geohash internamente. Para usar H3 con Redis, codificad las coordenadas a IDs de celda H3 en vuestra capa de aplicación y almacenadlos como claves de cadena normales o en conjuntos ordenados. A partir de Redis 7.x, no hay planes de añadir primitivas H3 al servidor principal.
¿Qué significa S2?
S2 es la abreviatura de Sphere² (esfera al cuadrado), en referencia al concepto matemático de proyectar una esfera sobre una superficie bidimensional. La librería fue desarrollada en Google y se usa internamente en Google Maps, Google Earth y otros productos geográficos de Google.
¿Cuántas celdas H3 cubren la Tierra en resolución 5?
En resolución 5, hay 2.016.842 celdas H3 cubriendo la superficie terrestre (incluidos los 12 pentágonos que cierran el icosaedro). Cada celda cubre aproximadamente 252,9 km², lo que hace que esta resolución sea útil para el análisis a nivel de país o de grandes áreas metropolitanas.
¿Puedo convertir entre H3 y Geohash?
No existe una conversión matemática directa. Las dos cuadrículas son independientes — un hexágono H3 en resolución 9 y una celda Geohash de 6 caracteres cubren áreas distintas y solo pueden mapearse de forma aproximada. El enfoque práctico es decodificar cualquiera de los dos formatos a una coordenada lat/lng y luego recodificarla en el formato de destino. Tened en cuenta que la celda resultante puede no contener perfectamente la celda original debido a las diferencias de tamaño y forma.
¿Cuál es la precisión máxima de Geohash?
La especificación Geohash admite hasta 12 caracteres, lo que produce celdas de aproximadamente 3,7 cm × 1,9 cm. En la práctica, la mayoría de las aplicaciones usan 6–9 caracteres (de 1 km² hasta ~7 m²). Por encima de los 9 caracteres, la precisión supera lo que la mayoría del hardware GPS puede proporcionar, y las claves de cadena largas generan una sobrecarga de almacenamiento e índice innecesaria.