// Get the index keys for elements that are GeoJSON. void S2AccessMethod::getGeoKeys(const BSONElementSet& elements, BSONObjSet* out) const { S2RegionCoverer coverer; _params.configureCoverer(&coverer); // See here for GeoJSON format: geojson.org/geojson-spec.html for (BSONElementSet::iterator i = elements.begin(); i != elements.end(); ++i) { uassert(16754, "Can't parse geometry from element: " + i->toString(), i->isABSONObj()); const BSONObj &obj = i->Obj(); vector<string> cells; S2Polyline line; S2Cell point; // We only support GeoJSON polygons. Why?: // 1. we don't automagically do WGS84/flat -> WGS84, and // 2. the old polygon format must die. if (GeoParser::isGeoJSONPolygon(obj)) { S2Polygon polygon; GeoParser::parseGeoJSONPolygon(obj, &polygon); keysFromRegion(&coverer, polygon, &cells); } else if (GeoParser::parseLineString(obj, &line)) { keysFromRegion(&coverer, line, &cells); } else if (GeoParser::parsePoint(obj, &point)) { S2CellId parent(point.id().parent(_params.finestIndexedLevel)); cells.push_back(parent.toString()); } else { uasserted(16755, "Can't extract geo keys from object, malformed geometry?:" + obj.toString()); } uassert(16756, "Unable to generate keys for (likely malformed) geometry: " + obj.toString(), cells.size() > 0); for (vector<string>::const_iterator it = cells.begin(); it != cells.end(); ++it) { BSONObjBuilder b; b.append("", *it); out->insert(b.obj()); } } if (0 == out->size()) { BSONObjBuilder b; b.appendNull(""); out->insert(b.obj()); } }
double S2NearCursor::distanceBetween(const QueryGeometry &field, const BSONObj &obj) { S2Point us = field.getCentroid(); S2Point them; S2Polygon polygon; S2Polyline line; S2Cell point; if (GeoJSONParser::parsePolygon(obj, &polygon)) { them = polygon.Project(us); } else if (GeoJSONParser::parseLineString(obj, &line)) { int tmp; them = line.Project(us, &tmp); } else if (GeoJSONParser::parsePoint(obj, &point)) { them = point.GetCenter(); } else { warning() << "unknown geometry: " << obj.toString(); } S1Angle angle(us, them); return angle.radians() * _params.radius; }
double S2NearIndexCursor::distanceTo(const BSONObj& obj) { const S2Point &us = _nearQuery.centroid; S2Point them; S2Polygon polygon; S2Polyline line; S2Cell point; if (GeoParser::parsePolygon(obj, &polygon)) { them = polygon.Project(us); } else if (GeoParser::parseLineString(obj, &line)) { int tmp; them = line.Project(us, &tmp); } else if (GeoParser::parsePoint(obj, &point)) { them = point.GetCenter(); } else { warning() << "unknown geometry: " << obj.toString(); return numeric_limits<double>::max(); } S1Angle angle(us, them); return angle.radians() * _params.radius; }
// Fill _results with the next shell of results. We may have to search several times to do // this. If _results.empty() after calling fillResults, there are no more possible results. void S2NearCursor::fillResults() { verify(_results.empty()); if (_innerRadius >= _outerRadius) { return; } if (_innerRadius > _maxDistance) { return; } // We iterate until 1. our search radius is too big or 2. we find results. do { // Some of these arguments are opaque, look at the definitions of the involved classes. FieldRangeSet frs(_details->parentNS().c_str(), makeFRSObject(), false, false); shared_ptr<FieldRangeVector> frv(new FieldRangeVector(frs, _specForFRV, 1)); scoped_ptr<BtreeCursor> cursor(BtreeCursor::make(nsdetails(_details->parentNS().c_str()), *_details, frv, 0, 1)); // Do the actual search through this annulus. size_t considered = 0; for (; cursor->ok(); cursor->advance()) { ++_nscanned; ++considered; MatchDetails details; bool matched = _matcher->matchesCurrent(cursor.get(), &details); if (!matched) { continue; } const BSONObj& indexedObj = cursor->currLoc().obj(); size_t geoFieldsInRange = 0; double minMatchingDistance = 1e20; // Calculate the distance from our query point(s) to the geo field(s). for (size_t i = 0; i < _fields.size(); ++i) { const GeoQueryField& field = _fields[i]; BSONElementSet geoFieldElements; indexedObj.getFieldsDotted(field.field, geoFieldElements); if (geoFieldElements.empty()) { continue; } S2Point us = field.getCentroid(); for (BSONElementSet::iterator oi = geoFieldElements.begin(); oi != geoFieldElements.end(); ++oi) { const BSONObj &geoObj = oi->Obj(); double dist = -1; S2Point them; if (GeoJSONParser::isPolygon(geoObj)) { S2Polygon shape; GeoJSONParser::parsePolygon(geoObj, &shape); them = shape.Project(us); } else if (GeoJSONParser::isLineString(geoObj)) { S2Polyline shape; GeoJSONParser::parseLineString(geoObj, &shape); int tmp; them = shape.Project(us, &tmp); } else if (GeoJSONParser::isPoint(geoObj)) { S2Cell point; GeoJSONParser::parsePoint(geoObj, &point); them = point.GetCenter(); } S1Angle angle(us, them); dist = angle.radians() * _params.radius; if (dist >= _innerRadius && dist <= _outerRadius) { ++geoFieldsInRange; minMatchingDistance = min(dist, minMatchingDistance); } } } if (_fields.size() == geoFieldsInRange) { if (_returned.end() == _returned.find(cursor->currLoc())) { _results.push(Result(cursor->currLoc(), cursor->currKey(), minMatchingDistance)); } } } if (_results.empty()) { _radiusIncrement *= 2; nextAnnulus(); } } while (_results.empty() && _innerRadius < M_PI * _params.radius); // TODO: consider shrinking _radiusIncrement if _results.size() meets some criteria. }