KunYu
h3geohashs2spatial-indexgis-basics

H3 vs Geohash vs S2: Cómo Elegir el Índice Espacial Correcto

Comparativa de los índices espaciales H3, Geohash y S2: tablas de precisión, distorsión por latitud, errores de frontera, ejemplos de código en JS y Python, y una matriz de decisión.

KunYu TeamMarch 12, 202614 min de lectura

Cuando se trabaja con búsqueda por proximidad sobre millones de puntos GPS, tarde o temprano se choca contra la misma pared: un escaneo de distancias por fuerza bruta es demasiado lento, y las coordenadas lat/lng brutas no se indexan bien. Los sistemas de grillas espaciales resuelven esto al acoplar las coordenadas a celdas precomputadas — búsqueda rápida, agregación limpia e identificadores compartibles. Geohash viene haciendo esto desde 2008. H3 llevó los hexágonos al mainstream cuando Uber lo liberó como código abierto en 2018. S2 es lo que Google Maps usa internamente. Los tres no son intercambiables, y las diferencias que parecen académicas en un prototipo se vuelven críticas cuando los datos cruzan un límite de celda o abarcan varios continentes.

¿Qué Son los Sistemas de Indexación Espacial por Grilla?

Los sistemas de indexación espacial por grilla dividen la superficie de la Tierra en celdas discretas, asignando a cada una un identificador único. En lugar de almacenar coordenadas brutas, se almacena un ID de celda — lo que habilita búsquedas rápidas, consultas por rango y agregación de datos simplemente comparando prefijos de cadenas o valores enteros. El intercambio central es entre uniformidad de celdas, simplicidad de implementación y rendimiento de consultas.

Los tres sistemas surgieron de culturas de ingeniería diferentes. 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 biblioteca de geometría para su infraestructura de mapas interna. H3 fue liberado como código abierto por Uber en 2018 para resolver el emparejamiento conductor/pasajero a escala de ciudad.

Sus formas de celda reflejan estos orígenes: Geohash produce rectángulos, S2 produce cuadriláteros esféricos (aproximadamente cuadrados en 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 fallas en producción.

H3 proyecta la Tierra sobre un icosaedro regular (un poliedro de 20 caras) y luego subdivide cada cara en hexágonos. El resultado son 15 niveles de resolución donde cada celda se subdivide en aproximadamente 7 hijos. Los IDs de celda son enteros de 64 bits. La forma hexagonal implica que cada celda tiene exactamente 6 vecinos compartiendo aristas — 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: u4pruydqqvj identifica de forma única un área 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 por rango sean trivialmente simples.

S2 despliega la superficie de la Tierra sobre las seis caras de un cubo, aplica una curva de Hilbert de relleno espacial sobre 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 soporta cobertura de polígonos arbitrarios mediante S2RegionCoverer, que aproxima cualquier forma con el 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 caracteres de Geohash y los 31 niveles de S2 no se mapean entre sí. La tabla siguiente los alinea por área de celda aproximada en tres escalas prácticas: agregación a nivel de ciudad, ruteo por barrios y precisión a nivel de edificio.

La reducción de los grados de longitud sigue una curva coseno y se vuelve severa más rápido de lo que la mayoría de los desarrolladores espera. A 30° de latitud (Los Ángeles, El Cairo, Shanghái), un grado de longitud cubre 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 llega a 2:1: un grado de longitud mide apenas 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án construyendo un servicio global con umbrales de proximidad uniformes (por ejemplo, "dentro de 500 metros"), Geohash con un nivel de precisión fijo dará resultados inconsistentes según la latitud — habría que usar cadenas más largas en latitudes bajas y más cortas cerca de los polos para obtener cobertura equivalente en el mundo real, lo cual complica la lógica de consultas. H3 y S2 compensan 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 vecindad son la operación más común en un índice espacial: "dada esta celda, encontrar todas las celdas adyacentes." Los tres sistemas manejan esto con ergonomías muy distintas.

H3 tiene el modelo de vecindad más limpio. Cada hexágono tiene exactamente 6 vecinos que comparten aristas — sin vecinos en esquinas, 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 (incluyendo el centro); k=2 devuelve 19. Como los hexágonos tienen la misma longitud de arista en todos los lados, las consultas de distancia por conteo 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 grilla: 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 de Greenwich (0° de longitud) o el ecuador pueden tener hashes completamente distintos. El síntoma clásico: una consulta devuelve resultados de un lado de la frontera pero omite silenciosamente los puntos del otro lado.

Un ejemplo real bien documentado: La Roche-Chalais, en el suroeste de Francia, tiene el prefijo Geohash u000, mientras que Pomerol — un pueblo a apenas 30 km — tiene el prefijo ezzz. Estas dos ubicaciones cercanas no comparten ningún prefijo común en ninguna longitud porque un límite de celda mayor corre directamente entre ellas. Una consulta de proximidad basada en prefijos para "puntos cerca de La Roche-Chalais" devolvería silenciosamente cero resultados de Pomerol. El mismo problema ocurre en el Meridiano de Greenwich (0° de longitud): dos puntos GPS a 10 metros de distancia en lados opuestos del Meridiano principal obtienen hashes que comienzan con e (lado oeste) y s (lado este) — sin prefijo compartido, sin rango de índice compartido. La solución estándar es siempre consultar la celda objetivo más sus 8 vecinos, y luego filtrar por distancia Haversine real. El comando GEOSEARCH de Redis maneja esto internamente, que es una razón por la que el soporte nativo de base de datos es preferible a implementar la lógica de proximidad Geohash desde cero.

S2 usa una curva de Hilbert que proporciona fuertes 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 el cálculo de vecinos a través de los límites de cara requiere manejo cuidadoso. S2CellId::EdgeNeighbors() de S2 maneja esto correctamente, pero implica más trabajo de implementación que gridDisk de H3.

Si los 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 grilla rectangular como Geohash tienen costuras en longitudes y latitudes predecibles, y depurar un error de frontera en producción es bastante desagradable.

Cuándo Usar Cada Sistema

Si ya están usando Redis o Elasticsearch, usen Geohash — la búsqueda por proximidad funciona de fábrica sin código extra. Si necesitan consistencia global o visualización hexagonal, H3. Recurran a S2 solo si necesitan indexar polígonos arbitrarios o están 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, sin configuración
Mapas de calor de demanda (viajes compartidos, 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 grilla para juegos (Pokémon GO lo usa) S2 Elección original de infraestructura de Niantic
Prototipos rápidos y geofencing simple Geohash Menor curva de aprendizaje, 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 ha estado integrado en Redis desde 2015 (GEOADD, GEORADIUS), y el tipo de campo geo_point de Elasticsearch usa Geohash internamente para las agregaciones de grilla. Si el stack ya incluye alguno de los dos, la integración con Geohash requiere casi cero trabajo adicional.

H3 tiene bibliotecas sólidas en Python (h3) y JavaScript (h3-js), un ecosistema de herramientas de visualización en crecimiento (el 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 de JavaScript más débil — la biblioteca principal está en C++, con ports mantenidos por la comunidad en otros lenguajes.

Conversor GeoHash

Convierte entre GeoHash, H3 y Plus Codes.

Try it now

Trabajando con H3, Geohash y S2 en Código

Los tres sistemas tienen bibliotecas en JavaScript y Python. Los siguientes ejemplos cubren las tres operaciones que más van a usar: codificar una coordenada, encontrar vecinos y obtener el polígono del límite de 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 — consulten nuestra guía h3-js vs ngeohash.

Dos detalles de las bibliotecas que conviene saber antes de llevar algo a producción:

Precisión de enteros en ngeohash: las funciones encode_int / decode_int están limitadas a 52 bits en JavaScript (rango seguro estándar de enteros float64 IEEE 754). Para la mayoría de los casos de uso esto está bien, pero si se establece bitDepth por encima de 52, los resultados pierden precisión silenciosamente. Usen la API de cadenas (encode / decode) a menos que tengan una razón específica para trabajar con enteros, y siempre pasen el mismo valor de bitDepth de forma consistente tanto en las llamadas de codificación como de decodificación.

Representación del ID de celda en h3-js: los IDs de celda H3 son enteros de 64 bits, que superan el rango de enteros seguros de JavaScript (2^53 − 1). La biblioteca h3-js los devuelve como cadenas por defecto (por ejemplo, "8928308280fffff"). Un error común en producción: si su API serializa los IDs de H3 como números JSON en lugar de cadenas, algunos parsers JSON silenciosamente redondean el valor y corrompen el ID de celda. Traten siempre los IDs de H3 como cadenas en todo su stack — en el esquema de base de datos, las respuestas de 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 mantenido por la comunidad (paquete npm s2-geometry) que va por detrás de la biblioteca en C++. Para trabajo serio con S2 en entornos JS, el enfoque más práctico es llamar a un servicio en Python/Java o usar Cloudflare Workers con WASM. En Python, la biblioteca 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)

Pueden experimentar con la codificación de Geohash y H3 directamente en el navegador usando la herramienta de codificación GeoHash de KunYu — sin necesidad de instalar ninguna biblioteca.

Conversor GeoHash

Convierte entre GeoHash, H3 y Plus Codes.

Try it now

Preguntas Frecuentes

¿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 vecindad más limpias y sin errores de frontera. Geohash gana cuando necesitan los comandos GEO de Redis o las agregaciones de Elasticsearch de fábrica, o cuando un ID legible por humanos importa — las cadenas Geohash son fáciles de leer en logs; los IDs de H3 como 8928308280fffff no lo son.

¿Redis soporta H3?

Redis no tiene soporte nativo para H3. Los comandos GEO integrados (GEOADD, GEORADIUS, GEOSEARCH) usan Geohash internamente. Para usar H3 con Redis, hay que codificar las coordenadas a IDs de celda H3 en la capa de aplicación y almacenarlos como claves de cadena regulares o en conjuntos ordenados. A partir de Redis 7.x, no hay planes de agregar primitivas H3 al servidor principal.

¿Qué significa S2?

S2 es la abreviatura de Sphere² (esfera al cuadrado), haciendo referencia al concepto matemático de mapear una esfera sobre una superficie bidimensional. La biblioteca fue desarrollada en Google y se usa internamente en Google Maps, Google Earth y otros productos geoespaciales 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 de la Tierra (incluyendo 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 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 grillas son independientes — un hexágono H3 en resolución 9 y una celda Geohash de 6 caracteres cubren áreas distintas y solo pueden mapearse aproximadamente. El enfoque práctico es decodificar cualquiera de los dos formatos a una coordenada lat/lng, y luego recodificar en el formato destino. Tengan en cuenta que la celda resultante puede no contener perfectamente a 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 soporta hasta 12 caracteres, produciendo celdas de aproximadamente 3,7 cm × 1,9 cm de tamaño. En la práctica, la mayoría de las aplicaciones usan entre 6 y 9 caracteres (desde ~1 km² hasta ~7 m²). Más allá de 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 innecesaria de almacenamiento e indexado.