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