]> git.mxchange.org Git - flightgear.git/blobdiff - src/Airports/simple.cxx
Cygwin fix.
[flightgear.git] / src / Airports / simple.cxx
index 6c1e711947b1afd27a50fc429a0ca9630956c269..e24090b07c41c968155502277f2cc3535943ef15 100644 (file)
 #include <algorithm>
 
 #include <simgear/compiler.h>
+
+#include <plib/sg.h>
+#include <plib/ul.h>
+
 #include <Environment/environment_mgr.hxx>
 #include <Environment/environment.hxx>
 #include <simgear/misc/sg_path.hxx>
 #include "simple.hxx"
 
 SG_USING_STD(sort);
+SG_USING_STD(random_shuffle);
+
+#ifdef __CYGWIN__
+#define HUGE HUGE_VAL
+#endif
 
 /******************************************************************************
  * ScheduleTime
@@ -113,7 +122,7 @@ string ScheduleTime::getName(time_t dayStart)
       //cerr << "Nr of items to process: " << nrItems << endl;
       if (nrItems > 0)
        {
-         for (int i = 0; i < start.size(); i++)
+         for (unsigned int i = 0; i < start.size(); i++)
            {
              //cerr << i << endl;
              if ((dayStart >= start[i]) && (dayStart <= end[i]))
@@ -209,8 +218,8 @@ void RunwayGroup::setActive(string aptId,
   FGRunway rwy;
   int activeRwys = rwyList.size(); // get the number of runways active
   int nrOfPreferences;
-  bool found = true;
-  double heading;
+  // bool found = true;
+  // double heading;
   double hdgDiff;
   double crossWind;
   double tailWind;
@@ -335,7 +344,7 @@ void RunwayGroup::getActive(int i, string *name, string *type)
     {
       return;
     }
-  if (nrActive == rwyList.size())
+  if (nrActive == (int)rwyList.size())
     {
       *name = rwyList[i].getRwyList(active);
       *type = rwyList[i].getType();
@@ -560,28 +569,10 @@ void  FGRunwayPreference::error (const char * message, int line, int column) {
        << endl;
 }
 
-/*********************************************************************************
- * FGParking
- ********************************************************************************/
-FGParking::FGParking(double lat,
-                    double lon,
-                    double hdg,
-                    double rad,
-                    int idx,
-                    string name,
-                    string tpe,
-                    string codes)
-{
-  latitude     = lat;
-  longitude    = lon;
-  heading      = hdg;
-  parkingName  = name;
-  index        = idx;
-  type         = tpe;
-  airlineCodes = codes;
-}
-
-double FGParking::processPosition(string pos)
+/*****************************************************************************
+ * Helper function for parsing position string
+ ****************************************************************************/
+double processPosition(string pos)
 {
   string prefix;
   string subs;
@@ -605,6 +596,29 @@ double FGParking::processPosition(string pos)
   return value;
 }
 
+
+/*********************************************************************************
+ * FGParking
+ ********************************************************************************/
+FGParking::FGParking(double lat,
+                    double lon,
+                    double hdg,
+                    double rad,
+                    int idx,
+                    string name,
+                    string tpe,
+                    string codes)
+{
+  latitude     = lat;
+  longitude    = lon;
+  heading      = hdg;
+  parkingName  = name;
+  index        = idx;
+  type         = tpe;
+  airlineCodes = codes;
+}
+
+
 /***************************************************************************
  * FGAirport
  ***************************************************************************/
@@ -661,6 +675,22 @@ FGAirport::FGAirport(string id, double lon, double lat, double elev, string name
  
 }
 
+// Initialization required after XMLRead
+void FGAirport::init() 
+{
+  // This may seem a bit weird to first randomly shuffle the parkings
+  // and then sort them again. However, parkings are sorted here by ascending 
+  // radius. Since many parkings have similar radii, with each radius class they will
+  // still be allocated relatively systematically. Randomizing prior to sorting will
+  // prevent any initial orderings to be destroyed, leading (hopefully) to a more 
+  // naturalistic gate assignment. 
+  random_shuffle(parkings.begin(), parkings.end());
+  sort(parkings.begin(), parkings.end());
+  // add the gate positions to the ground network. 
+  groundNetwork.addNodes(&parkings);
+  groundNetwork.init();
+}
+
 bool FGAirport::getAvailableParking(double *lat, double *lon, double *heading, int *gateId, double rad, string flType, string acType, string airline)
 {
   bool found = false;
@@ -851,14 +881,14 @@ void FGAirport::getParking (int id, double *lat, double* lon, double *heading)
 
 FGParking *FGAirport::getParking(int i) 
 { 
-  if (i < parkings.size()) 
+  if (i < (int)parkings.size()) 
     return &(parkings[i]); 
   else 
     return 0;
 }
 string FGAirport::getParkingName(int i) 
 { 
-  if (i < parkings.size() && i >= 0) 
+  if (i < (int)parkings.size() && i >= 0) 
     return (parkings[i].getName()); 
   else 
     return string("overflow");
@@ -888,8 +918,12 @@ void  FGAirport::endXML () {
 }
 
 void  FGAirport::startElement (const char * name, const XMLAttributes &atts) {
-  const char * attval;
+  // const char *attval;
   FGParking park;
+  FGTaxiNode taxiNode;
+  FGTaxiSegment taxiSegment;
+  int index = 0;
+  taxiSegment.setIndex(index);
   //cout << "Start element " << name << endl;
   string attname;
   string value;
@@ -929,9 +963,35 @@ void  FGAirport::startElement (const char * name, const XMLAttributes &atts) {
        }
       park.setName((gateName+gateNumber));
       parkings.push_back(park);
-    }  
+    }
+  if (name == string("node")) 
+    {
+      for (int i = 0; i < atts.size() ; i++)
+       {
+         attname = atts.getName(i);
+         if (attname == string("index"))
+           taxiNode.setIndex(atoi(atts.getValue(i)));
+         if (attname == string("lat"))
+           taxiNode.setLatitude(atts.getValue(i));
+         if (attname == string("lon"))
+           taxiNode.setLongitude(atts.getValue(i));
+       }
+      groundNetwork.addNode(taxiNode);
+    }
+  if (name == string("arc")) 
+    {
+      taxiSegment.setIndex(++index);
+      for (int i = 0; i < atts.size() ; i++)
+       {
+         attname = atts.getName(i);
+         if (attname == string("begin"))
+           taxiSegment.setStartNodeRef(atoi(atts.getValue(i)));
+         if (attname == string("end"))
+           taxiSegment.setEndNodeRef(atoi(atts.getValue(i)));
+       }
+      groundNetwork.addSegment(taxiSegment);
+    }
   // sort by radius, in asending order, so that smaller gates are first in the list
-  sort(parkings.begin(), parkings.end());
 }
 
 void  FGAirport::endElement (const char * name) {
@@ -1142,24 +1202,330 @@ void FGAirport::chooseRunwayFallback(string *runway)
   return; // generic fall back goes here
 }
 
+
+
+/**************************************************************************
+ * FGTaxiNode
+ *************************************************************************/
+FGTaxiNode::FGTaxiNode()
+{
+}
+
+/***************************************************************************
+ * FGTaxiSegment
+ **************************************************************************/
+FGTaxiSegment::FGTaxiSegment()
+{
+}
+
+void FGTaxiSegment::setStart(FGTaxiNodeVector *nodes)
+{
+  FGTaxiNodeVectorIterator i = nodes->begin();
+  while (i != nodes->end())
+    {
+      if (i->getIndex() == startNode)
+       {
+         start = i->getAddress();
+         i->addSegment(this);
+         return;
+       }
+      i++;
+    }
+}
+
+void FGTaxiSegment::setEnd(FGTaxiNodeVector *nodes)
+{
+  FGTaxiNodeVectorIterator i = nodes->begin();
+  while (i != nodes->end())
+    {
+      if (i->getIndex() == endNode)
+       {
+         end = i->getAddress();
+         return;
+       }
+      i++;
+    }
+}
+
+// There is probably a computationally cheaper way of 
+// doing this.
+void FGTaxiSegment::setTrackDistance()
+{
+  double course;
+  SGWayPoint first  (start->getLongitude(),
+                    start->getLatitude(),
+                    0);
+  SGWayPoint second (end->getLongitude(),
+                    end->getLatitude(),
+                    0);
+  first.CourseAndDistance(second, &course, &length);
+  
+}
+
+bool FGTaxiRoute::next(int *val) 
+{ 
+  //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
+  //  cerr << "FGTaxiRoute contains : " << *(i) << endl;
+  //cerr << "Offset from end: " << nodes.end() - currNode << endl;
+  //if (currNode != nodes.end())
+  //  cerr << "true" << endl;
+  //else
+  //  cerr << "false" << endl;
+      
+  if (currNode == nodes.end())
+    return false;
+  *val = *(currNode); 
+  currNode++;
+  return true;
+};
+/***************************************************************************
+ * FGGroundNetwork()
+ **************************************************************************/
+
+FGGroundNetwork::FGGroundNetwork()
+{
+  hasNetwork = false;
+}
+
+void FGGroundNetwork::addSegment(FGTaxiSegment seg)
+{
+  segments.push_back(seg);
+}
+
+void FGGroundNetwork::addNode(FGTaxiNode node)
+{
+  nodes.push_back(node);
+}
+
+void FGGroundNetwork::addNodes(FGParkingVec *parkings)
+{
+  FGTaxiNode n;
+  FGParkingVecIterator i = parkings->begin();
+  while (i != parkings->end())
+    {
+      n.setIndex(i->getIndex());
+      n.setLatitude(i->getLatitude());
+      n.setLongitude(i->getLongitude());
+      nodes.push_back(n);
+
+      i++;
+    }
+}
+
+
+
+void FGGroundNetwork::init()
+{
+  hasNetwork = true;
+  FGTaxiSegmentVectorIterator i = segments.begin();
+  while(i != segments.end()) {
+    //cerr << "initializing node " << i->getIndex() << endl;
+    i->setStart(&nodes);
+    i->setEnd  (&nodes);
+    i->setTrackDistance();
+    //cerr << "Track distance = " << i->getLength() << endl;
+    //cerr << "Track ends at"      << i->getEnd()->getIndex() << endl;
+    i++;
+  }
+  //exit(1);
+}
+
+int FGGroundNetwork::findNearestNode(double lat, double lon)
+{
+  double minDist = HUGE;
+  double course, dist;
+  int index;
+  SGWayPoint first  (lon,
+                    lat,
+                    0);
+  
+  for (FGTaxiNodeVectorIterator 
+        itr = nodes.begin();
+       itr != nodes.end(); itr++)
+    {
+      double course;
+      SGWayPoint second (itr->getLongitude(),
+                        itr->getLatitude(),
+                        0);
+      first.CourseAndDistance(second, &course, &dist);
+      if (dist < minDist)
+       {
+         minDist = dist;
+         index = itr->getIndex();
+         //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
+       }
+    }
+  return index;
+}
+
+FGTaxiNode *FGGroundNetwork::findNode(int idx)
+{
+  for (FGTaxiNodeVectorIterator 
+        itr = nodes.begin();
+       itr != nodes.end(); itr++)
+    {
+      if (itr->getIndex() == idx)
+       return itr->getAddress();
+    }
+  return 0;
+}
+
+FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end) 
+{
+  foundRoute = false;
+  totalDistance = 0;
+  FGTaxiNode *firstNode = findNode(start);
+  FGTaxiNode *lastNode  = findNode(end);
+  //prevNode = prevPrevNode = -1;
+  //prevNode = start;
+  routes.clear();
+  traceStack.clear();
+  trace(firstNode, end, 0, 0);
+  FGTaxiRoute empty;
+  
+  if (!foundRoute)
+    {
+      cerr << "Failed to find route from waypoint " << start << " to " << end << endl;
+      exit(1);
+    }
+  sort(routes.begin(), routes.end());
+  //for (intVecIterator i = route.begin(); i != route.end(); i++)
+  //  {
+  //    rte->push_back(*i);
+  //  }
+  
+  if (routes.begin() != routes.end())
+    return *(routes.begin());
+  else
+    return empty;
+}
+
+
+void FGGroundNetwork::trace(FGTaxiNode *currNode, int end, int depth, double distance)
+{
+  traceStack.push_back(currNode->getIndex());
+  totalDistance += distance;
+  //cerr << "Starting trace " << depth << " total distance: " << totalDistance<< endl;
+  //<< currNode->getIndex() << endl;
+
+  // If the current route matches the required end point we found a valid route
+  // So we can add this to the routing table
+  if (currNode->getIndex() == end)
+    {
+      //cerr << "Found route : " <<  totalDistance << "" << " " << *(traceStack.end()-1) << endl;
+      routes.push_back(FGTaxiRoute(traceStack,totalDistance));
+      traceStack.pop_back();
+      if (!(foundRoute))
+       maxDistance = totalDistance;
+      else
+       if (totalDistance < maxDistance)
+         maxDistance = totalDistance;
+      foundRoute = true;
+      totalDistance -= distance;
+      return;
+    }
+
+  // search if the currentNode has been encountered before
+  // if so, we should step back one level, because it is
+  // rather rediculous to proceed further from here. 
+  // if the current node has not been encountered before,
+  // i should point to traceStack.end()-1; and we can continue
+  // if i is not traceStack.end, the previous node was found, 
+  // and we should return. 
+  // This only works at trace levels of 1 or higher though
+  if (depth > 0) {
+    intVecIterator i = traceStack.begin();
+    while ((*i) != currNode->getIndex()) {
+      //cerr << "Route so far : " << (*i) << endl;
+      i++;
+    }
+    if (i != traceStack.end()-1) {
+      traceStack.pop_back();
+      totalDistance -= distance;
+      return;
+    }
+    // If the total distance from start to the current waypoint
+    // is longer than that of a route we can also stop this trace 
+    // and go back one level. 
+    if ((totalDistance > maxDistance) && foundRoute)
+      {
+       //cerr << "Stopping rediculously long trace: " << totalDistance << endl;
+       traceStack.pop_back();
+       totalDistance -= distance;
+       return;
+      }
+  }
+  
+  //cerr << "2" << endl;
+  if (currNode->getBeginRoute() != currNode->getEndRoute())
+    {
+      //cerr << "3" << endl;
+      for (FGTaxiSegmentPointerVectorIterator 
+            i = currNode->getBeginRoute();
+          i != currNode->getEndRoute();
+          i++)
+       {
+         //cerr << (*i)->getLenght() << endl;
+         trace((*i)->getEnd(), end, depth+1, (*i)->getLength());
+       //  {
+       //      // cerr << currNode -> getIndex() << " ";
+       //      route.push_back(currNode->getIndex());
+       //      return true;
+       //    }
+       }
+    }
+  else
+    {
+      cerr << "4" << endl;
+    }
+  traceStack.pop_back();
+  totalDistance -= distance;
+  return;
+}
+
+
+
 /******************************************************************************
  * 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.
+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) {
+        cerr << "Dent: " << dent->d_name; // DEBUG
+        ai_dirs.insert(dent->d_name);
+    }
+    ulCloseDir(d);
+}
+
+
+FGAirportList::~FGAirportList( void ) {
+    for(unsigned int i = 0; i < airports_array.size(); ++i) {
+        delete airports_array[i];
+    }
+}
+
 
 // add an entry to the list
 void FGAirportList::add( const string id, const double longitude,
                          const double latitude, const double elevation,
                          const string name, const bool has_metar )
 {
-  FGRunwayPreference rwyPrefs;
-    FGAirport a(id, longitude, latitude, elevation, name, has_metar);
-    //a._id = id;
-    //a._longitude = longitude;
-    //a._latitude = latitude;
-    //a._elevation = elevation;
-    //a._name = name;
-    //a._has_metar = has_metar;
+    FGRunwayPreference rwyPrefs;
+    FGAirport* a = new FGAirport(id, longitude, latitude, elevation, name, has_metar);
     SGPath parkpath( globals->get_fg_root() );
     parkpath.append( "/Airports/AI/" );
     parkpath.append(id);
@@ -1169,52 +1535,66 @@ void FGAirportList::add( const string id, const double longitude,
     rwyPrefPath.append( "/Airports/AI/" );
     rwyPrefPath.append(id);
     rwyPrefPath.append("rwyuse.xml");
-    if (parkpath.exists()) 
-      {
-       try {
-         readXML(parkpath.str(),a);
-       } 
-       catch  (const sg_exception &e) {
-         //cerr << "unable to read " << parkpath.str() << endl;
-       }
-      }
-    if (rwyPrefPath.exists()) 
-      {
-       try {
-         readXML(rwyPrefPath.str(), rwyPrefs);
-         a.setRwyUse(rwyPrefs);
-       }
-       catch  (const sg_exception &e) {
-         //cerr << "unable to read " << rwyPrefPath.str() << endl;
-         //exit(1);
-       }
-      }
+    if (ai_dirs.find(id.c_str()) != ai_dirs.end()
+        && parkpath.exists()) 
+    {
+        try {
+            readXML(parkpath.str(),*a);
+           a->init();
+        } 
+        catch  (const sg_exception &e) {
+            //cerr << "unable to read " << parkpath.str() << endl;
+        }
+    }
+    if (ai_dirs.find(id.c_str()) != ai_dirs.end()
+        && rwyPrefPath.exists()) 
+    {
+        try {
+            readXML(rwyPrefPath.str(), rwyPrefs);
+            a->setRwyUse(rwyPrefs);
+        }
+        catch  (const sg_exception &e) {
+            //cerr << "unable to read " << rwyPrefPath.str() << endl;
+            //exit(1);
+        }
+    }
     
-    airports_by_id[a.getId()] = a;
+    airports_by_id[a->getId()] = a;
     // try and read in an auxilary file
-   
-    airports_array.push_back( &airports_by_id[a.getId()] );
+    
+    airports_array.push_back( a );
     SG_LOG( SG_GENERAL, SG_BULK, "Adding " << id << " pos = " << longitude
             << ", " << latitude << " elev = " << elevation );
 }
 
 
 // search for the specified id
-FGAirport FGAirportList::search( const string& id) {
-    return airports_by_id[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);
 }
 
-// search for the specified id and return a pointer
-FGAirport* FGAirportList::search( const string& id, FGAirport *result) {
-  FGAirport* retval = airports_by_id[id].getAddress();
-  //cerr << "Returning Airport of string " << id << " results in " << retval->getId();
-  return retval;
+
+// search for first subsequent alphabetically to supplied id
+const FGAirport* FGAirportList::findFirstById( const string& id, bool exact ) {
+    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);
+    }
 }
 
+
 // search for the airport nearest the specified position
-FGAirport FGAirportList::search( double lon_deg, double lat_deg,
+FGAirport* FGAirportList::search( double lon_deg, double lat_deg,
                                  bool with_metar ) {
-    int closest = 0;
+    int closest = -1;
     double min_dist = 360.0;
     unsigned int i;
     for ( i = 0; i < airports_array.size(); ++i ) {
@@ -1229,23 +1609,23 @@ FGAirport FGAirportList::search( double lon_deg, double lat_deg,
         }
     }
 
-    return *airports_array[closest];
+    return ( closest > -1 ? airports_array[closest] : NULL );
 }
 
 
-// Destructor
-FGAirportList::~FGAirportList( void ) {
-}
-
 int
 FGAirportList::size () const
 {
     return airports_array.size();
 }
 
-const FGAirport *FGAirportList::getAirport( int index ) const
+const FGAirport *FGAirportList::getAirport( unsigned int index ) const
 {
-    return airports_array[index];
+    if(index < airports_array.size()) {
+        return(airports_array[index]);
+    } else {
+        return(NULL);
+    }
 }
 
 
@@ -1253,7 +1633,9 @@ const FGAirport *FGAirportList::getAirport( int index ) const
  * Mark the specified airport record as not having metar
  */
 void FGAirportList::no_metar( const string &id ) {
-    airports_by_id[id].setMetar(false);
+    if(airports_by_id.find(id) != airports_by_id.end()) { 
+        airports_by_id[id]->setMetar(false);
+    }
 }
 
 
@@ -1261,5 +1643,7 @@ void FGAirportList::no_metar( const string &id ) {
  * Mark the specified airport record as (yes) having metar
  */
 void FGAirportList::has_metar( const string &id ) {
-    airports_by_id[id].setMetar(true);
+    if(airports_by_id.find(id) != airports_by_id.end()) { 
+        airports_by_id[id]->setMetar(true);
+    }
 }