用Goleveldb 来作地理数据库,用到 Goleveldb、Geohash。利用leveldb 的byte ordered 特性可以做高效查询。
Geohash 可以把一个地点的经度和纬度(46.770 -71.304)转为一个8位字符串,如:f2m616nn,还能缩成4个字符:f2m61 (46.8 -71.3),只是精度不同。
这样就可以用 4 digits geohash + a uniq id 的方式存放地点的位置信息。
生成一个 int64 uniq id:
1
2
3
4
5
6
7
8
9
10
11
// NewKey generates a new key using time prefixed by 'K'
func NewKey() Key {
return NewKeyWithInt(time.Now().UnixNano())
}
// NewKeyWithInt returns a key prefixed by 'K' with value i
func NewKeyWithInt(id int64) Key {
key := bytes.NewBufferString("K")
binary.Write(key, binary.BigEndian, id)
return key.Bytes()
}
假设要生成的key 是下面这个样子:
1
G201509262105dhv766K��Ϸ�Y�
完善上面的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// NewGeoKey generates a new key using a position & a key
func NewGeoKey(latitude, longitude float64) GeoKey {
t := time.Now().UTC()
kint := t.UnixNano()
kid := NewKeyWithInt(kint)
// G + string date + geohash 6 + timestamped key
// G201508282105dhv766K....
gk := geohash.EncodeWithPrecision(latitude, longitude, 6)
ts := t.Format("2006010215")
// modulo 10 to store 10mn interval
m := t.Minute() - t.Minute()%10
zkey := []byte("G" + ts + fmt.Sprintf("%02d", m) + gk)
zkey = append(zkey, kid...)
return zkey
}
生成GeoKey 列表:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// GeoKeyPrefix return prefixes to lookup using a GeoKey and timerange
func GeoKeyPrefix(start, stop time.Time) []string {
var res []string
d := 10 * time.Minute
var t time.Time
t = start
for {
if t.After(stop) {
break
}
key := "G" + t.Format("2006010215") + fmt.Sprintf("%02d", t.Minute()-t.Minute()%10)
res = append(res, key)
t = t.Add(d)
}
return res
}
查询数据库:
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
d := time.Duration(-10) * time.Minute
geoPrefixs := GeoKeyPrefix(time.Now().UTC().Add(d), time.Now().UTC())
// find adjacent hashes in m
// 1, 5003530
// 2, 625441
// 3, 123264
// 4, 19545
// 5, 3803
// 6, 610
gk := geohash.EncodeWithPrecision(lat, long, 4)
adjs := geohash.CalculateAllAdjacent(gk)
adjs = append(adjs, gk)
// for each adjacent blocks
for _, gkl := range adjs {
// for each time range modulo 10
for _, geoPrefix := range geoPrefixs {
startGeoKey := []byte(geoPrefix + gkl)
iter := s.NewIterator(util.BytesPrefix(startGeoKey), nil)
for iter.Next() {
log.Println(iter.Value())
}
iter.Release()
}
}
用到的链接
原文《A blazing fast geo database with LevelDB, Go and Geohashes》
本文网址: https://golangnote.com/topic/84.html 转摘请注明来源