mirror of https://github.com/tidwall/tile38.git
337 lines
6.7 KiB
Go
337 lines
6.7 KiB
Go
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
|
|
}
|
|
|
|
// Valid ...
|
|
func (g *collection) Valid() bool {
|
|
return g.Rect().Valid()
|
|
}
|
|
|
|
// 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))
|
|
}
|
|
|
|
// MarshalJSON ...
|
|
func (g *collection) MarshalJSON() ([]byte, error) {
|
|
return g.AppendJSON(nil), 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())
|
|
}
|