mirror of https://github.com/tidwall/tile38.git
removed quadtree
This commit is contained in:
parent
8dc5e96669
commit
f69153efb0
100
index/index.go
100
index/index.go
|
@ -1,14 +1,11 @@
|
||||||
package index
|
package index
|
||||||
|
|
||||||
import (
|
import "github.com/tidwall/tile38/index/rtree"
|
||||||
"github.com/tidwall/tile38/index/qtree"
|
|
||||||
"github.com/tidwall/tile38/index/rtree"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Item represents an index item.
|
// Item represents an index item.
|
||||||
type Item interface {
|
type Item interface {
|
||||||
qtree.Item
|
Point() (x, y float64)
|
||||||
rtree.Item
|
Rect() (minX, minY, maxX, maxY float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlexItem can represent a point or a rectangle
|
// FlexItem can represent a point or a rectangle
|
||||||
|
@ -28,25 +25,17 @@ func (item *FlexItem) Point() (x, y float64) {
|
||||||
|
|
||||||
// Index is a geospatial index
|
// Index is a geospatial index
|
||||||
type Index struct {
|
type Index struct {
|
||||||
q *qtree.QTree
|
r *rtree.RTree
|
||||||
r *rtree.RTree
|
nr map[*rtree.Rect]Item // normalized points
|
||||||
|
nrr map[Item][]*rtree.Rect // normalized points
|
||||||
np map[*qtree.Point]Item // normalized points
|
mulm map[Item]bool // store items that contain multiple rects
|
||||||
npr map[Item]*qtree.Point // normalized points
|
|
||||||
nr map[*rtree.Rect]Item // normalized points
|
|
||||||
nrr map[Item][]*rtree.Rect // normalized points
|
|
||||||
|
|
||||||
mulm map[Item]bool // store items that contain multiple rects
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New create a new index
|
// New create a new index
|
||||||
func New() *Index {
|
func New() *Index {
|
||||||
return &Index{
|
return &Index{
|
||||||
q: qtree.New(-180, -90, 180, 90),
|
|
||||||
r: rtree.New(),
|
r: rtree.New(),
|
||||||
mulm: make(map[Item]bool),
|
mulm: make(map[Item]bool),
|
||||||
np: make(map[*qtree.Point]Item),
|
|
||||||
npr: make(map[Item]*qtree.Point),
|
|
||||||
nr: make(map[*rtree.Rect]Item),
|
nr: make(map[*rtree.Rect]Item),
|
||||||
nrr: make(map[Item][]*rtree.Rect),
|
nrr: make(map[Item][]*rtree.Rect),
|
||||||
}
|
}
|
||||||
|
@ -58,12 +47,12 @@ func (ix *Index) Insert(item Item) {
|
||||||
if minX == maxX && minY == maxY {
|
if minX == maxX && minY == maxY {
|
||||||
x, y, normd := normPoint(minY, minX)
|
x, y, normd := normPoint(minY, minX)
|
||||||
if normd {
|
if normd {
|
||||||
nitem := &qtree.Point{X: x, Y: y}
|
nitem := &rtree.Rect{MinX: x, MinY: y, MaxX: x, MaxY: y}
|
||||||
ix.np[nitem] = item
|
ix.nr[nitem] = item
|
||||||
ix.npr[item] = nitem
|
ix.nrr[item] = []*rtree.Rect{nitem}
|
||||||
ix.q.Insert(nitem)
|
ix.r.Insert(nitem)
|
||||||
} else {
|
} else {
|
||||||
ix.q.Insert(item)
|
ix.r.Insert(item)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mins, maxs, normd := normRect(minY, minX, maxY, maxX)
|
mins, maxs, normd := normRect(minY, minX, maxY, maxX)
|
||||||
|
@ -89,25 +78,14 @@ func (ix *Index) Insert(item Item) {
|
||||||
|
|
||||||
// Remove removed an item from the index
|
// Remove removed an item from the index
|
||||||
func (ix *Index) Remove(item Item) {
|
func (ix *Index) Remove(item Item) {
|
||||||
minX, minY, maxX, maxY := item.Rect()
|
if nitems, ok := ix.nrr[item]; ok {
|
||||||
if minX == maxX && minY == maxY {
|
for _, nitem := range nitems {
|
||||||
if nitem, ok := ix.npr[item]; ok {
|
ix.r.Remove(nitem)
|
||||||
ix.q.Remove(nitem)
|
delete(ix.nr, nitem)
|
||||||
delete(ix.np, nitem)
|
|
||||||
delete(ix.npr, item)
|
|
||||||
} else {
|
|
||||||
ix.q.Remove(item)
|
|
||||||
}
|
}
|
||||||
|
delete(ix.nrr, item)
|
||||||
} else {
|
} else {
|
||||||
if nitems, ok := ix.nrr[item]; ok {
|
ix.r.Remove(item)
|
||||||
for _, nitem := range nitems {
|
|
||||||
ix.r.Remove(nitem)
|
|
||||||
delete(ix.nr, nitem)
|
|
||||||
}
|
|
||||||
delete(ix.npr, item)
|
|
||||||
} else {
|
|
||||||
ix.r.Remove(item)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,17 +102,6 @@ func (ix *Index) Count() int {
|
||||||
// RemoveAll removes all items from the index.
|
// RemoveAll removes all items from the index.
|
||||||
func (ix *Index) RemoveAll() {
|
func (ix *Index) RemoveAll() {
|
||||||
ix.r.RemoveAll()
|
ix.r.RemoveAll()
|
||||||
ix.q.RemoveAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ix *Index) getQTreeItem(item qtree.Item) Item {
|
|
||||||
switch item := item.(type) {
|
|
||||||
case Item:
|
|
||||||
return item
|
|
||||||
case *qtree.Point:
|
|
||||||
return ix.np[item]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ix *Index) getRTreeItem(item rtree.Item) Item {
|
func (ix *Index) getRTreeItem(item rtree.Item) Item {
|
||||||
|
@ -156,19 +123,6 @@ func (ix *Index) Search(cursor uint64, swLat, swLon, neLat, neLon float64, itera
|
||||||
// Points
|
// Points
|
||||||
if len(mins) == 1 {
|
if len(mins) == 1 {
|
||||||
// There is only one rectangle.
|
// There is only one rectangle.
|
||||||
// Simply return all quad points in that search rect.
|
|
||||||
if active {
|
|
||||||
ix.q.Search(mins[0][0], mins[0][1], maxs[0][0], maxs[0][1], func(item qtree.Item) bool {
|
|
||||||
if idx >= cursor {
|
|
||||||
iitm := ix.getQTreeItem(item)
|
|
||||||
if iitm != nil {
|
|
||||||
active = iterator(iitm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
idx++
|
|
||||||
return active
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// It's possible that a r rect may span multiple entries. Check mulm map for spanning rects.
|
// It's possible that a r rect may span multiple entries. Check mulm map for spanning rects.
|
||||||
if active {
|
if active {
|
||||||
ix.r.Search(mins[0][0], mins[0][1], maxs[0][0], maxs[0][1], func(item rtree.Item) bool {
|
ix.r.Search(mins[0][0], mins[0][1], maxs[0][0], maxs[0][1], func(item rtree.Item) bool {
|
||||||
|
@ -191,24 +145,6 @@ func (ix *Index) Search(cursor uint64, swLat, swLon, neLat, neLon float64, itera
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// There are multiple rectangles. Duplicates might occur.
|
// There are multiple rectangles. Duplicates might occur.
|
||||||
for i := range mins {
|
|
||||||
if active {
|
|
||||||
ix.q.Search(mins[i][0], mins[i][1], maxs[i][0], maxs[i][1], func(item qtree.Item) bool {
|
|
||||||
if idx >= cursor {
|
|
||||||
iitm := ix.getQTreeItem(item)
|
|
||||||
if iitm != nil {
|
|
||||||
if !idm[iitm] {
|
|
||||||
idm[iitm] = true
|
|
||||||
active = iterator(iitm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
idx++
|
|
||||||
return active
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := range mins {
|
for i := range mins {
|
||||||
if active {
|
if active {
|
||||||
ix.r.Search(mins[i][0], mins[i][1], maxs[i][0], maxs[i][1], func(item rtree.Item) bool {
|
ix.r.Search(mins[i][0], mins[i][1], maxs[i][0], maxs[i][1], func(item rtree.Item) bool {
|
||||||
|
|
|
@ -126,9 +126,10 @@ func TestRandomInserts(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tr.RemoveAll()
|
tr.RemoveAll()
|
||||||
if tr.getQTreeItem(nil) != nil {
|
/* if tr.getQTreeItem(nil) != nil {
|
||||||
t.Fatal("getQTreeItem(nil) should return nil")
|
t.Fatal("getQTreeItem(nil) should return nil")
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
if tr.getRTreeItem(nil) != nil {
|
if tr.getRTreeItem(nil) != nil {
|
||||||
t.Fatal("getRTreeItem(nil) should return nil")
|
t.Fatal("getRTreeItem(nil) should return nil")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,189 +0,0 @@
|
||||||
package qtree
|
|
||||||
|
|
||||||
// Item is a qtree item
|
|
||||||
type Item interface {
|
|
||||||
Point() (x, y float64)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Point is point
|
|
||||||
type Point struct {
|
|
||||||
X, Y float64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Point returns the point
|
|
||||||
func (item *Point) Point() (x, y float64) {
|
|
||||||
return item.X, item.Y
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxPoints = 16
|
|
||||||
const appendGrowth = false // Set 'true' for faster inserts, Set 'false' for smaller memory size
|
|
||||||
|
|
||||||
type nodeT struct {
|
|
||||||
count int
|
|
||||||
points []Item
|
|
||||||
nodes [4]*nodeT
|
|
||||||
}
|
|
||||||
|
|
||||||
// QTree is an implementation of a quad tree
|
|
||||||
type QTree struct {
|
|
||||||
root *nodeT
|
|
||||||
minX, minY float64
|
|
||||||
maxX, maxY float64
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new QTree
|
|
||||||
func New(minX, minY, maxX, maxY float64) *QTree {
|
|
||||||
return &QTree{&nodeT{}, minX, minY, maxX, maxY}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *QTree) clip(item Item) (float64, float64) {
|
|
||||||
x, y := item.Point()
|
|
||||||
if x < tr.minX {
|
|
||||||
x = tr.minX
|
|
||||||
} else if x > tr.maxX {
|
|
||||||
x = tr.maxX
|
|
||||||
}
|
|
||||||
if y < tr.minY {
|
|
||||||
y = tr.minY
|
|
||||||
} else if y > tr.maxY {
|
|
||||||
y = tr.maxY
|
|
||||||
}
|
|
||||||
return x, y
|
|
||||||
}
|
|
||||||
|
|
||||||
func split(minX, minY, maxX, maxY, cx, cy float64) (quad int, nMinX, nMinY, nMaxX, nMaxY float64) {
|
|
||||||
if cx < (maxX-minX)/2+minX {
|
|
||||||
if cy < (maxY-minY)/2+minY {
|
|
||||||
return 2, minX, minY, (maxX-minX)/2 + minX, (maxY-minY)/2 + minY
|
|
||||||
}
|
|
||||||
return 0, minX, (maxY-minY)/2 + minY, (maxX-minX)/2 + minX, maxY
|
|
||||||
}
|
|
||||||
if cy < (maxY-minY)/2+minY {
|
|
||||||
return 3, (maxX-minX)/2 + minX, minY, maxX, (maxY-minY)/2 + minY
|
|
||||||
}
|
|
||||||
return 1, (maxX-minX)/2 + minX, (maxY-minY)/2 + minY, maxX, maxY
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert inserts an item into the tree
|
|
||||||
func (tr *QTree) Insert(item Item) {
|
|
||||||
cx, cy := tr.clip(item)
|
|
||||||
insert(tr.root, tr.minX, tr.minY, tr.maxX, tr.maxY, cx, cy, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove removes an item from the tree
|
|
||||||
func (tr *QTree) Remove(item Item) {
|
|
||||||
cx, cy := tr.clip(item)
|
|
||||||
remove(tr.root, tr.minX, tr.minY, tr.maxX, tr.maxY, cx, cy, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search finds all items contained in a bounding box
|
|
||||||
func (tr *QTree) Search(minX, minY, maxX, maxY float64, iterator func(item Item) bool) {
|
|
||||||
search(tr.root, tr.minX, tr.minY, tr.maxX, tr.maxY, minX, minY, maxX, maxY, true, iterator)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count counts all of the items in the tree
|
|
||||||
func (tr *QTree) Count() int {
|
|
||||||
return count(tr.root, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveAll removes all items from the tree
|
|
||||||
func (tr *QTree) RemoveAll() {
|
|
||||||
tr.root = &nodeT{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func insert(node *nodeT, nMinX, nMinY, nMaxX, nMaxY, cx, cy float64, item Item) {
|
|
||||||
if node.count < maxPoints {
|
|
||||||
if len(node.points) == node.count {
|
|
||||||
if appendGrowth {
|
|
||||||
node.points = append(node.points, item)
|
|
||||||
} else {
|
|
||||||
npoints := make([]Item, node.count+1)
|
|
||||||
copy(npoints, node.points)
|
|
||||||
node.points = npoints
|
|
||||||
node.points[node.count] = item
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
node.points[node.count] = item
|
|
||||||
}
|
|
||||||
node.count++
|
|
||||||
} else {
|
|
||||||
var quad int
|
|
||||||
quad, nMinX, nMinY, nMaxX, nMaxY = split(nMinX, nMinY, nMaxX, nMaxY, cx, cy)
|
|
||||||
if node.nodes[quad] == nil {
|
|
||||||
node.nodes[quad] = &nodeT{}
|
|
||||||
}
|
|
||||||
insert(node.nodes[quad], nMinX, nMinY, nMaxX, nMaxY, cx, cy, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func remove(node *nodeT, nMinX, nMinY, nMaxX, nMaxY, cx, cy float64, item Item) {
|
|
||||||
for i := 0; i < node.count; i++ {
|
|
||||||
if node.points[i] == item {
|
|
||||||
node.points[i] = node.points[node.count-1]
|
|
||||||
node.count--
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var quad int
|
|
||||||
quad, nMinX, nMinY, nMaxX, nMaxY = split(nMinX, nMinY, nMaxX, nMaxY, cx, cy)
|
|
||||||
if node.nodes[quad] != nil {
|
|
||||||
remove(node.nodes[quad], nMinX, nMinY, nMaxX, nMaxY, cx, cy, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func count(node *nodeT, counter int) int {
|
|
||||||
counter += node.count
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
if node.nodes[i] != nil {
|
|
||||||
counter = count(node.nodes[i], counter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return counter
|
|
||||||
}
|
|
||||||
|
|
||||||
func doesOverlap(nMinX, nMinY, nMaxX, nMaxY float64, minX, minY, maxX, maxY float64) bool {
|
|
||||||
if nMinX > maxX || minX > nMaxX {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if nMinY > maxY || minY > nMaxY {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func search(node *nodeT, nMinX, nMinY, nMaxX, nMaxY float64, minX, minY, maxX, maxY float64, overlap bool, iterator func(item Item) bool) bool {
|
|
||||||
if overlap {
|
|
||||||
overlap = doesOverlap(nMinX, nMinY, nMaxX, nMaxY, minX, minY, maxX, maxY)
|
|
||||||
}
|
|
||||||
if !overlap {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for i := 0; i < node.count; i++ {
|
|
||||||
item := node.points[i]
|
|
||||||
x, y := item.Point()
|
|
||||||
if x >= minX && x <= maxX && y >= minY && y <= maxY {
|
|
||||||
if !iterator(item) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var qMinX, qMaxX, qMinY, qMaxY float64
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
if node.nodes[i] != nil {
|
|
||||||
switch i {
|
|
||||||
case 0:
|
|
||||||
qMinX, qMinY, qMaxX, qMaxY = nMinX, (nMaxY-nMinY)/2+nMinY, (nMaxX-nMinX)/2+nMinX, nMaxY
|
|
||||||
case 1:
|
|
||||||
qMinX, qMinY, qMaxX, qMaxY = (nMaxX-nMinX)/2+nMinX, (nMaxY-nMinY)/2+nMinY, nMaxX, nMaxY
|
|
||||||
case 2:
|
|
||||||
qMinX, qMinY, qMaxX, qMaxY = nMinX, nMinY, (nMaxX-nMinX)/2+nMinX, (nMaxY-nMinY)/2+nMinY
|
|
||||||
case 3:
|
|
||||||
qMinX, qMinY, qMaxX, qMaxY = (nMaxX-nMinX)/2+nMinX, nMinY, nMaxX, (nMaxY-nMinY)/2+nMinY
|
|
||||||
}
|
|
||||||
if !search(node.nodes[i], qMinX, qMinY, qMaxX, qMaxY, minX, minY, maxX, maxY, overlap, iterator) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
package qtree
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func randf(min, max float64) float64 {
|
|
||||||
return rand.Float64()*(max-min) + min
|
|
||||||
}
|
|
||||||
func randXY() (x float64, y float64) {
|
|
||||||
return randf(0, 100), randf(0, 100)
|
|
||||||
}
|
|
||||||
func randPoint() (lat float64, lon float64) {
|
|
||||||
return randf(-90, 90), randf(-180, 180)
|
|
||||||
}
|
|
||||||
|
|
||||||
func wp(x, y float64) *Point {
|
|
||||||
return &Point{x, y}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClip(t *testing.T) {
|
|
||||||
tr := New(-180, -90, 180, 90)
|
|
||||||
if x, y := tr.clip(wp(-900, 100)); x != -180 || y != 90 {
|
|
||||||
t.Fatalf("x,y == %f,%f, expect %f,%f", x, y, -180.0, 90.0)
|
|
||||||
}
|
|
||||||
if x, y := tr.clip(wp(900, -100)); x != 180 || y != -90 {
|
|
||||||
t.Fatalf("x,y == %f,%f, expect %f,%f", x, y, 180.0, -90.0)
|
|
||||||
}
|
|
||||||
if x, y := tr.clip(wp(100, 100)); x != 100 || y != 90 {
|
|
||||||
t.Fatalf("x,y == %f,%f, expect %f,%f", x, y, 100.0, 90.0)
|
|
||||||
}
|
|
||||||
if x, y := tr.clip(wp(50, 50)); x != 50 || y != 50 {
|
|
||||||
t.Fatalf("x,y == %f,%f, expect %f,%f", x, y, 50.0, 50.0)
|
|
||||||
}
|
|
||||||
if x, y := tr.clip(wp(-50, -50)); x != -50 || y != -50 {
|
|
||||||
t.Fatalf("x,y == %f,%f, expect %f,%f", x, y, -50.0, -50.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSimpleSplit(t *testing.T) {
|
|
||||||
quad, nMinX, nMinY, nMaxX, nMaxY := split(0, 0, 100, 100, 0, 100)
|
|
||||||
if quad != 0 || nMinX != 0 || nMinY != 50 || nMaxX != 50 || nMaxY != 100 {
|
|
||||||
t.Fatalf("failed 0: %d, %f, %f, %f, %f\n", quad, nMinX, nMinY, nMaxX, nMaxY)
|
|
||||||
}
|
|
||||||
quad, nMinX, nMinY, nMaxX, nMaxY = split(0, 0, 100, 100, 100, 100)
|
|
||||||
if quad != 1 || nMinX != 50 || nMinY != 50 || nMaxX != 100 || nMaxY != 100 {
|
|
||||||
t.Fatalf("failed 1: %d, %f, %f, %f, %f\n", quad, nMinX, nMinY, nMaxX, nMaxY)
|
|
||||||
}
|
|
||||||
quad, nMinX, nMinY, nMaxX, nMaxY = split(0, 0, 100, 100, 0, 0)
|
|
||||||
if quad != 2 || nMinX != 0 || nMinY != 0 || nMaxX != 50 || nMaxY != 50 {
|
|
||||||
t.Fatalf("failed 2: %d, %f, %f, %f, %f\n", quad, nMinX, nMinY, nMaxX, nMaxY)
|
|
||||||
}
|
|
||||||
quad, nMinX, nMinY, nMaxX, nMaxY = split(0, 0, 100, 100, 100, 0)
|
|
||||||
if quad != 3 || nMinX != 50 || nMinY != 0 || nMaxX != 100 || nMaxY != 50 {
|
|
||||||
t.Fatalf("failed 3: %d, %f, %f, %f, %f\n", quad, nMinX, nMinY, nMaxX, nMaxY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGeoSplit(t *testing.T) {
|
|
||||||
quad, nMinX, nMinY, nMaxX, nMaxY := split(-180, -90, 180, 90, -180, 90)
|
|
||||||
if quad != 0 || nMinX != -180 || nMinY != 0 || nMaxX != 0 || nMaxY != 90 {
|
|
||||||
t.Fatalf("failed 0: %d, %f, %f, %f, %f\n", quad, nMinX, nMinY, nMaxX, nMaxY)
|
|
||||||
}
|
|
||||||
quad, nMinX, nMinY, nMaxX, nMaxY = split(-180, -90, 180, 90, 180, 90)
|
|
||||||
if quad != 1 || nMinX != 0 || nMinY != 0 || nMaxX != 180 || nMaxY != 90 {
|
|
||||||
t.Fatalf("failed 1: %d, %f, %f, %f, %f\n", quad, nMinX, nMinY, nMaxX, nMaxY)
|
|
||||||
}
|
|
||||||
quad, nMinX, nMinY, nMaxX, nMaxY = split(-180, -90, 180, 90, -180, -90)
|
|
||||||
if quad != 2 || nMinX != -180 || nMinY != -90 || nMaxX != 0 || nMaxY != 0 {
|
|
||||||
t.Fatalf("failed 2: %d, %f, %f, %f, %f\n", quad, nMinX, nMinY, nMaxX, nMaxY)
|
|
||||||
}
|
|
||||||
quad, nMinX, nMinY, nMaxX, nMaxY = split(-180, -90, 180, 90, 180, -90)
|
|
||||||
if quad != 3 || nMinX != 0 || nMinY != -90 || nMaxX != 180 || nMaxY != 0 {
|
|
||||||
t.Fatalf("failed 3: %d, %f, %f, %f, %f\n", quad, nMinX, nMinY, nMaxX, nMaxY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGeoInsert(t *testing.T) {
|
|
||||||
tr := New(-180, -90, 180, 90)
|
|
||||||
l := 50000
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
swLat, swLon := randPoint()
|
|
||||||
tr.Insert(wp(swLon, swLat))
|
|
||||||
}
|
|
||||||
count := 0
|
|
||||||
tr.Search(-180, -90, 180, 90, func(item Item) bool {
|
|
||||||
count++
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if count != l {
|
|
||||||
t.Fatalf("count == %d, expect %d", count, l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMemory(t *testing.T) {
|
|
||||||
rand.Seed(0)
|
|
||||||
tr := New(0, 0, 100, 100)
|
|
||||||
for i := 0; i < 500000; i++ {
|
|
||||||
x, y := randXY()
|
|
||||||
tr.Insert(wp(x, y))
|
|
||||||
}
|
|
||||||
runtime.GC()
|
|
||||||
var m runtime.MemStats
|
|
||||||
runtime.ReadMemStats(&m)
|
|
||||||
println(int(m.HeapAlloc)/tr.Count(), "bytes/point")
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkInsert(b *testing.B) {
|
|
||||||
rand.Seed(0)
|
|
||||||
tr := New(0, 0, 100, 100)
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
x, y := randXY()
|
|
||||||
tr.Insert(wp(x, y))
|
|
||||||
}
|
|
||||||
count := 0
|
|
||||||
tr.Search(0, 0, 100, 100, func(item Item) bool {
|
|
||||||
count++
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if count != b.N {
|
|
||||||
b.Fatalf("count == %d, expect %d", count, b.N)
|
|
||||||
}
|
|
||||||
|
|
||||||
// tr.Search(0, 0, 100, 100, func(id int) bool {
|
|
||||||
// count++
|
|
||||||
// return true
|
|
||||||
// })
|
|
||||||
//println(tr.Count())
|
|
||||||
}
|
|
Loading…
Reference in New Issue