diff options
-rw-r--r-- | jstests/core/geo_big_polygon.js | 95 | ||||
-rw-r--r-- | src/mongo/db/exec/geo_near.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/geo/geo_query.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/geo/geometry_container.cpp | 102 | ||||
-rw-r--r-- | src/mongo/db/geo/geoparser.cpp | 77 | ||||
-rw-r--r-- | src/mongo/db/geo/geoparser.h | 1 | ||||
-rw-r--r-- | src/mongo/db/geo/geoparser_test.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/geo/shapes.cpp | 29 | ||||
-rw-r--r-- | src/mongo/db/geo/shapes.h | 19 | ||||
-rw-r--r-- | src/mongo/db/index/expression_keys_private.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_geo.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/query/canonical_query.cpp | 6 |
12 files changed, 302 insertions, 59 deletions
diff --git a/jstests/core/geo_big_polygon.js b/jstests/core/geo_big_polygon.js new file mode 100644 index 00000000000..7bd23324393 --- /dev/null +++ b/jstests/core/geo_big_polygon.js @@ -0,0 +1,95 @@ +// +// Test of sample big polygon functionality +// + +var coll = db.geo_big_polygon; +coll.drop(); + +//coll.ensureIndex({ loc : "2dsphere" }); + +coll.getMongo().getDB("admin").runCommand({ setParameter : 1, verboseQueryLogging : true }); + +var bigCRS = { type : "name", + properties : { name : "urn:mongodb:strictwindingcrs:EPSG:4326" } }; + +var bigPoly20 = { type : "Polygon", coordinates : [[[10.0, 10.0], + [-10.0, 10.0], + [-10.0, -10.0], + [10.0, -10.0], + [10.0, 10.0]]], + crs : bigCRS }; + +var bigPoly20Comp = { type : "Polygon", coordinates : [[[10.0, 10.0], + [10.0, -10.0], + [-10.0, -10.0], + [-10.0, 10.0], + [10.0, 10.0]]], + crs : bigCRS }; + +var poly10 = { type : "Polygon", coordinates : [[[5.0, 5.0], + [5.0, -5.0], + [-5.0, -5.0], + [-5.0, 5.0], + [5.0, 5.0]]] }; + +var line10 = { type : "LineString", coordinates : [[5.0, 5.0], + [5.0, -5.0], + [-5.0, -5.0], + [-5.0, 5.0], + [5.0, 5.0]] }; + +var centerPoint = { type : "Point", coordinates : [0, 0] }; + +var polarPoint = { type : "Point", coordinates : [85, 85] }; + +var lineEquator = { type : "LineString", coordinates : [[-20, 0], [20, 0]] }; + +assert.writeOK(coll.insert({ loc : poly10 })); +assert.writeOK(coll.insert({ loc : line10 })); +assert.writeOK(coll.insert({ loc : centerPoint })); +assert.writeOK(coll.insert({ loc : polarPoint })); +assert.writeOK(coll.insert({ loc : lineEquator })); +assert.eq(coll.find({}).count(), 5); + +jsTest.log("Starting query..."); + +assert.eq(coll.find({ loc : { $geoWithin : { $geometry : bigPoly20 } } }).count(), 3); +assert.eq(coll.find({ loc : { $geoIntersects : { $geometry : bigPoly20 } } }).count(), 4); +assert.eq(coll.find({ loc : { $geoWithin : { $geometry : bigPoly20Comp } } }).count(), 1); +assert.eq(coll.find({ loc : { $geoIntersects : { $geometry : bigPoly20Comp } } }).count(), 2); + +assert.commandWorked(coll.ensureIndex({ loc : "2dsphere" })); + +assert.eq(coll.find({ loc : { $geoWithin : { $geometry : bigPoly20 } } }).count(), 3); +assert.eq(coll.find({ loc : { $geoIntersects : { $geometry : bigPoly20 } } }).count(), 4); +assert.eq(coll.find({ loc : { $geoWithin : { $geometry : bigPoly20Comp } } }).count(), 1); +assert.eq(coll.find({ loc : { $geoIntersects : { $geometry : bigPoly20Comp } } }).count(), 2); + + +// Test not indexing and querying big polygon +assert.commandWorked(coll.dropIndexes()); + +// 1. Without index, insert succeeds, but query ignores big polygon. +var bigPoly10 = { type : "Polygon", coordinates : [[[5.0, 5.0], + [-5.0, 5.0], + [-5.0, -5.0], + [5.0, -5.0], + [5.0, 5.0]]], + crs : bigCRS }; + +assert.writeOK(coll.insert({ _id: "bigPoly10", loc: bigPoly10})); + +assert.eq(coll.find({ loc : { $geoWithin : { $geometry : bigPoly20 } } }).count(), 3); +assert.eq(coll.find({ loc : { $geoIntersects : { $geometry : bigPoly20 } } }).count(), 4); +assert.eq(coll.find({ loc : { $geoWithin : { $geometry : bigPoly20Comp } } }).count(), 1); +assert.eq(coll.find({ loc : { $geoIntersects : { $geometry : bigPoly20Comp } } }).count(), 2); + +// 2. Building index fails due to big polygon +assert.commandFailed(coll.ensureIndex({ loc : "2dsphere" })); + +// 3. After removing big polygon, index builds successfully +assert.writeOK(coll.remove({_id: "bigPoly10"})); +assert.commandWorked(coll.ensureIndex({ loc : "2dsphere" })); + +// 4. With index, insert fails. +assert.writeError(coll.insert({ _id: "bigPoly10", loc: bigPoly10})); diff --git a/src/mongo/db/exec/geo_near.cpp b/src/mongo/db/exec/geo_near.cpp index 04355f25f88..1c5a045a914 100644 --- a/src/mongo/db/exec/geo_near.cpp +++ b/src/mongo/db/exec/geo_near.cpp @@ -151,6 +151,10 @@ namespace mongo { StoredGeometry& stored = **it; + // NOTE: A stored document with STRICT_SPHERE CRS is treated as a malformed document + // and ignored. Since GeoNear requires an index, there's no stored STRICT_SPHERE shape. + // So we don't check it here. + // NOTE: For now, we're sure that if we get this far in the query we'll have an // appropriate index which validates the type of geometry we're pulling back here. // TODO: It may make sense to change our semantics and, by default, only return diff --git a/src/mongo/db/geo/geo_query.cpp b/src/mongo/db/geo/geo_query.cpp index 05ae6fa4757..60cf9665bb7 100644 --- a/src/mongo/db/geo/geo_query.cpp +++ b/src/mongo/db/geo/geo_query.cpp @@ -271,6 +271,15 @@ namespace mongo { if (!(parseLegacyQuery(obj) || parseNewQuery(obj))) return false; + // Big polygon with strict winding order is represented as an S2Loop in SPHERE CRS. + // So converting the query to SPHERE CRS makes things easier than projecting all the data + // into STRICT_SPHERE CRS. + if (STRICT_SPHERE == geoContainer.getNativeCRS()) { + if (!geoContainer.supportsProject(SPHERE)) + return false; + geoContainer.projectInto(SPHERE); + } + // $geoIntersect queries are hardcoded to *always* be in SPHERE CRS // TODO: This is probably bad semantics, should not do this if (GeoQuery::INTERSECT == predicate) { diff --git a/src/mongo/db/geo/geometry_container.cpp b/src/mongo/db/geo/geometry_container.cpp index 568351b75d1..c7ad0706ded 100644 --- a/src/mongo/db/geo/geometry_container.cpp +++ b/src/mongo/db/geo/geometry_container.cpp @@ -55,7 +55,7 @@ namespace mongo { bool GeometryContainer::hasS2Region() const { return (NULL != _point && _point->crs == SPHERE) || NULL != _line - || (NULL != _polygon && _polygon->crs == SPHERE) + || (NULL != _polygon && (_polygon->crs == SPHERE || _polygon->crs == STRICT_SPHERE)) || (NULL != _cap && _cap->crs == SPHERE) || NULL != _multiPoint || NULL != _multiLine @@ -68,8 +68,10 @@ namespace mongo { return _point->cell; } else if (NULL != _line) { return _line->line; - } else if (NULL != _polygon && SPHERE == _polygon->crs) { - return _polygon->polygon; + } else if (NULL != _polygon && NULL != _polygon->s2Polygon) { + return *_polygon->s2Polygon; + } else if (NULL != _polygon && NULL != _polygon->bigPolygon) { + return *_polygon->bigPolygon; } else if (NULL != _cap && SPHERE == _cap->crs) { return _cap->cap; } else if (NULL != _multiPoint) { @@ -286,7 +288,8 @@ namespace mongo { } if (NULL != otherContainer._polygon) { - return contains(otherContainer._polygon->polygon); + invariant(NULL != otherContainer._polygon->s2Polygon); + return contains(*otherContainer._polygon->s2Polygon); } if (NULL != otherContainer._multiPoint) { @@ -331,7 +334,7 @@ namespace mongo { const vector<PolygonWithCRS*>& polys = c.polygons.vector(); for (size_t i = 0; i < polys.size(); ++i) { - if (!contains(polys[i]->polygon)) { return false; } + if (!contains(*polys[i]->s2Polygon)) { return false; } } const vector<MultiPointWithCRS*>& multipoints = c.multiPoints.vector(); @@ -372,8 +375,14 @@ namespace mongo { } bool GeometryContainer::contains(const S2Cell& otherCell, const S2Point& otherPoint) const { - if (NULL != _polygon && (_polygon->crs == SPHERE)) { - return containsPoint(_polygon->polygon, otherCell, otherPoint); + if (NULL != _polygon && (NULL != _polygon->s2Polygon)) { + return containsPoint(*_polygon->s2Polygon, otherCell, otherPoint); + } + + if (NULL != _polygon && (NULL != _polygon->bigPolygon)) { + if (_polygon->bigPolygon->Contains(otherPoint)) + return true; + return _polygon->bigPolygon->MayIntersect(otherCell); } if (NULL != _cap && (_cap->crs == SPHERE)) { @@ -390,9 +399,9 @@ namespace mongo { if (NULL != _geometryCollection) { const vector<PolygonWithCRS*>& polys = _geometryCollection->polygons.vector(); for (size_t i = 0; i < polys.size(); ++i) { - if (containsPoint(polys[i]->polygon, otherCell, otherPoint)) { return true; } + if (containsPoint(*polys[i]->s2Polygon, otherCell, otherPoint)) { return true; } } - + const vector<MultiPolygonWithCRS*>& multipolys =_geometryCollection->multiPolygons.vector(); for (size_t i = 0; i < multipolys.size(); ++i) { const vector<S2Polygon*>& innerpolys = multipolys[i]->polygons.vector(); @@ -424,8 +433,12 @@ namespace mongo { } bool GeometryContainer::contains(const S2Polyline& otherLine) const { - if (NULL != _polygon && (_polygon->crs == SPHERE)) { - return containsLine(_polygon->polygon, otherLine); + if (NULL != _polygon && NULL != _polygon->s2Polygon) { + return containsLine(*_polygon->s2Polygon, otherLine); + } + + if (NULL != _polygon && NULL != _polygon->bigPolygon) { + return _polygon->bigPolygon->Contains(otherLine); } if (NULL != _multiPolygon) { @@ -438,9 +451,9 @@ namespace mongo { if (NULL != _geometryCollection) { const vector<PolygonWithCRS*>& polys = _geometryCollection->polygons.vector(); for (size_t i = 0; i < polys.size(); ++i) { - if (containsLine(polys[i]->polygon, otherLine)) { return true; } + if (containsLine(*polys[i]->s2Polygon, otherLine)) { return true; } } - + const vector<MultiPolygonWithCRS*>& multipolys =_geometryCollection->multiPolygons.vector(); for (size_t i = 0; i < multipolys.size(); ++i) { const vector<S2Polygon*>& innerpolys = multipolys[i]->polygons.vector(); @@ -458,8 +471,12 @@ namespace mongo { } bool GeometryContainer::contains(const S2Polygon& otherPolygon) const { - if (NULL != _polygon && (_polygon->crs == SPHERE)) { - return containsPolygon(_polygon->polygon, otherPolygon); + if (NULL != _polygon && NULL != _polygon->s2Polygon) { + return containsPolygon(*_polygon->s2Polygon, otherPolygon); + } + + if (NULL != _polygon && NULL != _polygon->bigPolygon) { + return _polygon->bigPolygon->Contains(otherPolygon); } if (NULL != _multiPolygon) { @@ -472,9 +489,9 @@ namespace mongo { if (NULL != _geometryCollection) { const vector<PolygonWithCRS*>& polys = _geometryCollection->polygons.vector(); for (size_t i = 0; i < polys.size(); ++i) { - if (containsPolygon(polys[i]->polygon, otherPolygon)) { return true; } + if (containsPolygon(*polys[i]->s2Polygon, otherPolygon)) { return true; } } - + const vector<MultiPolygonWithCRS*>& multipolys =_geometryCollection->multiPolygons.vector(); for (size_t i = 0; i < multipolys.size(); ++i) { const vector<S2Polygon*>& innerpolys = multipolys[i]->polygons.vector(); @@ -493,8 +510,8 @@ namespace mongo { } else if (NULL != otherContainer._line) { return intersects(otherContainer._line->line); } else if (NULL != otherContainer._polygon) { - if (SPHERE != otherContainer._polygon->crs) { return false; } - return intersects(otherContainer._polygon->polygon); + if (NULL == otherContainer._polygon->s2Polygon) { return false; } + return intersects(*otherContainer._polygon->s2Polygon); } else if (NULL != otherContainer._multiPoint) { return intersects(*otherContainer._multiPoint); } else if (NULL != otherContainer._multiLine) { @@ -509,7 +526,7 @@ namespace mongo { } for (size_t i = 0; i < c.polygons.vector().size(); ++i) { - if (intersects(c.polygons.vector()[i]->polygon)) { return true; } + if (intersects(*c.polygons.vector()[i]->s2Polygon)) { return true; } } for (size_t i = 0; i < c.lines.vector().size(); ++i) { @@ -559,8 +576,10 @@ namespace mongo { return _point->cell.MayIntersect(otherPoint); } else if (NULL != _line) { return _line->line.MayIntersect(otherPoint); - } else if (NULL != _polygon) { - return _polygon->polygon.MayIntersect(otherPoint); + } else if (NULL != _polygon && NULL != _polygon->s2Polygon) { + return _polygon->s2Polygon->MayIntersect(otherPoint); + } else if (NULL != _polygon && NULL != _polygon->bigPolygon) { + return _polygon->bigPolygon->MayIntersect(otherPoint); } else if (NULL != _multiPoint) { const vector<S2Cell>& cells = _multiPoint->cells; for (size_t i = 0; i < cells.size(); ++i) { @@ -584,7 +603,7 @@ namespace mongo { } for (size_t i = 0; i < c.polygons.vector().size(); ++i) { - if (c.polygons.vector()[i]->polygon.MayIntersect(otherPoint)) { return true; } + if (c.polygons.vector()[i]->s2Polygon->MayIntersect(otherPoint)) { return true; } } for (size_t i = 0; i < c.lines.vector().size(); ++i) { @@ -633,8 +652,10 @@ namespace mongo { return otherLine.MayIntersect(_point->cell); } else if (NULL != _line) { return otherLine.Intersects(&_line->line); - } else if (NULL != _polygon && (_polygon->crs == SPHERE)) { - return polygonLineIntersection(otherLine, _polygon->polygon); + } else if (NULL != _polygon && NULL != _polygon->s2Polygon) { + return polygonLineIntersection(otherLine, *_polygon->s2Polygon); + } else if (NULL != _polygon && NULL != _polygon->bigPolygon) { + return _polygon->bigPolygon->Intersects(otherLine); } else if (NULL != _multiPoint) { for (size_t i = 0; i < _multiPoint->cells.size(); ++i) { if (otherLine.MayIntersect(_multiPoint->cells[i])) { return true; } @@ -659,7 +680,7 @@ namespace mongo { } for (size_t i = 0; i < c.polygons.vector().size(); ++i) { - if (polygonLineIntersection(otherLine, c.polygons.vector()[i]->polygon)) { + if (polygonLineIntersection(otherLine, *c.polygons.vector()[i]->s2Polygon)) { return true; } } @@ -703,8 +724,10 @@ namespace mongo { return otherPolygon.MayIntersect(_point->cell); } else if (NULL != _line) { return polygonLineIntersection(_line->line, otherPolygon); - } else if (NULL != _polygon) { - return otherPolygon.Intersects(&_polygon->polygon); + } else if (NULL != _polygon && NULL != _polygon->s2Polygon) { + return otherPolygon.Intersects(_polygon->s2Polygon.get()); + } else if (NULL != _polygon && NULL != _polygon->bigPolygon) { + return _polygon->bigPolygon->Intersects(otherPolygon); } else if (NULL != _multiPoint) { for (size_t i = 0; i < _multiPoint->cells.size(); ++i) { if (otherPolygon.MayIntersect(_multiPoint->cells[i])) { return true; } @@ -729,7 +752,7 @@ namespace mongo { } for (size_t i = 0; i < c.polygons.vector().size(); ++i) { - if (otherPolygon.Intersects(&c.polygons.vector()[i]->polygon)) { + if (otherPolygon.Intersects(c.polygons.vector()[i]->s2Polygon.get())) { return true; } } @@ -825,7 +848,7 @@ namespace mongo { _s2Region->Add(&_geometryCollection->lines.vector()[i]->line); } for (size_t i = 0; i < _geometryCollection->polygons.vector().size(); ++i) { - _s2Region->Add(&_geometryCollection->polygons.vector()[i]->polygon); + _s2Region->Add(_geometryCollection->polygons.vector()[i]->s2Polygon.get()); } for (size_t i = 0; i < _geometryCollection->multiPoints.vector().size(); ++i) { MultiPointWithCRS* multiPoint = _geometryCollection->multiPoints.vector()[i]; @@ -901,7 +924,9 @@ namespace mongo { } else if (NULL != _line) { return _line->crs == otherCRS; } else if (NULL != _box) { return _box->crs == otherCRS; } - else if (NULL != _polygon) { return _polygon->crs == otherCRS; } + else if (NULL != _polygon) { + return ShapeProjection::supportsProject(*_polygon, otherCRS); + } else if (NULL != _cap ) { return _cap->crs == otherCRS; } else if (NULL != _multiPoint) { return _multiPoint->crs == otherCRS; } else if (NULL != _multiLine) { return _multiLine->crs == otherCRS; } @@ -914,11 +939,14 @@ namespace mongo { void GeometryContainer::projectInto(CRS otherCRS) { - if (otherCRS == getNativeCRS()) + if (getNativeCRS() == otherCRS) return; + + if (NULL != _polygon) { + ShapeProjection::projectInto(_polygon.get(), otherCRS); return; + } invariant(NULL != _point); - ShapeProjection::projectInto(_point.get(), otherCRS); } @@ -995,7 +1023,9 @@ namespace mongo { it != geometryCollection.polygons.vector().end(); ++it) { invariant(SPHERE == (*it)->crs); - double nextDistance = S2Distance::minDistanceRad(s2Point, (*it)->polygon); + // We don't support distances for big polygons yet. + invariant(NULL != (*it)->s2Polygon); + double nextDistance = S2Distance::minDistanceRad(s2Point, *((*it)->s2Polygon)); if (minDistance < 0 || nextDistance < minDistance) { minDistance = nextDistance; } @@ -1060,7 +1090,9 @@ namespace mongo { minDistance = S2Distance::minDistanceRad(otherPoint.point, _line->line); } else if (NULL != _polygon) { - minDistance = S2Distance::minDistanceRad(otherPoint.point, _polygon->polygon); + // We don't support distances for big polygons yet. + invariant(NULL != _polygon->s2Polygon); + minDistance = S2Distance::minDistanceRad(otherPoint.point, *_polygon->s2Polygon); } else if (NULL != _cap) { minDistance = S2Distance::minDistanceRad(otherPoint.point, _cap->cap); diff --git a/src/mongo/db/geo/geoparser.cpp b/src/mongo/db/geo/geoparser.cpp index 6efecc3ec58..658e145efb1 100644 --- a/src/mongo/db/geo/geoparser.cpp +++ b/src/mongo/db/geo/geoparser.cpp @@ -153,7 +153,9 @@ namespace mongo { } static bool parseGeoJSONPolygonCoordinates(const vector<BSONElement>& coordinates, - const BSONObj &sourceObject, S2Polygon *out) { + const BSONObj &sourceObject, + S2Polygon *out) { + const vector<BSONElement>& exteriorRing = coordinates[0].Array(); vector<S2Point> exteriorVertices; if (!parsePoints(exteriorRing, &exteriorVertices)) { return false; } @@ -206,6 +208,38 @@ namespace mongo { return polyBuilder.AssemblePolygon(out, NULL); } + static bool parseBigSimplePolygonCoordinates(const vector<BSONElement>& coordinates, + const BSONObj &sourceObject, + BigSimplePolygon *out) { + + // Only one loop is allowed in a BigSimplePolygon + if (coordinates.size() != 1) + return false; + + const vector<BSONElement>& exteriorRing = coordinates[0].Array(); + + vector<S2Point> exteriorVertices; + if (!parsePoints(exteriorRing, &exteriorVertices)) + return false; + + eraseDuplicatePoints(&exteriorVertices); + + // The last point is duplicated. We drop it, since S2Loop expects no + // duplicate points + exteriorVertices.resize(exteriorVertices.size() - 1); + + // S2 Polygon loops must have 3 vertices + if (exteriorVertices.size() < 3) + return false; + + auto_ptr<S2Loop> loop(new S2Loop(exteriorVertices)); + if (!loop->IsValid()) + return false; + + out->Init(loop.release()); + return true; + } + static bool parseLegacyPoint(const BSONObj &obj, Point *out) { BSONObjIterator it(obj); BSONElement x = it.next(); @@ -326,7 +360,7 @@ namespace mongo { BSONElement x = it.next(); BSONElement y = it.next(); out->oldPoint.x = x.Number(); - out->oldPoint.y = y.Number(); + out->oldPoint.y = y.Number(); out->crs = FLAT; } else if (isGeoJSONPoint(obj)) { const vector<BSONElement>& coords = obj.getFieldDotted(GEOJSON_COORDINATES).Array(); @@ -401,8 +435,22 @@ namespace mongo { bool GeoParser::parsePolygon(const BSONObj &obj, PolygonWithCRS *out) { if (isGeoJSONPolygon(obj)) { const vector<BSONElement>& coordinates = obj.getFieldDotted(GEOJSON_COORDINATES).Array(); - if (!parseGeoJSONPolygonCoordinates(coordinates, obj, &out->polygon)) { return false; } - out->crs = SPHERE; + + if (!parseGeoJSONCRS(obj, &out->crs)) + return false; + + if (out->crs == SPHERE) { + out->s2Polygon.reset(new S2Polygon()); + if (!parseGeoJSONPolygonCoordinates(coordinates, obj, out->s2Polygon.get())) { + return false; + } + } + else if (out->crs == STRICT_SPHERE) { + out->bigPolygon.reset(new BigSimplePolygon()); + if (!parseBigSimplePolygonCoordinates(coordinates, obj, out->bigPolygon.get())) { + return false; + } + } } else { BSONObjIterator typeIt(obj); BSONElement type = typeIt.next(); @@ -580,7 +628,26 @@ namespace mongo { // 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); + return ("urn:ogc:def:crs:OGC:1.3:CRS84" == name) || ("EPSG:4326" == name) || + ("urn:mongodb:strictwindingcrs:EPSG:4326" == name); + } + + bool GeoParser::parseGeoJSONCRS(const BSONObj& obj, CRS* crs) { + + dassert(crsIsOK(obj)); + + *crs = SPHERE; + + if (!obj["crs"].eoo()) { + const string name = obj["crs"].Obj()["properties"].Obj()["name"].String(); + + if (name == "urn:mongodb:strictwindingcrs:EPSG:4326") + *crs = STRICT_SPHERE; + else + *crs = SPHERE; + } + + return true; } bool GeoParser::isCap(const BSONObj &obj) { diff --git a/src/mongo/db/geo/geoparser.h b/src/mongo/db/geo/geoparser.h index e8222bcce74..d996de84664 100644 --- a/src/mongo/db/geo/geoparser.h +++ b/src/mongo/db/geo/geoparser.h @@ -82,6 +82,7 @@ namespace mongo { // returning states: missing, invalid, unknown, ok, etc. -- whatever // needed. static bool crsIsOK(const BSONObj& obj); + static bool parseGeoJSONCRS(const BSONObj& obj, CRS* crs); }; } // namespace mongo diff --git a/src/mongo/db/geo/geoparser_test.cpp b/src/mongo/db/geo/geoparser_test.cpp index eeac3725336..e3f124d24c9 100644 --- a/src/mongo/db/geo/geoparser_test.cpp +++ b/src/mongo/db/geo/geoparser_test.cpp @@ -143,7 +143,7 @@ namespace { GeoParser::parsePolygon( fromjson("{'type':'Polygon', 'coordinates':[ [[0,0],[5,0],[5,5],[0,5],[0,0]] ]}"), &polygonA); - ASSERT_TRUE(polygonA.polygon.Contains(point.point)); + ASSERT_TRUE(polygonA.s2Polygon->Contains(point.point)); PolygonWithCRS polygonB; GeoParser::parsePolygon( @@ -151,7 +151,7 @@ namespace { " [[1,1],[1,4],[4,4],[4,1],[1,1]] ]}"), &polygonB); // We removed this in the hole. - ASSERT_FALSE(polygonB.polygon.Contains(point.point)); + ASSERT_FALSE(polygonB.s2Polygon->Contains(point.point)); // Now we reverse the orientations and verify that the code fixes it up // (outer loop must be CCW, inner CW). @@ -159,7 +159,7 @@ namespace { GeoParser::parsePolygon( fromjson("{'type':'Polygon', 'coordinates':[ [[0,0],[0,5],[5,5],[5,0],[0,0]] ]}"), &polygonC); - ASSERT_TRUE(polygonC.polygon.Contains(point.point)); + ASSERT_TRUE(polygonC.s2Polygon->Contains(point.point)); PolygonWithCRS polygonD; GeoParser::parsePolygon( @@ -167,7 +167,7 @@ namespace { " [[1,1],[1,4],[4,4],[4,1],[1,1]] ]}"), &polygonD); // Also removed in the loop. - ASSERT_FALSE(polygonD.polygon.Contains(point.point)); + ASSERT_FALSE(polygonD.s2Polygon->Contains(point.point)); // // Bad polygon examples @@ -300,7 +300,7 @@ namespace { fromjson("{'type':'MultiLine','coordinates':[ [[1,1],[2,2],[3,3]]," "[[4,5],[6,7]]]}"), &mls); - + GeoParser::parseMultiLine( fromjson("{'type':'MultiLine','coordinates':[ [[1,1],[2,2]]," "[[4,5],[6,7]]]}"), diff --git a/src/mongo/db/geo/shapes.cpp b/src/mongo/db/geo/shapes.cpp index 14a4fa3de04..562e35a46fc 100644 --- a/src/mongo/db/geo/shapes.cpp +++ b/src/mongo/db/geo/shapes.cpp @@ -759,6 +759,11 @@ namespace mongo { return isValidLngLat(point.oldPoint.x, point.oldPoint.y); } + bool ShapeProjection::supportsProject(const PolygonWithCRS& polygon, const CRS crs) { + return polygon.crs == crs + || (polygon.crs == STRICT_SPHERE && crs == SPHERE); + } + void ShapeProjection::projectInto(PointWithCRS* point, CRS crs) { dassert(supportsProject(*point, crs)); @@ -766,6 +771,7 @@ namespace mongo { return; if (FLAT == point->crs) { + // Prohibit projection to STRICT_SPHERE CRS invariant(SPHERE == crs); // Note that it's (lat, lng) for S2 but (lng, lat) for MongoDB. @@ -775,16 +781,23 @@ namespace mongo { point->point = latLng.ToPoint(); point->cell = S2Cell(point->point); point->crs = SPHERE; + return; } - else { - invariant(SPHERE == point->crs); - invariant(FLAT == crs); - // Just remove the additional spherical information - point->point = S2Point(); - point->cell = S2Cell(); - point->crs = FLAT; - } + // Prohibit projection to STRICT_SPHERE CRS + invariant(SPHERE == point->crs && FLAT == crs); + // Just remove the additional spherical information + point->point = S2Point(); + point->cell = S2Cell(); + point->crs = FLAT; + } + + void ShapeProjection::projectInto(PolygonWithCRS* polygon, CRS crs) { + if (polygon->crs == crs) return; + + // Only project from STRICT_SPHERE to SPHERE + invariant(STRICT_SPHERE == polygon->crs && SPHERE == crs); + polygon->crs = SPHERE; } } // namespace mongo diff --git a/src/mongo/db/geo/shapes.h b/src/mongo/db/geo/shapes.h index 4091ce9357c..074c71d2e4e 100644 --- a/src/mongo/db/geo/shapes.h +++ b/src/mongo/db/geo/shapes.h @@ -34,6 +34,7 @@ #include "mongo/base/owned_pointer_vector.h" #include "mongo/db/jsobj.h" +#include "mongo/db/geo/big_polygon.h" #include "mongo/db/geo/s2.h" #include "third_party/s2/s2cap.h" #include "third_party/s2/s2cell.h" @@ -250,8 +251,9 @@ namespace mongo { // Clearly this isn't right but currently it's sufficient. enum CRS { UNSET, - FLAT, - SPHERE + FLAT, // Equirectangular flat projection (i.e. trivial long/lat projection to flat map) + SPHERE, // WGS84 + STRICT_SPHERE // WGS84 with strict winding order }; // TODO: Make S2 less integral to these types - additional S2 shapes should be an optimization @@ -298,7 +300,11 @@ namespace mongo { PolygonWithCRS() : crs(UNSET) {} - S2Polygon polygon; + scoped_ptr<S2Polygon> s2Polygon; + // Simple polygons with strict winding order may be bigger or smaller than a hemisphere. + // Only used for query. We don't support storing/indexing big polygons. + scoped_ptr<BigSimplePolygon> bigPolygon; + Polygon oldPolygon; CRS crs; }; @@ -347,12 +353,15 @@ namespace mongo { }; // - // Projection functions - we don't project types other than points for now + // Projection functions - we only project following types for now + // - Point + // - Polygon (from STRICT_SPHERE TO SPHERE) // - struct ShapeProjection { static bool supportsProject(const PointWithCRS& point, const CRS crs); + static bool supportsProject(const PolygonWithCRS& polygon, const CRS crs); static void projectInto(PointWithCRS* point, CRS crs); + static void projectInto(PolygonWithCRS* point, CRS crs); }; } // namespace mongo diff --git a/src/mongo/db/index/expression_keys_private.cpp b/src/mongo/db/index/expression_keys_private.cpp index 9ca7eade530..44be3b10aa8 100644 --- a/src/mongo/db/index/expression_keys_private.cpp +++ b/src/mongo/db/index/expression_keys_private.cpp @@ -91,6 +91,11 @@ namespace { GeometryContainer geoContainer; if (!geoContainer.parseFrom(obj)) { return false; } + // Don't index big polygon + if (geoContainer.getNativeCRS() == STRICT_SPHERE) { + return false; + } + // Only certain geometries can be indexed in the old index format S2_INDEX_VERSION_1. See // definition of S2IndexVersion for details. if (params.indexVersion == S2_INDEX_VERSION_1 && !geoContainer.isSimpleContainer()) { diff --git a/src/mongo/db/matcher/expression_geo.cpp b/src/mongo/db/matcher/expression_geo.cpp index 3c55f8a9133..ee3142d1120 100644 --- a/src/mongo/db/matcher/expression_geo.cpp +++ b/src/mongo/db/matcher/expression_geo.cpp @@ -52,6 +52,10 @@ namespace mongo { if ( !geometry.parseFrom( e.Obj() ) ) return false; + // Never match big polygon + if (geometry.getNativeCRS() == STRICT_SPHERE) + return false; + // Project this geometry into the CRS of the query if (!geometry.supportsProject(_query->getGeometry().getNativeCRS())) return false; diff --git a/src/mongo/db/query/canonical_query.cpp b/src/mongo/db/query/canonical_query.cpp index b143f59e150..603c636a403 100644 --- a/src/mongo/db/query/canonical_query.cpp +++ b/src/mongo/db/query/canonical_query.cpp @@ -172,6 +172,9 @@ namespace { else if (SPHERE == geoQuery.getGeometry().getNativeCRS()) { *os << "sp"; } + else if (STRICT_SPHERE == geoQuery.getGeometry().getNativeCRS()) { + *os << "ss"; + } else { error() << "unknown CRS type " << (int)geoQuery.getGeometry().getNativeCRS() << " in geometry of type " << geoQuery.getGeometry().getDebugType(); @@ -192,10 +195,11 @@ namespace { // isNearSphere *os << (nearQuery.isNearSphere ? "ns" : "nr"); - // CRS (flat or spherical) + // CRS (flat or spherical or strict-winding spherical) switch (nearQuery.centroid.crs) { case FLAT: *os << "fl"; break; case SPHERE: *os << "sp"; break; + case STRICT_SPHERE: *os << "ss"; break; case UNSET: error() << "unknown CRS type " << (int)nearQuery.centroid.crs << " in point geometry for near query"; |