]> git.mxchange.org Git - flightgear.git/blob - src/Airports/simple.cxx
5c74391b486a05dd64dadd6f901084a76eb2bcf1
[flightgear.git] / src / Airports / simple.cxx
1 //
2 // simple.cxx -- a really simplistic class to manage airport ID,
3 //               lat, lon of the center of one of it's runways, and 
4 //               elevation in feet.
5 //
6 // Written by Curtis Olson, started April 1998.
7 // Updated by Durk Talsma, started December, 2004.
8 //
9 // Copyright (C) 1998  Curtis L. Olson  - http://www.flightgear.org/~curt
10 //
11 // This program is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU General Public License as
13 // published by the Free Software Foundation; either version 2 of the
14 // License, or (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful, but
17 // WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 // General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 //
25 // $Id$
26
27 #ifdef HAVE_CONFIG_H
28 #  include <config.h>
29 #endif
30
31 #include <math.h>
32
33 #include <simgear/compiler.h>
34 #include <Environment/environment_mgr.hxx>
35 #include <Environment/environment.hxx>
36 #include <simgear/misc/sg_path.hxx>
37 #include <simgear/props/props.hxx>
38 #include <simgear/structure/subsystem_mgr.hxx>
39 #include <simgear/debug/logstream.hxx>
40 #include <Main/globals.hxx>
41 #include <Main/fg_props.hxx>
42 #include <Airports/runways.hxx>
43
44 #include STL_STRING
45
46 #include "simple.hxx"
47
48 SG_USING_NAMESPACE(std);
49
50 /******************************************************************************
51  * ScheduleTime
52  ***************e*************************************************************/
53 void ScheduleTime::clear()
54
55   start.clear();
56   end.clear();
57   scheduleNames.clear();
58 }
59
60
61 ScheduleTime::ScheduleTime(const ScheduleTime &other) 
62 {
63   //timeVec   start;
64   timeVecConstIterator i;
65   for (i = other.start.begin(); i != other.start.end(); i++)
66     start.push_back(*i);
67    for (i = other.end.begin(); i != other.end.end(); i++)
68     end.push_back(*i);
69    stringVecConstIterator k;
70    for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); k++)
71      scheduleNames.push_back(*k);
72   
73   //timeVec   end;
74   //stringVec scheduleNames;
75   tailWind = other.tailWind;
76   crssWind = other.tailWind;
77 }
78
79
80 ScheduleTime & ScheduleTime::operator= (const ScheduleTime &other) 
81 {
82   //timeVec   start;
83   clear();
84   timeVecConstIterator i;
85   for (i = other.start.begin(); i != other.start.end(); i++)
86     start.push_back(*i);
87    for (i = other.end.begin(); i != other.end.end(); i++)
88     end.push_back(*i);
89    stringVecConstIterator k;
90    for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); k++)
91      scheduleNames.push_back(*k);
92   
93   //timeVec   end;
94   //stringVec scheduleNames;
95   tailWind = other.tailWind;
96   crssWind = other.tailWind;
97   return *this;
98 }
99 string ScheduleTime::getName(time_t dayStart)
100 {
101   if ((start.size() != end.size()) || (start.size() != scheduleNames.size()))
102     {
103       cerr << "Unable to parse schedule times" << endl;
104       exit(1);
105     }
106   else
107     {
108       int nrItems = start.size();
109       //cerr << "Nr of items to process: " << nrItems << endl;
110       if (nrItems > 0)
111         {
112           for (int i = 0; i < start.size(); i++)
113             {
114               //cerr << i << endl;
115               if ((dayStart >= start[i]) && (dayStart <= end[i]))
116                 return scheduleNames[i];
117             }
118         }
119       //couldn't find one so return 0;
120       //cerr << "Returning 0 " << endl;
121       return string(0);
122     }
123 }                             
124 /******************************************************************************
125  * RunwayList
126  *****************************************************************************/
127
128 RunwayList::RunwayList(const RunwayList &other)
129 {
130   type = other.type;
131   stringVecConstIterator i;
132   for (i = other.preferredRunways.begin(); i != other.preferredRunways.end(); i++)
133     preferredRunways.push_back(*i);
134 }
135 RunwayList& RunwayList::operator= (const RunwayList &other)
136 {
137   type = other.type;
138   preferredRunways.clear();
139   stringVecConstIterator i;
140   for (i = other.preferredRunways.begin(); i != other.preferredRunways.end(); i++)
141     preferredRunways.push_back(*i);
142   return *this;
143 }
144 void RunwayList::set(string tp, string lst)
145 {
146   //weekday          = atoi(timeCopy.substr(0,1).c_str());
147   //    timeOffsetInDays = weekday - currTimeDate->getGmt()->tm_wday;
148   //    timeCopy = timeCopy.substr(2,timeCopy.length());
149   type = tp;
150   string rwys = lst;
151   string rwy;
152   while (rwys.find(",") != string::npos)
153     {
154       rwy = rwys.substr(0, rwys.find(",",0));
155       //cerr << "adding runway [" << rwy << "] to the list " << endl;
156       preferredRunways.push_back(rwy);
157       rwys.erase(0, rwys.find(",",0)+1); // erase until after the first whitspace
158       while (rwys[0] == ' ')
159         rwys.erase(0, 1); // Erase any leading whitespaces.
160       //cerr << "Remaining runway list " << rwys;
161     } 
162   preferredRunways.push_back(rwys);
163   //exit(1);
164 }
165
166 void RunwayList::clear() 
167 {
168   type = "";
169   preferredRunways.clear();
170 }
171 /****************************************************************************
172  *
173  ***************************************************************************/
174
175 RunwayGroup::RunwayGroup(const RunwayGroup &other)
176 {
177   name = other.name; 
178   RunwayListVecConstIterator i;
179   for (i = other.rwyList.begin(); i != other.rwyList.end(); i++)
180     rwyList.push_back(*i);
181   choice[0] = other.choice[0];
182   choice[1] = other.choice[1];
183   nrActive = other.nrActive;
184 }
185 RunwayGroup& RunwayGroup:: operator= (const RunwayGroup &other)
186
187   rwyList.clear();
188   name = other.name; 
189   RunwayListVecConstIterator i;
190   for (i = other.rwyList.begin(); i != other.rwyList.end(); i++)
191     rwyList.push_back(*i); 
192   choice[0] = other.choice[0];
193   choice[1] = other.choice[1];
194   nrActive = other.nrActive;
195 }
196
197 void RunwayGroup::setActive(string aptId, 
198                             double windSpeed, 
199                             double windHeading, 
200                             double maxTail, 
201                             double maxCross)
202 {
203
204   FGRunway rwy;
205   int activeRwys = rwyList.size(); // get the number of runways active
206   int nrOfPreferences;
207   bool found = true;
208   double heading;
209   double hdgDiff;
210   double crossWind;
211   double tailWind;
212   string name;
213
214   if (activeRwys > 0)
215     {
216       nrOfPreferences = rwyList[0].getRwyList()->size();
217       for (int i = 0; i < nrOfPreferences; i++)
218         {
219           bool validSelection = true;
220           for (int j = 0; j < activeRwys; j++)
221             {
222               //cerr << "I J " << i << " " << j << endl;
223               name = rwyList[j].getRwyList(i);
224               //cerr << "Name of Runway: " << name << endl;
225               if (globals->get_runways()->search( aptId, 
226                                                   name, 
227                                                   &rwy))
228                 {
229                   //cerr << "Succes" << endl;
230                   hdgDiff = fabs(windHeading - rwy._heading);
231                   //cerr << "Wind Heading: " << windHeading << "Runway Heading: " <<rwy._heading << endl;
232                   //cerr << "Wind Speed  : " << windSpeed << endl;
233                   if (hdgDiff > 180)
234                     hdgDiff = 360 - hdgDiff;
235                   //cerr << "Heading diff: " << hdgDiff << endl;
236                   hdgDiff *= ((2*M_PI)/360.0); // convert to radians
237                   crossWind = windSpeed * sin(hdgDiff);
238                   tailWind  = -windSpeed * cos(hdgDiff);
239                   //cerr << "Tailwind : " << tailWind << endl;
240                   //cerr << "Crosswnd : " << crossWind << endl;
241                   if ((tailWind > maxTail) || (crossWind > maxCross))
242                     validSelection = false;
243                 }else {
244                   cerr << "Failed to find runway " << name << " at " << aptId << endl;
245                   exit(1);
246                 }
247
248             }
249           if (validSelection)
250             {
251               //cerr << "Valid runay selection : " << i << endl;
252               nrActive = activeRwys;
253               active = i;
254               return;
255             }
256         }
257       // If this didn't work, due to heavy winds, try again
258       // but select only one landing and one takeoff runway. 
259       choice[0] = 0;
260       choice[1] = 0;
261       for (int i = activeRwys-1;  i; i--)
262         {
263           if (rwyList[i].getType() == string("landing"))
264             choice[0] = i;
265           if (rwyList[i].getType() == string("takeoff"))
266             choice[1] = i;
267         }
268       //cerr << "Choosing " << choice[0] << " for landing and " << choice[1] << "for takeoff" << endl;
269       nrOfPreferences = rwyList[0].getRwyList()->size();
270       for (int i = 0; i < nrOfPreferences; i++)
271         {
272           bool validSelection = true;
273           for (int j = 0; j < 2; j++)
274             {
275               //cerr << "I J " << i << " " << j << endl;
276               name = rwyList[choice[j]].getRwyList(i);
277               //cerr << "Name of Runway: " << name << endl;
278               if (globals->get_runways()->search( aptId, 
279                                                   name, 
280                                                   &rwy))
281                 {
282                   //cerr << "Succes" << endl;
283                   hdgDiff = fabs(windHeading - rwy._heading);
284                   //cerr << "Wind Heading: " << windHeading << "Runway Heading: " <<rwy._heading << endl;
285                   //cerr << "Wind Speed  : " << windSpeed << endl;
286                   if (hdgDiff > 180)
287                     hdgDiff = 360 - hdgDiff;
288                   //cerr << "Heading diff: " << hdgDiff << endl;
289                   hdgDiff *= ((2*M_PI)/360.0); // convert to radians
290                   crossWind = windSpeed * sin(hdgDiff);
291                   tailWind  = -windSpeed * cos(hdgDiff);
292                   //cerr << "Tailwind : " << tailWind << endl;
293                   //cerr << "Crosswnd : " << crossWind << endl;
294                   if ((tailWind > maxTail) || (crossWind > maxCross))
295                     validSelection = false;
296                 }else {
297                   cerr << "Failed to find runway " << name << " at " << aptId << endl;
298                   exit(1);
299                 }
300
301             }
302           if (validSelection)
303             {
304               //cerr << "Valid runay selection : " << i << endl;
305               active = i;
306               nrActive = 2;
307               return;
308             }
309         }
310     }
311   active = -1;
312   //RunwayListVectorIterator i; // = rwlist.begin();
313   //stringVecIterator j;
314   //for (i = rwyList.begin(); i != rwyList.end(); i++)
315   //  {
316   //    cerr << i->getType();
317   //    for (j = i->getRwyList()->begin(); j != i->getRwyList()->end(); j++)
318   //    {                                 
319   //      cerr << (*j);
320   //    }
321   //    cerr << endl;
322   //  }
323   //for (int
324
325 }
326
327 void RunwayGroup::getActive(int i, string *name, string *type)
328 {
329   if (i == -1)
330     {
331       return;
332     }
333   if (nrActive == rwyList.size())
334     {
335       *name = rwyList[i].getRwyList(active);
336       *type = rwyList[i].getType();
337     }
338   else
339     { 
340       *name = rwyList[choice[i]].getRwyList(active);
341       *type = rwyList[choice[i]].getType();
342     }
343 }
344 /*****************************************************************************
345  * FGRunway preference
346  ****************************************************************************/
347 FGRunwayPreference::FGRunwayPreference()
348 {
349   //cerr << "Running default Constructor" << endl;
350   initialized = false;
351 }
352
353 FGRunwayPreference::FGRunwayPreference(const FGRunwayPreference &other)
354 {
355   initialized = other.initialized;
356   value = other.value;
357   scheduleName = other.scheduleName;
358
359   comTimes = other.comTimes; // Commercial Traffic;
360   genTimes = other.genTimes; // General Aviation;
361   milTimes = other.milTimes; // Military Traffic;
362   currTimes= other.currTimes; // Needed for parsing;
363
364   rwyList = other.rwyList;
365   rwyGroup = other.rwyGroup;
366   PreferenceListConstIterator i;
367   for (i = other.preferences.begin(); i != other.preferences.end(); i++)
368     preferences.push_back(*i);
369 }
370   
371 FGRunwayPreference & FGRunwayPreference::operator= (const FGRunwayPreference &other)
372 {
373   initialized = other.initialized;
374   value = other.value;
375   scheduleName = other.scheduleName;
376   
377   comTimes = other.comTimes; // Commercial Traffic;
378   genTimes = other.genTimes; // General Aviation;
379   milTimes = other.milTimes; // Military Traffic;
380   currTimes= other.currTimes; // Needed for parsing;
381   
382   rwyList = other.rwyList;
383   rwyGroup = other.rwyGroup;
384   PreferenceListConstIterator i;
385   preferences.clear();
386   for (i = other.preferences.begin(); i != other.preferences.end(); i++)
387     preferences.push_back(*i);
388   return *this;
389 }
390
391 ScheduleTime *FGRunwayPreference::getSchedule(const char *trafficType)
392 {
393   if (!(strcmp(trafficType, "com"))) {
394     return &comTimes;
395   }
396   if (!(strcmp(trafficType, "gen"))) {
397     return &genTimes;
398   }
399   if (!(strcmp(trafficType, "mil"))) {
400     return &milTimes;
401   }
402 }
403
404 RunwayGroup *FGRunwayPreference::getGroup(const string groupName)
405 {
406   PreferenceListIterator i = preferences.begin();
407   if (preferences.begin() == preferences.end())
408     return 0;
409   while (!(i == preferences.end() || i->getName() == groupName))
410     i++;
411   if (i != preferences.end())
412     return &(*i);
413   else
414     return 0;
415 }
416
417 void  FGRunwayPreference::startXML () {
418   //  cout << "Start XML" << endl;
419 }
420
421 void  FGRunwayPreference::endXML () {
422   cout << "End XML" << endl;
423 }
424
425 void  FGRunwayPreference::startElement (const char * name, const XMLAttributes &atts) {
426   //cout << "StartElement " << name << endl;
427   value = string("");
428   if (!(strcmp(name, "wind"))) {
429     //cerr << "Will be processing Wind" << endl;
430     for (int i = 0; i < atts.size(); i++)
431       {
432         //cout << "  " << atts.getName(i) << '=' << atts.getValue(i) << endl; 
433         //attname = atts.getName(i);
434         if (atts.getName(i) == string("tail")) {
435           //cerr << "Tail Wind = " << atts.getValue(i) << endl;
436           currTimes.setTailWind(atof(atts.getValue(i)));
437         }       
438         if (atts.getName(i) == string("cross")) {
439           //cerr << "Cross Wind = " << atts.getValue(i) << endl;
440           currTimes.setCrossWind(atof(atts.getValue(i)));
441         }
442      }
443   }
444     if (!(strcmp(name, "time"))) {
445       //cerr << "Will be processing time" << endl;      
446     for (int i = 0; i < atts.size(); i++)
447       {
448         if (atts.getName(i) == string("start")) {
449           //cerr << "Start Time = " << atts.getValue(i) << endl;
450           currTimes.addStartTime(processTime(atts.getValue(i)));
451         }
452         if (atts.getName(i) == string("end")) {
453           //cerr << "End time = " << atts.getValue(i) << endl;
454           currTimes.addEndTime(processTime(atts.getValue(i)));
455         }
456         if (atts.getName(i) == string("schedule")) {
457           //cerr << "Schedule Name  = " << atts.getValue(i) << endl;
458           currTimes.addScheduleName(atts.getValue(i));
459         }       
460     }
461   }
462   if (!(strcmp(name, "takeoff"))) {
463     rwyList.clear();
464   }
465   if  (!(strcmp(name, "landing")))
466     {
467       rwyList.clear();
468     }
469   if (!(strcmp(name, "schedule"))) {
470     for (int i = 0; i < atts.size(); i++)
471       {
472         //cout << "  " << atts.getName(i) << '=' << atts.getValue(i) << endl; 
473         //attname = atts.getName(i);
474         if (atts.getName(i) == string("name")) {
475           //cerr << "Schedule name = " << atts.getValue(i) << endl;
476           scheduleName = atts.getValue(i);
477         }
478       }
479   }
480 }
481
482 //based on a string containing hour and minute, return nr seconds since day start.
483 time_t FGRunwayPreference::processTime(string tme)
484 {
485   string hour   = tme.substr(0, tme.find(":",0));
486   string minute = tme.substr(tme.find(":",0)+1, tme.length());
487
488   //cerr << "hour = " << hour << " Minute = " << minute << endl;
489   return (atoi(hour.c_str()) * 3600 + atoi(minute.c_str()) * 60);
490 }
491
492 void  FGRunwayPreference::endElement (const char * name) {
493   //cout << "End element " << name << endl;
494   if (!(strcmp(name, "rwyuse"))) {
495     initialized = true;
496   }
497   if (!(strcmp(name, "com"))) { // Commercial Traffic
498     //cerr << "Setting time table for commerical traffic" << endl;
499     comTimes = currTimes;
500     currTimes.clear();
501   }
502   if (!(strcmp(name, "gen"))) { // General Aviation
503     //cerr << "Setting time table for general aviation" << endl;
504     genTimes = currTimes;
505     currTimes.clear();
506   }  
507   if (!(strcmp(name, "mil"))) { // Military Traffic
508     //cerr << "Setting time table for military traffic" << endl;
509     genTimes = currTimes;
510     currTimes.clear();
511   }
512
513   if (!(strcmp(name, "takeoff"))) {
514     //cerr << "Adding takeoff: " << value << endl;
515     rwyList.set(name, value);
516     rwyGroup.add(rwyList);
517   }
518   if (!(strcmp(name, "landing"))) {
519     //cerr << "Adding landing: " << value << endl;
520     rwyList.set(name, value);
521     rwyGroup.add(rwyList);
522   }
523   if (!(strcmp(name, "schedule"))) {
524     //cerr << "Adding schedule" << scheduleName << endl;
525     rwyGroup.setName(scheduleName);
526     //rwyGroup.addRunways(rwyList);
527     preferences.push_back(rwyGroup);
528     rwyGroup.clear();
529     //exit(1);
530   }
531 }
532
533 void  FGRunwayPreference::data (const char * s, int len) {
534   string token = string(s,len);
535   //cout << "Character data " << string(s,len) << endl;
536   //if ((token.find(" ") == string::npos && (token.find('\n')) == string::npos))
537   //  value += token;
538   //else
539   //  value = string("");
540   value += token;
541 }
542
543 void  FGRunwayPreference::pi (const char * target, const char * data) {
544   //cout << "Processing instruction " << target << ' ' << data << endl;
545 }
546
547 void  FGRunwayPreference::warning (const char * message, int line, int column) {
548   cout << "Warning: " << message << " (" << line << ',' << column << ')'   
549        << endl;
550 }
551
552 void  FGRunwayPreference::error (const char * message, int line, int column) {
553   cout << "Error: " << message << " (" << line << ',' << column << ')'
554        << endl;
555 }
556
557 /*********************************************************************************
558  * FGParking
559  ********************************************************************************/
560 FGParking::FGParking(double lat,
561                      double lon,
562                      double hdg,
563                      double rad,
564                      int idx,
565                      string name,
566                      string tpe,
567                      string codes)
568 {
569   latitude     = lat;
570   longitude    = lon;
571   heading      = hdg;
572   parkingName  = name;
573   index        = idx;
574   type         = tpe;
575   airlineCodes = codes;
576 }
577
578 double FGParking::processPosition(string pos)
579 {
580   string prefix;
581   string subs;
582   string degree;
583   string decimal;
584   int sign = 1;
585   double value;
586   subs = pos;
587   prefix= subs.substr(0,1);
588   if (prefix == string("S") || (prefix == string("W")))
589     sign = -1;
590   subs    = subs.substr(1, subs.length());
591   degree  = subs.substr(0, subs.find(" ",0));
592   decimal = subs.substr(subs.find(" ",0), subs.length());
593   
594               
595   //cerr << sign << " "<< degree << " " << decimal << endl;
596   value = sign * (atof(degree.c_str()) + atof(decimal.c_str())/60.0);
597   //cerr << value <<endl;
598   //exit(1);
599   return value;
600 }
601
602 /***************************************************************************
603  * FGAirport
604  ***************************************************************************/
605 FGAirport::FGAirport() : _longitude(0), _latitude(0), _elevation(0)
606 {
607   lastUpdate = 0;
608   for (int i = 0; i < 10; i++)
609     {
610       avWindHeading [i] = 0;
611       avWindSpeed   [i] = 0;
612     }
613 }
614
615 FGAirport::FGAirport(const FGAirport& other)
616 {
617   _id = other._id;
618   _longitude = other._longitude;
619   _latitude  = other._latitude;
620   _elevation = other._elevation;
621   _name      = other._name;
622   _has_metar = other._has_metar;
623   for (FGParkingVecConstIterator i= other.parkings.begin(); i != other.parkings.end(); i++)
624     parkings.push_back(*(i));
625   rwyPrefs = other.rwyPrefs;
626   lastUpdate = other.lastUpdate;
627   
628   stringVecConstIterator i;
629   for (i = other.landing.begin(); i != other.landing.end(); i++)
630     landing.push_back(*i);
631   for (i = other.takeoff.begin(); i != other.takeoff.end(); i++)
632     takeoff.push_back(*i);
633   lastUpdate = other.lastUpdate;
634   for (int i = 0; i < 10; i++)
635     {
636       avWindHeading [i] = other.avWindHeading[i];
637       avWindSpeed   [i] = other.avWindSpeed  [i];
638     }
639 }
640
641 FGAirport::FGAirport(string id, double lon, double lat, double elev, string name, bool has_metar)
642 {
643   _id = id;
644   _longitude = lon;
645   _latitude  = lat;
646   _elevation = elev;
647   _name      = name;
648   _has_metar = has_metar; 
649   lastUpdate = 0;
650   for (int i = 0; i < 10; i++)
651     {
652       avWindHeading [i] = 0;
653       avWindSpeed   [i] = 0;
654     }
655  
656 }
657
658 bool FGAirport::getAvailableParking(double *lat, double *lon, double *heading, int *gateId, double rad, string flType, string acType, string airline)
659 {
660   bool found = false;
661   bool available = false;
662   //string gateType;
663
664   FGParkingVecIterator i;
665 //   if (flType == "cargo")
666 //     {
667 //       gateType = "RAMP_CARGO";
668 //     }
669 //   else if (flType == "ga")
670 //     {
671 //       gateType = "RAMP_GA";
672 //     }
673 //   else gateType = "GATE";
674   
675   if (parkings.begin() == parkings.end())
676     {
677       //cerr << "Could not find parking spot at " << _id << endl;
678       *lat = _latitude;
679       *lon = _longitude;
680       *heading = 0;
681       found = true;
682     }
683   else
684     {
685       // First try finding a parking with a designated airline code
686       for (i = parkings.begin(); !(i == parkings.end() || found); i++)
687         {
688           //cerr << "Gate Id: " << i->getIndex()
689           //     << " Type  : " << i->getType()
690           //     << " Codes : " << i->getCodes()
691           //     << " Radius: " << i->getRadius()
692           //     << " Name  : " << i->getName()
693           //     << " Available: " << i->isAvailable() << endl;
694           available = true;
695           // Taken by another aircraft
696           if (!(i->isAvailable()))
697             {
698               available = false;
699               continue;
700             }
701           // No airline codes, so skip
702           if (i->getCodes().empty())
703             {
704               available = false;
705               continue;
706             }
707           else // Airline code doesn't match
708             if (i->getCodes().find(airline, 0) == string::npos)
709               {
710                 available = false;
711                 continue;
712               }
713           // Type doesn't match
714           if (i->getType() != flType)
715             {
716               available = false;
717               continue;
718             }
719           // too small
720           if (i->getRadius() < rad)
721             {
722               available = false;
723               continue;
724             }
725           
726           if (available)
727             {
728               *lat     = i->getLatitude ();
729               *lon     = i->getLongitude();
730               *heading = i->getHeading  ();
731               *gateId  = i->getIndex    ();
732               i->setAvailable(false);
733               found = true;
734             }
735         }
736       // then try again for those without codes. 
737       for (i = parkings.begin(); !(i == parkings.end() || found); i++)
738         {
739           available = true;
740           if (!(i->isAvailable()))
741             {
742               available = false;
743               continue;
744             }
745           if (!(i->getCodes().empty()))
746             {
747               if ((i->getCodes().find(airline,0) == string::npos))
748           {
749             available = false;
750             continue;
751           }
752             }
753           if (i->getType() != flType)
754             {
755               available = false;
756               continue;
757             }
758               
759           if (i->getRadius() < rad)
760             {
761               available = false;
762               continue;
763             }
764           
765           if (available)
766             {
767               *lat     = i->getLatitude ();
768               *lon     = i->getLongitude();
769               *heading = i->getHeading  ();
770               *gateId  = i->getIndex    ();
771               i->setAvailable(false);
772               found = true;
773             }
774         } 
775       // And finally once more if that didn't work. Now ignore the airline codes, as a last resort
776       for (i = parkings.begin(); !(i == parkings.end() || found); i++)
777         {
778           available = true;
779           if (!(i->isAvailable()))
780             {
781               available = false;
782               continue;
783             }
784           if (i->getType() != flType)
785             {
786               available = false;
787               continue;
788             }
789           
790           if (i->getRadius() < rad)
791             {
792               available = false;
793               continue;
794             }
795           
796           if (available)
797             {
798               *lat     = i->getLatitude ();
799               *lon     = i->getLongitude();
800               *heading = i->getHeading  ();
801               *gateId  = i->getIndex    ();
802               i->setAvailable(false);
803               found = true;
804             }
805         }
806     }
807   if (!found)
808     {
809       //cerr << "Traffic overflow at" << _id 
810       //           << ". flType = " << flType 
811       //           << ". airline = " << airline 
812       //           << " Radius = " <<rad
813       //           << endl;
814       *lat = _latitude;
815       *lon = _longitude;
816       *heading = 0;
817       *gateId  = -1;
818       //exit(1);
819     }
820   return found;
821 }
822
823 void FGAirport::getParking (int id, double *lat, double* lon, double *heading)
824 {
825   if (id < 0)
826     {
827       *lat = _latitude;
828       *lon = _longitude;
829       *heading = 0;
830     }
831   else
832     {
833       FGParkingVecIterator i = parkings.begin();
834       for (i = parkings.begin(); i != parkings.end(); i++)
835         {
836           if (id == i->getIndex())
837             {
838               *lat     = i->getLatitude();
839               *lon     = i->getLongitude();
840               *heading = i->getLongitude();
841             }
842         }
843     }
844
845
846 FGParking *FGAirport::getParking(int i) 
847
848   if (i < parkings.size()) 
849     return &(parkings[i]); 
850   else 
851     return 0;
852 }
853 string FGAirport::getParkingName(int i) 
854
855   if (i < parkings.size() && i >= 0) 
856     return (parkings[i].getName()); 
857   else 
858     return string("overflow");
859 }
860 void FGAirport::releaseParking(int id)
861 {
862   if (id >= 0)
863     {
864       
865       FGParkingVecIterator i = parkings.begin();
866       for (i = parkings.begin(); i != parkings.end(); i++)
867         {
868           if (id == i->getIndex())
869             {
870               i -> setAvailable(true);
871             }
872         }
873     }
874 }
875   
876 void  FGAirport::startXML () {
877   //cout << "Start XML" << endl;
878 }
879
880 void  FGAirport::endXML () {
881   //cout << "End XML" << endl;
882 }
883
884 void  FGAirport::startElement (const char * name, const XMLAttributes &atts) {
885   const char * attval;
886   FGParking park;
887   //cout << "Start element " << name << endl;
888   string attname;
889   string value;
890   string gateName;
891   string gateNumber;
892   string lat;
893   string lon;
894   if (name == string("Parking"))
895     {
896       for (int i = 0; i < atts.size(); i++)
897         {
898           //cout << "  " << atts.getName(i) << '=' << atts.getValue(i) << endl; 
899           attname = atts.getName(i);
900           if (attname == string("index"))
901             park.setIndex(atoi(atts.getValue(i)));
902           else if (attname == string("type"))
903             park.setType(atts.getValue(i));
904          else if (attname == string("name"))
905            gateName = atts.getValue(i);
906           else if (attname == string("number"))
907             gateNumber = atts.getValue(i);
908           else if (attname == string("lat"))
909            park.setLatitude(atts.getValue(i));
910           else if (attname == string("lon"))
911             park.setLongitude(atts.getValue(i)); 
912           else if (attname == string("heading"))
913             park.setHeading(atof(atts.getValue(i)));
914           else if (attname == string("radius")) {
915             string radius = atts.getValue(i);
916             if (radius.find("M") != string::npos)
917               radius = radius.substr(0, radius.find("M",0));
918             //cerr << "Radius " << radius <<endl;
919             park.setRadius(atof(radius.c_str()));
920           }
921            else if (attname == string("airlineCodes"))
922              park.setCodes(atts.getValue(i));
923         }
924       park.setName((gateName+gateNumber));
925       parkings.push_back(park);
926     }  
927   // sort by radius, in asending order, so that smaller gates are first in the list
928   sort(parkings.begin(), parkings.end());
929 }
930
931 void  FGAirport::endElement (const char * name) {
932   //cout << "End element " << name << endl;
933
934 }
935
936 void  FGAirport::data (const char * s, int len) {
937   string token = string(s,len);
938   //cout << "Character data " << string(s,len) << endl;
939   //if ((token.find(" ") == string::npos && (token.find('\n')) == string::npos))
940     //value += token;
941   //else
942     //value = string("");
943 }
944
945 void  FGAirport::pi (const char * target, const char * data) {
946   //cout << "Processing instruction " << target << ' ' << data << endl;
947 }
948
949 void  FGAirport::warning (const char * message, int line, int column) {
950   cout << "Warning: " << message << " (" << line << ',' << column << ')'   
951        << endl;
952 }
953
954 void  FGAirport::error (const char * message, int line, int column) {
955   cout << "Error: " << message << " (" << line << ',' << column << ')'
956        << endl;
957 }
958
959 void FGAirport::setRwyUse(FGRunwayPreference& ref)
960 {
961   rwyPrefs = ref;
962   //cerr << "Exiting due to not implemented yet" << endl;
963   //exit(1);
964 }
965 void FGAirport::getActiveRunway(string trafficType, int action, string *runway)
966 {
967   double windSpeed;
968   double windHeading;
969   double maxTail;
970   double maxCross;
971   string name;
972   string type;
973
974   if (!(rwyPrefs.available()))
975     {
976       chooseRunwayFallback(runway);
977       return; // generic fall back goes here
978     }
979   else
980     {
981       RunwayGroup *currRunwayGroup = 0;
982       int nrActiveRunways = 0;
983       time_t dayStart = fgGetLong("/sim/time/utc/day-seconds");
984       if (((dayStart - lastUpdate) > 600) || trafficType != prevTrafficType)
985         {
986           landing.clear();
987           takeoff.clear();
988           //lastUpdate = dayStart;
989           prevTrafficType = trafficType;
990
991           FGEnvironment 
992             stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
993             ->getEnvironment(getLatitude(), 
994                              getLongitude(), 
995                              getElevation());
996           
997           windSpeed = stationweather.get_wind_speed_kt();
998           windHeading = stationweather.get_wind_from_heading_deg();
999           double averageWindSpeed   = 0;
1000           double averageWindHeading = 0;
1001           double cosHeading         = 0;
1002           double sinHeading         = 0;
1003           // Initialize at the beginning of the next day or startup
1004           if ((lastUpdate == 0) || (dayStart < lastUpdate))
1005             {
1006               for (int i = 0; i < 10; i++)
1007                 {
1008                   avWindHeading [i] = windHeading;
1009                   avWindSpeed   [i] = windSpeed;
1010                 }
1011             }
1012           else
1013             {
1014               if (windSpeed != avWindSpeed[9]) // update if new metar data 
1015                 {
1016                   // shift the running average
1017                   for (int i = 0; i < 9 ; i++)
1018                     {
1019                       avWindHeading[i] = avWindHeading[i+1];
1020                       avWindSpeed  [i] = avWindSpeed  [i+1];
1021                     }
1022                 } 
1023               avWindHeading[9] = windHeading;
1024               avWindSpeed  [9] = windSpeed;
1025             }
1026           
1027           for (int i = 0; i < 10; i++)
1028             {
1029               averageWindSpeed   += avWindSpeed   [i];
1030               //averageWindHeading += avWindHeading [i];
1031               cosHeading += cos(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
1032               sinHeading += sin(avWindHeading[i] * SG_DEGREES_TO_RADIANS);
1033             }
1034           averageWindSpeed   /= 10;
1035           //averageWindHeading /= 10;
1036           cosHeading /= 10;
1037           sinHeading /= 10;
1038           averageWindHeading = atan2(sinHeading, cosHeading) *SG_RADIANS_TO_DEGREES;
1039           if (averageWindHeading < 0)
1040             averageWindHeading += 360.0;
1041           //cerr << "Wind Heading " << windHeading << " average " << averageWindHeading << endl;
1042           //cerr << "Wind Speed   " << windSpeed   << " average " << averageWindSpeed   << endl;
1043           lastUpdate = dayStart;
1044               //if (wind_speed == 0) {
1045           //  wind_heading = 270;        This forces West-facing rwys to be used in no-wind situations
1046             // which is consistent with Flightgear's initial setup.
1047           //}
1048           
1049           //string rwy_no = globals->get_runways()->search(apt->getId(), int(wind_heading));
1050           string scheduleName;
1051           //cerr << "finding active Runway for" << _id << endl;
1052           //cerr << "Nr of seconds since day start << " << dayStart << endl;
1053           ScheduleTime *currSched;
1054           //cerr << "A"<< endl;
1055           currSched = rwyPrefs.getSchedule(trafficType.c_str());
1056           if (!(currSched))
1057             return;   
1058           //cerr << "B"<< endl;
1059           scheduleName = currSched->getName(dayStart);
1060           maxTail  = currSched->getTailWind  ();
1061           maxCross = currSched->getCrossWind ();
1062           //cerr << "SChedule anme = " << scheduleName << endl;
1063           if (scheduleName.empty())
1064             return;
1065           //cerr << "C"<< endl;
1066           currRunwayGroup = rwyPrefs.getGroup(scheduleName); 
1067           //cerr << "D"<< endl;
1068           if (!(currRunwayGroup))
1069             return;
1070           nrActiveRunways = currRunwayGroup->getNrActiveRunways();
1071           //cerr << "Nr of Active Runways = " << nrActiveRunways << endl; 
1072           currRunwayGroup->setActive(_id, averageWindSpeed, averageWindHeading, maxTail, maxCross); 
1073           nrActiveRunways = currRunwayGroup->getNrActiveRunways();
1074           for (int i = 0; i < nrActiveRunways; i++)
1075             {
1076               type = "unknown"; // initialize to something other than landing or takeoff
1077               currRunwayGroup->getActive(i, &name, &type);
1078               if (type == "landing")
1079                 {
1080                   landing.push_back(name);
1081                   //cerr << "Landing " << name << endl; 
1082                 }
1083               if (type == "takeoff")
1084                 {
1085                   takeoff.push_back(name);
1086                   //cerr << "takeoff " << name << endl;
1087                 }
1088             }
1089         }
1090       if (action == 1) // takeoff 
1091         {
1092           int nr = takeoff.size();
1093           if (nr)
1094             {
1095               *runway = takeoff[(rand() %  nr)];
1096             }
1097           else
1098             { // Fallback
1099               chooseRunwayFallback(runway);
1100             }
1101         } 
1102       if (action == 2) // landing
1103         {
1104           int nr = landing.size();
1105           if (nr)
1106             {
1107               *runway = landing[(rand() % nr)];
1108             }
1109           else
1110             {  //fallback
1111                chooseRunwayFallback(runway);
1112             }
1113         }
1114       
1115       //*runway = globals->get_runways()->search(_id, int(windHeading));
1116       //cerr << "Seleceted runway: " << *runway << endl;
1117     }
1118 }
1119
1120 void FGAirport::chooseRunwayFallback(string *runway)
1121 {   
1122   FGEnvironment 
1123     stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
1124     ->getEnvironment(getLatitude(), 
1125                      getLongitude(),
1126                      getElevation());
1127   
1128   double windSpeed = stationweather.get_wind_speed_kt();
1129   double windHeading = stationweather.get_wind_from_heading_deg();
1130   if (windSpeed == 0) {
1131     windHeading = 270;  // This forces West-facing rwys to be used in no-wind situations
1132     //which is consistent with Flightgear's initial setup.
1133   }
1134   
1135   *runway = globals->get_runways()->search(_id, int(windHeading));
1136   return; // generic fall back goes here
1137 }
1138
1139 /******************************************************************************
1140  * FGAirportList
1141  *****************************************************************************/
1142
1143
1144 // add an entry to the list
1145 void FGAirportList::add( const string id, const double longitude,
1146                          const double latitude, const double elevation,
1147                          const string name, const bool has_metar )
1148 {
1149   FGRunwayPreference rwyPrefs;
1150     FGAirport a(id, longitude, latitude, elevation, name, has_metar);
1151     //a._id = id;
1152     //a._longitude = longitude;
1153     //a._latitude = latitude;
1154     //a._elevation = elevation;
1155     //a._name = name;
1156     //a._has_metar = has_metar;
1157     SGPath parkpath( globals->get_fg_root() );
1158     parkpath.append( "/Airports/AI/" );
1159     parkpath.append(id);
1160     parkpath.append("parking.xml"); 
1161     
1162     SGPath rwyPrefPath( globals->get_fg_root() );
1163     rwyPrefPath.append( "/Airports/AI/" );
1164     rwyPrefPath.append(id);
1165     rwyPrefPath.append("rwyuse.xml");
1166     if (parkpath.exists()) 
1167       {
1168         try {
1169           readXML(parkpath.str(),a);
1170         } 
1171         catch  (const sg_exception &e) {
1172           //cerr << "unable to read " << parkpath.str() << endl;
1173         }
1174       }
1175     if (rwyPrefPath.exists()) 
1176       {
1177         try {
1178           readXML(rwyPrefPath.str(), rwyPrefs);
1179           a.setRwyUse(rwyPrefs);
1180         }
1181         catch  (const sg_exception &e) {
1182           //cerr << "unable to read " << rwyPrefPath.str() << endl;
1183           //exit(1);
1184         }
1185       }
1186     
1187     airports_by_id[a.getId()] = a;
1188     // try and read in an auxilary file
1189    
1190     airports_array.push_back( &airports_by_id[a.getId()] );
1191     SG_LOG( SG_GENERAL, SG_BULK, "Adding " << id << " pos = " << longitude
1192             << ", " << latitude << " elev = " << elevation );
1193 }
1194
1195
1196 // search for the specified id
1197 FGAirport FGAirportList::search( const string& id) {
1198     return airports_by_id[id];
1199 }
1200
1201 // search for the specified id and return a pointer
1202 FGAirport* FGAirportList::search( const string& id, FGAirport *result) {
1203   FGAirport* retval = airports_by_id[id].getAddress();
1204   //cerr << "Returning Airport of string " << id << " results in " << retval->getId();
1205   return retval;
1206 }
1207
1208 // search for the airport nearest the specified position
1209 FGAirport FGAirportList::search( double lon_deg, double lat_deg,
1210                                  bool with_metar ) {
1211     int closest = 0;
1212     double min_dist = 360.0;
1213     unsigned int i;
1214     for ( i = 0; i < airports_array.size(); ++i ) {
1215         // crude manhatten distance based on lat/lon difference
1216         double d = fabs(lon_deg - airports_array[i]->getLongitude())
1217             + fabs(lat_deg - airports_array[i]->getLatitude());
1218         if ( d < min_dist ) {
1219             if ( !with_metar || (with_metar&&airports_array[i]->getMetar()) ) {
1220                 closest = i;
1221                 min_dist = d;
1222             }
1223         }
1224     }
1225
1226     return *airports_array[closest];
1227 }
1228
1229
1230 // Destructor
1231 FGAirportList::~FGAirportList( void ) {
1232 }
1233
1234 int
1235 FGAirportList::size () const
1236 {
1237     return airports_array.size();
1238 }
1239
1240 const FGAirport *FGAirportList::getAirport( int index ) const
1241 {
1242     return airports_array[index];
1243 }
1244
1245
1246 /**
1247  * Mark the specified airport record as not having metar
1248  */
1249 void FGAirportList::no_metar( const string &id ) {
1250     airports_by_id[id].setMetar(false);
1251 }
1252
1253
1254 /**
1255  * Mark the specified airport record as (yes) having metar
1256  */
1257 void FGAirportList::has_metar( const string &id ) {
1258     airports_by_id[id].setMetar(true);
1259 }