static bool parseGeoJSONPolygonCoordinates(const vector<BSONElement>& coordinates, const BSONObj &sourceObject, S2Polygon *out) { const vector<BSONElement>& exteriorRing = coordinates[0].Array(); vector<S2Point> exteriorVertices; if (!parsePoints(exteriorRing, &exteriorVertices)) { return false; } eraseDuplicatePoints(&exteriorVertices); // The last point is duplicated. We drop it, since S2Loop expects no // duplicate points exteriorVertices.resize(exteriorVertices.size() - 1); // S2 Polygon loops must have 3 vertices if (exteriorVertices.size() < 3) { return false; } S2PolygonBuilderOptions polyOptions; polyOptions.set_validate(true); // Don't silently eliminate duplicate edges. polyOptions.set_xor_edges(false); S2PolygonBuilder polyBuilder(polyOptions); S2Loop exteriorLoop(exteriorVertices); exteriorLoop.Normalize(); if (exteriorLoop.is_hole()) { exteriorLoop.Invert(); } if (!exteriorLoop.IsValid()) { return false; } polyBuilder.AddLoop(&exteriorLoop); // Subsequent arrays of coordinates are interior rings/holes. for (size_t i = 1; i < coordinates.size(); ++i) { vector<S2Point> holePoints; if (!parsePoints(coordinates[i].Array(), &holePoints)) { return false; } eraseDuplicatePoints(&holePoints); // Drop the duplicated last point. holePoints.resize(holePoints.size() - 1); // S2 Polygon loops must have 3 vertices if (holePoints.size() < 3) { return false; } // Interior rings are clockwise. S2Loop holeLoop(holePoints); holeLoop.Normalize(); if (!holeLoop.IsValid()) { return false; } if (!holeLoop.is_hole()) { if (!exteriorLoop.Contains(&holeLoop)) { return false; } holeLoop.Invert(); } else { // It's already clockwise; we need to invert once to check that it's contained in // the shell, then invert again. holeLoop.Invert(); if (!exteriorLoop.Contains(&holeLoop)) { return false; } holeLoop.Invert(); } polyBuilder.AddLoop(&holeLoop); } return polyBuilder.AssemblePolygon(out, NULL); }
void GeoParser::parseGeoJSONPolygon(const BSONObj& obj, S2Polygon* out) { const vector<BSONElement>& coordinates = obj.getFieldDotted(GEOJSON_COORDINATES).Array(); const vector<BSONElement>& exteriorRing = coordinates[0].Array(); vector<S2Point> exteriorVertices; parsePoints(exteriorRing, &exteriorVertices); // The last point is duplicated. We drop it, since S2Loop expects no // duplicate points exteriorVertices.resize(exteriorVertices.size() - 1); S2PolygonBuilderOptions polyOptions; polyOptions.set_validate(true); // Don't silently eliminate duplicate edges. polyOptions.set_xor_edges(false); S2PolygonBuilder polyBuilder(polyOptions); S2Loop exteriorLoop(exteriorVertices); exteriorLoop.Normalize(); if (exteriorLoop.is_hole()) { exteriorLoop.Invert(); } uassert(16693, "Exterior shell of polygon is invalid: " + obj.toString(), exteriorLoop.IsValid()); polyBuilder.AddLoop(&exteriorLoop); // Subsequent arrays of coordinates are interior rings/holes. for (size_t i = 1; i < coordinates.size(); ++i) { vector<S2Point> holePoints; parsePoints(coordinates[i].Array(), &holePoints); // Drop the duplicated last point. holePoints.resize(holePoints.size() - 1); // Interior rings are clockwise. S2Loop holeLoop(holePoints); holeLoop.Normalize(); uassert(16694, "Interior hole of polygon is invalid: " + obj.toString(), holeLoop.IsValid()); if (!holeLoop.is_hole()) { holeLoop.Invert(); } polyBuilder.AddLoop(&holeLoop); } uassert(16695, "Couldn't assemble polygon: " + obj.toString(), polyBuilder.AssemblePolygon(out, NULL)); }