_mode("init"),
_name(node->getStringValue("name", "gps")),
_num(node->getIntValue("number", 0)),
- _searchResultsCached(false),
_computeTurnData(false),
_anticipateTurn(false),
_inTurn(false)
_scratchValid = true;
_scratchNode->setDoubleValue("course", wp.get_track());
_scratchNode->setIntValue("index", aIndex);
-
- int lastResult = _routeMgr->size() - 1;
- _searchHasNext = (_searchResultIndex < lastResult);
}
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();
}
}
_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());
- } 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));
- clearScratch();
-
- FGPositionedRef r;
if (_searchNames) {
- if (_searchOrderByRange) {
- r = FGPositioned::findClosestWithPartialName(_indicated_pos, _searchQuery, f.get(), _searchResultIndex, _searchHasNext);
- } else {
- r = FGPositioned::findWithPartialName(_searchQuery, f.get(), _searchResultIndex, _searchHasNext);
- }
+ _searchResults = FGPositioned::findAllWithName(_searchQuery, f.get());
} else {
- if (_searchOrderByRange) {
- r = FGPositioned::findClosestWithPartialId(_indicated_pos, _searchQuery, f.get(), _searchResultIndex, _searchHasNext);
- } else {
- r = FGPositioned::findWithPartialId(_searchQuery, f.get(), _searchResultIndex, _searchHasNext);
- }
+ _searchResults = FGPositioned::findAllWithIdent(_searchQuery, f.get());
+ }
+
+ bool orderByRange = _scratchNode->getBoolValue("order-by-distance", true);
+ if (orderByRange) {
+ FGPositioned::sortByRange(_searchResults, _indicated_pos);
}
- if (!r) {
+ if (_searchResults.empty()) {
+ clearScratch();
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()
{
- assert(_searchResultsCached);
int index = _searchResultIndex;
if ((index < 0) || (index >= (int) _searchResults.size())) {
}
setScratchFromPositioned(_searchResults[index], index);
-
- int lastResult = (int) _searchResults.size() - 1;
- _searchHasNext = (_searchResultIndex < lastResult);
}
void GPS::setScratchFromPositioned(FGPositioned* aPos, int aIndex)
}
_scratchValid = true;
- if (_searchResultsCached) {
- _scratchNode->setIntValue("result-count", _searchResults.size());
- }
+ _scratchNode->setIntValue("result-count", _searchResults.size());
switch (aPos->type()) {
case FGPositioned::VOR:
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()
if (_searchIsRoute) {
setScratchFromRouteWaypoint(_searchResultIndex);
- } else if (_searchResultsCached) {
- setScratchFromCachedSearchResult();
} else {
- performSearch();
+ setScratchFromCachedSearchResult();
}
}
// 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);
}
# include "config.h"
#endif
+#include "positioned.hxx"
+
#include <map>
#include <set>
#include <algorithm> // for sort
#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;
}
}
-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
return (minType() <= aTy) && (maxType() >= aTy);
}
-static FGPositioned::List
-findAllSortedByRange(const NamedPositionedIndex& aIndex,
- const std::string& aName, const SGGeod& aPos, FGPositioned::Filter* aFilter)
+static FGPositioned::List
+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);
+ if (aName.empty()) {
+ return result;
}
- sortByDistance(aPos, result);
- return result;
-}
-
-static FGPositionedRef
-findWithPartial(const NamedPositionedIndex& aIndex, const std::string& aName,
- FGPositioned::Filter* aFilter, int aOffset, bool& aNext)
-{
- // see comment in findNextWithPartialId concerning upperBoundId
std::string upperBoundId = aName;
upperBoundId[upperBoundId.size()-1]++;
NamedPositionedIndex::const_iterator upperBound = aIndex.lower_bound(upperBoundId);
+ 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;
- if (aFilter) {
- if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) {
- continue;
- }
-
- if (!aFilter->pass(candidate)) {
- continue;
- }
+ for (; it != upperBound; ++it) {
+ FGPositionedRef candidate = it->second;
+ if (aFilter) {
+ if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) {
+ 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
+ if (!aFilter->pass(candidate)) {
+ continue;
}
}
- // Unable to match the filter with this range - try the next range.
- range = aIndex.equal_range(range.second->first);
+ result.push_back(candidate);
}
- // 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;
+ 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);
}
}
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)
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
}
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
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)
-{
- return findWithPartial(global_identIndex, aId, aFilter, aOffset, aNext);
-}
-
-
-FGPositionedRef
-FGPositioned::findWithPartialName(const std::string& aName, Filter* aFilter, int aOffset, bool& aNext)
-{
- 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
+void
+FGPositioned::sortByRange(List& aResult, const SGGeod& aPos)
{
-public:
- PartialIdentFilter(const std::string& ident, FGPositioned::Filter* filter) :
- _inner(filter)
- {
- _ident = boost::to_upper_copy(ident);
+ 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));
}
- 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);
+// sort
+ std::sort(r.begin(), r.end());
- 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
+// convert to a plain list
+ unsigned int count = aResult.size();
+ for (unsigned int i=0; i<count; ++i) {
+ 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);
-}
-
{ 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(); }
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
+ * Find all items with the specified ident
+ * @param aFilter - optional filter on items
*/
- static FGPositionedRef findWithPartialName(const std::string& aName, Filter* aFilter, int aOffset, bool& aNext);
+ static List findAllWithIdent(const std::string& aIdent, Filter* aFilter = NULL);
/**
- * Find all items with the specified ident, and return then sorted by
- * distance from a position
- *
- * @param aFilter - optional filter on items
+ * As above, but searches names instead of idents
*/
- static List findAllWithIdentSortedByRange(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter = NULL);
+ static List findAllWithName(const std::string& aName, Filter* aFilter = NULL);
/**
- * As above, but searches names instead of idents
+ * Sort an FGPositionedList by distance from a position
*/
- static List findAllWithNameSortedByRange(const std::string& aName, const SGGeod& aPos, Filter* aFilter = NULL);
+ static void sortByRange(List&, const SGGeod& aPos);
/**
* Find the closest item to a position, which pass the specified filter
*/
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.
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;
};