Hack geojson circle.go

This commit is contained in:
tidwall 2018-11-05 17:28:26 -07:00
parent 161c6faff9
commit edf5d22095
1 changed files with 75 additions and 24 deletions

View File

@ -2,6 +2,8 @@ package geojson
import ( import (
"strconv" "strconv"
"sync/atomic"
"unsafe"
"github.com/tidwall/geojson/geo" "github.com/tidwall/geojson/geo"
"github.com/tidwall/geojson/geometry" "github.com/tidwall/geojson/geometry"
@ -9,7 +11,7 @@ import (
// Circle ... // Circle ...
type Circle struct { type Circle struct {
Object object *Object
center geometry.Point center geometry.Point
meters float64 meters float64
haversine float64 haversine float64
@ -27,27 +29,6 @@ func NewCircle(center geometry.Point, meters float64, steps int) *Circle {
g.center = center g.center = center
g.meters = meters g.meters = meters
g.steps = steps g.steps = steps
if meters <= 0 {
g.Object = NewPoint(center)
} else {
meters = geo.NormalizeDistance(meters)
var points []geometry.Point
step := 360.0 / float64(steps)
i := 0
for deg := 360.0; deg > 0; deg -= step {
lat, lon := geo.DestinationPoint(center.Y, center.X, meters, deg)
points = append(points, geometry.Point{X: lon, Y: lat})
i++
}
// TODO: account for the pole and antimerdian. In most cases only a
// polygon is needed, but when the circle bounds passes the 90/180
// lines, we need to create a multipolygon
points = append(points, points[0])
g.Object = NewPolygon(
geometry.NewPoly(points, nil, geometry.DefaultIndexOptions),
)
g.haversine = geo.DistanceToHaversine(meters)
}
return g return g
} }
@ -128,7 +109,7 @@ func (g *Circle) Contains(obj Object) bool {
return true return true
default: default:
// No simple cases, so using polygon approximation. // No simple cases, so using polygon approximation.
return g.Object.Contains(other) return g.getObject().Contains(other)
} }
} }
@ -185,6 +166,76 @@ func (g *Circle) Intersects(obj Object) bool {
return false return false
default: default:
// No simple cases, so using polygon approximation. // No simple cases, so using polygon approximation.
return g.Object.Intersects(obj) return g.getObject().Intersects(obj)
} }
} }
// Empty ...
func (g *Circle) Empty() bool {
return false
}
// ForEach ...
func (g *Circle) ForEach(iter func(geom Object) bool) bool {
return iter(g)
}
// NumPoints ...
func (g *Circle) NumPoints() int {
return 1
}
// Distance ...
func (g *Circle) Distance(other Object) float64 {
return g.getObject().Distance(other)
}
// Rect ...
func (g *Circle) Rect() geometry.Rect {
return g.getObject().Rect()
}
// Spatial ...
func (g *Circle) Spatial() Spatial {
return g.getObject().Spatial()
}
func (g *Circle) getObject() Object {
for {
object := (*Object)(atomic.LoadPointer(
(*unsafe.Pointer)(unsafe.Pointer(&g.object))),
)
if object != nil {
return *object
}
newObject := makeCircleObject(g.center, g.meters, g.steps)
if atomic.CompareAndSwapPointer(
(*unsafe.Pointer)(unsafe.Pointer(&g.object)),
nil, unsafe.Pointer(&newObject),
) {
return *object
}
}
}
func makeCircleObject(center geometry.Point, meters float64, steps int) Object {
if meters <= 0 {
return NewPoint(center)
}
meters = geo.NormalizeDistance(meters)
var points []geometry.Point
step := 360.0 / float64(steps)
i := 0
for deg := 360.0; deg > 0; deg -= step {
lat, lon := geo.DestinationPoint(center.Y, center.X, meters, deg)
points = append(points, geometry.Point{X: lon, Y: lat})
i++
}
// TODO: account for the pole and antimerdian. In most cases only a
// polygon is needed, but when the circle bounds passes the 90/180
// lines, we need to create a multipolygon
points = append(points, points[0])
return NewPolygon(
geometry.NewPoly(points, nil, geometry.DefaultIndexOptions),
)
}