Permalink
Browse files

near only works w/points, rename intersect

  • Loading branch information...
1 parent d72a2d2 commit 03471699fc78ae9f4e05ae71f159b6e2621ebfad Hari Khalsa committed Dec 12, 2012
@@ -32,9 +32,9 @@ t.insert({geo : penguin2})
t.ensureIndex( { geo : "2dsphere", nonGeo: 1 } )
-res = t.find({ "geo" : { "$intersect" : { "$geometry" : roundworldpoint} } });
+res = t.find({ "geo" : { "$geoIntersects" : { "$geometry" : roundworldpoint} } });
assert.eq(res.count(), 2);
-res = t.find({ "geo" : { "$intersect" : { "$geometry" : santapoint} } });
+res = t.find({ "geo" : { "$geoIntersects" : { "$geometry" : santapoint} } });
assert.eq(res.count(), 2);
-res = t.find({ "geo" : { "$intersect" : { "$geometry" : penguinpoint} } });
+res = t.find({ "geo" : { "$geoIntersects" : { "$geometry" : penguinpoint} } });
assert.eq(res.count(), 2);
View
@@ -26,26 +26,26 @@ somepoly = { "type" : "Polygon",
t.insert( {geo : somepoly, nonGeo: ["somepoly"] })
t.ensureIndex( { geo : "2dsphere", nonGeo: 1 } )
-res = t.find({ "geo" : { "$intersect" : { "$geometry" : pointA} } });
+res = t.find({ "geo" : { "$geoIntersects" : { "$geometry" : pointA} } });
assert.eq(res.count(), 3);
-res = t.find({ "geo" : { "$intersect" : { "$geometry" : pointB} } });
+res = t.find({ "geo" : { "$geoIntersects" : { "$geometry" : pointB} } });
assert.eq(res.count(), 4);
-res = t.find({ "geo" : { "$intersect" : { "$geometry" : pointD} } });
+res = t.find({ "geo" : { "$geoIntersects" : { "$geometry" : pointD} } });
assert.eq(res.count(), 1);
-res = t.find({ "geo" : { "$intersect" : { "$geometry" : someline} } })
+res = t.find({ "geo" : { "$geoIntersects" : { "$geometry" : someline} } })
assert.eq(res.count(), 5);
-res = t.find({ "geo" : { "$intersect" : { "$geometry" : somepoly} } })
+res = t.find({ "geo" : { "$geoIntersects" : { "$geometry" : somepoly} } })
assert.eq(res.count(), 6);
-res = t.find({ "geo" : { "$intersect" : { "$geometry" : somepoly} } }).limit(1)
+res = t.find({ "geo" : { "$geoIntersects" : { "$geometry" : somepoly} } }).limit(1)
assert.eq(res.itcount(), 1);
res = t.find({ "nonGeo": "pointA",
- "geo" : { "$intersect" : { "$geometry" : somepoly} } })
+ "geo" : { "$geoIntersects" : { "$geometry" : somepoly} } })
assert.eq(res.count(), 1);
// Don't crash mongod if we give it bad input.
@@ -54,5 +54,18 @@ t.ensureIndex({loc: "2dsphere", x:1})
t.save({loc: [0,0]})
assert.throws(function() { return t.count({loc: {$foo:[0,0]}}) })
assert.throws(function() { return t.find({ "nonGeo": "pointA",
- "geo" : { "$intersect" : { "$geometry" : somepoly},
+ "geo" : { "$geoIntersects" : { "$geometry" : somepoly},
"$near": {"$geometry" : somepoly }}}).count()})
+
+// If we specify a datum, it has to be valid (WGS84).
+t.drop()
+t.ensureIndex({loc: "2dsphere"})
+t.insert({loc: {type:'Point', coordinates: [40, 5], crs:{ type: 'name', properties:{name:'EPSG:2000'}}}})
+assert(db.getLastError());
+assert.eq(0, t.find().itcount())
+t.insert({loc: {type:'Point', coordinates: [40, 5]}})
+assert(!db.getLastError());
+t.insert({loc: {type:'Point', coordinates: [40, 5], crs:{ type: 'name', properties:{name:'EPSG:4326'}}}})
+assert(!db.getLastError());
+t.insert({loc: {type:'Point', coordinates: [40, 5], crs:{ type: 'name', properties:{name:'urn:ogc:def:crs:OGC:1.3:CRS84'}}}})
+assert(!db.getLastError());
@@ -13,11 +13,11 @@ t.insert( {geo : {a:{x:40,y:5},b:{x:40,y:6},c:{x:41,y:6},d:{x:41,y:5}}})
t.ensureIndex( { geo : "2dsphere", nonGeo: 1 } )
-res = t.find({ "geo" : { "$intersect" : { "$geometry": {x:40, y:5}}}})
+res = t.find({ "geo" : { "$geoIntersects" : { "$geometry": {x:40, y:5}}}})
assert.eq(res.count(), 3);
-res = t.find({ "geo" : { "$intersect" : {"$geometry": [41,6]}}})
+res = t.find({ "geo" : { "$geoIntersects" : {"$geometry": [41,6]}}})
assert.eq(res.count(), 4);
-res = t.find({ "geo" : { "$intersect" : {"$geometry": [[40,5],[40,6],[41,6],[41,5]]}}})
+res = t.find({ "geo" : { "$geoIntersects" : {"$geometry": [[40,5],[40,6],[41,6],[41,5]]}}})
assert.eq(res.count(), 6);
View
@@ -2,6 +2,7 @@
t = db.geo_s2near
t.drop();
+
// FYI:
// One degree of long @ 0 is 111km or so.
// One degree of lat @ 0 is 110km or so.
@@ -18,6 +19,16 @@ origin = { "type" : "Point", "coordinates": [ lng, lat ] }
t.ensureIndex({ geo : "2dsphere" })
+// Near only works when the query is a point.
+someline = { "type" : "LineString", "coordinates": [ [ 40, 5], [41, 6]]}
+somepoly = { "type" : "Polygon",
+ "coordinates" : [ [ [40,5], [40,6], [41,6], [41,5], [40,5]]]}
+assert.throws(function() { return t.find({ "geo" : { "$near" : { "$geometry" : someline } } }).count()})
+assert.throws(function() { return t.find({ "geo" : { "$near" : { "$geometry" : somepoly } } }).count()})
+assert.throws(function() { return db.runCommand({geoNear : t.getName(), near: someline }).results.length})
+assert.throws(function() { return db.runCommand({geoNear : t.getName(), near: somepoly }).results.length})
+
+// Do some basic near searches.
res = t.find({ "geo" : { "$near" : { "$geometry" : origin, $maxDistance: 2000} } }).limit(10)
resNear = db.runCommand({geoNear : t.getName(), near: [0,0], num: 10, maxDistance: 2000})
assert.eq(res.itcount(), resNear.results.length, 10)
View
@@ -421,7 +421,7 @@ namespace mongo {
opNEAR = 0x13,
opWITHIN = 0x14,
opMAX_DISTANCE = 0x15,
- opINTERSECT = 0x16,
+ opGEO_INTERSECTS = 0x16,
};
/** add all elements of the object to the specified vector */
@@ -88,6 +88,11 @@ namespace mongo {
if (type.eoo() || (String != type.type())) { return false; }
if (GEOJSON_TYPE_POINT != type.String()) { return false; }
+ if (!crsIsOK(obj)) {
+ warning() << "Invalid CRS: " << obj.toString() << endl;
+ return false;
+ }
+
BSONElement coordElt = obj.getFieldDotted(GEOJSON_COORDINATES);
if (coordElt.eoo() || (Array != coordElt.type())) { return false; }
@@ -111,6 +116,11 @@ namespace mongo {
if (type.eoo() || (String != type.type())) { return false; }
if (GEOJSON_TYPE_LINESTRING != type.String()) { return false; }
+ if (!crsIsOK(obj)) {
+ warning() << "Invalid CRS: " << obj.toString() << endl;
+ return false;
+ }
+
BSONElement coordElt = obj.getFieldDotted(GEOJSON_COORDINATES);
if (coordElt.eoo() || (Array != coordElt.type())) { return false; }
@@ -130,6 +140,11 @@ namespace mongo {
if (type.eoo() || (String != type.type())) { return false; }
if (GEOJSON_TYPE_POLYGON != type.String()) { return false; }
+ if (!crsIsOK(obj)) {
+ warning() << "Invalid CRS: " << obj.toString() << endl;
+ return false;
+ }
+
BSONElement coordElt = obj.getFieldDotted(GEOJSON_COORDINATES);
if (coordElt.eoo() || (Array != coordElt.type())) { return false; }
@@ -305,4 +320,27 @@ namespace mongo {
bool GeoJSONParser::isPolygon(const BSONObj &obj) {
return isGeoJSONPolygon(obj) || isLegacyPolygon(obj);
}
+
+ bool GeoJSONParser::crsIsOK(const BSONObj &obj) {
+ if (!obj.hasField("crs")) { return true; }
+
+ if (!obj["crs"].isABSONObj()) { return false; }
+
+ BSONObj crsObj = obj["crs"].embeddedObject();
+ if (!crsObj.hasField("type")) { return false; }
+ if (String != crsObj["type"].type()) { return false; }
+ if ("name" != crsObj["type"].String()) { return false; }
+ if (!crsObj.hasField("properties")) { return false; }
+ if (!crsObj["properties"].isABSONObj()) { return false; }
+
+ BSONObj propertiesObj = crsObj["properties"].embeddedObject();
+ if (!propertiesObj.hasField("name")) { return false; }
+ if (String != propertiesObj["name"].type()) { return false; }
+ const string& name = propertiesObj["name"].String();
+
+ // see http://portal.opengeospatial.org/files/?artifact_id=24045
+ // and http://spatialreference.org/ref/epsg/4326/
+ // and http://www.geojson.org/geojson-spec.html#named-crs
+ return ("urn:ogc:def:crs:OGC:1.3:CRS84" == name) || ("EPSG:4326" == name);
+ }
} // namespace mongo
@@ -63,5 +63,12 @@ namespace mongo {
static bool parsePoint(const BSONObj &obj, S2Cell *out);
static bool parseLineString(const BSONObj &obj, S2Polyline *out);
static bool parsePolygon(const BSONObj &obj, S2Polygon *out);
+
+ // Return true if the CRS field is 1. missing, or 2. is well-formed and
+ // has a datum we accept. Otherwise, return false.
+ // TODO(hk): If this is ever used anywhere but internally, consider
+ // returning states: missing, invalid, unknown, ok, etc. -- whatever
+ // needed.
+ static bool crsIsOK(const BSONObj& obj);
};
} // namespace mongo
@@ -23,17 +23,20 @@
#include "mongo/db/geo/geojsonparser.h"
#include "mongo/db/json.h"
+#include "mongo/db/jsobj.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/assert_util.h"
#include "third_party/s2/s2.h"
#include "third_party/s2/s2polygon.h"
#include "third_party/s2/s2polyline.h"
-using mongo::GeoJSONParser;
+using mongo::BSONObj;
using mongo::fromjson;
+using mongo::GeoJSONParser;
namespace {
+
TEST(GeoJSONParser, isValidPoint) {
ASSERT_TRUE(GeoJSONParser::isPoint(fromjson("{'type':'Point', 'coordinates': [40, 5]}")));
ASSERT_TRUE(GeoJSONParser::isPoint(
@@ -90,6 +93,9 @@ namespace {
GeoJSONParser::parseLineString(
fromjson("{'type':'LineString', 'coordinates':[[1,2], [3,4], [5,6]]}"),
&polyline);
+ GeoJSONParser::parseLineString(
+ fromjson("{'type':'LineString', 'coordinates':[[1,2], [3,4], [5,6]]}"),
+ &polyline);
}
TEST(GeoJSONParser, parsePolygon) {
@@ -139,6 +145,42 @@ namespace {
ASSERT_FALSE(GeoJSONParser::parsePoint(fromjson("{x: 5}"), &point));
}
+ TEST(GeoJSONParser, verifyCRS) {
+ string goodCRS1 = "crs:{ type: 'name', properties:{name:'EPSG:4326'}}";
+ string goodCRS2 = "crs:{ type: 'name', properties:{name:'urn:ogc:def:crs:OGC:1.3:CRS84'}}";
+ string badCRS1 = "crs:{ type: 'name', properties:{name:'EPSG:2000'}}";
+ string badCRS2 = "crs:{ type: 'name', properties:{name:'urn:ogc:def:crs:OGC:1.3:CRS83'}}";
+
+ BSONObj point1 = fromjson("{'type':'Point', 'coordinates': [40, 5], " + goodCRS1 + "}");
+ BSONObj point2 = fromjson("{'type':'Point', 'coordinates': [40, 5], " + goodCRS2 + "}");
+ ASSERT(GeoJSONParser::isGeoJSONPoint(point1));
+ ASSERT(GeoJSONParser::crsIsOK(point1));
+ ASSERT(GeoJSONParser::isGeoJSONPoint(point2));
+ ASSERT(GeoJSONParser::crsIsOK(point2));
+ BSONObj point3 = fromjson("{'type':'Point', 'coordinates': [40, 5], " + badCRS1 + "}");
+ BSONObj point4 = fromjson("{'type':'Point', 'coordinates': [40, 5], " + badCRS2 + "}");
+ ASSERT_FALSE(GeoJSONParser::isGeoJSONPoint(point3));
+ ASSERT_FALSE(GeoJSONParser::crsIsOK(point3));
+ ASSERT_FALSE(GeoJSONParser::isGeoJSONPoint(point4));
+ ASSERT_FALSE(GeoJSONParser::crsIsOK(point4));
+
+ BSONObj polygon1 = fromjson("{'type':'Polygon', 'coordinates':[ [[0,0],[5,0],[5,5],[0,5],[0,0]],"
+ " [[1,1],[1,4],[4,4],[4,1],[1,1]] ]," + goodCRS1 + "}");
+ ASSERT(GeoJSONParser::isGeoJSONPolygon(polygon1));
+ ASSERT(GeoJSONParser::crsIsOK(polygon1));
+ BSONObj polygon2 = fromjson("{'type':'Polygon', 'coordinates':[ [[0,0],[5,0],[5,5],[0,5],[0,0]],"
+ " [[1,1],[1,4],[4,4],[4,1],[1,1]] ]," + badCRS2 + "}");
+ ASSERT_FALSE(GeoJSONParser::isGeoJSONPolygon(polygon2));
+ ASSERT_FALSE(GeoJSONParser::crsIsOK(polygon2));
+
+ BSONObj line1 = fromjson("{'type':'LineString', 'coordinates':[[1,2], [3,4], [5,6]]," + goodCRS2 + "}");
+ ASSERT(GeoJSONParser::isGeoJSONLineString(line1));
+ ASSERT(GeoJSONParser::crsIsOK(line1));
+ BSONObj line2 = fromjson("{'type':'LineString', 'coordinates':[[1,2], [3,4], [5,6]]," + badCRS1 + "}");
+ ASSERT_FALSE(GeoJSONParser::isGeoJSONLineString(line2));
+ ASSERT_FALSE(GeoJSONParser::crsIsOK(line2));
+ }
+
TEST(GeoJSONParser, parseLegacyPolygon) {
S2Polygon polygon;
ASSERT(GeoJSONParser::parsePolygon(BSON_ARRAY(BSON_ARRAY(10 << 20) << BSON_ARRAY(10 << 40)
@@ -96,11 +96,11 @@ namespace mongo {
string QueryGeometry::toString() const {
stringstream ss;
ss << "field = " << field;
- if (NULL != cell) {
+ if (NULL != cell.get()) {
ss << ", cell";
- } else if (NULL != line) {
+ } else if (NULL != line.get()) {
ss << ", line = ";
- } else if (NULL != polygon) {
+ } else if (NULL != polygon.get()) {
ss << ", polygon = ";
}
return ss.str();
@@ -109,14 +109,14 @@ namespace mongo {
bool QueryGeometry::parseFrom(const BSONObj& obj) {
if (GeoJSONParser::isPolygon(obj)) {
// We can't really pass these things around willy-nilly except by ptr.
- polygon = new S2Polygon();
- GeoJSONParser::parsePolygon(obj, polygon);
+ polygon.reset(new S2Polygon());
+ GeoJSONParser::parsePolygon(obj, polygon.get());
} else if (GeoJSONParser::isPoint(obj)) {
- cell = new S2Cell();
- GeoJSONParser::parsePoint(obj, cell);
+ cell.reset(new S2Cell());
+ GeoJSONParser::parsePoint(obj, cell.get());
} else if (GeoJSONParser::isLineString(obj)) {
- line = new S2Polyline();
- GeoJSONParser::parseLineString(obj, line);
+ line.reset(new S2Polyline());
+ GeoJSONParser::parseLineString(obj, line.get());
} else {
return false;
}
@@ -138,7 +138,7 @@ namespace mongo {
if (NULL != cell) {
return otherLine.MayIntersect(*cell);
} else if (NULL != line) {
- return otherLine.Intersects(line);
+ return otherLine.Intersects(line.get());
} else {
// TODO(hk): modify s2 library to just let us know if it intersected
// rather than returning all this.
@@ -158,12 +158,12 @@ namespace mongo {
// TODO(hk): modify s2 library to just let us know if it intersected
// rather than returning all this.
vector<S2Polyline*> clipped;
- otherPolygon.IntersectWithPolyline(line, &clipped);
+ otherPolygon.IntersectWithPolyline(line.get(), &clipped);
bool ret = clipped.size() > 0;
for (size_t i = 0; i < clipped.size(); ++i) delete clipped[i];
return ret;
} else {
- return otherPolygon.Intersects(polygon);
+ return otherPolygon.Intersects(polygon.get());
}
}
@@ -191,14 +191,4 @@ namespace mongo {
return *polygon;
}
}
-
- void QueryGeometry::free() {
- if (NULL != cell) {
- delete cell;
- } else if (NULL != line) {
- delete line;
- } else if (NULL != polygon) {
- delete polygon;
- }
- }
} // namespace mongo
@@ -36,16 +36,16 @@ namespace mongo {
// Used for passing geo data from the newCursor entry point to the S2Cursor class.
struct QueryGeometry {
- QueryGeometry(const string& f) : field(f), cell(NULL), line(NULL), polygon(NULL) {}
+ QueryGeometry(const string& f) : field(f) {} //, cell(NULL), line(NULL), polygon(NULL) {}
// Name of the field in the query.
string field;
// Only one of these should be non-NULL. S2Region is a superclass but it only supports
// testing against S2Cells. We need the most specific class we can get.
// Owned by S2Cursor.
- S2Cell *cell;
- S2Polyline *line;
- S2Polygon *polygon;
+ shared_ptr<S2Cell> cell;
+ shared_ptr<S2Polyline> line;
+ shared_ptr<S2Polygon> polygon;
string toString() const;
@@ -56,8 +56,6 @@ namespace mongo {
bool intersectsPolygon(const S2Polygon& otherPolygon);
// One region is not NULL and this returns it.
const S2Region& getRegion() const;
- // Delete the not NULL region.
- void free();
// Get the centroid, boring if we're a point, interesting if we're not.
S2Point getCentroid() const;
// Try to parse the provided object into the right place.
@@ -37,12 +37,7 @@ namespace mongo {
_matcher.reset(new CoveredIndexMatcher(_filteredQuery, keyPattern));
}
- S2Cursor::~S2Cursor() {
- // We own these pointers.
- for (size_t i = 0; i < _fields.size(); ++i) {
- _fields[i].free();
- }
- }
+ S2Cursor::~S2Cursor() { }
CoveredIndexMatcher* S2Cursor::matcher() const { return _matcher.get(); }
Oops, something went wrong.

0 comments on commit 0347169

Please sign in to comment.