# patrick-higgins/rtreego forked from dhconnelly/rtreego

### Subversion checkout URL

You can clone with HTTPS or Subversion.

Fetching contributors…

Cannot retrieve contributors at this time

260 lines (230 sloc) 6.418 kb
 // Copyright 2012 Daniel Connelly. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package rtreego import ( "fmt" "math" "strings" ) // DistError is an improper distance measurement. It implements the error // and is generated when a distance-related assertion fails. type DistError float64 func (err DistError) Error() string { return "rtreego: improper distance" } // Point represents a point in 3-dimensional Euclidean space. type Point [Dim]float64 // Dist computes the Euclidean distance between two points p and q. func (p Point) dist(q Point) float64 { sum := 0.0 for i := range p { dx := p[i] - q[i] sum += dx * dx } return math.Sqrt(sum) } // minDist computes the square of the distance from a point to a rectangle. // If the point is contained in the rectangle then the distance is zero. // // Implemented per Definition 2 of "Nearest Neighbor Queries" by // N. Roussopoulos, S. Kelley and F. Vincent, ACM SIGMOD, pages 71-79, 1995. func (p Point) minDist(r *Rect) float64 { sum := 0.0 for i, pi := range p { if pi < r.p[i] { d := pi - r.p[i] sum += d * d } else if pi > r.q[i] { d := pi - r.q[i] sum += d * d } else { sum += 0 } } return sum } // minMaxDist computes the minimum of the maximum distances from p to points // on r. If r is the bounding box of some geometric objects, then there is // at least one object contained in r within minMaxDist(p, r) of p. // // Implemented per Definition 4 of "Nearest Neighbor Queries" by // N. Roussopoulos, S. Kelley and F. Vincent, ACM SIGMOD, pages 71-79, 1995. func (p Point) minMaxDist(r *Rect) float64 { // by definition, MinMaxDist(p, r) = // min{1<=k<=n}(|pk - rmk|^2 + sum{1<=i<=n, i != k}(|pi - rMi|^2)) // where rmk and rMk are defined as follows: rm := func(k int) float64 { if p[k] <= (r.p[k]+r.q[k])/2 { return r.p[k] } return r.q[k] } rM := func(k int) float64 { if p[k] >= (r.p[k]+r.q[k])/2 { return r.p[k] } return r.q[k] } // This formula can be computed in linear time by precomputing // S = sum{1<=i<=n}(|pi - rMi|^2). S := 0.0 for i := range p { d := p[i] - rM(i) S += d * d } // Compute MinMaxDist using the precomputed S. min := math.MaxFloat64 for k := range p { d1 := p[k] - rM(k) d2 := p[k] - rm(k) d := S - d1*d1 + d2*d2 if d < min { min = d } } return min } // Rect represents a subset of 3-dimensional Euclidean space of the form // [a1, b1] x [a2, b2] x ... x [an, bn], where ai < bi for all 1 <= i <= n. type Rect struct { p, q Point // Enforced by NewRect: p[i] <= q[i] for all i. } func (r *Rect) String() string { var s [Dim]string for i, a := range r.p { b := r.q[i] s[i] = fmt.Sprintf("[%.2f, %.2f]", a, b) } return strings.Join(s[:], "x") } // NewRect constructs and returns a pointer to a Rect given a corner point and // the lengths of each dimension. The point p should be the most-negative point // on the rectangle (in every dimension) and every length should be positive. func NewRect(p Point, lengths [Dim]float64) (r Rect, err error) { r.p = p r.q = lengths for i, l := range r.q { if l <= 0 { return r, DistError(l) } r.q[i] += r.p[i] } return r, nil } // size computes the measure of a rectangle (the product of its side lengths). func (r *Rect) size() float64 { size := 1.0 for i, a := range r.p { b := r.q[i] size *= b - a } return size } // margin computes the sum of the edge lengths of a rectangle. func (r *Rect) margin() float64 { // The number of edges in an n-dimensional rectangle is n * 2^(n-1) // (http://en.wikipedia.org/wiki/Hypercube_graph). Thus the number // of edges of length (ai - bi), where the rectangle is determined // by p = (a1, a2, ..., an) and q = (b1, b2, ..., bn), is 2^(n-1). // // The margin of the rectangle, then, is given by the formula // 2^(n-1) * [(b1 - a1) + (b2 - a2) + ... + (bn - an)]. sum := 0.0 for i, a := range r.p { b := r.q[i] sum += b - a } return 4.0 * sum } // containsPoint tests whether p is located inside or on the boundary of r. func (r *Rect) containsPoint(p Point) bool { for i, a := range p { // p is contained in (or on) r if and only if p <= a <= q for // every dimension. if a < r.p[i] || a > r.q[i] { return false } } return true } // containsRect tests whether r2 is is located inside r1. func (r1 *Rect) containsRect(r2 *Rect) bool { for i, a1 := range r1.p { b1, a2, b2 := r1.q[i], r2.p[i], r2.q[i] // enforced by constructor: a1 <= b1 and a2 <= b2. // so containment holds if and only if a1 <= a2 <= b2 <= b1 // for every dimension. if a1 > a2 || b2 > b1 { return false } } return true } // intersect computes the intersection of two rectangles. If no intersection // exists, the intersection is nil. func intersect(r1, r2 *Rect) bool { // There are four cases of overlap: // // 1. a1------------b1 // a2------------b2 // p--------q // // 2. a1------------b1 // a2------------b2 // p--------q // // 3. a1-----------------b1 // a2-------b2 // p--------q // // 4. a1-------b1 // a2-----------------b2 // p--------q // // Thus there are only two cases of non-overlap: // // 1. a1------b1 // a2------b2 // // 2. a1------b1 // a2------b2 // // Enforced by constructor: a1 <= b1 and a2 <= b2. So we can just // check the endpoints. for i := 0; i < Dim; i++ { if r2.q[i] <= r1.p[i] || r1.q[i] <= r2.p[i] { return false } } return true } // ToRect constructs a rectangle containing p with side lengths 2*tol. func (p Point) ToRect(tol float64) *Rect { var r Rect for i := range p { r.p[i] = p[i] - tol r.q[i] = p[i] + tol } return &r } // boundingBox constructs the smallest rectangle containing both r1 and r2. func boundingBox(r1, r2 *Rect) *Rect { var r Rect for i := 0; i < Dim; i++ { r.p[i] = math.Min(r1.p[i], r2.p[i]) r.q[i] = math.Max(r1.q[i], r2.q[i]) } return &r } // boundingBoxN constructs the smallest rectangle containing all of r... func boundingBoxN(rects ...*Rect) *Rect { if len(rects) == 1 { return rects[0] } bb := boundingBox(rects[0], rects[1]) for _, rect := range rects[2:] { bb = boundingBox(bb, rect) } return bb }
Something went wrong with that request. Please try again.