diff --git a/spatial_id.go b/spatial_id.go index a44e109..2c662c4 100644 --- a/spatial_id.go +++ b/spatial_id.go @@ -2,7 +2,6 @@ package spatialID import ( "math" - "slices" "strconv" "strings" @@ -100,7 +99,7 @@ func MergeSpatialIDs (spatialIDs []*SpatialID) []*SpatialID { return mergedSpatialIDs } -func CompareSpatialIDs (a, b *SpatialID) int { +func CompareSpatialIDs(a, b *SpatialID) int { if a.GetZ() < b.GetZ() { return -1 } else if a.GetZ() > b.GetZ() { @@ -130,10 +129,10 @@ func CompareSpatialIDs (a, b *SpatialID) int { // SpatialID 空間IDクラス type SpatialID struct { - z int8 // 精度 - f int64 // 高さID - x int64 // 経度ID - y int64 // 緯度ID + z int8 // 精度 + f int64 // 高さID + x int64 // 経度ID + y int64 // 緯度ID } func NewSpatialIDFromString(string string) (*SpatialID, error) { @@ -167,7 +166,7 @@ func NewSpatialIDFromGeodetic(geodetic coordinates.Geodetic, z int8) (*SpatialID max := float64(int(1) << z) // 経度方向のインデックスの計算 - x := math.Floor(max * math.Mod(*geodetic.Longitude() + 180.0, 360.0)) + x := math.Floor(max * math.Mod(*geodetic.Longitude()+180.0, 360.0)) radianLatitude := mathematics.RadianPerDegree * *geodetic.Latitude() @@ -205,7 +204,7 @@ func NewSpatialID( func (id *SpatialID) SetX(x int64) { limit := int64(1 << id.GetZ()) - id.x = x%limit + id.x = x % limit if id.x < 0 { id.x += limit } @@ -257,34 +256,61 @@ func (id SpatialID) GetY() int64 { func (id SpatialID) String() string { return strconv.FormatInt(int64(id.GetZ()), 10) + delimiter + - strconv.FormatInt(id.GetF(), 10) + delimiter + - strconv.FormatInt(id.GetX(), 10) + delimiter + - strconv.FormatInt(id.GetY(), 10) + strconv.FormatInt(id.GetF(), 10) + delimiter + + strconv.FormatInt(id.GetX(), 10) + delimiter + + strconv.FormatInt(id.GetY(), 10) } func (id SpatialID) NewParent(number int8) (*SpatialID, error) { return NewSpatialID( id.GetZ()-number, - id.GetF() >> number, - id.GetX() >> number, - id.GetY() >> number, + id.GetF()>>number, + id.GetX()>>number, + id.GetY()>>number, ) } func (id SpatialID) NewMinChild(number int8) (*SpatialID, error) { return NewSpatialID( id.GetZ()+number, - id.GetF() << number, - id.GetX() << number, - id.GetY() << number, + id.GetF()< targetId.GetZ() { + id, _ = id.GetZoomedDownTo(targetId.GetZ()) + } + return *id == *targetId +} + +type SpatialIDs []*SpatialID + +func (ids SpatialIDs) Overlaps(targetIDs SpatialIDs) bool { + for _, id := range ids { + for _, targetId := range targetIDs { + if id.Overlaps(targetId) { + return true + } + } + } + return false +} diff --git a/spatial_id_detector.go b/spatial_id_detector.go new file mode 100644 index 0000000..c634cbe --- /dev/null +++ b/spatial_id_detector.go @@ -0,0 +1,68 @@ +package spatialID + +import ( + radixtree "github.com/trajectoryjp/multidimensional-radix-tree/src/tree" +) + +type SpatialIDDetector interface { + IsOverlap(ids SpatialIDs) bool +} + +type SpatialIDGreedyDetector struct { + ids SpatialIDs +} + +func NewSpatialIDGreedyDetector(ids SpatialIDs) SpatialIDDetector { + return &SpatialIDGreedyDetector{ids} +} + +func (detector *SpatialIDGreedyDetector) IsOverlap(targetIDs SpatialIDs) bool { + return detector.ids.Overlaps(targetIDs) +} + +type SpatialIDTreeDetector struct { + positiveTree radixtree.TreeInterface + negativeTree radixtree.TreeInterface +} + +func NewSpatialIDTreeDetector(ids SpatialIDs) SpatialIDDetector { + var positiveTree radixtree.TreeInterface + var negativeTree radixtree.TreeInterface + for _, id := range ids { + if id.GetF() >= 0 { + if positiveTree == nil { + positiveTree = radixtree.CreateTree(radixtree.Create3DTable()) + } + treeIndex := radixtree.Indexs{id.GetF(), id.GetX(), id.GetY()} + positiveTree.Append(treeIndex, radixtree.ZoomSetLevel(id.GetZ()), struct{}{}) + } else { + if negativeTree == nil { + negativeTree = radixtree.CreateTree(radixtree.Create3DTable()) + } + treeIndex := radixtree.Indexs{^id.GetF(), id.GetX(), id.GetY()} + negativeTree.Append(treeIndex, radixtree.ZoomSetLevel(id.GetZ()), struct{}{}) + } + } + return &SpatialIDTreeDetector{positiveTree, negativeTree} +} + +func (tree *SpatialIDTreeDetector) IsOverlap(targetIds SpatialIDs) bool { + for _, id := range targetIds { + var isOverlap bool + if id.GetF() >= 0 { + if tree.positiveTree != nil { + treeIndex := radixtree.Indexs{id.GetF(), id.GetX(), id.GetY()} + isOverlap = tree.positiveTree.IsOverlap(treeIndex, radixtree.ZoomSetLevel(id.GetZ())) + } + } else { + if tree.negativeTree != nil { + treeIndex := radixtree.Indexs{^id.GetF(), id.GetX(), id.GetY()} + isOverlap = tree.negativeTree.IsOverlap(treeIndex, radixtree.ZoomSetLevel(id.GetZ())) + } + } + if isOverlap { + return true + } + } + return false +} diff --git a/spatial_id_detector_test.go b/spatial_id_detector_test.go new file mode 100644 index 0000000..a0dfd18 --- /dev/null +++ b/spatial_id_detector_test.go @@ -0,0 +1,75 @@ +package spatialID + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSpatialIDDetector(t *testing.T) { + testCases := []struct { + spatialIDs SpatialIDs + targetSpatialIDs SpatialIDs + expectedResult bool + }{ + // ズームレベル最小、インデックス最小値/最大値 + { + spatialIDs: SpatialIDs{{0, 0, 0, 0}}, + targetSpatialIDs: SpatialIDs{{0, 0, 0, 0}}, + expectedResult: true, + }, + // ズームレベル最大、インデックス最小値 + { + spatialIDs: SpatialIDs{{MaxZ, 0, 0, 0}}, + targetSpatialIDs: SpatialIDs{{MaxZ, 0, 0, 0}}, + expectedResult: true, + }, + // ズームレベル最大、インデックス最大値 + { + spatialIDs: SpatialIDs{{MaxZ, int64(1)<