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