From 15139a42b6a064e66bff74958893dd748769d68c Mon Sep 17 00:00:00 2001
From: mfranz <mfranz>
Date: Fri, 5 Oct 2007 21:54:52 +0000
Subject: [PATCH] - make FGAirport::search() more versatile, so that it can't
 only search   the next airport or airport with METAR station, but about any
 type of   airport - as a side effect this change makes it also 30 to 50%
 faster  :-)

In the long run this linear search shall be replaced with a spatial
algorithm (like octree), which will be a much bigger performance gain.
---
 src/Airports/simple.cxx              | 38 ++++++++++++++++++----------
 src/Airports/simple.hxx              | 15 +++++++----
 src/Environment/environment_ctrl.cxx |  7 +++--
 src/Instrumentation/gps.cxx          |  2 +-
 src/Scripting/NasalSys.cxx           |  4 +--
 5 files changed, 42 insertions(+), 24 deletions(-)

diff --git a/src/Airports/simple.cxx b/src/Airports/simple.cxx
index 6efac4c0e..e92b08fbd 100644
--- a/src/Airports/simple.cxx
+++ b/src/Airports/simple.cxx
@@ -187,25 +187,35 @@ const FGAirport* FGAirportList::findFirstById( const string& id, bool exact )
 
 
 // search for the airport nearest the specified position
-FGAirport* FGAirportList::search( double lon_deg, double lat_deg,
-                                  bool with_metar )
+FGAirport* FGAirportList::search(double lon_deg, double lat_deg)
+{
+    static FGAirportSearchFilter accept_any;
+    return search(lon_deg, lat_deg, accept_any);
+}
+
+
+// search for the airport nearest the specified position and
+// passing the filter
+FGAirport* FGAirportList::search(double lon_deg, double lat_deg,
+        FGAirportSearchFilter& search)
 {
-    int closest = -1;
     double min_dist = 360.0;
-    unsigned int i;
-    for ( i = 0; i < airports_array.size(); ++i ) {
+    airport_list_iterator it = airports_array.begin();
+    airport_list_iterator end = airports_array.end();
+    airport_list_iterator closest = end;
+    for (; it != end; ++it) {
+        if (!search.acceptable(*it))
+            continue;
+
         // crude manhatten distance based on lat/lon difference
-        double d = fabs(lon_deg - airports_array[i]->getLongitude())
-            + fabs(lat_deg - airports_array[i]->getLatitude());
-        if ( d < min_dist ) {
-            if ( !with_metar || (with_metar&&airports_array[i]->getMetar()) ) {
-                closest = i;
-                min_dist = d;
-            }
+        double d = fabs(lon_deg - (*it)->getLongitude())
+                + fabs(lat_deg - (*it)->getLatitude());
+        if (d < min_dist) {
+            closest = it;
+            min_dist = d;
         }
     }
-
-    return ( closest > -1 ? airports_array[closest] : NULL );
+    return closest != end ? *closest : 0;
 }
 
 
diff --git a/src/Airports/simple.hxx b/src/Airports/simple.hxx
index 9251ee423..d1930730a 100644
--- a/src/Airports/simple.hxx
+++ b/src/Airports/simple.hxx
@@ -106,6 +106,12 @@ private:
 };
 
 
+class FGAirportSearchFilter {
+public:
+    virtual ~FGAirportSearchFilter() {}
+    virtual bool acceptable(FGAirport*) { return true; }
+};
+
 
 typedef map < string, FGAirport* > airport_map;
 typedef airport_map::iterator airport_map_iterator;
@@ -122,7 +128,6 @@ private:
 
     airport_map airports_by_id;
     airport_list airports_array;
-    //set < string > ai_dirs;
 
 public:
     // Constructor (new)
@@ -150,10 +155,10 @@ public:
 
     // search for the airport closest to the specified position
     // (currently a linear inefficient search so it's probably not
-    // best to use this at runtime.)  If with_metar is true, then only
-    // return station id's marked as having metar data.
-    // Returns NULL if fails (unlikely unless none have metar and with_metar spec'd!)
-    FGAirport* search( double lon_deg, double lat_deg, bool with_metar );
+    // best to use this at runtime.)  An FGAirportSearchFilter class
+    // can be used to restrict the possible choices to a subtype.
+    FGAirport* search( double lon_deg, double lat_deg );
+    FGAirport* search( double lon_deg, double lat_deg, FGAirportSearchFilter& );
 
     /**
      * Return the number of airports in the list.
diff --git a/src/Environment/environment_ctrl.cxx b/src/Environment/environment_ctrl.cxx
index 82af0e027..5733ed67a 100644
--- a/src/Environment/environment_ctrl.cxx
+++ b/src/Environment/environment_ctrl.cxx
@@ -41,6 +41,9 @@
 
 SG_USING_STD(sort);
 
+class metar_filter : public FGAirportSearchFilter {
+    virtual bool acceptable(FGAirport *a) { return a->getMetar(); }
+} metar_only;
 
 
 ////////////////////////////////////////////////////////////////////////
@@ -648,7 +651,7 @@ FGMetarEnvironmentCtrl::init ()
         const FGAirport* a = globals->get_airports()
                    ->search( longitude->getDoubleValue(),
                              latitude->getDoubleValue(),
-                             true );
+                             metar_only );
         if ( a ) {  
             FGMetarResult result = fetch_data( a->getId() );
             if ( result.m != NULL ) {
@@ -711,7 +714,7 @@ FGMetarEnvironmentCtrl::update(double delta_time_sec)
         const FGAirport* a = globals->get_airports()
                    ->search( longitude->getDoubleValue(),
                              latitude->getDoubleValue(),
-                             true );
+                             metar_only );
         if ( a ) {
             if ( !last_apt || last_apt->getId() != a->getId()
                  || fetch_elapsed > same_station_interval_sec )
diff --git a/src/Instrumentation/gps.cxx b/src/Instrumentation/gps.cxx
index ae0f3c810..b466948c4 100644
--- a/src/Instrumentation/gps.cxx
+++ b/src/Instrumentation/gps.cxx
@@ -308,7 +308,7 @@ GPS::update (double delta_time_sec)
         // If the get-nearest-airport-node is true.
         // Get the nearest airport, and set it as waypoint 1.
         if (_get_nearest_airport_node->getBoolValue()) {
-            const FGAirport* a = globals->get_airports()->search(longitude_deg, latitude_deg, false);
+            const FGAirport* a = globals->get_airports()->search(longitude_deg, latitude_deg);
             if(a) {
                 _wp1_ID_node->setStringValue(a->getId().c_str());
                 wp1_longitude_deg = a->getLongitude();
diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx
index 32dcb98e6..423935d34 100644
--- a/src/Scripting/NasalSys.cxx
+++ b/src/Scripting/NasalSys.cxx
@@ -496,11 +496,11 @@ static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args)
     FGAirportList *aptlst = globals->get_airports();
     FGAirport *apt;
     if(argc == 0)
-        apt = aptlst->search(lon->getDoubleValue(), lat->getDoubleValue(), false);
+        apt = aptlst->search(lon->getDoubleValue(), lat->getDoubleValue());
     else if(argc == 1 && naIsString(args[0]))
         apt = aptlst->search(naStr_data(args[0]));
     else if(argc == 2 && naIsNum(args[0]) && naIsNum(args[1]))
-        apt = aptlst->search(args[1].num, args[0].num, false);
+        apt = aptlst->search(args[1].num, args[0].num);
     else {
         naRuntimeError(c, "airportinfo() with invalid function arguments");
         return naNil();