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