]> git.mxchange.org Git - flightgear.git/blob - src/Airports/simple.cxx
Allow airports system to function without an environment manager - useful in some...
[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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
24 //
25 // $Id$
26
27 #ifdef HAVE_CONFIG_H
28 #  include <config.h>
29 #endif
30
31 #include "simple.hxx"
32
33 #include <simgear/misc/sg_path.hxx>
34 #include <simgear/props/props.hxx>
35 #include <simgear/props/props_io.hxx>
36 #include <simgear/debug/logstream.hxx>
37 #include <simgear/sg_inlines.h>
38
39 #include <Environment/environment_mgr.hxx>
40 #include <Environment/environment.hxx>
41 #include <Main/fg_props.hxx>
42 #include <Airports/runways.hxx>
43 #include <Airports/pavement.hxx>
44 #include <Airports/dynamics.hxx>
45 #include <Airports/xmlloader.hxx>
46
47 // magic import of a helper which uses FGPositioned internals
48 extern char** searchAirportNamesAndIdents(const std::string& aFilter);
49
50 /***************************************************************************
51  * FGAirport
52  ***************************************************************************/
53
54 FGAirport::FGAirport(const string &id, const SGGeod& location, const SGGeod& tower_location,
55         const string &name, bool has_metar, Type aType) :
56     FGPositioned(aType, id, location),
57     _tower_location(tower_location),
58     _name(name),
59     _has_metar(has_metar),
60     _dynamics(0),
61     mRunwaysLoaded(false),
62     mTaxiwaysLoaded(true)
63 {
64 }
65
66
67 FGAirport::~FGAirport()
68 {
69     delete _dynamics;
70 }
71
72 bool FGAirport::isAirport() const
73 {
74   return type() == AIRPORT;
75 }
76
77 bool FGAirport::isSeaport() const
78 {
79   return type() == SEAPORT;
80 }
81
82 bool FGAirport::isHeliport() const
83 {
84   return type() == HELIPORT;
85 }
86
87 FGAirportDynamics * FGAirport::getDynamics()
88 {
89     if (_dynamics != 0) {
90         return _dynamics;
91     } else {
92         //cerr << "Trying to load dynamics for " << _id << endl;
93         _dynamics = new FGAirportDynamics(this);
94         XMLLoader::load(_dynamics);
95
96         FGRunwayPreference rwyPrefs(this);
97         XMLLoader::load(&rwyPrefs);
98         _dynamics->setRwyUse(rwyPrefs);
99
100         //FGSidStar SIDs(this);
101         XMLLoader::load(_dynamics->getSIDs());
102    }
103     return _dynamics;
104 }
105
106 unsigned int FGAirport::numRunways() const
107 {
108   loadRunways();
109   return mRunways.size();
110 }
111
112 FGRunway* FGAirport::getRunwayByIndex(unsigned int aIndex) const
113 {
114   loadRunways();
115   
116   assert(aIndex >= 0 && aIndex < mRunways.size());
117   return mRunways[aIndex];
118 }
119
120 bool FGAirport::hasRunwayWithIdent(const string& aIdent) const
121 {
122   return (getIteratorForRunwayIdent(aIdent) != mRunways.end());
123 }
124
125 FGRunway* FGAirport::getRunwayByIdent(const string& aIdent) const
126 {
127   Runway_iterator it = getIteratorForRunwayIdent(aIdent);
128   if (it == mRunways.end()) {
129     SG_LOG(SG_GENERAL, SG_ALERT, "no such runway '" << aIdent << "' at airport " << ident());
130     throw sg_range_exception("unknown runway " + aIdent + " at airport:" + ident(), "FGAirport::getRunwayByIdent");
131   }
132   
133   return *it;
134 }
135
136 FGAirport::Runway_iterator
137 FGAirport::getIteratorForRunwayIdent(const string& aIdent) const
138
139   loadRunways();
140   
141   string ident(aIdent);
142   if ((aIdent.size() == 1) || !isdigit(aIdent[1])) {
143     ident = "0" + aIdent;
144   }
145
146   Runway_iterator it = mRunways.begin();
147   for (; it != mRunways.end(); ++it) {
148     if ((*it)->ident() == ident) {
149       return it;
150     }
151   }
152
153   return it; // end()
154 }
155
156 FGRunway* FGAirport::findBestRunwayForHeading(double aHeading) const
157 {
158   loadRunways();
159   
160   Runway_iterator it = mRunways.begin();
161   FGRunway* result = NULL;
162   double currentBestQuality = 0.0;
163   
164   SGPropertyNode *param = fgGetNode("/sim/airport/runways/search", true);
165   double lengthWeight = param->getDoubleValue("length-weight", 0.01);
166   double widthWeight = param->getDoubleValue("width-weight", 0.01);
167   double surfaceWeight = param->getDoubleValue("surface-weight", 10);
168   double deviationWeight = param->getDoubleValue("deviation-weight", 1);
169     
170   for (; it != mRunways.end(); ++it) {
171     double good = (*it)->score(lengthWeight, widthWeight, surfaceWeight);
172     
173     double dev = aHeading - (*it)->headingDeg();
174     SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
175     double bad = fabs(deviationWeight * dev) + 1e-20;
176     double quality = good / bad;
177     
178     if (quality > currentBestQuality) {
179       currentBestQuality = quality;
180       result = *it;
181     }
182   }
183
184   return result;
185 }
186
187 bool FGAirport::hasHardRunwayOfLengthFt(double aLengthFt) const
188 {
189   loadRunways();
190   
191   unsigned int numRunways(mRunways.size());
192   for (unsigned int r=0; r<numRunways; ++r) {
193     FGRunway* rwy = mRunways[r];
194     if (rwy->isReciprocal()) {
195       continue; // we only care about lengths, so don't do work twice
196     }
197
198     if (rwy->isHardSurface() && (rwy->lengthFt() >= aLengthFt)) {
199       return true; // we're done!
200     }
201   } // of runways iteration
202
203   return false;
204 }
205
206 unsigned int FGAirport::numTaxiways() const
207 {
208   loadTaxiways();
209   return mTaxiways.size();
210 }
211
212 FGTaxiway* FGAirport::getTaxiwayByIndex(unsigned int aIndex) const
213 {
214   loadTaxiways();
215   assert(aIndex >= 0 && aIndex < mTaxiways.size());
216   return mTaxiways[aIndex];
217 }
218
219 unsigned int FGAirport::numPavements() const
220 {
221   loadTaxiways();
222   return mPavements.size();
223 }
224
225 FGPavement* FGAirport::getPavementByIndex(unsigned int aIndex) const
226 {
227   loadTaxiways();
228   assert(aIndex >= 0 && aIndex < mPavements.size());
229   return mPavements[aIndex];
230 }
231
232 void FGAirport::setRunwaysAndTaxiways(vector<FGRunwayPtr>& rwys,
233        vector<FGTaxiwayPtr>& txwys,
234        vector<FGPavementPtr>& pvts)
235 {
236   mRunways.swap(rwys);
237   Runway_iterator it = mRunways.begin();
238   for (; it != mRunways.end(); ++it) {
239     (*it)->setAirport(this);
240   }
241
242   mTaxiways.swap(txwys);
243   mPavements.swap(pvts);
244 }
245
246 FGRunway* FGAirport::getActiveRunwayForUsage() const
247 {
248   static FGEnvironmentMgr* envMgr = NULL;
249   if (!envMgr) {
250     envMgr = (FGEnvironmentMgr *) globals->get_subsystem("environment");
251   }
252   
253   // This forces West-facing rwys to be used in no-wind situations
254   // which is consistent with Flightgear's initial setup.
255   double hdg = 270;
256   
257   if (envMgr) {
258     FGEnvironment stationWeather(envMgr->getEnvironment(mPosition));
259   
260     double windSpeed = stationWeather.get_wind_speed_kt();
261     if (windSpeed > 0.0) {
262       hdg = stationWeather.get_wind_from_heading_deg();
263     }
264   }
265   
266   return findBestRunwayForHeading(hdg);
267 }
268
269 FGAirport* FGAirport::findClosest(const SGGeod& aPos, double aCuttofNm, Filter* filter)
270 {
271   AirportFilter aptFilter;
272   if (filter == NULL) {
273     filter = &aptFilter;
274   }
275   
276   FGPositionedRef r = FGPositioned::findClosest(aPos, aCuttofNm, filter);
277   if (!r) {
278     return NULL;
279   }
280   
281   return static_cast<FGAirport*>(r.ptr());
282 }
283
284 FGAirport::HardSurfaceFilter::HardSurfaceFilter(double minLengthFt) :
285   mMinLengthFt(minLengthFt)
286 {
287 }
288       
289 bool FGAirport::HardSurfaceFilter::passAirport(FGAirport* aApt) const
290 {
291   return aApt->hasHardRunwayOfLengthFt(mMinLengthFt);
292 }
293
294 FGAirport* FGAirport::findByIdent(const std::string& aIdent)
295 {
296   FGPositionedRef r;
297   AirportFilter filter;
298   r = FGPositioned::findNextWithPartialId(r, aIdent, &filter);
299   if (!r) {
300     return NULL; // we don't warn here, let the caller do that
301   }
302   return static_cast<FGAirport*>(r.ptr());
303 }
304
305 FGAirport* FGAirport::getByIdent(const std::string& aIdent)
306 {
307   FGPositionedRef r;
308   AirportFilter filter;
309   r = FGPositioned::findNextWithPartialId(r, aIdent, &filter);
310   if (!r) {
311     throw sg_range_exception("No such airport with ident: " + aIdent);
312   }
313   return static_cast<FGAirport*>(r.ptr());
314 }
315
316 char** FGAirport::searchNamesAndIdents(const std::string& aFilter)
317 {
318   // we delegate all the work to a horrible helper in FGPositioned, which can
319   // access the (private) index data.
320   return searchAirportNamesAndIdents(aFilter);
321 }
322
323 // find basic airport location info from airport database
324 const FGAirport *fgFindAirportID( const string& id)
325 {
326     if ( id.empty() ) {
327         return NULL;
328     }
329     
330     return FGAirport::findByIdent(id);
331 }
332
333 void FGAirport::loadRunways() const
334 {
335   if (mRunwaysLoaded) {
336     return; // already loaded, great
337   }
338   
339   mRunwaysLoaded = true;
340   loadSceneryDefintions();
341 }
342
343 void FGAirport::loadTaxiways() const
344 {
345   if (mTaxiwaysLoaded) {
346     return; // already loaded, great
347   }
348 }
349
350 void FGAirport::loadSceneryDefintions() const
351 {  
352   // allow users to disable the scenery data in the short-term
353   // longer term, this option can probably disappear
354   if (!fgGetBool("/sim/use-scenery-airport-data")) {
355     return; 
356   }
357   
358   SGPath path;
359   SGPropertyNode_ptr rootNode = new SGPropertyNode;
360   if (XMLLoader::findAirportData(ident(), "threshold", path)) {
361     readProperties(path.str(), rootNode);
362     const_cast<FGAirport*>(this)->readThresholdData(rootNode);
363   }
364   
365   // repeat for the tower data
366   rootNode = new SGPropertyNode;
367   if (XMLLoader::findAirportData(ident(), "twr", path)) {
368     readProperties(path.str(), rootNode);
369     const_cast<FGAirport*>(this)->readTowerData(rootNode);
370   }
371 }
372
373 void FGAirport::readThresholdData(SGPropertyNode* aRoot)
374 {
375   SGPropertyNode* runway;
376   int runwayIndex = 0;
377   for (; (runway = aRoot->getChild("runway", runwayIndex)) != NULL; ++runwayIndex) {
378     SGPropertyNode* t0 = runway->getChild("threshold", 0),
379       *t1 = runway->getChild("threshold", 1);
380     assert(t0);
381     assert(t1); // too strict? mayeb we should finally allow single-ended runways
382     
383     processThreshold(t0);
384     processThreshold(t1);
385   } // of runways iteration
386 }
387
388 void FGAirport::processThreshold(SGPropertyNode* aThreshold)
389 {
390   // first, let's identify the current runway
391   string id(aThreshold->getStringValue("rwy"));
392   if (!hasRunwayWithIdent(id)) {
393     SG_LOG(SG_GENERAL, SG_WARN, "FGAirport::processThreshold: "
394       "found runway not defined in the global data:" << ident() << "/" << id);
395     return;
396   }
397   
398   FGRunway* rwy = getRunwayByIdent(id);
399   rwy->processThreshold(aThreshold);
400 }
401
402 void FGAirport::readTowerData(SGPropertyNode* aRoot)
403 {
404   SGPropertyNode* twrNode = aRoot->getChild("tower")->getChild("twr");
405   double lat = twrNode->getDoubleValue("lat"), 
406     lon = twrNode->getDoubleValue("lon"), 
407     elevM = twrNode->getDoubleValue("elev-m");
408     
409   _tower_location = SGGeod::fromDegM(lon, lat, elevM);
410 }
411
412 // get airport elevation
413 double fgGetAirportElev( const string& id )
414 {
415     const FGAirport *a=fgFindAirportID( id);
416     if (a) {
417         return a->getElevation();
418     } else {
419         return -9999.0;
420     }
421 }
422
423
424 // get airport position
425 SGGeod fgGetAirportPos( const string& id )
426 {
427     const FGAirport *a = fgFindAirportID( id);
428
429     if (a) {
430         return SGGeod::fromDegM(a->getLongitude(), a->getLatitude(), a->getElevation());
431     } else {
432         return SGGeod::fromDegM(0.0, 0.0, -9999.0);
433     }
434 }