2016-03-05 02:08:16 +03:00
|
|
|
package index
|
|
|
|
|
2016-10-03 21:37:16 +03:00
|
|
|
import (
|
|
|
|
"math"
|
|
|
|
|
|
|
|
"github.com/tidwall/tile38/index/rtree"
|
|
|
|
)
|
2016-03-05 02:08:16 +03:00
|
|
|
|
|
|
|
// Item represents an index item.
|
|
|
|
type Item interface {
|
2016-10-03 21:37:16 +03:00
|
|
|
Point() (x, y, z float64)
|
|
|
|
Rect() (minX, minY, minZ, maxX, maxY, maxZ float64)
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// FlexItem can represent a point or a rectangle
|
|
|
|
type FlexItem struct {
|
2016-10-03 21:37:16 +03:00
|
|
|
MinX, MinY, MinZ, MaxX, MaxY, MaxZ float64
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Rect returns the rectangle
|
2016-10-03 21:37:16 +03:00
|
|
|
func (item *FlexItem) Rect() (minX, minY, minZ, maxX, maxY, maxZ float64) {
|
|
|
|
return item.MinX, item.MinY, item.MinZ, item.MaxX, item.MaxY, item.MaxZ
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Point returns the point
|
2016-10-03 21:37:16 +03:00
|
|
|
func (item *FlexItem) Point() (x, y, z float64) {
|
|
|
|
return item.MinX, item.MinY, item.MinZ
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Index is a geospatial index
|
|
|
|
type Index struct {
|
2016-08-19 17:47:39 +03:00
|
|
|
r *rtree.RTree
|
|
|
|
nr map[*rtree.Rect]Item // normalized points
|
|
|
|
nrr map[Item][]*rtree.Rect // normalized points
|
|
|
|
mulm map[Item]bool // store items that contain multiple rects
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// New create a new index
|
|
|
|
func New() *Index {
|
|
|
|
return &Index{
|
|
|
|
r: rtree.New(),
|
|
|
|
mulm: make(map[Item]bool),
|
|
|
|
nr: make(map[*rtree.Rect]Item),
|
|
|
|
nrr: make(map[Item][]*rtree.Rect),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert inserts an item into the index
|
|
|
|
func (ix *Index) Insert(item Item) {
|
2016-10-03 21:37:16 +03:00
|
|
|
minX, minY, minZ, maxX, maxY, maxZ := item.Rect()
|
2016-03-05 02:08:16 +03:00
|
|
|
if minX == maxX && minY == maxY {
|
|
|
|
x, y, normd := normPoint(minY, minX)
|
|
|
|
if normd {
|
2016-10-03 21:37:16 +03:00
|
|
|
nitem := &rtree.Rect{MinX: x, MinY: y, MinZ: minZ, MaxX: x, MaxY: y, MaxZ: maxZ}
|
2016-08-19 17:47:39 +03:00
|
|
|
ix.nr[nitem] = item
|
|
|
|
ix.nrr[item] = []*rtree.Rect{nitem}
|
|
|
|
ix.r.Insert(nitem)
|
2016-03-05 02:08:16 +03:00
|
|
|
} else {
|
2016-08-19 17:47:39 +03:00
|
|
|
ix.r.Insert(item)
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mins, maxs, normd := normRect(minY, minX, maxY, maxX)
|
|
|
|
if normd {
|
|
|
|
var nitems []*rtree.Rect
|
|
|
|
for i := range mins {
|
|
|
|
minX, minY, maxX, maxY := mins[i][0], mins[i][1], maxs[i][0], maxs[i][1]
|
2016-10-03 21:37:16 +03:00
|
|
|
nitem := &rtree.Rect{MinX: minX, MinY: minY, MinZ: minZ, MaxX: maxX, MaxY: maxY, MaxZ: maxZ}
|
2016-03-05 02:08:16 +03:00
|
|
|
ix.nr[nitem] = item
|
|
|
|
nitems = append(nitems, nitem)
|
|
|
|
ix.r.Insert(nitem)
|
|
|
|
}
|
|
|
|
ix.nrr[item] = nitems
|
|
|
|
if len(mins) > 1 {
|
|
|
|
ix.mulm[item] = true
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ix.r.Insert(item)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove removed an item from the index
|
|
|
|
func (ix *Index) Remove(item Item) {
|
2016-08-19 17:47:39 +03:00
|
|
|
if nitems, ok := ix.nrr[item]; ok {
|
|
|
|
for _, nitem := range nitems {
|
|
|
|
ix.r.Remove(nitem)
|
|
|
|
delete(ix.nr, nitem)
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
2016-08-19 17:47:39 +03:00
|
|
|
delete(ix.nrr, item)
|
2016-03-05 02:08:16 +03:00
|
|
|
} else {
|
2016-08-19 17:47:39 +03:00
|
|
|
ix.r.Remove(item)
|
2016-03-05 02:08:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Count counts all items in the index.
|
|
|
|
func (ix *Index) Count() int {
|
|
|
|
count := 0
|
2016-10-03 21:37:16 +03:00
|
|
|
ix.Search(0, -90, -180, 90, 180, math.Inf(-1), math.Inf(+1), func(item Item) bool {
|
2016-03-05 02:08:16 +03:00
|
|
|
count++
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
return count
|
|
|
|
}
|
|
|
|
|
2016-08-19 18:33:58 +03:00
|
|
|
// Bounds returns the minimum bounding rectangle of all items in the index.
|
2016-10-03 21:37:16 +03:00
|
|
|
func (ix *Index) Bounds() (MinX, MinY, MinZ, MaxX, MaxY, MaxZ float64) {
|
2016-08-19 18:33:58 +03:00
|
|
|
return ix.r.Bounds()
|
|
|
|
}
|
|
|
|
|
2016-03-05 02:08:16 +03:00
|
|
|
// RemoveAll removes all items from the index.
|
|
|
|
func (ix *Index) RemoveAll() {
|
|
|
|
ix.r.RemoveAll()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ix *Index) getRTreeItem(item rtree.Item) Item {
|
|
|
|
switch item := item.(type) {
|
|
|
|
case Item:
|
|
|
|
return item
|
|
|
|
case *rtree.Rect:
|
|
|
|
return ix.nr[item]
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-01-31 02:41:12 +03:00
|
|
|
func (ix *Index) NearestNeighbors(k int, lat, lon float64, iterator func(item Item) bool) {
|
|
|
|
x, y, _ := normPoint(lat, lon)
|
|
|
|
items := ix.r.NearestNeighbors(k, x, y, 0)
|
|
|
|
for _, item := range items {
|
|
|
|
iitm := ix.getRTreeItem(item)
|
|
|
|
if item == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !iterator(iitm) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-05 02:08:16 +03:00
|
|
|
// Search returns all items that intersect the bounding box.
|
2016-10-03 21:37:16 +03:00
|
|
|
func (ix *Index) Search(cursor uint64, swLat, swLon, neLat, neLon, minZ, maxZ float64, iterator func(item Item) bool) (ncursor uint64) {
|
2016-03-05 02:08:16 +03:00
|
|
|
var idx uint64
|
|
|
|
var active = true
|
|
|
|
var idm = make(map[Item]bool)
|
|
|
|
mins, maxs, _ := normRect(swLat, swLon, neLat, neLon)
|
|
|
|
// Points
|
|
|
|
if len(mins) == 1 {
|
|
|
|
// There is only one rectangle.
|
|
|
|
// It's possible that a r rect may span multiple entries. Check mulm map for spanning rects.
|
|
|
|
if active {
|
2016-10-03 21:37:16 +03:00
|
|
|
ix.r.Search(mins[0][0], mins[0][1], minZ, maxs[0][0], maxs[0][1], maxZ, func(item rtree.Item) bool {
|
2016-03-05 02:08:16 +03:00
|
|
|
if idx >= cursor {
|
|
|
|
iitm := ix.getRTreeItem(item)
|
|
|
|
if iitm != nil {
|
|
|
|
if ix.mulm[iitm] {
|
|
|
|
if !idm[iitm] {
|
|
|
|
idm[iitm] = true
|
|
|
|
active = iterator(iitm)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
active = iterator(iitm)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
idx++
|
|
|
|
return active
|
|
|
|
})
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// There are multiple rectangles. Duplicates might occur.
|
|
|
|
for i := range mins {
|
|
|
|
if active {
|
2016-10-03 21:37:16 +03:00
|
|
|
ix.r.Search(mins[i][0], mins[i][1], minZ, maxs[i][0], maxs[i][1], maxZ, func(item rtree.Item) bool {
|
2016-03-05 02:08:16 +03:00
|
|
|
if idx >= cursor {
|
|
|
|
iitm := ix.getRTreeItem(item)
|
|
|
|
if iitm != nil {
|
2016-04-03 17:59:23 +03:00
|
|
|
if ix.mulm[iitm] {
|
|
|
|
if !idm[iitm] {
|
|
|
|
idm[iitm] = true
|
|
|
|
active = iterator(iitm)
|
|
|
|
}
|
|
|
|
} else {
|
2016-03-05 02:08:16 +03:00
|
|
|
active = iterator(iitm)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
idx++
|
|
|
|
return active
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return idx
|
|
|
|
}
|