From 04b30f322de856c9993f328e15cb9a0509be65c9 Mon Sep 17 00:00:00 2001 From: jmt Date: Sat, 26 Sep 2009 11:46:42 +0000 Subject: [PATCH 1/7] Extend FGPositioned to allow mapping from a string to a type. --- src/Navaids/positioned.cxx | 35 +++++++++++++++++++++++++++++++++++ src/Navaids/positioned.hxx | 8 ++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Navaids/positioned.cxx b/src/Navaids/positioned.cxx index f43e4b82f..6324578c3 100644 --- a/src/Navaids/positioned.cxx +++ b/src/Navaids/positioned.cxx @@ -31,6 +31,8 @@ #include #include +#include +#include #include "positioned.hxx" @@ -470,6 +472,39 @@ FGPositioned::cart() const return SGVec3d::fromGeod(mPosition); } +FGPositioned::Type FGPositioned::typeFromName(const std::string& aName) +{ + typedef struct { + const char* _name; + Type _ty; + } NameTypeEntry; + + const NameTypeEntry names[] = { + {"airport", AIRPORT}, + {"vor", VOR}, + {"ndb", NDB}, + {"wpt", WAYPOINT}, + {"fix", FIX}, + {"tacan", TACAN}, + {"dme", DME}, + // aliases + {"waypoint", WAYPOINT}, + + {NULL, INVALID} + }; + + std::string lowerName(simgear::strutils::convertToLowerCase(aName)); + + for (const NameTypeEntry* n = names; (n->_name != NULL); ++n) { + if (::strcmp(n->_name, lowerName.c_str()) == 0) { + return n->_ty; + } + } + + SG_LOG(SG_GENERAL, SG_WARN, "FGPositioned::typeFromName: couldn't match:" << aName); + return INVALID; +} + const char* FGPositioned::nameForType(Type aTy) { switch (aTy) { diff --git a/src/Navaids/positioned.hxx b/src/Navaids/positioned.hxx index 93e4f6bbf..4d1f6081f 100644 --- a/src/Navaids/positioned.hxx +++ b/src/Navaids/positioned.hxx @@ -188,10 +188,14 @@ public: */ static List findClosestN(const SGGeod& aPos, unsigned int aN, double aCutoffNm, Filter* aFilter = NULL); - + /** + * Map a candidate type string to a real type. Returns INVALID if the string + * does not correspond to a defined type. + */ + static Type typeFromName(const std::string& aName); /** - * Debug helper, map a type to a human-readable string + * Map a type to a human-readable string */ static const char* nameForType(Type aTy); protected: From 5755b7ae0e989d2059d69ea0238ba813a73df250 Mon Sep 17 00:00:00 2001 From: jmt Date: Sun, 27 Sep 2009 23:12:58 +0000 Subject: [PATCH 2/7] Further extensions to FGPositioned to support ongoing GPS work. --- src/Navaids/positioned.cxx | 119 +++++++++++++++++++++++++++---------- src/Navaids/positioned.hxx | 13 +++- 2 files changed, 100 insertions(+), 32 deletions(-) diff --git a/src/Navaids/positioned.cxx b/src/Navaids/positioned.cxx index 6324578c3..93204670b 100644 --- a/src/Navaids/positioned.cxx +++ b/src/Navaids/positioned.cxx @@ -474,6 +474,10 @@ FGPositioned::cart() const FGPositioned::Type FGPositioned::typeFromName(const std::string& aName) { + if (aName.empty() || (aName == "")) { + return INVALID; + } + typedef struct { const char* _name; Type _ty; @@ -489,7 +493,8 @@ FGPositioned::Type FGPositioned::typeFromName(const std::string& aName) {"dme", DME}, // aliases {"waypoint", WAYPOINT}, - + {"apt", AIRPORT}, + {NULL, INVALID} }; @@ -585,36 +590,6 @@ FGPositioned::findClosestN(const SGGeod& aPos, unsigned int aN, double aCutoffNm return spatialGetClosest(aPos, aN, aCutoffNm, aFilter); } -/* -FGPositionedRef -FGPositioned::findNextWithPartialId(FGPositionedRef aCur, const std::string& aId, Filter* aFilter) -{ - NamedIndexRange range = global_namedIndex.equal_range(aId); - for (; range.first != range.second; ++range.first) { - FGPositionedRef candidate = range.first->second; - if (aCur == candidate) { - aCur = NULL; // found our start point, next match will pass - continue; - } - - if (aFilter) { - if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) { - continue; - } - - if(!aFilter->pass(candidate)) { - continue; - } - } - - if (!aCur) { - return candidate; - } - } - - return NULL; // fell out, no match in range -}*/ - FGPositionedRef FGPositioned::findNextWithPartialId(FGPositionedRef aCur, const std::string& aId, Filter* aFilter) { @@ -656,4 +631,86 @@ FGPositioned::findNextWithPartialId(FGPositionedRef aCur, const std::string& aId return NULL; // Reached the end of the valid sequence with no match. } +FGPositionedRef +FGPositioned::findWithPartialId(const std::string& aId, Filter* aFilter, int aOffset) +{ + // see comment in findNextWithPartialId concerning upperBoundId + std::string upperBoundId = aId; + upperBoundId[upperBoundId.size()-1]++; + NamedPositionedIndex::const_iterator upperBound = global_namedIndex.lower_bound(upperBoundId); + + NamedIndexRange range = global_namedIndex.equal_range(aId); + + while (range.first != upperBound) { + for (; range.first != range.second; ++range.first) { + FGPositionedRef candidate = range.first->second; + if (aFilter) { + if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) { + continue; + } + + if (!aFilter->pass(candidate)) { + continue; + } + } + + if (aOffset == 0) { + return candidate; + } else { + --aOffset; // seen one more valid result, decrement the count + } + } + + // Unable to match the filter with this range - try the next range. + range = global_namedIndex.equal_range(range.second->first); + } + + return NULL; // Reached the end of the valid sequence with no match. +} + +/** + * Wrapper filter which proxies to an inner filter, but also ensures the + * ident starts with supplied partial ident. + */ +class PartialIdentFilter : public FGPositioned::Filter +{ +public: + PartialIdentFilter(const std::string& ident, FGPositioned::Filter* filter) : + _ident(ident), + _inner(filter) + { ; } + + virtual bool pass(FGPositioned* aPos) const + { + if (!_inner->pass(aPos)) { + return false; + } + + return (::strncmp(aPos->ident().c_str(), _ident.c_str(), _ident.size()) == 0); + } + + virtual FGPositioned::Type minType() const + { return _inner->minType(); } + + virtual FGPositioned::Type maxType() const + { return _inner->maxType(); } + +private: + std::string _ident; + FGPositioned::Filter* _inner; +}; + +FGPositionedRef +FGPositioned::findClosestWithPartialId(const SGGeod& aPos, const std::string& aId, Filter* aFilter, int aOffset) +{ + PartialIdentFilter pf(aId, aFilter); + List matches = spatialGetClosest(aPos, aOffset + 1, 1000.0, &pf); + + if ((int) matches.size() <= aOffset) { + SG_LOG(SG_GENERAL, SG_INFO, "FGPositioned::findClosestWithPartialId, couldn't match enough with prefix:" << aId); + return NULL; // couldn't find a match within the cutoff distance + } + + return matches[aOffset]; +} diff --git a/src/Navaids/positioned.hxx b/src/Navaids/positioned.hxx index 4d1f6081f..b56f3b093 100644 --- a/src/Navaids/positioned.hxx +++ b/src/Navaids/positioned.hxx @@ -158,6 +158,11 @@ public: */ static FGPositionedRef findNextWithPartialId(FGPositionedRef aCur, const std::string& aId, Filter* aFilter = NULL); + /** + * As above, but searches using an offset index + */ + static FGPositionedRef findWithPartialId(const std::string& aId, Filter* aFilter, int aOffset); + /** * Find all items with the specified ident, and return then sorted by * distance from a position @@ -182,12 +187,18 @@ public: * Very large cutoff values will make this slow. * * @result The matches (possibly less than N, depending on the filter and cutoff), - * sorted by distance from the search pos + * sorted by distance from the search pos * @param aN - number of matches to find * @param aCutoffNm - maximum distance to search within, in nautical miles */ static List findClosestN(const SGGeod& aPos, unsigned int aN, double aCutoffNm, Filter* aFilter = NULL); + /** + * Find the closest match based on partial id (with an offset to allow selecting the n-th closest). + * Cutoff distance is limited internally, to avoid making this very slow. + */ + static FGPositionedRef findClosestWithPartialId(const SGGeod& aPos, const std::string& aId, Filter* aFilter, int aOffset); + /** * Map a candidate type string to a real type. Returns INVALID if the string * does not correspond to a defined type. From e1555ba9a570aafc66cef8312f9e18aa4ce18c05 Mon Sep 17 00:00:00 2001 From: jmt Date: Mon, 28 Sep 2009 23:42:10 +0000 Subject: [PATCH 3/7] Positioned support for adding user waypoints. --- src/Navaids/positioned.cxx | 6 ++++++ src/Navaids/positioned.hxx | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/Navaids/positioned.cxx b/src/Navaids/positioned.cxx index 93204670b..28e723811 100644 --- a/src/Navaids/positioned.cxx +++ b/src/Navaids/positioned.cxx @@ -460,6 +460,12 @@ FGPositioned::~FGPositioned() removeFromIndices(this); } +FGPositioned* +FGPositioned::createUserWaypoint(const std::string& aIdent, const SGGeod& aPos) +{ + return new FGPositioned(WAYPOINT, aIdent, aPos, true); +} + SGBucket FGPositioned::bucket() const { diff --git a/src/Navaids/positioned.hxx b/src/Navaids/positioned.hxx index b56f3b093..8ee897aaa 100644 --- a/src/Navaids/positioned.hxx +++ b/src/Navaids/positioned.hxx @@ -209,6 +209,8 @@ public: * Map a type to a human-readable string */ static const char* nameForType(Type aTy); + + static FGPositioned* createUserWaypoint(const std::string& aIdent, const SGGeod& aPos); protected: FGPositioned(Type ty, const std::string& aIdent, const SGGeod& aPos, bool aIndex = true); From 9ebd19babff8a598d378e2bccd09cbe61c36450f Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 1 Oct 2009 00:15:37 +0200 Subject: [PATCH 4/7] Use boost::to_lower_copy instead of function from simgear The simgear function is about to get removed. --- src/Navaids/positioned.cxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Navaids/positioned.cxx b/src/Navaids/positioned.cxx index 28e723811..0553956e1 100644 --- a/src/Navaids/positioned.cxx +++ b/src/Navaids/positioned.cxx @@ -29,10 +29,11 @@ #include +#include + #include #include #include -#include #include "positioned.hxx" @@ -504,7 +505,7 @@ FGPositioned::Type FGPositioned::typeFromName(const std::string& aName) {NULL, INVALID} }; - std::string lowerName(simgear::strutils::convertToLowerCase(aName)); + std::string lowerName(boost::to_lower_copy(aName)); for (const NameTypeEntry* n = names; (n->_name != NULL); ++n) { if (::strcmp(n->_name, lowerName.c_str()) == 0) { From c08cc9693e19a08ad59371ae6bdcb97f6e1117d3 Mon Sep 17 00:00:00 2001 From: jmt Date: Thu, 1 Oct 2009 16:50:54 +0000 Subject: [PATCH 5/7] Bugfix use of type checks in FGPositioned::findAllWithIdentSortedByRange --- src/Navaids/positioned.cxx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Navaids/positioned.cxx b/src/Navaids/positioned.cxx index 0553956e1..e603e9eb0 100644 --- a/src/Navaids/positioned.cxx +++ b/src/Navaids/positioned.cxx @@ -568,8 +568,15 @@ FGPositioned::findAllWithIdentSortedByRange(const std::string& aIdent, const SGG List result; NamedIndexRange range = global_namedIndex.equal_range(aIdent); for (; range.first != range.second; ++range.first) { - if (aFilter && !aFilter->pass(range.first->second)) { - continue; + FGPositioned* candidate = range.first->second; + if (aFilter) { + if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) { + continue; + } + + if (!aFilter->pass(candidate)) { + continue; + } } result.push_back(range.first->second); From 5c2dbb42394ed7fc0aa46568fc9424b124219087 Mon Sep 17 00:00:00 2001 From: jmt Date: Sun, 4 Oct 2009 22:14:14 +0000 Subject: [PATCH 6/7] Extender iterative search APIs with a 'has-next' return arg, to enable better iteration UI. --- src/Navaids/positioned.cxx | 24 ++++++++++++++++++------ src/Navaids/positioned.hxx | 4 ++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/Navaids/positioned.cxx b/src/Navaids/positioned.cxx index e603e9eb0..38bce7d25 100644 --- a/src/Navaids/positioned.cxx +++ b/src/Navaids/positioned.cxx @@ -646,7 +646,7 @@ FGPositioned::findNextWithPartialId(FGPositionedRef aCur, const std::string& aId } FGPositionedRef -FGPositioned::findWithPartialId(const std::string& aId, Filter* aFilter, int aOffset) +FGPositioned::findWithPartialId(const std::string& aId, Filter* aFilter, int aOffset, bool& aNext) { // see comment in findNextWithPartialId concerning upperBoundId std::string upperBoundId = aId; @@ -654,6 +654,7 @@ FGPositioned::findWithPartialId(const std::string& aId, Filter* aFilter, int aOf NamedPositionedIndex::const_iterator upperBound = global_namedIndex.lower_bound(upperBoundId); NamedIndexRange range = global_namedIndex.equal_range(aId); + FGPositionedRef result; while (range.first != upperBound) { for (; range.first != range.second; ++range.first) { @@ -668,8 +669,12 @@ FGPositioned::findWithPartialId(const std::string& aId, Filter* aFilter, int aOf } } - if (aOffset == 0) { - return candidate; + if (result) { + aNext = true; + return result; + } else if (aOffset == 0) { + // okay, found our result. we need to go around once more to set aNext + result = candidate; } else { --aOffset; // seen one more valid result, decrement the count } @@ -679,7 +684,10 @@ FGPositioned::findWithPartialId(const std::string& aId, Filter* aFilter, int aOf range = global_namedIndex.equal_range(range.second->first); } - return NULL; // Reached the end of the valid sequence with no match. + // if we fell out, we reached the end of the valid range. We might have a + // valid result, but we definitiely don't have a next result. + aNext = false; + return result; } /** @@ -715,16 +723,20 @@ private: }; FGPositionedRef -FGPositioned::findClosestWithPartialId(const SGGeod& aPos, const std::string& aId, Filter* aFilter, int aOffset) +FGPositioned::findClosestWithPartialId(const SGGeod& aPos, const std::string& aId, Filter* aFilter, int aOffset, bool& aNext) { PartialIdentFilter pf(aId, aFilter); - List matches = spatialGetClosest(aPos, aOffset + 1, 1000.0, &pf); + // why aOffset +2 ? at offset=3, we want the fourth search result, but also + // to know if the fifth result exists (to set aNext flag for iterative APIs) + List matches = spatialGetClosest(aPos, aOffset + 2, 1000.0, &pf); if ((int) matches.size() <= aOffset) { SG_LOG(SG_GENERAL, SG_INFO, "FGPositioned::findClosestWithPartialId, couldn't match enough with prefix:" << aId); + aNext = false; return NULL; // couldn't find a match within the cutoff distance } + aNext = ((int) matches.size() >= (aOffset + 2)); return matches[aOffset]; } diff --git a/src/Navaids/positioned.hxx b/src/Navaids/positioned.hxx index 8ee897aaa..10591a211 100644 --- a/src/Navaids/positioned.hxx +++ b/src/Navaids/positioned.hxx @@ -161,7 +161,7 @@ public: /** * As above, but searches using an offset index */ - static FGPositionedRef findWithPartialId(const std::string& aId, Filter* aFilter, int aOffset); + static FGPositionedRef findWithPartialId(const std::string& aId, Filter* aFilter, int aOffset, bool& aNext); /** * Find all items with the specified ident, and return then sorted by @@ -197,7 +197,7 @@ public: * Find the closest match based on partial id (with an offset to allow selecting the n-th closest). * Cutoff distance is limited internally, to avoid making this very slow. */ - static FGPositionedRef findClosestWithPartialId(const SGGeod& aPos, const std::string& aId, Filter* aFilter, int aOffset); + static FGPositionedRef findClosestWithPartialId(const SGGeod& aPos, const std::string& aId, Filter* aFilter, int aOffset, bool& aNext); /** * Map a candidate type string to a real type. Returns INVALID if the string From 2d5924939e189c271745a5c449609386471c1cff Mon Sep 17 00:00:00 2001 From: jmt Date: Mon, 5 Oct 2009 19:58:24 +0000 Subject: [PATCH 7/7] Extend FGPositioned API to support queries by name as well as ident. --- src/Navaids/positioned.cxx | 252 +++++++++++++++++++++++++------------ src/Navaids/positioned.hxx | 16 +++ 2 files changed, 191 insertions(+), 77 deletions(-) diff --git a/src/Navaids/positioned.cxx b/src/Navaids/positioned.cxx index 38bce7d25..534ffa223 100644 --- a/src/Navaids/positioned.cxx +++ b/src/Navaids/positioned.cxx @@ -82,7 +82,8 @@ public: typedef std::set BucketEntry; typedef std::map SpatialPositionedIndex; -static NamedPositionedIndex global_namedIndex; +static NamedPositionedIndex global_identIndex; +static NamedPositionedIndex global_nameIndex; static SpatialPositionedIndex global_spatialIndex; SpatialPositionedIndex::iterator @@ -103,10 +104,16 @@ addToIndices(FGPositioned* aPos) { assert(aPos); if (!aPos->ident().empty()) { - global_namedIndex.insert(global_namedIndex.begin(), + global_identIndex.insert(global_identIndex.begin(), std::make_pair(aPos->ident(), aPos)); } - + + if (!aPos->name().empty()) { + global_nameIndex.insert(global_nameIndex.begin(), + std::make_pair(aPos->name(), aPos)); + } + + SpatialPositionedIndex::iterator it = bucketEntryForPositioned(aPos); it->second.insert(aPos); } @@ -117,10 +124,10 @@ removeFromIndices(FGPositioned* aPos) assert(aPos); if (!aPos->ident().empty()) { - NamedPositionedIndex::iterator it = global_namedIndex.find(aPos->ident()); - while (it != global_namedIndex.end() && (it->first == aPos->ident())) { + NamedPositionedIndex::iterator it = global_identIndex.find(aPos->ident()); + while (it != global_identIndex.end() && (it->first == aPos->ident())) { if (it->second == aPos) { - global_namedIndex.erase(it); + global_identIndex.erase(it); break; } @@ -128,6 +135,18 @@ removeFromIndices(FGPositioned* aPos) } // of multimap walk } + if (!aPos->name().empty()) { + NamedPositionedIndex::iterator it = global_nameIndex.find(aPos->name()); + while (it != global_nameIndex.end() && (it->first == aPos->name())) { + if (it->second == aPos) { + global_nameIndex.erase(it); + break; + } + + ++it; + } // of multimap walk + } + SpatialPositionedIndex::iterator sit = bucketEntryForPositioned(aPos); sit->second.erase(aPos); } @@ -236,9 +255,10 @@ sortByDistance(const SGGeod& aPos, FGPositioned::List& aResult) } static FGPositionedRef -namedFindClosest(const std::string& aIdent, const SGGeod& aOrigin, FGPositioned::Filter* aFilter) +namedFindClosest(const NamedPositionedIndex& aIndex, const std::string& aName, + const SGGeod& aOrigin, FGPositioned::Filter* aFilter) { - NamedIndexRange range = global_namedIndex.equal_range(aIdent); + NamedIndexRange range = aIndex.equal_range(aName); if (range.first == range.second) { return NULL; } @@ -361,8 +381,8 @@ char** searchAirportNamesAndIdents(const std::string& aFilter) ct.toupper((char *)filter.data(), (char *)filter.data() + filter.size()); } - NamedPositionedIndex::const_iterator it = global_namedIndex.begin(); - NamedPositionedIndex::const_iterator end = global_namedIndex.end(); + NamedPositionedIndex::const_iterator it = global_identIndex.begin(); + NamedPositionedIndex::const_iterator end = global_identIndex.end(); // note this is a vector of raw pointers, not smart pointers, because it // may get very large and smart-pointer-atomicity-locking then becomes a @@ -440,6 +460,77 @@ FGPositioned::Filter::passType(Type aTy) const return (minType() <= aTy) && (maxType() >= aTy); } +static FGPositioned::List +findAllSortedByRange(const NamedPositionedIndex& aIndex, + const std::string& aName, const SGGeod& aPos, FGPositioned::Filter* aFilter) +{ + FGPositioned::List result; + NamedIndexRange range = aIndex.equal_range(aName); + for (; range.first != range.second; ++range.first) { + FGPositioned* candidate = range.first->second; + if (aFilter) { + if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) { + continue; + } + + if (!aFilter->pass(candidate)) { + continue; + } + } + + result.push_back(range.first->second); + } + + sortByDistance(aPos, result); + return result; +} + +static FGPositionedRef +findWithPartial(const NamedPositionedIndex& aIndex, const std::string& aName, + FGPositioned::Filter* aFilter, int aOffset, bool& aNext) +{ + // see comment in findNextWithPartialId concerning upperBoundId + std::string upperBoundId = aName; + upperBoundId[upperBoundId.size()-1]++; + NamedPositionedIndex::const_iterator upperBound = aIndex.lower_bound(upperBoundId); + + NamedIndexRange range = aIndex.equal_range(aName); + FGPositionedRef result; + + while (range.first != upperBound) { + for (; range.first != range.second; ++range.first) { + FGPositionedRef candidate = range.first->second; + if (aFilter) { + if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) { + continue; + } + + if (!aFilter->pass(candidate)) { + continue; + } + } + + if (result) { + aNext = true; + return result; + } else if (aOffset == 0) { + // okay, found our result. we need to go around once more to set aNext + result = candidate; + } else { + --aOffset; // seen one more valid result, decrement the count + } + } + + // Unable to match the filter with this range - try the next range. + range = aIndex.equal_range(range.second->first); + } + + // if we fell out, we reached the end of the valid range. We might have a + // valid result, but we definitiely don't have a next result. + aNext = false; + return result; +} + /////////////////////////////////////////////////////////////////////////////// FGPositioned::FGPositioned(Type ty, const std::string& aIdent, const SGGeod& aPos, bool aIndexed) : @@ -501,6 +592,8 @@ FGPositioned::Type FGPositioned::typeFromName(const std::string& aName) // aliases {"waypoint", WAYPOINT}, {"apt", AIRPORT}, + {"any", INVALID}, + {"all", INVALID}, {NULL, INVALID} }; @@ -550,7 +643,7 @@ const char* FGPositioned::nameForType(Type aTy) FGPositionedRef FGPositioned::findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter) { - return namedFindClosest(aIdent, aPos, aFilter); + return namedFindClosest(global_identIndex, aIdent, aPos, aFilter); } FGPositioned::List @@ -565,25 +658,13 @@ FGPositioned::findWithinRange(const SGGeod& aPos, double aRangeNm, Filter* aFilt FGPositioned::List FGPositioned::findAllWithIdentSortedByRange(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter) { - List result; - NamedIndexRange range = global_namedIndex.equal_range(aIdent); - for (; range.first != range.second; ++range.first) { - FGPositioned* candidate = range.first->second; - if (aFilter) { - if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) { - continue; - } + return findAllSortedByRange(global_identIndex, aIdent, aPos, aFilter); +} - if (!aFilter->pass(candidate)) { - continue; - } - } - - result.push_back(range.first->second); - } - - sortByDistance(aPos, result); - return result; +FGPositioned::List +FGPositioned::findAllWithNameSortedByRange(const std::string& aName, const SGGeod& aPos, Filter* aFilter) +{ + return findAllSortedByRange(global_nameIndex, aName, aPos, aFilter); } FGPositionedRef @@ -612,9 +693,9 @@ FGPositioned::findNextWithPartialId(FGPositionedRef aCur, const std::string& aId // e.g., if the partial ID is "KI", we wish to search "KIxxx" but not "KJ". std::string upperBoundId = aId; upperBoundId[upperBoundId.size()-1]++; - NamedPositionedIndex::const_iterator upperBound = global_namedIndex.lower_bound(upperBoundId); + NamedPositionedIndex::const_iterator upperBound = global_identIndex.lower_bound(upperBoundId); - NamedIndexRange range = global_namedIndex.equal_range(aId); + NamedIndexRange range = global_identIndex.equal_range(aId); while (range.first != upperBound) { for (; range.first != range.second; ++range.first) { FGPositionedRef candidate = range.first->second; @@ -639,7 +720,7 @@ FGPositioned::findNextWithPartialId(FGPositionedRef aCur, const std::string& aId } // Unable to match the filter with this range - try the next range. - range = global_namedIndex.equal_range(range.second->first); + range = global_identIndex.equal_range(range.second->first); } return NULL; // Reached the end of the valid sequence with no match. @@ -648,46 +729,14 @@ FGPositioned::findNextWithPartialId(FGPositionedRef aCur, const std::string& aId FGPositionedRef FGPositioned::findWithPartialId(const std::string& aId, Filter* aFilter, int aOffset, bool& aNext) { - // see comment in findNextWithPartialId concerning upperBoundId - std::string upperBoundId = aId; - upperBoundId[upperBoundId.size()-1]++; - NamedPositionedIndex::const_iterator upperBound = global_namedIndex.lower_bound(upperBoundId); + return findWithPartial(global_identIndex, aId, aFilter, aOffset, aNext); +} - NamedIndexRange range = global_namedIndex.equal_range(aId); - FGPositionedRef result; - - while (range.first != upperBound) { - for (; range.first != range.second; ++range.first) { - FGPositionedRef candidate = range.first->second; - if (aFilter) { - if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) { - continue; - } - if (!aFilter->pass(candidate)) { - continue; - } - } - - if (result) { - aNext = true; - return result; - } else if (aOffset == 0) { - // okay, found our result. we need to go around once more to set aNext - result = candidate; - } else { - --aOffset; // seen one more valid result, decrement the count - } - } - - // Unable to match the filter with this range - try the next range. - range = global_namedIndex.equal_range(range.second->first); - } - - // if we fell out, we reached the end of the valid range. We might have a - // valid result, but we definitiely don't have a next result. - aNext = false; - return result; +FGPositionedRef +FGPositioned::findWithPartialName(const std::string& aName, Filter* aFilter, int aOffset, bool& aNext) +{ + return findWithPartial(global_nameIndex, aName, aFilter, aOffset, aNext); } /** @@ -698,9 +747,10 @@ class PartialIdentFilter : public FGPositioned::Filter { public: PartialIdentFilter(const std::string& ident, FGPositioned::Filter* filter) : - _ident(ident), _inner(filter) - { ; } + { + _ident = boost::to_upper_copy(ident); + } virtual bool pass(FGPositioned* aPos) const { @@ -722,21 +772,69 @@ private: FGPositioned::Filter* _inner; }; -FGPositionedRef -FGPositioned::findClosestWithPartialId(const SGGeod& aPos, const std::string& aId, Filter* aFilter, int aOffset, bool& aNext) +static FGPositionedRef +findClosestWithPartial(const SGGeod& aPos, FGPositioned::Filter* aFilter, int aOffset, bool& aNext) { - PartialIdentFilter pf(aId, aFilter); // why aOffset +2 ? at offset=3, we want the fourth search result, but also // to know if the fifth result exists (to set aNext flag for iterative APIs) - List matches = spatialGetClosest(aPos, aOffset + 2, 1000.0, &pf); + FGPositioned::List matches = + spatialGetClosest(aPos, aOffset + 2, 1000.0, aFilter); if ((int) matches.size() <= aOffset) { - SG_LOG(SG_GENERAL, SG_INFO, "FGPositioned::findClosestWithPartialId, couldn't match enough with prefix:" << aId); + SG_LOG(SG_GENERAL, SG_INFO, "findClosestWithPartial, couldn't match enough with prefix"); aNext = false; return NULL; // couldn't find a match within the cutoff distance } aNext = ((int) matches.size() >= (aOffset + 2)); return matches[aOffset]; + +} + +FGPositionedRef +FGPositioned::findClosestWithPartialId(const SGGeod& aPos, const std::string& aId, Filter* aFilter, int aOffset, bool& aNext) +{ + PartialIdentFilter pf(aId, aFilter); + return findClosestWithPartial(aPos, &pf, aOffset, aNext); +} + +/** + * Wrapper filter which proxies to an inner filter, but also ensures the + * name starts with supplied partial name. + */ +class PartialNameFilter : public FGPositioned::Filter +{ +public: + PartialNameFilter(const std::string& nm, FGPositioned::Filter* filter) : + _inner(filter) + { + _name = nm; + } + + virtual bool pass(FGPositioned* aPos) const + { + if (!_inner->pass(aPos)) { + return false; + } + + return (::strncasecmp(aPos->name().c_str(), _name.c_str(), _name.size()) == 0); + } + + virtual FGPositioned::Type minType() const + { return _inner->minType(); } + + virtual FGPositioned::Type maxType() const + { return _inner->maxType(); } + +private: + std::string _name; + FGPositioned::Filter* _inner; +}; + +FGPositionedRef +FGPositioned::findClosestWithPartialName(const SGGeod& aPos, const std::string& aName, Filter* aFilter, int aOffset, bool& aNext) +{ + PartialNameFilter pf(aName, aFilter); + return findClosestWithPartial(aPos, &pf, aOffset, aNext); } diff --git a/src/Navaids/positioned.hxx b/src/Navaids/positioned.hxx index 10591a211..37f8910c1 100644 --- a/src/Navaids/positioned.hxx +++ b/src/Navaids/positioned.hxx @@ -162,6 +162,11 @@ public: * As above, but searches using an offset index */ static FGPositionedRef findWithPartialId(const std::string& aId, Filter* aFilter, int aOffset, bool& aNext); + + /** + * As above, but search names instead of idents + */ + static FGPositionedRef findWithPartialName(const std::string& aName, Filter* aFilter, int aOffset, bool& aNext); /** * Find all items with the specified ident, and return then sorted by @@ -171,6 +176,11 @@ public: */ static List findAllWithIdentSortedByRange(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter = NULL); + /** + * As above, but searches names instead of idents + */ + static List findAllWithNameSortedByRange(const std::string& aName, const SGGeod& aPos, Filter* aFilter = NULL); + /** * Find the closest item to a position, which pass the specified filter * A cutoff range in NM must be specified, to constrain the search acceptably. @@ -199,6 +209,12 @@ public: */ static FGPositionedRef findClosestWithPartialId(const SGGeod& aPos, const std::string& aId, Filter* aFilter, int aOffset, bool& aNext); + /** + * As above, but matches on name + */ + static FGPositionedRef findClosestWithPartialName(const SGGeod& aPos, const std::string& aName, Filter* aFilter, int aOffset, bool& aNext); + + /** * Map a candidate type string to a real type. Returns INVALID if the string * does not correspond to a defined type.