Geohash: Difference between revisions
(Initial submission.) |
(Added Go) |
||
Line 16: | Line 16: | ||
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." |
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." |
||
=={{header|Go}}== |
|||
{{trans|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> |
|||
{{out}} |
|||
<pre> |
|||
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 |
|||
</pre> |
|||
=={{header|Swift}}== |
=={{header|Swift}}== |
Revision as of 09:47, 13 June 2020
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
<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
}