package geojson import ( "github.com/tidwall/boxtree/d2" "github.com/tidwall/geojson/geometry" ) type collection struct { children []Object extra *extra tree *d2.BoxTree prect geometry.Rect pempty bool } func (g *collection) Indexed() bool { return g.tree != nil } func (g *collection) Children() []Object { return g.children } // ForEach ... func (g *collection) ForEach(iter func(geom Object) bool) bool { for _, child := range g.children { if !child.ForEach(iter) { return false } } return true } // Base ... func (g *collection) Base() []Object { return g.children } func (g *collection) Search(rect geometry.Rect, iter func(child Object) bool) { if g.tree != nil { g.tree.Search( []float64{rect.Min.X, rect.Min.Y}, []float64{rect.Max.X, rect.Max.Y}, func(_, _ []float64, value interface{}) bool { return iter(value.(Object)) }, ) } else { for _, child := range g.children { if child.Empty() { continue } if child.Rect().IntersectsRect(rect) { if !iter(child) { break } } } } } // Empty ... func (g *collection) Empty() bool { return g.pempty } // Rect ... func (g *collection) Rect() geometry.Rect { return g.prect } // Center ... func (g *collection) Center() geometry.Point { return g.Rect().Center() } // AppendJSON ... func (g *collection) AppendJSON(dst []byte) []byte { // this should never be called return append(dst, "null"...) } // JSON ... func (g *collection) JSON() string { return string(g.AppendJSON(nil)) } // String ... func (g *collection) String() string { return string(g.AppendJSON(nil)) } // Within ... func (g *collection) Within(obj Object) bool { return obj.Contains(g) } // Contains ... func (g *collection) Contains(obj Object) bool { if g.Empty() { return false } // all of obj must be contained by any number of the collection children var objContained bool obj.ForEach(func(geom Object) bool { if geom.Empty() { // ignore empties return true } var geomContained bool g.Search(geom.Rect(), func(child Object) bool { if child.Contains(geom) { // found a child object that contains geom, end inner loop geomContained = true return false } return true }) if !geomContained { // unmark and quit the loop objContained = false return false } // mark that at least one geom is contained objContained = true return true }) return objContained } func (g *collection) Spatial() Spatial { return g } func (g *collection) WithinRect(rect geometry.Rect) bool { if g.Empty() { return false } var withinCount int g.Search(rect, func(child Object) bool { if child.Spatial().WithinRect(rect) { withinCount++ return true } return false }) return withinCount == len(g.children) } func (g *collection) WithinPoint(point geometry.Point) bool { if g.Empty() { return false } var withinCount int g.Search(point.Rect(), func(child Object) bool { if child.Spatial().WithinPoint(point) { withinCount++ return true } return false }) return withinCount == len(g.children) } func (g *collection) WithinLine(line *geometry.Line) bool { if g.Empty() { return false } var withinCount int g.Search(line.Rect(), func(child Object) bool { if child.Spatial().WithinLine(line) { withinCount++ return true } return false }) return withinCount == len(g.children) } func (g *collection) WithinPoly(poly *geometry.Poly) bool { if g.Empty() { return false } var withinCount int g.Search(poly.Rect(), func(child Object) bool { if child.Spatial().WithinPoly(poly) { withinCount++ return true } return false }) return withinCount == len(g.children) } // Intersects ... func (g *collection) Intersects(obj Object) bool { // check if any of obj intersects with any of collection var intersects bool obj.ForEach(func(geom Object) bool { if geom.Empty() { // ignore the empties return true } g.Search(geom.Rect(), func(child Object) bool { if child.Intersects(geom) { intersects = true return false } return true }) if intersects { return false } return true }) return intersects } func (g *collection) IntersectsPoint(point geometry.Point) bool { var intersects bool g.Search(point.Rect(), func(child Object) bool { if child.Spatial().IntersectsPoint(point) { intersects = true return false } return true }) return intersects } func (g *collection) IntersectsRect(rect geometry.Rect) bool { var intersects bool g.Search(rect, func(child Object) bool { if child.Spatial().IntersectsRect(rect) { intersects = true return false } return true }) return intersects } func (g *collection) IntersectsLine(line *geometry.Line) bool { var intersects bool g.Search(line.Rect(), func(child Object) bool { if child.Spatial().IntersectsLine(line) { intersects = true return false } return true }) return intersects } func (g *collection) IntersectsPoly(poly *geometry.Poly) bool { var intersects bool g.Search(poly.Rect(), func(child Object) bool { if child.Spatial().IntersectsPoly(poly) { intersects = true return false } return true }) return intersects } // NumPoints ... func (g *collection) NumPoints() int { var n int for _, child := range g.children { n += child.NumPoints() } return n } func (g *collection) parseInitRectIndex(opts *ParseOptions) { g.pempty = true var count int for _, child := range g.children { if child.Empty() { continue } if g.pempty && !child.Empty() { g.pempty = false } if count == 0 { g.prect = child.Rect() } else { if len(g.children) == 1 { g.prect = child.Rect() } else { g.prect = unionRects(g.prect, child.Rect()) } } count++ } if count > 0 && opts.IndexChildren != 0 && count >= opts.IndexChildren { g.tree = new(d2.BoxTree) for _, child := range g.children { if child.Empty() { continue } rect := child.Rect() g.tree.Insert( []float64{rect.Min.X, rect.Min.Y}, []float64{rect.Max.X, rect.Max.Y}, child, ) } } } // Distance ... func (g *collection) Distance(obj Object) float64 { return obj.Spatial().DistancePoint(g.Center()) } func (g *collection) DistancePoint(point geometry.Point) float64 { return geoDistancePoints(g.Center(), point) } func (g *collection) DistanceRect(rect geometry.Rect) float64 { return geoDistancePoints(g.Center(), rect.Center()) } func (g *collection) DistanceLine(line *geometry.Line) float64 { return geoDistancePoints(g.Center(), line.Rect().Center()) } func (g *collection) DistancePoly(poly *geometry.Poly) float64 { return geoDistancePoints(g.Center(), poly.Rect().Center()) }