-
Notifications
You must be signed in to change notification settings - Fork 0
/
geo.go
151 lines (140 loc) · 4.65 KB
/
geo.go
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package main
import (
"fmt"
"github.com/golang/geo/s1"
"github.com/golang/geo/s2"
"log"
"net/url"
"time"
"tinderFetch/api"
)
// EarthRadiusM Earth radius in meters
const (
EarthRadiusM = 6371000.0
DefaultMaxLevel = 13
DefaultMinLevel = 2
DefaultMaxCells = 1024
MilesInKm = 1.60934
)
func getSidewalkURL(reqCoord api.Coordinates, cu s2.CellUnion) string {
v := url.Values{}
v.Set("center", fmt.Sprintf("%f,%f", reqCoord.Latitude, reqCoord.Longitude))
v.Set("zoom", "15")
var cellTokens string
for _, cellID := range cu {
if len(cellTokens) < 1 {
cellTokens = cellID.ToToken()
}
cellTokens = fmt.Sprintf("%s,%s", cellTokens, cellID.ToToken())
}
v.Set("cells", cellTokens)
return "https://s2.sidewalklabs.com/regioncoverer/?" + v.Encode()
}
func getAngleByRadius(radius float64) s1.Angle {
centerAngle := float64(radius) / EarthRadiusM
return s1.Angle(centerAngle)
}
func getRadiusByAngle(angle s1.Angle) float64 {
return EarthRadiusM * angle.Radians()
}
func getEdgeCellsByRadius(cu s2.CellUnion, reqCoord api.Coordinates, radius float64) s2.CellUnion {
ll := s2.LatLngFromDegrees(reqCoord.Latitude, reqCoord.Longitude)
centerPoint := s2.PointFromLatLng(ll)
var edgeCells s2.CellUnion
for _, cellID := range cu {
cell := s2.CellFromCellID(cellID)
distance := getRadiusByAngle(cell.MaxDistance(centerPoint).Angle())
if distance >= float64(radius) {
edgeCells = append(edgeCells, cellID)
}
}
return edgeCells
}
// take the smallest cell from union
func getRandomPosition(cu s2.CellUnion) api.Coordinates {
var chosenPosition api.Coordinates
var maxLevel int
for _, cellID := range cu {
if cellID.Level() > maxLevel {
maxLevel = cellID.Level()
ll := cellID.LatLng()
chosenPosition = api.Coordinates{Latitude: ll.Lat.Degrees(), Longitude: ll.Lng.Degrees()}
}
}
return chosenPosition
}
func milesToMeters(mi int) float64 {
return float64(mi) * MilesInKm * 1000
}
func getCellsByCoord(reqCoord api.Coordinates, radius float64) s2.CellUnion {
ll := s2.LatLngFromDegrees(reqCoord.Latitude, reqCoord.Longitude)
point := s2.PointFromLatLng(ll)
cap := s2.CapFromCenterAngle(point, getAngleByRadius(radius))
rc := s2.RegionCoverer{MaxLevel: DefaultMaxLevel, MinLevel: DefaultMinLevel, MaxCells: DefaultMaxCells}
return rc.Covering(cap)
}
func getUserPosition(tinderAPI *api.TinderAPI, userID string, profilePos api.Coordinates, tiles s2.CellUnion) (api.Coordinates, s2.CellUnion, error) {
user, err := tinderAPI.GetUser(userID)
if err != nil {
return profilePos, nil, err
}
distance := milesToMeters(user.Distance)
log.Printf("Distance between you and user: %f meters", distance)
oldTiles := tiles
if user.Distance <= 1 {
log.Println("Distance too short. Return cell related to current position")
tiles = getCellsByCoord(profilePos, distance)
if oldTiles != nil {
tiles = s2.CellUnionFromIntersection(oldTiles, tiles)
}
log.Println(getSidewalkURL(profilePos, tiles))
return profilePos, tiles, nil
}
tiles = getEdgeCellsByRadius(getCellsByCoord(profilePos, distance), profilePos, distance)
if oldTiles != nil {
log.Printf("Make intersection between two tile sets")
tiles = s2.CellUnionFromIntersection(oldTiles, getEdgeCellsByRadius(tiles, profilePos, distance))
}
log.Println(getSidewalkURL(profilePos, tiles))
if len(tiles) < 1 {
log.Println("No intersected tiles. Something went wrong. Return last non-empty tile set")
return profilePos, oldTiles, nil
}
if len(tiles) <= 1 {
log.Println("Only one tile, return it")
if tiles[0].Level() < DefaultMaxLevel {
log.Println("Tile are too big. Split it")
children := tiles[0].Children()
return getUserPosition(tinderAPI, userID, profilePos, children[:])
}
return profilePos, tiles, nil
}
newCoord := getRandomPosition(tiles)
log.Printf("Change user position to %v (sleep for 5 minutes)", newCoord)
time.Sleep(time.Minute * 5)
if err := tinderAPI.ChangeProfilePosition(newCoord); err != nil {
log.Printf("Unable to change user position: %v", err)
return profilePos, tiles, nil
}
return getUserPosition(tinderAPI, userID, newCoord, tiles)
}
// GetUserPosition get user approximate geo position by user ID. It doesn't restore your position
func GetUserPosition(tinderAPI *api.TinderAPI, userID string) (string, error) {
profile, err := tinderAPI.GetProfile()
if err != nil {
return "", err
}
/*
currentPosition := profile.Position
defer func() {
if err := api.ChangeProfilePosition(currentPosition); err != nil {
log.Printf("Unable to restore user position: %v", err)
}
}()
*/
newPosition, tiles, err := getUserPosition(tinderAPI, userID, profile.Position, nil)
if err != nil {
return "", err
}
return getSidewalkURL(newPosition, tiles), nil
}