diff --git a/pkg/collection/collection.go b/pkg/collection/collection.go index 36cb0b89..0b850ed0 100644 --- a/pkg/collection/collection.go +++ b/pkg/collection/collection.go @@ -417,9 +417,11 @@ func (c *Collection) Nearby(sparse uint8, lat, lon, meters, minZ, maxZ float64, } // Within returns all object that are fully contained within an object or bounding box. Set obj to nil in order to use the bounding box. -func (c *Collection) Within(sparse uint8, obj geojson.Object, minLat, minLon, maxLat, maxLon, minZ, maxZ float64, iterator func(id string, obj geojson.Object, fields []float64) bool) bool { +func (c *Collection) Within(sparse uint8, obj geojson.Object, minLat, minLon, maxLat, maxLon, lat, lon, meters, minZ, maxZ float64, iterator func(id string, obj geojson.Object, fields []float64) bool) bool { var keepon = true var bbox geojson.BBox + center := geojson.Position{X: lon, Y: lat, Z: 0} + if obj != nil { bbox = obj.CalculatedBBox() if minZ == math.Inf(-1) && maxZ == math.Inf(+1) { @@ -428,6 +430,8 @@ func (c *Collection) Within(sparse uint8, obj geojson.Object, minLat, minLon, ma bbox.Max.Z = maxZ } } + } else if meters != -1 { + bbox = geojson.BBoxesFromCenter(lat, lon, meters) } else { bbox = geojson.BBox{Min: geojson.Position{X: minLon, Y: minLat, Z: minZ}, Max: geojson.Position{X: maxLon, Y: maxLat, Z: maxZ}} } @@ -443,6 +447,15 @@ func (c *Collection) Within(sparse uint8, obj geojson.Object, minLat, minLon, ma } return true }) + } else if meters != -1 { + keepon = c.geoSearch(bbox, func(id string, o geojson.Object, fields []float64) bool { + if o.WithinCircle(center, meters) { + if iterator(id, o, fields) { + return false + } + } + return true + }) } if keepon { keepon = c.geoSearch(bbox, func(id string, o geojson.Object, fields []float64) bool { @@ -467,6 +480,13 @@ func (c *Collection) Within(sparse uint8, obj geojson.Object, minLat, minLon, ma } return true }) + } else if meters != -1 { + return c.geoSearch(bbox, func(id string, o geojson.Object, fields []float64) bool { + if o.WithinCircle(center, meters) { + return iterator(id, o, fields) + } + return true + }) } return c.geoSearch(bbox, func(id string, o geojson.Object, fields []float64) bool { if o.WithinBBox(bbox) { @@ -477,9 +497,10 @@ func (c *Collection) Within(sparse uint8, obj geojson.Object, minLat, minLon, ma } // Intersects returns all object that are intersect an object or bounding box. Set obj to nil in order to use the bounding box. -func (c *Collection) Intersects(sparse uint8, obj geojson.Object, minLat, minLon, maxLat, maxLon, minZ, maxZ float64, iterator func(id string, obj geojson.Object, fields []float64) bool) bool { +func (c *Collection) Intersects(sparse uint8, obj geojson.Object, minLat, minLon, maxLat, maxLon, lat, lon, meters, minZ, maxZ float64, iterator func(id string, obj geojson.Object, fields []float64) bool) bool { var keepon = true var bbox geojson.BBox + center := geojson.Position{X: lon, Y: lat, Z: 0} if obj != nil { bbox = obj.CalculatedBBox() if minZ == math.Inf(-1) && maxZ == math.Inf(+1) { @@ -488,6 +509,8 @@ func (c *Collection) Intersects(sparse uint8, obj geojson.Object, minLat, minLon bbox.Max.Z = maxZ } } + } else if meters != -1 { + bbox = geojson.BBoxesFromCenter(lat, lon, meters) } else { bbox = geojson.BBox{Min: geojson.Position{X: minLon, Y: minLat, Z: minZ}, Max: geojson.Position{X: maxLon, Y: maxLat, Z: maxZ}} } @@ -514,6 +537,15 @@ func (c *Collection) Intersects(sparse uint8, obj geojson.Object, minLat, minLon } return true }) + } else if meters != -1 { + keepon = c.geoSearch(bbox, func(id string, o geojson.Object, fields []float64) bool { + if o.IntersectsCircle(center, meters) { + if iterator(id, o, fields) { + return false + } + } + return true + }) } if keepon { keepon = c.geoSearch(bbox, func(id string, o geojson.Object, fields []float64) bool { @@ -538,6 +570,13 @@ func (c *Collection) Intersects(sparse uint8, obj geojson.Object, minLat, minLon } return true }) + } else if meters != -1 { + return c.geoSearch(bbox, func(id string, o geojson.Object, fields []float64) bool { + if o.IntersectsCircle(center, meters) { + return iterator(id, o, fields) + } + return true + }) } return c.geoSearch(bbox, func(id string, o geojson.Object, fields []float64) bool { if o.IntersectsBBox(bbox) { diff --git a/pkg/controller/search.go b/pkg/controller/search.go index d38c95ce..ddd503c8 100644 --- a/pkg/controller/search.go +++ b/pkg/controller/search.go @@ -87,6 +87,7 @@ func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string) err = errInvalidArgument(typ) return } + s.meters = -1 // this will become non-negative if search is within a circle switch ltyp { case "point": var slat, slon, smeters string @@ -127,6 +128,41 @@ func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string) err = errInvalidArgument(smeters) return } + if s.meters < 0 { + err = errInvalidArgument(smeters) + return + } + } + case "circle": + var slat, slon, smeters string + if vs, slat, ok = tokenval(vs); !ok || slat == "" { + err = errInvalidNumberOfArguments + return + } + if vs, slon, ok = tokenval(vs); !ok || slon == "" { + err = errInvalidNumberOfArguments + return + } + if vs, smeters, ok = tokenval(vs); !ok || smeters == "" { + err = errInvalidArgument(slat) + return + } + + if s.lat, err = strconv.ParseFloat(slat, 64); err != nil { + err = errInvalidArgument(slat) + return + } + if s.lon, err = strconv.ParseFloat(slon, 64); err != nil { + err = errInvalidArgument(slon) + return + } + if s.meters, err = strconv.ParseFloat(smeters, 64); err != nil { + err = errInvalidArgument(smeters) + return + } + if s.meters < 0 { + err = errInvalidArgument(smeters) + return } case "object": var obj string @@ -292,7 +328,8 @@ func (c *Controller) cmdSearchArgs(cmd string, vs []resp.Value, types []string) } var nearbyTypes = []string{"point"} -var withinOrIntersectsTypes = []string{"geo", "bounds", "hash", "tile", "quadkey", "get", "object"} +var withinOrIntersectsTypes = []string{ + "geo", "bounds", "hash", "tile", "quadkey", "get", "object", "circle"} func (c *Controller) cmdNearby(msg *server.Message) (res resp.Value, err error) { start := time.Now() @@ -449,7 +486,11 @@ func (c *Controller) cmdWithinOrIntersects(cmd string, msg *server.Message) (res if sw.col != nil { minZ, maxZ := zMinMaxFromWheres(s.wheres) if cmd == "within" { - sw.col.Within(s.sparse, s.o, s.minLat, s.minLon, s.maxLat, s.maxLon, minZ, maxZ, + sw.col.Within(s.sparse, + s.o, + s.minLat, s.minLon, s.maxLat, s.maxLon, + s.lat, s.lon, s.meters, + minZ, maxZ, func(id string, o geojson.Object, fields []float64) bool { if c.hasExpired(s.key, id) { return true @@ -463,7 +504,11 @@ func (c *Controller) cmdWithinOrIntersects(cmd string, msg *server.Message) (res }, ) } else if cmd == "intersects" { - sw.col.Intersects(s.sparse, s.o, s.minLat, s.minLon, s.maxLat, s.maxLon, minZ, maxZ, + sw.col.Intersects(s.sparse, + s.o, + s.minLat, s.minLon, s.maxLat, s.maxLon, + s.lat, s.lon, s.meters, + minZ, maxZ, func(id string, o geojson.Object, fields []float64) bool { if c.hasExpired(s.key, id) { return true diff --git a/pkg/core/commands.json b/pkg/core/commands.json index d8c21d1e..9f45c9b5 100644 --- a/pkg/core/commands.json +++ b/pkg/core/commands.json @@ -674,7 +674,7 @@ "group": "search" }, "WITHIN": { - "summary": "Searches for ids that are nearby a point", + "summary": "Searches for ids that completely within the area", "complexity": "O(log(N)) where N is the number of ids in the area", "arguments":[ { @@ -836,6 +836,23 @@ } ] }, + { + "name": "CIRCLE", + "arguments": [ + { + "name": "lat", + "type": "double" + }, + { + "name": "lon", + "type": "double" + }, + { + "name": "meters", + "type": "double" + } + ] + }, { "name": "TILE", "arguments":[ @@ -878,7 +895,7 @@ "group": "search" }, "INTERSECTS": { - "summary": "Searches for ids that are nearby a point", + "summary": "Searches for ids that intersect an area", "complexity": "O(log(N)) where N is the number of ids in the area", "arguments":[ { @@ -1040,6 +1057,23 @@ } ] }, + { + "name": "CIRCLE", + "arguments": [ + { + "name": "lat", + "type": "double" + }, + { + "name": "lon", + "type": "double" + }, + { + "name": "meters", + "type": "double" + } + ] + }, { "name": "TILE", "arguments":[ diff --git a/pkg/core/commands_gen.go b/pkg/core/commands_gen.go index 971c761f..d0b1f6c7 100644 --- a/pkg/core/commands_gen.go +++ b/pkg/core/commands_gen.go @@ -840,7 +840,7 @@ var commandsJSON = `{ "group": "search" }, "WITHIN": { - "summary": "Searches for ids that are nearby a point", + "summary": "Searches for ids that completely within the area", "complexity": "O(log(N)) where N is the number of ids in the area", "arguments":[ { @@ -1002,6 +1002,23 @@ var commandsJSON = `{ } ] }, + { + "name": "CIRCLE", + "arguments": [ + { + "name": "lat", + "type": "double" + }, + { + "name": "lon", + "type": "double" + }, + { + "name": "meters", + "type": "double" + } + ] + }, { "name": "TILE", "arguments":[ @@ -1044,7 +1061,7 @@ var commandsJSON = `{ "group": "search" }, "INTERSECTS": { - "summary": "Searches for ids that are nearby a point", + "summary": "Searches for ids that intersect an area", "complexity": "O(log(N)) where N is the number of ids in the area", "arguments":[ { @@ -1206,6 +1223,23 @@ var commandsJSON = `{ } ] }, + { + "name": "CIRCLE", + "arguments": [ + { + "name": "lat", + "type": "double" + }, + { + "name": "lon", + "type": "double" + }, + { + "name": "meters", + "type": "double" + } + ] + }, { "name": "TILE", "arguments":[ diff --git a/pkg/geojson/circle.go b/pkg/geojson/circle.go new file mode 100644 index 00000000..6ebdc44e --- /dev/null +++ b/pkg/geojson/circle.go @@ -0,0 +1,34 @@ +package geojson + +import ( + "github.com/tidwall/tile38/pkg/geojson/geo" +) + +func SegmentIntersectsCircle(start, end, center Position, meters float64) bool { + // These are faster checks. If they succeed there's no need do complicate things. + if center.DistanceTo(start) <= meters { + return true + } + if center.DistanceTo(end) <= meters { + return true + } + + // Distance between start and end + l := geo.DistanceTo(start.Y, start.X, end.Y, end.X) + + // Unit direction vector + dx := (end.X - start.X) / l + dy := (end.Y - start.Y) / l + + // Point of the line closest to the center + t := dx * (center.X - start.X) + dy * (center.Y - start.Y) + px := t * dx + start.X + py := t * dy + start.Y + if px < start.X || px > end.X || py < start.Y || py > end.Y { + // closest point is outside the segment + return false + } + + // Distance from the closest point to the center + return geo.DistanceTo(center.Y, center.X, py, px) <= meters +} diff --git a/pkg/geojson/feature.go b/pkg/geojson/feature.go index 32b6908e..5120f2a1 100644 --- a/pkg/geojson/feature.go +++ b/pkg/geojson/feature.go @@ -199,6 +199,11 @@ func (g Feature) Within(o Object) bool { ) } +// WithinCircle detects if the object is fully contained inside a circle. +func (g Feature) WithinCircle(center Position, meters float64) bool { + return g.Geometry.WithinCircle(center, meters) +} + // Intersects detects if the object intersects another object. func (g Feature) Intersects(o Object) bool { return intersectsObjectShared(g, o, @@ -208,6 +213,11 @@ func (g Feature) Intersects(o Object) bool { ) } +// IntersectsCircle detects if the object intersects a circle. +func (g Feature) IntersectsCircle(center Position, meters float64) bool { + return g.Geometry.IntersectsCircle(center, meters) +} + // Nearby detects if the object is nearby a position. func (g Feature) Nearby(center Position, meters float64) bool { return nearbyObjectShared(g, center.X, center.Y, meters) diff --git a/pkg/geojson/featurecollection.go b/pkg/geojson/featurecollection.go index e41e0c51..8e5bd4da 100644 --- a/pkg/geojson/featurecollection.go +++ b/pkg/geojson/featurecollection.go @@ -193,6 +193,19 @@ func (g FeatureCollection) Within(o Object) bool { ) } +// WithinCircle detects if the object is fully contained inside a circle. +func (g FeatureCollection) WithinCircle(center Position, meters float64) bool { + if len(g.Features) == 0 { + return false + } + for _, feature := range g.Features { + if !feature.WithinCircle(center, meters) { + return false + } + } + return true +} + // Intersects detects if the object intersects another object. func (g FeatureCollection) Intersects(o Object) bool { return intersectsObjectShared(g, o, @@ -211,6 +224,16 @@ func (g FeatureCollection) Intersects(o Object) bool { ) } +// IntersectsCircle detects if the object intersects a circle. +func (g FeatureCollection) IntersectsCircle(center Position, meters float64) bool { + for _, feature := range g.Features { + if feature.IntersectsCircle(center, meters) { + return true + } + } + return false +} + // Nearby detects if the object is nearby a position. func (g FeatureCollection) Nearby(center Position, meters float64) bool { return nearbyObjectShared(g, center.X, center.Y, meters) diff --git a/pkg/geojson/geometrycollection.go b/pkg/geojson/geometrycollection.go index e73f2250..516dc94b 100644 --- a/pkg/geojson/geometrycollection.go +++ b/pkg/geojson/geometrycollection.go @@ -192,6 +192,19 @@ func (g GeometryCollection) Within(o Object) bool { ) } +// WithinCircle detects if the object is fully contained inside a circle. +func (g GeometryCollection) WithinCircle(center Position, meters float64) bool { + if len(g.Geometries) == 0 { + return false + } + for _, geometry := range g.Geometries { + if !geometry.WithinCircle(center, meters) { + return false + } + } + return true +} + // Intersects detects if the object intersects another object. func (g GeometryCollection) Intersects(o Object) bool { return intersectsObjectShared(g, o, @@ -209,6 +222,16 @@ func (g GeometryCollection) Intersects(o Object) bool { ) } +// IntersectsCircle detects if the object intersects a circle. +func (g GeometryCollection) IntersectsCircle(center Position, meters float64) bool { + for _, geometry := range g.Geometries { + if geometry.IntersectsCircle(center, meters) { + return true + } + } + return false +} + // Nearby detects if the object is nearby a position. func (g GeometryCollection) Nearby(center Position, meters float64) bool { return nearbyObjectShared(g, center.X, center.Y, meters) diff --git a/pkg/geojson/linestring.go b/pkg/geojson/linestring.go index e195272e..e7a68fda 100644 --- a/pkg/geojson/linestring.go +++ b/pkg/geojson/linestring.go @@ -105,6 +105,19 @@ func (g LineString) Within(o Object) bool { ) } +// WithinCircle detects if the object is fully contained inside a circle. +func (g LineString) WithinCircle(center Position, meters float64) bool { + if len(g.Coordinates) == 0 { + return false + } + for _, position := range g.Coordinates { + if center.DistanceTo(position) >= meters { + return false + } + } + return true +} + // Intersects detects if the object intersects another object. func (g LineString) Intersects(o Object) bool { return intersectsObjectShared(g, o, @@ -114,6 +127,16 @@ func (g LineString) Intersects(o Object) bool { ) } +// IntersectsCircle detects if the object intersects a circle. +func (g LineString) IntersectsCircle(center Position, meters float64) bool { + for i := 0; i < len(g.Coordinates) - 1 ; i++ { + if SegmentIntersectsCircle(g.Coordinates[i], g.Coordinates[i + 1], center, meters) { + return true + } + } + return false +} + // Nearby detects if the object is nearby a position. func (g LineString) Nearby(center Position, meters float64) bool { return nearbyObjectShared(g, center.X, center.Y, meters) diff --git a/pkg/geojson/multilinestring.go b/pkg/geojson/multilinestring.go index 37c4bb04..a6dd24ee 100644 --- a/pkg/geojson/multilinestring.go +++ b/pkg/geojson/multilinestring.go @@ -148,6 +148,24 @@ func (g MultiLineString) Within(o Object) bool { ) } +// WithinCircle detects if the object is fully contained inside a circle. +func (g MultiLineString) WithinCircle(center Position, meters float64) bool { + if len(g.Coordinates) == 0 { + return false + } + for _, ls := range g.Coordinates { + if len(ls) == 0 { + return false + } + for _, position := range ls { + if center.DistanceTo(position) >= meters { + return false + } + } + } + return true +} + // Intersects detects if the object intersects another object. func (g MultiLineString) Intersects(o Object) bool { return intersectsObjectShared(g, o, @@ -165,6 +183,18 @@ func (g MultiLineString) Intersects(o Object) bool { ) } +// IntersectsCircle detects if the object intersects a circle. +func (g MultiLineString) IntersectsCircle(center Position, meters float64) bool { + for _, ls := range g.Coordinates { + for i := 0; i < len(ls) - 1 ; i++ { + if SegmentIntersectsCircle(ls[i], ls[i + 1], center, meters) { + return true + } + } + } + return false +} + // Nearby detects if the object is nearby a position. func (g MultiLineString) Nearby(center Position, meters float64) bool { return nearbyObjectShared(g, center.X, center.Y, meters) diff --git a/pkg/geojson/multipoint.go b/pkg/geojson/multipoint.go index bc410ff8..5bdf564a 100644 --- a/pkg/geojson/multipoint.go +++ b/pkg/geojson/multipoint.go @@ -123,6 +123,19 @@ func (g MultiPoint) Within(o Object) bool { ) } +// WithinCircle detects if the object is fully contained inside a circle. +func (g MultiPoint) WithinCircle(center Position, meters float64) bool { + if len(g.Coordinates) == 0 { + return false + } + for _, position := range g.Coordinates { + if center.DistanceTo(position) >= meters { + return false + } + } + return true +} + // Intersects detects if the object intersects another object. func (g MultiPoint) Intersects(o Object) bool { return intersectsObjectShared(g, o, @@ -140,6 +153,16 @@ func (g MultiPoint) Intersects(o Object) bool { ) } +// IntersectsCircle detects if the object intersects a circle. +func (g MultiPoint) IntersectsCircle(center Position, meters float64) bool { + for _, position := range g.Coordinates { + if center.DistanceTo(position) <= meters { + return true + } + } + return false +} + // Nearby detects if the object is nearby a position. func (g MultiPoint) Nearby(center Position, meters float64) bool { return nearbyObjectShared(g, center.X, center.Y, meters) diff --git a/pkg/geojson/multipolygon.go b/pkg/geojson/multipolygon.go index b1d6ed81..8d9d1762 100644 --- a/pkg/geojson/multipolygon.go +++ b/pkg/geojson/multipolygon.go @@ -161,6 +161,19 @@ func (g MultiPolygon) Within(o Object) bool { ) } +// WithinCircle detects if the object is fully contained inside a circle. +func (g MultiPolygon) WithinCircle(center Position, meters float64) bool { + if len(g.polygons) == 0 { + return false + } + for _, polygon := range g.polygons { + if !polygon.WithinCircle(center, meters) { + return false + } + } + return true +} + // Intersects detects if the object intersects another object. func (g MultiPolygon) Intersects(o Object) bool { return intersectsObjectShared(g, o, @@ -176,6 +189,16 @@ func (g MultiPolygon) Intersects(o Object) bool { ) } +// IntersectsCircle detects if the object intersects a circle. +func (g MultiPolygon) IntersectsCircle(center Position, meters float64) bool { + for _, polygon := range g.polygons { + if polygon.IntersectsCircle(center, meters) { + return true + } + } + return false +} + // Nearby detects if the object is nearby a position. func (g MultiPolygon) Nearby(center Position, meters float64) bool { return nearbyObjectShared(g, center.X, center.Y, meters) diff --git a/pkg/geojson/object.go b/pkg/geojson/object.go index c149b419..ca1f6570 100644 --- a/pkg/geojson/object.go +++ b/pkg/geojson/object.go @@ -68,6 +68,10 @@ type Object interface { Within(o Object) bool // Intersects detects if the object intersects another object. Intersects(o Object) bool + // WithinCircle detects if the object is fully contained inside a circle. + WithinCircle(center Position, meters float64) bool + // IntersectsCircle detects if the object intersects a circle. + IntersectsCircle(center Position, meters float64) bool // Nearby detects if the object is nearby a position. Nearby(center Position, meters float64) bool // CalculatedBBox is exterior bbox containing the object. diff --git a/pkg/geojson/point.go b/pkg/geojson/point.go index e75c8ecb..72c3e2df 100644 --- a/pkg/geojson/point.go +++ b/pkg/geojson/point.go @@ -112,6 +112,11 @@ func (g Point) Within(o Object) bool { ) } +// WithinCircle detects if the object is fully contained inside a circle. +func (g Point) WithinCircle(center Position, meters float64) bool { + return geo.DistanceTo(g.Coordinates.Y, g.Coordinates.X, center.Y, center.X) < meters +} + // Intersects detects if the object intersects another object. func (g Point) Intersects(o Object) bool { return intersectsObjectShared(g, o, @@ -121,6 +126,11 @@ func (g Point) Intersects(o Object) bool { ) } +// IntersectsCircle detects if the object intersects a circle. +func (g Point) IntersectsCircle(center Position, meters float64) bool { + return geo.DistanceTo(g.Coordinates.Y, g.Coordinates.X, center.Y, center.X) <= meters +} + // Nearby detects if the object is nearby a position. func (g Point) Nearby(center Position, meters float64) bool { return geo.DistanceTo(g.Coordinates.Y, g.Coordinates.X, center.Y, center.X) <= meters diff --git a/pkg/geojson/polygon.go b/pkg/geojson/polygon.go index 504b91cc..7344bdb6 100644 --- a/pkg/geojson/polygon.go +++ b/pkg/geojson/polygon.go @@ -155,6 +155,19 @@ func (g Polygon) Within(o Object) bool { ) } +// WithinCircle detects if the object is fully contained inside a circle. +func (g Polygon) WithinCircle(center Position, meters float64) bool { + if len(g.Coordinates) == 0 { + return false + } + for _, position := range g.Coordinates[0] { + if center.DistanceTo(position) >= meters { + return false + } + } + return true +} + // Intersects detects if the object intersects another object. func (g Polygon) Intersects(o Object) bool { return intersectsObjectShared(g, o, @@ -167,6 +180,21 @@ func (g Polygon) Intersects(o Object) bool { ) } +// IntersectsCircle detects if the object intersects a circle. +func (g Polygon) IntersectsCircle(center Position, meters float64) bool { + if g.Intersects(New2DPoint(center.X, center.Y)) { + return true + } + for _, polygon := range g.Coordinates { + for i := 0; i < len(polygon) - 1 ; i++ { + if SegmentIntersectsCircle(polygon[i], polygon[i + 1], center, meters) { + return true + } + } + } + return false +} + // Nearby detects if the object is nearby a position. func (g Polygon) Nearby(center Position, meters float64) bool { return nearbyObjectShared(g, center.X, center.Y, meters) diff --git a/pkg/geojson/simplepoint.go b/pkg/geojson/simplepoint.go index fdb60d41..358c484a 100644 --- a/pkg/geojson/simplepoint.go +++ b/pkg/geojson/simplepoint.go @@ -94,6 +94,11 @@ func (g SimplePoint) Within(o Object) bool { ) } +// WithinCircle detects if the object is fully contained inside a circle. +func (g SimplePoint) WithinCircle(center Position, meters float64) bool { + return geo.DistanceTo(center.Y, center.X, g.Y, g.X) < meters +} + // Intersects detects if the object intersects another object. func (g SimplePoint) Intersects(o Object) bool { return intersectsObjectShared(g, o, @@ -103,6 +108,11 @@ func (g SimplePoint) Intersects(o Object) bool { ) } +// IntersectsCircle detects if the object intersects a circle. +func (g SimplePoint) IntersectsCircle(center Position, meters float64) bool { + return geo.DistanceTo(center.Y, center.X, g.Y, g.X) <= meters +} + // Nearby detects if the object is nearby a position. func (g SimplePoint) Nearby(center Position, meters float64) bool { return geo.DistanceTo(center.Y, center.X, g.Y, g.X) <= meters diff --git a/pkg/geojson/string.go b/pkg/geojson/string.go index 24372297..cf85738d 100644 --- a/pkg/geojson/string.go +++ b/pkg/geojson/string.go @@ -25,10 +25,18 @@ func (s String) Within(o Object) bool { return false } +// WithinCircle detects if the object is fully contained inside a circle. +func (s String) WithinCircle(center Position, meters float64) bool { + return false +} + // Intersects detects if the object intersects another object. func (s String) Intersects(o Object) bool { return false } +func (s String) IntersectsCircle(center Position, meters float64) bool { + return false +} // Nearby detects if the object is nearby a position. func (s String) Nearby(center Position, meters float64) bool {