Geohash: Difference between revisions
(→{{header|Swift}}: Insert Python header) |
|||
Line 200: | Line 200: | ||
</pre> |
</pre> |
||
Not surprisingly, "gc" is not even accurate to one significant digit, but a precision of 9 is accurate to 5 or 6 significant digits, and 11 to 6 or 7 digits. |
Not surprisingly, "gc" is not even accurate to one significant digit, but a precision of 9 is accurate to 5 or 6 significant digits, and 11 to 6 or 7 digits. |
||
=={{header|Python}}== |
|||
=={{header|Swift}}== |
=={{header|Swift}}== |
Revision as of 09:19, 15 June 2020
Geohashes 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."
- Reference
Factor
Factor comes with the geohash
vocabulary. See the implementation here.
<lang factor>USING: formatting generalizations geohash io kernel sequences ;
- encode-geohash ( latitude longitude precision -- str )
[ >geohash ] [ head ] bi* ;
! Encoding 51.433718 -0.214126 2 51.433718 -0.214126 9 57.649110 10.407440 11 [
3dup encode-geohash "geohash for [%f, %f], precision %2d = %s\n" printf
] 3 3 mnapply nl
! Decoding "u4pruydqqvj" dup geohash> "coordinates for %s ~= [%f, %f]\n" printf</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 coordinates for u4pruydqqvj ~= [57.649110, 10.407439]
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
Phix
<lang Phix>constant gBase32 = "0123456789bcdefghjkmnpqrstuvwxyz"
function encode_geohash(sequence location, integer precision)
sequence r = {{-90,90},{-180,180}} -- lat/long integer ll = 2, -- " " " hashval = 0, bits = 0 string hash = "" while length(hash) < precision do atom mid = sum(r[ll])/2, gt = location[ll]>mid hashval = hashval*2+gt r[ll][2-gt] = mid bits += 1 if bits=5 then hash &= gBase32[hashval+1] {hashval,bits} = {0,0} end if ll = 3-ll -- (1 <==> 2) end while return hash
end function
function decode_geohash(string hash) -- output is {{lat_lo,lat_hi},{long_lo,long_hi}}
sequence r = {{-90,90},{-180,180}} -- lat/long integer ll = 2 -- " " " for h=1 to length(hash) do string b = sprintf("%05b",find(hash[h],gBase32)-1) for it=1 to 5 do r[ll][2-(b[it]='1')] = sum(r[ll])/2 ll = 3-ll -- (1 <==> 2) end for end for return r
end function
sequence tests = {{{51.433718, -0.214126}, 2},
{{51.433718, -0.214126}, 9}, {{57.64911, 10.40744 }, 11}}
for i=1 to length(tests) do
{sequence location, integer precision} = tests[i] string geohash = encode_geohash(location, precision) printf(1,"geohash for %v, precision %d = %s\n",{location, precision, geohash}) tests[i] = geohash
end for
printf(1,"\ndecode tests:\n") tests = append(tests,"ezs42") for i=1 to length(tests) do
printf(1,"%-12s ==> %v\n",{tests[i],decode_geohash(tests[i])})
end for</lang>
- Output:
geohash for {51.433718,-0.214126}, precision 2 = gc geohash for {51.433718,-0.214126}, precision 9 = gcpue5hp4 geohash for {57.64911,10.40744}, precision 11 = u4pruydqqvj decode tests: gc ==> {{50.625,56.25},{-11.25,0}} gcpue5hp4 ==> {{51.43369675,51.43373966},{-0.2141475677,-0.2141046524}} u4pruydqqvj ==> {{57.64910996,57.6491113},{10.40743902,10.40744036}} ezs42 ==> {{42.58300781,42.62695312},{-5.625,-5.581054688}}
Not surprisingly, "gc" is not even accurate to one significant digit, but a precision of 9 is accurate to 5 or 6 significant digits, and 11 to 6 or 7 digits.
Python
Swift
<lang Swift>let base32 = "0123456789bcdefghjkmnpqrstuvwxyz" // no "a", "i", "l", or "o"
extension String {
subscript(i: Int) -> String { String(self[index(startIndex, offsetBy: i)]) }
}
struct Coordinate {
var latitude: Double var longitude: Double
func toString() -> String { var latitudeHemisphere = "" var longitudeHemisphere = ""
latitudeHemisphere = latitude < 0 ? " S" : " N" longitudeHemisphere = longitude < 0 ? " W" : " E"
return "\(abs(latitude))\(latitudeHemisphere), \(abs(longitude))\(longitudeHemisphere)" }
}
func encodeGeohash (for coordinate: Coordinate, 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 ? coordinate.longitude: coordinate.latitude 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 range = range.lowerBound...mid
if even { longitudeRange = longitudeRange.lowerBound...mid } else { latitudeRange = latitudeRange.lowerBound...mid } }
even = !even
if (bits < 4) { bits += 1 } else { bits = 0 hash += base32[hashVal] hashVal = 0 } } return hash
}
let coordinate1 = Coordinate(latitude: 51.433718, longitude: -0.214126) let coordinate2 = Coordinate(latitude: 57.649110, longitude: 10.407440)
print ("Geohash for: \(coordinate1.toString()), precision = 5 : \(encodeGeohash(for: coordinate, withPrecision: 5))") print ("Geohash for: \(coordinate1.toString()), precision = 9 : \(encodeGeohash(for: coordinate))") print ("Geohash for: \(coordinate2.toString()), precision = 11 : \(encodeGeohash(for: coordinate, withPrecision: 11))")
</lang>
- Output:
Geohash for: 51.433718 N, 0.214126 W, precision = 5 : gcpue Geohash for: 51.433718 N, 0.214126 W, precision = 9 : gcpue5hp4 Geohash for: 57.64911 N, 10.40744 E, precision = 11 : u4pruydqqvj
Wren
<lang ecmascript>import "/fmt" for Fmt
var gBase32 = "0123456789bcdefghjkmnpqrstuvwxyz"
var encodeGeohash = Fn.new { |location, prec|
var latRange = -90..90 var lngRange = -180..180 var hash = "" var hashVal = 0 var bits = 0 var even = true while (hash.count < prec) { var val = even ? location[1] : location[0] var rng = even ? lngRange : latRange var mid = (rng.from + rng.to) / 2 if (val > mid) { hashVal = hashVal*2 + 1 rng = mid..rng.to if (even) lngRange = mid..lngRange.to else latRange = mid..latRange.to } else { hashVal = hashVal * 2 if (even) lngRange = lngRange.from..mid else latRange = latRange.from..mid } even = !even if (bits < 4) { bits = bits + 1 } else { bits = 0 hash = hash + gBase32[hashVal] hashVal = 0 } } return hash
}
var data = [
[[51.433718, -0.214126], 2], [[51.433718, -0.214126], 9], [[57.64911, 10.40744 ], 11]
]
for (d in data) {
var geohash = encodeGeohash.call(d[0], d[1]) var loc = "[%(Fmt.f(9, d[0][0], 6)), %(Fmt.f(9, d[0][1], 6))]" System.print("geohash for %(loc), precision %(Fmt.d(-2, d[1])) = %(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