-
Notifications
You must be signed in to change notification settings - Fork 16
/
geo3x3.ex
60 lines (49 loc) · 1.43 KB
/
geo3x3.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
defmodule Geo3x3 do
def encode(_lat, _lng, level) when level < 1, do: ""
def encode(lat, lng, level) do
{res, lng} = if lng >= 0, do: {"E", lng}, else: {"W", lng + 180}
lat = lat + 90 # 180:the North Pole, 0:the South Pole
unit = 180.0
{_, _, _, res} =
1..level-1
|> Enum.reduce({unit, lng, lat, res}, fn _lv, {unit, lng, lat, res} ->
unit = unit / 3
x = floor(lng / unit)
y = floor(lat / unit)
res = "#{res}#{x + y * 3 + 1}"
lng = lng - x * unit
lat = lat - y * unit
{unit, lng, lat, res}
end)
res
end
def decode(code) do
{flg, code} =
case code do
"-" <> rest -> {true, rest}
"W" <> rest -> {true, rest}
"+" <> rest -> {false, rest}
"E" <> rest -> {false, rest}
_ -> {false, code}
end
{lat, lng, level, unit} =
code
|> String.codepoints()
|> Enum.reduce_while({0.0, 0.0, 1, 180.0}, fn c, {lat, lng, level, unit} = acc ->
with {n, _} <- Integer.parse(c), true <- n > 0 do
n = n - 1
unit = unit / 3
lat = lat + div(n, 3) * unit
lng = lng + rem(n, 3) * unit
{:cont, {lat, lng, level + 1, unit}}
else
_ -> {:halt, acc}
end
end)
lat = lat + (unit / 2)
lat = lat - 90
lng = lng + (unit / 2)
lng = if flg, do: lng - 180.0, else: lng
[lat, lng, level, unit]
end
end