From 660d59a098c1033571337da0938c79906a577f4a Mon Sep 17 00:00:00 2001 From: jmt Date: Fri, 26 Dec 2008 15:26:42 +0000 Subject: [PATCH] Another clean-up iteration: FGAirportList::search is gone, replaced by two static FGAirport helpers. As a result, another global index goes away. Use the helpers to avoid ugly FGPositioned down-casts in various places. Also converts the environment/METAR code to deal with FGAirport pointers, instead of string identifiers, and contains work-in-progress code to implement the AirportList dialog using FGPositioned. This isn't enabled yet for various reasons, but is the final piece to allow FGAirportList to be removed. --- src/Airports/apt_loader.cxx | 6 +- src/Airports/simple.cxx | 83 ++++++++++++---------------- src/Airports/simple.hxx | 30 ++++++---- src/Environment/environment_ctrl.cxx | 40 ++++++-------- src/Environment/environment_ctrl.hxx | 9 ++- src/Environment/fgclouds.cxx | 2 +- src/Instrumentation/mk_viii.cxx | 4 +- src/Navaids/positioned.cxx | 75 ++++++++++++++++++++++++- src/Scripting/NasalSys.cxx | 14 ++--- src/Traffic/SchedFlight.cxx | 4 +- 10 files changed, 163 insertions(+), 104 deletions(-) diff --git a/src/Airports/apt_loader.cxx b/src/Airports/apt_loader.cxx index 92495181a..d0602ac69 100644 --- a/src/Airports/apt_loader.cxx +++ b/src/Airports/apt_loader.cxx @@ -289,8 +289,10 @@ bool fgAirportDBLoad( FGAirportList *airports, if ( ident == "#" || ident == "//" ) { metar_in >> skipeol; } else { - const FGAirport* a = airports->search( ident ); - if ( a ) const_cast(a)->setMetar(true); + FGAirport* apt = FGAirport::findByIdent(ident); + if (apt) { + apt->setMetar(true); + } } } diff --git a/src/Airports/simple.cxx b/src/Airports/simple.cxx index e7b9a48d8..af1e0de19 100644 --- a/src/Airports/simple.cxx +++ b/src/Airports/simple.cxx @@ -52,8 +52,8 @@ using std::sort; using std::random_shuffle; - - +// magic import of a helper which uses FGPositioned internals +extern char** searchAirportNamesAndIdents(const std::string& aFilter); /*************************************************************************** * FGAirport @@ -279,32 +279,41 @@ bool FGAirport::HardSurfaceFilter::pass(FGPositioned* aPos) const return static_cast(aPos)->hasHardRunwayOfLengthFt(mMinLengthFt); } +FGAirport* FGAirport::findByIdent(const std::string& aIdent) +{ + FGPositionedRef r; + AirportFilter filter; + r = FGPositioned::findNextWithPartialId(r, aIdent, &filter); + if (!r) { + return NULL; // we don't warn here, let the caller do that + } + return static_cast(r.ptr()); +} + +FGAirport* FGAirport::getByIdent(const std::string& aIdent) +{ + FGPositionedRef r; + AirportFilter filter; + r = FGPositioned::findNextWithPartialId(r, aIdent, &filter); + if (!r) { + throw sg_range_exception("No such airport with ident: " + aIdent); + } + return static_cast(r.ptr()); +} + +char** FGAirport::searchNamesAndIdents(const std::string& aFilter) +{ + // we delegate all the work to a horrible helper in FGPositioned, which can + // access the (private) index data. + return searchAirportNamesAndIdents(aFilter); +} + /****************************************************************************** * FGAirportList *****************************************************************************/ -// Populates a list of subdirectories of $FG_ROOT/Airports/AI so that -// the add() method doesn't have to try opening 2 XML files in each of -// thousands of non-existent directories. FIXME: should probably add -// code to free this list after parsing of apt.dat is finished; -// non-issue at the moment, however, as there are no AI subdirectories -// in the base package. -// -// Note: 2005/12/23: This is probably not necessary anymore, because I'm -// Switching to runtime airport dynamics loading (DT). FGAirportList::FGAirportList() { -// ulDir* d; -// ulDirEnt* dent; -// SGPath aid( globals->get_fg_root() ); -// aid.append( "/Airports/AI" ); -// if((d = ulOpenDir(aid.c_str())) == NULL) -// return; -// while((dent = ulReadDir(d)) != NULL) { -// SG_LOG( SG_GENERAL, SG_DEBUG, "Dent: " << dent->d_name ); -// ai_dirs.insert(dent->d_name); -// } -// ulCloseDir(d); } @@ -321,20 +330,11 @@ FGAirport* FGAirportList::add( const string &id, const SGGeod& location, const S const string &name, bool has_metar, FGPositioned::Type aType) { FGAirport* a = new FGAirport(id, location, tower_location, name, has_metar, aType); - airports_by_id[a->getId()] = a; // try and read in an auxilary file - airports_array.push_back( a ); return a; } -// search for the specified id -FGAirport* FGAirportList::search( const string& id) -{ - airport_map_iterator itr = airports_by_id.find(id); - return (itr == airports_by_id.end() ? NULL : itr->second); -} - int FGAirportList::size () const { @@ -354,26 +354,11 @@ const FGAirport *FGAirportList::getAirport( unsigned int index ) const // find basic airport location info from airport database const FGAirport *fgFindAirportID( const string& id) { - const FGAirport* result = NULL; - if ( id.length() ) { - SG_LOG( SG_GENERAL, SG_BULK, "Searching for airport code = " << id ); - - result = globals->get_airports()->search( id ); - - if ( result == NULL ) { - SG_LOG( SG_GENERAL, SG_ALERT, - "Failed to find " << id << " in apt.dat.gz" ); - return NULL; - } - } else { + if ( id.empty() ) { return NULL; } - SG_LOG( SG_GENERAL, SG_BULK, - "Position for " << id << " is (" - << result->getLongitude() << ", " - << result->getLatitude() << ")" ); - - return result; + + return FGAirport::findByIdent(id); } diff --git a/src/Airports/simple.hxx b/src/Airports/simple.hxx index 8b2aaf10f..6d95bcf87 100644 --- a/src/Airports/simple.hxx +++ b/src/Airports/simple.hxx @@ -30,7 +30,6 @@ #include #include -#include #include #include "Navaids/positioned.hxx" @@ -123,6 +122,26 @@ public: * passes all airports, including seaports and heliports. */ static FGAirport* findClosest(const SGGeod& aPos, double aCuttofNm, Filter* filter = NULL); + + /** + * Helper to look up an FGAirport instance by unique ident. Throws an + * exception if the airport could not be found - so callers can assume + * the result is non-NULL. + */ + static FGAirport* getByIdent(const std::string& aIdent); + + /** + * Helper to look up an FGAirport instance by unique ident. Returns NULL + * if the airport could not be found. + */ + static FGAirport* findByIdent(const std::string& aIdent); + + /** + * Specialised helper to implement the AirportList dialog. Performs a + * case-insensitive search on airport names and ICAO codes, and returns + * matches in a format suitable for use by a puaList. + */ + static char** searchNamesAndIdents(const std::string& aFilter); private: typedef std::vector::const_iterator Runway_iterator; /** @@ -137,10 +156,6 @@ private: std::vector mTaxiways; }; -typedef std::map < std::string, FGAirport* > airport_map; -typedef airport_map::iterator airport_map_iterator; -typedef airport_map::const_iterator const_airport_map_iterator; - typedef std::vector < FGAirport * > airport_list; typedef airport_list::iterator airport_list_iterator; typedef airport_list::const_iterator const_airport_list_iterator; @@ -150,7 +165,6 @@ typedef airport_list::const_iterator const_airport_list_iterator; class FGAirportList { private: - airport_map airports_by_id; airport_list airports_array; public: @@ -164,10 +178,6 @@ public: FGAirport* add( const std::string& id, const SGGeod& location, const SGGeod& tower, const std::string& name, bool has_metar, FGPositioned::Type aType); - // search for the specified id. - // Returns NULL if unsucessfull. - FGAirport* search( const std::string& id ); - /** * Return the number of airports in the list. */ diff --git a/src/Environment/environment_ctrl.cxx b/src/Environment/environment_ctrl.cxx index e0844d81d..8fb0ad8fe 100644 --- a/src/Environment/environment_ctrl.cxx +++ b/src/Environment/environment_ctrl.cxx @@ -336,7 +336,6 @@ FGInterpolateEnvironmentCtrl::bucket::lessThan(bucket *a, bucket *b) FGMetarEnvironmentCtrl::FGMetarEnvironmentCtrl () : env( new FGInterpolateEnvironmentCtrl ), - _icao( "" ), metar_loaded( false ), search_interval_sec( 60.0 ), // 1 minute same_station_interval_sec( 900.0 ), // 15 minutes @@ -667,17 +666,16 @@ FGMetarEnvironmentCtrl::init () while ( !found_metar && (_error_count < 3) ) { AirportWithMetar filter; - FGPositionedRef a = FGPositioned::findClosest(pos, 10000.0, &filter); + FGAirport* a = FGAirport::findClosest(pos, 10000.0, &filter); if (!a) { break; } - FGMetarResult result = fetch_data(a->ident()); + FGMetarResult result = fetch_data(a); if ( result.m != NULL ) { SG_LOG( SG_GENERAL, SG_INFO, "closest station w/ metar = " << a->ident()); last_apt = a; - _icao = a->ident(); search_elapsed = 0.0; fetch_elapsed = 0.0; update_metar_properties( result.m ); @@ -688,7 +686,7 @@ FGMetarEnvironmentCtrl::init () // mark as no metar so it doesn't show up in subsequent // searches. SG_LOG( SG_GENERAL, SG_INFO, "no metar at metar = " << a->ident() ); - static_cast(a.ptr())->setMetar(false); + a->setMetar(false); } } // of airprot-with-metar search iteration @@ -729,16 +727,15 @@ FGMetarEnvironmentCtrl::update(double delta_time_sec) // queue if ( search_elapsed > search_interval_sec ) { AirportWithMetar filter; - FGPositionedRef a = FGPositioned::findClosest(pos, 10000.0, &filter); + FGAirport* a = FGAirport::findClosest(pos, 10000.0, &filter); if (a) { if ( !last_apt || last_apt->ident() != a->ident() || fetch_elapsed > same_station_interval_sec ) { SG_LOG( SG_GENERAL, SG_INFO, "closest station w/ metar = " << a->ident()); - request_queue.push( a->ident() ); + request_queue.push(a); last_apt = a; - _icao = a->ident(); search_elapsed = 0.0; fetch_elapsed = 0.0; } else { @@ -760,15 +757,15 @@ FGMetarEnvironmentCtrl::update(double delta_time_sec) #if !defined(ENABLE_THREADS) // No loader thread running so manually fetch the data - string id = ""; + FGAirport* apt = NULL; while ( !request_queue.empty() ) { - id = request_queue.front(); + apt = request_queue.front(); request_queue.pop(); } - if ( !id.empty() ) { - SG_LOG( SG_GENERAL, SG_INFO, "inline fetching = " << id ); - result = fetch_data( id ); + if (apt) { + SG_LOG( SG_GENERAL, SG_INFO, "inline fetching = " << apt->ident() ); + result = fetch_data( apt ); result_queue.push( result ); } #endif // ENABLE_THREADS @@ -786,9 +783,8 @@ FGMetarEnvironmentCtrl::update(double delta_time_sec) // mark as no metar so it doesn't show up in subsequent // searches, and signal an immediate re-search. SG_LOG( SG_GENERAL, SG_WARN, - "no metar at station = " << result.icao ); - const FGAirport* apt = globals->get_airports()->search(result.icao); - const_cast(apt)->setMetar(false); + "no metar at station = " << result.airport->ident() ); + result.airport->setMetar(false); search_elapsed = 9999.0; } } @@ -804,10 +800,10 @@ FGMetarEnvironmentCtrl::setEnvironment (FGEnvironment * environment) } FGMetarResult -FGMetarEnvironmentCtrl::fetch_data( const string &icao ) +FGMetarEnvironmentCtrl::fetch_data(FGAirport* apt) { FGMetarResult result; - result.icao = icao; + result.airport = apt; // if the last error was more than three seconds ago, // then pretent nothing happened. @@ -819,18 +815,14 @@ FGMetarEnvironmentCtrl::fetch_data( const string &icao ) _error_count = 0; } - // fetch station elevation if exists - const FGAirport* a = globals->get_airports()->search( icao ); - if ( a ) { - station_elevation_ft = a->getElevation(); - } + station_elevation_ft = apt->getElevation(); // fetch current metar data try { string host = proxy_host->getStringValue(); string auth = proxy_auth->getStringValue(); string port = proxy_port->getStringValue(); - result.m = new FGMetar( icao, host, port, auth); + result.m = new FGMetar( apt->ident(), host, port, auth); long max_age = metar_max_age->getLongValue(); long age = result.m->getAge_min(); diff --git a/src/Environment/environment_ctrl.hxx b/src/Environment/environment_ctrl.hxx index 9538548ec..b49610e32 100644 --- a/src/Environment/environment_ctrl.hxx +++ b/src/Environment/environment_ctrl.hxx @@ -137,7 +137,7 @@ private: // A convenience wrapper around FGMetar struct FGMetarResult { - std::string icao; + FGAirport* airport; FGMetar *m; }; @@ -160,7 +160,6 @@ public: private: FGInterpolateEnvironmentCtrl *env; - std::string _icao; bool metar_loaded; float station_elevation_ft; float search_interval_sec; @@ -174,7 +173,7 @@ private: SGPropertyNode_ptr proxy_auth; SGPropertyNode_ptr metar_max_age; - FGMetarResult fetch_data( const string &icao ); + FGMetarResult fetch_data(FGAirport* apt); virtual void update_metar_properties( const FGMetar *m ); void update_env_config(); double interpolate_prop(const char * currentname, const char * requiredname, double dvalue); @@ -200,7 +199,7 @@ private: /** * FIFO queue which holds a pointer to the fetched metar data. */ - SGBlockingQueue request_queue; + SGBlockingQueue request_queue; /** * FIFO queue which holds a pointer to the fetched metar data. @@ -210,7 +209,7 @@ private: /** * FIFO queue which holds a pointer to the fetched metar data. */ - std::queue request_queue; + std::queue request_queue; /** * FIFO queue which holds a pointer to the fetched metar data. diff --git a/src/Environment/fgclouds.cxx b/src/Environment/fgclouds.cxx index 04e2dbe60..1a3897981 100644 --- a/src/Environment/fgclouds.cxx +++ b/src/Environment/fgclouds.cxx @@ -464,7 +464,7 @@ void FGClouds::buildScenario( const string& scenario ) { if( station == "XXXX" ) station_elevation_ft = fgGetDouble("/position/ground-elev-m", 0.0); else { - const FGAirport* a = globals->get_airports()->search( station ); + const FGAirport* a = FGAirport::findByIdent(station); station_elevation_ft = (a ? a->getElevation() : 0.0); } diff --git a/src/Instrumentation/mk_viii.cxx b/src/Instrumentation/mk_viii.cxx index 8908078c9..50e1df1a7 100755 --- a/src/Instrumentation/mk_viii.cxx +++ b/src/Instrumentation/mk_viii.cxx @@ -4521,7 +4521,7 @@ MK_VIII::TCFHandler::update_runway () // large airports, which may have a runway located far away from // the airport's reference point. AirportFilter filter(mk); - FGPositionedRef apt = FGPositioned::findClosest( + FGAirport* apt = FGAirport::findClosest( SGGeod::fromDeg(mk_data(gps_longitude).get(), mk_data(gps_latitude).get()), 30.0, &filter); @@ -4529,7 +4529,7 @@ MK_VIII::TCFHandler::update_runway () has_runway = true; - FGRunway* _runway = select_runway(static_cast(apt.ptr())); + FGRunway* _runway = select_runway(apt); runway.center.latitude = _runway->latitude(); runway.center.longitude = _runway->longitude(); diff --git a/src/Navaids/positioned.cxx b/src/Navaids/positioned.cxx index 54bc57c4e..374450739 100644 --- a/src/Navaids/positioned.cxx +++ b/src/Navaids/positioned.cxx @@ -24,7 +24,8 @@ #include #include -#include +#include // for sort +#include // for char-traits toupper #include @@ -336,6 +337,78 @@ spatialGetClosest(const SGGeod& aPos, unsigned int aN, double aCutoffNm, FGPosit return result; } +////////////////////////////////////////////////////////////////////////////// + +/** + * A special purpose helper (imported by FGAirport::searchNamesAndIdents) to + * implement the AirportList dialog. It's unfortunate that it needs to reside + * here, but for now it's least ugly solution. + */ +char** searchAirportNamesAndIdents(const std::string& aFilter) +{ + const std::ctype &ct = std::use_facet >(std::locale()); + std::string filter(aFilter); + if (!filter.empty()) { + ct.toupper((char *)filter.data(), (char *)filter.data() + filter.size()); + } + + NamedPositionedIndex::const_iterator it = global_namedIndex.begin(); + NamedPositionedIndex::const_iterator end = global_namedIndex.end(); + + FGPositioned::List matches; + if (aFilter.empty()) { + matches.reserve(2000); + } + + for (; it != end; ++it) { + FGPositioned::Type ty = it->second->type(); + if ((ty < FGPositioned::AIRPORT) || (ty > FGPositioned::SEAPORT)) { + continue; + } + + if (aFilter.empty()) { + matches.push_back(it->second); + continue; + } + + if ((it->second->name().find(aFilter) == std::string::npos) && + (it->second->ident().find(aFilter) == std::string::npos)) { + continue; + } + + matches.push_back(it->second); + } + + // convert results to format comptible with puaList + unsigned int numMatches = matches.size(); + char** result = new char*[numMatches + 1]; + result[numMatches] = NULL; // end-of-list marker + + // nasty code to avoid excessive string copying and allocations. + // We format results as follows (note whitespace!): + // ' name-of-airport-chars (icao)' + // so the total length is: + // 1 + strlen(name) + 4 + 4 (for the ICAO) + 1 + 1 (for the null) + // which gives a grand total of 11 + the length of the name. + + for (unsigned int i=0; iname().size(); + char* entry = new char[nameLength + 11]; + entry[0] = ' '; + memcpy(entry + 1, matches[i]->name().c_str(), nameLength); + entry[nameLength + 1] = ' '; + entry[nameLength + 2] = ' '; + entry[nameLength + 3] = ' '; + entry[nameLength + 4] = '('; + memcpy(entry + nameLength + 5, matches[i]->ident().c_str(), 4); + entry[nameLength + 9] = ')'; + entry[nameLength + 10] = 0; + result[i] = entry; + } + + return result; +} + /////////////////////////////////////////////////////////////////////////////// FGPositioned::FGPositioned(Type ty, const std::string& aIdent, const SGGeod& aPos, bool aIndexed) : diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index 61055ce4e..af2d51c66 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -524,7 +524,7 @@ static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args) static SGConstPropertyNode_ptr latn = fgGetNode("/position/latitude-deg", true); static SGConstPropertyNode_ptr lonn = fgGetNode("/position/longitude-deg", true); SGGeod pos; - FGPositionedRef ref; + FGAirport* apt = NULL; if(argc >= 2 && naIsNum(args[0]) && naIsNum(args[1])) { pos = SGGeod::fromDeg(args[1].num, args[0].num); @@ -547,8 +547,8 @@ static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args) else if(!strcmp(s, "heliport")) filter.type = FGPositioned::HELIPORT; else { // user provided an , hopefully - ref = globals->get_airports()->search(s); - if (!ref) { + apt = FGAirport::findByIdent(s); + if (!apt) { naRuntimeError(c, "airportinfo() couldn't find airport:%s", s); return naNil(); } @@ -558,13 +558,11 @@ static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args) return naNil(); } - if (!ref) { - ref = FGPositioned::findClosest(pos, maxRange, &filter); + if (!apt) { + apt = FGAirport::findClosest(pos, maxRange, &filter); + if(!apt) return naNil(); } - if(!ref) return naNil(); - FGAirport *apt = static_cast(ref.ptr()); - string id = apt->ident(); string name = apt->name(); diff --git a/src/Traffic/SchedFlight.cxx b/src/Traffic/SchedFlight.cxx index 0fad5b88d..56db6204c 100644 --- a/src/Traffic/SchedFlight.cxx +++ b/src/Traffic/SchedFlight.cxx @@ -271,13 +271,13 @@ FGAirport * FGScheduledFlight::getArrivalAirport () bool FGScheduledFlight::initializeAirports() { //cerr << "Initializing using : " << depId << " " << arrId << endl; - departurePort = globals->get_airports()->search(depId); + departurePort = FGAirport::findByIdent(depId); if(departurePort == NULL) { SG_LOG( SG_GENERAL, SG_WARN, "Traffic manager could not find departure airport : " << depId); return false; } - arrivalPort = globals->get_airports()->search(arrId); + arrivalPort = FGAirport::findByIdent(arrId); if(arrivalPort == NULL) { SG_LOG( SG_GENERAL, SG_WARN, "Traffic manager could not find arrival airport : " << arrId); -- 2.39.5