diff options
Diffstat (limited to 'src/mongo/db/geo/geoparser.cpp')
-rw-r--r-- | src/mongo/db/geo/geoparser.cpp | 1228 |
1 files changed, 645 insertions, 583 deletions
diff --git a/src/mongo/db/geo/geoparser.cpp b/src/mongo/db/geo/geoparser.cpp index 95c0f33ac57..1f374f42215 100644 --- a/src/mongo/db/geo/geoparser.cpp +++ b/src/mongo/db/geo/geoparser.cpp @@ -44,683 +44,745 @@ namespace mongo { - using std::unique_ptr; - using std::stringstream; - - // This field must be present, and... - static const string GEOJSON_TYPE = "type"; - // Have one of these values: - static const string GEOJSON_TYPE_POINT = "Point"; - static const string GEOJSON_TYPE_LINESTRING = "LineString"; - static const string GEOJSON_TYPE_POLYGON = "Polygon"; - static const string GEOJSON_TYPE_MULTI_POINT = "MultiPoint"; - static const string GEOJSON_TYPE_MULTI_LINESTRING = "MultiLineString"; - static const string GEOJSON_TYPE_MULTI_POLYGON = "MultiPolygon"; - static const string GEOJSON_TYPE_GEOMETRY_COLLECTION = "GeometryCollection"; - // This field must also be present. The value depends on the type. - static const string GEOJSON_COORDINATES = "coordinates"; - static const string GEOJSON_GEOMETRIES = "geometries"; - - // Coordinate System Reference - // 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 - static const string CRS_CRS84 = "urn:ogc:def:crs:OGC:1.3:CRS84"; - static const string CRS_EPSG_4326 = "EPSG:4326"; - static const string CRS_STRICT_WINDING = "urn:x-mongodb:crs:strictwinding:EPSG:4326"; - - static Status parseFlatPoint(const BSONElement &elem, Point *out, bool allowAddlFields = false) { - if (!elem.isABSONObj()) return BAD_VALUE("Point must be an array or object"); - BSONObjIterator it(elem.Obj()); - BSONElement x = it.next(); - if (!x.isNumber()) { return BAD_VALUE("Point must only contain numeric elements"); } - BSONElement y = it.next(); - if (!y.isNumber()) { return BAD_VALUE("Point must only contain numeric elements"); } - if (!allowAddlFields && it.more()) { return BAD_VALUE("Point must only contain two numeric elements"); } - out->x = x.number(); - out->y = y.number(); - // Point coordinates must be finite numbers, neither NaN or infinite. - if (!std::isfinite(out->x) || !std::isfinite(out->y)) { - return BAD_VALUE("Point coordinates must be finite numbers"); - } - return Status::OK(); +using std::unique_ptr; +using std::stringstream; + +// This field must be present, and... +static const string GEOJSON_TYPE = "type"; +// Have one of these values: +static const string GEOJSON_TYPE_POINT = "Point"; +static const string GEOJSON_TYPE_LINESTRING = "LineString"; +static const string GEOJSON_TYPE_POLYGON = "Polygon"; +static const string GEOJSON_TYPE_MULTI_POINT = "MultiPoint"; +static const string GEOJSON_TYPE_MULTI_LINESTRING = "MultiLineString"; +static const string GEOJSON_TYPE_MULTI_POLYGON = "MultiPolygon"; +static const string GEOJSON_TYPE_GEOMETRY_COLLECTION = "GeometryCollection"; +// This field must also be present. The value depends on the type. +static const string GEOJSON_COORDINATES = "coordinates"; +static const string GEOJSON_GEOMETRIES = "geometries"; + +// Coordinate System Reference +// 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 +static const string CRS_CRS84 = "urn:ogc:def:crs:OGC:1.3:CRS84"; +static const string CRS_EPSG_4326 = "EPSG:4326"; +static const string CRS_STRICT_WINDING = "urn:x-mongodb:crs:strictwinding:EPSG:4326"; + +static Status parseFlatPoint(const BSONElement& elem, Point* out, bool allowAddlFields = false) { + if (!elem.isABSONObj()) + return BAD_VALUE("Point must be an array or object"); + BSONObjIterator it(elem.Obj()); + BSONElement x = it.next(); + if (!x.isNumber()) { + return BAD_VALUE("Point must only contain numeric elements"); } - - Status GeoParser::parseLegacyPoint(const BSONElement &elem, PointWithCRS *out, bool allowAddlFields) { - out->crs = FLAT; - return parseFlatPoint(elem, &out->oldPoint, allowAddlFields); + BSONElement y = it.next(); + if (!y.isNumber()) { + return BAD_VALUE("Point must only contain numeric elements"); + } + if (!allowAddlFields && it.more()) { + return BAD_VALUE("Point must only contain two numeric elements"); } + out->x = x.number(); + out->y = y.number(); + // Point coordinates must be finite numbers, neither NaN or infinite. + if (!std::isfinite(out->x) || !std::isfinite(out->y)) { + return BAD_VALUE("Point coordinates must be finite numbers"); + } + return Status::OK(); +} + +Status GeoParser::parseLegacyPoint(const BSONElement& elem, + PointWithCRS* out, + bool allowAddlFields) { + out->crs = FLAT; + return parseFlatPoint(elem, &out->oldPoint, allowAddlFields); +} + +static Status coordToPoint(double lng, double lat, S2Point* out) { + // We don't rely on drem to clean up non-sane points. We just don't let them become + // spherical. + if (!isValidLngLat(lng, lat)) + return BAD_VALUE("longitude/latitude is out of bounds, lng: " << lng << " lat: " << lat); + // Note that it's (lat, lng) for S2 but (lng, lat) for MongoDB. + S2LatLng ll = S2LatLng::FromDegrees(lat, lng).Normalized(); + // This shouldn't happen since we should only have valid lng/lats. + if (!ll.is_valid()) { + stringstream ss; + ss << "coords invalid after normalization, lng = " << lng << " lat = " << lat << endl; + uasserted(17125, ss.str()); + } + *out = ll.ToPoint(); + return Status::OK(); +} - static Status coordToPoint(double lng, double lat, S2Point* out) { - // We don't rely on drem to clean up non-sane points. We just don't let them become - // spherical. - if (!isValidLngLat(lng, lat)) - return BAD_VALUE("longitude/latitude is out of bounds, lng: " << lng << " lat: " << lat); - // Note that it's (lat, lng) for S2 but (lng, lat) for MongoDB. - S2LatLng ll = S2LatLng::FromDegrees(lat, lng).Normalized(); - // This shouldn't happen since we should only have valid lng/lats. - if (!ll.is_valid()) { - stringstream ss; - ss << "coords invalid after normalization, lng = " << lng << " lat = " << lat << endl; - uasserted(17125, ss.str()); - } - *out = ll.ToPoint(); - return Status::OK(); +static Status parseGeoJSONCoordinate(const BSONElement& elem, S2Point* out) { + if (Array != elem.type()) { + return BAD_VALUE("GeoJSON coordinates must be an array"); } + Point p; + // GeoJSON allows extra elements, e.g. altitude. + Status status = parseFlatPoint(elem, &p, true); + if (!status.isOK()) + return status; - static Status parseGeoJSONCoordinate(const BSONElement& elem, S2Point* out) { - if (Array != elem.type()) { return BAD_VALUE("GeoJSON coordinates must be an array"); } - Point p; - // GeoJSON allows extra elements, e.g. altitude. - Status status = parseFlatPoint(elem, &p, true); - if (!status.isOK()) return status; + status = coordToPoint(p.x, p.y, out); + return status; +} - status = coordToPoint(p.x, p.y, out); - return status; +// "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] +static Status parseArrayOfCoordinates(const BSONElement& elem, vector<S2Point>* out) { + if (Array != elem.type()) { + return BAD_VALUE("GeoJSON coordinates must be an array of coordinates"); } - - // "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] - static Status parseArrayOfCoordinates(const BSONElement& elem, vector<S2Point>* out) { - if (Array != elem.type()) { return BAD_VALUE("GeoJSON coordinates must be an array of coordinates"); } - BSONObjIterator it(elem.Obj()); - // Iterate all coordinates in array - while (it.more()) { - S2Point p; - Status status = parseGeoJSONCoordinate(it.next(), &p); - if (!status.isOK()) return status; - out->push_back(p); - } - return Status::OK(); + BSONObjIterator it(elem.Obj()); + // Iterate all coordinates in array + while (it.more()) { + S2Point p; + Status status = parseGeoJSONCoordinate(it.next(), &p); + if (!status.isOK()) + return status; + out->push_back(p); } - - static void eraseDuplicatePoints(vector<S2Point>* vertices) { - for (size_t i = 1; i < vertices->size(); ++i) { - if ((*vertices)[i - 1] == (*vertices)[i]) { - vertices->erase(vertices->begin() + i); - // We could have > 2 adjacent identical vertices, and must examine i again. - --i; - } + return Status::OK(); +} + +static void eraseDuplicatePoints(vector<S2Point>* vertices) { + for (size_t i = 1; i < vertices->size(); ++i) { + if ((*vertices)[i - 1] == (*vertices)[i]) { + vertices->erase(vertices->begin() + i); + // We could have > 2 adjacent identical vertices, and must examine i again. + --i; } } +} - static Status isLoopClosed(const vector<S2Point>& loop, const BSONElement loopElt) { - if (loop.empty()) { +static Status isLoopClosed(const vector<S2Point>& loop, const BSONElement loopElt) { + if (loop.empty()) { return BAD_VALUE("Loop has no vertices: " << loopElt.toString(false)); - } + } - if (loop[0] != loop[loop.size() - 1]) { + if (loop[0] != loop[loop.size() - 1]) { return BAD_VALUE("Loop is not closed: " << loopElt.toString(false)); - } + } - return Status::OK(); + return Status::OK(); +} + +static Status parseGeoJSONPolygonCoordinates(const BSONElement& elem, S2Polygon* out) { + if (Array != elem.type()) { + return BAD_VALUE("Polygon coordinates must be an array"); } - static Status parseGeoJSONPolygonCoordinates(const BSONElement& elem, S2Polygon *out) { - if (Array != elem.type()) { return BAD_VALUE("Polygon coordinates must be an array"); } + OwnedPointerVector<S2Loop> loops; + Status status = Status::OK(); + string err; + + BSONObjIterator it(elem.Obj()); + // Iterate all loops of the polygon. + while (it.more()) { + // Parse the array of vertices of a loop. + BSONElement coordinateElt = it.next(); + vector<S2Point> points; + status = parseArrayOfCoordinates(coordinateElt, &points); + if (!status.isOK()) + return status; + + // Check if the loop is closed. + status = isLoopClosed(points, coordinateElt); + if (!status.isOK()) + return status; + + eraseDuplicatePoints(&points); + // Drop the duplicated last point. + points.resize(points.size() - 1); - OwnedPointerVector<S2Loop> loops; - Status status = Status::OK(); - string err; - - BSONObjIterator it(elem.Obj()); - // Iterate all loops of the polygon. - while (it.more()) { - // Parse the array of vertices of a loop. - BSONElement coordinateElt = it.next(); - vector<S2Point> points; - status = parseArrayOfCoordinates(coordinateElt, &points); - if (!status.isOK()) return status; - - // Check if the loop is closed. - status = isLoopClosed(points, coordinateElt); - if (!status.isOK()) return status; - - eraseDuplicatePoints(&points); - // Drop the duplicated last point. - points.resize(points.size() - 1); - - // At least 3 vertices. - if (points.size() < 3) { - return BAD_VALUE("Loop must have at least 3 different vertices: " << - coordinateElt.toString(false)); - } - - S2Loop* loop = new S2Loop(points); - loops.push_back(loop); - - // Check whether this loop is valid. - // 1. At least 3 vertices. - // 2. All vertices must be unit length. Guaranteed by parsePoints(). - // 3. Loops are not allowed to have any duplicate vertices. - // 4. Non-adjacent edges are not allowed to intersect. - if (!loop->IsValid(&err)) { - return BAD_VALUE("Loop is not valid: " << coordinateElt.toString(false) << " " - << err); - } - // If the loop is more than one hemisphere, invert it. - loop->Normalize(); - - // Check the first loop must be the exterior ring and any others must be - // interior rings or holes. - if (loops.size() > 1 && !loops[0]->Contains(loop)) { - return BAD_VALUE("Secondary loops not contained by first exterior loop - " - "secondary loops must be holes: " << coordinateElt.toString(false) - << " first loop: " << elem.Obj().firstElement().toString(false)); - } + // At least 3 vertices. + if (points.size() < 3) { + return BAD_VALUE( + "Loop must have at least 3 different vertices: " << coordinateElt.toString(false)); } - if (loops.empty()) { - return BAD_VALUE("Polygon has no loops."); - } + S2Loop* loop = new S2Loop(points); + loops.push_back(loop); - // Check if the given loops form a valid polygon. - // 1. If a loop contains an edge AB, then no other loop may contain AB or BA. - // 2. No loop covers more than half of the sphere. - // 3. No two loops cross. - if (!S2Polygon::IsValid(loops.vector(), &err)) - return BAD_VALUE("Polygon isn't valid: " << err << " " << elem.toString(false)); - - // Given all loops are valid / normalized and S2Polygon::IsValid() above returns true. - // The polygon must be valid. See S2Polygon member function IsValid(). - - // Transfer ownership of the loops and clears loop vector. - out->Init(&loops.mutableVector()); - - // Check if every loop of this polygon shares at most one vertex with - // its parent loop. - if (!out->IsNormalized(&err)) - // "err" looks like "Loop 1 shares more than one vertex with its parent loop 0" - return BAD_VALUE(err << ": " << elem.toString(false)); - - // S2Polygon contains more than one ring, which is allowed by S2, but not by GeoJSON. - // - // Loops are indexed according to a preorder traversal of the nesting hierarchy. - // GetLastDescendant() returns the index of the last loop that is contained within - // a given loop. We guarantee that the first loop is the exterior ring. - if (out->GetLastDescendant(0) < out->num_loops() - 1) { - return BAD_VALUE("Only one exterior polygon loop is allowed: " << elem.toString(false)); + // Check whether this loop is valid. + // 1. At least 3 vertices. + // 2. All vertices must be unit length. Guaranteed by parsePoints(). + // 3. Loops are not allowed to have any duplicate vertices. + // 4. Non-adjacent edges are not allowed to intersect. + if (!loop->IsValid(&err)) { + return BAD_VALUE("Loop is not valid: " << coordinateElt.toString(false) << " " << err); } - - // In GeoJSON, only one nesting is allowed. - // The depth of a loop is set by polygon according to the nesting hierarchy of polygon, - // so the exterior ring's depth is 0, a hole in it is 1, etc. - for (int i = 0; i < out->num_loops(); i++) { - if (out->loop(i)->depth() > 1) { - return BAD_VALUE("Polygon interior loops cannot be nested: "<< elem.toString(false)); - } + // If the loop is more than one hemisphere, invert it. + loop->Normalize(); + + // Check the first loop must be the exterior ring and any others must be + // interior rings or holes. + if (loops.size() > 1 && !loops[0]->Contains(loop)) { + return BAD_VALUE( + "Secondary loops not contained by first exterior loop - " + "secondary loops must be holes: " + << coordinateElt.toString(false) + << " first loop: " << elem.Obj().firstElement().toString(false)); } - return Status::OK(); } - static Status parseBigSimplePolygonCoordinates(const BSONElement& elem, - BigSimplePolygon *out) { - if (Array != elem.type()) - return BAD_VALUE("Coordinates of polygon must be an array"); + if (loops.empty()) { + return BAD_VALUE("Polygon has no loops."); + } + // Check if the given loops form a valid polygon. + // 1. If a loop contains an edge AB, then no other loop may contain AB or BA. + // 2. No loop covers more than half of the sphere. + // 3. No two loops cross. + if (!S2Polygon::IsValid(loops.vector(), &err)) + return BAD_VALUE("Polygon isn't valid: " << err << " " << elem.toString(false)); + + // Given all loops are valid / normalized and S2Polygon::IsValid() above returns true. + // The polygon must be valid. See S2Polygon member function IsValid(). + + // Transfer ownership of the loops and clears loop vector. + out->Init(&loops.mutableVector()); + + // Check if every loop of this polygon shares at most one vertex with + // its parent loop. + if (!out->IsNormalized(&err)) + // "err" looks like "Loop 1 shares more than one vertex with its parent loop 0" + return BAD_VALUE(err << ": " << elem.toString(false)); + + // S2Polygon contains more than one ring, which is allowed by S2, but not by GeoJSON. + // + // Loops are indexed according to a preorder traversal of the nesting hierarchy. + // GetLastDescendant() returns the index of the last loop that is contained within + // a given loop. We guarantee that the first loop is the exterior ring. + if (out->GetLastDescendant(0) < out->num_loops() - 1) { + return BAD_VALUE("Only one exterior polygon loop is allowed: " << elem.toString(false)); + } - const vector<BSONElement>& coordinates = elem.Array(); - // Only one loop is allowed in a BigSimplePolygon - if (coordinates.size() != 1) { - return BAD_VALUE("Only one simple loop is allowed in a big polygon: " - << elem.toString(false)); + // In GeoJSON, only one nesting is allowed. + // The depth of a loop is set by polygon according to the nesting hierarchy of polygon, + // so the exterior ring's depth is 0, a hole in it is 1, etc. + for (int i = 0; i < out->num_loops(); i++) { + if (out->loop(i)->depth() > 1) { + return BAD_VALUE("Polygon interior loops cannot be nested: " << elem.toString(false)); } + } + return Status::OK(); +} - vector<S2Point> exteriorVertices; - Status status = Status::OK(); - string err; +static Status parseBigSimplePolygonCoordinates(const BSONElement& elem, BigSimplePolygon* out) { + if (Array != elem.type()) + return BAD_VALUE("Coordinates of polygon must be an array"); - status = parseArrayOfCoordinates(coordinates.front(), &exteriorVertices); - if (!status.isOK()) return status; - status = isLoopClosed(exteriorVertices, coordinates.front()); - if (!status.isOK()) return status; + const vector<BSONElement>& coordinates = elem.Array(); + // Only one loop is allowed in a BigSimplePolygon + if (coordinates.size() != 1) { + return BAD_VALUE( + "Only one simple loop is allowed in a big polygon: " << elem.toString(false)); + } - eraseDuplicatePoints(&exteriorVertices); + vector<S2Point> exteriorVertices; + Status status = Status::OK(); + string err; - // The last point is duplicated. We drop it, since S2Loop expects no - // duplicate points - exteriorVertices.resize(exteriorVertices.size() - 1); + status = parseArrayOfCoordinates(coordinates.front(), &exteriorVertices); + if (!status.isOK()) + return status; - // At least 3 vertices. - if (exteriorVertices.size() < 3) { - return BAD_VALUE("Loop must have at least 3 different vertices: " << - elem.toString(false)); - } + status = isLoopClosed(exteriorVertices, coordinates.front()); + if (!status.isOK()) + return status; - unique_ptr<S2Loop> loop(new S2Loop(exteriorVertices)); - // Check whether this loop is valid. - if (!loop->IsValid(&err)) { - return BAD_VALUE("Loop is not valid: " << elem.toString(false) << " " << err); - } + eraseDuplicatePoints(&exteriorVertices); - out->Init(loop.release()); - return Status::OK(); + // The last point is duplicated. We drop it, since S2Loop expects no + // duplicate points + exteriorVertices.resize(exteriorVertices.size() - 1); + + // At least 3 vertices. + if (exteriorVertices.size() < 3) { + return BAD_VALUE("Loop must have at least 3 different vertices: " << elem.toString(false)); } - // Parse "crs" field of BSON object. - // "crs": { - // "type": "name", - // "properties": { - // "name": "urn:ogc:def:crs:OGC:1.3:CRS84" - // } - // } - static Status parseGeoJSONCRS(const BSONObj &obj, CRS* crs, bool allowStrictSphere = false) { - *crs = SPHERE; + unique_ptr<S2Loop> loop(new S2Loop(exteriorVertices)); + // Check whether this loop is valid. + if (!loop->IsValid(&err)) { + return BAD_VALUE("Loop is not valid: " << elem.toString(false) << " " << err); + } - BSONElement crsElt = obj["crs"]; - // "crs" field doesn't exist, return the default SPHERE - if (crsElt.eoo()) { - return Status::OK(); - } + out->Init(loop.release()); + return Status::OK(); +} + +// Parse "crs" field of BSON object. +// "crs": { +// "type": "name", +// "properties": { +// "name": "urn:ogc:def:crs:OGC:1.3:CRS84" +// } +// } +static Status parseGeoJSONCRS(const BSONObj& obj, CRS* crs, bool allowStrictSphere = false) { + *crs = SPHERE; + + BSONElement crsElt = obj["crs"]; + // "crs" field doesn't exist, return the default SPHERE + if (crsElt.eoo()) { + return Status::OK(); + } - if (!crsElt.isABSONObj()) return BAD_VALUE("GeoJSON CRS must be an object"); - BSONObj crsObj = crsElt.embeddedObject(); - - // "type": "name" - if (String != crsObj["type"].type() || "name" != crsObj["type"].String()) - return BAD_VALUE("GeoJSON CRS must have field \"type\": \"name\""); - - // "properties" - BSONElement propertiesElt = crsObj["properties"]; - if (!propertiesElt.isABSONObj()) - return BAD_VALUE("CRS must have field \"properties\" which is an object"); - BSONObj propertiesObj = propertiesElt.embeddedObject(); - if (String != propertiesObj["name"].type()) - return BAD_VALUE("In CRS, \"properties.name\" must be a string"); - const string& name = propertiesObj["name"].String(); - if (CRS_CRS84 == name || CRS_EPSG_4326 == name) { - *crs = SPHERE; - } else if (CRS_STRICT_WINDING == name) { - if (!allowStrictSphere) { - return BAD_VALUE("Strict winding order is only supported by polygon"); - } - *crs = STRICT_SPHERE; - } else { - return BAD_VALUE("Unknown CRS name: " << name); + if (!crsElt.isABSONObj()) + return BAD_VALUE("GeoJSON CRS must be an object"); + BSONObj crsObj = crsElt.embeddedObject(); + + // "type": "name" + if (String != crsObj["type"].type() || "name" != crsObj["type"].String()) + return BAD_VALUE("GeoJSON CRS must have field \"type\": \"name\""); + + // "properties" + BSONElement propertiesElt = crsObj["properties"]; + if (!propertiesElt.isABSONObj()) + return BAD_VALUE("CRS must have field \"properties\" which is an object"); + BSONObj propertiesObj = propertiesElt.embeddedObject(); + if (String != propertiesObj["name"].type()) + return BAD_VALUE("In CRS, \"properties.name\" must be a string"); + const string& name = propertiesObj["name"].String(); + if (CRS_CRS84 == name || CRS_EPSG_4326 == name) { + *crs = SPHERE; + } else if (CRS_STRICT_WINDING == name) { + if (!allowStrictSphere) { + return BAD_VALUE("Strict winding order is only supported by polygon"); } - return Status::OK(); + *crs = STRICT_SPHERE; + } else { + return BAD_VALUE("Unknown CRS name: " << name); } + return Status::OK(); +} + +// Parse "coordinates" field of GeoJSON LineString +// e.g. "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] +// Or a line in "coordinates" field of GeoJSON MultiLineString +static Status parseGeoJSONLineCoordinates(const BSONElement& elem, S2Polyline* out) { + vector<S2Point> vertices; + Status status = parseArrayOfCoordinates(elem, &vertices); + if (!status.isOK()) + return status; - // Parse "coordinates" field of GeoJSON LineString - // e.g. "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] - // Or a line in "coordinates" field of GeoJSON MultiLineString - static Status parseGeoJSONLineCoordinates(const BSONElement& elem, S2Polyline* out) { - vector<S2Point> vertices; - Status status = parseArrayOfCoordinates(elem, &vertices); - if (!status.isOK()) return status; + eraseDuplicatePoints(&vertices); + if (vertices.size() < 2) + return BAD_VALUE( + "GeoJSON LineString must have at least 2 vertices: " << elem.toString(false)); + + string err; + if (!S2Polyline::IsValid(vertices, &err)) + return BAD_VALUE("GeoJSON LineString is not valid: " << err << " " << elem.toString(false)); + out->Init(vertices); + return Status::OK(); +} + +// Parse legacy point or GeoJSON point, used by geo near. +// Only stored legacy points allow additional fields. +Status parsePoint(const BSONElement& elem, PointWithCRS* out, bool allowAddlFields) { + if (!elem.isABSONObj()) + return BAD_VALUE("Point must be an array or object"); + + BSONObj obj = elem.Obj(); + // location: [1, 2] or location: {x: 1, y:2} + if (Array == elem.type() || obj.firstElement().isNumber()) { + // Legacy point + return GeoParser::parseLegacyPoint(elem, out, allowAddlFields); + } - eraseDuplicatePoints(&vertices); - if (vertices.size() < 2) - return BAD_VALUE("GeoJSON LineString must have at least 2 vertices: " << elem.toString(false)); + // GeoJSON point. location: { type: "Point", coordinates: [1, 2] } + return GeoParser::parseGeoJSONPoint(obj, out); +} - string err; - if (!S2Polyline::IsValid(vertices, &err)) - return BAD_VALUE("GeoJSON LineString is not valid: " << err << " " << elem.toString(false)); - out->Init(vertices); - return Status::OK(); - } +/** exported **/ +Status GeoParser::parseStoredPoint(const BSONElement& elem, PointWithCRS* out) { + return parsePoint(elem, out, true); +} - // Parse legacy point or GeoJSON point, used by geo near. - // Only stored legacy points allow additional fields. - Status parsePoint(const BSONElement &elem, PointWithCRS *out, bool allowAddlFields) { - if (!elem.isABSONObj()) return BAD_VALUE("Point must be an array or object"); +Status GeoParser::parseQueryPoint(const BSONElement& elem, PointWithCRS* out) { + return parsePoint(elem, out, false); +} - BSONObj obj = elem.Obj(); - // location: [1, 2] or location: {x: 1, y:2} - if (Array == elem.type() || obj.firstElement().isNumber()) { - // Legacy point - return GeoParser::parseLegacyPoint(elem, out, allowAddlFields); - } +Status GeoParser::parseLegacyBox(const BSONObj& obj, BoxWithCRS* out) { + Point ptA, ptB; + Status status = Status::OK(); - // GeoJSON point. location: { type: "Point", coordinates: [1, 2] } - return GeoParser::parseGeoJSONPoint(obj, out); + BSONObjIterator coordIt(obj); + status = parseFlatPoint(coordIt.next(), &ptA); + if (!status.isOK()) { + return status; } - - /** exported **/ - Status GeoParser::parseStoredPoint(const BSONElement &elem, PointWithCRS *out) { - return parsePoint(elem, out, true); + status = parseFlatPoint(coordIt.next(), &ptB); + if (!status.isOK()) { + return status; } + // XXX: VERIFY AREA >= 0 - Status GeoParser::parseQueryPoint(const BSONElement &elem, PointWithCRS *out) { - return parsePoint(elem, out, false); + out->box.init(ptA, ptB); + out->crs = FLAT; + return status; +} + +Status GeoParser::parseLegacyPolygon(const BSONObj& obj, PolygonWithCRS* out) { + BSONObjIterator coordIt(obj); + vector<Point> points; + while (coordIt.more()) { + Point p; + // A coordinate + Status status = parseFlatPoint(coordIt.next(), &p); + if (!status.isOK()) + return status; + points.push_back(p); } + if (points.size() < 3) + return BAD_VALUE("Polygon must have at least 3 points"); + out->oldPolygon.init(points); + out->crs = FLAT; + return Status::OK(); +} + +// { "type": "Point", "coordinates": [100.0, 0.0] } +Status GeoParser::parseGeoJSONPoint(const BSONObj& obj, PointWithCRS* out) { + Status status = Status::OK(); + // "crs" + status = parseGeoJSONCRS(obj, &out->crs); + if (!status.isOK()) + return status; - Status GeoParser::parseLegacyBox(const BSONObj& obj, BoxWithCRS *out) { - Point ptA, ptB; - Status status = Status::OK(); + // "coordinates" + status = parseFlatPoint(obj[GEOJSON_COORDINATES], &out->oldPoint, true); + if (!status.isOK()) + return status; - BSONObjIterator coordIt(obj); - status = parseFlatPoint(coordIt.next(), &ptA); - if (!status.isOK()) { return status; } - status = parseFlatPoint(coordIt.next(), &ptB); - if (!status.isOK()) { return status; } - // XXX: VERIFY AREA >= 0 + // Projection + out->crs = FLAT; + if (!ShapeProjection::supportsProject(*out, SPHERE)) + return BAD_VALUE("longitude/latitude is out of bounds, lng: " << out->oldPoint.x << " lat: " + << out->oldPoint.y); + ShapeProjection::projectInto(out, SPHERE); + return Status::OK(); +} + +// { "type": "LineString", "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] } +Status GeoParser::parseGeoJSONLine(const BSONObj& obj, LineWithCRS* out) { + Status status = Status::OK(); + // "crs" + status = parseGeoJSONCRS(obj, &out->crs); + if (!status.isOK()) + return status; - out->box.init(ptA, ptB); - out->crs = FLAT; + // "coordinates" + status = parseGeoJSONLineCoordinates(obj[GEOJSON_COORDINATES], &out->line); + if (!status.isOK()) return status; - } - Status GeoParser::parseLegacyPolygon(const BSONObj& obj, PolygonWithCRS *out) { - BSONObjIterator coordIt(obj); - vector<Point> points; - while (coordIt.more()) { - Point p; - // A coordinate - Status status = parseFlatPoint(coordIt.next(), &p); - if (!status.isOK()) return status; - points.push_back(p); - } - if (points.size() < 3) return BAD_VALUE("Polygon must have at least 3 points"); - out->oldPolygon.init(points); - out->crs = FLAT; - return Status::OK(); - } + return Status::OK(); +} - // { "type": "Point", "coordinates": [100.0, 0.0] } - Status GeoParser::parseGeoJSONPoint(const BSONObj &obj, PointWithCRS *out) { - Status status = Status::OK(); - // "crs" - status = parseGeoJSONCRS(obj, &out->crs); - if (!status.isOK()) return status; - - // "coordinates" - status = parseFlatPoint(obj[GEOJSON_COORDINATES], &out->oldPoint, true); - if (!status.isOK()) return status; - - // Projection - out->crs = FLAT; - if (!ShapeProjection::supportsProject(*out, SPHERE)) - return BAD_VALUE("longitude/latitude is out of bounds, lng: " - << out->oldPoint.x << " lat: " << out->oldPoint.y); - ShapeProjection::projectInto(out, SPHERE); - return Status::OK(); +Status GeoParser::parseGeoJSONPolygon(const BSONObj& obj, PolygonWithCRS* out) { + const BSONElement coordinates = obj[GEOJSON_COORDINATES]; + + Status status = Status::OK(); + // "crs", allow strict sphere + status = parseGeoJSONCRS(obj, &out->crs, true); + if (!status.isOK()) + return status; + + // "coordinates" + if (out->crs == SPHERE) { + out->s2Polygon.reset(new S2Polygon()); + status = parseGeoJSONPolygonCoordinates(coordinates, out->s2Polygon.get()); + } else if (out->crs == STRICT_SPHERE) { + out->bigPolygon.reset(new BigSimplePolygon()); + status = parseBigSimplePolygonCoordinates(coordinates, out->bigPolygon.get()); } + return status; +} - // { "type": "LineString", "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] } - Status GeoParser::parseGeoJSONLine(const BSONObj& obj, LineWithCRS* out) { - Status status = Status::OK(); - // "crs" - status = parseGeoJSONCRS(obj, &out->crs); - if (!status.isOK()) return status; +Status GeoParser::parseMultiPoint(const BSONObj& obj, MultiPointWithCRS* out) { + Status status = Status::OK(); + status = parseGeoJSONCRS(obj, &out->crs); + if (!status.isOK()) + return status; - // "coordinates" - status = parseGeoJSONLineCoordinates(obj[GEOJSON_COORDINATES], &out->line); - if (!status.isOK()) return status; + out->points.clear(); + BSONElement coordElt = obj.getFieldDotted(GEOJSON_COORDINATES); + status = parseArrayOfCoordinates(coordElt, &out->points); + if (!status.isOK()) + return status; - return Status::OK(); + if (0 == out->points.size()) + return BAD_VALUE("MultiPoint coordinates must have at least 1 element"); + out->cells.resize(out->points.size()); + for (size_t i = 0; i < out->points.size(); ++i) { + out->cells[i] = S2Cell(out->points[i]); } - Status GeoParser::parseGeoJSONPolygon(const BSONObj &obj, PolygonWithCRS *out) { - const BSONElement coordinates = obj[GEOJSON_COORDINATES]; + return Status::OK(); +} - Status status = Status::OK(); - // "crs", allow strict sphere - status = parseGeoJSONCRS(obj, &out->crs, true); - if (!status.isOK()) return status; - - // "coordinates" - if (out->crs == SPHERE) { - out->s2Polygon.reset(new S2Polygon()); - status = parseGeoJSONPolygonCoordinates(coordinates, out->s2Polygon.get()); - } - else if (out->crs == STRICT_SPHERE) { - out->bigPolygon.reset(new BigSimplePolygon()); - status = parseBigSimplePolygonCoordinates(coordinates, out->bigPolygon.get()); - } +Status GeoParser::parseMultiLine(const BSONObj& obj, MultiLineWithCRS* out) { + Status status = Status::OK(); + status = parseGeoJSONCRS(obj, &out->crs); + if (!status.isOK()) return status; - } - Status GeoParser::parseMultiPoint(const BSONObj &obj, MultiPointWithCRS *out) { - Status status = Status::OK(); - status = parseGeoJSONCRS(obj, &out->crs); - if (!status.isOK()) return status; - - out->points.clear(); - BSONElement coordElt = obj.getFieldDotted(GEOJSON_COORDINATES); - status = parseArrayOfCoordinates(coordElt, &out->points); - if (!status.isOK()) return status; - - if (0 == out->points.size()) - return BAD_VALUE("MultiPoint coordinates must have at least 1 element"); - out->cells.resize(out->points.size()); - for (size_t i = 0; i < out->points.size(); ++i) { - out->cells[i] = S2Cell(out->points[i]); - } + BSONElement coordElt = obj.getFieldDotted(GEOJSON_COORDINATES); + if (Array != coordElt.type()) + return BAD_VALUE("MultiLineString coordinates must be an array"); - return Status::OK(); - } + out->lines.clear(); + vector<S2Polyline*>& lines = out->lines.mutableVector(); - Status GeoParser::parseMultiLine(const BSONObj &obj, MultiLineWithCRS *out) { - Status status = Status::OK(); - status = parseGeoJSONCRS(obj, &out->crs); - if (!status.isOK()) return status; + BSONObjIterator it(coordElt.Obj()); - BSONElement coordElt = obj.getFieldDotted(GEOJSON_COORDINATES); - if (Array != coordElt.type()) - return BAD_VALUE("MultiLineString coordinates must be an array"); + // Iterate array + while (it.more()) { + lines.push_back(new S2Polyline()); + status = parseGeoJSONLineCoordinates(it.next(), lines.back()); + if (!status.isOK()) + return status; + } + if (0 == lines.size()) + return BAD_VALUE("MultiLineString coordinates must have at least 1 element"); - out->lines.clear(); - vector<S2Polyline*>& lines = out->lines.mutableVector(); + return Status::OK(); +} - BSONObjIterator it(coordElt.Obj()); +Status GeoParser::parseMultiPolygon(const BSONObj& obj, MultiPolygonWithCRS* out) { + Status status = Status::OK(); + status = parseGeoJSONCRS(obj, &out->crs); + if (!status.isOK()) + return status; - // Iterate array - while (it.more()) { - lines.push_back(new S2Polyline()); - status = parseGeoJSONLineCoordinates(it.next(), lines.back()); - if (!status.isOK()) return status; - } - if (0 == lines.size()) - return BAD_VALUE("MultiLineString coordinates must have at least 1 element"); + BSONElement coordElt = obj.getFieldDotted(GEOJSON_COORDINATES); + if (Array != coordElt.type()) + return BAD_VALUE("MultiPolygon coordinates must be an array"); - return Status::OK(); + out->polygons.clear(); + vector<S2Polygon*>& polygons = out->polygons.mutableVector(); + + BSONObjIterator it(coordElt.Obj()); + // Iterate array + while (it.more()) { + polygons.push_back(new S2Polygon()); + status = parseGeoJSONPolygonCoordinates(it.next(), polygons.back()); + if (!status.isOK()) + return status; } + if (0 == polygons.size()) + return BAD_VALUE("MultiPolygon coordinates must have at least 1 element"); - Status GeoParser::parseMultiPolygon(const BSONObj &obj, MultiPolygonWithCRS *out) { - Status status = Status::OK(); - status = parseGeoJSONCRS(obj, &out->crs); - if (!status.isOK()) return status; - - BSONElement coordElt = obj.getFieldDotted(GEOJSON_COORDINATES); - if (Array != coordElt.type()) - return BAD_VALUE("MultiPolygon coordinates must be an array"); - - out->polygons.clear(); - vector<S2Polygon*>& polygons = out->polygons.mutableVector(); - - BSONObjIterator it(coordElt.Obj()); - // Iterate array - while (it.more()) { - polygons.push_back(new S2Polygon()); - status = parseGeoJSONPolygonCoordinates(it.next(), polygons.back()); - if (!status.isOK()) return status; - } - if (0 == polygons.size()) - return BAD_VALUE("MultiPolygon coordinates must have at least 1 element"); + return Status::OK(); +} - return Status::OK(); - } +Status GeoParser::parseLegacyCenter(const BSONObj& obj, CapWithCRS* out) { + BSONObjIterator objIt(obj); + + // Center + BSONElement center = objIt.next(); + Status status = parseFlatPoint(center, &out->circle.center); + if (!status.isOK()) + return status; - Status GeoParser::parseLegacyCenter(const BSONObj& obj, CapWithCRS *out) { - BSONObjIterator objIt(obj); + // Radius + BSONElement radius = objIt.next(); + // radius >= 0 and is not NaN + if (!radius.isNumber() || !(radius.number() >= 0)) + return BAD_VALUE("radius must be a non-negative number"); + + // No more + if (objIt.more()) + return BAD_VALUE("Only 2 fields allowed for circular region"); + + out->circle.radius = radius.number(); + out->crs = FLAT; + return Status::OK(); +} + +Status GeoParser::parseCenterSphere(const BSONObj& obj, CapWithCRS* out) { + BSONObjIterator objIt(obj); + + // Center + BSONElement center = objIt.next(); + Point p; + // Check the object has and only has 2 numbers. + Status status = parseFlatPoint(center, &p); + if (!status.isOK()) + return status; - // Center - BSONElement center = objIt.next(); - Status status = parseFlatPoint(center, &out->circle.center); - if (!status.isOK()) return status; + S2Point centerPoint; + status = coordToPoint(p.x, p.y, ¢erPoint); + if (!status.isOK()) + return status; - // Radius - BSONElement radius = objIt.next(); - // radius >= 0 and is not NaN - if (!radius.isNumber() || !(radius.number() >= 0)) - return BAD_VALUE("radius must be a non-negative number"); + // Radius + BSONElement radiusElt = objIt.next(); + // radius >= 0 and is not NaN + if (!radiusElt.isNumber() || !(radiusElt.number() >= 0)) + return BAD_VALUE("radius must be a non-negative number"); + double radius = radiusElt.number(); + + // No more elements + if (objIt.more()) + return BAD_VALUE("Only 2 fields allowed for circular region"); + + out->cap = S2Cap::FromAxisAngle(centerPoint, S1Angle::Radians(radius)); + out->circle.radius = radius; + out->circle.center = p; + out->crs = SPHERE; + return Status::OK(); +} + +// { "type": "GeometryCollection", +// "geometries": [ +// { "type": "Point", +// "coordinates": [100.0, 0.0] +// }, +// { "type": "LineString", +// "coordinates": [ [101.0, 0.0], [102.0, 1.0] ] +// } +// ] +// } +Status GeoParser::parseGeometryCollection(const BSONObj& obj, GeometryCollection* out) { + BSONElement coordElt = obj.getFieldDotted(GEOJSON_GEOMETRIES); + if (Array != coordElt.type()) + return BAD_VALUE("GeometryCollection geometries must be an array"); + + const vector<BSONElement>& geometries = coordElt.Array(); + if (0 == geometries.size()) + return BAD_VALUE("GeometryCollection geometries must have at least 1 element"); + + for (size_t i = 0; i < geometries.size(); ++i) { + if (Object != geometries[i].type()) + return BAD_VALUE("Element " << i << " of \"geometries\" is not an object"); + + const BSONObj& geoObj = geometries[i].Obj(); + GeoJSONType type = parseGeoJSONType(geoObj); + + if (GEOJSON_UNKNOWN == type) + return BAD_VALUE("Unknown GeoJSON type: " << geometries[i].toString(false)); + + if (GEOJSON_GEOMETRY_COLLECTION == type) + return BAD_VALUE( + "GeometryCollections cannot be nested: " << geometries[i].toString(false)); - // No more - if (objIt.more()) - return BAD_VALUE("Only 2 fields allowed for circular region"); + Status status = Status::OK(); + if (GEOJSON_POINT == type) { + out->points.resize(out->points.size() + 1); + status = parseGeoJSONPoint(geoObj, &out->points.back()); + } else if (GEOJSON_LINESTRING == type) { + out->lines.mutableVector().push_back(new LineWithCRS()); + status = parseGeoJSONLine(geoObj, out->lines.vector().back()); + } else if (GEOJSON_POLYGON == type) { + out->polygons.mutableVector().push_back(new PolygonWithCRS()); + status = parseGeoJSONPolygon(geoObj, out->polygons.vector().back()); + } else if (GEOJSON_MULTI_POINT == type) { + out->multiPoints.mutableVector().push_back(new MultiPointWithCRS()); + status = parseMultiPoint(geoObj, out->multiPoints.mutableVector().back()); + } else if (GEOJSON_MULTI_LINESTRING == type) { + out->multiLines.mutableVector().push_back(new MultiLineWithCRS()); + status = parseMultiLine(geoObj, out->multiLines.mutableVector().back()); + } else if (GEOJSON_MULTI_POLYGON == type) { + out->multiPolygons.mutableVector().push_back(new MultiPolygonWithCRS()); + status = parseMultiPolygon(geoObj, out->multiPolygons.mutableVector().back()); + } else { + // Should not reach here. + invariant(false); + } - out->circle.radius = radius.number(); - out->crs = FLAT; - return Status::OK(); + // Check parsing result. + if (!status.isOK()) + return status; } - Status GeoParser::parseCenterSphere(const BSONObj& obj, CapWithCRS *out) { - BSONObjIterator objIt(obj); + return Status::OK(); +} - // Center - BSONElement center = objIt.next(); - Point p; - // Check the object has and only has 2 numbers. - Status status = parseFlatPoint(center, &p); - if (!status.isOK()) return status; - - S2Point centerPoint; - status = coordToPoint(p.x, p.y, ¢erPoint); - if (!status.isOK()) return status; - - // Radius - BSONElement radiusElt = objIt.next(); - // radius >= 0 and is not NaN - if (!radiusElt.isNumber() || !(radiusElt.number() >= 0)) - return BAD_VALUE("radius must be a non-negative number"); - double radius = radiusElt.number(); - - // No more elements - if (objIt.more()) - return BAD_VALUE("Only 2 fields allowed for circular region"); - - out->cap = S2Cap::FromAxisAngle(centerPoint, S1Angle::Radians(radius)); - out->circle.radius = radius; - out->circle.center = p; - out->crs = SPHERE; - return Status::OK(); +bool GeoParser::parsePointWithMaxDistance(const BSONObj& obj, PointWithCRS* out, double* maxOut) { + BSONObjIterator it(obj); + if (!it.more()) { + return false; } - // { "type": "GeometryCollection", - // "geometries": [ - // { "type": "Point", - // "coordinates": [100.0, 0.0] - // }, - // { "type": "LineString", - // "coordinates": [ [101.0, 0.0], [102.0, 1.0] ] - // } - // ] - // } - Status GeoParser::parseGeometryCollection(const BSONObj &obj, GeometryCollection *out) { - BSONElement coordElt = obj.getFieldDotted(GEOJSON_GEOMETRIES); - if (Array != coordElt.type()) - return BAD_VALUE("GeometryCollection geometries must be an array"); - - const vector<BSONElement>& geometries = coordElt.Array(); - if (0 == geometries.size()) - return BAD_VALUE("GeometryCollection geometries must have at least 1 element"); - - for (size_t i = 0; i < geometries.size(); ++i) { - if (Object != geometries[i].type()) - return BAD_VALUE("Element " << i << " of \"geometries\" is not an object"); - - const BSONObj& geoObj = geometries[i].Obj(); - GeoJSONType type = parseGeoJSONType(geoObj); - - if (GEOJSON_UNKNOWN == type) - return BAD_VALUE("Unknown GeoJSON type: " << geometries[i].toString(false)); - - if (GEOJSON_GEOMETRY_COLLECTION == type) - return BAD_VALUE("GeometryCollections cannot be nested: " - << geometries[i].toString(false)); - - Status status = Status::OK(); - if (GEOJSON_POINT == type) { - out->points.resize(out->points.size() + 1); - status = parseGeoJSONPoint(geoObj, &out->points.back()); - } else if (GEOJSON_LINESTRING == type) { - out->lines.mutableVector().push_back(new LineWithCRS()); - status = parseGeoJSONLine(geoObj, out->lines.vector().back()); - } else if (GEOJSON_POLYGON == type) { - out->polygons.mutableVector().push_back(new PolygonWithCRS()); - status = parseGeoJSONPolygon(geoObj, out->polygons.vector().back()); - } else if (GEOJSON_MULTI_POINT == type) { - out->multiPoints.mutableVector().push_back(new MultiPointWithCRS()); - status = parseMultiPoint(geoObj, out->multiPoints.mutableVector().back()); - } else if (GEOJSON_MULTI_LINESTRING == type) { - out->multiLines.mutableVector().push_back(new MultiLineWithCRS()); - status = parseMultiLine(geoObj, out->multiLines.mutableVector().back()); - } else if (GEOJSON_MULTI_POLYGON == type) { - out->multiPolygons.mutableVector().push_back(new MultiPolygonWithCRS()); - status = parseMultiPolygon(geoObj, out->multiPolygons.mutableVector().back()); - } else { - // Should not reach here. - invariant(false); - } - - // Check parsing result. - if (!status.isOK()) return status; - } + BSONElement lng = it.next(); + if (!lng.isNumber()) { + return false; + } + if (!it.more()) { + return false; + } - return Status::OK(); + BSONElement lat = it.next(); + if (!lat.isNumber()) { + return false; + } + if (!it.more()) { + return false; } - bool GeoParser::parsePointWithMaxDistance(const BSONObj& obj, PointWithCRS* out, double* maxOut) { - BSONObjIterator it(obj); - if (!it.more()) { return false; } - - BSONElement lng = it.next(); - if (!lng.isNumber()) { return false; } - if (!it.more()) { return false; } - - BSONElement lat = it.next(); - if (!lat.isNumber()) { return false; } - if (!it.more()) { return false; } - - BSONElement dist = it.next(); - if (!dist.isNumber()) { return false; } - if (it.more()) { return false; } - - out->oldPoint.x = lng.number(); - out->oldPoint.y = lat.number(); - out->crs = FLAT; - *maxOut = dist.number(); - return true; - } - - GeoParser::GeoSpecifier GeoParser::parseGeoSpecifier(const BSONElement& type) { - if (!type.isABSONObj()) { return GeoParser::UNKNOWN; } - const char* fieldName = type.fieldName(); - if (mongoutils::str::equals(fieldName, "$box")) { - return GeoParser::BOX; - } else if (mongoutils::str::equals(fieldName, "$center")) { - return GeoParser::CENTER; - } else if (mongoutils::str::equals(fieldName, "$polygon")) { - return GeoParser::POLYGON; - } else if (mongoutils::str::equals(fieldName, "$centerSphere")) { - return GeoParser::CENTER_SPHERE; - } else if (mongoutils::str::equals(fieldName, "$geometry")) { - return GeoParser::GEOMETRY; - } + BSONElement dist = it.next(); + if (!dist.isNumber()) { + return false; + } + if (it.more()) { + return false; + } + + out->oldPoint.x = lng.number(); + out->oldPoint.y = lat.number(); + out->crs = FLAT; + *maxOut = dist.number(); + return true; +} + +GeoParser::GeoSpecifier GeoParser::parseGeoSpecifier(const BSONElement& type) { + if (!type.isABSONObj()) { return GeoParser::UNKNOWN; } + const char* fieldName = type.fieldName(); + if (mongoutils::str::equals(fieldName, "$box")) { + return GeoParser::BOX; + } else if (mongoutils::str::equals(fieldName, "$center")) { + return GeoParser::CENTER; + } else if (mongoutils::str::equals(fieldName, "$polygon")) { + return GeoParser::POLYGON; + } else if (mongoutils::str::equals(fieldName, "$centerSphere")) { + return GeoParser::CENTER_SPHERE; + } else if (mongoutils::str::equals(fieldName, "$geometry")) { + return GeoParser::GEOMETRY; + } + return GeoParser::UNKNOWN; +} - GeoParser::GeoJSONType GeoParser::parseGeoJSONType(const BSONObj& obj) { - BSONElement type = obj.getFieldDotted(GEOJSON_TYPE); - if (String != type.type()) { return GeoParser::GEOJSON_UNKNOWN; } - const string& typeString = type.String(); - if (GEOJSON_TYPE_POINT == typeString) { - return GeoParser::GEOJSON_POINT; - } else if (GEOJSON_TYPE_LINESTRING == typeString) { - return GeoParser::GEOJSON_LINESTRING; - } else if (GEOJSON_TYPE_POLYGON == typeString) { - return GeoParser::GEOJSON_POLYGON; - } else if (GEOJSON_TYPE_MULTI_POINT == typeString) { - return GeoParser::GEOJSON_MULTI_POINT; - } else if (GEOJSON_TYPE_MULTI_LINESTRING == typeString) { - return GeoParser::GEOJSON_MULTI_LINESTRING; - } else if (GEOJSON_TYPE_MULTI_POLYGON == typeString) { - return GeoParser::GEOJSON_MULTI_POLYGON; - } else if (GEOJSON_TYPE_GEOMETRY_COLLECTION == typeString) { - return GeoParser::GEOJSON_GEOMETRY_COLLECTION; - } +GeoParser::GeoJSONType GeoParser::parseGeoJSONType(const BSONObj& obj) { + BSONElement type = obj.getFieldDotted(GEOJSON_TYPE); + if (String != type.type()) { return GeoParser::GEOJSON_UNKNOWN; } + const string& typeString = type.String(); + if (GEOJSON_TYPE_POINT == typeString) { + return GeoParser::GEOJSON_POINT; + } else if (GEOJSON_TYPE_LINESTRING == typeString) { + return GeoParser::GEOJSON_LINESTRING; + } else if (GEOJSON_TYPE_POLYGON == typeString) { + return GeoParser::GEOJSON_POLYGON; + } else if (GEOJSON_TYPE_MULTI_POINT == typeString) { + return GeoParser::GEOJSON_MULTI_POINT; + } else if (GEOJSON_TYPE_MULTI_LINESTRING == typeString) { + return GeoParser::GEOJSON_MULTI_LINESTRING; + } else if (GEOJSON_TYPE_MULTI_POLYGON == typeString) { + return GeoParser::GEOJSON_MULTI_POLYGON; + } else if (GEOJSON_TYPE_GEOMETRY_COLLECTION == typeString) { + return GeoParser::GEOJSON_GEOMETRY_COLLECTION; + } + return GeoParser::GEOJSON_UNKNOWN; +} } // namespace mongo |