fix perf regression

This commit is contained in:
Josh Baker 2018-03-09 19:05:31 -07:00
parent 9428b84484
commit 0ce2dab945
13 changed files with 385 additions and 425 deletions

View File

@ -1,294 +0,0 @@
package geojson
import (
"github.com/tidwall/tile38/geojson/poly"
)
// withinObjectShared returns true if g is within o
func withinObjectShared(g, o Object) bool {
bbp := o.bboxPtr()
if bbp != nil {
if !g.WithinBBox(*bbp) {
return false
}
if o.IsBBoxDefined() {
return true
}
}
switch o := o.(type) {
default:
return false
case SimplePoint:
return g.WithinBBox(o.CalculatedBBox())
case Point:
return g.WithinBBox(o.CalculatedBBox())
case MultiPoint:
for i := range o.Coordinates {
if g.Within(o.getPoint(i)) {
return true
}
}
return false
case LineString:
if len(o.Coordinates) == 0 {
return false
}
switch g := g.(type) {
default:
return false
case SimplePoint:
return poly.Point(Position{X: g.X, Y: g.Y, Z: 0}).IntersectsLineString(polyPositions(o.Coordinates))
case Point:
return poly.Point(g.Coordinates).IntersectsLineString(polyPositions(o.Coordinates))
case MultiPoint:
if len(o.Coordinates) == 0 {
return false
}
for _, p := range o.Coordinates {
if !poly.Point(p).IntersectsLineString(polyPositions(o.Coordinates)) {
return false
}
}
return true
}
case MultiLineString:
for i := range o.Coordinates {
if g.Within(o.getLineString(i)) {
return true
}
}
return false
case MultiPolygon:
for i := range o.Coordinates {
if g.Within(o.getPolygon(i)) {
return true
}
}
return false
case Feature:
return g.Within(o.Geometry)
case FeatureCollection:
for _, o := range o.Features {
if g.Within(o) {
return true
}
}
return false
case GeometryCollection:
for _, o := range o.Geometries {
if g.Within(o) {
return true
}
}
return false
case Polygon:
if len(o.Coordinates) == 0 {
return false
}
exterior, holes := polyExteriorHoles(o.Coordinates)
switch g := g.(type) {
default:
return false
case SimplePoint:
return poly.Point(Position{X: g.X, Y: g.Y, Z: 0}).Inside(exterior, holes)
case Point:
return poly.Point(g.Coordinates).Inside(exterior, holes)
case MultiPoint:
if len(g.Coordinates) == 0 {
return false
}
for i := range g.Coordinates {
if !g.getPoint(i).Within(o) {
return false
}
}
return true
case LineString:
return polyPositions(g.Coordinates).Inside(exterior, holes)
case MultiLineString:
if len(g.Coordinates) == 0 {
return false
}
for i := range g.Coordinates {
if !g.getLineString(i).Within(o) {
return false
}
}
return true
case Polygon:
if len(g.Coordinates) == 0 {
return false
}
return polyPositions(g.Coordinates[0]).Inside(exterior, holes)
case MultiPolygon:
if len(g.Coordinates) == 0 {
return false
}
for i := range g.Coordinates {
if !g.getPolygon(i).Within(o) {
return false
}
}
return true
case GeometryCollection:
if len(g.Geometries) == 0 {
return false
}
for _, g := range g.Geometries {
if !g.Within(o) {
return false
}
}
return true
case Feature:
return g.Geometry.Within(o)
case FeatureCollection:
if len(g.Features) == 0 {
return false
}
for _, g := range g.Features {
if !g.Within(o) {
return false
}
}
return true
}
}
}
// intersectsObjectShared detects if g intersects with o
func intersectsObjectShared(g, o Object) bool {
bbp := o.bboxPtr()
if bbp != nil {
if !g.IntersectsBBox(*bbp) {
return false
}
if o.IsBBoxDefined() {
return true
}
}
switch o := o.(type) {
default:
return false
case SimplePoint:
return g.IntersectsBBox(o.CalculatedBBox())
case Point:
return g.IntersectsBBox(o.CalculatedBBox())
case MultiPoint:
for i := range o.Coordinates {
if o.getPoint(i).Intersects(g) {
return true
}
}
return false
case LineString:
if g, ok := g.(LineString); ok {
a := polyPositions(g.Coordinates)
b := polyPositions(o.Coordinates)
return a.LineStringIntersectsLineString(b)
}
return o.Intersects(g)
case MultiLineString:
for i := range o.Coordinates {
if g.Intersects(o.getLineString(i)) {
return true
}
}
return false
case MultiPolygon:
for i := range o.Coordinates {
if g.Intersects(o.getPolygon(i)) {
return true
}
}
return false
case Feature:
return g.Intersects(o.Geometry)
case FeatureCollection:
for _, f := range o.Features {
if g.Intersects(f) {
return true
}
}
return false
case GeometryCollection:
for _, f := range o.Geometries {
if g.Intersects(f) {
return true
}
}
return false
case Polygon:
if len(o.Coordinates) == 0 {
return false
}
exterior, holes := polyExteriorHoles(o.Coordinates)
switch g := g.(type) {
default:
return false
case SimplePoint:
return poly.Point(Position{X: g.X, Y: g.Y, Z: 0}).Intersects(exterior, holes)
case Point:
return poly.Point(g.Coordinates).Intersects(exterior, holes)
case MultiPoint:
for i := range g.Coordinates {
if g.getPoint(i).Intersects(o) {
return true
}
}
return false
case LineString:
return polyPositions(g.Coordinates).LineStringIntersects(exterior, holes)
case MultiLineString:
for i := range g.Coordinates {
if g.getLineString(i).Intersects(o) {
return true
}
}
return false
case Polygon:
if len(g.Coordinates) == 0 {
return false
}
return polyPositions(g.Coordinates[0]).Intersects(exterior, holes)
case MultiPolygon:
for i := range g.Coordinates {
if g.getPolygon(i).Intersects(o) {
return true
}
}
return false
case GeometryCollection:
for _, g := range g.Geometries {
if g.Intersects(o) {
return true
}
}
return false
case Feature:
return g.Geometry.Intersects(o)
case FeatureCollection:
for _, g := range g.Features {
if g.Intersects(o) {
return true
}
}
return false
}
}
}
// The object's calculated bounding box must intersect the radius of the circle to pass.
func nearbyObjectShared(g Object, x, y float64, meters float64) bool {
if !g.hasPositions() {
return false
}
center := Position{X: x, Y: y, Z: 0}
bbox := g.CalculatedBBox()
if bbox.Min.X == bbox.Max.X && bbox.Min.Y == bbox.Max.Y {
// just a point, return is point is inside of the circle
return center.DistanceTo(bbox.Min) <= meters
}
circlePoly := CirclePolygon(x, y, meters, 12)
return g.Intersects(circlePoly)
}

View File

@ -192,12 +192,20 @@ func (g Feature) IntersectsBBox(bbox BBox) bool {
// Within detects if the object is fully contained inside another object. // Within detects if the object is fully contained inside another object.
func (g Feature) Within(o Object) bool { func (g Feature) Within(o Object) bool {
return withinObjectShared(g, o) return withinObjectShared(g, o,
func(v Polygon) bool {
return g.Geometry.Within(o)
},
)
} }
// Intersects detects if the object intersects another object. // Intersects detects if the object intersects another object.
func (g Feature) Intersects(o Object) bool { func (g Feature) Intersects(o Object) bool {
return intersectsObjectShared(g, o) return intersectsObjectShared(g, o,
func(v Polygon) bool {
return g.Geometry.Intersects(o)
},
)
} }
// Nearby detects if the object is nearby a position. // Nearby detects if the object is nearby a position.

View File

@ -178,12 +178,36 @@ func (g FeatureCollection) IntersectsBBox(bbox BBox) bool {
// Within detects if the object is fully contained inside another object. // Within detects if the object is fully contained inside another object.
func (g FeatureCollection) Within(o Object) bool { func (g FeatureCollection) Within(o Object) bool {
return withinObjectShared(g, o) return withinObjectShared(g, o,
func(v Polygon) bool {
if len(g.Features) == 0 {
return false
}
for _, f := range g.Features {
if !f.Within(o) {
return false
}
}
return true
},
)
} }
// Intersects detects if the object intersects another object. // Intersects detects if the object intersects another object.
func (g FeatureCollection) Intersects(o Object) bool { func (g FeatureCollection) Intersects(o Object) bool {
return intersectsObjectShared(g, o) return intersectsObjectShared(g, o,
func(v Polygon) bool {
if len(g.Features) == 0 {
return false
}
for _, f := range g.Features {
if f.Intersects(o) {
return true
}
}
return false
},
)
} }
// Nearby detects if the object is nearby a position. // Nearby detects if the object is nearby a position.

View File

@ -177,12 +177,36 @@ func (g GeometryCollection) IntersectsBBox(bbox BBox) bool {
// Within detects if the object is fully contained inside another object. // Within detects if the object is fully contained inside another object.
func (g GeometryCollection) Within(o Object) bool { func (g GeometryCollection) Within(o Object) bool {
return withinObjectShared(g, o) return withinObjectShared(g, o,
func(v Polygon) bool {
if len(g.Geometries) == 0 {
return false
}
for _, g := range g.Geometries {
if !g.Within(o) {
return false
}
}
return true
},
)
} }
// Intersects detects if the object intersects another object. // Intersects detects if the object intersects another object.
func (g GeometryCollection) Intersects(o Object) bool { func (g GeometryCollection) Intersects(o Object) bool {
return intersectsObjectShared(g, o) return intersectsObjectShared(g, o,
func(v Polygon) bool {
if len(g.Geometries) == 0 {
return false
}
for _, g := range g.Geometries {
if g.Intersects(o) {
return true
}
}
return false
},
)
} }
// Nearby detects if the object is nearby a position. // Nearby detects if the object is nearby a position.

View File

@ -98,12 +98,20 @@ func (g LineString) IntersectsBBox(bbox BBox) bool {
// Within detects if the object is fully contained inside another object. // Within detects if the object is fully contained inside another object.
func (g LineString) Within(o Object) bool { func (g LineString) Within(o Object) bool {
return withinObjectShared(g, o) return withinObjectShared(g, o,
func(v Polygon) bool {
return polyPositions(g.Coordinates).Inside(polyExteriorHoles(v.Coordinates))
},
)
} }
// Intersects detects if the object intersects another object. // Intersects detects if the object intersects another object.
func (g LineString) Intersects(o Object) bool { func (g LineString) Intersects(o Object) bool {
return intersectsObjectShared(g, o) return intersectsObjectShared(g, o,
func(v Polygon) bool {
return polyPositions(g.Coordinates).LineStringIntersects(polyExteriorHoles(v.Coordinates))
},
)
} }
// Nearby detects if the object is nearby a position. // Nearby detects if the object is nearby a position.

View File

@ -2,6 +2,7 @@ package geojson
import ( import (
"github.com/tidwall/tile38/geojson/geohash" "github.com/tidwall/tile38/geojson/geohash"
"github.com/tidwall/tile38/geojson/poly"
) )
// MultiLineString is a geojson object with the type "MultiLineString" // MultiLineString is a geojson object with the type "MultiLineString"
@ -9,50 +10,36 @@ type MultiLineString struct {
Coordinates [][]Position Coordinates [][]Position
BBox *BBox BBox *BBox
bboxDefined bool bboxDefined bool
linestrings []LineString
} }
func fillMultiLineString(coordinates [][]Position, bbox *BBox, err error) (MultiLineString, error) { func fillMultiLineString(coordinates [][]Position, bbox *BBox, err error) (MultiLineString, error) {
linestrings := make([]LineString, len(coordinates))
if err == nil { if err == nil {
for i, ps := range coordinates { for _, coordinates := range coordinates {
linestrings[i], err = fillLineString(ps, nil, nil) if len(coordinates) < 2 {
if err != nil { err = errLineStringInvalidCoordinates
break break
} }
} }
} }
bboxDefined := bbox != nil bboxDefined := bbox != nil
if !bboxDefined { if !bboxDefined {
cbbox := mlCalculatedBBox(linestrings, nil) cbbox := level3CalculatedBBox(coordinates, nil, false)
bbox = &cbbox bbox = &cbbox
} }
return MultiLineString{ return MultiLineString{
Coordinates: coordinates, Coordinates: coordinates,
BBox: bbox, BBox: bbox,
bboxDefined: bboxDefined, bboxDefined: bboxDefined,
linestrings: linestrings,
}, err }, err
} }
func mlCalculatedBBox(linestrings []LineString, bbox *BBox) BBox { func (g MultiLineString) getLineString(index int) LineString {
if bbox != nil { return LineString{Coordinates: g.Coordinates[index]}
return *bbox
}
var cbbox BBox
for i, g := range linestrings {
if i == 0 {
cbbox = g.CalculatedBBox()
} else {
cbbox = cbbox.union(g.CalculatedBBox())
}
}
return cbbox
} }
// CalculatedBBox is exterior bbox containing the object. // CalculatedBBox is exterior bbox containing the object.
func (g MultiLineString) CalculatedBBox() BBox { func (g MultiLineString) CalculatedBBox() BBox {
return mlCalculatedBBox(g.linestrings, g.BBox) return level3CalculatedBBox(g.Coordinates, g.BBox, false)
} }
// CalculatedPoint is a point representation of the object. // CalculatedPoint is a point representation of the object.
@ -110,13 +97,6 @@ func (g MultiLineString) hasPositions() bool {
return false return false
} }
func (g MultiLineString) getLineString(index int) LineString {
if index < len(g.linestrings) {
return g.linestrings[index]
}
return LineString{Coordinates: g.Coordinates[index]}
}
// WithinBBox detects if the object is fully contained inside a bbox. // WithinBBox detects if the object is fully contained inside a bbox.
func (g MultiLineString) WithinBBox(bbox BBox) bool { func (g MultiLineString) WithinBBox(bbox BBox) bool {
if g.bboxDefined { if g.bboxDefined {
@ -125,10 +105,15 @@ func (g MultiLineString) WithinBBox(bbox BBox) bool {
if len(g.Coordinates) == 0 { if len(g.Coordinates) == 0 {
return false return false
} }
for i := range g.Coordinates { for _, ls := range g.Coordinates {
if !g.getLineString(i).WithinBBox(bbox) { if len(ls) == 0 {
return false return false
} }
for _, p := range ls {
if !poly.Point(p).InsideRect(rectBBox(bbox)) {
return false
}
}
} }
return true return true
} }
@ -138,8 +123,8 @@ func (g MultiLineString) IntersectsBBox(bbox BBox) bool {
if g.bboxDefined { if g.bboxDefined {
return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox)) return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox))
} }
for i := range g.Coordinates { for _, ls := range g.Coordinates {
if g.getLineString(i).IntersectsBBox(bbox) { if polyPositions(ls).IntersectsRect(rectBBox(bbox)) {
return true return true
} }
} }
@ -148,12 +133,36 @@ func (g MultiLineString) IntersectsBBox(bbox BBox) bool {
// Within detects if the object is fully contained inside another object. // Within detects if the object is fully contained inside another object.
func (g MultiLineString) Within(o Object) bool { func (g MultiLineString) Within(o Object) bool {
return withinObjectShared(g, o) return withinObjectShared(g, o,
func(v Polygon) bool {
if len(g.Coordinates) == 0 {
return false
}
for _, ls := range g.Coordinates {
if !polyPositions(ls).Inside(polyExteriorHoles(v.Coordinates)) {
return false
}
}
return true
},
)
} }
// Intersects detects if the object intersects another object. // Intersects detects if the object intersects another object.
func (g MultiLineString) Intersects(o Object) bool { func (g MultiLineString) Intersects(o Object) bool {
return intersectsObjectShared(g, o) return intersectsObjectShared(g, o,
func(v Polygon) bool {
if len(g.Coordinates) == 0 {
return false
}
for _, ls := range g.Coordinates {
if polyPositions(ls).Intersects(polyExteriorHoles(v.Coordinates)) {
return true
}
}
return false
},
)
} }
// Nearby detects if the object is nearby a position. // Nearby detects if the object is nearby a position.

View File

@ -25,10 +25,6 @@ func fillMultiPoint(coordinates []Position, bbox *BBox, err error) (MultiPoint,
}, err }, err
} }
func (g MultiPoint) getPoint(index int) Point {
return Point{Coordinates: g.Coordinates[index]}
}
// CalculatedBBox is exterior bbox containing the object. // CalculatedBBox is exterior bbox containing the object.
func (g MultiPoint) CalculatedBBox() BBox { func (g MultiPoint) CalculatedBBox() BBox {
return level2CalculatedBBox(g.Coordinates, g.BBox) return level2CalculatedBBox(g.Coordinates, g.BBox)
@ -112,12 +108,36 @@ func (g MultiPoint) IntersectsBBox(bbox BBox) bool {
// Within detects if the object is fully contained inside another object. // Within detects if the object is fully contained inside another object.
func (g MultiPoint) Within(o Object) bool { func (g MultiPoint) Within(o Object) bool {
return withinObjectShared(g, o) return withinObjectShared(g, o,
func(v Polygon) bool {
if len(g.Coordinates) == 0 {
return false
}
for _, p := range g.Coordinates {
if !poly.Point(p).Inside(polyExteriorHoles(v.Coordinates)) {
return false
}
}
return true
},
)
} }
// Intersects detects if the object intersects another object. // Intersects detects if the object intersects another object.
func (g MultiPoint) Intersects(o Object) bool { func (g MultiPoint) Intersects(o Object) bool {
return intersectsObjectShared(g, o) return intersectsObjectShared(g, o,
func(v Polygon) bool {
if len(g.Coordinates) == 0 {
return false
}
for _, p := range g.Coordinates {
if poly.Point(p).Intersects(polyExteriorHoles(v.Coordinates)) {
return true
}
}
return true
},
)
} }
// Nearby detects if the object is nearby a position. // Nearby detects if the object is nearby a position.

View File

@ -22,7 +22,7 @@ func fillMultiPolygon(coordinates [][][]Position, bbox *BBox, err error) (MultiP
} }
bboxDefined := bbox != nil bboxDefined := bbox != nil
if !bboxDefined { if !bboxDefined {
cbbox := mpCalculatedBBox(polygons, nil) cbbox := calculatedBBox(polygons, nil)
bbox = &cbbox bbox = &cbbox
} }
return MultiPolygon{ return MultiPolygon{
@ -33,16 +33,16 @@ func fillMultiPolygon(coordinates [][][]Position, bbox *BBox, err error) (MultiP
}, err }, err
} }
func mpCalculatedBBox(polygons []Polygon, bbox *BBox) BBox { func calculatedBBox(polygons []Polygon, bbox *BBox) BBox {
if bbox != nil { if bbox != nil {
return *bbox return *bbox
} }
var cbbox BBox var cbbox BBox
for i, g := range polygons { for i, p := range polygons {
if i == 0 { if i == 0 {
cbbox = g.CalculatedBBox() cbbox = p.CalculatedBBox()
} else { } else {
cbbox = cbbox.union(g.CalculatedBBox()) cbbox = cbbox.union(p.CalculatedBBox())
} }
} }
return cbbox return cbbox
@ -50,7 +50,7 @@ func mpCalculatedBBox(polygons []Polygon, bbox *BBox) BBox {
// CalculatedBBox is exterior bbox containing the object. // CalculatedBBox is exterior bbox containing the object.
func (g MultiPolygon) CalculatedBBox() BBox { func (g MultiPolygon) CalculatedBBox() BBox {
return mpCalculatedBBox(g.polygons, g.BBox) return calculatedBBox(g.polygons, g.BBox)
} }
// CalculatedPoint is a point representation of the object. // CalculatedPoint is a point representation of the object.
@ -148,12 +148,32 @@ func (g MultiPolygon) IntersectsBBox(bbox BBox) bool {
// Within detects if the object is fully contained inside another object. // Within detects if the object is fully contained inside another object.
func (g MultiPolygon) Within(o Object) bool { func (g MultiPolygon) Within(o Object) bool {
return withinObjectShared(g, o) return withinObjectShared(g, o,
func(v Polygon) bool {
if len(g.Coordinates) == 0 {
return false
}
if !v.Within(o) {
return false
}
return true
},
)
} }
// Intersects detects if the object intersects another object. // Intersects detects if the object intersects another object.
func (g MultiPolygon) Intersects(o Object) bool { func (g MultiPolygon) Intersects(o Object) bool {
return intersectsObjectShared(g, o) return intersectsObjectShared(g, o,
func(v Polygon) bool {
if len(g.Coordinates) == 0 {
return false
}
if v.Intersects(o) {
return true
}
return false
},
)
} }
// Nearby detects if the object is nearby a position. // Nearby detects if the object is nearby a position.

View File

@ -182,6 +182,171 @@ func objectMap(json string, from int) (Object, error) {
return o, err return o, err
} }
func withinObjectShared(g Object, o Object, pin func(v Polygon) bool) bool {
bbp := o.bboxPtr()
if bbp != nil {
if !g.WithinBBox(*bbp) {
return false
}
if o.IsBBoxDefined() {
return true
}
}
switch v := o.(type) {
default:
return false
case Point:
return g.WithinBBox(v.CalculatedBBox())
case SimplePoint:
return g.WithinBBox(v.CalculatedBBox())
case MultiPoint:
for i := range v.Coordinates {
if g.Within(Point{Coordinates: v.Coordinates[i]}) {
return true
}
}
return false
case LineString:
if len(v.Coordinates) == 0 {
return false
}
switch g := g.(type) {
default:
return false
case SimplePoint:
return poly.Point(Position{X: g.X, Y: g.Y, Z: 0}).IntersectsLineString(polyPositions(v.Coordinates))
case Point:
return poly.Point(g.Coordinates).IntersectsLineString(polyPositions(v.Coordinates))
case MultiPoint:
if len(v.Coordinates) == 0 {
return false
}
for _, p := range v.Coordinates {
if !poly.Point(p).IntersectsLineString(polyPositions(v.Coordinates)) {
return false
}
}
return true
}
case MultiLineString:
for i := range v.Coordinates {
if g.Within(v.getLineString(i)) {
return true
}
}
return false
case Polygon:
if len(v.Coordinates) == 0 {
return false
}
return pin(v)
case MultiPolygon:
for i := range v.Coordinates {
if pin(v.getPolygon(i)) {
return true
}
}
return false
case Feature:
return g.Within(v.Geometry)
case FeatureCollection:
if len(v.Features) == 0 {
return false
}
for _, f := range v.Features {
if !g.Within(f) {
return false
}
}
return true
case GeometryCollection:
if len(v.Geometries) == 0 {
return false
}
for _, f := range v.Geometries {
if !g.Within(f) {
return false
}
}
return true
}
}
func intersectsObjectShared(g Object, o Object, pin func(v Polygon) bool) bool {
bbp := o.bboxPtr()
if bbp != nil {
if !g.IntersectsBBox(*bbp) {
return false
}
if o.IsBBoxDefined() {
return true
}
}
switch v := o.(type) {
default:
return false
case Point:
return g.IntersectsBBox(v.CalculatedBBox())
case SimplePoint:
return g.IntersectsBBox(v.CalculatedBBox())
case MultiPoint:
for i := range v.Coordinates {
if (Point{Coordinates: v.Coordinates[i]}).Intersects(g) {
return true
}
}
return false
case LineString:
if g, ok := g.(LineString); ok {
a := polyPositions(g.Coordinates)
b := polyPositions(v.Coordinates)
return a.LineStringIntersectsLineString(b)
}
return o.Intersects(g)
case MultiLineString:
for i := range v.Coordinates {
if g.Intersects(v.getLineString(i)) {
return true
}
}
return false
case Polygon:
if len(v.Coordinates) == 0 {
return false
}
return pin(v)
case MultiPolygon:
for _, coords := range v.Coordinates {
if pin(Polygon{Coordinates: coords}) {
return true
}
}
return false
case Feature:
return g.Intersects(v.Geometry)
case FeatureCollection:
if len(v.Features) == 0 {
return false
}
for _, f := range v.Features {
if g.Intersects(f) {
return true
}
}
return false
case GeometryCollection:
if len(v.Geometries) == 0 {
return false
}
for _, f := range v.Geometries {
if g.Intersects(f) {
return true
}
}
return false
}
}
// CirclePolygon returns a Polygon around the radius. // CirclePolygon returns a Polygon around the radius.
func CirclePolygon(x, y, meters float64, steps int) Polygon { func CirclePolygon(x, y, meters float64, steps int) Polygon {
if steps < 3 { if steps < 3 {
@ -202,6 +367,21 @@ func CirclePolygon(x, y, meters float64, steps int) Polygon {
return p return p
} }
// The object's calculated bounding box must intersect the radius of the circle to pass.
func nearbyObjectShared(g Object, x, y float64, meters float64) bool {
if !g.hasPositions() {
return false
}
center := Position{X: x, Y: y, Z: 0}
bbox := g.CalculatedBBox()
if bbox.Min.X == bbox.Max.X && bbox.Min.Y == bbox.Max.Y {
// just a point, return is point is inside of the circle
return center.DistanceTo(bbox.Min) <= meters
}
circlePoly := CirclePolygon(x, y, meters, 12)
return g.Intersects(circlePoly)
}
func jsonMarshalString(s string) []byte { func jsonMarshalString(s string) []byte {
for i := 0; i < len(s); i++ { for i := 0; i < len(s); i++ {
if s[i] < ' ' || s[i] == '\\' || s[i] == '"' || s[i] > 126 { if s[i] < ' ' || s[i] == '\\' || s[i] == '"' || s[i] > 126 {

View File

@ -42,72 +42,3 @@ func TestCirclePolygon(t *testing.T) {
t.Fatal("should intersect") t.Fatal("should intersect")
} }
} }
func TestIssue281(t *testing.T) {
p := testJSON(t, `{"type":"Polygon","coordinates":[[
[-74.008283,40.718249],
[-74.007339,40.713305],
[-73.999013,40.714866],
[-74.001760,40.720851],
[-74.008283,40.718249]
]]}`)
// intersects polygon
ls1 := testJSON(t, `{"type":"LineString","coordinates":[
[-74.003648,40.717533],
[-73.99575233459473,40.72046126415031],
[-73.99721145629883,40.72338850378556]
]}`)
// outside polygon
ls2 := testJSON(t, `{"type":"LineString","coordinates":[
[-74.007682,40.722998],
[-74.001932,40.728462],
[-74.001846,40.723583]
]}`)
// inside polygon
ls3 := testJSON(t, `{"type":"LineString","coordinates":[
[-74.006910,40.717598],
[-74.006137,40.715387],
[-74.001331,40.715907]
]}`)
if !ls1.Intersects(p) {
t.Fatalf("expected true")
}
if !p.Intersects(ls1) {
t.Fatalf("expected true")
}
if ls1.Within(p) {
t.Fatalf("expected false")
}
if p.Within(ls1) {
t.Fatalf("expected false")
}
if ls2.Intersects(p) {
t.Fatalf("expected false")
}
if p.Intersects(ls2) {
t.Fatalf("expected false")
}
if ls2.Within(p) {
t.Fatalf("expected false")
}
if p.Within(ls2) {
t.Fatalf("expected false")
}
if !ls3.Intersects(p) {
t.Fatalf("expected true")
}
if !p.Intersects(ls3) {
t.Fatalf("expected true")
}
if !ls3.Within(p) {
t.Fatalf("expected true")
}
if p.Within(ls3) {
t.Fatalf("expected false")
}
}

View File

@ -105,12 +105,20 @@ func (g Point) IntersectsBBox(bbox BBox) bool {
// Within detects if the object is fully contained inside another object. // Within detects if the object is fully contained inside another object.
func (g Point) Within(o Object) bool { func (g Point) Within(o Object) bool {
return withinObjectShared(g, o) return withinObjectShared(g, o,
func(v Polygon) bool {
return poly.Point(g.Coordinates).Inside(polyExteriorHoles(v.Coordinates))
},
)
} }
// Intersects detects if the object intersects another object. // Intersects detects if the object intersects another object.
func (g Point) Intersects(o Object) bool { func (g Point) Intersects(o Object) bool {
return intersectsObjectShared(g, o) return intersectsObjectShared(g, o,
func(v Polygon) bool {
return poly.Point(g.Coordinates).Intersects(polyExteriorHoles(v.Coordinates))
},
)
} }
// Nearby detects if the object is nearby a position. // Nearby detects if the object is nearby a position.

View File

@ -145,12 +145,26 @@ func (g Polygon) IntersectsBBox(bbox BBox) bool {
// Within detects if the object is fully contained inside another object. // Within detects if the object is fully contained inside another object.
func (g Polygon) Within(o Object) bool { func (g Polygon) Within(o Object) bool {
return withinObjectShared(g, o) return withinObjectShared(g, o,
func(v Polygon) bool {
if len(g.Coordinates) == 0 {
return false
}
return polyPositions(g.Coordinates[0]).Inside(polyExteriorHoles(v.Coordinates))
},
)
} }
// Intersects detects if the object intersects another object. // Intersects detects if the object intersects another object.
func (g Polygon) Intersects(o Object) bool { func (g Polygon) Intersects(o Object) bool {
return intersectsObjectShared(g, o) return intersectsObjectShared(g, o,
func(v Polygon) bool {
if len(g.Coordinates) == 0 {
return false
}
return polyPositions(g.Coordinates[0]).Intersects(polyExteriorHoles(v.Coordinates))
},
)
} }
// Nearby detects if the object is nearby a position. // Nearby detects if the object is nearby a position.

View File

@ -87,12 +87,20 @@ func (g SimplePoint) IntersectsBBox(bbox BBox) bool {
// Within detects if the object is fully contained inside another object. // Within detects if the object is fully contained inside another object.
func (g SimplePoint) Within(o Object) bool { func (g SimplePoint) Within(o Object) bool {
return withinObjectShared(g, o) return withinObjectShared(g, o,
func(v Polygon) bool {
return poly.Point(Position{X: g.X, Y: g.Y, Z: 0}).Inside(polyExteriorHoles(v.Coordinates))
},
)
} }
// Intersects detects if the object intersects another object. // Intersects detects if the object intersects another object.
func (g SimplePoint) Intersects(o Object) bool { func (g SimplePoint) Intersects(o Object) bool {
return intersectsObjectShared(g, o) return intersectsObjectShared(g, o,
func(v Polygon) bool {
return poly.Point(Position{X: g.X, Y: g.Y, Z: 0}).Intersects(polyExteriorHoles(v.Coordinates))
},
)
} }
// Nearby detects if the object is nearby a position. // Nearby detects if the object is nearby a position.