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"
|
#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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue