geohash

The geohash library provides a geohash_protocol protocol and a geohash object for encoding and decoding geographic coordinates using the standard geohash algorithm.

It uses the standard geohash base-32 alphabet:

  • 0123456789bcdefghjkmnpqrstuvwxyz

The library complements the geospatial library and uses the same geographic(Latitude,Longitude) coordinate representation and bbox(geographic(MinLatitude,MinLongitude),geographic(MaxLatitude,MaxLongitude)) bounding-box representation.

For encoding, longitudes are canonicalized to the [-180.0,180.0[ range so that geographic(Latitude,180.0) and geographic(Latitude,-180.0) encode to the same geohash.

API documentation

Open the ../../apis/library_index.html#geohash link in a web browser.

Loading

To load all entities in this library, load the loader.lgt file:

| ?- logtalk_load(geohash(loader)).

Testing

To test this library predicates, load the tester.lgt file:

| ?- logtalk_load(geohash(tester)).

Supported operations

The library provides predicates for:

  • validating geohash atoms

  • encoding coordinates at a requested precision

  • decoding a geohash to its cell center

  • decoding a geohash to its cell bounding box

  • querying the nominal latitude and longitude precision of a geohash length

  • querying cell dimensions for a geohash length

  • computing adjacent and neighboring cells

  • covering a geographic bounding box with geohashes of a fixed precision

  • covering a geographic bounding box using fixed-depth or adaptive mixed-depth covers

  • compressing complete sibling sets into parent prefixes

  • navigating geohash parent-child relationships and shared prefixes

  • converting between atom geohashes and integer-backed 5-bit packed representations

  • converting between atom and integer geohash representations explicitly

  • encoding and decoding integer geohashes using bit precision

  • expanding a geohash to the center cell plus neighbors

  • covering polygons and polylines with geohashes

Examples

Encode a coordinate at a selected precision:

| ?- geohash::encode(geographic(42.6, -5.6), 5, Geohash).
Geohash = 'ezs42'
yes

| ?- geohash::encode(geographic(57.64911, 10.40744), 11, Geohash).
Geohash = 'u4pruydqqvj'
yes

Decode a geohash to its center coordinate and bounding box:

| ?- geohash::decode('ezs42', Coordinate).
Coordinate = geographic(42.60498046875, -5.60302734375)
yes

| ?- geohash::bounding_box('ezs42', BoundingBox).
BoundingBox = bbox(geographic(42.5830078125, -5.625), geographic(42.626953125, -5.5810546875))
yes

Inspect cell precision metadata:

| ?- geohash::precision(5, LatitudeError, LongitudeError).
| ?- geohash::cell_dimensions(5, LatitudeSpan, LongitudeSpan).

Compute adjacent cells and neighbors:

| ?- geohash::adjacent('ezs42', north, North).
| ?- geohash::neighbors('ezs42', Neighbors).

Cover a bounding box with geohashes of a fixed precision:

| ?- geohash::covering(bbox(geographic(42.58, -5.63), geographic(42.63, -5.58)), 5, Geohashes).

Compute adaptive and compact covers:

| ?- geohash::covering(bbox(geographic(42.58, -5.63), geographic(42.63, -5.58)), max_precision(6), Geohashes, []).

| ?- geohash::covering(bbox(geographic(42.58, -5.63), geographic(42.63, -5.58)), precision(6), Geohashes, [compact(true), min_precision(4)]).

Inspect hierarchy and integer representations:

| ?- geohash::parent('ezs42', Parent).

| ?- geohash::children('ezs4', Children).

| ?- geohash::encode_int(geographic(42.6, -5.6), 5, HashInteger).

| ?- geohash::bounding_box_int(HashInteger, 5, BoundingBox).

| ?- geohash::geohash_to_int('ezs42', HashInteger).

| ?- geohash::int_to_geohash(HashInteger, 5, Geohash).

| ?- geohash::encode_bits(geographic(42.6, -5.6), 25, HashInteger).

| ?- geohash::expand('ezs42', ExpandedGeohashes).

Cover polygons and polylines:

| ?- geohash::polygon_covering(
|       [geographic(42.5830078125, -5.625), geographic(42.626953125, -5.625), geographic(42.626953125, -5.5810546875), geographic(42.5830078125, -5.5810546875)],
|       precision(5),
|       Geohashes,
|       []
|   ).

| ?- geohash::polyline_covering(
|       [geographic(42.60498046875, -5.60302734375), geographic(42.60498046875, -5.55)],
|       precision(5),
|       Geohashes,
|       [buffer(0.1)]
|   ).

Notes

  • Geohash cell width and height alternate between equal and unequal angular resolution depending on whether the geohash length is even or odd.

  • Encoding canonicalizes longitude 180.0 to -180.0 so dateline-equivalent coordinates map to the same hash.

  • Neighbor and covering computations use decoded cell dimensions and therefore handle antimeridian wrapping for eastward and westward movement.

  • Northward adjacency from the northernmost cells and southward adjacency from the southernmost cells fails because there is no cell beyond the poles.

  • Bounding-box covering accepts antimeridian-crossing boxes by allowing the minimum longitude to be greater than the maximum longitude.

  • Degenerate point bounding boxes are accepted by covering/3 and return the single geohash cell containing the point.

  • covering/4 keeps covering/3 as the fixed-precision shorthand while adding a CoverSpec argument with precision(Precision) and max_precision(MaxPrecision) forms.

  • geohash now imports the shared options library and uses it to validate and merge covering options.

  • covering/4 and polygon_covering/4 accept compact(Boolean) and min_precision(PositiveInteger) options. Compact covers never compress below the selected minimum precision.

  • polyline_covering/4 also accepts a buffer(Distance) option where the distance is expressed in kilometers.

  • compress/2 operates on complete sibling sets only, replacing them with parent prefixes while preserving the covered area.

  • Integer-backed predicates mirror the string predicates and keep the precision argument explicit, avoiding any hidden dependence on integer bit width.

  • Bit-precision predicates provide the same packed integer representation while measuring precision directly in bits instead of characters.

  • polygon_covering/4 and polyline_covering/4 rely on generic geospatial geometry predicates and currently do not support antimeridian-crossing input geometries.

References