]> git.mxchange.org Git - flightgear.git/blob - src/Airports/simple.cxx
Bugfix - don't exclude seaports or heliports when looking up by ICAO.
[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   FGEnvironment stationWeather(envMgr->getEnvironment(mPosition));
254   
255   double windSpeed = stationWeather.get_wind_speed_kt();
256   double hdg = stationWeather.get_wind_from_heading_deg();
257   if (windSpeed <= 0.0) {
258     hdg = 270;  // This forces West-facing rwys to be used in no-wind situations
259     // which is consistent with Flightgear's initial setup.
260   }
261   
262   return findBestRunwayForHeading(hdg);
263 }
264
265 FGAirport* FGAirport::findClosest(const SGGeod& aPos, double aCuttofNm, Filter* filter)
266 {
267   AirportFilter aptFilter;
268   if (filter == NULL) {
269     filter = &aptFilter;
270   }
271   
272   FGPositionedRef r = FGPositioned::findClosest(aPos, aCuttofNm, filter);
273   if (!r) {
274     return NULL;
275   }
276   
277   return static_cast<FGAirport*>(r.ptr());
278 }
279
280 FGAirport::HardSurfaceFilter::HardSurfaceFilter(double minLengthFt) :
281   mMinLengthFt(minLengthFt)
282 {
283 }
284       
285 bool FGAirport::HardSurfaceFilter::passAirport(FGAirport* aApt) const
286 {
287   return aApt->hasHardRunwayOfLengthFt(mMinLengthFt);
288 }
289
290 FGAirport* FGAirport::findByIdent(const std::string& aIdent)
291 {
292   FGPositionedRef r;
293   PortsFilter filter;
294   r = FGPositioned::findNextWithPartialId(r, aIdent, &filter);
295   if (!r) {
296     return NULL; // we don't warn here, let the caller do that
297   }
298   return static_cast<FGAirport*>(r.ptr());
299 }
300
301 FGAirport* FGAirport::getByIdent(const std::string& aIdent)
302 {
303   FGPositionedRef r;
304   PortsFilter filter;
305   r = FGPositioned::findNextWithPartialId(r, aIdent, &filter);
306   if (!r) {
307     throw sg_range_exception("No such airport with ident: " + aIdent);
308   }
309   return static_cast<FGAirport*>(r.ptr());
310 }
311
312 char** FGAirport::searchNamesAndIdents(const std::string& aFilter)
313 {
314   // we delegate all the work to a horrible helper in FGPositioned, which can
315   // access the (private) index data.
316   return searchAirportNamesAndIdents(aFilter);
317 }
318
319 // find basic airport location info from airport database
320 const FGAirport *fgFindAirportID( const string& id)
321 {
322     if ( id.empty() ) {
323         return NULL;
324     }
325     
326     return FGAirport::findByIdent(id);
327 }
328
329 void FGAirport::loadRunways() const
330 {
331   if (mRunwaysLoaded) {
332     return; // already loaded, great
333   }
334   
335   mRunwaysLoaded = true;
336   loadSceneryDefintions();
337 }
338
339 void FGAirport::loadTaxiways() const
340 {
341   if (mTaxiwaysLoaded) {
342     return; // already loaded, great
343   }
344 }
345
346 void FGAirport::loadSceneryDefintions() const
347 {  
348   // allow users to disable the scenery data in the short-term
349   // longer term, this option can probably disappear
350   if (!fgGetBool("/sim/use-scenery-airport-data")) {
351     return; 
352   }
353   
354   SGPath path;
355   SGPropertyNode_ptr rootNode = new SGPropertyNode;
356   if (XMLLoader::findAirportData(ident(), "threshold", path)) {
357     readProperties(path.str(), rootNode);
358     const_cast<FGAirport*>(this)->readThresholdData(rootNode);
359   }
360   
361   // repeat for the tower data
362   rootNode = new SGPropertyNode;
363   if (XMLLoader::findAirportData(ident(), "twr", path)) {
364     readProperties(path.str(), rootNode);
365     const_cast<FGAirport*>(this)->readTowerData(rootNode);
366   }
367 }
368
369 void FGAirport::readThresholdData(SGPropertyNode* aRoot)
370 {
371   SGPropertyNode* runway;
372   int runwayIndex = 0;
373   for (; (runway = aRoot->getChild("runway", runwayIndex)) != NULL; ++runwayIndex) {
374     SGPropertyNode* t0 = runway->getChild("threshold", 0),
375       *t1 = runway->getChild("threshold", 1);
376     assert(t0);
377     assert(t1); // too strict? mayeb we should finally allow single-ended runways
378     
379     processThreshold(t0);
380     processThreshold(t1);
381   } // of runways iteration
382 }
383
384 void FGAirport::processThreshold(SGPropertyNode* aThreshold)
385 {
386   // first, let's identify the current runway
387   string id(aThreshold->getStringValue("rwy"));
388   if (!hasRunwayWithIdent(id)) {
389     SG_LOG(SG_GENERAL, SG_WARN, "FGAirport::processThreshold: "
390       "found runway not defined in the global data:" << ident() << "/" << id);
391     return;
392   }
393   
394   FGRunway* rwy = getRunwayByIdent(id);
395   rwy->processThreshold(aThreshold);
396 }
397
398 void FGAirport::readTowerData(SGPropertyNode* aRoot)
399 {
400   SGPropertyNode* twrNode = aRoot->getChild("tower")->getChild("twr");
401   double lat = twrNode->getDoubleValue("lat"), 
402     lon = twrNode->getDoubleValue("lon"), 
403     elevM = twrNode->getDoubleValue("elev-m");
404     
405   _tower_location = SGGeod::fromDegM(lon, lat, elevM);
406 }
407
408 // get airport elevation
409 double fgGetAirportElev( const string& id )
410 {
411     const FGAirport *a=fgFindAirportID( id);
412     if (a) {
413         return a->getElevation();
414     } else {
415         return -9999.0;
416     }
417 }
418
419
420 // get airport position
421 SGGeod fgGetAirportPos( const string& id )
422 {
423     const FGAirport *a = fgFindAirportID( id);
424
425     if (a) {
426         return SGGeod::fromDegM(a->getLongitude(), a->getLatitude(), a->getElevation());
427     } else {
428         return SGGeod::fromDegM(0.0, 0.0, -9999.0);
429     }
430 }