diff options
Diffstat (limited to 'src/mongo/db/geo/big_polygon_test.cpp')
-rw-r--r-- | src/mongo/db/geo/big_polygon_test.cpp | 1010 |
1 files changed, 491 insertions, 519 deletions
diff --git a/src/mongo/db/geo/big_polygon_test.cpp b/src/mongo/db/geo/big_polygon_test.cpp index c0c01abdba7..3ac82b03768 100644 --- a/src/mongo/db/geo/big_polygon_test.cpp +++ b/src/mongo/db/geo/big_polygon_test.cpp @@ -34,562 +34,534 @@ namespace { - using namespace mongo; - using std::unique_ptr; - using std::string; - using std::vector; - - // Helper to build a vector of S2Point - struct PointBuilder { - - vector<S2Point> points; - - PointBuilder& operator<<(const S2LatLng& LatLng) { - points.push_back(LatLng.ToPoint()); - return *this; - } - }; - - vector<S2Point> pointVec(const PointBuilder& builder) { - vector<S2Point> points(builder.points.begin(), builder.points.end()); - return points; - } - - S2Loop* loop(const PointBuilder& builder) { - return new S2Loop(builder.points); - } - - vector<S2Loop*>* loopVec(const PointBuilder& builder) { - static vector<S2Loop*> loops; - loops.clear(); - loops.push_back(loop(builder)); - return &loops; - } - - S2LatLng LatLng(double lat, double lng) { - return S2LatLng::FromDegrees(lat, lng); - } - - // Syntax sugar for PointBuilder, which can be used to construct - // - vector<S2Point> pointVec() - // - S2Loop* loop() - // - vector<S2Loop*>* loopVec() - // - // e.g. points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) << LatLng(0.0, 0.0)) - typedef PointBuilder points; - - TEST(BigSimplePolygon, Basic) { - - // A 20x20 square centered at [0,0] - BigSimplePolygon bigPoly20(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) - << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); - - // A 10x10 square centered at [0,0] - S2Polygon poly10(loopVec(points() << LatLng(5.0, 5.0) << LatLng(5.0, -5.0) - << LatLng(-5.0, -5.0) << LatLng(-5.0, 5.0))); - - ASSERT_LESS_THAN(bigPoly20.GetArea(), 2 * M_PI); - ASSERT_LESS_THAN(poly10.GetArea(), bigPoly20.GetArea()); - ASSERT(bigPoly20.Contains(poly10)); - ASSERT(bigPoly20.Intersects(poly10)); - - // A 20x20 square centered at [0,20] - BigSimplePolygon bigPoly20Offset(loop(points() << LatLng(10.0, 30.0) << LatLng(10.0, 10.0) - << LatLng(-10.0, 10.0) << LatLng(-10.0, 30.0))); - - ASSERT_LESS_THAN(bigPoly20Offset.GetArea(), 2 * M_PI); - ASSERT_LESS_THAN(poly10.GetArea(), bigPoly20Offset.GetArea()); - ASSERT_FALSE(bigPoly20Offset.Contains(poly10)); - ASSERT_FALSE(bigPoly20Offset.Intersects(poly10)); - } - - TEST(BigSimplePolygon, BasicWithHole) { - // A 30x30 square centered at [0,0] with a 20X20 hole - vector<S2Loop*> loops; - loops.push_back(loop(points() << LatLng(15.0, 15.0) << LatLng(15.0, -15.0) - << LatLng(-15.0, -15.0) << LatLng(-15.0, 15.0))); - loops.push_back(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) - << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); - - S2Polygon holePoly(&loops); - - // A 16X16 square centered at [0,0] - BigSimplePolygon bigPoly16(loop(points() << LatLng(8.0, 8.0) << LatLng(8.0, -8.0) - << LatLng(-8.0, -8.0) << LatLng(-8.0, 8.0))); - - ASSERT_LESS_THAN(bigPoly16.GetArea(), 2 * M_PI); - ASSERT_FALSE(bigPoly16.Contains(holePoly)); - ASSERT_FALSE(bigPoly16.Intersects(holePoly)); - - // A big polygon bigger than the hole. - BigSimplePolygon bigPoly24(loop(points() << LatLng(12.0, 12.0) << LatLng(12.0, -12.0) - << LatLng(-12.0, -12.0) << LatLng(-12.0, 12.0))); - ASSERT_LESS_THAN(bigPoly24.GetArea(), 2 * M_PI); - ASSERT_FALSE(bigPoly24.Contains(holePoly)); - ASSERT_TRUE(bigPoly24.Intersects(holePoly)); - } - - TEST(BigSimplePolygon, BasicWithHoleAndShell) { - // A 30x30 square centered at [0,0] with a 20X20 hole and 10X10 shell - vector<S2Loop*> loops; - // Border - loops.push_back(loop(points() << LatLng(15.0, 15.0) << LatLng(15.0, -15.0) - << LatLng(-15.0, -15.0) << LatLng(-15.0, 15.0))); - // Hole - loops.push_back(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) - << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); - // Shell - loops.push_back(loop(points() << LatLng(5.0, 5.0) << LatLng(5.0, -5.0) - << LatLng(-5.0, -5.0) << LatLng(-5.0, 5.0))); - S2Polygon shellPoly(&loops); - - // A 16X16 square centered at [0,0] containing the shell - BigSimplePolygon bigPoly16(loop(points() << LatLng(8.0, 8.0) << LatLng(8.0, -8.0) - << LatLng(-8.0, -8.0) << LatLng(-8.0, 8.0))); - ASSERT_LESS_THAN(bigPoly16.GetArea(), 2 * M_PI); - ASSERT_FALSE(bigPoly16.Contains(shellPoly)); - ASSERT_TRUE(bigPoly16.Intersects(shellPoly)); - - // Try a big polygon bigger than the hole. - BigSimplePolygon bigPoly24(loop(points() << LatLng(12.0, 12.0) << LatLng(12.0, -12.0) - << LatLng(-12.0, -12.0) << LatLng(-12.0, 12.0))); - ASSERT_LESS_THAN(bigPoly24.GetArea(), 2 * M_PI); - ASSERT_FALSE(bigPoly24.Contains(shellPoly)); - ASSERT_TRUE(bigPoly24.Intersects(shellPoly)); - - // Try a big polygon smaller than the shell. - BigSimplePolygon bigPoly8(loop(points() << LatLng(4.0, 4.0) << LatLng(4.0, -4.0) - << LatLng(-4.0, -4.0) << LatLng(-4.0, 4.0))); - ASSERT_LESS_THAN(bigPoly8.GetArea(), 2 * M_PI); - ASSERT_FALSE(bigPoly8.Contains(shellPoly)); - ASSERT_TRUE(bigPoly8.Intersects(shellPoly)); - } - - TEST(BigSimplePolygon, BasicComplement) { - - // Everything *not* in a 20x20 square centered at [0,0] - BigSimplePolygon bigPoly20Comp(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) - << LatLng(-10.0, -10.0) - << LatLng(-10.0, 10.0))); - bigPoly20Comp.Invert(); - - // A 10x10 square centered at [0,0] - S2Polygon poly10(loopVec(points() << LatLng(5.0, 5.0) << LatLng(5.0, -5.0) - << LatLng(-5.0, -5.0) << LatLng(-5.0, 5.0))); - - ASSERT_GREATER_THAN(bigPoly20Comp.GetArea(), 2 * M_PI); - ASSERT_FALSE(bigPoly20Comp.Contains(poly10)); - ASSERT_FALSE(bigPoly20Comp.Intersects(poly10)); - - // A 10x10 square centered at [0,20], contained by bigPoly20Comp - S2Polygon poly10Contained(loopVec(points() << LatLng(25.0, 25.0) << LatLng(25.0, 15.0) - << LatLng(15.0, 15.0) << LatLng(15.0, 25.0))); - - ASSERT_LESS_THAN(poly10Contained.GetArea(), bigPoly20Comp.GetArea()); - ASSERT(bigPoly20Comp.Contains(poly10Contained)); - ASSERT(bigPoly20Comp.Intersects(poly10Contained)); - - // A 30x30 square centered at [0,0], so that bigPoly20Comp contains its complement entirely, - // which is not allowed by S2. - S2Polygon poly30(loopVec(points() << LatLng(15.0, 15.0) << LatLng(15.0, -15.0) - << LatLng(-15.0, -15.0) << LatLng(-15.0, 15.0))); - ASSERT_LESS_THAN(poly30.GetArea(), bigPoly20Comp.GetArea()); - ASSERT_FALSE(bigPoly20Comp.Contains(poly30)); - ASSERT_TRUE(bigPoly20Comp.Intersects(poly30)); - } - - TEST(BigSimplePolygon, BasicIntersects) { - - // Everything *not* in a 20x20 square centered at [0,0] - BigSimplePolygon bigPoly20(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) - << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); - bigPoly20.Invert(); - - // A 10x10 square centered at [10,10] (partial overlap) - S2Polygon poly10(loopVec(points() << LatLng(15.0, 15.0) << LatLng(15.0, 5.0) - << LatLng(5.0, 5.0) << LatLng(5.0, 15.0))); - - ASSERT_FALSE(bigPoly20.Contains(poly10)); - ASSERT(bigPoly20.Intersects(poly10)); +using namespace mongo; +using std::unique_ptr; +using std::string; +using std::vector; + +// Helper to build a vector of S2Point +struct PointBuilder { + vector<S2Point> points; + + PointBuilder& operator<<(const S2LatLng& LatLng) { + points.push_back(LatLng.ToPoint()); + return *this; } +}; - TEST(BigSimplePolygon, BasicComplementWithHole) { - // A 30x30 square centered at [0,0] with a 20X20 hole - vector<S2Loop*> loops; - loops.push_back(loop(points() << LatLng(15.0, 15.0) << LatLng(15.0, -15.0) - << LatLng(-15.0, -15.0) << LatLng(-15.0, 15.0))); - loops.push_back(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) - << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); - - S2Polygon holePoly(&loops); - - // 1. BigPolygon doesn't touch holePoly - // Everything *not* in a 40x40 square centered at [0,0] - BigSimplePolygon bigPoly40Comp(loop(points() << LatLng(20.0, 20.0) << LatLng(20.0, -20.0) - << LatLng(-20.0, -20.0) - << LatLng(-20.0, 20.0))); - bigPoly40Comp.Invert(); - ASSERT_GREATER_THAN(bigPoly40Comp.GetArea(), 2 * M_PI); - ASSERT_FALSE(bigPoly40Comp.Contains(holePoly)); - ASSERT_FALSE(bigPoly40Comp.Intersects(holePoly)); - - // 2. BigPolygon intersects holePoly - // Everything *not* in a 24X24 square centered at [0,0] - BigSimplePolygon bigPoly24Comp(loop(points() << LatLng(12.0, 12.0) << LatLng(12.0, -12.0) - << LatLng(-12.0, -12.0) - << LatLng(-12.0, 12.0))); - bigPoly24Comp.Invert(); - ASSERT_GREATER_THAN(bigPoly24Comp.GetArea(), 2 * M_PI); - ASSERT_FALSE(bigPoly24Comp.Contains(holePoly)); - ASSERT_TRUE(bigPoly24Comp.Intersects(holePoly)); - - // 3. BigPolygon contains holePoly - // Everything *not* in a 16X16 square centered at [0,0] - BigSimplePolygon bigPoly16Comp(loop(points() << LatLng(8.0, 8.0) << LatLng(8.0, -8.0) - << LatLng(-8.0, -8.0) - << LatLng(-8.0, 8.0))); - bigPoly16Comp.Invert(); - ASSERT_GREATER_THAN(bigPoly16Comp.GetArea(), 2 * M_PI); - ASSERT_TRUE(bigPoly16Comp.Contains(holePoly)); - ASSERT_TRUE(bigPoly16Comp.Intersects(holePoly)); - - // 4. BigPolygon contains the right half of holePoly - // Everything *not* in a 40x40 square centered at [0,20] - BigSimplePolygon bigPoly40CompOffset(loop(points() << LatLng(20.0, 40.0) - << LatLng(20.0, 0.0) - << LatLng(-20.0, 0.0) - << LatLng(-20.0, 40.0))); - bigPoly40CompOffset.Invert(); - ASSERT_GREATER_THAN(bigPoly40CompOffset.GetArea(), 2 * M_PI); - ASSERT_FALSE(bigPoly40CompOffset.Contains(holePoly)); - ASSERT_TRUE(bigPoly40CompOffset.Intersects(holePoly)); - } +vector<S2Point> pointVec(const PointBuilder& builder) { + vector<S2Point> points(builder.points.begin(), builder.points.end()); + return points; +} - TEST(BigSimplePolygon, BasicComplementWithHoleAndShell) { - // A 30x30 square centered at [0,0] with a 20X20 hole and 10X10 shell - vector<S2Loop*> loops; - // Border - loops.push_back(loop(points() << LatLng(15.0, 15.0) << LatLng(15.0, -15.0) - << LatLng(-15.0, -15.0) << LatLng(-15.0, 15.0))); - // Hole - loops.push_back(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) - << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); - // Shell - loops.push_back(loop(points() << LatLng(5.0, 5.0) << LatLng(5.0, -5.0) - << LatLng(-5.0, -5.0) << LatLng(-5.0, 5.0))); - S2Polygon shellPoly(&loops); - - // 1. BigPolygon doesn't touch shellPoly - // Everything *not* in a 40x40 square centered at [0,0] - BigSimplePolygon bigPoly40Comp(loop(points() << LatLng(20.0, 20.0) << LatLng(20.0, -20.0) - << LatLng(-20.0, -20.0) - << LatLng(-20.0, 20.0))); - bigPoly40Comp.Invert(); - ASSERT_GREATER_THAN(bigPoly40Comp.GetArea(), 2 * M_PI); - ASSERT_FALSE(bigPoly40Comp.Contains(shellPoly)); - ASSERT_FALSE(bigPoly40Comp.Intersects(shellPoly)); - - // 2. BigPolygon intersects shellPoly - // Everything *not* in a 24X24 square centered at [0,0] - BigSimplePolygon bigPoly24Comp(loop(points() << LatLng(12.0, 12.0) << LatLng(12.0, -12.0) - << LatLng(-12.0, -12.0) - << LatLng(-12.0, 12.0))); - bigPoly24Comp.Invert(); - ASSERT_GREATER_THAN(bigPoly24Comp.GetArea(), 2 * M_PI); - ASSERT_FALSE(bigPoly24Comp.Contains(shellPoly)); - ASSERT_TRUE(bigPoly24Comp.Intersects(shellPoly)); - - // 3. BigPolygon contains shellPoly's outer ring - // Everything *not* in a 16X16 square centered at [0,0] - BigSimplePolygon bigPoly16Comp(loop(points() << LatLng(8.0, 8.0) << LatLng(8.0, -8.0) - << LatLng(-8.0, -8.0) - << LatLng(-8.0, 8.0))); - bigPoly16Comp.Invert(); - ASSERT_GREATER_THAN(bigPoly16Comp.GetArea(), 2 * M_PI); - ASSERT_FALSE(bigPoly16Comp.Contains(shellPoly)); - ASSERT_TRUE(bigPoly16Comp.Intersects(shellPoly)); - - // 4. BigPolygon contains the right half of shellPoly - // Everything *not* in a 40x40 square centered at [0,20] - BigSimplePolygon bigPoly40CompOffset(loop(points() << LatLng(20.0, 40.0) - << LatLng(20.0, 0.0) - << LatLng(-20.0, 0.0) - << LatLng(-20.0, 40.0))); - bigPoly40CompOffset.Invert(); - ASSERT_GREATER_THAN(bigPoly40CompOffset.GetArea(), 2 * M_PI); - ASSERT_FALSE(bigPoly40CompOffset.Contains(shellPoly)); - ASSERT_TRUE(bigPoly40CompOffset.Intersects(shellPoly)); - - // 5. BigPolygon contain shellPoly (CW) - BigSimplePolygon bigPolyCompOffset(loop(points() << LatLng(6.0, 6.0) - << LatLng(6.0, 8.0) - << LatLng(-6.0, 8.0) - << LatLng(-6.0, 6.0))); - ASSERT_GREATER_THAN(bigPolyCompOffset.GetArea(), 2 * M_PI); - ASSERT_TRUE(bigPolyCompOffset.Contains(shellPoly)); - ASSERT_TRUE(bigPolyCompOffset.Intersects(shellPoly)); - } +S2Loop* loop(const PointBuilder& builder) { + return new S2Loop(builder.points); +} - TEST(BigSimplePolygon, BasicWinding) { +vector<S2Loop*>* loopVec(const PointBuilder& builder) { + static vector<S2Loop*> loops; + loops.clear(); + loops.push_back(loop(builder)); + return &loops; +} - // A 20x20 square centered at [0,0] (CCW) - BigSimplePolygon bigPoly20(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) - << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); +S2LatLng LatLng(double lat, double lng) { + return S2LatLng::FromDegrees(lat, lng); +} - // Everything *not* in a 20x20 square centered at [0,0] (CW) - BigSimplePolygon bigPoly20Comp(loop(points() << LatLng(10.0, 10.0) << LatLng(-10.0, 10.0) - << LatLng(-10.0, -10.0) - << LatLng(10.0, -10.0))); +// Syntax sugar for PointBuilder, which can be used to construct +// - vector<S2Point> pointVec() +// - S2Loop* loop() +// - vector<S2Loop*>* loopVec() +// +// e.g. points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) << LatLng(0.0, 0.0)) +typedef PointBuilder points; + +TEST(BigSimplePolygon, Basic) { + // A 20x20 square centered at [0,0] + BigSimplePolygon bigPoly20(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) + << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); + + // A 10x10 square centered at [0,0] + S2Polygon poly10(loopVec(points() << LatLng(5.0, 5.0) << LatLng(5.0, -5.0) << LatLng(-5.0, -5.0) + << LatLng(-5.0, 5.0))); + + ASSERT_LESS_THAN(bigPoly20.GetArea(), 2 * M_PI); + ASSERT_LESS_THAN(poly10.GetArea(), bigPoly20.GetArea()); + ASSERT(bigPoly20.Contains(poly10)); + ASSERT(bigPoly20.Intersects(poly10)); + + // A 20x20 square centered at [0,20] + BigSimplePolygon bigPoly20Offset(loop(points() << LatLng(10.0, 30.0) << LatLng(10.0, 10.0) + << LatLng(-10.0, 10.0) << LatLng(-10.0, 30.0))); + + ASSERT_LESS_THAN(bigPoly20Offset.GetArea(), 2 * M_PI); + ASSERT_LESS_THAN(poly10.GetArea(), bigPoly20Offset.GetArea()); + ASSERT_FALSE(bigPoly20Offset.Contains(poly10)); + ASSERT_FALSE(bigPoly20Offset.Intersects(poly10)); +} - ASSERT_LESS_THAN(bigPoly20.GetArea(), 2 * M_PI); - ASSERT_GREATER_THAN(bigPoly20Comp.GetArea(), 2 * M_PI); - } +TEST(BigSimplePolygon, BasicWithHole) { + // A 30x30 square centered at [0,0] with a 20X20 hole + vector<S2Loop*> loops; + loops.push_back(loop(points() << LatLng(15.0, 15.0) << LatLng(15.0, -15.0) + << LatLng(-15.0, -15.0) << LatLng(-15.0, 15.0))); + loops.push_back(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) + << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); + + S2Polygon holePoly(&loops); + + // A 16X16 square centered at [0,0] + BigSimplePolygon bigPoly16(loop(points() << LatLng(8.0, 8.0) << LatLng(8.0, -8.0) + << LatLng(-8.0, -8.0) << LatLng(-8.0, 8.0))); + + ASSERT_LESS_THAN(bigPoly16.GetArea(), 2 * M_PI); + ASSERT_FALSE(bigPoly16.Contains(holePoly)); + ASSERT_FALSE(bigPoly16.Intersects(holePoly)); + + // A big polygon bigger than the hole. + BigSimplePolygon bigPoly24(loop(points() << LatLng(12.0, 12.0) << LatLng(12.0, -12.0) + << LatLng(-12.0, -12.0) << LatLng(-12.0, 12.0))); + ASSERT_LESS_THAN(bigPoly24.GetArea(), 2 * M_PI); + ASSERT_FALSE(bigPoly24.Contains(holePoly)); + ASSERT_TRUE(bigPoly24.Intersects(holePoly)); +} - TEST(BigSimplePolygon, LineRelations) { +TEST(BigSimplePolygon, BasicWithHoleAndShell) { + // A 30x30 square centered at [0,0] with a 20X20 hole and 10X10 shell + vector<S2Loop*> loops; + // Border + loops.push_back(loop(points() << LatLng(15.0, 15.0) << LatLng(15.0, -15.0) + << LatLng(-15.0, -15.0) << LatLng(-15.0, 15.0))); + // Hole + loops.push_back(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) + << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); + // Shell + loops.push_back(loop(points() << LatLng(5.0, 5.0) << LatLng(5.0, -5.0) << LatLng(-5.0, -5.0) + << LatLng(-5.0, 5.0))); + S2Polygon shellPoly(&loops); + + // A 16X16 square centered at [0,0] containing the shell + BigSimplePolygon bigPoly16(loop(points() << LatLng(8.0, 8.0) << LatLng(8.0, -8.0) + << LatLng(-8.0, -8.0) << LatLng(-8.0, 8.0))); + ASSERT_LESS_THAN(bigPoly16.GetArea(), 2 * M_PI); + ASSERT_FALSE(bigPoly16.Contains(shellPoly)); + ASSERT_TRUE(bigPoly16.Intersects(shellPoly)); + + // Try a big polygon bigger than the hole. + BigSimplePolygon bigPoly24(loop(points() << LatLng(12.0, 12.0) << LatLng(12.0, -12.0) + << LatLng(-12.0, -12.0) << LatLng(-12.0, 12.0))); + ASSERT_LESS_THAN(bigPoly24.GetArea(), 2 * M_PI); + ASSERT_FALSE(bigPoly24.Contains(shellPoly)); + ASSERT_TRUE(bigPoly24.Intersects(shellPoly)); + + // Try a big polygon smaller than the shell. + BigSimplePolygon bigPoly8(loop(points() << LatLng(4.0, 4.0) << LatLng(4.0, -4.0) + << LatLng(-4.0, -4.0) << LatLng(-4.0, 4.0))); + ASSERT_LESS_THAN(bigPoly8.GetArea(), 2 * M_PI); + ASSERT_FALSE(bigPoly8.Contains(shellPoly)); + ASSERT_TRUE(bigPoly8.Intersects(shellPoly)); +} - // A 20x20 square centered at [0,0] - BigSimplePolygon bigPoly20(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) +TEST(BigSimplePolygon, BasicComplement) { + // Everything *not* in a 20x20 square centered at [0,0] + BigSimplePolygon bigPoly20Comp(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); + bigPoly20Comp.Invert(); + + // A 10x10 square centered at [0,0] + S2Polygon poly10(loopVec(points() << LatLng(5.0, 5.0) << LatLng(5.0, -5.0) << LatLng(-5.0, -5.0) + << LatLng(-5.0, 5.0))); + + ASSERT_GREATER_THAN(bigPoly20Comp.GetArea(), 2 * M_PI); + ASSERT_FALSE(bigPoly20Comp.Contains(poly10)); + ASSERT_FALSE(bigPoly20Comp.Intersects(poly10)); + + // A 10x10 square centered at [0,20], contained by bigPoly20Comp + S2Polygon poly10Contained(loopVec(points() << LatLng(25.0, 25.0) << LatLng(25.0, 15.0) + << LatLng(15.0, 15.0) << LatLng(15.0, 25.0))); + + ASSERT_LESS_THAN(poly10Contained.GetArea(), bigPoly20Comp.GetArea()); + ASSERT(bigPoly20Comp.Contains(poly10Contained)); + ASSERT(bigPoly20Comp.Intersects(poly10Contained)); + + // A 30x30 square centered at [0,0], so that bigPoly20Comp contains its complement entirely, + // which is not allowed by S2. + S2Polygon poly30(loopVec(points() << LatLng(15.0, 15.0) << LatLng(15.0, -15.0) + << LatLng(-15.0, -15.0) << LatLng(-15.0, 15.0))); + ASSERT_LESS_THAN(poly30.GetArea(), bigPoly20Comp.GetArea()); + ASSERT_FALSE(bigPoly20Comp.Contains(poly30)); + ASSERT_TRUE(bigPoly20Comp.Intersects(poly30)); +} - // A 10x10 line circling [0,0] - S2Polyline line10(pointVec(points() << LatLng(5.0, 5.0) << LatLng(5.0, -5.0) - << LatLng(-5.0, -5.0) << LatLng(-5.0, 5.0))); - - ASSERT_LESS_THAN(bigPoly20.GetArea(), 2 * M_PI); - ASSERT(bigPoly20.Contains(line10)); - ASSERT(bigPoly20.Intersects(line10)); +TEST(BigSimplePolygon, BasicIntersects) { + // Everything *not* in a 20x20 square centered at [0,0] + BigSimplePolygon bigPoly20(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) + << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); + bigPoly20.Invert(); - // Line segment disjoint from big polygon - S2Polyline lineDisjoint(pointVec(points() << LatLng(15.0, 5.0) << LatLng(15.0, -5.0))); - ASSERT_FALSE(bigPoly20.Contains(lineDisjoint)); - ASSERT_FALSE(bigPoly20.Intersects(lineDisjoint)); + // A 10x10 square centered at [10,10] (partial overlap) + S2Polygon poly10(loopVec(points() << LatLng(15.0, 15.0) << LatLng(15.0, 5.0) << LatLng(5.0, 5.0) + << LatLng(5.0, 15.0))); - // Line segment intersects big polygon - S2Polyline lineIntersect(pointVec(points() << LatLng(0.0, 0.0) << LatLng(15.0, 0.0))); - ASSERT_FALSE(bigPoly20.Contains(lineIntersect)); - ASSERT_TRUE(bigPoly20.Intersects(lineIntersect)); - } + ASSERT_FALSE(bigPoly20.Contains(poly10)); + ASSERT(bigPoly20.Intersects(poly10)); +} - TEST(BigSimplePolygon, LineRelationsComplement) { +TEST(BigSimplePolygon, BasicComplementWithHole) { + // A 30x30 square centered at [0,0] with a 20X20 hole + vector<S2Loop*> loops; + loops.push_back(loop(points() << LatLng(15.0, 15.0) << LatLng(15.0, -15.0) + << LatLng(-15.0, -15.0) << LatLng(-15.0, 15.0))); + loops.push_back(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) + << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); + + S2Polygon holePoly(&loops); + + // 1. BigPolygon doesn't touch holePoly + // Everything *not* in a 40x40 square centered at [0,0] + BigSimplePolygon bigPoly40Comp(loop(points() << LatLng(20.0, 20.0) << LatLng(20.0, -20.0) + << LatLng(-20.0, -20.0) << LatLng(-20.0, 20.0))); + bigPoly40Comp.Invert(); + ASSERT_GREATER_THAN(bigPoly40Comp.GetArea(), 2 * M_PI); + ASSERT_FALSE(bigPoly40Comp.Contains(holePoly)); + ASSERT_FALSE(bigPoly40Comp.Intersects(holePoly)); + + // 2. BigPolygon intersects holePoly + // Everything *not* in a 24X24 square centered at [0,0] + BigSimplePolygon bigPoly24Comp(loop(points() << LatLng(12.0, 12.0) << LatLng(12.0, -12.0) + << LatLng(-12.0, -12.0) << LatLng(-12.0, 12.0))); + bigPoly24Comp.Invert(); + ASSERT_GREATER_THAN(bigPoly24Comp.GetArea(), 2 * M_PI); + ASSERT_FALSE(bigPoly24Comp.Contains(holePoly)); + ASSERT_TRUE(bigPoly24Comp.Intersects(holePoly)); + + // 3. BigPolygon contains holePoly + // Everything *not* in a 16X16 square centered at [0,0] + BigSimplePolygon bigPoly16Comp(loop(points() << LatLng(8.0, 8.0) << LatLng(8.0, -8.0) + << LatLng(-8.0, -8.0) << LatLng(-8.0, 8.0))); + bigPoly16Comp.Invert(); + ASSERT_GREATER_THAN(bigPoly16Comp.GetArea(), 2 * M_PI); + ASSERT_TRUE(bigPoly16Comp.Contains(holePoly)); + ASSERT_TRUE(bigPoly16Comp.Intersects(holePoly)); + + // 4. BigPolygon contains the right half of holePoly + // Everything *not* in a 40x40 square centered at [0,20] + BigSimplePolygon bigPoly40CompOffset(loop(points() << LatLng(20.0, 40.0) << LatLng(20.0, 0.0) + << LatLng(-20.0, 0.0) + << LatLng(-20.0, 40.0))); + bigPoly40CompOffset.Invert(); + ASSERT_GREATER_THAN(bigPoly40CompOffset.GetArea(), 2 * M_PI); + ASSERT_FALSE(bigPoly40CompOffset.Contains(holePoly)); + ASSERT_TRUE(bigPoly40CompOffset.Intersects(holePoly)); +} - // A 20x20 square centered at [0,0] - BigSimplePolygon bigPoly20Comp(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) - << LatLng(-10.0, -10.0) - << LatLng(-10.0, 10.0))); - bigPoly20Comp.Invert(); +TEST(BigSimplePolygon, BasicComplementWithHoleAndShell) { + // A 30x30 square centered at [0,0] with a 20X20 hole and 10X10 shell + vector<S2Loop*> loops; + // Border + loops.push_back(loop(points() << LatLng(15.0, 15.0) << LatLng(15.0, -15.0) + << LatLng(-15.0, -15.0) << LatLng(-15.0, 15.0))); + // Hole + loops.push_back(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) + << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); + // Shell + loops.push_back(loop(points() << LatLng(5.0, 5.0) << LatLng(5.0, -5.0) << LatLng(-5.0, -5.0) + << LatLng(-5.0, 5.0))); + S2Polygon shellPoly(&loops); + + // 1. BigPolygon doesn't touch shellPoly + // Everything *not* in a 40x40 square centered at [0,0] + BigSimplePolygon bigPoly40Comp(loop(points() << LatLng(20.0, 20.0) << LatLng(20.0, -20.0) + << LatLng(-20.0, -20.0) << LatLng(-20.0, 20.0))); + bigPoly40Comp.Invert(); + ASSERT_GREATER_THAN(bigPoly40Comp.GetArea(), 2 * M_PI); + ASSERT_FALSE(bigPoly40Comp.Contains(shellPoly)); + ASSERT_FALSE(bigPoly40Comp.Intersects(shellPoly)); + + // 2. BigPolygon intersects shellPoly + // Everything *not* in a 24X24 square centered at [0,0] + BigSimplePolygon bigPoly24Comp(loop(points() << LatLng(12.0, 12.0) << LatLng(12.0, -12.0) + << LatLng(-12.0, -12.0) << LatLng(-12.0, 12.0))); + bigPoly24Comp.Invert(); + ASSERT_GREATER_THAN(bigPoly24Comp.GetArea(), 2 * M_PI); + ASSERT_FALSE(bigPoly24Comp.Contains(shellPoly)); + ASSERT_TRUE(bigPoly24Comp.Intersects(shellPoly)); + + // 3. BigPolygon contains shellPoly's outer ring + // Everything *not* in a 16X16 square centered at [0,0] + BigSimplePolygon bigPoly16Comp(loop(points() << LatLng(8.0, 8.0) << LatLng(8.0, -8.0) + << LatLng(-8.0, -8.0) << LatLng(-8.0, 8.0))); + bigPoly16Comp.Invert(); + ASSERT_GREATER_THAN(bigPoly16Comp.GetArea(), 2 * M_PI); + ASSERT_FALSE(bigPoly16Comp.Contains(shellPoly)); + ASSERT_TRUE(bigPoly16Comp.Intersects(shellPoly)); + + // 4. BigPolygon contains the right half of shellPoly + // Everything *not* in a 40x40 square centered at [0,20] + BigSimplePolygon bigPoly40CompOffset(loop(points() << LatLng(20.0, 40.0) << LatLng(20.0, 0.0) + << LatLng(-20.0, 0.0) + << LatLng(-20.0, 40.0))); + bigPoly40CompOffset.Invert(); + ASSERT_GREATER_THAN(bigPoly40CompOffset.GetArea(), 2 * M_PI); + ASSERT_FALSE(bigPoly40CompOffset.Contains(shellPoly)); + ASSERT_TRUE(bigPoly40CompOffset.Intersects(shellPoly)); + + // 5. BigPolygon contain shellPoly (CW) + BigSimplePolygon bigPolyCompOffset(loop(points() << LatLng(6.0, 6.0) << LatLng(6.0, 8.0) + << LatLng(-6.0, 8.0) << LatLng(-6.0, 6.0))); + ASSERT_GREATER_THAN(bigPolyCompOffset.GetArea(), 2 * M_PI); + ASSERT_TRUE(bigPolyCompOffset.Contains(shellPoly)); + ASSERT_TRUE(bigPolyCompOffset.Intersects(shellPoly)); +} - // A 10x10 line circling [0,0] - S2Polyline line10(pointVec(points() << LatLng(5.0, 5.0) << LatLng(5.0, -5.0) - << LatLng(-5.0, -5.0) << LatLng(-5.0, 5.0))); +TEST(BigSimplePolygon, BasicWinding) { + // A 20x20 square centered at [0,0] (CCW) + BigSimplePolygon bigPoly20(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) + << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); - ASSERT_GREATER_THAN(bigPoly20Comp.GetArea(), 2 * M_PI); - ASSERT_FALSE(bigPoly20Comp.Contains(line10)); - ASSERT_FALSE(bigPoly20Comp.Intersects(line10)); + // Everything *not* in a 20x20 square centered at [0,0] (CW) + BigSimplePolygon bigPoly20Comp(loop(points() << LatLng(10.0, 10.0) << LatLng(-10.0, 10.0) + << LatLng(-10.0, -10.0) << LatLng(10.0, -10.0))); - // Line segment (0, 0) -> (0, 15) - S2Polyline lineIntersect(pointVec(points() << LatLng(0.0, 0.0) << LatLng(0.0, 15.0))); - ASSERT_FALSE(bigPoly20Comp.Contains(lineIntersect)); - ASSERT_TRUE(bigPoly20Comp.Intersects(lineIntersect)); + ASSERT_LESS_THAN(bigPoly20.GetArea(), 2 * M_PI); + ASSERT_GREATER_THAN(bigPoly20Comp.GetArea(), 2 * M_PI); +} - // A 10x10 line circling [0,0] - S2Polyline line30(pointVec(points() << LatLng(15.0, 15.0) << LatLng(15.0, -15.0) - << LatLng(-15.0, -15.0) << LatLng(-15.0, 15.0))); - ASSERT_TRUE(bigPoly20Comp.Contains(line30)); - ASSERT_TRUE(bigPoly20Comp.Intersects(line30)); - } +TEST(BigSimplePolygon, LineRelations) { + // A 20x20 square centered at [0,0] + BigSimplePolygon bigPoly20(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) + << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); - TEST(BigSimplePolygon, LineRelationsWinding) { + // A 10x10 line circling [0,0] + S2Polyline line10(pointVec(points() << LatLng(5.0, 5.0) << LatLng(5.0, -5.0) + << LatLng(-5.0, -5.0) << LatLng(-5.0, 5.0))); - // Everything *not* in a 20x20 square centered at [0,0] (CW winding) - BigSimplePolygon bigPoly20Comp(loop(points() << LatLng(10.0, 10.0) << LatLng(-10.0, 10.0) - << LatLng(-10.0, -10.0) - << LatLng(10.0, -10.0))); + ASSERT_LESS_THAN(bigPoly20.GetArea(), 2 * M_PI); + ASSERT(bigPoly20.Contains(line10)); + ASSERT(bigPoly20.Intersects(line10)); - // A 10x10 line circling [0,0] - S2Polyline line10(pointVec(points() << LatLng(5.0, 5.0) << LatLng(5.0, -5.0) - << LatLng(-5.0, -5.0) << LatLng(-5.0, 5.0))); + // Line segment disjoint from big polygon + S2Polyline lineDisjoint(pointVec(points() << LatLng(15.0, 5.0) << LatLng(15.0, -5.0))); + ASSERT_FALSE(bigPoly20.Contains(lineDisjoint)); + ASSERT_FALSE(bigPoly20.Intersects(lineDisjoint)); - ASSERT_GREATER_THAN(bigPoly20Comp.GetArea(), 2 * M_PI); - ASSERT_FALSE(bigPoly20Comp.Contains(line10)); - ASSERT_FALSE(bigPoly20Comp.Intersects(line10)); - } + // Line segment intersects big polygon + S2Polyline lineIntersect(pointVec(points() << LatLng(0.0, 0.0) << LatLng(15.0, 0.0))); + ASSERT_FALSE(bigPoly20.Contains(lineIntersect)); + ASSERT_TRUE(bigPoly20.Intersects(lineIntersect)); +} - TEST(BigSimplePolygon, PolarContains) { +TEST(BigSimplePolygon, LineRelationsComplement) { + // A 20x20 square centered at [0,0] + BigSimplePolygon bigPoly20Comp(loop(points() << LatLng(10.0, 10.0) << LatLng(10.0, -10.0) + << LatLng(-10.0, -10.0) << LatLng(-10.0, 10.0))); + bigPoly20Comp.Invert(); - // Square 10 degrees from the north pole [90,0] - BigSimplePolygon bigNorthPoly(loop(points() << LatLng(80.0, 0.0) << LatLng(80.0, 90.0) - << LatLng(80.0, 180.0) << LatLng(80.0, -90.0))); + // A 10x10 line circling [0,0] + S2Polyline line10(pointVec(points() << LatLng(5.0, 5.0) << LatLng(5.0, -5.0) + << LatLng(-5.0, -5.0) << LatLng(-5.0, 5.0))); - // Square 5 degrees from the north pole [90, 0] - S2Polygon northPoly(loopVec(points() << LatLng(85.0, 0.0) << LatLng(85.0, 90.0) - << LatLng(85.0, 180.0) << LatLng(85.0, -90.0))); + ASSERT_GREATER_THAN(bigPoly20Comp.GetArea(), 2 * M_PI); + ASSERT_FALSE(bigPoly20Comp.Contains(line10)); + ASSERT_FALSE(bigPoly20Comp.Intersects(line10)); - ASSERT_LESS_THAN(bigNorthPoly.GetArea(), 2 * M_PI); - ASSERT_LESS_THAN(northPoly.GetArea(), bigNorthPoly.GetArea()); - ASSERT(bigNorthPoly.Contains(northPoly)); - ASSERT(bigNorthPoly.Intersects(northPoly)); - } + // Line segment (0, 0) -> (0, 15) + S2Polyline lineIntersect(pointVec(points() << LatLng(0.0, 0.0) << LatLng(0.0, 15.0))); + ASSERT_FALSE(bigPoly20Comp.Contains(lineIntersect)); + ASSERT_TRUE(bigPoly20Comp.Intersects(lineIntersect)); - TEST(BigSimplePolygon, PolarContainsWithHoles) { + // A 10x10 line circling [0,0] + S2Polyline line30(pointVec(points() << LatLng(15.0, 15.0) << LatLng(15.0, -15.0) + << LatLng(-15.0, -15.0) << LatLng(-15.0, 15.0))); + ASSERT_TRUE(bigPoly20Comp.Contains(line30)); + ASSERT_TRUE(bigPoly20Comp.Intersects(line30)); +} - // Square 10 degrees from the north pole [90,0] - BigSimplePolygon bigNorthPoly(loop(points() << LatLng(80.0, 0.0) << LatLng(80.0, 90.0) - << LatLng(80.0, 180.0) << LatLng(80.0, -90.0))); +TEST(BigSimplePolygon, LineRelationsWinding) { + // Everything *not* in a 20x20 square centered at [0,0] (CW winding) + BigSimplePolygon bigPoly20Comp(loop(points() << LatLng(10.0, 10.0) << LatLng(-10.0, 10.0) + << LatLng(-10.0, -10.0) << LatLng(10.0, -10.0))); - // Square 5 degrees from the north pole [90, 0] with a concentric hole 1 degree from the - // north pole - vector<S2Loop*> loops; - loops.push_back(loop(points() << LatLng(85.0, 0.0) << LatLng(85.0, 90.0) - << LatLng(85.0, 180.0) << LatLng(85.0, -90.0))); - loops.push_back(loop(points() << LatLng(89.0, 0.0) << LatLng(89.0, 90.0) - << LatLng(89.0, 180.0) << LatLng(89.0, -90.0))); - S2Polygon northPolyHole(&loops); + // A 10x10 line circling [0,0] + S2Polyline line10(pointVec(points() << LatLng(5.0, 5.0) << LatLng(5.0, -5.0) + << LatLng(-5.0, -5.0) << LatLng(-5.0, 5.0))); - ASSERT_LESS_THAN(northPolyHole.GetArea(), bigNorthPoly.GetArea()); - ASSERT(bigNorthPoly.Contains(northPolyHole)); - ASSERT(bigNorthPoly.Intersects(northPolyHole)); - } + ASSERT_GREATER_THAN(bigPoly20Comp.GetArea(), 2 * M_PI); + ASSERT_FALSE(bigPoly20Comp.Contains(line10)); + ASSERT_FALSE(bigPoly20Comp.Intersects(line10)); +} - TEST(BigSimplePolygon, PolarIntersectsWithHoles) { +TEST(BigSimplePolygon, PolarContains) { + // Square 10 degrees from the north pole [90,0] + BigSimplePolygon bigNorthPoly(loop(points() << LatLng(80.0, 0.0) << LatLng(80.0, 90.0) + << LatLng(80.0, 180.0) << LatLng(80.0, -90.0))); - // Square 10 degrees from the north pole [90,0] - BigSimplePolygon bigNorthPoly(loop(points() << LatLng(80.0, 0.0) << LatLng(80.0, 90.0) - << LatLng(80.0, 180.0) << LatLng(80.0, -90.0))); + // Square 5 degrees from the north pole [90, 0] + S2Polygon northPoly(loopVec(points() << LatLng(85.0, 0.0) << LatLng(85.0, 90.0) + << LatLng(85.0, 180.0) << LatLng(85.0, -90.0))); - // 5-degree square with 1-degree-wide concentric hole, centered on [80.0, 0.0] - vector<S2Loop*> loops; - loops.push_back(loop(points() << LatLng(85.0, 5.0) << LatLng(85.0, -5.0) - << LatLng(75.0, -5.0) << LatLng(75.0, 5.0))); - loops.push_back(loop(points() << LatLng(81.0, 1.0) << LatLng(81.0, -1.0) - << LatLng(79.0, -1.0) << LatLng(79.0, 1.0))); - S2Polygon northPolyHole(&loops); + ASSERT_LESS_THAN(bigNorthPoly.GetArea(), 2 * M_PI); + ASSERT_LESS_THAN(northPoly.GetArea(), bigNorthPoly.GetArea()); + ASSERT(bigNorthPoly.Contains(northPoly)); + ASSERT(bigNorthPoly.Intersects(northPoly)); +} - ASSERT_LESS_THAN(northPolyHole.GetArea(), bigNorthPoly.GetArea()); - ASSERT_FALSE(bigNorthPoly.Contains(northPolyHole)); - ASSERT(bigNorthPoly.Intersects(northPolyHole)); - } +TEST(BigSimplePolygon, PolarContainsWithHoles) { + // Square 10 degrees from the north pole [90,0] + BigSimplePolygon bigNorthPoly(loop(points() << LatLng(80.0, 0.0) << LatLng(80.0, 90.0) + << LatLng(80.0, 180.0) << LatLng(80.0, -90.0))); + + // Square 5 degrees from the north pole [90, 0] with a concentric hole 1 degree from the + // north pole + vector<S2Loop*> loops; + loops.push_back(loop(points() << LatLng(85.0, 0.0) << LatLng(85.0, 90.0) << LatLng(85.0, 180.0) + << LatLng(85.0, -90.0))); + loops.push_back(loop(points() << LatLng(89.0, 0.0) << LatLng(89.0, 90.0) << LatLng(89.0, 180.0) + << LatLng(89.0, -90.0))); + S2Polygon northPolyHole(&loops); + + ASSERT_LESS_THAN(northPolyHole.GetArea(), bigNorthPoly.GetArea()); + ASSERT(bigNorthPoly.Contains(northPolyHole)); + ASSERT(bigNorthPoly.Intersects(northPolyHole)); +} - // Edge cases - // - // No promise in terms of points on border - they may be inside or outside the big polygon. - // But we need to ensure the result is consistent: - // 1. If a polygon/line is contained by a big polygon, they must intersect with each other. - // 2. Relation doesn't change as long as the touch point doesn't change, no matter the big - // polygon is larger or less then a hemisphere. - // 3. Relations for big polygons less than a hemisphere are consistent with ordinary (simple) - // polygon results. - - template <typename TShape> - void checkConsistency(const BigSimplePolygon& bigPoly, - const BigSimplePolygon& expandedBigPoly, - const TShape& shape) { - // Contain() => Intersects() - if (bigPoly.Contains(shape)) ASSERT(bigPoly.Intersects(shape)); - if (expandedBigPoly.Contains(shape)) ASSERT(expandedBigPoly.Intersects(shape)); - // Relation doesn't change - ASSERT_EQUALS(bigPoly.Contains(shape), expandedBigPoly.Contains(shape)); - ASSERT_EQUALS(bigPoly.Intersects(shape), expandedBigPoly.Intersects(shape)); - } +TEST(BigSimplePolygon, PolarIntersectsWithHoles) { + // Square 10 degrees from the north pole [90,0] + BigSimplePolygon bigNorthPoly(loop(points() << LatLng(80.0, 0.0) << LatLng(80.0, 90.0) + << LatLng(80.0, 180.0) << LatLng(80.0, -90.0))); + + // 5-degree square with 1-degree-wide concentric hole, centered on [80.0, 0.0] + vector<S2Loop*> loops; + loops.push_back(loop(points() << LatLng(85.0, 5.0) << LatLng(85.0, -5.0) << LatLng(75.0, -5.0) + << LatLng(75.0, 5.0))); + loops.push_back(loop(points() << LatLng(81.0, 1.0) << LatLng(81.0, -1.0) << LatLng(79.0, -1.0) + << LatLng(79.0, 1.0))); + S2Polygon northPolyHole(&loops); + + ASSERT_LESS_THAN(northPolyHole.GetArea(), bigNorthPoly.GetArea()); + ASSERT_FALSE(bigNorthPoly.Contains(northPolyHole)); + ASSERT(bigNorthPoly.Intersects(northPolyHole)); +} - // Polygon shares big polygon's edge (disjoint) - TEST(BigSimplePolygon, ShareEdgeDisjoint) { - // Big polygon smaller than a hemisphere. - BigSimplePolygon bigPoly(loop(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) - << LatLng(-80.0, 90.0) << LatLng(80.0, 90.0))); - ASSERT_LESS_THAN(bigPoly.GetArea(), 2 * M_PI); - - // Vertex point and collinear point - S2Point point = LatLng(80.0, 0.0).ToPoint(); - S2Point collinearPoint = LatLng(0.0, 0.0).ToPoint(); - - // Polygon shares one edge - S2Polygon poly(loopVec(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) - << LatLng(-80.0, -10.0) << LatLng(80.0, -10.0))); - // Polygon shares a segment of one edge - S2Polygon collinearPoly(loopVec(points() << LatLng(50.0, 0.0) << LatLng(-50.0, 0.0) - << LatLng(-50.0, -10.0) << LatLng(50.0, -10.0))); - - // Line - S2Polyline line(pointVec(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) - << LatLng(-80.0, -10.0))); - // Line share a segment of one edge - S2Polyline collinearLine(pointVec(points() << LatLng(50.0, 0.0) << LatLng(-50.0, 0.0) - << LatLng(-50.0, -10.0))); - - // Big polygon larger than a hemisphere. - BigSimplePolygon expandedBigPoly(loop(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) - << LatLng(-80.0, 90.0) - << LatLng(-80.0, 180.0) - << LatLng(-80.0, -90.0) - << LatLng(80.0, -90.0) << LatLng(80.0, 180.0) - << LatLng(80.0, 90.0))); - ASSERT_GREATER_THAN(expandedBigPoly.GetArea(), 2 * M_PI); - - checkConsistency(bigPoly, expandedBigPoly, point); - checkConsistency(bigPoly, expandedBigPoly, collinearPoint); - checkConsistency(bigPoly, expandedBigPoly, poly); - checkConsistency(bigPoly, expandedBigPoly, collinearPoly); - checkConsistency(bigPoly, expandedBigPoly, line); - checkConsistency(bigPoly, expandedBigPoly, collinearLine); - - // Check the complement of big polygon - bigPoly.Invert(); - ASSERT_GREATER_THAN(bigPoly.GetArea(), 2 * M_PI); - expandedBigPoly.Invert(); - ASSERT_LESS_THAN(expandedBigPoly.GetArea(), 2 * M_PI); - - checkConsistency(bigPoly, expandedBigPoly, point); - checkConsistency(bigPoly, expandedBigPoly, collinearPoint); - checkConsistency(bigPoly, expandedBigPoly, poly); - checkConsistency(bigPoly, expandedBigPoly, collinearPoly); - checkConsistency(bigPoly, expandedBigPoly, line); - checkConsistency(bigPoly, expandedBigPoly, collinearLine); - } +// Edge cases +// +// No promise in terms of points on border - they may be inside or outside the big polygon. +// But we need to ensure the result is consistent: +// 1. If a polygon/line is contained by a big polygon, they must intersect with each other. +// 2. Relation doesn't change as long as the touch point doesn't change, no matter the big +// polygon is larger or less then a hemisphere. +// 3. Relations for big polygons less than a hemisphere are consistent with ordinary (simple) +// polygon results. + +template <typename TShape> +void checkConsistency(const BigSimplePolygon& bigPoly, + const BigSimplePolygon& expandedBigPoly, + const TShape& shape) { + // Contain() => Intersects() + if (bigPoly.Contains(shape)) + ASSERT(bigPoly.Intersects(shape)); + if (expandedBigPoly.Contains(shape)) + ASSERT(expandedBigPoly.Intersects(shape)); + // Relation doesn't change + ASSERT_EQUALS(bigPoly.Contains(shape), expandedBigPoly.Contains(shape)); + ASSERT_EQUALS(bigPoly.Intersects(shape), expandedBigPoly.Intersects(shape)); +} - // Polygon/line shares big polygon's edge (contained by big polygon) - TEST(BigSimplePolygon, ShareEdgeContained) { - // Big polygon smaller than a hemisphere. - BigSimplePolygon bigPoly(loop(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) - << LatLng(-80.0, 90.0) << LatLng(80.0, 90.0))); - ASSERT_LESS_THAN(bigPoly.GetArea(), 2 * M_PI); - - // Polygon - S2Polygon poly(loopVec(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) - << LatLng(-80.0, 10.0) << LatLng(80.0, 10.0))); - // Polygon shares a segment of one edge - S2Polygon collinearPoly(loopVec(points() << LatLng(50.0, 0.0) << LatLng(-50.0, 0.0) - << LatLng(-50.0, 10.0) << LatLng(50.0, 10.0))); - // Line - S2Polyline line(pointVec(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) - << LatLng(0.0, 10.0))); - // Line shares a segment of one edge - S2Polyline collinearLine(pointVec(points() << LatLng(50.0, 0.0) << LatLng(-50.0, 0.0) - << LatLng(-50.0, 10.0))); - - // Big polygon larger than a hemisphere. - BigSimplePolygon expandedBigPoly(loop(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) - << LatLng(-80.0, 90.0) - << LatLng(-80.0, 180.0) - << LatLng(-80.0, -90.0) - << LatLng(80.0, -90.0) << LatLng(80.0, 180.0) - << LatLng(80.0, 90.0))); - ASSERT_GREATER_THAN(expandedBigPoly.GetArea(), 2 * M_PI); - - checkConsistency(bigPoly, expandedBigPoly, poly); - checkConsistency(bigPoly, expandedBigPoly, collinearPoly); - checkConsistency(bigPoly, expandedBigPoly, line); - checkConsistency(bigPoly, expandedBigPoly, collinearLine); - - // Check the complement of big polygon - bigPoly.Invert(); - ASSERT_GREATER_THAN(bigPoly.GetArea(), 2 * M_PI); - expandedBigPoly.Invert(); - ASSERT_LESS_THAN(expandedBigPoly.GetArea(), 2 * M_PI); - - checkConsistency(bigPoly, expandedBigPoly, poly); - checkConsistency(bigPoly, expandedBigPoly, collinearPoly); - checkConsistency(bigPoly, expandedBigPoly, line); - checkConsistency(bigPoly, expandedBigPoly, collinearLine); - } +// Polygon shares big polygon's edge (disjoint) +TEST(BigSimplePolygon, ShareEdgeDisjoint) { + // Big polygon smaller than a hemisphere. + BigSimplePolygon bigPoly(loop(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) + << LatLng(-80.0, 90.0) << LatLng(80.0, 90.0))); + ASSERT_LESS_THAN(bigPoly.GetArea(), 2 * M_PI); + + // Vertex point and collinear point + S2Point point = LatLng(80.0, 0.0).ToPoint(); + S2Point collinearPoint = LatLng(0.0, 0.0).ToPoint(); + + // Polygon shares one edge + S2Polygon poly(loopVec(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) + << LatLng(-80.0, -10.0) << LatLng(80.0, -10.0))); + // Polygon shares a segment of one edge + S2Polygon collinearPoly(loopVec(points() << LatLng(50.0, 0.0) << LatLng(-50.0, 0.0) + << LatLng(-50.0, -10.0) << LatLng(50.0, -10.0))); + + // Line + S2Polyline line( + pointVec(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) << LatLng(-80.0, -10.0))); + // Line share a segment of one edge + S2Polyline collinearLine( + pointVec(points() << LatLng(50.0, 0.0) << LatLng(-50.0, 0.0) << LatLng(-50.0, -10.0))); + + // Big polygon larger than a hemisphere. + BigSimplePolygon expandedBigPoly(loop(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) + << LatLng(-80.0, 90.0) << LatLng(-80.0, 180.0) + << LatLng(-80.0, -90.0) << LatLng(80.0, -90.0) + << LatLng(80.0, 180.0) << LatLng(80.0, 90.0))); + ASSERT_GREATER_THAN(expandedBigPoly.GetArea(), 2 * M_PI); + + checkConsistency(bigPoly, expandedBigPoly, point); + checkConsistency(bigPoly, expandedBigPoly, collinearPoint); + checkConsistency(bigPoly, expandedBigPoly, poly); + checkConsistency(bigPoly, expandedBigPoly, collinearPoly); + checkConsistency(bigPoly, expandedBigPoly, line); + checkConsistency(bigPoly, expandedBigPoly, collinearLine); + + // Check the complement of big polygon + bigPoly.Invert(); + ASSERT_GREATER_THAN(bigPoly.GetArea(), 2 * M_PI); + expandedBigPoly.Invert(); + ASSERT_LESS_THAN(expandedBigPoly.GetArea(), 2 * M_PI); + + checkConsistency(bigPoly, expandedBigPoly, point); + checkConsistency(bigPoly, expandedBigPoly, collinearPoint); + checkConsistency(bigPoly, expandedBigPoly, poly); + checkConsistency(bigPoly, expandedBigPoly, collinearPoly); + checkConsistency(bigPoly, expandedBigPoly, line); + checkConsistency(bigPoly, expandedBigPoly, collinearLine); +} +// Polygon/line shares big polygon's edge (contained by big polygon) +TEST(BigSimplePolygon, ShareEdgeContained) { + // Big polygon smaller than a hemisphere. + BigSimplePolygon bigPoly(loop(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) + << LatLng(-80.0, 90.0) << LatLng(80.0, 90.0))); + ASSERT_LESS_THAN(bigPoly.GetArea(), 2 * M_PI); + + // Polygon + S2Polygon poly(loopVec(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) + << LatLng(-80.0, 10.0) << LatLng(80.0, 10.0))); + // Polygon shares a segment of one edge + S2Polygon collinearPoly(loopVec(points() << LatLng(50.0, 0.0) << LatLng(-50.0, 0.0) + << LatLng(-50.0, 10.0) << LatLng(50.0, 10.0))); + // Line + S2Polyline line( + pointVec(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) << LatLng(0.0, 10.0))); + // Line shares a segment of one edge + S2Polyline collinearLine( + pointVec(points() << LatLng(50.0, 0.0) << LatLng(-50.0, 0.0) << LatLng(-50.0, 10.0))); + + // Big polygon larger than a hemisphere. + BigSimplePolygon expandedBigPoly(loop(points() << LatLng(80.0, 0.0) << LatLng(-80.0, 0.0) + << LatLng(-80.0, 90.0) << LatLng(-80.0, 180.0) + << LatLng(-80.0, -90.0) << LatLng(80.0, -90.0) + << LatLng(80.0, 180.0) << LatLng(80.0, 90.0))); + ASSERT_GREATER_THAN(expandedBigPoly.GetArea(), 2 * M_PI); + + checkConsistency(bigPoly, expandedBigPoly, poly); + checkConsistency(bigPoly, expandedBigPoly, collinearPoly); + checkConsistency(bigPoly, expandedBigPoly, line); + checkConsistency(bigPoly, expandedBigPoly, collinearLine); + + // Check the complement of big polygon + bigPoly.Invert(); + ASSERT_GREATER_THAN(bigPoly.GetArea(), 2 * M_PI); + expandedBigPoly.Invert(); + ASSERT_LESS_THAN(expandedBigPoly.GetArea(), 2 * M_PI); + + checkConsistency(bigPoly, expandedBigPoly, poly); + checkConsistency(bigPoly, expandedBigPoly, collinearPoly); + checkConsistency(bigPoly, expandedBigPoly, line); + checkConsistency(bigPoly, expandedBigPoly, collinearLine); +} } |