From: ehofman Date: Fri, 22 Aug 2008 11:22:22 +0000 (+0000) Subject: James Turner: X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=289f2f5f0d36cfb0e89aeb12ad07feb544ddb089;p=flightgear.git James Turner: This is a little intrusive on the KLN89 code, but avoids the wasteful cloning of the airports, runways and navaids which current happens, and also combines the ugly string ordering code. --- diff --git a/src/Airports/simple.cxx b/src/Airports/simple.cxx index 89c2e6ec2..c550f4822 100644 --- a/src/Airports/simple.cxx +++ b/src/Airports/simple.cxx @@ -321,24 +321,42 @@ FGAirport* FGAirportList::search( const string& id) return (itr == airports_by_id.end() ? NULL : itr->second); } +// wrap an FGIdentOrdering in an STL-compatible functor. not the most +// efficent / pretty thing in the world, but avoids template nastiness in the +// headers, and we're only doing O(log(N)) comparisoms per search +class orderingFunctor +{ +public: + orderingFunctor(FGIdentOrdering* aOrder) : + mOrdering(aOrder) + { assert(aOrder); } + + bool operator()(const airport_map::value_type& aA, const std::string& aB) const + { + return mOrdering->compare(aA.first,aB); + } + +private: + FGIdentOrdering* mOrdering; +}; -// search for first subsequent alphabetically to supplied id -const FGAirport* FGAirportList::findFirstById( const string& id, bool exact ) +const FGAirport* FGAirportList::findFirstById(const std::string& aIdent, FGIdentOrdering* aOrder) { - airport_map_iterator itr; - if (exact) { - itr = airports_by_id.find(id); - } else { - itr = airports_by_id.lower_bound(id); - } - if (itr == airports_by_id.end()) { - return (NULL); - } else { - return (itr->second); - } + airport_map_iterator itr; + if (aOrder) { + orderingFunctor func(aOrder); + itr = std::lower_bound(airports_by_id.begin(),airports_by_id.end(), aIdent, func); + } else { + itr = airports_by_id.lower_bound(aIdent); + } + + if (itr == airports_by_id.end()) { + return NULL; + } + + return itr->second; } - // search for the airport nearest the specified position FGAirport* FGAirportList::search(double lon_deg, double lat_deg, double max_range) { @@ -354,6 +372,7 @@ FGAirport* FGAirportList::search(double lon_deg, double lat_deg, FGAirportSearchFilter& filter) { double min_dist = max_range; + airport_list_iterator it = airports_array.begin(); airport_list_iterator end = airports_array.end(); airport_list_iterator closest = end; diff --git a/src/Airports/simple.hxx b/src/Airports/simple.hxx index b9b0c3e33..bedbd7d91 100644 --- a/src/Airports/simple.hxx +++ b/src/Airports/simple.hxx @@ -117,6 +117,14 @@ public: virtual bool pass(FGAirport*) { return true; } }; +class FGIdentOrdering { +public: + virtual ~FGIdentOrdering() + { ; } + + virtual bool compare(const std::string& aA, const std::string& aB) const + { return aA < aB; } +}; typedef std::map < std::string, FGAirport* > airport_map; typedef airport_map::iterator airport_map_iterator; @@ -152,11 +160,11 @@ public: // Search for the next airport in ASCII sequence to the supplied id. // eg. id = "KDC" or "KDCA" would both return "KDCA". - // If exact = true then only exact matches are returned. // NOTE: Numbers come prior to A-Z in ASCII sequence so id = "LD" would return "LD57", not "LDDP" + // optional ordering can make letters come before numbers // Implementation assumes airport codes are unique. // Returns NULL if unsucessfull. - const FGAirport* findFirstById( const std::string& id, bool exact = false ); + const FGAirport* findFirstById(const std::string& aIdent, FGIdentOrdering* aOrder = NULL); // search for the airport closest to the specified position // (currently a linear inefficient search so it's probably not diff --git a/src/Instrumentation/KLN89/kln89_page_fpl.cxx b/src/Instrumentation/KLN89/kln89_page_fpl.cxx index 24b04055e..b78934371 100644 --- a/src/Instrumentation/KLN89/kln89_page_fpl.cxx +++ b/src/Instrumentation/KLN89/kln89_page_fpl.cxx @@ -940,21 +940,21 @@ void KLN89FplPage::Knob2Left1() { _bEntWp = true; _fp0SelWpId.clear(); // Waypoints don't become the DTO default whilst being entered. - bool multi; - const GPSWaypoint* wp = _kln89->FindFirstById(_entWpStr.substr(0, _wLinePos+1), multi, false); + GPSWaypoint* wp = _kln89->FindFirstById(_entWpStr.substr(0, _wLinePos+1)); if(NULL == wp) { // no-op } else { - if(_entWp == NULL) { - _entWp = new GPSWaypoint; - if(_fplPos + (_uLinePos - 4) >= _kln89->_flightPlans[_subPage]->waypoints.size()) { + if(_entWp) { + *_entWp = *wp; // copy + delete wp; + } else { + _entWp = wp; + if(_fplPos + (_uLinePos - 4) >= _kln89->_flightPlans[_subPage]->waypoints.size()) { _kln89->_flightPlans[_subPage]->waypoints.push_back(_entWp); } else { _kln89->_flightPlans[_subPage]->waypoints.insert(_kln89->_flightPlans[_subPage]->waypoints.begin()+(_fplPos + (_uLinePos - 4)), _entWp); } - } - // copy - *_entWp = *wp; + } } } } @@ -1013,21 +1013,21 @@ void KLN89FplPage::Knob2Right1() { _bEntWp = true; _fp0SelWpId.clear(); // Waypoints don't become the DTO default whilst being entered. - bool multi; - const GPSWaypoint* wp = _kln89->FindFirstById(_entWpStr.substr(0, _wLinePos+1), multi, false); + GPSWaypoint* wp = _kln89->FindFirstById(_entWpStr.substr(0, _wLinePos+1)); if(NULL == wp) { // no-op } else { - if(_entWp == NULL) { - _entWp = new GPSWaypoint; - if(_fplPos + (_uLinePos - 4) >= _kln89->_flightPlans[_subPage]->waypoints.size()) { + if(_entWp) { + *_entWp = *wp; // copy + delete wp; + } else { + _entWp = wp; + if(_fplPos + (_uLinePos - 4) >= _kln89->_flightPlans[_subPage]->waypoints.size()) { _kln89->_flightPlans[_subPage]->waypoints.push_back(_entWp); } else { _kln89->_flightPlans[_subPage]->waypoints.insert(_kln89->_flightPlans[_subPage]->waypoints.begin()+(_fplPos + (_uLinePos - 4)), _entWp); } - } - // copy - *_entWp = *wp; + } } } } diff --git a/src/Instrumentation/dclgps.cxx b/src/Instrumentation/dclgps.cxx index 9c4ada712..bff360b94 100644 --- a/src/Instrumentation/dclgps.cxx +++ b/src/Instrumentation/dclgps.cxx @@ -119,6 +119,15 @@ GPSWaypoint::GPSWaypoint() { appType = GPS_APP_NONE; } +GPSWaypoint::GPSWaypoint(const std::string& aIdent, float aLat, float aLon, GPSWpType aType) : + id(aIdent), + lat(aLat), + lon(aLon), + type(aType), + appType(GPS_APP_NONE) +{ +} + GPSWaypoint::~GPSWaypoint() {} string GPSWaypoint::GetAprId() { @@ -129,6 +138,33 @@ string GPSWaypoint::GetAprId() { else return(id); } +GPSWaypoint* GPSWaypoint::createFromFix(const FGFix* aFix) +{ + 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)); +} + +GPSWaypoint* GPSWaypoint::createFromAirport(const FGAirport* aApt) +{ + assert(aApt); + return new GPSWaypoint(aApt->getId(), + aApt->getLatitude() * SG_DEGREES_TO_RADIANS, + aApt->getLongitude() * SG_DEGREES_TO_RADIANS, + GPS_WP_APT); +} + ostream& operator << (ostream& os, GPSAppWpType type) { switch(type) { case(GPS_IAF): return(os << "IAF"); @@ -301,12 +337,7 @@ DCLGPS::DCLGPS(RenderArea2D* instrument) { DCLGPS::~DCLGPS() { delete _time; - for(gps_waypoint_map_iterator itr = _waypoints.begin(); itr != _waypoints.end(); ++itr) { - for(unsigned int i = 0; i < (*itr).second.size(); ++i) { - delete(((*itr).second)[i]); - } - } - delete _approachFP; // Don't need to delete the waypoints inside since they point to + delete _approachFP; // Don't need to delete the waypoints inside since they point to // the waypoints in the approach database. // TODO - may need to delete the approach database!! } @@ -329,332 +360,9 @@ void DCLGPS::init() { globals->get_commands()->addCommand("kln89_knob1right1", do_kln89_knob1right1); globals->get_commands()->addCommand("kln89_knob2left1", do_kln89_knob2left1); globals->get_commands()->addCommand("kln89_knob2right1", do_kln89_knob2right1); - - // Build the GPS-specific databases. - // TODO - consider splitting into real life GPS database regions - eg Americas, Europe etc. - // Note that this needs to run after FG's airport and nav databases are up and running - _waypoints.clear(); - const airport_list* apts = globals->get_airports()->getAirportList(); - for(unsigned int i = 0; i < apts->size(); ++i) { - FGAirport* a = (*apts)[i]; - GPSWaypoint* w = new GPSWaypoint; - w->id = a->getId(); - w->lat = a->getLatitude() * SG_DEGREES_TO_RADIANS; - w->lon = a->getLongitude() * SG_DEGREES_TO_RADIANS; - w->type = GPS_WP_APT; - gps_waypoint_map_iterator wtr = _waypoints.find(a->getId()); - if(wtr == _waypoints.end()) { - gps_waypoint_array arr; - arr.push_back(w); - _waypoints[w->id] = arr; - } else { - wtr->second.push_back(w); - } - } - nav_map_type navs = globals->get_navlist()->get_navaids(); - for(nav_map_iterator itr = navs.begin(); itr != navs.end(); ++itr) { - nav_list_type nlst = itr->second; - for(unsigned int i = 0; i < nlst.size(); ++i) { - FGNavRecord* n = nlst[i]; - if(n->get_fg_type() == FG_NAV_VOR || n->get_fg_type() == FG_NAV_NDB) { // We don't bother with ILS etc. - GPSWaypoint* w = new GPSWaypoint; - w->id = n->get_ident(); - w->lat = n->get_lat() * SG_DEGREES_TO_RADIANS; - w->lon = n->get_lon() * SG_DEGREES_TO_RADIANS; - w->type = (n->get_fg_type() == FG_NAV_VOR ? GPS_WP_VOR : GPS_WP_NDB); - gps_waypoint_map_iterator wtr = _waypoints.find(n->get_ident()); - if(wtr == _waypoints.end()) { - gps_waypoint_array arr; - arr.push_back(w); - _waypoints[w->id] = arr; - } else { - wtr->second.push_back(w); - } - } - } - } - const fix_map_type* fixes = globals->get_fixlist()->getFixList(); - for(fix_map_const_iterator itr = fixes->begin(); itr != fixes->end(); ++itr) { - FGFix f = itr->second; - GPSWaypoint* w = new GPSWaypoint; - w->id = f.get_ident(); - w->lat = f.get_lat() * SG_DEGREES_TO_RADIANS; - w->lon = f.get_lon() * SG_DEGREES_TO_RADIANS; - w->type = GPS_WP_INT; - gps_waypoint_map_iterator wtr = _waypoints.find(f.get_ident()); - if(wtr == _waypoints.end()) { - gps_waypoint_array arr; - arr.push_back(w); - _waypoints[w->id] = arr; - } else { - wtr->second.push_back(w); - } - } - // TODO - add USR waypoints as well. - + // Not sure if this should be here, but OK for now. CreateDefaultFlightPlans(); - - // Hack - hardwire some instrument approaches for testing. - // TODO - read these from file - either all at startup or as needed. - FGNPIAP* iap = new FGNPIAP; - iap->_id = "KHWD"; - iap->_name = "VOR/DME OR GPS-B"; - iap->_abbrev = "VOR/D"; - iap->_rwyStr = "B"; - iap->_IAF.clear(); - iap->_IAP.clear(); - iap->_MAP.clear(); - // ------- - GPSWaypoint* wp = new GPSWaypoint; - wp->id = "SUNOL"; - bool multi; - // Nasty using the find any function here, but it saves converting data from FGFix etc. - const GPSWaypoint* fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_IAF; - iap->_IAF.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "MABRY"; - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_IAF; - iap->_IAF.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "IMPLY"; - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_IAP; - iap->_IAP.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "DECOT"; - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_FAF; - iap->_IAP.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "MAPVV"; - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_MAP; - iap->_IAP.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "OAK"; - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_MAHP; - iap->_MAP.push_back(wp); - // ------- - _np_iap[iap->_id].push_back(iap); - // ----------------------- - // ----------------------- - iap = new FGNPIAP; - iap->_id = "KHWD"; - iap->_name = "VOR OR GPS-A"; - iap->_abbrev = "VOR-"; - iap->_rwyStr = "A"; - iap->_IAF.clear(); - iap->_IAP.clear(); - iap->_MAP.clear(); - // ------- - wp = new GPSWaypoint; - wp->id = "SUNOL"; - // Nasty using the find any function here, but it saves converting data from FGFix etc. - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_IAF; - iap->_IAF.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "MABRY"; - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_IAF; - iap->_IAF.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "IMPLY"; - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_IAP; - iap->_IAP.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "DECOT"; - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_FAF; - iap->_IAP.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "MAPVV"; - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_MAP; - iap->_IAP.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "OAK"; - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_MAHP; - iap->_MAP.push_back(wp); - // ------- - _np_iap[iap->_id].push_back(iap); - // ------------------ - // ------------------ - /* - // Ugh - don't load this one - the waypoints required aren't in fix.dat.gz - result: program crash! - // TODO - make the IAP loader robust to absent waypoints. - iap = new FGNPIAP; - iap->_id = "KHWD"; - iap->_name = "GPS RWY 28L"; - iap->_abbrev = "GPS"; - iap->_rwyStr = "28L"; - iap->_IAF.clear(); - iap->_IAP.clear(); - iap->_MAP.clear(); - // ------- - wp = new GPSWaypoint; - wp->id = "SUNOL"; - // Nasty using the find any function here, but it saves converting data from FGFix etc. - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_IAF; - iap->_IAF.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "SJC"; - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_IAF; - iap->_IAF.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "JOCPI"; - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_IAP; - iap->_IAP.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "SUDGE"; - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_FAF; - iap->_IAP.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "RW28L"; - wp->appType = GPS_MAP; - if(wp->id.substr(0, 2) == "RW" && wp->appType == GPS_MAP) { - // Assume that this is a missed-approach point based on the runway number - // Get the runway threshold location etc - } else { - fp = FindFirstById(wp->id, multi, true); - if(fp == NULL) { - cout << "Failed to find waypoint " << wp->id << " in database...\n"; - } else { - *wp = *fp; - } - } - iap->_IAP.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "OAK"; - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_MAHP; - iap->_MAP.push_back(wp); - // ------- - _np_iap[iap->_id].push_back(iap); - */ - iap = new FGNPIAP; - iap->_id = "C83"; - iap->_name = "GPS RWY 30"; - iap->_abbrev = "GPS"; - iap->_rwyStr = "30"; - iap->_IAF.clear(); - iap->_IAP.clear(); - iap->_MAP.clear(); - // ------- - wp = new GPSWaypoint; - wp->id = "MAXNI"; - // Nasty using the find any function here, but it saves converting data from FGFix etc. - fp = FindFirstById(wp->id, multi, true); - if(fp) { - *wp = *fp; - wp->appType = GPS_IAF; - iap->_IAF.push_back(wp); - } - // ------- - wp = new GPSWaypoint; - wp->id = "PATYY"; - fp = FindFirstById(wp->id, multi, true); - if(fp) { - *wp = *fp; - wp->appType = GPS_IAF; - iap->_IAF.push_back(wp); - } - // ------- - wp = new GPSWaypoint; - wp->id = "TRACY"; - fp = FindFirstById(wp->id, multi, true); - if(fp) { - *wp = *fp; - wp->appType = GPS_IAF; - iap->_IAF.push_back(wp); - } - // ------- - wp = new GPSWaypoint; - wp->id = "TRACY"; - fp = FindFirstById(wp->id, multi, true); - if(fp) { - *wp = *fp; - wp->appType = GPS_IAP; - iap->_IAP.push_back(wp); - } - // ------- - wp = new GPSWaypoint; - wp->id = "BABPI"; - fp = FindFirstById(wp->id, multi, true); - if(fp) { - *wp = *fp; - wp->appType = GPS_FAF; - iap->_IAP.push_back(wp); - } - // ------- - wp = new GPSWaypoint; - wp->id = "AMOSY"; - wp->appType = GPS_MAP; - if(wp->id.substr(0, 2) == "RW" && wp->appType == GPS_MAP) { - // Assume that this is a missed-approach point based on the runway number - // TODO: Get the runway threshold location etc - cout << "TODO - implement missed-approach point based on rwy no.\n"; - } else { - fp = FindFirstById(wp->id, multi, true); - if(fp == NULL) { - cout << "Failed to find waypoint " << wp->id << " in database...\n"; - } else { - *wp = *fp; - wp->appType = GPS_MAP; - } - } - iap->_IAP.push_back(wp); - // ------- - wp = new GPSWaypoint; - wp->id = "HAIRE"; - fp = FindFirstById(wp->id, multi, true); - *wp = *fp; - wp->appType = GPS_MAHP; - iap->_MAP.push_back(wp); - // ------- - _np_iap[iap->_id].push_back(iap); } void DCLGPS::bind() { @@ -962,8 +670,7 @@ double DCLGPS::GetCDIDeflection() const { void DCLGPS::DtoInitiate(const string& s) { //cout << "DtoInitiate, s = " << s << '\n'; - bool multi; - const GPSWaypoint* wp = FindFirstById(s, multi, true); + const GPSWaypoint* wp = FindFirstByExactId(s); if(wp) { //cout << "Waypoint found, starting dto operation!\n"; _dto = true; @@ -972,6 +679,7 @@ void DCLGPS::DtoInitiate(const string& s) { _fromWaypoint.lon = _gpsLon; _fromWaypoint.type = GPS_WP_VIRT; _fromWaypoint.id = "DTOWP"; + delete wp; } else { //cout << "Waypoint not found, ignoring dto request\n"; // Should bring up the user waypoint page, but we're not implementing that yet. @@ -1105,10 +813,10 @@ double DCLGPS::GetTimeToWaypoint(const string& id) { } else if(id == _activeWaypoint.id) { return(_eta); } else { - bool multi; - const GPSWaypoint* wp = FindFirstById(id, multi, true); + const GPSWaypoint* wp = FindFirstByExactId(id); if(wp == NULL) return(-1.0); double distm = GetGreatCircleDistance(_gpsLat, _gpsLon, wp->lat, wp->lon); + delete wp; return(distm / _groundSpeed_ms); } return(-1.0); // Hopefully we never get here! @@ -1312,79 +1020,116 @@ void DCLGPS::CreateFlightPlan(GPSFlightPlan* fp, vector ids, vectorsecond)[0]); +/** + * STL functor for use with algorithms. This comapres strings according to + * the KLN-89's notion of ordering, with digits after letters. + * Also implements FGIdentOrdering so it can be passed into the various list + * find helpers. + */ + +class stringOrderKLN89 : public FGIdentOrdering +{ +public: + bool operator()(const gps_waypoint_map::value_type& aA, const std::string& aB) const + { + return compare(aA.first, aB); + } + + bool operator()(const std::string& aS1, const std::string& aS2) const + { + return compare(aS1, aS2); + } + + virtual bool compare(const std::string& aS1, const std::string& aS2) const + { + if (aS1.empty()) return true; + if (aS2.empty()) return false; + + char* a = (char*) aS1.c_str(); + char* b = (char*) aS2.c_str(); + + for ( ; *a && *b; ++a, ++b) { + if (*a == *b) continue; + + bool aDigit = isdigit(*a); + bool bDigit = isdigit(*b); + + if (aDigit == bDigit) { + return (*a < *b); // we already know they're not equal + } + + // digit-ness differs + if (aDigit) return false; // s1 = KS9 goes *after* s2 = KSA + assert(bDigit); + return true; // s1 = KSF, s2 = KS5, s1 is indeed < s2 } -} - -const GPSWaypoint* DCLGPS::FindFirstById(const string& id, bool &multi, bool exact) { - multi = false; - if(exact) return(ActualFindFirstById(id, exact)); - - // OK, that was the easy case, now the fuzzy case - const GPSWaypoint* w1 = ActualFindFirstById(id); - if(w1 == NULL) return(w1); - - // The non-trivial code from here to the end of the function is all to deal with the fact that - // the KLN89 alphabetical order (numbers AFTER letters) differs from ASCII order (numbers BEFORE letters). - string id2 = id; - //string id3 = id+'0'; - string id4 = id+'A'; - // Increment the last char to provide the boundary. Note that 'Z' -> '[' but we also need to check '0' for all since GPS has numbers after letters - //bool alfa = isalpha(id2[id2.size() - 1]); - id2[id2.size() - 1] = id2[id2.size() - 1] + 1; - const GPSWaypoint* w2 = ActualFindFirstById(id2); - //FGAirport* a3 = globals->get_airports()->findFirstById(id3); - const GPSWaypoint* w4 = ActualFindFirstById(id4); - //cout << "Strings sent were " << id << ", " << id2 << " and " << id4 << '\n'; - //cout << "Airports returned were (a1, a2, a4): " << a1->getId() << ", " << a2->getId() << ", " << a4->getId() << '\n'; - //cout << "Pointers were " << a1 << ", " << a2 << ", " << a4 << '\n'; - - // TODO - the below handles the imediately following char OK - // eg id = "KD" returns "KDAA" instead of "KD5" - // but it doesn't handle numbers / letters further down the string, - // eg - id = "I" returns "IA01" instead of "IAN" - // We either need to provide a custom comparison operator, or recurse this function if !isalpha further down the string. - // (Currenly fixed with recursion). - - if(w4 != w2) { // A-Z match - preferred - //cout << "A-Z match!\n"; - if(w4->id.size() - id.size() > 2) { - // Check for numbers further on - for(unsigned int i=id.size(); iid.size(); ++i) { - if(!isalpha(w4->id[i])) { - //cout << "SUBSTR is " << (a4->getId()).substr(0, i) << '\n'; - return(FindFirstById(w4->id.substr(0, i), multi, exact)); - } - } - } - return(w4); - } else if(w1 != w2) { // 0-9 match - //cout << "0-9 match!\n"; - if(w1->id.size() - id.size() > 2) { - // Check for numbers further on - for(unsigned int i=id.size(); iid.size(); ++i) { - if(!isalpha(w1->id[i])) { - //cout << "SUBSTR2 is " << (a4->getId()).substr(0, i) << '\n'; - return(FindFirstById(w1->id.substr(0, i), multi, exact)); - } - } - } - return(w1); - } else { // No match - return(NULL); - } - return NULL; + + if (*b) return true; // *a == 0, s2 is longer + return false; // s1 is longer, or strings are equal + } +}; + +GPSWaypoint* DCLGPS::FindFirstById(const string& id) const +{ + 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 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 + } +} + +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()); + } + + nav_list_type ndbs = globals->get_navlist()->findFirstByIdent(id, FG_NAV_NDB, true); + if (!ndbs.empty()) { + return GPSWaypoint::createFromNav(ndbs.front()); + } + + return NULL; } // Host specific lookup functions @@ -1407,55 +1152,6 @@ FGNavRecord* DCLGPS::FindFirstVorById(const string& id, bool &multi, bool exact) } return(NULL); // Shouldn't get here! } -#if 0 -Overlays::NAV* 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)); - - // OK, that was the easy case, now the fuzzy case - Overlays::NAV* n1 = _overlays->FindFirstVorById(id); - if(n1 == NULL) return(n1); - - string id2 = id; - string id3 = id+'0'; - string id4 = id+'A'; - // Increment the last char to provide the boundary. Note that 'Z' -> '[' but we also need to check '0' for all since GPS has numbers after letters - bool alfa = isalpha(id2[id2.size() - 1]); - id2[id2.size() - 1] = id2[id2.size() - 1] + 1; - Overlays::NAV* n2 = _overlays->FindFirstVorById(id2); - //Overlays::NAV* n3 = _overlays->FindFirstVorById(id3); - //Overlays::NAV* n4 = _overlays->FindFirstVorById(id4); - //cout << "Strings sent were " << id << ", " << id2 << ", " << id3 << ", " << id4 << '\n'; - - - if(alfa) { - if(n1 != n2) { // match - return(n1); - } else { - return(NULL); - } - } - - /* - if(n1 != n2) { - // Something matches - the problem is the number/letter preference order is reversed between the GPS and the STL - if(n4 != n2) { - // There's a letter match - return that - return(n4); - } else { - // By definition we must have a number match - if(n3 == n2) cout << "HELP - LOGIC FLAW in find VOR!\n"; - return(n3); - } - } else { - // No match - return(NULL); - } - */ - return NULL; -} -#endif //0 // TODO - add the ASCII / alphabetical stuff from the Atlas version FGNavRecord* DCLGPS::FindFirstNDBById(const string& id, bool &multi, bool exact) { @@ -1476,179 +1172,25 @@ FGNavRecord* DCLGPS::FindFirstNDBById(const string& id, bool &multi, bool exact) } return(NULL); // Shouldn't get here! } -#if 0 -Overlays::NAV* DCLGPS::FindFirstNDBById(const string& id, bool &multi, bool exact) { - // NOTE - at the moment multi is never set. - multi = false; - if(exact) return(_overlays->FindFirstNDBById(id, exact)); - - // OK, that was the easy case, now the fuzzy case - Overlays::NAV* n1 = _overlays->FindFirstNDBById(id); - if(n1 == NULL) return(n1); - - string id2 = id; - string id3 = id+'0'; - string id4 = id+'A'; - // Increment the last char to provide the boundary. Note that 'Z' -> '[' but we also need to check '0' for all since GPS has numbers after letters - bool alfa = isalpha(id2[id2.size() - 1]); - id2[id2.size() - 1] = id2[id2.size() - 1] + 1; - Overlays::NAV* n2 = _overlays->FindFirstNDBById(id2); - //Overlays::NAV* n3 = _overlays->FindFirstNDBById(id3); - //Overlays::NAV* n4 = _overlays->FindFirstNDBById(id4); - //cout << "Strings sent were " << id << ", " << id2 << ", " << id3 << ", " << id4 << '\n'; - - - if(alfa) { - if(n1 != n2) { // match - return(n1); - } else { - return(NULL); - } - } - - /* - if(n1 != n2) { - // Something matches - the problem is the number/letter preference order is reversed between the GPS and the STL - if(n4 != n2) { - // There's a letter match - return that - return(n4); - } else { - // By definition we must have a number match - if(n3 == n2) cout << "HELP - LOGIC FLAW in find VOR!\n"; - return(n3); - } - } else { - // No match - return(NULL); - } - */ - return NULL; -} -#endif //0 -// TODO - add the ASCII / alphabetical stuff from the Atlas version 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()->findFirstByIdent(id, exact)); - - const FGFix* f1 = globals->get_fixlist()->findFirstByIdent(id, exact); - if(f1 == NULL) return(f1); + if (exact) return globals->get_fixlist()->search(id); - // The non-trivial code from here to the end of the function is all to deal with the fact that - // the KLN89 alphabetical order (numbers AFTER letters) differs from ASCII order (numbers BEFORE letters). - // It is copied from the airport version which is definately needed, but at present I'm not actually - // sure if any fixes in FG or real-life have numbers in them! - string id2 = id; - //string id3 = id+'0'; - string id4 = id+'A'; - // Increment the last char to provide the boundary. Note that 'Z' -> '[' but we also need to check '0' for all since GPS has numbers after letters - //bool alfa = isalpha(id2[id2.size() - 1]); - id2[id2.size() - 1] = id2[id2.size() - 1] + 1; - const FGFix* f2 = globals->get_fixlist()->findFirstByIdent(id2); - //const FGFix* a3 = globals->get_fixlist()->findFirstByIdent(id3); - const FGFix* f4 = globals->get_fixlist()->findFirstByIdent(id4); - - // TODO - the below handles the imediately following char OK - // eg id = "KD" returns "KDAA" instead of "KD5" - // but it doesn't handle numbers / letters further down the string, - // eg - id = "I" returns "IA01" instead of "IAN" - // We either need to provide a custom comparison operator, or recurse this function if !isalpha further down the string. - // (Currenly fixed with recursion). - - if(f4 != f2) { // A-Z match - preferred - //cout << "A-Z match!\n"; - if(f4->get_ident().size() - id.size() > 2) { - // Check for numbers further on - for(unsigned int i=id.size(); iget_ident().size(); ++i) { - if(!isalpha(f4->get_ident()[i])) { - //cout << "SUBSTR is " << (a4->getId()).substr(0, i) << '\n'; - return(FindFirstIntById(f4->get_ident().substr(0, i), multi, exact)); - } - } - } - return(f4); - } else if(f1 != f2) { // 0-9 match - //cout << "0-9 match!\n"; - if(f1->get_ident().size() - id.size() > 2) { - // Check for numbers further on - for(unsigned int i=id.size(); iget_ident().size(); ++i) { - if(!isalpha(f1->get_ident()[i])) { - //cout << "SUBSTR2 is " << (a4->getId()).substr(0, i) << '\n'; - return(FindFirstIntById(f1->get_ident().substr(0, i), multi, exact)); - } - } - } - return(f1); - } else { // No match - return(NULL); - } - - return NULL; // Don't think we can ever get here. + stringOrderKLN89 ordering; + return globals->get_fixlist()->findFirstByIdent(id, &ordering); } 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()->findFirstById(id, exact)); - - // OK, that was the easy case, now the fuzzy case - const FGAirport* a1 = globals->get_airports()->findFirstById(id); - if(a1 == NULL) return(a1); + if(exact) return(globals->get_airports()->search(id)); - // The non-trivial code from here to the end of the function is all to deal with the fact that - // the KLN89 alphabetical order (numbers AFTER letters) differs from ASCII order (numbers BEFORE letters). - string id2 = id; - //string id3 = id+'0'; - string id4 = id+'A'; - // Increment the last char to provide the boundary. Note that 'Z' -> '[' but we also need to check '0' for all since GPS has numbers after letters - //bool alfa = isalpha(id2[id2.size() - 1]); - id2[id2.size() - 1] = id2[id2.size() - 1] + 1; - const FGAirport* a2 = globals->get_airports()->findFirstById(id2); - //FGAirport* a3 = globals->get_airports()->findFirstById(id3); - const FGAirport* a4 = globals->get_airports()->findFirstById(id4); - //cout << "Strings sent were " << id << ", " << id2 << " and " << id4 << '\n'; - //cout << "Airports returned were (a1, a2, a4): " << a1->getId() << ", " << a2->getId() << ", " << a4->getId() << '\n'; - //cout << "Pointers were " << a1 << ", " << a2 << ", " << a4 << '\n'; - - // TODO - the below handles the imediately following char OK - // eg id = "KD" returns "KDAA" instead of "KD5" - // but it doesn't handle numbers / letters further down the string, - // eg - id = "I" returns "IA01" instead of "IAN" - // We either need to provide a custom comparison operator, or recurse this function if !isalpha further down the string. - // (Currenly fixed with recursion). - - if(a4 != a2) { // A-Z match - preferred - //cout << "A-Z match!\n"; - if(a4->getId().size() - id.size() > 2) { - // Check for numbers further on - for(unsigned int i=id.size(); igetId().size(); ++i) { - if(!isalpha(a4->getId()[i])) { - //cout << "SUBSTR is " << (a4->getId()).substr(0, i) << '\n'; - return(FindFirstAptById(a4->getId().substr(0, i), multi, exact)); - } - } - } - return(a4); - } else if(a1 != a2) { // 0-9 match - //cout << "0-9 match!\n"; - if(a1->getId().size() - id.size() > 2) { - // Check for numbers further on - for(unsigned int i=id.size(); igetId().size(); ++i) { - if(!isalpha(a1->getId()[i])) { - //cout << "SUBSTR2 is " << (a4->getId()).substr(0, i) << '\n'; - return(FindFirstAptById(a1->getId().substr(0, i), multi, exact)); - } - } - } - return(a1); - } else { // No match - return(NULL); - } - - return NULL; + stringOrderKLN89 ordering; + return globals->get_airports()->findFirstById(id, &ordering); } FGNavRecord* DCLGPS::FindClosestVor(double lat_rad, double lon_rad) { diff --git a/src/Instrumentation/dclgps.hxx b/src/Instrumentation/dclgps.hxx index 552691827..3766e6932 100644 --- a/src/Instrumentation/dclgps.hxx +++ b/src/Instrumentation/dclgps.hxx @@ -91,6 +91,13 @@ ostream& operator << (ostream& os, GPSAppWpType type); struct GPSWaypoint { GPSWaypoint(); + + 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); + ~GPSWaypoint(); string GetAprId(); // Returns the id with i, f, m or h added if appropriate. (Initial approach fix, final approach fix, etc) string id; @@ -407,14 +414,13 @@ protected: // // Data and lookup functions - // All waypoints mapped by id. - gps_waypoint_map _waypoints; -private: - // Worker function for the below. - const GPSWaypoint* ActualFindFirstById(const string& id, bool exact = false); + + protected: // Find first of any type of waypoint by id. (TODO - Possibly we should return multiple waypoints here). - const GPSWaypoint* FindFirstById(const string& id, bool &multi, bool exact = false); + GPSWaypoint* FindFirstById(const string& id) const; + GPSWaypoint* FindFirstByExactId(const string& id) const; + FGNavRecord* FindFirstVorById(const string& id, bool &multi, bool exact = false); FGNavRecord* FindFirstNDBById(const string& id, bool &multi, bool exact = false); const FGAirport* FindFirstAptById(const string& id, bool &multi, bool exact = false); diff --git a/src/Navaids/fixlist.cxx b/src/Navaids/fixlist.cxx index 36bdf420d..29b246f9b 100644 --- a/src/Navaids/fixlist.cxx +++ b/src/Navaids/fixlist.cxx @@ -30,6 +30,8 @@ #include #include "fixlist.hxx" +#include "Airports/simple.hxx"; + using std::pair; @@ -120,17 +122,45 @@ bool FGFixList::query_and_offset( const string& ident, double lon, double lat, return true; } -const FGFix* FGFixList::findFirstByIdent( const string& ident, bool exact) +const FGFix* FGFixList::search(const string& ident) { - fix_map_iterator itr; - if(exact) { - itr = fixlist.find(ident); - } else { - itr = fixlist.lower_bound(ident); - } - if(itr == fixlist.end()) { - return(NULL); - } else { - return(&(itr->second)); - } + fix_map_iterator itr = fixlist.find(ident); + if (itr == fixlist.end()) { + return NULL; + } + + return &itr->second; +} + +class orderingFunctor +{ +public: + orderingFunctor(FGIdentOrdering* aOrder) : + mOrdering(aOrder) + { assert(aOrder); } + + bool operator()(const fix_map_type::value_type& aA, const std::string& aB) const + { + return mOrdering->compare(aA.first,aB); + } + +private: + FGIdentOrdering* mOrdering; +}; + +const FGFix* FGFixList::findFirstByIdent( const string& ident, FGIdentOrdering* aOrder) +{ + fix_map_iterator itr; + if (aOrder) { + orderingFunctor func(aOrder); + itr = std::lower_bound(fixlist.begin(),fixlist.end(), ident, func); + } else { + itr = fixlist.lower_bound(ident); + } + + if (itr == fixlist.end()) { + return NULL; + } + + return &itr->second; } diff --git a/src/Navaids/fixlist.hxx b/src/Navaids/fixlist.hxx index cf81e8ce2..d0b32eb80 100644 --- a/src/Navaids/fixlist.hxx +++ b/src/Navaids/fixlist.hxx @@ -43,6 +43,8 @@ typedef multimap < string, FGFix > fix_map_type; typedef fix_map_type::iterator fix_map_iterator; typedef fix_map_type::const_iterator fix_map_const_iterator; +class FGIdentOrdering; // FIXME, currently declared in Airports/simple.hxx + class FGFixList { fix_map_type fixlist; @@ -58,11 +60,13 @@ public: // query the database for the specified fix bool query( const string& ident, FGFix *f ); + const FGFix* search(const string& ident); + // Find fix of requested type with closest exact or following ident // (by ACSII values) to that supplied (ie. a lower-bound lookup). // Supplying true for exact forces only exact matches to be returned (similar to above function) // Returns NULL if no match found. - const FGFix* findFirstByIdent( const string& ident, bool exact = false ); + const FGFix* findFirstByIdent( const string& ident, FGIdentOrdering* aOrder = NULL); // query the database for the specified fix, lon and lat are // in degrees, elev is in meters