summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/geo_s2holesameasshell.js31
-rw-r--r--jstests/geo_s2intersection.js141
-rw-r--r--jstests/geo_s2meridian.js108
-rw-r--r--jstests/geo_s2nearComplex.js237
-rw-r--r--jstests/geo_s2oddshapes.js144
-rw-r--r--jstests/geo_s2overlappingpolys.js213
-rwxr-xr-xjstests/geo_s2polywithholes.js51
-rw-r--r--jstests/geo_s2selfintersectingpoly.js12
8 files changed, 931 insertions, 6 deletions
diff --git a/jstests/geo_s2holesameasshell.js b/jstests/geo_s2holesameasshell.js
index e40a32c5214..1605684828e 100644
--- a/jstests/geo_s2holesameasshell.js
+++ b/jstests/geo_s2holesameasshell.js
@@ -1,10 +1,10 @@
-t = db.geo_s2holessameasshell
+var t = db.geo_s2holessameasshell
t.drop();
t.ensureIndex({geo: "2dsphere"});
-centerPoint = {"type": "Point", "coordinates": [0.5, 0.5]};
-edgePoint = {"type": "Point", "coordinates": [0, 0.5]};
-cornerPoint = {"type": "Point", "coordinates": [0, 0]};
+var centerPoint = {"type": "Point", "coordinates": [0.5, 0.5]};
+var edgePoint = {"type": "Point", "coordinates": [0, 0.5]};
+var cornerPoint = {"type": "Point", "coordinates": [0, 0]};
// Various "edge" cases. None of them should be returned by the non-polygon
// polygon below.
@@ -13,7 +13,7 @@ t.insert({geo : edgePoint});
t.insert({geo : cornerPoint});
// This generates an empty covering.
-polygonWithFullHole = { "type" : "Polygon", "coordinates": [
+var polygonWithFullHole = { "type" : "Polygon", "coordinates": [
[[0,0], [0,1], [1, 1], [1, 0], [0, 0]],
[[0,0], [0,1], [1, 1], [1, 0], [0, 0]]
]
@@ -24,5 +24,24 @@ t.insert({geo: polygonWithFullHole})
assert(db.getLastError())
// No covering to search over should give an empty result set.
-res = t.find({geo: {$within: {$geometry: polygonWithFullHole}}});
+var res = t.find({geo: {$within: {$geometry: polygonWithFullHole}}});
assert.eq(res.count(), 0)
+
+
+// Similar polygon to the one above, but is covered by two holes instead of
+// one.
+var polygonWithTwoHolesCoveringWholeArea = {"type" : "Polygon", "coordinates": [
+ [[0,0], [0,1], [1, 1], [1, 0], [0, 0]],
+ [[0,0], [0,0.5], [1, 0.5], [1, 0], [0, 0]],
+ [[0,0.5], [0,1], [1, 1], [1, 0.5], [0, 0.5]]
+ ]
+};
+
+// No keys for insert should error.
+t.insert({geo: polygonWithTwoHolesCoveringWholeArea});
+assert(db.getLastError());
+
+// No covering to search over should give an empty result set.
+res = t.find({geo: {$within: {$geometry: ppolygonWithTwoHolesCoveringWholeArea}}});
+assert.eq(res.count(), 0);
+
diff --git a/jstests/geo_s2intersection.js b/jstests/geo_s2intersection.js
new file mode 100644
index 00000000000..42abacca98d
--- /dev/null
+++ b/jstests/geo_s2intersection.js
@@ -0,0 +1,141 @@
+var t = db.geo_s2intersectinglines
+t.drop()
+t.ensureIndex( { geo : "2dsphere" } );
+
+/* All the tests in this file are generally confirming intersections based upon
+ * these three geo objects.
+ */
+var canonLine = {
+ name: 'canonLine',
+ geo: {
+ type: "LineString",
+ coordinates: [[0.0, 0.0], [1.0, 0.0]]
+ }
+};
+
+var canonPoint = {
+ name: 'canonPoint',
+ geo: {
+ type: "Point",
+ coordinates: [10.0, 10.0]
+ }
+};
+
+var canonPoly = {
+ name: 'canonPoly',
+ geo: {
+ type: "Polygon",
+ coordinates: [
+ [[50.0, 50.0], [51.0, 50.0], [51.0, 51.0], [50.0, 51.0], [50.0, 50.0]]
+ ]
+ }
+};
+
+t.insert(canonLine);
+t.insert(canonPoint);
+t.insert(canonPoly);
+
+
+//Case 1: Basic sanity intersection.
+var testLine = {type: "LineString",
+ coordinates: [[0.5, 0.5], [0.5, -0.5]]};
+
+var result = t.find({geo: {$geoIntersects: {$geometry: testLine}}});
+assert.eq(result.count(), 1);
+assert.eq(result[0]['name'], 'canonLine');
+
+
+//Case 2: Basic Polygon intersection.
+// we expect that the canonLine should intersect with this polygon.
+var testPoly = {type: "Polygon",
+ coordinates: [
+ [[0.4, -0.1],[0.4, 0.1], [0.6, 0.1], [0.6, -0.1], [0.4, -0.1]]
+ ]}
+
+result = t.find({geo: {$geoIntersects: {$geometry: testPoly}}});
+assert.eq(result.count(), 1);
+assert.eq(result[0]['name'], 'canonLine');
+
+
+//Case 3: Intersects the vertex of a line.
+// When a line intersects the vertex of a line, we expect this to
+// count as a geoIntersection.
+testLine = {type: "LineString",
+ coordinates: [[0.0, 0.5], [0.0, -0.5]]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: testLine}}});
+assert.eq(result.count(), 1);
+assert.eq(result[0]['name'], 'canonLine');
+
+// Case 4: Sanity no intersection.
+// This line just misses the canonLine in the negative direction. This
+// should not count as a geoIntersection.
+testLine = {type: "LineString",
+ coordinates: [[-0.1, 0.5], [-0.1, -0.5]]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: testLine}}});
+assert.eq(result.count(), 0);
+
+
+// Case 5: Overlapping line - only partially overlaps.
+// Undefined behaviour: does intersect
+testLine = {type: "LineString",
+ coordinates: [[-0.5, 0.0], [0.5, 0.0]]};
+
+var result = t.find({geo: {$geoIntersects: {$geometry: testLine}}});
+assert.eq(result.count(), 1);
+assert.eq(result[0]['name'], 'canonLine');
+
+
+// Case 6: Contained line - this line is fully contained by the canonLine
+// Undefined behaviour: doesn't intersect.
+testLine = {type: "LineString",
+ coordinates: [[0.1, 0.0], [0.9, 0.0]]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: testLine}}});
+assert.eq(result.count(), 0);
+
+// Case 7: Identical line in the identical position.
+// Undefined behaviour: does intersect.
+testLine = {type: "LineString",
+ coordinates: [[0.0, 0.0], [1.0, 0.0]]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: testLine}}});
+assert.eq(result.count(), 1);
+assert.eq(result[0]['name'], 'canonLine');
+
+// Case 8: Point intersection - we search with a line that intersects
+// with the canonPoint.
+testLine = {type: "LineString",
+ coordinates: [[10.0, 11.0], [10.0, 9.0]]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: testLine}}});
+assert.eq(result.count(), 1);
+assert.eq(result[0]['name'], 'canonPoint');
+
+// Case 9: Point point intersection
+// as above but with an identical point to the canonPoint. We expect an
+// intersection here.
+testPoint = {type: "Point",
+ coordinates: [10.0, 10.0]}
+
+result = t.find({geo: {$geoIntersects: {$geometry: testPoint}}});
+assert.eq(result.count(), 1);
+assert.eq(result[0]['name'], 'canonPoint');
+
+
+//Case 10: Sanity point non-intersection.
+var testPoint = {type: "Point",
+ coordinates: [12.0, 12.0]}
+
+result = t.find({geo: {$geoIntersects: {$geometry: testPoint}}});
+assert.eq(result.count(), 0);
+
+// Case 11: Point polygon intersection
+// verify that a point inside a polygon $geoIntersects.
+testPoint = {type: "Point",
+ coordinates: [50.5, 50.5]}
+
+result = t.find({geo: {$geoIntersects: {$geometry: testPoint}}});
+assert.eq(result.count(), 1);
+assert.eq(result[0]['name'], 'canonPoly');
diff --git a/jstests/geo_s2meridian.js b/jstests/geo_s2meridian.js
new file mode 100644
index 00000000000..0114f36505e
--- /dev/null
+++ b/jstests/geo_s2meridian.js
@@ -0,0 +1,108 @@
+t = db.geo_s2meridian;
+t.drop();
+t.ensureIndex({geo: "2dsphere"});
+
+/*
+ * Test 1: check that intersection works on the meridian. We insert a line
+ * that crosses the meridian, and then run a geoIntersect with a line
+ * that runs along the meridian.
+ */
+
+meridianCrossingLine = {
+ geo: {
+ type: "LineString",
+ coordinates: [
+ [-178.0, 10.0],
+ [178.0, 10.0]]
+ }
+};
+
+t.insert(meridianCrossingLine);
+assert(! db.getLastError());
+
+lineAlongMeridian = {
+ type: "LineString",
+ coordinates: [
+ [180.0, 11.0],
+ [180.0, 9.0]
+ ]
+}
+
+result = t.find({geo: {$geoIntersects: {$geometry: lineAlongMeridian}}});
+assert.eq(result.count(), 1);
+
+t.drop();
+t.ensureIndex({geo: "2dsphere"});
+/*
+ * Test 2: check that within work across the meridian. We insert points
+ * on the meridian, and immediately on either side, and confirm that a poly
+ * covering all of them returns them all.
+ */
+pointOnNegativeSideOfMeridian = {
+ geo: {
+ type: "Point",
+ coordinates: [-179.0, 1.0]
+ }
+};
+pointOnMeridian = {
+ geo: {
+ type: "Point",
+ coordinates: [180.0, 1.0]
+ }
+};
+pointOnPositiveSideOfMeridian = {
+ geo: {
+ type: "Point",
+ coordinates: [179.0, 1.0]
+ }
+};
+
+t.insert(pointOnMeridian);
+t.insert(pointOnNegativeSideOfMeridian);
+t.insert(pointOnPositiveSideOfMeridian);
+
+meridianCrossingPoly = {
+ type: "Polygon",
+ coordinates: [
+ [[-178.0, 10.0], [178.0, 10.0], [178.0, -10.0], [-178.0, -10.0], [-178.0, 10.0]]
+ ]
+};
+
+result = t.find({geo: {$within: {$geometry: meridianCrossingPoly}}});
+assert.eq(result.count(), 3);
+
+t.drop();
+t.ensureIndex({geo: "2dsphere"});
+/*
+ * Test 3: Check that near works around the meridian. Insert two points, one
+ * closer, but across the meridian, and confirm they both come back, and
+ * that the order is correct.
+ */
+pointOnNegativeSideOfMerid = {
+ name: "closer",
+ geo: {
+ type: "Point",
+ coordinates: [-179.0, 0.0]
+ }
+};
+
+pointOnPositiveSideOfMerid = {
+ name: "farther",
+ geo: {
+ type: "Point",
+ coordinates: [176.0, 0.0]
+ }
+};
+
+t.insert(pointOnNegativeSideOfMerid);
+t.insert(pointOnPositiveSideOfMerid);
+
+pointOnPositiveSideOfMeridian = {
+ type: "Point",
+ coordinates: [179.0, 0.0]
+};
+
+result = t.find({geo: {$near: pointOnPositiveSideOfMeridian}});
+assert.eq(result.count(), 2);
+assert.eq(result[0].name, "closer");
+assert.eq(result[1].name, "farther");
diff --git a/jstests/geo_s2nearComplex.js b/jstests/geo_s2nearComplex.js
new file mode 100644
index 00000000000..61bb5551fb8
--- /dev/null
+++ b/jstests/geo_s2nearComplex.js
@@ -0,0 +1,237 @@
+var t = db.get_s2nearcomplex
+t.drop()
+t.ensureIndex({geo: "2dsphere"})
+
+/* Short names for math operations */
+var random = Math.random;
+var PI = Math.PI;
+var asin = Math.asin;
+var sin = Math.sin;
+var cos = Math.cos;
+var atan2 = Math.atan2
+
+
+var originGeo = {type: "Point", coordinates: [20.0, 20.0]};
+// Center point for all tests.
+var origin = {
+ name: "origin",
+ geo: originGeo
+}
+
+
+/*
+ * Convenience function for checking that coordinates match. threshold let's you
+ * specify how accurate equals should be.
+ */
+function coordinateEqual(first, second, threshold){
+ threshold = threshold || 0.001
+ first = first['geo']['coordinates']
+ second = second['geo']['coordinates']
+ if(Math.abs(first[0] - second[0]) <= threshold){
+ if(Math.abs(first[1] - second[1]) <= threshold){
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Creates `count` random and uniformly distributed points centered around `origin`
+ * no points will be closer to origin than minDist, and no points will be further
+ * than maxDist. Points will be inserted into the global `t` collection, and will
+ * be returned.
+ * based on this algorithm: http://williams.best.vwh.net/avform.htm#LL
+ */
+function uniformPoints(origin, count, minDist, maxDist){
+ var i;
+ var lng = origin['geo']['coordinates'][0];
+ var lat = origin['geo']['coordinates'][1];
+ var distances = [];
+ var points = [];
+ for(i=0; i < count; i++){
+ distances.push((random() * (maxDist - minDist)) + minDist);
+ }
+ distances.sort();
+ while(points.length < count){
+ var angle = random() * 2 * PI;
+ var distance = distances[points.length];
+ var pointLat = asin((sin(lat) * cos(distance)) + (cos(lat) * sin(distance) * cos(angle)));
+ var pointDLng = atan2(sin(angle) * sin(distance) * cos(lat), cos(distance) - sin(lat) * sin(pointLat));
+ var pointLng = ((lng - pointDLng + PI) % 2*PI) - PI;
+ var newPoint = {
+ geo: {
+ type: "Point",
+ coordinates: [lng + pointLng, lat + pointLat]
+ }
+ };
+ if(lat + pointLat > 90.0){
+ continue;
+ }
+ points.push(newPoint);
+ }
+ for(i=0; i < points.length; i++){
+ t.insert(points[i]);
+ assert(!db.getLastError());
+ }
+ return points;
+}
+
+/*
+ * Creates a random uniform field as above, excepting for `numberOfHoles` gaps that
+ * have `sizeOfHoles` points missing centered around a random point.
+ */
+function uniformPointsWithGaps(origin, count, minDist, maxDist, numberOfHoles, sizeOfHoles){
+ var points = uniformPoints(origin, count, minDist, maxDist);
+ var i;
+ for(i=0; i<numberOfHoles; i++){
+ var randomPoint = points[Math.floor(random() * points.length)];
+ removeNearest(randomPoint, sizeOfHoles);
+ }
+}
+
+/*
+ * Creates a random uniform field as above, expcepting for `numberOfClusters` clusters,
+ * which will consist of N points where `minClusterSize` <= N <= `maxClusterSize.
+ * you may specify an optional `distRatio` parameter which will specify the area that the cluster
+ * covers as a fraction of the full area that points are created on. Defaults to 10.
+ */
+function uniformPointsWithClusters(origin, count, minDist, maxDist, numberOfClusters, minClusterSize, maxClusterSize, distRatio){
+ distRatio = distRatio || 10
+ var points = uniformPoints(origin, count, minDist, maxDist);
+ for(j=0; j<numberOfClusters; j++){
+ var randomPoint = points[Math.floor(random() * points.length)];
+ var clusterSize = (random() * (maxClusterSize - minClusterSize)) + minClusterSize;
+ uniformPoints(randomPoint, clusterSize, minDist / distRatio, maxDist / distRatio);
+ }
+}
+/*
+ * Function used to create gaps in existing point field. Will remove the `number` nearest
+ * geo objects to the specified `point`.
+ */
+function removeNearest(point, number){
+ var pointsToRemove = t.find({geo: {$near: {$geometry: point['geo']}}}).limit(number);
+ var idsToRemove = [];
+ while(pointsToRemove.hasNext()){
+ point = pointsToRemove.next();
+ idsToRemove.push(point['_id']);
+ }
+
+ t.remove({_id: {$in: idsToRemove}});
+}
+/*
+ * Validates the ordering of the nearest results is the same no matter how many
+ * geo objects are requested. This could fail if two points have the same dist
+ * from origin, because they may not be well-ordered. If we see strange failures,
+ * we should consider that.
+ */
+function validateOrdering(query){
+ var near10 = t.find(query).limit(10);
+ var near20 = t.find(query).limit(20);
+ var near30 = t.find(query).limit(30);
+ var near40 = t.find(query).limit(40);
+
+ for(i=0;i<10;i++){
+ assert(coordinateEqual(near10[i], near20[i]));
+ assert(coordinateEqual(near10[i], near30[i]));
+ assert(coordinateEqual(near10[i], near40[i]));
+ }
+
+ for(i=0;i<20;i++){
+ assert(coordinateEqual(near20[i], near30[i]));
+ assert(coordinateEqual(near20[i], near40[i]));
+ }
+
+ for(i=0;i<30;i++){
+ assert(coordinateEqual(near30[i], near40[i]));
+ }
+}
+
+var query = {geo: {$near: {$geometry: originGeo}}};
+
+// Test a uniform distribution of 10000 points.
+uniformPoints(origin, 10000, 0.5, 1.5);
+
+validateOrdering({geo: {$near: {$geometry: originGeo}}})
+
+print("Millis for uniform:")
+print(t.find(query).explain().millis)
+print("Total points:");
+print(t.find(query).count());
+
+t.drop()
+t.ensureIndex({geo: "2dsphere"})
+// Test a uniform distribution with 5 gaps each with 10 points missing.
+uniformPointsWithGaps(origin, 10000, 1, 10.0, 5, 10);
+
+validateOrdering({geo: {$near: {$geometry: originGeo}}})
+
+print("Millis for uniform with gaps:")
+print(t.find(query).explain().millis)
+print("Total points:");
+print(t.find(query).count());
+
+t.drop()
+t.ensureIndex({geo: "2dsphere"})
+
+// Test a uniform distribution with 5 clusters each with between 10 and 100 points.
+uniformPointsWithClusters(origin, 10000, 1, 10.0, 5, 10, 100);
+
+validateOrdering({geo: {$near: {$geometry: originGeo}}})
+
+print("Millis for uniform with clusters:");
+print(t.find(query).explain().millis);
+print("Total points:");
+print(t.find(query).count());
+
+t.drop()
+t.ensureIndex({geo: "2dsphere"})
+
+// Test a uniform near search with origin around the pole.
+
+// Center point near pole.
+originGeo = {type: "Point", coordinates: [0.0, 89.0]};
+origin = {
+ name: "origin",
+ geo: originGeo
+}
+uniformPoints(origin, 50, 0.5, 1.5);
+
+validateOrdering({geo: {$near: {$geometry: originGeo}}})
+
+print("Millis for uniform near pole:")
+print(t.find({geo: {$near: {$geometry: originGeo}}}).explain().millis)
+assert.eq(t.find({geo: {$near: {$geometry: originGeo}}}).count(), 50);
+
+t.drop()
+t.ensureIndex({geo: "2dsphere"})
+
+// Center point near the meridian
+originGeo = {type: "Point", coordinates: [179.0, 0.0]};
+origin = {
+ name: "origin",
+ geo: originGeo
+}
+uniformPoints(origin, 50, 0.5, 1.5);
+
+validateOrdering({geo: {$near: {$geometry: originGeo}}})
+
+print("Millis for uniform on meridian:")
+print(t.find({geo: {$near: {$geometry: originGeo}}}).explain().millis)
+assert.eq(t.find({geo: {$near: {$geometry: originGeo}}}).count(), 50);
+
+t.drop()
+t.ensureIndex({geo: "2dsphere"})
+
+// Center point near the negative meridian
+originGeo = {type: "Point", coordinates: [-179.0, 0.0]};
+origin = {
+ name: "origin",
+ geo: originGeo
+}
+uniformPoints(origin, 50, 0.5, 1.5);
+
+validateOrdering({geo: {$near: {$geometry: originGeo}}})
+
+print("Millis for uniform on negative meridian:");
+print(t.find({geo: {$near: {$geometry: originGeo}}}).explain().millis);
+assert.eq(t.find({geo: {$near: {$geometry: originGeo}}}).count(), 50);
diff --git a/jstests/geo_s2oddshapes.js b/jstests/geo_s2oddshapes.js
new file mode 100644
index 00000000000..1b975f996de
--- /dev/null
+++ b/jstests/geo_s2oddshapes.js
@@ -0,0 +1,144 @@
+var t = db.geo_s2oddshapes
+t.drop()
+t.ensureIndex( { geo : "2dsphere" } );
+
+var testPoint = {
+ name: "origin",
+ geo: {
+ type: "Point",
+ coordinates: [0.0, 0.0]
+ }
+};
+
+var testHorizLine = {
+ name: "horiz",
+ geo: {
+ type: "LineString",
+ coordinates: [[-2.0, 10.0], [2.0, 10.0]]
+ }
+};
+
+var testVertLine = {
+ name: "vert",
+ geo: {
+ type: "LineString",
+ coordinates: [[10.0, -2.0], [10.0, 2.0]]
+ }
+};
+
+t.insert(testPoint);
+t.insert(testHorizLine);
+t.insert(testVertLine);
+
+//Test a poly that runs vertically all the way along the meridian.
+
+var tallPoly = {type: "Polygon",
+ coordinates: [
+ [[1.0, 89.0], [-1.0, 89.0], [-1.0, -89.0], [1.0, -89.0], [1.0, 89.0]]
+ ]};
+//We expect that the testPoint (at the origin) will be within this poly.
+var result = t.find({geo: {$within: {$geometry: tallPoly}}});
+assert.eq(result.count(), 1);
+assert.eq(result[0].name, 'origin');
+
+//We expect that the testPoint, and the testHorizLine should geoIntersect
+//with this poly.
+result = t.find({geo: {$geoIntersects: {$geometry: tallPoly}}});
+assert.eq(result.count(), 2);
+assert.eq(result[0].name, 'horiz');
+assert.eq(result[1].name, 'origin');
+
+//Test a poly that runs horizontally along the equator.
+
+var longPoly = {type: "Polygon",
+ coordinates: [
+ [[89.0, 1.0], [-89.0, 1.0], [-89.0, -1.0], [89.0, -1.0], [89.0, 1.0]]
+ ]};
+
+//We expect that the testPoint (at the origin) will be within this poly.
+/*
+ * Tests commented out because they are currently failing. There is a
+ * bug tracked https://jira.mongodb.org/browse/SERVER-8180 .
+ */
+/*
+result = t.find({geo: {$within: {$geometry: longPoly}}});
+assert.eq(result.count(), 1);
+assert.eq(result[0].name, 'origin');
+
+//We expect that the testPoint, and the testVertLine should geoIntersect
+//with this poly.
+result = t.find({geo: {$geoIntersects: {$geometry: longPoly}}});
+assert.eq(result.count(), 2);
+assert.eq(result[0].name, 'vert');
+assert.eq(result[1].name, 'origin');*/
+
+//Test a poly that is the size of half the earth.
+
+t.drop()
+t.ensureIndex( { geo : "2dsphere" } );
+
+var insidePoint = {
+ name: "inside",
+ geo: {
+ type: "Point",
+ name: "inside",
+ coordinates: [100.0, 0.0]
+ }
+};
+
+var outsidePoint = {
+ name: "inside",
+ geo: {
+ type: "Point",
+ name: "inside",
+ coordinates: [-100.0, 0.0]
+ }
+};
+
+t.insert(insidePoint);
+t.insert(outsidePoint);
+
+var largePoly = {type: "Polygon",
+ coordinates: [
+ [[0.0, -90.0], [0.0, 90.0], [180.0, 0], [0.0, -90.0]]
+ ]};
+
+result = t.find({geo: {$within: {$geometry: largePoly}}});
+assert.eq(result.count(), 1);
+var point = result[0]
+assert.eq(point.name, 'inside');
+
+//Test a poly that is very small. A couple meters around.
+
+t.drop()
+t.ensureIndex( { geo : "2dsphere" } );
+
+insidePoint = {
+ name: "inside",
+ geo: {
+ type: "Point",
+ name: "inside",
+ coordinates: [0.01, 0.0]
+ }};
+
+outsidePoint = {
+ name: "inside",
+ geo: {
+ type: "Point",
+ name: "inside",
+ coordinates: [0.2, 0.0]
+ }};
+
+t.insert(insidePoint);
+t.insert(outsidePoint);
+
+smallPoly = {type: "Polygon",
+ coordinates: [
+ [[0.0, -0.01], [0.015, -0.01], [0.015, 0.01], [0.0, 0.01], [0.0, -0.01]]
+ ]};
+
+result = t.find({geo: {$within: {$geometry: smallPoly}}});
+assert.eq(result.count(), 1);
+point = result[0]
+assert.eq(point.name, 'inside');
+
diff --git a/jstests/geo_s2overlappingpolys.js b/jstests/geo_s2overlappingpolys.js
new file mode 100644
index 00000000000..197792a1f03
--- /dev/null
+++ b/jstests/geo_s2overlappingpolys.js
@@ -0,0 +1,213 @@
+var t = db.geo_s2overlappingpolys
+t.drop()
+
+t.ensureIndex( { geo : "2dsphere" } );
+
+var minError = 0.8e-13;
+
+var canonPoly = {type: "Polygon",
+ coordinates: [
+ [[-1.0, -1.0], [1.0, -1.0], [1.0, 1.0], [-1.0, 1.0], [-1.0, -1.0]]
+ ]};
+t.insert({geo: canonPoly});
+
+// Test 1: If a poly completely encloses the canonPoly, we expect the canonPoly
+// to be returned for both $within and $geoIntersect
+
+var outerPoly = {type: "Polygon",
+ coordinates: [
+ [[-2.0, -2.0], [2.0, -2.0], [2.0, 2.0], [-2.0, 2.0], [-2.0, -2.0]]
+ ]};
+var result = t.find({geo: {$within: {$geometry: outerPoly}}});
+assert.eq(result.count(), 1);
+result = t.find({geo: {$geoIntersects: {$geometry: outerPoly}}});
+assert.eq(result.count(), 1);
+
+
+// Test 2: If a poly that covers half of the canonPoly, we expect that it should
+// geoIntersect, but should not be within.
+
+var partialPoly = {type: "Polygon",
+ coordinates: [
+ [[-2.0, -2.0], [2.0, -2.0], [2.0, 0.0], [-2.0, 0.0], [-2.0, -2.0]]
+ ]};
+
+//Should not be within
+result = t.find({geo: {$within: {$geometry: partialPoly}}});
+assert.eq(result.count(), 0);
+
+//This should however count as a geoIntersect
+result = t.find({geo: {$geoIntersects: {$geometry: partialPoly}}});
+assert.eq(result.count(), 1);
+
+
+// Test 3: Polygons that intersect at a point or an edge have undefined
+// behaviour in s2 The s2 library we're using appears to have
+// the following behaviour.
+
+// Case (a): Polygons that intersect at one point (not a vertex).
+// behaviour: geoIntersects.
+
+var sharedPointPoly = {type: "Polygon",
+ coordinates: [
+ [[0.0, -2.0], [0.0, -1.0], [1.0, -2.0], [0.0, -2.0]]
+ ]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: sharedPointPoly}}});
+assert.eq(result.count(), 1);
+
+// Case (b): Polygons that intersect at one point (a vertex).
+// behaviour: not geoIntersect
+
+var sharedVertexPoly = {type: "Polygon",
+ coordinates: [
+ [[0.0, -2.0], [1.0, -1.0], [1.0, -2.0], [0.0, -2.0]]
+ ]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: sharedVertexPoly}}});
+assert.eq(result.count(), 0);
+
+// Case (c): Polygons that intesersect at one point that is very close to a
+// vertex should have the same behaviour as Case (b).
+
+var almostSharedVertexPoly = {type: "Polygon",
+ coordinates: [
+ [[0.0, -2.0], [1.0 - minError, -1.0], [1.0, -2.0], [0.0, -2.0]]
+ ]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: almostSharedVertexPoly}}});
+assert.eq(result.count(), 0);
+
+
+// Case (d): Polygons that intesersect at one point that is not quite as close
+// to a vertex should behave as though it were not a vertex, and should
+// geoIntersect
+
+var notCloseEnoughSharedVertexPoly = {type: "Polygon",
+ coordinates: [
+ [[0.0, -2.0], [1.0 - (10 * minError), -1.0], [1.0, -2.0], [0.0, -2.0]]
+ ]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: notCloseEnoughSharedVertexPoly}}});
+assert.eq(result.count(), 1);
+
+// Case (e): Polygons that come very close to having a point intersection
+// on a non-vertex coordinate should intersect.
+
+var almostSharedPointPoly = {type: "Polygon",
+ coordinates: [
+ [[0.0, -2.0], [0.0, (-1.0 - minError)], [1.0, -2.0], [0.0, -2.0]]
+ ]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: almostSharedPointPoly}}});
+assert.eq(result.count(), 1);
+
+
+// Case (f): If we increase the error a little, it should no longer act
+// as though it's intersecting.
+// NOTE: I think this error bound seems odd. Going to 0.000152297 will break this test.
+// I've confirmed there is an error bound, but it's a lot larger than we experienced above.
+var errorBound = 0.000152298
+var notCloseEnoughSharedPointPoly = {type: "Polygon",
+ coordinates: [
+ [[0.0, -2.0], [0.0, -1.0 - errorBound], [1.0, -2.0], [0.0, -2.0]]
+ ]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: notCloseEnoughSharedPointPoly}}});
+assert.eq(result.count(), 0);
+
+/* Test 3: Importantly, polygons with shared edges have undefined intersection
+ * under s2. Therefore these test serve more to make sure nothing changes than
+ * to confirm an expected behaviour.
+ */
+
+// Case 1: A polygon who shares an edge with another polygon, where the searching
+// polygon's edge is fully covered by the canon polygon's edge.
+// Result: No intersection.
+var fullyCoveredEdgePoly = {type: "Polygon",
+ coordinates: [
+ [[-2.0, -0.5], [-1.0, -0.5], [-1.0, 0.5], [-2.0, 0.5], [-2.0, -0.5]]
+ ]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: fullyCoveredEdgePoly}}});
+assert.eq(result.count(), 0);
+
+// Case 2: A polygon who shares an edge with another polygon, where the searching
+// polygon's edge fully covers the canon polygon's edge.
+// Result: Intersection.
+var coveringEdgePoly = {type: "Polygon",
+ coordinates: [
+ [[-2.0, -1.5], [-1.0, -1.5], [-1.0, 1.5], [-2.0, 1.5], [-2.0, -1.5]]
+ ]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: coveringEdgePoly}}});
+assert.eq(result.count(), 1);
+
+// Case 2a: same as Case 2, except pulled slightly away from the polygon.
+// Result: Intersection.
+// NOTE: Scales of errors?
+var closebyCoveringEdgePoly = {type: "Polygon",
+ coordinates: [
+ [[-2.0, -1.5], [-1.0 - (minError / 1000), -1.5], [-1.0 - (minError / 1000), 1.5], [-2.0, 1.5], [-2.0, -1.5]]
+ ]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: closebyCoveringEdgePoly}}});
+assert.eq(result.count(), 1);
+
+// Case 2b: same as Case 4, except pulled slightly away from the polygon, so that it's not intersecting.
+// Result: No Intersection.
+// NOTE: Scales of errors?
+var notCloseEnoughCoveringEdgePoly = {type: "Polygon",
+ coordinates: [
+ [[-2.0, -1.5], [-1.0 - (minError / 100), -1.5], [-1.0 - (minError / 100), 1.5], [-2.0, 1.5], [-2.0, -1.5]]
+ ]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: notCloseEnoughCoveringEdgePoly}}});
+assert.eq(result.count(), 0);
+
+// Case 3: A polygon who shares an edge with another polygon, where the searching
+// polygon's edge partially covers by the canon polygon's edge.
+// Result: No intersection.
+var partiallyCoveringEdgePoly = {type: "Polygon",
+ coordinates: [
+ [[-2.0, -1.5], [-1.0, -1.5], [-1.0, 0.5], [-2.0, 0.5], [-2.0, -1.5]]
+ ]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: partiallyCoveringEdgePoly}}});
+assert.eq(result.count(), 0);
+
+
+//Polygons that intersect at three non-co-linear points should geoIntersect
+var sharedPointsPoly = {type: "Polygon",
+ coordinates: [
+ [[0.0, -3.0], [0.0, -1.0], [2.0, -2.0], [1.0, 0.0], [2.0, 2.0], [0.0, 1.0], [0.0, 3.0], [3.0, 3.0], [3.0, -3.0], [0.0, -3.0]]
+ ]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: sharedPointsPoly}}});
+assert.eq(result.count(), 1);
+
+//If a polygon contains a hole, and another polygon is within that hole, it should not be within or intersect.
+
+var bigHolePoly = {type: "Polygon",
+ coordinates: [
+ [[-3.0, -3.0], [3.0, -3.0], [3.0, 3.0], [-3.0, 3.0], [-3.0, -3.0]],
+ [[-2.0, -2.0], [2.0, -2.0], [2.0, 2.0], [-2.0, 2.0], [-2.0, -2.0]]
+ ]};
+result = t.find({geo: {$within: {$geometry: bigHolePoly}}});
+assert.eq(result.count(), 0);
+result = t.find({geo: {$geoIntersects: {$geometry: bigHolePoly}}});
+assert.eq(result.count(), 0);
+
+// If a polygon has a hole, and another polygon is contained partially by that hole, it should be an intersection
+// but not a within.
+
+var internalOverlapPoly = {type: "Polygon",
+ coordinates: [
+ [[-3.0, -3.0], [3.0, -3.0], [3.0, 3.0], [-3.0, 3.0], [-3.0, -3.0]],
+ [[-2.0, 0.0], [2.0, 0.0], [2.0, 2.0], [-2.0, 2.0], [-2.0, 0.0]]
+ ]};
+
+result = t.find({geo: {$geoIntersects: {$geometry: internalOverlapPoly}}});
+assert.eq(result.count(), 1);
+result = t.find({geo: {$within: {$geometry: internalOverlapPoly}}});
+assert.eq(result.count(), 0);
diff --git a/jstests/geo_s2polywithholes.js b/jstests/geo_s2polywithholes.js
new file mode 100755
index 00000000000..83dec71ac26
--- /dev/null
+++ b/jstests/geo_s2polywithholes.js
@@ -0,0 +1,51 @@
+var t = db.geo_s2weirdpolys;
+t.drop();
+t.ensureIndex({geo: "2dsphere"});
+
+var centerPoint = {"type": "Point", "coordinates": [0.5, 0.5]};
+var edgePoint = {"type": "Point", "coordinates": [0, 0.5]};
+var cornerPoint = {"type": "Point", "coordinates": [0, 0]};
+
+t.insert({geo : centerPoint});
+t.insert({geo : edgePoint});
+t.insert({geo : cornerPoint});
+
+var polygonWithNoHole = {"type" : "Polygon", "coordinates": [
+ [[0,0], [0,1], [1, 1], [1, 0], [0, 0]]
+ ]
+};
+
+// Test 1: Sanity check. Expect all three points.
+var sanityResult = t.find({geo: {$within: {$geometry: polygonWithNoHole}}});
+
+assert.eq(sanityResult.count(), 3);
+
+// Test 2: Polygon with a hole that isn't contained byt the poly shell.
+var polygonWithProtrudingHole = {"type" : "Polygon", "coordinates": [
+ [[0,0], [0,1], [1, 1], [1, 0], [0, 0]],
+ [[0.4,0.9], [0.4,1.1], [0.5, 1.1], [0.5, 0.9], [0.4, 0.9]]
+ ]
+};
+
+// Bad shell, should error.
+t.insert({geo: polygonWithProtrudingHole});
+assert(db.getLastError());
+
+// When used for a within search, the shell seems to be respected, and all points
+// are returned. Since this errors on insert, I don't think it's behaviour
+// actually matters much.
+var protrudingResult = t.find({geo: {$within: {$geometry: polygonWithProtrudingHole}}});
+assert(protrudingResult, 3);
+
+// Test 3: This test will confirm that a polygon with overlapping holes throws
+// an error.
+
+var polyWithOverlappingHoles = {"type" : "Polygon", "coordinates": [
+ [[0,0], [0,1], [1, 1], [1, 0], [0, 0]],
+ [[0.2,0.6], [0.2,0.9], [0.6, 0.9], [0.6, 0.6], [0.2, 0.6]],
+ [[0.5,0.4], [0.5,0.7], [0.8, 0.7], [0.8, 0.4], [0.5, 0.4]]
+ ]
+};
+
+t.insert({geo: polyWithOverlappingHoles});
+assert(db.getLastError());
diff --git a/jstests/geo_s2selfintersectingpoly.js b/jstests/geo_s2selfintersectingpoly.js
new file mode 100644
index 00000000000..4b7e0d4eff3
--- /dev/null
+++ b/jstests/geo_s2selfintersectingpoly.js
@@ -0,0 +1,12 @@
+var t = db.geo_s2selfintersectingpoly;
+t.drop();
+t.ensureIndex({geo: "2dsphere"});
+
+var intersectingPolygon = {"type": "Polygon", "coordinates": [
+ [[0.0, 0.0], [0.0, 4.0], [-3.0, 2.0], [1.0, 2.0], [0.0, 0.0]]
+]};
+/*
+ * Self intersecting polygons should cause a parse exception.
+ */
+t.insert({geo : intersectingPolygon});
+assert(db.getLastError());