Geohash: Difference between revisions
Add Scala implementation
No edit summary |
(Add Scala implementation) |
||
(13 intermediate revisions by 7 users not shown) | |||
Line 30:
{{trans|Python}}
<
V bool2ch = Dict(enumerate(ch32), (i, ch) -> (bin(i).zfill(5), ch))
V ch2bool = Dict(bool2ch.items(), (k, v) -> (v, k))
Line 71:
(51.433718, -0.214126, 9),
(57.64911, 10.40744, 11)]
print(‘encoder(lat=#.6, lng=#.6, pre=#.) = '#.'’.format(lat, lng, pre, encoder(lat, lng, pre)))</
{{out}}
Line 83:
{{libheader|Action! Tool Kit}}
{{libheader|Action! Real Math}}
<
CHAR ARRAY code32="0123456789bcdefghjkmnpqrstuvwxyz"
Line 202:
Test("51.433718","-0.214126",9)
Test("57.64911","10.40744",11)
RETURN</
{{out}}
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/Geohash.png Screenshot from Atari 8-bit computer]
Line 222:
Factor comes with the <code>geohash</code> vocabulary. See the implementation [https://docs.factorcode.org/content/vocab-geohash.html here].
{{works with|Factor|0.99 2020-03-02}}
<
: encode-geohash ( latitude longitude precision -- str )
Line 238:
! Decoding
"u4pruydqqvj" dup geohash>
"coordinates for %s ~= [%f, %f]\n" printf</
{{out}}
<pre>
Line 249:
=={{header|F_Sharp|F#}}==
<
// Create a geoHash String. Nigel Galloway: June 26th., 2020
let fG n g=Seq.unfold(fun(α,β)->let τ=(α+β)/2.0 in Some(if τ>g then (0,(α,τ)) else (1,(τ,b)))) n
Line 257:
let geoHash n g z=let N="0123456789bcdefghjkmnpqrstuvwxyz" in [|for τ in fN n g z do yield N.[fI τ]|] |> System.String
printfn "%s\n%s\n%s" (geoHash 51.433718 -0.214126 2) (geoHash 51.433718 -0.214126 9) (geoHash 57.64911 10.40744 11)
</syntaxhighlight>
{{out}}
<pre>
Line 266:
=={{header|Go}}==
{{trans|Swift}}
<
import (
Line 336:
fmt.Printf("geohash for %v, precision %-2d = %s\n", loc, precs[i], geohash)
}
}</
{{out}}
Line 343:
geohash for [51.433718, -0.214126], precision 9 = gcpue5hp4
geohash for [57.649110, 10.407440], precision 11 = u4pruydqqvj
</pre>
=={{header|J}}==
<syntaxhighlight lang=J>gdigits=: '0123456789bcdefghjkmnpqrstuvwxyz'
geohash=: {{
bits=. 3*x
x{.gdigits{~_5 #.\,|:|.(bits#2)#:<.(2^bits)*(y+90 180)%180 360
}}</syntaxhighlight>
Note that the test cases suggest that rounding should never be used when generating a geohash. This guarantees that a short geohash is always a prefix of a longer geohash for the same location.
<syntaxhighlight lang=J> 2 geohash 51.433718 _0.214126
gc
9 geohash 51.433718 _0.214126
gcpue5hp4
11 geohash 57.64911 10.40744
u4pruydqqvj</syntaxhighlight>
And, going the other direction (producing a min and max lat and long value for the geohash):
<syntaxhighlight lang=J>hsahoeg=: {{
bits=: |.|:0,~_2]\,(5#2)#:gdigits i.y
scale=: %2^{:$bits
lo=: scale*#.bits
hi=: scale*(2^1+1 0*2|#y)+#.bits
0.5*_180+360*lo,.hi
}}</syntaxhighlight>
This gives us:
<syntaxhighlight lang=J> hsahoeg 'gc'
50.625 56.25
_5.625 0
hsahoeg 'gcpue5hp4'
51.4337 51.4337
_0.107074 _0.107052
hsahoeg 'u4pruydqqvj'
57.6491 57.6491
5.20372 5.20372</syntaxhighlight>
Or
<syntaxhighlight lang=J> 9!:11]10 NB. display 10 digits of floating point precision
hsahoeg 'gcpue5hp4'
51.43369675 51.43373966
_0.1070737839 _0.1070523262
hsahoeg 'u4pruydqqvj'
57.64910996 57.6491113
5.203719512 5.203720182</syntaxhighlight>
=={{header|jq}}==
'''Adapted from [[#Wren|Wren]] and [[#Python|Python]]'''
{{works with|jq}}
'''Also works with gojq, the Go implementation of jq, and with fq.'''
'''Generic Utilities'''
<syntaxhighlight lang=jq>
def lpad($len; $c): tostring | ($len - length) as $l | ($c * $l)[:$l] + .;
def lpad($len): lpad($len; " ");
def round($digits): pow(10; $digits) as $p | . * $p | round | floor | . / $p;
# Convert the input integer to a string in the specified base (2 to 36 inclusive)
def convert(base):
def stream:
recurse(if . >= base then ./base|floor else empty end) | . % base ;
[stream] | reverse
| if base < 10 then map(tostring) | join("")
elif base <= 36 then map(if . < 10 then 48 + . else . + 87 end) | implode
else error("base too large")
end;
# counting from 0
def enumerate(s): foreach s as $x (-1; .+1; [., $x]);
def to_object(s; o):
reduce s as $x ({}; . + ($x|o));
</syntaxhighlight>
'''GeoHash'''
<syntaxhighlight lang=jq>
def gBase32: "0123456789bcdefghjkmnpqrstuvwxyz";
# Output: the dictionary mapping the characters in gBase32 to bitstrings:
# {"0":"00000", ... "z":"11111"}
def gBase32dict:
to_object( enumerate(gBase32|explode[]|[.]|implode);
{ (.[1]): (.[0]|convert(2)|lpad(5; "0")) } ) ;
def encodeGeohash($location; $prec):
{ latRange: [ -90, 90],
lngRange: [-180, 180],
hash: "",
hashVal: 0,
bits: 0,
even: true
}
| until (.hash|length >= $prec;
.val = if .even then $location[1] else $location[0] end
| .rng = if .even then .lngRange else .latRange end
| .mid = (.rng[0] + .rng[1]) / 2
| if .val > .mid
then .hashVal |= .*2 + 1
| .rng = [.mid, .rng[1]]
| if .even then .lngRange = [.mid, .lngRange[1]] else .latRange = [.mid, .latRange[1]] end
else .hashVal *= 2
| if .even then .lngRange = [.lngRange[0], .mid] else .latRange = [.latRange[0], .mid] end
end
| .even |= not
| if .bits < 4 then .bits += 1
else
.bits = 0
| .hash += gBase32[.hashVal:.hashVal+1]
| .hashVal = 0
end)
| .hash;
def decodeGeohash:
def flip: if . == 0 then 1 else 0 end;
def chars: explode[] | [.] | implode;
# input: a 0/1 string
# output: a stream of 0/1 integers
def bits: explode[] | . - 48;
. as $geo
| gBase32dict as $gBase32dict
| {minmaxes: [[-90.0, 90.0], [-180.0, 180.0]], latlong: 1 }
| reduce ($geo | chars) as $c (.;
reduce ($gBase32dict[$c]|bits) as $bit (.;
.minmaxes[.latlong][$bit|flip] = ((.minmaxes[.latlong] | add) / 2)
| .latlong |= flip))
| .minmaxes ;
def data:
[[51.433718, -0.214126], 2],
[[51.433718, -0.214126], 9],
[[57.64911, 10.40744 ], 11]
;
data
| encodeGeohash(.[0]; .[1]) as $geohash
| (.[0] | map(lpad(10)) | join(",") | "[\(.)]" ) as $loc
| "geohash for \($loc), precision \(.[1]|lpad(3)) = \($geohash)",
" decode => \($geohash|decodeGeohash|map(map(round(6))) )"
</syntaxhighlight>
{{output}}
<pre>
geohash for [ 51.433718, -0.214126], precision 2 = gc
decode => [[50.625,56.25],[-11.25,0]]
geohash for [ 51.433718, -0.214126], precision 9 = gcpue5hp4
decode => [[51.433697,51.43374],[-0.214148,-0.214105]]
geohash for [ 57.64911, 10.40744], precision 11 = u4pruydqqvj
decode => [[57.64911,57.649111],[10.407439,10.40744]]
</pre>
=={{header|Julia}}==
{{trans|Python}}
<
const bool2ch = Dict(string(i-1, base=2, pad=5) => ch for (i, ch) in enumerate(ch32))
const ch2bool = Dict(v => k for (k, v) in bool2ch)
Line 399 ⟶ 552:
println("decoded = ", decoder(encoded))
end
</
<pre>
encoder(lat=51.433718, lng=-0.214126, pre=2) = gc
Line 415 ⟶ 568:
{{trans|Julia}}
We omitted the test with precision 22 as it exceeds the capacity of a 64 bits integer.
<
const
Line 472 ⟶ 625:
let encoded = encode(lat, long, pre)
echo &"encoder(lat = {lat}, long = {long}, pre = {pre}) = {encoded}"
echo &"decoded = {decode(encoded)}</
{{out}}
Line 484 ⟶ 637:
=={{header|Perl}}==
{{trans|Raku}}
<
use warnings;
use feature 'say';
Line 536 ⟶ 689:
map { sprintf("%.@{[max(3,$precision-3)]}f", ( -($$_[0] + $$_[1]) / 2)) .
' ± ' . sprintf('%.3e', (abs($$_[0] - $$_[1]) / 2))
} geo_decode($enc);}</
{{out}}
<pre>51.433718, -0.214126, 2 (Ireland, most of England and Wales, small part of Scotland):
Line 563 ⟶ 716:
=={{header|Phix}}==
<!--<
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">gBase32</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"0123456789bcdefghjkmnpqrstuvwxyz"</span>
Line 618 ⟶ 771:
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%-22s ==> %v\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #000000;">decode_geohash</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<!--</
{{out}}
<pre>
Line 636 ⟶ 789:
=={{header|PicoLisp}}==
<
(setq *GBASE32 (chop "0123456789bcdefghjkmnpqrstuvwxyz"))
(de encode (Lat Lng Prec)
Line 662 ⟶ 815:
(println (encode 51.433718 -0.214126 2))
(println (encode 51.433718 -0.214126 9))
(println (encode 57.649110 10.407440 11))</
{{out}}
<pre>
Line 671 ⟶ 824:
=={{header|Python}}==
<
bool2ch = {f"{i:05b}": ch for i, ch in enumerate(ch32)}
ch2bool = {v : k for k, v in bool2ch.items()}
Line 718 ⟶ 871:
([57.64911, 10.40744] , 22)]:
print("encoder(lat=%f, lng=%f, pre=%i) = %r"
% (lat, lng, pre, encoder(lat, lng, pre)))</
{{out}}
Line 726 ⟶ 879:
encoder(lat=57.649110, lng=10.407440, pre=22) = 'u4pruydqqvj8pr9yc27rjr'</pre>
Note: The precision can be increased but would need
=={{header|Raku}}==
===Module based===
Reference: Used [https://www.movable-type.co.uk/scripts/geohash.html this] for verification.
<syntaxhighlight lang="raku"
use Geo::Hash;
Line 747 ⟶ 900:
# Village Raku is a development committee in north-western Nepal
# https://goo.gl/maps/33s7k2h3UrHCg8Tb6
say geo-encode(29.2021188e0, 81.5324561e0, 4);</
{{out}}
<pre>gc
Line 760 ⟶ 913:
to specify an odd precision so the error interval ends up the same for both latitude and longitude.
<syntaxhighlight lang="raku"
sub geo-encode ( Rat(Real) $latitude, Rat(Real) $longitude, Int $precision = 9 ) {
Line 800 ⟶ 953:
say 'geo-decoded: ', geo-decode($enc).map( {-.sum/2 ~ ' ± ' ~
(abs(.[0]-.[1])/2).Num.fmt('%.3e')} ).join(', ') ~ "\n";
}</
<pre>51.433718, -0.214126, 2:
geo-encoded: gc
Line 824 ⟶ 977:
geo-encoded: tv1ypk4
geo-decoded: 29.202347 ± 6.866e-04, 81.532974 ± 6.866e-04
</pre>
=={{header|RPL}}==
{{trans|Python}}
{{works with|HP|48}}
« ROT ROT DUP ∑LIST 2 /
ROT OVER <
ROT OVER 1 + 4 ROLL PUT
ROT SL ROT NOT R→B OR
» '<span style="color:blue">BISECT</span>' STO <span style="color:grey">''@ ( val (mn,mx) bits → (a,b) bits )''</span>
« "0123456789bcdefghjkmnpqrstuvwxyz" "" → coord pre ch32 hash
« { -90 90 } { -180 180 } #0
0 pre 5 * 1 - FOR j
'''IF''' j 2 MOD '''THEN'''
coord 1 GET 4 ROLL ROT <span style="color:blue">BISECT</span> ROT SWAP
'''ELSE'''
coord 2 GET ROT ROT <span style="color:blue">BISECT</span>
'''END'''
'''NEXT'''
1 pre '''START'''
ch32 OVER #31d AND B→R 1 + DUP SUB
'hash' STO+ 32 /
'''NEXT'''
3 DROPN hash
» » '<span style="color:blue">→GEOH</span>' STO <span style="color:grey">''@ ( { lat long } pre → "geohash" )''</span>
« "0123456789bcdefghjkmnpqrstuvwxyz" "" → hash ch32
« "" BIN
1 hash SIZE '''FOR''' j
ch32 hash j DUP SUB POS
1 - 32 + R→B →STR 4 OVER SIZE 1 - SUB +
'''NEXT'''
'hash' STO
{ {-90,90} {-180,180} }
1 hash SIZE '''FOR''' j
j 2 MOD 1 + DUP2 GET
hash j DUP SUB "0" == 1 +
OVER ∑LIST 2 / PUT PUT
'''NEXT'''
» » '<span style="color:blue">GEOH→</span>' STO <span style="color:grey">''@ ( "geohash" → { { latmin latmax } { longmin longmax ) }''</span>
{ 51.433718 -0.214126 } 2 <span style="color:blue">GEOH→</span>
{ 51.433718 -0.214126 } 9 <span style="color:blue">GEOH→</span>
{ 57.649110 10.407440 } 11 <span style="color:blue">GEOH→</span>
{{out}}
<pre>
3: "gc"
2: "gcpuxe0rj"
1: "u4pruydqqvj"
</pre>
RPL floating-point numbers have only 12 significant digits, which could explain the error in the second case.
"gc" <span style="color:blue">GEOH→</span>
"gcpue5hp4" <span style="color:blue">GEOH→</span>
"u4pruydqqvj" <span style="color:blue">GEOH→</span>
{{out}}
<pre>
3: { { 50.625 56.25 } { -11.25 0 } }
2: { { 51.4336967465 51.433739662 } { -.21414756775 -.214104652406 } }
1: { { 57.6491099595 57.649111301 } { 10.4074390233 10.4074403644 } }
</pre>
=={{header|Scala}}==
{{trans|Swift}}
<syntaxhighlight lang="Scala">
object Base32 {
val base32 = "0123456789bcdefghjkmnpqrstuvwxyz" // no "a", "i", "l", or "o"
}
case class Coordinate(latitude: Double, longitude: Double) {
override def toString: String = {
val latitudeHemisphere = if (latitude < 0) " S" else " N"
val longitudeHemisphere = if (longitude < 0) " W" else " E"
s"${math.abs(latitude)}$latitudeHemisphere, ${math.abs(longitude)}$longitudeHemisphere"
}
}
object GeoHashEncoder {
def encodeGeohash(coordinate: Coordinate, precision: Int = 9): String = {
var latitudeRange: (Double, Double) = (-90.0, 90.0)
var longitudeRange: (Double, Double) = (-180.0, 180.0)
var hash = ""
var hashVal = 0
var bits = 0
var even = true
while (hash.length < precision) {
val valCoord = if (even) coordinate.longitude else coordinate.latitude
val (rangeStart, rangeEnd) = if (even) longitudeRange else latitudeRange
val mid = (rangeStart + rangeEnd) / 2
if (valCoord > mid) {
hashVal = (hashVal << 1) + 1
if (even) longitudeRange = (mid, rangeEnd) else latitudeRange = (mid, rangeEnd)
} else {
hashVal = (hashVal << 1)
if (even) longitudeRange = (rangeStart, mid) else latitudeRange = (rangeStart, mid)
}
even = !even
if (bits < 4) {
bits += 1
} else {
bits = 0
hash += Base32.base32.charAt(hashVal)
hashVal = 0
}
}
hash
}
}
object Main extends App {
val coordinate1 = Coordinate(51.433718, -0.214126)
val coordinate2 = Coordinate(57.649110, 10.407440)
println(s"Geohash for: ${coordinate1.toString}, precision = 5 : ${GeoHashEncoder.encodeGeohash(coordinate1, 5)}")
println(s"Geohash for: ${coordinate1.toString}, precision = 9 : ${GeoHashEncoder.encodeGeohash(coordinate1)}")
println(s"Geohash for: ${coordinate2.toString}, precision = 11 : ${GeoHashEncoder.encodeGeohash(coordinate2, 11)}")
}
</syntaxhighlight>
{{out}}
<pre>
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
</pre>
=={{header|Swift}}==
<
extension String {
Line 899 ⟶ 1,184:
print ("Geohash for: \(coordinate2.toString()), precision = 11 : \(encodeGeohash(for: coordinate, withPrecision: 11))")
</syntaxhighlight>
{{out}}
Line 908 ⟶ 1,193:
</pre>
=={{header|V (Vlang)}}==
{{trans|go}}
<syntaxhighlight lang="v (vlang)">struct Location {
lat f64
lng f64
Line 978 ⟶ 1,263:
println("geohash for $loc, precision ${precs[i]:-2} = $geohash")
}
}</
{{out}}
<pre>geohash for [51.433718, -0.214126], precision 2 = gc
Line 987 ⟶ 1,272:
{{trans|Swift}}
{{libheader|Wren-fmt}}
<
var gBase32 = "0123456789bcdefghjkmnpqrstuvwxyz"
Line 1,032 ⟶ 1,317:
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)")
}</
{{out}}
|