tile38/index/rtree/knn.go

61 lines
1.3 KiB
Go

package rtree
import (
"github.com/tidwall/tinyqueue"
)
type queueItem struct {
node *d3nodeT
data interface{}
isItem bool
dist float64
}
func (item *queueItem) Less(b tinyqueue.Item) bool {
return item.dist < b.(*queueItem).dist
}
func boxDistPoint(point []float64, childBox d3rectT) float64 {
var dist float64
for i := 0; i < len(point); i++ {
d := axisDist(point[i], float64(childBox.min[i]), float64(childBox.max[i]))
dist += d * d
}
return dist
}
func axisDist(k, min, max float64) float64 {
if k < min {
return min - k
}
if k <= max {
return 0
}
return k - max
}
// NearestNeighbors gets the closest Spatials to the Point.
func (tr *RTree) NearestNeighbors(x, y, z float64, iter func(item Item, dist float64) bool) bool {
knnPoint := []float64{x, y, z}
queue := tinyqueue.New(nil)
node := tr.tr.root
for node != nil {
for i := 0; i < node.count; i++ {
child := node.branch[i]
dist := boxDistPoint(knnPoint, node.branch[i].rect)
queue.Push(&queueItem{node: child.child, data: child.data, isItem: node.isLeaf(), dist: dist})
}
for queue.Len() > 0 && queue.Peek().(*queueItem).isItem {
item := queue.Pop().(*queueItem)
if !iter(item.data.(Item), item.dist) {
return false
}
}
last := queue.Pop()
if last != nil {
node = last.(*queueItem).node
} else {
node = nil
}
}
return true
}