From 0ce2dab9459a06a6ea38da8d2d81542494c610b9 Mon Sep 17 00:00:00 2001 From: Josh Baker Date: Fri, 9 Mar 2018 19:05:31 -0700 Subject: [PATCH] fix perf regression --- geojson/detect.go | 294 ---------------------------------- geojson/feature.go | 12 +- geojson/featurecollection.go | 28 +++- geojson/geometrycollection.go | 28 +++- geojson/linestring.go | 12 +- geojson/multilinestring.go | 77 +++++---- geojson/multipoint.go | 32 +++- geojson/multipolygon.go | 36 ++++- geojson/object.go | 180 +++++++++++++++++++++ geojson/object_test.go | 69 -------- geojson/point.go | 12 +- geojson/polygon.go | 18 ++- geojson/simplepoint.go | 12 +- 13 files changed, 385 insertions(+), 425 deletions(-) delete mode 100644 geojson/detect.go diff --git a/geojson/detect.go b/geojson/detect.go deleted file mode 100644 index e06b1336..00000000 --- a/geojson/detect.go +++ /dev/null @@ -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) -} diff --git a/geojson/feature.go b/geojson/feature.go index 56bb28e2..8991378e 100644 --- a/geojson/feature.go +++ b/geojson/feature.go @@ -192,12 +192,20 @@ func (g Feature) IntersectsBBox(bbox BBox) bool { // Within detects if the object is fully contained inside another object. 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. 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. diff --git a/geojson/featurecollection.go b/geojson/featurecollection.go index bdee6cc4..9870c64d 100644 --- a/geojson/featurecollection.go +++ b/geojson/featurecollection.go @@ -178,12 +178,36 @@ func (g FeatureCollection) IntersectsBBox(bbox BBox) bool { // Within detects if the object is fully contained inside another object. 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. 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. diff --git a/geojson/geometrycollection.go b/geojson/geometrycollection.go index 3aacc46a..ac01af2f 100644 --- a/geojson/geometrycollection.go +++ b/geojson/geometrycollection.go @@ -177,12 +177,36 @@ func (g GeometryCollection) IntersectsBBox(bbox BBox) bool { // Within detects if the object is fully contained inside another object. 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. 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. diff --git a/geojson/linestring.go b/geojson/linestring.go index e4de9d50..eee21c22 100644 --- a/geojson/linestring.go +++ b/geojson/linestring.go @@ -98,12 +98,20 @@ func (g LineString) IntersectsBBox(bbox BBox) bool { // Within detects if the object is fully contained inside another object. 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. 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. diff --git a/geojson/multilinestring.go b/geojson/multilinestring.go index 01123d3a..d30b8ce6 100644 --- a/geojson/multilinestring.go +++ b/geojson/multilinestring.go @@ -2,6 +2,7 @@ package geojson import ( "github.com/tidwall/tile38/geojson/geohash" + "github.com/tidwall/tile38/geojson/poly" ) // MultiLineString is a geojson object with the type "MultiLineString" @@ -9,50 +10,36 @@ type MultiLineString struct { Coordinates [][]Position BBox *BBox bboxDefined bool - linestrings []LineString } func fillMultiLineString(coordinates [][]Position, bbox *BBox, err error) (MultiLineString, error) { - linestrings := make([]LineString, len(coordinates)) if err == nil { - for i, ps := range coordinates { - linestrings[i], err = fillLineString(ps, nil, nil) - if err != nil { + for _, coordinates := range coordinates { + if len(coordinates) < 2 { + err = errLineStringInvalidCoordinates break } } } bboxDefined := bbox != nil if !bboxDefined { - cbbox := mlCalculatedBBox(linestrings, nil) + cbbox := level3CalculatedBBox(coordinates, nil, false) bbox = &cbbox } return MultiLineString{ Coordinates: coordinates, BBox: bbox, bboxDefined: bboxDefined, - linestrings: linestrings, }, err } -func mlCalculatedBBox(linestrings []LineString, bbox *BBox) BBox { - if bbox != nil { - return *bbox - } - var cbbox BBox - for i, g := range linestrings { - if i == 0 { - cbbox = g.CalculatedBBox() - } else { - cbbox = cbbox.union(g.CalculatedBBox()) - } - } - return cbbox +func (g MultiLineString) getLineString(index int) LineString { + return LineString{Coordinates: g.Coordinates[index]} } // CalculatedBBox is exterior bbox containing the object. 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. @@ -110,13 +97,6 @@ func (g MultiLineString) hasPositions() bool { 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. func (g MultiLineString) WithinBBox(bbox BBox) bool { if g.bboxDefined { @@ -125,10 +105,15 @@ func (g MultiLineString) WithinBBox(bbox BBox) bool { if len(g.Coordinates) == 0 { return false } - for i := range g.Coordinates { - if !g.getLineString(i).WithinBBox(bbox) { + for _, ls := range g.Coordinates { + if len(ls) == 0 { return false } + for _, p := range ls { + if !poly.Point(p).InsideRect(rectBBox(bbox)) { + return false + } + } } return true } @@ -138,8 +123,8 @@ func (g MultiLineString) IntersectsBBox(bbox BBox) bool { if g.bboxDefined { return rectBBox(g.CalculatedBBox()).IntersectsRect(rectBBox(bbox)) } - for i := range g.Coordinates { - if g.getLineString(i).IntersectsBBox(bbox) { + for _, ls := range g.Coordinates { + if polyPositions(ls).IntersectsRect(rectBBox(bbox)) { return true } } @@ -148,12 +133,36 @@ func (g MultiLineString) IntersectsBBox(bbox BBox) bool { // Within detects if the object is fully contained inside another object. 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. 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. diff --git a/geojson/multipoint.go b/geojson/multipoint.go index 8a1af796..3328f7c5 100644 --- a/geojson/multipoint.go +++ b/geojson/multipoint.go @@ -25,10 +25,6 @@ func fillMultiPoint(coordinates []Position, bbox *BBox, err error) (MultiPoint, }, err } -func (g MultiPoint) getPoint(index int) Point { - return Point{Coordinates: g.Coordinates[index]} -} - // CalculatedBBox is exterior bbox containing the object. func (g MultiPoint) CalculatedBBox() 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. 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. 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. diff --git a/geojson/multipolygon.go b/geojson/multipolygon.go index f55f9d50..af2dc96a 100644 --- a/geojson/multipolygon.go +++ b/geojson/multipolygon.go @@ -22,7 +22,7 @@ func fillMultiPolygon(coordinates [][][]Position, bbox *BBox, err error) (MultiP } bboxDefined := bbox != nil if !bboxDefined { - cbbox := mpCalculatedBBox(polygons, nil) + cbbox := calculatedBBox(polygons, nil) bbox = &cbbox } return MultiPolygon{ @@ -33,16 +33,16 @@ func fillMultiPolygon(coordinates [][][]Position, bbox *BBox, err error) (MultiP }, err } -func mpCalculatedBBox(polygons []Polygon, bbox *BBox) BBox { +func calculatedBBox(polygons []Polygon, bbox *BBox) BBox { if bbox != nil { return *bbox } var cbbox BBox - for i, g := range polygons { + for i, p := range polygons { if i == 0 { - cbbox = g.CalculatedBBox() + cbbox = p.CalculatedBBox() } else { - cbbox = cbbox.union(g.CalculatedBBox()) + cbbox = cbbox.union(p.CalculatedBBox()) } } return cbbox @@ -50,7 +50,7 @@ func mpCalculatedBBox(polygons []Polygon, bbox *BBox) BBox { // CalculatedBBox is exterior bbox containing the object. 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. @@ -148,12 +148,32 @@ func (g MultiPolygon) IntersectsBBox(bbox BBox) bool { // Within detects if the object is fully contained inside another object. 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. 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. diff --git a/geojson/object.go b/geojson/object.go index ac19f4aa..62f52d8d 100644 --- a/geojson/object.go +++ b/geojson/object.go @@ -182,6 +182,171 @@ func objectMap(json string, from int) (Object, error) { 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. func CirclePolygon(x, y, meters float64, steps int) Polygon { if steps < 3 { @@ -202,6 +367,21 @@ func CirclePolygon(x, y, meters float64, steps int) Polygon { 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 { for i := 0; i < len(s); i++ { if s[i] < ' ' || s[i] == '\\' || s[i] == '"' || s[i] > 126 { diff --git a/geojson/object_test.go b/geojson/object_test.go index d2328441..f6e9ef66 100644 --- a/geojson/object_test.go +++ b/geojson/object_test.go @@ -42,72 +42,3 @@ func TestCirclePolygon(t *testing.T) { 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") - } - -} diff --git a/geojson/point.go b/geojson/point.go index fd02b75c..59dfed47 100644 --- a/geojson/point.go +++ b/geojson/point.go @@ -105,12 +105,20 @@ func (g Point) IntersectsBBox(bbox BBox) bool { // Within detects if the object is fully contained inside another object. 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. 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. diff --git a/geojson/polygon.go b/geojson/polygon.go index f1ad09f0..375b06c3 100644 --- a/geojson/polygon.go +++ b/geojson/polygon.go @@ -145,12 +145,26 @@ func (g Polygon) IntersectsBBox(bbox BBox) bool { // Within detects if the object is fully contained inside another object. 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. 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. diff --git a/geojson/simplepoint.go b/geojson/simplepoint.go index 01dd7c65..0d09ee97 100644 --- a/geojson/simplepoint.go +++ b/geojson/simplepoint.go @@ -87,12 +87,20 @@ func (g SimplePoint) IntersectsBBox(bbox BBox) bool { // Within detects if the object is fully contained inside another object. 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. 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.