Function Repository Resource:

GeoHash

Source Notebook

Geohash encoding and decoding

Contributed by: Anton Antonov

ResourceFunction["GeoHash"][lat,lon]

finds the geohash for the geo location with coordinates lat and lon.

ResourceFunction["GeoHash"][GeoPosition[lat,lon]]

finds the geohash of a geo position.

ResourceFunction["GeoHash"][geoHash]

decodes the given geoHash into a geo position.

ResourceFunction["GeoHash"][geoHash,fmt]

give the decoded result in the form fmt.

Details and Options

ResourceFunction["GeoHash"] is overloaded to do both encoding and decoding.
ResourceFunction["GeoHash"] is a geo coding system that encodes geographic coordinates into a short string of letters and digits.
The system allows arbitrary precision and can gradually lose precision by removing characters from the end of the code.
The system divides space into buckets of grid shape, using an application of a Z-order curve and space-filling curves.
The longer the shared prefix between two geohashes, the closer they are spatially. This does not apply in reverse though -- two points can be very close but may not share a prefix.
Geohashes can be used as unique identifiers and to represent point data in databases.
Geohashes have been proposed for geotagging.
The data structure offers advantages in databases. Geohashed data makes queries on a single index easier or faster than multiple-index queries. The structure can also provide a quick, though not entirely accurate, proximity search.
ResourceFunction["GeoHash"] uses a spatial index of base 4 and is transformed into a discrete grid for a more compact code. It uses base 32 and represents the values using an alphabet of standard ASCII characters.
The function ResourceFunction["GeoHash"] is overloaded to do encoding, decoding and provide properties. Here is table showing different forms of input and corresponding types.

Examples

Basic Examples (2) 

Encode a given geo position:

In[1]:=
ResourceFunction["GeoHash"][GeoPosition[{57.64911, 10.40744}]]
Out[1]=

Decode:

In[2]:=
ResourceFunction["GeoHash"][%] // N
Out[2]=

Scope (8) 

Decoding result form (5) 

By default the decoding gives the mean point of the corresponding geo rectangle as a GeoPosition object:

In[3]:=
ResourceFunction["GeoHash"]["u4pruydqq", GeoPosition] // N
Out[3]=

If Mean is given a second argument then the mean point of the corresponding geo rectangle is returned:

In[4]:=
ResourceFunction["GeoHash"]["u4pruydqq", Mean] // N
Out[4]=

Using Association as a second argument gives the geo rectangle coordinates in a nested association:

In[5]:=
ResourceFunction["GeoHash"]["u4pruydqq", Association] // N // InputForm
Out[5]=

Often it is convenient to get the decoding result as a list of min-min and max-max geo positions of the corresponding geo rectangle:

In[6]:=
b = ResourceFunction["GeoHash"]["dp1hpgt", GeoBoundingBox]
Out[6]=

Visualize the boundary of the result above:

In[7]:=
GeoGraphics[GeoBoundsRegionBoundary[b]]
Out[7]=

Neighbors (2) 

If the first argument is a valid geohash string and the second argument is "Neighbors" then a list of neighboring geohashes is returned:

In[8]:=
gh = "dp1hpgt";
nns = ResourceFunction["GeoHash"][gh, "Neighbors"]
Out[4]=

Here is a corresponding geo plot:

In[9]:=
GeoGraphics[{Map[
    GeoMarker[ResourceFunction["GeoHash"][#, GeoPosition], "Color" -> Blue] &, nns], GeoMarker[ResourceFunction["GeoHash"][gh, GeoPosition]]}] // Image
Out[9]=

Alphabet (1) 

If the first argument is "Alphabet" then the Geohash encoding alphabet is returned:

In[10]:=
ResourceFunction["GeoHash"]["Alphabet"]
Out[10]=

Options (2) 

Precision (2) 

Precision is used to confine the given location into into geo rectangle with an universal identifier (i.e. the geohash). Here are geohashes of the same geo point with different precisions:

In[11]:=
p = GeoPosition[{40.101976`, -88.2314378`}];
a = Association@
  Map[# -> ResourceFunction["GeoHash"][p, "Precision" -> #] &, {4, 5, 6}]
Out[4]=

Here are maps with the corresponding bounding regions:

In[12]:=
Map[Image[#, ImageSize -> 300] &@
   GeoGraphics[{GeoMarker[p], Blue, GeoBoundsRegionBoundary@
      ResourceFunction["GeoHash"][#, GeoBoundingBox]}, ImageSize -> Medium] &, a]
Out[12]=

Applications (10) 

Find and plot geohash nearest neighbors (2) 

Find nearest neighbors of a geohash at different precision levels:

In[13]:=
gh = "dp1hpgt";
nns = Table[
  ResourceFunction["GeoHash"][StringDrop[gh, -i], "Neighbors"], {i, 0,
    1}]
Out[4]=

Here is a corresponding Geographics plot:

In[14]:=
GeoGraphics[{
   MapIndexed[
    Function[{nnsBelt, ind}, Map[GeoMarker[ResourceFunction["GeoHash"][#, GeoPosition], "Color" -> Nest[Lighter, Blue, ind[[1]]]] &, nnsBelt]], nns],
   GeoMarker[ResourceFunction["GeoHash"][gh, GeoPosition]]
   }] // Image
Out[14]=

Location nearest neighbors (8) 

Get all cities in Illinois, USA:

In[15]:=
lsCities = CityData[{All, "Illinois", "UnitedStates"}];
Length[lsCities]
Out[4]=

Make an association of the city names to corresponding coordinates:

In[16]:=
aCityLocations = Association@
   Map[CityData[#, "Name"] -> CityData[#, "Coordinates"] &, lsCities];
Length[aCityLocations]
Out[6]=

Compute geohashes for all cities:

In[17]:=
AbsoluteTiming[
 aCityGeoHashes = ResourceFunction["GeoHash"] /@ aCityLocations;
 ]
Out[17]=

Find the geo coordinates of a certain focus location:

In[18]:=
loc = FindGeoLocation["100 Trade Centre Dr, Champaign, IL"]
Out[18]=

Find the corresponding geohash:

In[19]:=
gh = ResourceFunction["GeoHash"][loc]
Out[19]=

Find the Nearest Neighbors (NNs) of the location by matching a prefix of the focus location geohash in the association of Illinois-geohashes:

In[20]:=
aNNs = Select[aCityGeoHashes, StringStartsQ[#, StringTake[gh, 4]] &];
aNNs
Out[21]=

Plot the focus location and the found NNs:

In[22]:=
GeoGraphics[{KeyValueMap[
   Tooltip[GeoMarker[ResourceFunction["GeoHash"][#2, GeoPosition], "Color" -> Blue], #1] &, aNNs], GeoMarker[loc, "Color" -> Red]},
  GeoScaleBar -> {"Imperial", "Metric"}]
Out[22]=

Using a shorter prefix produces more NNs:

In[23]:=
aNNs3 = Select[aCityGeoHashes, StringStartsQ[#, StringTake[gh, 3]] &];
GeoGraphics[{KeyValueMap[
    Tooltip[GeoMarker[ResourceFunction["GeoHash"][#2, GeoPosition], "Color" -> Blue], #1] &, aNNs3], GeoMarker[loc, "Color" -> Red]}, GeoScaleBar -> {"Imperial", "Metric"}, ImageSize -> Medium] // Image
Out[24]=

Properties and Relations (3) 

The geohashes represent nested rectangles -- adding a new "legal" alphabet character to geohash produces nested rectangles of finer granularity:

In[25]:=
coreHash = "dp1hp";
Legended[
 GeoGraphics[{
    Red, GeoBoundsRegionBoundary /@ Map[ResourceFunction["GeoHash"][coreHash <> #, GeoBoundingBox] &,
       ResourceFunction["GeoHash"]["Alphabet"]],
    Lighter[Blue], GeoBoundsRegionBoundary /@ Map[ResourceFunction["GeoHash"][coreHash <> "7" <> #, GeoBoundingBox] &, ResourceFunction["GeoHash"]["Alphabet"]],
    GrayLevel[0.4], GeoBoundsRegionBoundary /@ Map[ResourceFunction["GeoHash"][coreHash <> "d" <> #, GeoBoundingBox] &, ResourceFunction["GeoHash"]["Alphabet"]],
    }, ImageSize -> Medium, PlotLabel -> coreHash] // Image,
 SwatchLegend[{Red, Lighter[Blue], GrayLevel[0.4]}, coreHash <> # & /@ {"", "7", "d"}]
 ]
Out[4]=

Here is another illustration of the geohash nesting -- the core geohash is extended with all characters of the alphabet twice:

In[26]:=
coreHash = "dp2f";
GeoGraphics[{
   Red, GeoBoundsRegionBoundary /@ Map[ResourceFunction["GeoHash"][coreHash <> #, GeoBoundingBox] &, ResourceFunction["GeoHash"]["Alphabet"]],
   Blue, Thickness[0.003], Line@Flatten[
     Map[Function[{p}, Map[ResourceFunction["GeoHash"][coreHash <> p <> #, GeoPosition] &, ResourceFunction["GeoHash"]["Alphabet"]]], ResourceFunction["GeoHash"]["Alphabet"]], 1],
   Black, Thickness[0.005], Arrow@Map[
     ResourceFunction["GeoHash"][coreHash <> #, GeoPosition] &, ResourceFunction["GeoHash"]["Alphabet"]],
   FaceForm[Yellow], EdgeForm[None], Map[{Disk[ResourceFunction["GeoHash"][coreHash <> #, GeoPosition], 0.007], Text[
       Style[#, Red, Bold, Background -> Yellow, FontSize -> 15], ResourceFunction["GeoHash"][coreHash <> #, GeoPosition]]} &, ResourceFunction["GeoHash"]["Alphabet"]]
   }, GeoBackground -> "StreetMapNoLabels", ImageSize -> Large] // Image
Out[6]=

Geohashes with an even number of digits result in a regular grid. Those with an odd number of digits result in an irregular intermediary grid.

The system can be used for proximity searches, but limitations exist. Locations close but on either side of the equator or Greenwich meridian will not have a shared prefix. The proximity search also breaks down at the 180 - degree meridians and the poles.


Here is how Rectangle and RegionMember can be used to verify that a geo position point belongs to the geo rectangle given by GeoHash:

In[27]:=
p = GeoPosition[{23.32, -32.3}];
h = ResourceFunction["GeoHash"][p];
reg = Rectangle @@ Map[N@*First, ResourceFunction["GeoHash"][h, GeoBoundingBox]];
RegionMember[reg, p[[1]]]
Out[6]=

Possible Issues (1) 

Not every ASCII string is a valid geohash. Here are the characters not used for GeoHash encoding:

In[28]:=
Complement[CharacterRange["0", "9"]~Join~CharacterRange["a", "z"], ResourceFunction["GeoHash"]["Alphabet"]]
Out[28]=

Neat Examples (1) 

Visualize the Z-filling curve that corresponds to found hashes:

In[29]:=
n = 2;
(*p=FindGeoLocation["Gaborone Botswana"];*)
p = GeoPosition[{-24.655319`, 25.908728`}];
coreHash = ResourceFunction["GeoHash"][p, "Precision" -> 1];
ghs = Nest[
   Flatten[Function[{h}, Map[h <> # &, ResourceFunction["GeoHash"]["Alphabet"]]] /@ #] &, {coreHash},
    n];
line1 = Line[ResourceFunction["GeoHash"][#, GeoPosition] & /@ ghs];
p = GeoPosition[{14.655319`, 25.908728`}];
coreHash = ResourceFunction["GeoHash"][p, "Precision" -> 1];
ghs = Nest[
   Flatten[Function[{h}, Map[h <> # &, ResourceFunction["GeoHash"]["Alphabet"]]] /@ #] &, {coreHash},
    n];
line2 = Line[ResourceFunction["GeoHash"][#, GeoPosition] & /@ ghs];
GeoGraphics[{GeoStyling["ContourMap", Opacity[0.8]], Polygon[Entity["GeographicRegion", "Africa"]], Blue, line1, line2},
  GeoProjection -> "Orthographic", GeoGridLines -> Automatic, GeoGridLinesStyle -> LightYellow, ImageSize -> {500, Automatic}] // Image
Out[22]=

Publisher

Anton Antonov

Requirements

Wolfram Language 13.0 (December 2021) or above

Version History

  • 1.0.0 – 10 June 2024

Author Notes

The Geohash system was invented by Gustavo Niemeyer in 2008.
Despite limitations, Geohash has been used successfully in Elasticsearch, MongoDB, HBase, Redis, and Accumulo for proximity searches.
Geohash was placed in the public domain by its inventor in 2008. Although comparable algorithms have been patented or had copyright claimed, Geohash is based on a separate algorithm and approach. It is standardized as CTA-5009.
Other similar indexing systems exist, including Geohash-36, Grid (spatial index), Maidenhead Locator System, Natural Area Code, and many others.

License Information