golang高效内存地理空间索引插件库go-geoindex的使用

Golang高效内存地理空间索引插件库go-geoindex的使用

概述

go-geoindex是一个将地球表面划分为网格的地理空间索引库。在每个单元格中可以存储数据,如点列表、点计数等。它支持K最近邻(KNearest)和范围(Range)查询。

示例Demo

下面是一个完整的go-geoindex使用示例:

package main

import (
	"fmt"
	"github.com/hailocab/go-geoindex"
)

// 定义Driver结构体
type Driver struct {
	lat           float64
	lon           float64
	id            string
	canAcceptJobs bool
}

// 实现Point接口
func (d *Driver) Lat() float64 { return d.lat }
func (d *Driver) Lon() float64 { return d.lon }
func (d *Driver) Id() string   { return d.id }

func main() {
	// 创建点索引,分辨率为0.5公里
	index := geoindex.NewPointsIndex(geoindex.Km(0.5))

	// 添加点到索引中
	index.Add(&Driver{lat: 51.51, lon: -0.08, id: "driver1", canAcceptJobs: true})
	index.Add(&Driver{lat: 51.52, lon: -0.07, id: "driver2", canAcceptJobs: false})
	index.Add(&Driver{lat: 51.53, lon: -0.09, id: "driver3", canAcceptJobs: true})

	// 移除一个点
	index.Remove("driver2")

	// 查询5公里范围内最近的5个可接单司机
	center := &geoindex.GeoPoint{Id: "center", Lat: 51.511, Lon: -0.081}
	points := index.KNearest(
		center,
		5,
		geoindex.Km(5),
		func(p geoindex.Point) bool {
			return p.(*Driver).canAcceptJobs
		},
	)

	// 打印结果
	for _, point := range points {
		driver := point.(*Driver)
		fmt.Printf("找到司机: ID=%s, 纬度=%.3f, 经度=%.3f\n", driver.id, driver.lat, driver.lon)
	}

	// 范围查询
	topLeft := &geoindex.GeoPoint{Lat: 51.515, Lon: -0.085}
	bottomRight := &geoindex.GeoPoint{Lat: 51.505, Lon: -0.075}
	pointsInRange := index.Range(topLeft, bottomRight)
	fmt.Printf("范围内找到%d个点\n", len(pointsInRange))
}

API说明

基本操作

// 创建点索引,分辨率为0.5公里
index := NewPointsIndex(Km(0.5))

// 添加点
index.Add(&Driver{"id1", lat, lng, true})

// 移除点
index.Remove("id1")

// K最近邻查询
points := index.KNearest(&GeoPoint{id, lat, lng}, 5, Km(5), func(p Point) bool {
    return p.(*Driver).canAcceptJobs
})

// 范围查询
points := index.Range(topLeftPoint, bottomRightPoint)

索引类型

go-geoindex提供了多种索引类型:

NewPointsIndex(Km(0.5)) // 创建维护点的索引
NewExpiringPointsIndex(Km(0.5), Minutes(5)) // 创建会过期的点索引
NewCountIndex(Km(0.5)) // 创建维护点计数的索引
NewExpiringCountIndex(Km(0.5), Minutes(15)) // 创建会过期的计数索引
NewClusteringIndex() // 创建在不同缩放级别聚类点的索引,用于地图显示
NewExpiringClusteringIndex(Minutes(15)) // 创建会过期并在不同缩放级别聚类的索引

性能基准

以下是go-geoindex的性能基准测试结果:

BenchmarkClusterIndexAdd                    500000          5395 ns/op
BenchmarkClusterIndexStreetRange            100000         22207 ns/op
BenchmarkClusterIndexCityRange              100000         16389 ns/op
BenchmarkClusterIndexEuropeRange            50000          36559 ns/op

BenchmarkExpiringClusterIndexAdd            300000          7124 ns/op
BenchmarkExpiringClusterIndexStreetRange    50000          27030 ns/op
BenchmarkExpiringClusterIndexCityRange      100000         22185 ns/op
BenchmarkExpiringClusterIndexEuropeRange    30000          52080 ns/op

BenchmarkCountIndexAdd                      1000000         1670 ns/op
BenchmarkCountIndexCityRange                100000         20325 ns/op

BenchmarkExpiringCountIndexAdd              500000          2808 ns/op
BenchmarkExpiringCountIndexRange            50000          35791 ns/op

BenchmarkPointIndexRange                    100000         15945 ns/op
BenchmarkPointIndexAdd                      1000000         2416 ns/op
BenchmarkPointIndexKNearest                 100000         13788 ns/op

BenchmarkExpiringPointIndexAdd              500000          4324 ns/op
BenchmarkExpiringPointIndexKNearest         100000         15638 ns/op
BenchmarkExpiringPointIndexRange            100000         20386 ns/op

这些基准测试展示了go-geoindex在各种操作上的性能表现,包括添加点、范围查询和K最近邻查询等。


更多关于golang高效内存地理空间索引插件库go-geoindex的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang高效内存地理空间索引插件库go-geoindex的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Go-GeoIndex 高效内存地理空间索引库使用指南

Go-GeoIndex 是一个高效的 Golang 内存地理空间索引库,用于快速查询附近的地理位置点。它基于四叉树(QuadTree)实现,适合需要快速地理位置查询的应用场景。

主要特性

  • 高效的内存索引结构
  • 支持点(point)和矩形(rectangle)索引
  • 快速范围查询和最近邻查询
  • 线程安全
  • 简单易用的API

安装

go get github.com/hailocab/go-geoindex

基本使用示例

1. 创建索引并添加点

package main

import (
	"fmt"
	"github.com/hailocab/go-geoindex"
)

type Place struct {
	id string
	lat, lon float64
}

func (p *Place) Lat() float64 { return p.lat }
func (p *Place) Lon() float64 { return p.lon }
func (p *Place) Id() string   { return p.id }

func main() {
	// 创建索引,参数是每个节点的最大点数(当超过时会分裂)
	index := geoindex.NewPointsIndex(geoindex.Km(0.5))
	
	// 添加地点
	index.Add(&Place{id: "Eiffel Tower", lat: 48.858370, lon: 2.294481})
	index.Add(&Place{id: "Louvre", lat: 48.860611, lon: 2.337644})
	index.Add(&Place{id: "Arc de Triomphe", lat: 48.873792, lon: 2.295028})
	index.Add(&Place{id: "Notre Dame", lat: 48.852968, lon: 2.349902})
	
	// 查询埃菲尔铁塔附近1公里内的地点
	center := &geoindex.GeoPoint{Lon: 2.294481, Lat: 48.858370}
	results := index.Range(center, geoindex.Km(1), func(p geoindex.Point) bool {
		return p.Id() != "Eiffel Tower" // 排除自身
	})
	
	for _, p := range results {
		fmt.Printf("附近地点: %s (距离 %.2fkm)\n", 
			p.Id(), 
			geoindex.Distance(center, p)/1000)
	}
}

2. 查找最近的K个点

// 查找最近的2个地点
nearest := index.KNearest(
	&geoindex.GeoPoint{Lon: 2.294481, Lat: 48.858370}, // 中心点
	2,      // 数量
	geoindex.Km(10), // 搜索半径
	func(p geoindex.Point) bool { return true }, // 过滤条件
)

for _, p := range nearest {
	fmt.Printf("最近地点: %s (距离 %.2fkm)\n", 
		p.Id(), 
		geoindex.Distance(center, p)/1000)
}

3. 矩形范围查询

// 定义矩形范围 (minLat, minLon, maxLat, maxLon)
bounds := geoindex.NewBoundingBox(48.85, 2.29, 48.86, 2.34)
withinBounds := index.Inside(bounds)

for _, p := range withinBounds {
	fmt.Printf("矩形范围内地点: %s\n", p.Id())
}

性能优化建议

  1. 节点大小选择:创建索引时选择合适的节点大小(NewPointsIndex参数)。较小的值会提高查询精度但增加内存使用,较大的值则相反。

  2. 批量添加:如果需要添加大量点,考虑使用批量添加而非单个添加。

  3. 合理使用过滤函数:Range和KNearest方法都接受过滤函数,可以用于进一步筛选结果。

  4. 避免频繁重建索引:索引一旦建立,重建成本较高。

实际应用场景

  • 查找附近的服务点(餐厅、加油站等)
  • 地理围栏(geofencing)应用
  • 实时位置服务
  • 地理数据分析

注意事项

  • 这是一个内存索引库,不适合超大规模数据集(数百万以上)
  • 不支持持久化,应用重启后需要重建索引
  • 对于需要持久化的场景,可以考虑结合数据库使用

Go-GeoIndex 提供了简单高效的地理位置查询能力,适合需要快速响应的地理位置服务应用。根据你的具体需求,可以灵活运用其提供的各种查询方法。

回到顶部