1
0
Fork 0

Fix bug 150 (airports not found in GPS search)

Overhaul and simplify the GPS search logic based
on experience and testing with the API. Also fix
search-by-name, which was broken.
This commit is contained in:
James Turner 2010-08-14 19:16:28 +01:00
parent 71fbacc6dc
commit 57cb0a809b
11 changed files with 116 additions and 361 deletions

View file

@ -25,8 +25,9 @@
#include "pavement.hxx" #include "pavement.hxx"
FGPavement::FGPavement(const std::string& aIdent, const SGGeod& aPos) : FGPavement::FGPavement(const std::string& aIdent, const SGGeod& aPos) :
FGPositioned(PAVEMENT, aIdent, aPos, false) FGPositioned(PAVEMENT, aIdent, aPos)
{ {
init(false); // FGPositioned::init
} }
void FGPavement::addNode(const SGGeod &aPos, bool aClose) void FGPavement::addNode(const SGGeod &aPos, bool aClose)

View file

@ -51,12 +51,14 @@ FGRunwayBase::FGRunwayBase(Type aTy, const string& aIdent,
const double width, const double width,
const int surface_code, const int surface_code,
bool index) : bool index) :
FGPositioned(aTy, aIdent, aGeod, index) FGPositioned(aTy, aIdent, aGeod)
{ {
_heading = heading; _heading = heading;
_length = length; _length = length;
_width = width; _width = width;
_surface_code = surface_code; _surface_code = surface_code;
init(index);
} }
SGGeod FGRunwayBase::pointOnCenterline(double aOffset) const SGGeod FGRunwayBase::pointOnCenterline(double aOffset) const

View file

@ -63,6 +63,7 @@ FGAirport::FGAirport(const string &id, const SGGeod& location, const SGGeod& tow
mRunwaysLoaded(false), mRunwaysLoaded(false),
mTaxiwaysLoaded(true) mTaxiwaysLoaded(true)
{ {
init(true); // init FGPositioned
} }

View file

@ -1102,7 +1102,8 @@ FGPositioned* DCLGPS::FindTypedFirstById(const string& id, FGPositioned::Type ty
if (exact) { if (exact) {
FGPositioned::List matches = FGPositioned::List matches =
FGPositioned::findAllWithIdentSortedByRange(id, SGGeod::fromRad(_lon, _lat), &filter); FGPositioned::findAllWithIdent(id, &filter);
FGPositioned::sortByRange(matches, SGGeod::fromRad(_lon, _lat));
multi = (matches.size() > 1); multi = (matches.size() > 1);
return matches.empty() ? NULL : matches.front().ptr(); return matches.empty() ? NULL : matches.front().ptr();
} }

View file

@ -220,7 +220,6 @@ GPS::GPS ( SGPropertyNode *node) :
_mode("init"), _mode("init"),
_name(node->getStringValue("name", "gps")), _name(node->getStringValue("name", "gps")),
_num(node->getIntValue("number", 0)), _num(node->getIntValue("number", 0)),
_searchResultsCached(false),
_computeTurnData(false), _computeTurnData(false),
_anticipateTurn(false), _anticipateTurn(false),
_inTurn(false) _inTurn(false)
@ -1384,9 +1383,6 @@ void GPS::setScratchFromRouteWaypoint(int aIndex)
_scratchValid = true; _scratchValid = true;
_scratchNode->setDoubleValue("course", wp.get_track()); _scratchNode->setDoubleValue("course", wp.get_track());
_scratchNode->setIntValue("index", aIndex); _scratchNode->setIntValue("index", aIndex);
int lastResult = _routeMgr->size() - 1;
_searchHasNext = (_searchResultIndex < lastResult);
} }
void GPS::loadNearest() void GPS::loadNearest()
@ -1411,17 +1407,14 @@ void GPS::loadNearest()
_searchResults = _searchResults =
FGPositioned::findClosestN(searchPos, limitCount, cutoffDistance, f.get()); FGPositioned::findClosestN(searchPos, limitCount, cutoffDistance, f.get());
_searchResultsCached = true;
_searchResultIndex = 0; _searchResultIndex = 0;
_searchIsRoute = false; _searchIsRoute = false;
_searchHasNext = false;
if (_searchResults.empty()) { if (_searchResults.empty()) {
SG_LOG(SG_INSTR, SG_INFO, "GPS:loadNearest: no matches at all"); SG_LOG(SG_INSTR, SG_INFO, "GPS:loadNearest: no matches at all");
return; return;
} }
_searchHasNext = (_searchResults.size() > 1);
setScratchFromCachedSearchResult(); setScratchFromCachedSearchResult();
} }
@ -1478,66 +1471,47 @@ void GPS::search()
} }
_searchExact = _scratchNode->getBoolValue("exact", true); _searchExact = _scratchNode->getBoolValue("exact", true);
_searchOrderByRange = _scratchNode->getBoolValue("order-by-distance", true);
_searchResultIndex = 0; _searchResultIndex = 0;
_searchIsRoute = false; _searchIsRoute = false;
_searchHasNext = false;
if (_searchExact && _searchOrderByRange) {
// immediate mode search, get all the results now and cache them
auto_ptr<FGPositioned::Filter> f(createFilter(_searchType));
if (_searchNames) {
_searchResults = FGPositioned::findAllWithNameSortedByRange(_searchQuery, _indicated_pos, f.get());
} else {
_searchResults = FGPositioned::findAllWithIdentSortedByRange(_searchQuery, _indicated_pos, f.get());
}
_searchResultsCached = true;
if (_searchResults.empty()) {
clearScratch();
return;
}
_searchHasNext = (_searchResults.size() > 1);
setScratchFromCachedSearchResult();
} else {
// iterative search, look up result zero
_searchResultsCached = false;
performSearch();
}
}
void GPS::performSearch()
{
auto_ptr<FGPositioned::Filter> f(createFilter(_searchType)); auto_ptr<FGPositioned::Filter> f(createFilter(_searchType));
clearScratch();
FGPositionedRef r;
if (_searchNames) { if (_searchNames) {
if (_searchOrderByRange) { _searchResults = FGPositioned::findAllWithName(_searchQuery, f.get());
r = FGPositioned::findClosestWithPartialName(_indicated_pos, _searchQuery, f.get(), _searchResultIndex, _searchHasNext);
} else {
r = FGPositioned::findWithPartialName(_searchQuery, f.get(), _searchResultIndex, _searchHasNext);
}
} else { } else {
if (_searchOrderByRange) { _searchResults = FGPositioned::findAllWithIdent(_searchQuery, f.get());
r = FGPositioned::findClosestWithPartialId(_indicated_pos, _searchQuery, f.get(), _searchResultIndex, _searchHasNext);
} else {
r = FGPositioned::findWithPartialId(_searchQuery, f.get(), _searchResultIndex, _searchHasNext);
}
} }
if (!r) { bool orderByRange = _scratchNode->getBoolValue("order-by-distance", true);
if (orderByRange) {
FGPositioned::sortByRange(_searchResults, _indicated_pos);
}
if (_searchResults.empty()) {
clearScratch();
return; return;
} }
setScratchFromPositioned(r.get(), _searchResultIndex); setScratchFromCachedSearchResult();
}
bool GPS::getScratchHasNext() const
{
int lastResult;
if (_searchIsRoute) {
lastResult = _routeMgr->size() - 1;
} else {
lastResult = (int) _searchResults.size() - 1;
}
if (lastResult < 0) { // search array might be empty
return false;
}
return (_searchResultIndex < lastResult);
} }
void GPS::setScratchFromCachedSearchResult() void GPS::setScratchFromCachedSearchResult()
{ {
assert(_searchResultsCached);
int index = _searchResultIndex; int index = _searchResultIndex;
if ((index < 0) || (index >= (int) _searchResults.size())) { if ((index < 0) || (index >= (int) _searchResults.size())) {
@ -1546,9 +1520,6 @@ void GPS::setScratchFromCachedSearchResult()
} }
setScratchFromPositioned(_searchResults[index], index); setScratchFromPositioned(_searchResults[index], index);
int lastResult = (int) _searchResults.size() - 1;
_searchHasNext = (_searchResultIndex < lastResult);
} }
void GPS::setScratchFromPositioned(FGPositioned* aPos, int aIndex) void GPS::setScratchFromPositioned(FGPositioned* aPos, int aIndex)
@ -1566,9 +1537,7 @@ void GPS::setScratchFromPositioned(FGPositioned* aPos, int aIndex)
} }
_scratchValid = true; _scratchValid = true;
if (_searchResultsCached) { _scratchNode->setIntValue("result-count", _searchResults.size());
_scratchNode->setIntValue("result-count", _searchResults.size());
}
switch (aPos->type()) { switch (aPos->type()) {
case FGPositioned::VOR: case FGPositioned::VOR:
@ -1652,20 +1621,17 @@ void GPS::selectLegMode()
void GPS::nextResult() void GPS::nextResult()
{ {
if (!_searchHasNext) { if (!getScratchHasNext()) {
return; return;
} }
clearScratch(); clearScratch();
if (_searchIsRoute) { if (_searchIsRoute) {
setScratchFromRouteWaypoint(++_searchResultIndex); setScratchFromRouteWaypoint(++_searchResultIndex);
} else if (_searchResultsCached) {
++_searchResultIndex;
setScratchFromCachedSearchResult();
} else { } else {
++_searchResultIndex; ++_searchResultIndex;
performSearch(); setScratchFromCachedSearchResult();
} // of iterative search case }
} }
void GPS::previousResult() void GPS::previousResult()
@ -1679,10 +1645,8 @@ void GPS::previousResult()
if (_searchIsRoute) { if (_searchIsRoute) {
setScratchFromRouteWaypoint(_searchResultIndex); setScratchFromRouteWaypoint(_searchResultIndex);
} else if (_searchResultsCached) {
setScratchFromCachedSearchResult();
} else { } else {
performSearch(); setScratchFromCachedSearchResult();
} }
} }
@ -1701,14 +1665,15 @@ void GPS::defineWaypoint()
// check for duplicate idents // check for duplicate idents
FGPositioned::TypeFilter f(FGPositioned::WAYPOINT); FGPositioned::TypeFilter f(FGPositioned::WAYPOINT);
FGPositioned::List dups = FGPositioned::findAllWithIdentSortedByRange(ident, _indicated_pos, &f); FGPositioned::List dups = FGPositioned::findAllWithIdent(ident, &f);
if (!dups.empty()) { if (!dups.empty()) {
SG_LOG(SG_INSTR, SG_WARN, "GPS:defineWaypoint: non-unique waypoint identifier, ho-hum"); SG_LOG(SG_INSTR, SG_WARN, "GPS:defineWaypoint: non-unique waypoint identifier, ho-hum");
} }
SG_LOG(SG_INSTR, SG_INFO, "GPS:defineWaypoint: creating waypoint:" << ident); SG_LOG(SG_INSTR, SG_INFO, "GPS:defineWaypoint: creating waypoint:" << ident);
FGPositionedRef wpt = FGPositioned::createUserWaypoint(ident, _scratchPos); FGPositionedRef wpt = FGPositioned::createUserWaypoint(ident, _scratchPos);
_searchResultsCached = false; _searchResults.clear();
_searchResults.push_back(wpt);
setScratchFromPositioned(wpt.get(), -1); setScratchFromPositioned(wpt.get(), -1);
} }

View file

@ -264,7 +264,7 @@ private:
double getScratchDistance() const; double getScratchDistance() const;
double getScratchMagBearing() const; double getScratchMagBearing() const;
double getScratchTrueBearing() const; double getScratchTrueBearing() const;
bool getScratchHasNext() const { return _searchHasNext; } bool getScratchHasNext() const;
double getSelectedCourse() const { return _selectedCourse; } double getSelectedCourse() const { return _selectedCourse; }
void setSelectedCourse(double crs); void setSelectedCourse(double crs);
@ -394,8 +394,6 @@ private:
std::string _searchQuery; std::string _searchQuery;
FGPositioned::Type _searchType; FGPositioned::Type _searchType;
bool _searchExact; bool _searchExact;
bool _searchOrderByRange;
bool _searchResultsCached;
FGPositioned::List _searchResults; FGPositioned::List _searchResults;
bool _searchIsRoute; ///< set if 'search' is actually the current route bool _searchIsRoute; ///< set if 'search' is actually the current route
bool _searchHasNext; ///< is there a result after this one? bool _searchHasNext; ///< is there a result after this one?

View file

@ -39,6 +39,7 @@
FGFix::FGFix(const std::string& aIdent, const SGGeod& aPos) : FGFix::FGFix(const std::string& aIdent, const SGGeod& aPos) :
FGPositioned(FIX, aIdent, aPos) FGPositioned(FIX, aIdent, aPos)
{ {
init(true); // init FGPositioned
} }
// Constructor // Constructor

View file

@ -65,4 +65,5 @@ FGMarkerBeaconRecord::FGMarkerBeaconRecord(Type aTy, FGRunway* aRunway, const SG
FGPositioned(aTy, string(), aPos), FGPositioned(aTy, string(), aPos),
_runway(aRunway) _runway(aRunway)
{ {
init(true); // init FGPositioned
} }

View file

@ -82,6 +82,7 @@ FGNavRecord::FGNavRecord(Type aTy, const std::string& aIdent,
} }
} }
init(true); // init FGPositioned (now position is adjusted)
} }
void FGNavRecord::initAirportRelation() void FGNavRecord::initAirportRelation()

View file

@ -22,6 +22,8 @@
# include "config.h" # include "config.h"
#endif #endif
#include "positioned.hxx"
#include <map> #include <map>
#include <set> #include <set>
#include <algorithm> // for sort #include <algorithm> // for sort
@ -35,7 +37,7 @@
#include <simgear/structure/exception.hxx> #include <simgear/structure/exception.hxx>
#include <simgear/math/SGGeometry.hxx> #include <simgear/math/SGGeometry.hxx>
#include "positioned.hxx"
typedef std::multimap<std::string, FGPositioned*> NamedPositionedIndex; typedef std::multimap<std::string, FGPositioned*> NamedPositionedIndex;
typedef std::pair<NamedPositionedIndex::const_iterator, NamedPositionedIndex::const_iterator> NamedIndexRange; typedef std::pair<NamedPositionedIndex::const_iterator, NamedPositionedIndex::const_iterator> NamedIndexRange;
@ -402,91 +404,6 @@ removeFromIndices(FGPositioned* aPos)
} }
} }
class DistanceOrdering
{
public:
DistanceOrdering(const SGGeod& aPos) :
mPos(SGVec3d::fromGeod(aPos))
{ }
bool operator()(const FGPositionedRef& a, const FGPositionedRef& b) const
{
if (!a || !b) {
throw sg_exception("empty reference passed to DistanceOrdering");
}
double dA = distSqr(a->cart(), mPos),
dB = distSqr(b->cart(), mPos);
return dA < dB;
}
private:
SGVec3d mPos;
};
static void
sortByDistance(const SGGeod& aPos, FGPositioned::List& aResult)
{
std::sort(aResult.begin(), aResult.end(), DistanceOrdering(aPos));
}
static FGPositionedRef
namedFindClosest(const NamedPositionedIndex& aIndex, const std::string& aName,
const SGGeod& aOrigin, FGPositioned::Filter* aFilter)
{
NamedIndexRange range = aIndex.equal_range(aName);
if (range.first == range.second) {
return NULL;
}
// common case, only one result. looks a bit ugly because these are
// sequential iterators, not random-access ones
NamedPositionedIndex::const_iterator check = range.first;
if (++check == range.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
double minDist = HUGE_VAL;
FGPositionedRef result;
NamedPositionedIndex::const_iterator it = range.first;
SGVec3d cartOrigin(SGVec3d::fromGeod(aOrigin));
for (; it != range.second; ++it) {
FGPositioned* r = it->second;
if (aFilter) {
if (aFilter->hasTypeRange() && !aFilter->passType(r->type())) {
continue;
}
if (!aFilter->pass(r)) {
continue;
}
}
// find distance
double d2 = distSqr(cartOrigin, r->cart());
if (d2 < minDist) {
minDist = d2;
result = r;
}
}
return result;
}
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
class OrderByName class OrderByName
@ -592,14 +509,22 @@ FGPositioned::Filter::passType(Type aTy) const
return (minType() <= aTy) && (maxType() >= aTy); return (minType() <= aTy) && (maxType() >= aTy);
} }
static FGPositioned::List static FGPositioned::List
findAllSortedByRange(const NamedPositionedIndex& aIndex, findAll(const NamedPositionedIndex& aIndex,
const std::string& aName, const SGGeod& aPos, FGPositioned::Filter* aFilter) const std::string& aName, FGPositioned::Filter* aFilter)
{ {
FGPositioned::List result; FGPositioned::List result;
NamedIndexRange range = aIndex.equal_range(aName); if (aName.empty()) {
for (; range.first != range.second; ++range.first) { return result;
FGPositioned* candidate = range.first->second; }
std::string upperBoundId = aName;
upperBoundId[upperBoundId.size()-1]++;
NamedPositionedIndex::const_iterator upperBound = aIndex.lower_bound(upperBoundId);
NamedPositionedIndex::const_iterator it = aIndex.lower_bound(aName);
for (; it != upperBound; ++it) {
FGPositionedRef candidate = it->second;
if (aFilter) { if (aFilter) {
if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) { if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) {
continue; continue;
@ -610,70 +535,28 @@ findAllSortedByRange(const NamedPositionedIndex& aIndex,
} }
} }
result.push_back(range.first->second); result.push_back(candidate);
} }
sortByDistance(aPos, result);
return 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) : FGPositioned::FGPositioned(Type ty, const std::string& aIdent, const SGGeod& aPos) :
mPosition(aPos), mPosition(aPos),
mType(ty), mType(ty),
mIdent(aIdent) mIdent(aIdent)
{ {
}
void FGPositioned::init(bool aIndexed)
{
SGReferenced::get(this); // hold an owning ref, for the moment SGReferenced::get(this); // hold an owning ref, for the moment
mCart = SGVec3d::fromGeod(mPosition);
if (aIndexed) { if (aIndexed) {
assert(ty != TAXIWAY && ty != PAVEMENT); assert(mType != TAXIWAY && mType != PAVEMENT);
addToIndices(this); addToIndices(this);
} }
} }
@ -687,13 +570,15 @@ FGPositioned::~FGPositioned()
FGPositioned* FGPositioned*
FGPositioned::createUserWaypoint(const std::string& aIdent, const SGGeod& aPos) FGPositioned::createUserWaypoint(const std::string& aIdent, const SGGeod& aPos)
{ {
return new FGPositioned(WAYPOINT, aIdent, aPos, true); FGPositioned* wpt = new FGPositioned(WAYPOINT, aIdent, aPos);
wpt->init(true);
return wpt;
} }
SGVec3d const SGVec3d&
FGPositioned::cart() const FGPositioned::cart() const
{ {
return SGVec3d::fromGeod(mPosition); return mCart;
} }
FGPositioned::Type FGPositioned::typeFromName(const std::string& aName) FGPositioned::Type FGPositioned::typeFromName(const std::string& aName)
@ -770,7 +655,13 @@ const char* FGPositioned::nameForType(Type aTy)
FGPositionedRef FGPositionedRef
FGPositioned::findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter) FGPositioned::findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter)
{ {
return namedFindClosest(global_identIndex, aIdent, aPos, aFilter); FGPositioned::List r(findAll(global_identIndex, aIdent, aFilter));
if (r.empty()) {
return FGPositionedRef();
}
sortByRange(r, aPos);
return r.front();
} }
FGPositioned::List FGPositioned::List
@ -783,15 +674,15 @@ FGPositioned::findWithinRange(const SGGeod& aPos, double aRangeNm, Filter* aFilt
} }
FGPositioned::List FGPositioned::List
FGPositioned::findAllWithIdentSortedByRange(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter) FGPositioned::findAllWithIdent(const std::string& aIdent, Filter* aFilter)
{ {
return findAllSortedByRange(global_identIndex, aIdent, aPos, aFilter); return findAll(global_identIndex, aIdent, aFilter);
} }
FGPositioned::List FGPositioned::List
FGPositioned::findAllWithNameSortedByRange(const std::string& aName, const SGGeod& aPos, Filter* aFilter) FGPositioned::findAllWithName(const std::string& aName, Filter* aFilter)
{ {
return findAllSortedByRange(global_nameIndex, aName, aPos, aFilter); return findAll(global_nameIndex, aName, aFilter);
} }
FGPositionedRef FGPositionedRef
@ -861,115 +752,24 @@ FGPositioned::findNextWithPartialId(FGPositionedRef aCur, const std::string& aId
return NULL; // Reached the end of the valid sequence with no match. return NULL; // Reached the end of the valid sequence with no match.
} }
FGPositionedRef void
FGPositioned::findWithPartialId(const std::string& aId, Filter* aFilter, int aOffset, bool& aNext) FGPositioned::sortByRange(List& aResult, const SGGeod& aPos)
{ {
return findWithPartial(global_identIndex, aId, aFilter, aOffset, aNext); SGVec3d cartPos(SGVec3d::fromGeod(aPos));
} // computer ordering values
Octree::FindNearestResults r;
List::iterator it = aResult.begin(), lend = aResult.end();
FGPositionedRef for (; it != lend; ++it) {
FGPositioned::findWithPartialName(const std::string& aName, Filter* aFilter, int aOffset, bool& aNext) double d2 = distSqr((*it)->cart(), cartPos);
{ r.push_back(Octree::OrderedPositioned(*it, d2));
return findWithPartial(global_nameIndex, aName, aFilter, aOffset, aNext);
}
/**
* 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) :
_inner(filter)
{
_ident = boost::to_upper_copy(ident);
} }
virtual bool pass(FGPositioned* aPos) const // sort
{ std::sort(r.begin(), r.end());
if (!_inner->pass(aPos)) {
return false;
}
return (boost::algorithm::starts_with(aPos->ident(), _ident));
}
virtual FGPositioned::Type minType() const
{ return _inner->minType(); }
virtual FGPositioned::Type maxType() const
{ return _inner->maxType(); }
private:
std::string _ident;
FGPositioned::Filter* _inner;
};
static FGPositionedRef
findClosestWithPartial(const SGGeod& aPos, FGPositioned::Filter* aFilter, int aOffset, bool& aNext)
{
// 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)
FGPositioned::List matches;
Octree::findNearestN(SGVec3d::fromGeod(aPos), aOffset + 2, 1000 * SG_NM_TO_METER, aFilter, matches);
if ((int) matches.size() <= aOffset) { // convert to a plain list
SG_LOG(SG_GENERAL, SG_INFO, "findClosestWithPartial, couldn't match enough with prefix"); unsigned int count = aResult.size();
aNext = false; for (unsigned int i=0; i<count; ++i) {
return NULL; // couldn't find a match within the cutoff distance aResult[i] = r[i].get();
} }
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 (boost::algorithm::istarts_with(aPos->name(), _name));
}
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);
}

View file

@ -84,9 +84,9 @@ public:
{ return mPosition; } { return mPosition; }
/** /**
* Compute the cartesian position associated with this object * The cartesian position associated with this object
*/ */
SGVec3d cart() const; const SGVec3d& cart() const;
double latitude() const double latitude() const
{ return mPosition.getLatitudeDeg(); } { return mPosition.getLatitudeDeg(); }
@ -157,27 +157,20 @@ public:
static FGPositionedRef findNextWithPartialId(FGPositionedRef aCur, const std::string& aId, Filter* aFilter = NULL); static FGPositionedRef findNextWithPartialId(FGPositionedRef aCur, const std::string& aId, Filter* aFilter = NULL);
/** /**
* As above, but searches using an offset index * Find all items with the specified ident
*/
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
* distance from a position
*
* @param aFilter - optional filter on items * @param aFilter - optional filter on items
*/ */
static List findAllWithIdentSortedByRange(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter = NULL); static List findAllWithIdent(const std::string& aIdent, Filter* aFilter = NULL);
/** /**
* As above, but searches names instead of idents * As above, but searches names instead of idents
*/ */
static List findAllWithNameSortedByRange(const std::string& aName, const SGGeod& aPos, Filter* aFilter = NULL); static List findAllWithName(const std::string& aName, Filter* aFilter = NULL);
/**
* Sort an FGPositionedList by distance from a position
*/
static void sortByRange(List&, const SGGeod& aPos);
/** /**
* Find the closest item to a position, which pass the specified filter * Find the closest item to a position, which pass the specified filter
@ -201,18 +194,6 @@ public:
*/ */
static List findClosestN(const SGGeod& aPos, unsigned int aN, double aCutoffNm, Filter* aFilter = NULL); 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, 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 * Map a candidate type string to a real type. Returns INVALID if the string
* does not correspond to a defined type. * does not correspond to a defined type.
@ -227,12 +208,15 @@ public:
static FGPositioned* createUserWaypoint(const std::string& aIdent, const SGGeod& aPos); static FGPositioned* createUserWaypoint(const std::string& aIdent, const SGGeod& aPos);
protected: protected:
FGPositioned(Type ty, const std::string& aIdent, const SGGeod& aPos, bool aIndex = true); FGPositioned(Type ty, const std::string& aIdent, const SGGeod& aPos);
void init(bool aIndexed);
// can't be const right now, navrecord at least needs to fix up the position // can't be const right now, navrecord at least needs to fix up the position
// after navaids are parsed // after navaids are parsed
SGGeod mPosition; SGGeod mPosition;
SGVec3d mCart; // once mPosition is const, this can be const too
const Type mType; const Type mType;
const std::string mIdent; const std::string mIdent;
}; };