summaryrefslogtreecommitdiff
path: root/src/mongo/db/geo/geoparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/geo/geoparser.cpp')
-rw-r--r--src/mongo/db/geo/geoparser.cpp1228
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, &centerPoint);
+ 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, &centerPoint);
- 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