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. }
double dist(const S2Point& a, const S2Polyline& b) { int tmp; S1Angle angle(a, b.Project(a, &tmp)); return angle.radians(); }