- updates how filtering is done on the various FGPositioned query functions - both spatial and ident-based.
- updates the KLN-89b / DCLGPS code to use FGPositioned for all Navaid/Airport queries.
me:
Add few include directives in globals.cxx to avoid such MSVC warning messages :
deletion of pointer to incomplete type 'FGAirportList'; no destructor called
deletion of pointer to incomplete type 'FGNavList'; no destructor called
deletion of pointer to incomplete type 'FGTACANList'; no destructor called
deletion of pointer to incomplete type 'FGFixList'; no destructor called
#include <ATCDCL/ATCProjection.hxx>
#include <Main/fg_props.hxx>
#include <simgear/math/point3d.hxx>
+#include "Airports/simple.hxx"
using std::cout;
#endif
#include "kln89_page_apt.hxx"
-#include <ATCDCL/commlist.hxx>
-#include <Main/globals.hxx>
-#include <Airports/runways.hxx>
-
+#include "ATCDCL/commlist.hxx"
+#include "Main/globals.hxx"
+#include "Airports/runways.hxx"
+#include "Airports/simple.hxx"
// This function is copied from Airports/runways.cxx
// TODO - Make the original properly available and remove this instance!!!!
#include "kln89_page_int.hxx"
#include "Navaids/fix.hxx"
+#include "Navaids/navrecord.hxx"
KLN89IntPage::KLN89IntPage(KLN89* parent)
: KLN89Page(parent) {
#endif
#include "kln89_page_ndb.hxx"
+#include "Navaids/navrecord.hxx"
KLN89NDBPage::KLN89NDBPage(KLN89* parent)
: KLN89Page(parent) {
#endif
#include "kln89_page_vor.hxx"
+#include "Navaids/navrecord.hxx"
KLN89VorPage::KLN89VorPage(KLN89* parent)
: KLN89Page(parent) {
#include <Main/fg_props.hxx>
#include <Navaids/fix.hxx>
+#include <Navaids/navrecord.hxx>
+#include <Airports/simple.hxx>
#include <iostream>
-using std::cout;
-//using namespace std;
+using namespace std;
// Command callbacks for FlightGear
else return(id);
}
-GPSWaypoint* GPSWaypoint::createFromFix(const FGFix* aFix)
+static GPSWpType
+GPSWpTypeFromFGPosType(FGPositioned::Type aType)
{
- assert(aFix);
- return new GPSWaypoint(aFix->get_ident(),
- aFix->get_lat() * SG_DEGREES_TO_RADIANS,
- aFix->get_lon() * SG_DEGREES_TO_RADIANS,
- GPS_WP_INT);
-}
-
-GPSWaypoint* GPSWaypoint::createFromNav(const FGNavRecord* aNav)
-{
- assert(aNav);
- return new GPSWaypoint(aNav->get_ident(),
- aNav->get_lat() * SG_DEGREES_TO_RADIANS,
- aNav->get_lon() * SG_DEGREES_TO_RADIANS,
- (aNav->get_fg_type() == FG_NAV_VOR ? GPS_WP_VOR : GPS_WP_NDB));
+ switch (aType) {
+ case FGPositioned::AIRPORT:
+ case FGPositioned::SEAPORT:
+ case FGPositioned::HELIPORT:
+ return GPS_WP_APT;
+
+ case FGPositioned::VOR:
+ return GPS_WP_VOR;
+
+ case FGPositioned::NDB:
+ return GPS_WP_NDB;
+
+ case FGPositioned::WAYPOINT:
+ return GPS_WP_USR;
+
+ case FGPositioned::FIX:
+ return GPS_WP_INT;
+
+ default:
+ return GPS_WP_USR;
+ }
}
-GPSWaypoint* GPSWaypoint::createFromAirport(const FGAirport* aApt)
+GPSWaypoint* GPSWaypoint::createFromPositioned(const FGPositioned* aPos)
{
- assert(aApt);
- return new GPSWaypoint(aApt->getId(),
- aApt->getLatitude() * SG_DEGREES_TO_RADIANS,
- aApt->getLongitude() * SG_DEGREES_TO_RADIANS,
- GPS_WP_APT);
+ if (!aPos) {
+ return NULL; // happens if find returns no match
+ }
+
+ return new GPSWaypoint(aPos->ident(),
+ aPos->latitude() * SG_DEGREES_TO_RADIANS,
+ aPos->longitude() * SG_DEGREES_TO_RADIANS,
+ GPSWpTypeFromFGPosType(aPos->type())
+ );
}
ostream& operator << (ostream& os, GPSAppWpType type) {
}
};
-GPSWaypoint* DCLGPS::FindFirstById(const string& id) const
+class DCLGPSFilter : public FGPositioned::Filter
{
- stringOrderKLN89 ordering;
- nav_list_type vors = globals->get_navlist()->findFirstByIdent(id, FG_NAV_VOR, false);
- nav_list_type ndbs = globals->get_navlist()->findFirstByIdent(id, FG_NAV_NDB, false);
- const FGFix* fix = globals->get_fixlist()->findFirstByIdent(id, &ordering);
- const FGAirport* apt = globals->get_airports()->findFirstById(id, &ordering);
- // search local gps waypoints (USR)
-
-// pick the best - ugly logic, sorry. This is a temporary fix to getting rid
-// of the huge local waypoint table, it'll die when there's a way to query
-// this stuff centrally.
-// what we're doing is using map inserts to order the result, then using
-// the first entry (begin()) as the lowest, hence best, match
- map<string, GPSWpType, stringOrderKLN89> sorter;
- if (fix) sorter[fix->get_ident()] = GPS_WP_INT;
- if (apt) sorter[apt->getId()] = GPS_WP_APT;
- if (!vors.empty()) sorter[vors.front()->get_ident()] = GPS_WP_VOR;
- if (!ndbs.empty()) sorter[ndbs.front()->get_ident()] = GPS_WP_NDB;
-
- if (sorter.empty()) return NULL; // no results at all
- GPSWpType ty = sorter.begin()->second;
-
- switch (ty) {
- case GPS_WP_INT:
- return GPSWaypoint::createFromFix(fix);
-
- case GPS_WP_APT:
- return GPSWaypoint::createFromAirport(apt);
-
- case GPS_WP_VOR:
- return GPSWaypoint::createFromNav(vors.front());
-
- case GPS_WP_NDB:
- return GPSWaypoint::createFromNav(ndbs.front());
- default:
- return NULL; // can't happen
+public:
+ virtual bool pass(const FGPositioned* aPos) const {
+ switch (aPos->type()) {
+ case FGPositioned::AIRPORT:
+ // how about heliports and seaports?
+ case FGPositioned::NDB:
+ case FGPositioned::VOR:
+ case FGPositioned::WAYPOINT:
+ case FGPositioned::FIX:
+ break;
+ default: return false; // reject all other types
+ }
+ return true;
}
+};
+
+GPSWaypoint* DCLGPS::FindFirstById(const string& id) const
+{
+ DCLGPSFilter filter;
+ FGPositionedRef result = FGPositioned::findNextWithPartialId(NULL, id, &filter);
+ return GPSWaypoint::createFromPositioned(result);
}
GPSWaypoint* DCLGPS::FindFirstByExactId(const string& id) const
{
- if (const FGAirport* apt = globals->get_airports()->search(id)) {
- return GPSWaypoint::createFromAirport(apt);
- }
-
- if (const FGFix* fix = globals->get_fixlist()->search(id)) {
- return GPSWaypoint::createFromFix(fix);
- }
-
- nav_list_type vors = globals->get_navlist()->findFirstByIdent(id, FG_NAV_VOR, true);
- if (!vors.empty()) {
- return GPSWaypoint::createFromNav(vors.front());
- }
+ SGGeod pos(SGGeod::fromRad(_lon, _lat));
+ FGPositionedRef result = FGPositioned::findClosestWithIdent(id, pos);
+ return GPSWaypoint::createFromPositioned(result);
+}
+
+// TODO - add the ASCII / alphabetical stuff from the Atlas version
+FGPositioned* DCLGPS::FindTypedFirstById(const string& id, FGPositioned::Type ty, bool &multi, bool exact)
+{
+ multi = false;
+ FGPositioned::TypeFilter filter(ty);
- nav_list_type ndbs = globals->get_navlist()->findFirstByIdent(id, FG_NAV_NDB, true);
- if (!ndbs.empty()) {
- return GPSWaypoint::createFromNav(ndbs.front());
+ if (exact) {
+ FGPositioned::List matches =
+ FGPositioned::findAllWithIdentSortedByRange(id, SGGeod::fromRad(_lon, _lat), &filter);
+ multi = (matches.size() > 1);
+ return matches.empty() ? NULL : matches.front().ptr();
}
- return NULL;
+ return FGPositioned::findNextWithPartialId(NULL, id, &filter);
}
-// Host specific lookup functions
-// TODO - add the ASCII / alphabetical stuff from the Atlas version
-FGNavRecord* DCLGPS::FindFirstVorById(const string& id, bool &multi, bool exact) {
- // NOTE - at the moment multi is never set.
- multi = false;
- //if(exact) return(_overlays->FindFirstVorById(id, exact));
-
- nav_list_type nav = globals->get_navlist()->findFirstByIdent(id, FG_NAV_VOR, exact);
-
- if(nav.size() > 1) multi = true;
- //return(nav.empty() ? NULL : *(nav.begin()));
-
- // The above is sort of what we want - unfortunately we can't guarantee no NDB/ILS at the moment
- if(nav.empty()) return(NULL);
-
- for(nav_list_iterator it = nav.begin(); it != nav.end(); ++it) {
- if((*it)->type() == FGPositioned::VOR) return(*it);
- }
- return(NULL); // Shouldn't get here!
+FGNavRecord* DCLGPS::FindFirstVorById(const string& id, bool &multi, bool exact)
+{
+ return dynamic_cast<FGNavRecord*>(FindTypedFirstById(id, FGPositioned::VOR, multi, exact));
}
-// TODO - add the ASCII / alphabetical stuff from the Atlas version
-FGNavRecord* DCLGPS::FindFirstNDBById(const string& id, bool &multi, bool exact) {
- // NOTE - at the moment multi is never set.
- multi = false;
- //if(exact) return(_overlays->FindFirstVorById(id, exact));
-
- nav_list_type nav = globals->get_navlist()->findFirstByIdent(id, FG_NAV_NDB, exact);
-
- if(nav.size() > 1) multi = true;
- //return(nav.empty() ? NULL : *(nav.begin()));
-
- // The above is sort of what we want - unfortunately we can't guarantee no NDB/ILS at the moment
- if(nav.empty()) return(NULL);
-
- for(nav_list_iterator it = nav.begin(); it != nav.end(); ++it) {
- if((*it)->type() == FGPositioned::NDB) return(*it);
- }
- return(NULL); // Shouldn't get here!
+FGNavRecord* DCLGPS::FindFirstNDBById(const string& id, bool &multi, bool exact)
+{
+ return dynamic_cast<FGNavRecord*>(FindTypedFirstById(id, FGPositioned::NDB, multi, exact));
}
-const FGFix* DCLGPS::FindFirstIntById(const string& id, bool &multi, bool exact) {
- // NOTE - at the moment multi is never set, and indeed can't be
- // since FG can only map one Fix per ID at the moment.
- multi = false;
- if (exact) return globals->get_fixlist()->search(id);
-
- stringOrderKLN89 ordering;
- return globals->get_fixlist()->findFirstByIdent(id, &ordering);
+const FGFix* DCLGPS::FindFirstIntById(const string& id, bool &multi, bool exact)
+{
+ return dynamic_cast<FGFix*>(FindTypedFirstById(id, FGPositioned::FIX, multi, exact));
}
-const FGAirport* DCLGPS::FindFirstAptById(const string& id, bool &multi, bool exact) {
- // NOTE - at the moment multi is never set.
- //cout << "FindFirstAptById, id = " << id << '\n';
- multi = false;
- if(exact) return(globals->get_airports()->search(id));
-
- stringOrderKLN89 ordering;
- return globals->get_airports()->findFirstById(id, &ordering);
+const FGAirport* DCLGPS::FindFirstAptById(const string& id, bool &multi, bool exact)
+{
+ return dynamic_cast<FGAirport*>(FindTypedFirstById(id, FGPositioned::AIRPORT, multi, exact));
}
-FGNavRecord* DCLGPS::FindClosestVor(double lat_rad, double lon_rad) {
- return(globals->get_navlist()->findClosest(lon_rad, lat_rad, 0.0, FG_NAV_VOR));
+FGNavRecord* DCLGPS::FindClosestVor(double lat_rad, double lon_rad) {
+ FGPositioned::TypeFilter filter(FGPositioned::VOR);
+ double cutoff = 1000; // nautical miles
+ FGPositionedRef v = FGPositioned::findClosest(SGGeod::fromRad(lon_rad, lat_rad), cutoff, &filter);
+ if (!v) {
+ return NULL;
+ }
+
+ return dynamic_cast<FGNavRecord*>(v.ptr());
}
//----------------------------------------------------------------------------------------------------------
#include <vector>
#include <map>
-#include <Navaids/navrecord.hxx>
-#include <Navaids/navlist.hxx>
-#include <Navaids/fixlist.hxx>
-#include <Airports/simple.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
-
-using namespace std;
+#include <Navaids/positioned.hxx>
class SGTime;
+class FGPositioned;
+
+// XXX fix me
+class FGNavRecord;
+class FGAirport;
+class FGFix;
enum GPSDistanceUnits {
GPS_DIST_UNITS_NM = 0,
GPSWaypoint(const std::string& aIdent, float lat, float lon, GPSWpType aType);
- static GPSWaypoint* createFromFix(const FGFix* aFix);
- static GPSWaypoint* createFromNav(const FGNavRecord* aNav);
- static GPSWaypoint* createFromAirport(const FGAirport* aApt);
+ static GPSWaypoint* createFromPositioned(const FGPositioned* aFix);
~GPSWaypoint();
string GetAprId(); // Returns the id with i, f, m or h added if appropriate. (Initial approach fix, final approach fix, etc)
// Find the closest VOR to a position in RADIANS.
FGNavRecord* FindClosestVor(double lat_rad, double lon_rad);
+ // helper to implement the above FindFirstXXX methods
+ FGPositioned* FindTypedFirstById(const std::string& id, FGPositioned::Type ty, bool &multi, bool exact);
+
// Position, orientation and velocity.
// These should be read from FG's built-in GPS logic if possible.
// Use the property node pointers below to do this.
#include <Navaids/awynet.hxx>
#include <Scenery/scenery.hxx>
#include <Scenery/tilemgr.hxx>
+#include <Airports\simple.hxx>
+#include <Navaids\navlist.hxx>
+#include <Navaids\fixlist.hxx>
#include "globals.hxx"
#include "renderer.hxx"
}
static void
-spatialFilterInBucket(const SGBucket& aBucket, const FGPositioned::Filter& aFilter, FGPositioned::List& aResult)
+spatialFilterInBucket(const SGBucket& aBucket, FGPositioned::Filter* aFilter, FGPositioned::List& aResult)
{
SpatialPositionedIndex::const_iterator it;
it = global_spatialIndex.find(aBucket.gen_index());
BucketEntry::const_iterator l = it->second.begin();
BucketEntry::const_iterator u = it->second.end();
+ if (!aFilter) { // pass everything
+ aResult.insert(aResult.end(), l, u);
+ return;
+ }
+
for ( ; l != u; ++l) {
- if (aFilter(*l)) {
+ if ((*aFilter)(*l)) {
aResult.push_back(*l);
}
}
static void
spatialFind(const SGGeod& aPos, double aRange,
- const FGPositioned::Filter& aFilter, FGPositioned::List& aResult)
+ FGPositioned::Filter* aFilter, FGPositioned::List& aResult)
{
SGBucket buck(aPos);
double lat = aPos.getLatitudeDeg(),
} // of i-iteration
}
+/*
class LowerLimitOfType
{
public:
}
};
+
static void
spatialFindTyped(const SGGeod& aPos, double aRange, FGPositioned::Type aLower, FGPositioned::Type aUpper, FGPositioned::List& aResult)
{
} // of j-iteration
} // of i-iteration
}
+*/
/**
*/
}
static FGPositionedRef
-namedFindClosestTyped(const std::string& aIdent, const SGGeod& aOrigin,
- FGPositioned::Type aLower, FGPositioned::Type aUpper)
+namedFindClosest(const std::string& aIdent, const SGGeod& aOrigin, FGPositioned::Filter* aFilter)
{
NamedIndexRange range = global_namedIndex.equal_range(aIdent);
if (range.first == range.second) {
NamedPositionedIndex::const_iterator check = range.first;
if (++check == range.second) {
// excellent, only one match in the range - all we care about is the type
- FGPositioned::Type ty = range.first->second->type();
- if ((ty < aLower) || (ty > aUpper)) {
+ if (aFilter && !aFilter->pass(range.first->second)) {
return NULL; // type check failed
}
for (; it != range.second; ++it) {
// filter by type
FGPositioned::Type ty = it->second->type();
- if ((ty < aLower) || (ty > aUpper)) {
+ if (aFilter && !aFilter->pass(range.first->second)) {
continue;
}
}
static FGPositioned::List
-spatialGetClosest(const SGGeod& aPos, unsigned int aN, double aCutoffNm, const FGPositioned::Filter& aFilter)
+spatialGetClosest(const SGGeod& aPos, unsigned int aN, double aCutoffNm, FGPositioned::Filter* aFilter)
{
FGPositioned::List result;
int radius = 1; // start at 1, radius 0 is handled explicitly
// search / query functions
FGPositionedRef
-FGPositioned::findClosestWithIdent(const std::string& aIdent, double aLat, double aLon)
-{
- return findClosestWithIdent(aIdent, SGGeod::fromDeg(aLon, aLat));
-}
-
-FGPositionedRef
-FGPositioned::findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos)
+FGPositioned::findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter)
{
- return namedFindClosestTyped(aIdent, aPos, INVALID, LAST_TYPE);
+ return namedFindClosest(aIdent, aPos, aFilter);
}
FGPositioned::List
-FGPositioned::findWithinRangeByType(const SGGeod& aPos, double aRangeNm, Type aTy)
-{
- List result;
- spatialFindTyped(aPos, aRangeNm, aTy, aTy, result);
- filterListByRange(aPos, aRangeNm, result);
- return result;
-}
-
-FGPositioned::List
-FGPositioned::findWithinRange(const SGGeod& aPos, double aRangeNm, const Filter& aFilter)
+FGPositioned::findWithinRange(const SGGeod& aPos, double aRangeNm, Filter* aFilter)
{
List result;
spatialFind(aPos, aRangeNm, aFilter, result);
}
FGPositioned::List
-FGPositioned::findAllWithIdent(const std::string& aIdent)
+FGPositioned::findAllWithIdentSortedByRange(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter)
{
List result;
NamedIndexRange range = global_namedIndex.equal_range(aIdent);
for (; range.first != range.second; ++range.first) {
+ if (aFilter && !aFilter->pass(range.first->second)) {
+ continue;
+ }
+
result.push_back(range.first->second);
}
+ sortByDistance(aPos, result);
return result;
}
+FGPositionedRef
+FGPositioned::findClosest(const SGGeod& aPos, double aCutoffNm, Filter* aFilter)
+{
+ FGPositioned::List l(spatialGetClosest(aPos, 1, aCutoffNm, aFilter));
+ if (l.empty()) {
+ return NULL;
+ }
+
+ assert(l.size() == 1);
+ return l.front();
+}
+
FGPositioned::List
-FGPositioned::findClosestN(const SGGeod& aPos, unsigned int aN, double aCutoffNm, const Filter& aFilter)
+FGPositioned::findClosestN(const SGGeod& aPos, unsigned int aN, double aCutoffNm, Filter* aFilter)
{
return spatialGetClosest(aPos, aN, aCutoffNm, aFilter);
}
+FGPositionedRef
+FGPositioned::findNextWithPartialId(FGPositionedRef aCur, const std::string& aId, Filter* aFilter)
+{
+ NamedIndexRange range = global_namedIndex.equal_range(aId);
+ for (; range.first != range.second; ++range.first) {
+ FGPositionedRef candidate = range.first->second;
+ if (aCur == candidate) {
+ aCur = NULL; // found our start point, next match will pass
+ continue;
+ }
+
+ if (aFilter && !aFilter->pass(candidate)) {
+ continue;
+ }
+
+ if (!aCur) {
+ return candidate;
+ }
+ }
+
+ return NULL; // fell out, no match in range
+}
+
const std::string& ident() const
{ return mIdent; }
+ /**
+ * Return the name of this positioned. By default this is the same as the
+ * ident, but for many derived classes it's more meaningful - the aiport or
+ * navaid name, for example.
+ */
+ virtual const std::string& name() const
+ { return mIdent; }
+
const SGGeod& geod() const
{ return mPosition; }
/**
* Predicate class to support custom filtering of FGPositioned queries
+ * Default implementation of this passes any FGPositioned instance.
*/
class Filter
{
public:
virtual ~Filter() { ; }
- virtual bool pass(FGPositioned* aPos) const = 0;
+ /**
+ * Over-rideable filter method. Default implementation returns true.
+ */
+ virtual bool pass(FGPositioned* aPos) const
+ { return true; }
bool operator()(FGPositioned* aPos) const
{ return pass(aPos); }
};
- static List findWithinRange(const SGGeod& aPos, double aRangeNm, const Filter& aFilter);
-
- static List findWithinRangeByType(const SGGeod& aPos, double aRangeNm, Type aTy);
-
- static FGPositionedRef findClosestWithIdent(const std::string& aIdent, double aLat, double aLon);
+ class TypeFilter : public Filter
+ {
+ public:
+ TypeFilter(Type aTy) : mType(aTy) { ; }
+ virtual bool pass(FGPositioned* aPos) const
+ { return (mType == aPos->type()); }
+ private:
+ const Type mType;
+ };
+
+ static List findWithinRange(const SGGeod& aPos, double aRangeNm, Filter* aFilter = NULL);
+
+ static FGPositionedRef findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter = NULL);
+
+ /**
+ * Find the next item with the specified partial ID, after the 'current' item
+ * Note this function is not hyper-efficient, particular where the partial id
+ * spans a large number of candidates.
+ *
+ * @param aCur - Current item, or NULL to retrieve the first item with partial id
+ * @param aId - the (partial) id to lookup
+ */
+ static FGPositionedRef findNextWithPartialId(FGPositionedRef aCur, const std::string& aId, Filter* aFilter = NULL);
- static FGPositionedRef findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos);
+ /**
+ * Find all items with the specified ident, and return then sorted by
+ * distance from a position
+ *
+ * @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);
+ /**
+ * Find the closest item to a position, which pass the specified filter
+ * A cutoff range in NM must be specified, to constrain the search acceptably.
+ * Very large cutoff values will make this slow.
+ *
+ * @result The closest item passing the filter, or NULL
+ * @param aCutoffNm - maximum distance to search within, in nautical miles
+ */
+ static FGPositionedRef findClosest(const SGGeod& aPos, double aCutoffNm, Filter* aFilter = NULL);
/**
* Find the closest N items to a position, which pass the specified filter
* @param aN - number of matches to find
* @param aCutoffNm - maximum distance to search within, in nautical miles
*/
- static List findClosestN(const SGGeod& aPos, unsigned int aN, double aCutoffNm, const Filter& aFilter);
+ static List findClosestN(const SGGeod& aPos, unsigned int aN, double aCutoffNm, Filter* aFilter = NULL);
+
+
/**
* Debug helper, map a type to a human-readable string