FGPositioned::Type FGPositioned::typeFromName(const std::string& aName)
{
+ if (aName.empty() || (aName == "")) {
+ return INVALID;
+ }
+
typedef struct {
const char* _name;
Type _ty;
{"dme", DME},
// aliases
{"waypoint", WAYPOINT},
-
+ {"apt", AIRPORT},
+
{NULL, INVALID}
};
return spatialGetClosest(aPos, aN, aCutoffNm, aFilter);
}
-/*
FGPositionedRef
FGPositioned::findNextWithPartialId(FGPositionedRef aCur, const std::string& aId, Filter* aFilter)
{
+ // It is essential to bound our search, to avoid iterating all the way to the end of the database.
+ // Do this by generating a second ID with the final character incremented by 1.
+ // 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);
+
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())) {
+ while (range.first != upperBound) {
+ 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->pass(candidate)) {
- continue;
+ if (aFilter) {
+ if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) {
+ continue;
+ }
+
+ if (!aFilter->pass(candidate)) {
+ continue;
+ }
+ }
+
+ if (!aCur) {
+ return candidate;
}
}
-
- if (!aCur) {
- return candidate;
- }
+
+ // Unable to match the filter with this range - try the next range.
+ range = global_namedIndex.equal_range(range.second->first);
}
-
- return NULL; // fell out, no match in range
-}*/
+ return NULL; // Reached the end of the valid sequence with no match.
+}
+
FGPositionedRef
-FGPositioned::findNextWithPartialId(FGPositionedRef aCur, const std::string& aId, Filter* aFilter)
+FGPositioned::findWithPartialId(const std::string& aId, Filter* aFilter, int aOffset)
{
- // It is essential to bound our search, to avoid iterating all the way to the end of the database.
- // Do this by generating a second ID with the final character incremented by 1.
- // e.g., if the partial ID is "KI", we wish to search "KIxxx" but not "KJ".
+ // 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 (aCur == candidate) {
- aCur = NULL; // found our start point, next match will pass
- continue;
- }
-
if (aFilter) {
if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) {
continue;
}
}
- if (!aCur) {
+ if (aOffset == 0) {
return candidate;
+ } else {
+ --aOffset; // seen one more valid result, decrement the count
}
}
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];
+}
*/
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
* 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.