diff --git a/arangod/GeoIndex/GeoIndex.cpp b/arangod/GeoIndex/GeoIndex.cpp index 040cde88c2..14c9b248d7 100644 --- a/arangod/GeoIndex/GeoIndex.cpp +++ b/arangod/GeoIndex/GeoIndex.cpp @@ -654,10 +654,13 @@ void GeoMkDetail(GeoIx* gix, GeoDetailedPoint* gd, GeoCoordinate* c) { gd->gc = c; /* The GeoString computation takes about 0.17 microseconds */ gd->gs = GeoMkHilbert(c); + double const lat = c->latitude * M_PI / 180.0; + double const lon = c->longitude * M_PI / 180.0; + double const latCos = cos(lat); /* This part takes about 0.32 microseconds */ - gd->z = sin(c->latitude * M_PI / 180.0); - gd->x = cos(c->latitude * M_PI / 180.0) * cos(c->longitude * M_PI / 180.0); - gd->y = cos(c->latitude * M_PI / 180.0) * sin(c->longitude * M_PI / 180.0); + gd->z = sin(lat); + gd->x = latCos * cos(lon); + gd->y = latCos * sin(lon); /* And this bit takes about 0.45 microseconds */ for (int i = 0; i < GeoIndexFIXEDPOINTS; i++) { double xx1 = (gix->fixed.x)[i]; @@ -927,7 +930,7 @@ int GeoResultsGrow(GeoResults* gr) { /* distances that could be calculated by a separate */ /* call to GeoIndex_distance because of rounding errors*/ /* =================================================== */ -GeoCoordinates* GeoAnswers(GeoIx* gix, GeoResults* gr) { +GeoCoordinates* GeoAnswers(GeoIx* gix, GeoResults* gr, bool withDistances) { GeoCoordinates* ans; GeoCoordinate* gc; int i, j; @@ -967,11 +970,14 @@ GeoCoordinates* GeoAnswers(GeoIx* gix, GeoResults* gr) { ans->coordinates[j].latitude = (gix->gc)[slot].latitude; ans->coordinates[j].longitude = (gix->gc)[slot].longitude; ans->coordinates[j].data = (gix->gc)[slot].data; - mole = sqrt(gr->snmd[i]); - if (mole > 2.0) mole = 2.0; /* make sure arcsin succeeds! */ - gr->snmd[j] = 2.0 * EARTHRADIAN * asin(mole / 2.0); + if (withDistances) { + mole = sqrt(gr->snmd[i]); + if (mole > 2.0) mole = 2.0; /* make sure arcsin succeeds! */ + gr->snmd[j] = 2.0 * EARTHRADIAN * asin(mole / 2.0); + } j++; } + // note that these are uncalculated if withDistances is false! ans->distances = gr->snmd; TRI_Free(TRI_UNKNOWN_MEM_ZONE, gr->slot); @@ -1094,7 +1100,7 @@ GeoCoordinates* GeoIndex_PointsWithinRadius(GeoIdx* gi, GeoCoordinate* c, gk.potid[gk.stacksize++] = gp->RorPoints; } } - answer = GeoAnswers(gix, gres); + answer = GeoAnswers(gix, gres, true); return answer; /* note - this may be NULL */ } /* =================================================== */ @@ -1159,7 +1165,7 @@ GeoCoordinates* GeoIndex_NearestCountPoints(GeoIdx* gi, GeoCoordinate* c, } } } - answer = GeoAnswers(gix, gr); + answer = GeoAnswers(gix, gr, true); return answer; /* note - this may be NULL */ } /* =================================================== */ @@ -2060,7 +2066,7 @@ GeoCursor* GeoIndex_NewCursor(GeoIdx* gi, GeoCoordinate* c) { return (GeoCursor*)gcr; } -GeoCoordinates* GeoIndex_ReadCursor(GeoCursor* gc, int count) { +GeoCoordinates* GeoIndex_ReadCursor(GeoCursor* gc, int count, bool withDistances) { int i, j, r; GeoCoordinate* ct; GeoResults* gr; @@ -2137,7 +2143,7 @@ GeoCoordinates* GeoIndex_ReadCursor(GeoCursor* gc, int count) { } } } - gcts = GeoAnswers(gcr->Ix, gr); + gcts = GeoAnswers(gcr->Ix, gr, withDistances); return gcts; } diff --git a/arangod/GeoIndex/GeoIndex.h b/arangod/GeoIndex/GeoIndex.h index ebaef46677..b402877505 100644 --- a/arangod/GeoIndex/GeoIndex.h +++ b/arangod/GeoIndex/GeoIndex.h @@ -101,7 +101,7 @@ GeoCoordinates* GeoIndex_PointsWithinRadius(GeoIdx* gi, GeoCoordinate* c, GeoCoordinates* GeoIndex_NearestCountPoints(GeoIdx* gi, GeoCoordinate* c, int count); GeoCursor* GeoIndex_NewCursor(GeoIdx* gi, GeoCoordinate* c); -GeoCoordinates* GeoIndex_ReadCursor(GeoCursor* gc, int count); +GeoCoordinates* GeoIndex_ReadCursor(GeoCursor* gc, int count, bool withDistances = true); void GeoIndex_CursorFree(GeoCursor* gc); void GeoIndex_CoordinatesFree(GeoCoordinates* clist); #ifdef TRI_GEO_DEBUG diff --git a/arangod/Indexes/GeoIndex.cpp b/arangod/Indexes/GeoIndex.cpp index 3de6505777..87ea413754 100644 --- a/arangod/Indexes/GeoIndex.cpp +++ b/arangod/Indexes/GeoIndex.cpp @@ -127,31 +127,66 @@ void GeoIndexIterator::nextBabies(std::vector& result, size_t } if (batchSize > 0) { - auto coords = std::unique_ptr(::GeoIndex_ReadCursor(_cursor, batchSize)); + // only need to calculate distances for WITHIN queries, but not for NEAR queries + bool const withDistances = !_near; + auto coords = std::unique_ptr(::GeoIndex_ReadCursor(_cursor, batchSize, withDistances)); size_t const length = coords ? coords->length : 0; - if (!length) { + + if (length == 0) { return; } // determine which documents to return... size_t numDocs = length; + if (!_near) { // WITHIN // only return those documents that are within the specified radius TRI_ASSERT(numDocs > 0); - // scan backwards because documents with higher distances are more interesting - // this can be improved to use a binary search if block size is increased in the future - while ((_inclusive && coords->distances[numDocs - 1] > _radius) || - (!_inclusive && coords->distances[numDocs - 1] >= _radius)) { - // document is outside the specified radius! - --numDocs; - if (numDocs == 0) { - break; + + if (numDocs <= 8) { + // linear scan for the first document outside the specified radius + // scan backwards because documents with higher distances are more interesting + while ((_inclusive && coords->distances[numDocs - 1] > _radius) || + (!_inclusive && coords->distances[numDocs - 1] >= _radius)) { + // document is outside the specified radius! + --numDocs; + if (numDocs == 0) { + break; + } + } + } else { + // binary search for documents inside/outside the specified radius + size_t l = 0; + size_t r = numDocs - 1; + + while (true) { + // determine midpoint + size_t m = l + ((r - l) / 2); + if ((_inclusive && coords->distances[m] > _radius) || + (!_inclusive && coords->distances[m] >= _radius)) { + // document is outside the specified radius! + if (m == 0) { + numDocs = 0; + break; + } + r = m - 1; + } else { + // still inside the radius + numDocs = m + 1; + l = m + 1; + } + + if (r < l) { + break; + } } } } - + + result.reserve(numDocs); + for (size_t i = 0; i < numDocs; ++i) { result.emplace_back(IndexLookupResult(::GeoIndex::toRevision(coords->coordinates[i].data))); }