rtreego

repository·master·Indexed 20 days ago

https://github.com/dhconnelly/rtreego

A Go library for efficiently storing and querying N-dimensional spatial data using the R-tree data structure. It supports bounding-box intersection queries, k-nearest-neighbor (KNN) searches, and bulk-loading via the Overlap Minimizing Top-down (OMT) algorithm. The library provides a Spatial interface for custom objects, as well as Point and Rect types for defining n-dimensional Euclidean space and axis-aligned bounding boxes.

Tokens
3.5K
Snippets
19
Records
19
Agent score
71%

What's inside rtreego

  1. How to use the Spatial interface

    master

    To store custom objects in an R-tree, your type must implement the Spatial interface by providing a Bounds() method that returns a *Rect.

    type Spatial interface {
      Bounds() *Rect
    }
    type Thing struct {
      where *Rect
      name string
    }
    
    func (t *Thing) Bounds() *Rect {
      return t.where
    }
  2. Insert, delete, and update objects in the tree

    master

    Inserting objects

    Use rt.Insert(object) to add a Spatial object to the tree.

    Deleting objects

    • Standard Delete: rt.Delete(object) compares memory addresses of the objects. This requires you to have the original pointer.
    • Custom Comparator: If you don't have the original pointer, use rt.DeleteWithComparator(object, comparator). The comparator must match the signature: type Comparator func(obj1, obj2 Spatial) (equal bool).

    Updating objects

    Warning: You cannot update an object's location by modifying its internal *Rect. Doing so will corrupt the tree. To update an object's position, you must:

    1. Delete the existing object.
    2. Update the object's coordinates.
    3. Insert the object back into the tree.

    Converting Points to Rectangles

    If you want to store a Point as a spatial object, use the ToRect(tolerance) method to create a small rectangle centered at that point.

    // Example of custom deletion
    cmp := func(obj1, obj2 Spatial) bool {
      sp1 := obj1.(*IDRect)
      sp2 := obj2.(*IDRect)
      return sp1.ID == sp2.ID
    }
    rt.DeleteWithComparator(obj, cmp)
    
    // Example of converting a point to a rectangle for storage
    type Somewhere struct {
      location rtreego.Point
      name string
    }
    
    func (s *Somewhere) Bounds() *Rect {
      var tol = 0.01
      return s.location.ToRect(tol)
    }
  3. Implement the Spatial interface

    master

    To store custom objects in an Rtree, your type must implement the Spatial interface by providing a Bounds() method that returns a Rect.

    type MyObject struct {
        // ... fields
    }
    
    func (m *MyObject) Bounds() rtreego.Rect {
        return rtreego.Rect{...
    }
  4. Create and initialize an R-tree

    master

    You can create a new R-tree by specifying the number of spatial dimensions, the minimum branching factor, and the maximum branching factor. You can also perform a bulk-load by passing existing objects during initialization.

    Standard initialization: rt := rtreego.NewTree(dimensions, min, max)

    Bulk-load initialization: rt := rtreego.NewTree(dimensions, min, max, objects...)

    // Create a 2D tree with min branching 25 and max 50
    rt := rtreego.NewTree(2, 25, 50)
    
    // Or bulk-load with objects
    rt := rtreego.NewTree(2, 25, 50, objects...)
  5. Perform bounding-box and k-nearest-neighbor queries

    master

    Bounding-box queries

    Use rt.SearchIntersect(searchRect) to find all objects that have a non-zero intersection volume with the provided *Rect.

    K-Nearest-Neighbors (KNN) queries

    Use rt.NearestNeighbors(k, queryPoint) to find the k objects in the tree closest to the specified rtreego.Point.

    // Bounding-box search
    bb, _ := rtreego.NewRect(rtreego.Point{1.7, -3.4}, []float64{3.2, 1.9})
    results := rt.SearchIntersect(bb)
    
    // K-Nearest-Neighbors search
    q := rtreego.Point{6.5, -2.47}
    k := 5
    results = rt.NearestNeighbors(k, q)
  6. Filter query results

    master

    You can refine search results by providing a Filter function to the search methods. A filter can decide to refuse a specific object or abort the entire search.

    Filter signature: type Filter func(results []Spatial, object Spatial) (refuse, abort bool)

    Example of using the built-in LimitFilter to restrict the number of returned results:

    // Returns a maximum of three results
    results := tree.SearchIntersect(bb, rtreego.LimitFilter(3))
  7. Search for intersecting objects

    master

    Use SearchIntersect(bb Rect, filters ...Filter) to find all objects whose bounding boxes intersect the provided Rect. You can optionally pass Filter functions to refine the results (e.g., to limit the number of results returned).

    searchArea := rtreego.Rect{...}
    results := tree.SearchIntersect(searchArea)
  8. Delete objects from the Rtree

    master

    You can remove objects using two methods:

    1. Delete(obj Spatial): Uses the default equality comparator to find and remove the object.
    2. DeleteWithComparator(obj Spatial, cmp Comparator): Allows you to provide a custom Comparator function. This is useful if you want to remove an object based on its properties without having the exact original pointer/instance.
    // Using default comparator
    success := tree.Delete(myObj)
    
    // Using custom comparator
    success := tree.DeleteWithComparator(myObj, func(obj1, obj2 rtreego.Spatial) bool {
        return obj1.(*MyType).ID == obj2.(*MyType).ID
    })
  9. Query Rect properties and dimensions

    master

    Once you have a Rect, you can inspect its dimensions and size:

    • PointCoord(i int) float64: Returns the coordinate of the minimum corner p at dimension i.
    • LengthsCoord(i int) float64: Returns the length of the rectangle in dimension i (q[i] - p[i]).
    • Size() float64: Returns the total measure (volume/area) of the rectangle (the product of all side lengths).
    rect, _ := rtreego.NewRect(rtreego.Point{0, 0}, []float64{10, 5})
    
    fmt.Println(rect.PointCoord(0))    // 0
    fmt.Println(rect.LengthsCoord(1)) // 5
    fmt.Println(rect.Size())          // 50