GeoSearch::GeoSearch(Collection* collection, TwoDAccessMethod* accessMethod, const Point& startPt, int numWanted, MatchExpression* filter, double maxDistance, GeoDistType type) : GeoHopper(collection, accessMethod, numWanted, startPt, filter, maxDistance, type), _start(accessMethod->getParams().geoHashConverter->hash(startPt.x, startPt.y)), _numWanted(numWanted), _type(type), _params(accessMethod->getParams()) { _nscanned = 0; _found = 0; if(_maxDistance < 0){ _scanDistance = numeric_limits<double>::max(); } else if (type == GEO_PLANE) { _scanDistance = maxDistance + _params.geoHashConverter->getError(); } else if (type == GEO_SPHERE) { checkEarthBounds(startPt); // TODO: consider splitting into x and y scan distances _scanDistance = computeXScanDistance(startPt.y, rad2deg(_maxDistance) + _params.geoHashConverter->getError()); } verify(_scanDistance > 0); }
GeoCircleBrowse::GeoCircleBrowse(const TwoDParams& params, TwoDAccessMethod* accessMethod) : GeoBrowse(accessMethod, "circle", params.filter, params.gq.uniqueDocs()) { _converter = accessMethod->getParams().geoHashConverter; const CapWithCRS& cap = *params.gq.getGeometry()._cap; _startPt = cap.circle.center; _start = _converter->hash(_startPt); _maxDistance = cap.circle.radius; if (FLAT == cap.crs) { _type = GEO_PLANE; xScanDistance = _maxDistance + _converter->getError(); yScanDistance = _maxDistance + _converter->getError(); } else { _type = GEO_SPHERE; yScanDistance = rad2deg(_maxDistance) + _converter->getError(); xScanDistance = computeXScanDistance(_startPt.y, yScanDistance); } // Bounding box includes fudge factor. // TODO: Is this correct, since fudge factor may be spherically transformed? _bBox._min = Point(_startPt.x - xScanDistance, _startPt.y - yScanDistance); _bBox._max = Point(_startPt.x + xScanDistance, _startPt.y + yScanDistance); ok(); }
static R2Annulus projectBoundsToTwoDDegrees(R2Annulus sphereBounds) { const double outerDegrees = rad2deg(sphereBounds.getOuter() / kRadiusOfEarthInMeters); const double innerDegrees = rad2deg(sphereBounds.getInner() / kRadiusOfEarthInMeters); const double maxErrorDegrees = computeXScanDistance(sphereBounds.center().y, outerDegrees); return R2Annulus(sphereBounds.center(), max(0.0, innerDegrees - maxErrorDegrees), outerDegrees + maxErrorDegrees); }
/** * 2d indices don't handle wrapping so we can't use them for queries that wrap. */ static bool twoDWontWrap(const Circle& circle, const IndexEntry& index) { GeoHashConverter::Parameters hashParams; Status paramStatus = GeoHashConverter::parseParameters(index.infoObj, &hashParams); verify(paramStatus.isOK()); // we validated the params on index creation GeoHashConverter conv(hashParams); // FYI: old code used flat not spherical error. double yscandist = rad2deg(circle.radius) + conv.getErrorSphere(); double xscandist = computeXScanDistance(circle.center.y, yscandist); bool ret = circle.center.x + xscandist < 180 && circle.center.x - xscandist > -180 && circle.center.y + yscandist < 90 && circle.center.y - yscandist > -90; return ret; }
/** * 2d indices don't handle wrapping so we can't use them for queries that wrap. */ static bool twoDWontWrap(const Circle& circle, const IndexEntry& index) { // XXX: where does this really belong GeoHashConverter::Parameters params; params.bits = static_cast<unsigned>(fieldWithDefault(index.infoObj, "bits", 26)); params.max = fieldWithDefault(index.infoObj, "max", 180.0); params.min = fieldWithDefault(index.infoObj, "min", -180.0); double numBuckets = (1024 * 1024 * 1024 * 4.0); params.scaling = numBuckets / (params.max - params.min); GeoHashConverter conv(params); // FYI: old code used flat not spherical error. double yscandist = rad2deg(circle.radius) + conv.getErrorSphere(); double xscandist = computeXScanDistance(circle.center.y, yscandist); bool ret = circle.center.x + xscandist < 180 && circle.center.x - xscandist > -180 && circle.center.y + yscandist < 90 && circle.center.y - yscandist > -90; return ret; }
void GeoSearch::exec() { if(_numWanted == 0) return; /* * Search algorithm * 1) use geohash prefix to find X items * 2) compute max distance from want to an item * 3) find optimal set of boxes that complete circle * 4) use regular btree cursors to scan those boxes */ // Part 1 { do { long long f = found(); verify(f <= 0x7fffffff); fillStack(maxPointsHeuristic, _numWanted - static_cast<int>(f), true); processExtraPoints(); } while(_state != DONE && _state != DONE_NEIGHBOR && found() < _numWanted && (!_prefix.constrains() || _params.geoHashConverter->sizeEdge(_prefix) <= _scanDistance)); // If we couldn't scan or scanned everything, we're done if(_state == DONE){ expandEndPoints(); return; } } // Part 2 { // Find farthest distance for completion scan double farDist = farthest(); if(found() < _numWanted) { // Not enough found in Phase 1 farDist = _scanDistance; } else if (_type == GEO_PLANE) { // Enough found, but need to search neighbor boxes farDist += _params.geoHashConverter->getError(); } else if (_type == GEO_SPHERE) { // Enough found, but need to search neighbor boxes farDist = std::min(_scanDistance, computeXScanDistance(_near.y, rad2deg(farDist)) + 2 * _params.geoHashConverter->getError()); } verify(farDist >= 0); // Find the box that includes all the points we need to return _want = Box(_near.x - farDist, _near.y - farDist, farDist * 2); // Remember the far distance for further scans _scanDistance = farDist; // Reset the search, our distances have probably changed if(_state == DONE_NEIGHBOR){ _state = DOING_EXPAND; _neighbor = -1; } // Do regular search in the full region do { fillStack(maxPointsHeuristic); processExtraPoints(); } while(_state != DONE); } expandEndPoints(); }