Geohash

From Rosetta Code
Revision as of 09:47, 13 June 2020 by PureFox (talk | contribs) (Added Go)
Geohash is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Geohashes[1] are used to represent standard latitude and longitude coordinates as single values in the form of a simple string -- using the digits (0-9) and the letters (B-Z excluding I, L, O). They can vary in length, with more characters in the string representing more precision.


Task

Generate a Geohash with a desired precision from a coordinate represented as an array of two doubles, latitude and longitude.


Example 1:
print (encodeGeohash (for: [51.433718, -0.214126], withPrecision: 2))
// Result: "gc" (all of Ireland, most of England and Wales, small part of Scotland)
Example 2:
print (encodeGeohash (for: [51.433718, -0.214126], withPrecision: 9))
// Result: "gcpue5hp4" (the umpire's chair on Center Court at Wimbledon)


From the Wikipedia page, geohashes can be "useful in database systems where queries on a single index are much easier or faster than multiple-index queries."

Go

Translation of: Swift

<lang go>package main

import (

   "fmt"
   "strings"

)

type Location struct{ lat, lng float64 }

func (loc Location) String() string { return fmt.Sprintf("[%f, %f]", loc.lat, loc.lng) }

type Range struct{ lower, upper float64 }

var gBase32 = "0123456789bcdefghjkmnpqrstuvwxyz"

func encodeGeohash(loc Location, prec int) string {

   latRange := Range{-90, 90}
   lngRange := Range{-180, 180}
   var hash strings.Builder
   hashVal := 0
   bits := 0
   even := true
   for hash.Len() < prec {
       val := loc.lat
       rng := latRange
       if even {
           val = loc.lng
           rng = lngRange
       }
       mid := (rng.lower + rng.upper) / 2
       if val > mid {
           hashVal = (hashVal << 1) + 1
           rng = Range{mid, rng.upper}
           if even {
               lngRange = Range{mid, lngRange.upper}
           } else {
               latRange = Range{mid, latRange.upper}
           }
       } else {
           hashVal <<= 1
           if even {
               lngRange = Range{lngRange.lower, mid}
           } else {
               latRange = Range{latRange.lower, mid}
           }
       }
       even = !even
       if bits < 4 {
           bits++
       } else {
           bits = 0
           hash.WriteByte(gBase32[hashVal])
           hashVal = 0
       }
   }
   return hash.String()

}

func main() {

   locs := []Location{
       {51.433718, -0.214126},
       {51.433718, -0.214126},
       {57.64911, 10.40744},
   }
   precs := []int{2, 9, 11}
   for i, loc := range locs {
       geohash := encodeGeohash(loc, precs[i])
       fmt.Printf("geohash for %v, precision %-2d = %s\n", loc, precs[i], geohash)
   }

}</lang>

Output:
geohash for [51.433718, -0.214126], precision 2  = gc
geohash for [51.433718, -0.214126], precision 9  = gcpue5hp4
geohash for [57.649110, 10.407440], precision 11 = u4pruydqqvj


Swift

<lang Swift>let gBase32 = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "b", "c", "d", "e", "f", "g", "h", "j", "k", "m", "n", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]

func encodeGeohash (for location: Array<Double>, withPrecision precision: Int = 9) -> String {

 var latitudeRange = -90.0...90.0
 var longitudeRange = -180...180.0
 var hash = ""
 var hashVal = 0
 var bits = 0
 var even = true
 while (hash.count < precision) {
   let val     = even ? location[1]: location[0]
   var range   = even ? longitudeRange : latitudeRange
   let mid     = (range.lowerBound + range.upperBound) / 2
   if (val > mid) {
     hashVal = (hashVal << 1) + 1
     range = mid...range.upperBound
     if even { longitudeRange = mid...longitudeRange.upperBound }
     else    { latitudeRange	 = mid...latitudeRange.upperBound }
   } else {
     hashVal = (hashVal << 1) + 0
     if even	{ longitudeRange = longitudeRange.lowerBound...mid }
     else    { latitudeRange	 = latitudeRange.lowerBound...mid }
   }
   even = !even
   if (bits < 4) {
     bits += 1
   } else {
     bits = 0
     hash += gBase32[hashVal]
     hashVal = 0
   }
 }
 return hash

}