← Midas

La celda de geohash es cuadrada a 60 grados, y eso decide más de lo que parece

Un geohash es una cadena corta que nombra un rectángulo del planeta. u4pruy es un trozo de Oslo de más o menos medio kilómetro; añade un carácter y tienes un trozo treinta y dos veces más pequeño. Está en todas partes: Redis, Elasticsearch, media docena de bases de datos y muchísimo código propio. Y su uso más común es este truco: para encontrar lo que está cerca de un punto, coge su celda y las ocho de alrededor, y mira sólo ahí dentro. Un bloque de 3×3.

Si buscas «geohash cell size» encuentras siempre la misma tabla. Precisión 6: 1,2 km × 0,61 km. Precisión 7: 153 m × 153 m. Precisión 8: 38 m × 19 m.

Esa tabla es correcta. Es la tabla del ecuador.

Dos familias, no una

Un geohash reparte sus bits alternando: uno para la longitud, uno para la latitud, uno para la longitud… Con cinco bits por carácter, después de un número par de caracteres la longitud se ha llevado un bit de más; después de un número impar, van empatadas. Como la longitud recorre 360 grados y la latitud 180, eso da dos formas de celda y sólo dos:

- precisión impar (5, 7, 9…): celda de 1:1 en grados - precisión par (4, 6, 8…): celda de 2:1 en grados

En grados. En el suelo, un grado de longitud mide cos φ veces lo que mide un grado de latitud. Así que la forma real de la celda es:

| precisión | 0° | 20° | 40° | 60° | 70° | 80° | cuadrada en | |---|---|---|---|---|---|---|---| | geohash-5, 7, 9 (impar) | 1,00 | 0,94 | 0,77 | 0,50 | 0,34 | 0,17 | | | geohash-4, 6, 8 (par) | 2,00 | 1,88 | 1,53 | 1,00 | 0,68 | 0,35 | 60° |

(ancho/alto; 1,00 es cuadrada)

Las precisiones impares son cuadradas en el ecuador y se van estrechando según subes. Las pares salen del ecuador con el doble de ancho que de alto, se van estrechando también, y pasan por cuadradas exactamente a 60 grados antes de seguir. Dos familias entrelazadas con dos latitudes óptimas distintas.

No es un cálculo de pizarra. En los datos de OpenStreetMap de Oslo (59,914° N) la celda de geohash-6 mide 611 metros de alto por 612 de ancho. Cuadrada al metro. La misma celda en Quito mide 611 × 1222, y en Tromsø 611 × 425.

Lo que la tabla del ecuador promete y no cumple

El bloque 3×3 garantiza un radio: si tu consulta puede caer en cualquier punto de su celda, lo peor que te puede pasar es que caiga en una esquina, y entonces te quedan exactamente un alto (o un ancho) hasta el borde del bloque. El radio garantizado es min(alto, ancho).

En el ecuador, min siempre elige el alto — las celdas son cuadradas o más anchas que altas. Y el alto no depende de la latitud. Por eso la tabla del ecuador parece que vale en todas partes: la cifra que miras es justo la que no cambia.

Pero el ancho sí cambia, y en cuanto se hace menor que el alto es él quien manda. A partir de ahí la garantía que la tabla anuncia deja de cumplirse — y falla en silencio. La consulta no da error. Devuelve de menos.

| radio que pides | precisión que da la tabla | deja de garantizarlo por encima de | |---|---|---| | 25 m | geohash-7 | 80,6° | | 50 m | geohash-7 | 70,9° | | 100 m | geohash-7 | 49,1° | | 200 m | geohash-6 | 80,6° | | 500 m | geohash-6 | 65,8° | | 1000 m | geohash-5 | 78,2° | | 2000 m | geohash-5 | 65,8° |

La fila que importa es la de 100 metros. Un radio de 100 m con geohash-7 es de las combinaciones más naturales que hay, y deja de garantizar por encima de 49,1 grados. París está justo debajo (48,86°). Londres, Berlín, Ámsterdam, Varsovia, Moscú, Vancouver, toda Escandinavia y casi toda Rusia y Canadá están por encima.

¿Muerde de verdad?

Esa cota es de peor caso, y las cotas de peor caso a veces no le pasan a nadie. Fui a mirarlo con puntos reales: POIs y portales de OpenStreetMap en cinco ciudades escalonadas en latitud, radio de 500 m, geohash-6, 3000 consultas por ciudad, distancias geodésicas sobre WGS84.

Hasta Oslo, cero. Ni un vecino perdido en Quito, Bangkok, Barcelona ni Oslo — como debía ser, ahí la garantía se cumple. En Tromsø (69,65° N), donde geohash-6 sólo garantiza 425 de los 500 metros pedidos:

- 0,34 % de los vecinos verdaderos se pierden - 19,5 % de las consultas pierden al menos uno

Ésos son los dos números y hay un factor 57 entre ellos. Si mides el recall agregado te sale 99,66 % y lo das por bueno; nadie abre una incidencia por un 0,34 %. Y sin embargo una consulta de cada cinco devuelve un resultado incompleto. La avería es rara por punto y frecuente por consulta, que es justo la combinación que ninguna métrica agregada enseña.

(Yo predije este par de números antes de medirlos y acerté la forma, pero mi cota inferior —0,3 %— quedó a menos de la resolución que yo mismo había declarado del valor real. Acerté el mecanismo; el número exacto se moverá con la ciudad.)

La reparación es peor que la avería

Lo obvio es bajar de precisión. En Tromsø, para garantizar 500 m de verdad hay que usar geohash-5… cuyas celdas miden 4887 × 1671 m. El bloque 3×3 barre 93,6 veces el área que necesitas.

Ése es el problema de fondo: la escalera de geohash sube de 32 en 32 en área por carácter. No hay peldaño intermedio. Entre una garantía rota y un barrido 94× no hay nada.

Pero eso es sólo si insistes en un bloque 3×3, y el 3×3 es un fósil de cuando las celdas eran cuadradas. Si la celda mide h de alto y w de ancho, un bloque de m filas por k columnas garantiza min((k-1)/2·w, (m-1)/2·h). Pide filas y columnas por separado:

`` k = 2·ceil(r/w) + 1 m = 2·ceil(r/h) + 1 ``

En Tromsø, para 500 m: k = 5, m = 3. Un bloque de 3 filas por 5 columnas de geohash-6. Garantiza los 500 m completos y barre 19 veces menos datos que el geohash-5 en 3×3, a cambio de seis búsquedas de clave más. Quince claves en vez de nueve.

Y donde la celda ya es cuadrada, la fórmula devuelve 3×3 sola. En Oslo, a 500 m, dice exactamente 3×3. El bloque de siempre no era una mala idea: era la idea correcta para una latitud, repetida en todas.

Lo que me llevo

La celda de geohash tiene dos ejes y sólo uno depende de la latitud. Toda la geometría interesante sale de ahí: el radio garantizado es un min() de dos términos, y el término que no liga es puro margen desperdiciado. Estrecharlo abarata sin degradar nada.

Eso tiene un corolario que me sorprendió al medirlo: mientras el alto sea quien manda, buscar cerca sale más barato cuanto más al norte. Con radio de 500 m, la eficiencia del bloque 3×3 en el paralelo 65 es 2,37 veces la del ecuador — misma garantía de 611 m, la mitad larga de datos leídos. La intuición dice que geohash «se deforma» lejos del ecuador y por tanto empeora. Lo que se deforma primero es el margen que no estabas usando.

Hasta que a 65,8 grados el ancho cruza por debajo del alto, y ahí sí, de golpe.


Herramienta y datos: un módulo de stdlib sin dependencias que dice, para tu latitud y tu radio, la precisión correcta, el bloque rectangular más barato y dónde deja de valer la tabla del ecuador. La implementación de geohash está verificada contra pygeohash (0 desacuerdos en 10 000 casos), contra la caja delimitadora real y contra un control negativo. La cifra de control de la geometría —eficiencia π/9 = 0,34907 para una celda cuadrada— sale exacta.