From 372744b192beb9b609ec28d43124614e22a1bf01 Mon Sep 17 00:00:00 2001 From: tidwall Date: Tue, 6 Nov 2018 03:40:52 -0700 Subject: [PATCH] More hacking vendored circle.go --- cmd/tile38-benchmark/main.go | 52 ++++++++++-- internal/collection/collection.go | 25 ++++++ vendor/github.com/tidwall/geojson/circle.go | 26 +++--- vendor/github.com/tidwall/geojson/geo/geo.go | 83 +++++++++++++++++++- vendor/github.com/tidwall/geojson/rect.go | 4 +- 5 files changed, 163 insertions(+), 27 deletions(-) diff --git a/cmd/tile38-benchmark/main.go b/cmd/tile38-benchmark/main.go index 772568cc..49e93d89 100644 --- a/cmd/tile38-benchmark/main.go +++ b/cmd/tile38-benchmark/main.go @@ -23,7 +23,7 @@ var ( pipeline = 1 csv = false json = false - tests = "PING,SET,GET,SEARCH,EVAL" + tests = "PING,SET,GET,INTERSECTS,WITHIN,NEARBY,EVAL" redis = false ) @@ -270,36 +270,72 @@ func main() { }, ) } - case "SEARCH": + case "INTERSECTS": if !redis { - redbench.Bench("SEARCH (nearby 1km)", addr, opts, prepFn, + + redbench.Bench("INTERSECTS (intersects-circle 1km)", addr, opts, prepFn, func(buf []byte) []byte { lat, lon := randPoint() - return redbench.AppendCommand(buf, "NEARBY", "key:bench", "COUNT", "POINT", + return redbench.AppendCommand(buf, + "INTERSECTS", "key:bench", "COUNT", "CIRCLE", strconv.FormatFloat(lat, 'f', 5, 64), strconv.FormatFloat(lon, 'f', 5, 64), "1000") }, ) - redbench.Bench("SEARCH (nearby 10km)", addr, opts, prepFn, + redbench.Bench("INTERSECTS (intersects-circle 10km)", addr, opts, prepFn, func(buf []byte) []byte { lat, lon := randPoint() - return redbench.AppendCommand(buf, "NEARBY", "key:bench", "COUNT", "POINT", + return redbench.AppendCommand(buf, + "INTERSECTS", "key:bench", "COUNT", "CIRCLE", strconv.FormatFloat(lat, 'f', 5, 64), strconv.FormatFloat(lon, 'f', 5, 64), "10000") }, ) - redbench.Bench("SEARCH (nearby 100km)", addr, opts, prepFn, + redbench.Bench("INTERSECTS (intersects-circle 100km)", addr, opts, prepFn, func(buf []byte) []byte { lat, lon := randPoint() - return redbench.AppendCommand(buf, "NEARBY", "key:bench", "COUNT", "POINT", + return redbench.AppendCommand(buf, + "INTERSECTS", "key:bench", "COUNT", "CIRCLE", strconv.FormatFloat(lat, 'f', 5, 64), strconv.FormatFloat(lon, 'f', 5, 64), "100000") }, ) } + case "NEARBY": + if !redis { + redbench.Bench("NEARBY (limit 1)", addr, opts, prepFn, + func(buf []byte) []byte { + lat, lon := randPoint() + return redbench.AppendCommand(buf, + "NEARBY", "key:bench", "LIMIT", "1", "COUNT", "POINT", + strconv.FormatFloat(lat, 'f', 5, 64), + strconv.FormatFloat(lon, 'f', 5, 64), + ) + }, + ) + redbench.Bench("NEARBY (limit 10)", addr, opts, prepFn, + func(buf []byte) []byte { + lat, lon := randPoint() + return redbench.AppendCommand(buf, + "NEARBY", "key:bench", "LIMIT", "10", "COUNT", "POINT", + strconv.FormatFloat(lat, 'f', 5, 64), + strconv.FormatFloat(lon, 'f', 5, 64), + ) + }, + ) + redbench.Bench("NEARBY (limit 100)", addr, opts, prepFn, + func(buf []byte) []byte { + lat, lon := randPoint() + return redbench.AppendCommand(buf, + "NEARBY", "key:bench", "LIMIT", "100", "COUNT", "POINT", + strconv.FormatFloat(lat, 'f', 5, 64), + strconv.FormatFloat(lon, 'f', 5, 64)) + }, + ) + } case "EVAL": if !redis { var i int64 diff --git a/internal/collection/collection.go b/internal/collection/collection.go index 1063f37b..ed8555ce 100644 --- a/internal/collection/collection.go +++ b/internal/collection/collection.go @@ -4,6 +4,7 @@ import ( "github.com/tidwall/boxtree/d2" "github.com/tidwall/btree" "github.com/tidwall/geojson" + "github.com/tidwall/geojson/geo" "github.com/tidwall/geojson/geometry" "github.com/tidwall/tile38/internal/ds" ) @@ -673,6 +674,30 @@ func (c *Collection) Nearby( cursor Cursor, iter func(id string, obj geojson.Object, fields []float64) bool, ) bool { + + if circle, ok := target.(*geojson.Circle); ok { + + meters := circle.Meters() + if meters > 0 { + center := circle.Center() + minLat, minLon, maxLat, maxLon := + geo.RectFromCenter(center.Y, center.X, meters) + var exists bool + c.index.Search( + []float64{minLon, minLat}, + []float64{maxLon, maxLat}, + func(_, _ []float64, itemv interface{}) bool { + exists = true + return false + }, + ) + if !exists { + return true + } + return true + } + } + alive := true center := target.Center() var count uint64 diff --git a/vendor/github.com/tidwall/geojson/circle.go b/vendor/github.com/tidwall/geojson/circle.go index 7d4876bb..3cdd8492 100644 --- a/vendor/github.com/tidwall/geojson/circle.go +++ b/vendor/github.com/tidwall/geojson/circle.go @@ -2,8 +2,6 @@ package geojson import ( "strconv" - "sync/atomic" - "unsafe" "github.com/tidwall/geojson/geo" "github.com/tidwall/geojson/geometry" @@ -11,7 +9,7 @@ import ( // Circle ... type Circle struct { - object *Object + object Object center geometry.Point meters float64 haversine float64 @@ -29,6 +27,10 @@ func NewCircle(center geometry.Point, meters float64, steps int) *Circle { g.center = center g.meters = meters g.steps = steps + if meters > 0 { + meters = geo.NormalizeDistance(meters) + g.haversine = geo.DistanceToHaversine(meters) + } return g } @@ -182,6 +184,7 @@ func (g *Circle) ForEach(iter func(geom Object) bool) bool { // NumPoints ... func (g *Circle) NumPoints() int { + // should this be g.steps? return 1 } @@ -201,21 +204,10 @@ func (g *Circle) Spatial() 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 - } + if g.object != nil { + return g.object } + return makeCircleObject(g.center, g.meters, g.steps) } func makeCircleObject(center geometry.Point, meters float64, steps int) Object { diff --git a/vendor/github.com/tidwall/geojson/geo/geo.go b/vendor/github.com/tidwall/geojson/geo/geo.go index d82fc9a3..fe5c497e 100644 --- a/vendor/github.com/tidwall/geojson/geo/geo.go +++ b/vendor/github.com/tidwall/geojson/geo/geo.go @@ -45,7 +45,7 @@ func DistanceToHaversine(meters float64) float64 { return sin * sin } -// DistanceFromHaversine... +// DistanceFromHaversine ... func DistanceFromHaversine(haversine float64) float64 { return earthRadius * 2 * math.Asin(math.Sqrt(haversine)) } @@ -88,3 +88,84 @@ func BearingTo(latA, lonA, latB, lonB float64) float64 { return math.Mod(θ*degrees+360, 360) } + +// RectFromCenter calculates the bounding box surrounding a circle. +func RectFromCenter(lat, lon, meters float64) ( + minLat, minLon, maxLat, maxLon float64, +) { + + // see http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#Latitude + lat *= radians + lon *= radians + + r := meters / earthRadius // angular radius + + minLat = lat - r + maxLat = lat + r + + latT := math.Asin(math.Sin(lat) / math.Cos(r)) + lonΔ := math.Acos((math.Cos(r) - math.Sin(latT)*math.Sin(lat)) / (math.Cos(latT) * math.Cos(lat))) + + minLon = lon - lonΔ + maxLon = lon + lonΔ + + // Adjust for north poll + if maxLat > math.Pi/2 { + minLon = -math.Pi + maxLat = math.Pi / 2 + maxLon = math.Pi + } + + // Adjust for south poll + if minLat < -math.Pi/2 { + minLat = -math.Pi / 2 + minLon = -math.Pi + maxLon = math.Pi + } + + // Adjust for wraparound. Remove this if the commented-out condition below this block is added. + if minLon < -math.Pi || maxLon > math.Pi { + minLon = -math.Pi + maxLon = math.Pi + } + + /* + // Consider splitting area into two bboxes, using the below checks, and erasing above block for performance. See http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#PolesAnd180thMeridian + // Adjust for wraparound if minimum longitude is less than -180 degrees. + if lonMin < -math.Pi { + // box 1: + latMin = latMin + latMax = latMax + lonMin += 2*math.Pi + lonMax = math.Pi + // box 2: + latMin = latMin + latMax = latMax + lonMin = -math.Pi + lonMax = lonMax + } + // Adjust for wraparound if maximum longitude is greater than 180 degrees. + if lonMax > math.Pi { + // box 1: + latMin = latMin + latMax = latMax + lonMin = lonMin + lonMax = -math.Pi + // box 2: + latMin = latMin + latMax = latMax + lonMin = -math.Pi + lonMax -= 2*math.Pi + } + */ + + minLon = math.Mod(minLon+3*math.Pi, 2*math.Pi) - math.Pi // normalise to -180..+180° + maxLon = math.Mod(maxLon+3*math.Pi, 2*math.Pi) - math.Pi + + minLat *= degrees + minLon *= degrees + maxLat *= degrees + maxLon *= degrees + return + +} diff --git a/vendor/github.com/tidwall/geojson/rect.go b/vendor/github.com/tidwall/geojson/rect.go index 04b593a6..5a11286e 100644 --- a/vendor/github.com/tidwall/geojson/rect.go +++ b/vendor/github.com/tidwall/geojson/rect.go @@ -1,6 +1,8 @@ package geojson -import "github.com/tidwall/geojson/geometry" +import ( + "github.com/tidwall/geojson/geometry" +) // Rect ... type Rect struct {