-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathgeo-index.go
115 lines (90 loc) · 2.73 KB
/
geo-index.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
package geoindex
var (
minLon = -180.0
minLat = -90.0
latDegreeLength = Km(111.0)
lonDegreeLength = Km(85.0)
)
type Meters float64
func Km(km float64) Meters {
return Meters(km * 1000)
}
func Meter(meters float64) Meters {
return Meters(meters)
}
type cell struct {
x int
y int
}
func cellOf(point Point, resolution Meters) cell {
x := int((-minLat + point.Lat()) * float64(latDegreeLength) / float64(resolution))
y := int((-minLon + point.Lon()) * float64(lonDegreeLength) / float64(resolution))
return cell{x, y}
}
type geoIndex struct {
resolution Meters
index map[cell]interface{}
newEntry func() interface{}
}
// Creates new geo index with resolution a function that returns a new entry that is stored in each cell.
func newGeoIndex(resolution Meters, newEntry func() interface{}) *geoIndex {
return &geoIndex{resolution, make(map[cell]interface{}), newEntry}
}
func (i *geoIndex) Clone() *geoIndex {
clone := &geoIndex{
resolution: i.resolution,
index: make(map[cell]interface{}, len(i.index)),
newEntry: i.newEntry,
}
for k, v := range i.index {
set, ok := v.(set)
if !ok {
panic("Cannot cast value to set")
}
clone.index[k] = set.Clone()
}
return clone
}
// AddEntryAt adds an entry if missing, returns the entry at specific position.
func (geoIndex *geoIndex) AddEntryAt(point Point) interface{} {
square := cellOf(point, geoIndex.resolution)
if _, ok := geoIndex.index[square]; !ok {
geoIndex.index[square] = geoIndex.newEntry()
}
return geoIndex.index[square]
}
// GetEntryAt gets an entry from the geoindex, if missing returns an empty entry without changing the index.
func (geoIndex *geoIndex) GetEntryAt(point Point) interface{} {
square := cellOf(point, geoIndex.resolution)
entries, ok := geoIndex.index[square]
if !ok {
return geoIndex.newEntry()
}
return entries
}
// Range returns the index entries within lat, lng range.
func (geoIndex *geoIndex) Range(topLeft Point, bottomRight Point) []interface{} {
topLeftIndex := cellOf(topLeft, geoIndex.resolution)
bottomRightIndex := cellOf(bottomRight, geoIndex.resolution)
return geoIndex.get(bottomRightIndex.x, topLeftIndex.x, topLeftIndex.y, bottomRightIndex.y)
}
func (geoIndex *geoIndex) get(minx int, maxx int, miny int, maxy int) []interface{} {
entries := make([]interface{}, 0, 0)
for x := minx; x <= maxx; x++ {
for y := miny; y <= maxy; y++ {
if indexEntry, ok := geoIndex.index[cell{x, y}]; ok {
entries = append(entries, indexEntry)
}
}
}
return entries
}
func (g *geoIndex) getCells(minx int, maxx int, miny int, maxy int) []cell {
indices := make([]cell, 0)
for x := minx; x <= maxx; x++ {
for y := miny; y <= maxy; y++ {
indices = append(indices, cell{x, y})
}
}
return indices
}