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:
parent
71fbacc6dc
commit
57cb0a809b
11 changed files with 116 additions and 361 deletions
|
@ -25,8 +25,9 @@
|
|||
#include "pavement.hxx"
|
||||
|
||||
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)
|
||||
|
|
|
@ -51,12 +51,14 @@ FGRunwayBase::FGRunwayBase(Type aTy, const string& aIdent,
|
|||
const double width,
|
||||
const int surface_code,
|
||||
bool index) :
|
||||
FGPositioned(aTy, aIdent, aGeod, index)
|
||||
FGPositioned(aTy, aIdent, aGeod)
|
||||
{
|
||||
_heading = heading;
|
||||
_length = length;
|
||||
_width = width;
|
||||
_surface_code = surface_code;
|
||||
|
||||
init(index);
|
||||
}
|
||||
|
||||
SGGeod FGRunwayBase::pointOnCenterline(double aOffset) const
|
||||
|
|
|
@ -63,6 +63,7 @@ FGAirport::FGAirport(const string &id, const SGGeod& location, const SGGeod& tow
|
|||
mRunwaysLoaded(false),
|
||||
mTaxiwaysLoaded(true)
|
||||
{
|
||||
init(true); // init FGPositioned
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1102,7 +1102,8 @@ FGPositioned* DCLGPS::FindTypedFirstById(const string& id, FGPositioned::Type ty
|
|||
|
||||
if (exact) {
|
||||
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);
|
||||
return matches.empty() ? NULL : matches.front().ptr();
|
||||
}
|
||||
|
|
|
@ -220,7 +220,6 @@ GPS::GPS ( SGPropertyNode *node) :
|
|||
_mode("init"),
|
||||
_name(node->getStringValue("name", "gps")),
|
||||
_num(node->getIntValue("number", 0)),
|
||||
_searchResultsCached(false),
|
||||
_computeTurnData(false),
|
||||
_anticipateTurn(false),
|
||||
_inTurn(false)
|
||||
|
@ -1384,9 +1383,6 @@ void GPS::setScratchFromRouteWaypoint(int aIndex)
|
|||
_scratchValid = true;
|
||||
_scratchNode->setDoubleValue("course", wp.get_track());
|
||||
_scratchNode->setIntValue("index", aIndex);
|
||||
|
||||
int lastResult = _routeMgr->size() - 1;
|
||||
_searchHasNext = (_searchResultIndex < lastResult);
|
||||
}
|
||||
|
||||
void GPS::loadNearest()
|
||||
|
@ -1411,17 +1407,14 @@ void GPS::loadNearest()
|
|||
|
||||
_searchResults =
|
||||
FGPositioned::findClosestN(searchPos, limitCount, cutoffDistance, f.get());
|
||||
_searchResultsCached = true;
|
||||
_searchResultIndex = 0;
|
||||
_searchIsRoute = false;
|
||||
_searchHasNext = false;
|
||||
|
||||
if (_searchResults.empty()) {
|
||||
SG_LOG(SG_INSTR, SG_INFO, "GPS:loadNearest: no matches at all");
|
||||
return;
|
||||
}
|
||||
|
||||
_searchHasNext = (_searchResults.size() > 1);
|
||||
setScratchFromCachedSearchResult();
|
||||
}
|
||||
|
||||
|
@ -1478,66 +1471,47 @@ void GPS::search()
|
|||
}
|
||||
|
||||
_searchExact = _scratchNode->getBoolValue("exact", true);
|
||||
_searchOrderByRange = _scratchNode->getBoolValue("order-by-distance", true);
|
||||
_searchResultIndex = 0;
|
||||
_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());
|
||||
_searchResults = FGPositioned::findAllWithName(_searchQuery, f.get());
|
||||
} else {
|
||||
_searchResults = FGPositioned::findAllWithIdentSortedByRange(_searchQuery, _indicated_pos, f.get());
|
||||
_searchResults = FGPositioned::findAllWithIdent(_searchQuery, f.get());
|
||||
}
|
||||
|
||||
_searchResultsCached = true;
|
||||
bool orderByRange = _scratchNode->getBoolValue("order-by-distance", true);
|
||||
if (orderByRange) {
|
||||
FGPositioned::sortByRange(_searchResults, _indicated_pos);
|
||||
}
|
||||
|
||||
if (_searchResults.empty()) {
|
||||
clearScratch();
|
||||
return;
|
||||
}
|
||||
|
||||
_searchHasNext = (_searchResults.size() > 1);
|
||||
setScratchFromCachedSearchResult();
|
||||
} else {
|
||||
// iterative search, look up result zero
|
||||
_searchResultsCached = false;
|
||||
performSearch();
|
||||
}
|
||||
}
|
||||
|
||||
void GPS::performSearch()
|
||||
bool GPS::getScratchHasNext() const
|
||||
{
|
||||
auto_ptr<FGPositioned::Filter> f(createFilter(_searchType));
|
||||
clearScratch();
|
||||
|
||||
FGPositionedRef r;
|
||||
if (_searchNames) {
|
||||
if (_searchOrderByRange) {
|
||||
r = FGPositioned::findClosestWithPartialName(_indicated_pos, _searchQuery, f.get(), _searchResultIndex, _searchHasNext);
|
||||
int lastResult;
|
||||
if (_searchIsRoute) {
|
||||
lastResult = _routeMgr->size() - 1;
|
||||
} else {
|
||||
r = FGPositioned::findWithPartialName(_searchQuery, f.get(), _searchResultIndex, _searchHasNext);
|
||||
}
|
||||
} else {
|
||||
if (_searchOrderByRange) {
|
||||
r = FGPositioned::findClosestWithPartialId(_indicated_pos, _searchQuery, f.get(), _searchResultIndex, _searchHasNext);
|
||||
} else {
|
||||
r = FGPositioned::findWithPartialId(_searchQuery, f.get(), _searchResultIndex, _searchHasNext);
|
||||
}
|
||||
lastResult = (int) _searchResults.size() - 1;
|
||||
}
|
||||
|
||||
if (!r) {
|
||||
return;
|
||||
if (lastResult < 0) { // search array might be empty
|
||||
return false;
|
||||
}
|
||||
|
||||
setScratchFromPositioned(r.get(), _searchResultIndex);
|
||||
return (_searchResultIndex < lastResult);
|
||||
}
|
||||
|
||||
void GPS::setScratchFromCachedSearchResult()
|
||||
{
|
||||
assert(_searchResultsCached);
|
||||
int index = _searchResultIndex;
|
||||
|
||||
if ((index < 0) || (index >= (int) _searchResults.size())) {
|
||||
|
@ -1546,9 +1520,6 @@ void GPS::setScratchFromCachedSearchResult()
|
|||
}
|
||||
|
||||
setScratchFromPositioned(_searchResults[index], index);
|
||||
|
||||
int lastResult = (int) _searchResults.size() - 1;
|
||||
_searchHasNext = (_searchResultIndex < lastResult);
|
||||
}
|
||||
|
||||
void GPS::setScratchFromPositioned(FGPositioned* aPos, int aIndex)
|
||||
|
@ -1566,9 +1537,7 @@ void GPS::setScratchFromPositioned(FGPositioned* aPos, int aIndex)
|
|||
}
|
||||
|
||||
_scratchValid = true;
|
||||
if (_searchResultsCached) {
|
||||
_scratchNode->setIntValue("result-count", _searchResults.size());
|
||||
}
|
||||
|
||||
switch (aPos->type()) {
|
||||
case FGPositioned::VOR:
|
||||
|
@ -1652,20 +1621,17 @@ void GPS::selectLegMode()
|
|||
|
||||
void GPS::nextResult()
|
||||
{
|
||||
if (!_searchHasNext) {
|
||||
if (!getScratchHasNext()) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearScratch();
|
||||
if (_searchIsRoute) {
|
||||
setScratchFromRouteWaypoint(++_searchResultIndex);
|
||||
} else if (_searchResultsCached) {
|
||||
++_searchResultIndex;
|
||||
setScratchFromCachedSearchResult();
|
||||
} else {
|
||||
++_searchResultIndex;
|
||||
performSearch();
|
||||
} // of iterative search case
|
||||
setScratchFromCachedSearchResult();
|
||||
}
|
||||
}
|
||||
|
||||
void GPS::previousResult()
|
||||
|
@ -1679,10 +1645,8 @@ void GPS::previousResult()
|
|||
|
||||
if (_searchIsRoute) {
|
||||
setScratchFromRouteWaypoint(_searchResultIndex);
|
||||
} else if (_searchResultsCached) {
|
||||
setScratchFromCachedSearchResult();
|
||||
} else {
|
||||
performSearch();
|
||||
setScratchFromCachedSearchResult();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1701,14 +1665,15 @@ void GPS::defineWaypoint()
|
|||
|
||||
// check for duplicate idents
|
||||
FGPositioned::TypeFilter f(FGPositioned::WAYPOINT);
|
||||
FGPositioned::List dups = FGPositioned::findAllWithIdentSortedByRange(ident, _indicated_pos, &f);
|
||||
FGPositioned::List dups = FGPositioned::findAllWithIdent(ident, &f);
|
||||
if (!dups.empty()) {
|
||||
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);
|
||||
FGPositionedRef wpt = FGPositioned::createUserWaypoint(ident, _scratchPos);
|
||||
_searchResultsCached = false;
|
||||
_searchResults.clear();
|
||||
_searchResults.push_back(wpt);
|
||||
setScratchFromPositioned(wpt.get(), -1);
|
||||
}
|
||||
|
||||
|
|
|
@ -264,7 +264,7 @@ private:
|
|||
double getScratchDistance() const;
|
||||
double getScratchMagBearing() const;
|
||||
double getScratchTrueBearing() const;
|
||||
bool getScratchHasNext() const { return _searchHasNext; }
|
||||
bool getScratchHasNext() const;
|
||||
|
||||
double getSelectedCourse() const { return _selectedCourse; }
|
||||
void setSelectedCourse(double crs);
|
||||
|
@ -394,8 +394,6 @@ private:
|
|||
std::string _searchQuery;
|
||||
FGPositioned::Type _searchType;
|
||||
bool _searchExact;
|
||||
bool _searchOrderByRange;
|
||||
bool _searchResultsCached;
|
||||
FGPositioned::List _searchResults;
|
||||
bool _searchIsRoute; ///< set if 'search' is actually the current route
|
||||
bool _searchHasNext; ///< is there a result after this one?
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
FGFix::FGFix(const std::string& aIdent, const SGGeod& aPos) :
|
||||
FGPositioned(FIX, aIdent, aPos)
|
||||
{
|
||||
init(true); // init FGPositioned
|
||||
}
|
||||
|
||||
// Constructor
|
||||
|
|
|
@ -65,4 +65,5 @@ FGMarkerBeaconRecord::FGMarkerBeaconRecord(Type aTy, FGRunway* aRunway, const SG
|
|||
FGPositioned(aTy, string(), aPos),
|
||||
_runway(aRunway)
|
||||
{
|
||||
init(true); // init FGPositioned
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ FGNavRecord::FGNavRecord(Type aTy, const std::string& aIdent,
|
|||
}
|
||||
}
|
||||
|
||||
init(true); // init FGPositioned (now position is adjusted)
|
||||
}
|
||||
|
||||
void FGNavRecord::initAirportRelation()
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "positioned.hxx"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <algorithm> // for sort
|
||||
|
@ -35,7 +37,7 @@
|
|||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/math/SGGeometry.hxx>
|
||||
|
||||
#include "positioned.hxx"
|
||||
|
||||
|
||||
typedef std::multimap<std::string, FGPositioned*> NamedPositionedIndex;
|
||||
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
|
||||
|
@ -593,45 +510,21 @@ FGPositioned::Filter::passType(Type aTy) const
|
|||
}
|
||||
|
||||
static FGPositioned::List
|
||||
findAllSortedByRange(const NamedPositionedIndex& aIndex,
|
||||
const std::string& aName, const SGGeod& aPos, FGPositioned::Filter* aFilter)
|
||||
findAll(const NamedPositionedIndex& aIndex,
|
||||
const std::string& aName, 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);
|
||||
if (aName.empty()) {
|
||||
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);
|
||||
NamedPositionedIndex::const_iterator it = aIndex.lower_bound(aName);
|
||||
|
||||
NamedIndexRange range = aIndex.equal_range(aName);
|
||||
FGPositionedRef result;
|
||||
|
||||
while (range.first != upperBound) {
|
||||
for (; range.first != range.second; ++range.first) {
|
||||
FGPositionedRef candidate = range.first->second;
|
||||
for (; it != upperBound; ++it) {
|
||||
FGPositionedRef candidate = it->second;
|
||||
if (aFilter) {
|
||||
if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) {
|
||||
continue;
|
||||
|
@ -642,38 +535,28 @@ findWithPartial(const NamedPositionedIndex& aIndex, const std::string& aName,
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
result.push_back(candidate);
|
||||
}
|
||||
|
||||
// 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),
|
||||
mType(ty),
|
||||
mIdent(aIdent)
|
||||
{
|
||||
}
|
||||
|
||||
void FGPositioned::init(bool aIndexed)
|
||||
{
|
||||
SGReferenced::get(this); // hold an owning ref, for the moment
|
||||
mCart = SGVec3d::fromGeod(mPosition);
|
||||
|
||||
if (aIndexed) {
|
||||
assert(ty != TAXIWAY && ty != PAVEMENT);
|
||||
assert(mType != TAXIWAY && mType != PAVEMENT);
|
||||
addToIndices(this);
|
||||
}
|
||||
}
|
||||
|
@ -687,13 +570,15 @@ FGPositioned::~FGPositioned()
|
|||
FGPositioned*
|
||||
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
|
||||
{
|
||||
return SGVec3d::fromGeod(mPosition);
|
||||
return mCart;
|
||||
}
|
||||
|
||||
FGPositioned::Type FGPositioned::typeFromName(const std::string& aName)
|
||||
|
@ -770,7 +655,13 @@ const char* FGPositioned::nameForType(Type aTy)
|
|||
FGPositionedRef
|
||||
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
|
||||
|
@ -783,15 +674,15 @@ FGPositioned::findWithinRange(const SGGeod& aPos, double aRangeNm, Filter* aFilt
|
|||
}
|
||||
|
||||
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::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
|
||||
|
@ -861,115 +752,24 @@ 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, bool& aNext)
|
||||
void
|
||||
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();
|
||||
for (; it != lend; ++it) {
|
||||
double d2 = distSqr((*it)->cart(), cartPos);
|
||||
r.push_back(Octree::OrderedPositioned(*it, d2));
|
||||
}
|
||||
|
||||
// sort
|
||||
std::sort(r.begin(), r.end());
|
||||
|
||||
FGPositionedRef
|
||||
FGPositioned::findWithPartialName(const std::string& aName, Filter* aFilter, int aOffset, bool& aNext)
|
||||
{
|
||||
return findWithPartial(global_nameIndex, aName, aFilter, aOffset, aNext);
|
||||
// convert to a plain list
|
||||
unsigned int count = aResult.size();
|
||||
for (unsigned int i=0; i<count; ++i) {
|
||||
aResult[i] = r[i].get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
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) {
|
||||
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 (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);
|
||||
}
|
||||
|
||||
|
|
|
@ -84,9 +84,9 @@ public:
|
|||
{ 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
|
||||
{ return mPosition.getLatitudeDeg(); }
|
||||
|
@ -157,27 +157,20 @@ 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, 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
|
||||
*
|
||||
* Find all items with the specified ident
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
|
@ -201,18 +194,6 @@ public:
|
|||
*/
|
||||
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
|
||||
* does not correspond to a defined type.
|
||||
|
@ -227,12 +208,15 @@ public:
|
|||
static FGPositioned* createUserWaypoint(const std::string& aIdent, const SGGeod& aPos);
|
||||
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
|
||||
// after navaids are parsed
|
||||
SGGeod mPosition;
|
||||
|
||||
SGVec3d mCart; // once mPosition is const, this can be const too
|
||||
const Type mType;
|
||||
const std::string mIdent;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue