diff --git a/src/Navaids/positioned.cxx b/src/Navaids/positioned.cxx index b01418a17..133dc7a41 100644 --- a/src/Navaids/positioned.cxx +++ b/src/Navaids/positioned.cxx @@ -37,6 +37,9 @@ typedef std::multimap NamedPositionedIndex; typedef std::pair NamedIndexRange; +using std::lower_bound; +using std::upper_bound; + /** * Order positioned elements by type, then pointer address. This allows us to * use range searches (lower_ and upper_bound) to grab items of a particular @@ -52,6 +55,21 @@ public: } }; +class LowerLimitOfType +{ +public: + bool operator()(const FGPositioned* a, const FGPositioned::Type b) const + { + return a->type() < b; + } + + bool operator()(const FGPositioned::Type a, const FGPositioned* b) const + { + return a < b->type(); + } +}; + + typedef std::set BucketEntry; typedef std::map SpatialPositionedIndex; @@ -122,6 +140,12 @@ spatialFilterInBucket(const SGBucket& aBucket, FGPositioned::Filter* aFilter, FG return; } + if (aFilter->hasTypeRange()) { + // avoid many calls to the filter hook + l = lower_bound(it->second.begin(), it->second.end(), aFilter->minType(), LowerLimitOfType()); + u = upper_bound(l, it->second.end(), aFilter->maxType(), LowerLimitOfType()); + } + for ( ; l != u; ++l) { if ((*aFilter)(*l)) { aResult.push_back(*l); @@ -148,55 +172,6 @@ spatialFind(const SGGeod& aPos, double aRange, } // of i-iteration } -/* -class LowerLimitOfType -{ -public: - bool operator()(const FGPositioned* a, const FGPositioned::Type b) const - { - return a->type() < b; - } - - bool operator()(const FGPositioned::Type a, const FGPositioned* b) const - { - return a < b->type(); - } -}; - - -static void -spatialFindTyped(const SGGeod& aPos, double aRange, FGPositioned::Type aLower, FGPositioned::Type aUpper, FGPositioned::List& aResult) -{ - SGBucket buck(aPos); - double lat = aPos.getLatitudeDeg(), - lon = aPos.getLongitudeDeg(); - - int bx = (int)( aRange*SG_NM_TO_METER / buck.get_width_m() / 2); - int by = (int)( aRange*SG_NM_TO_METER / buck.get_height_m() / 2 ); - - // loop over bucket range - for ( int i=-bx; i<=bx; i++) { - for ( int j=-by; j<=by; j++) { - buck = sgBucketOffset(lon, lat, i, j); - - SpatialPositionedIndex::const_iterator it; - it = global_spatialIndex.find(buck.gen_index()); - if (it == global_spatialIndex.end()) { - continue; - } - - BucketEntry::const_iterator l = std::lower_bound(it->second.begin(), it->second.end(), aLower, LowerLimitOfType()); - BucketEntry::const_iterator u = std::upper_bound(l, it->second.end(), aUpper, LowerLimitOfType()); - - for ( ; l != u; ++l) { - aResult.push_back(*l); - } - - } // of j-iteration - } // of i-iteration -} -*/ - /** */ class RangePredictate @@ -263,12 +238,19 @@ namedFindClosest(const std::string& aIdent, const SGGeod& aOrigin, FGPositioned: // sequential iterators, not random-access ones NamedPositionedIndex::const_iterator check = range.first; if (++check == range.second) { - // excellent, only one match in the range - all we care about is the type - if (aFilter && !aFilter->pass(range.first->second)) { - return NULL; // type check failed - } - - return range.first->second; + // excellent, only one match in the range + FGPositioned* r = range.first->second; + if (aFilter) { + if (aFilter->hasTypeRange() && !aFilter->passType(r->type())) { + return NULL; + } + + if (!aFilter->pass(r)) { + return NULL; + } + } // of have a filter + + return r; } // of short-circuit logic for single-element range // multiple matches, we need to actually check the distance to each one @@ -278,8 +260,15 @@ namedFindClosest(const std::string& aIdent, const SGGeod& aOrigin, FGPositioned: SGVec3d cartOrigin(SGVec3d::fromGeod(aOrigin)); for (; it != range.second; ++it) { - if (aFilter && !aFilter->pass(range.first->second)) { - continue; + FGPositioned::Type ty = range.first->second->type(); + if (aFilter) { + if (aFilter->hasTypeRange() && !aFilter->passType(ty)) { + continue; + } + + if (!aFilter->pass(range.first->second)) { + continue; + } } // find distance @@ -428,6 +417,22 @@ char** searchAirportNamesAndIdents(const std::string& aFilter) /////////////////////////////////////////////////////////////////////////////// +bool +FGPositioned::Filter::hasTypeRange() const +{ + assert(minType() <= maxType()); + return (minType() != INVALID) && (maxType() != INVALID); +} + +bool +FGPositioned::Filter::passType(Type aTy) const +{ + assert(hasTypeRange()); + return (minType() <= aTy) && (maxType() >= aTy); +} + +/////////////////////////////////////////////////////////////////////////////// + FGPositioned::FGPositioned(Type ty, const std::string& aIdent, const SGGeod& aPos, bool aIndexed) : mType(ty), mPosition(aPos), diff --git a/src/Navaids/positioned.hxx b/src/Navaids/positioned.hxx index 1c275dcf4..088955fdc 100644 --- a/src/Navaids/positioned.hxx +++ b/src/Navaids/positioned.hxx @@ -113,6 +113,22 @@ public: virtual bool pass(FGPositioned* aPos) const { return true; } + virtual Type minType() const + { return INVALID; } + + virtual Type maxType() const + { return INVALID; } + + /** + * Test if this filter has a non-empty type range + */ + bool hasTypeRange() const; + + /** + * Assuming hasTypeRange is true, test if a given type passes the range + */ + bool passType(Type aTy) const; + bool operator()(FGPositioned* aPos) const { return pass(aPos); } };