FlightGear next
airport.cxx
Go to the documentation of this file.
1/*
2 * SPDX-FileName: airport.cxx
3 * SPDX-FileComment: Classes representing airports, seaports and helipads
4 * SPDX-FileCopyrightText: Copyright (C) 1998 Curtis L. Olson - http://www.flightgear.org/~curt
5 * SPDX-FileContributor: Updated by Durk Talsma, started December, 2004
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9#include <config.h>
10
11#include "airport.hxx"
12
13#include <algorithm>
14#include <cassert>
15#include <utility>
16
17#include <simgear/debug/ErrorReportingCallback.hxx>
18#include <simgear/debug/logstream.hxx>
19#include <simgear/misc/sg_path.hxx>
20#include <simgear/props/props.hxx>
21#include <simgear/props/props_io.hxx>
22#include <simgear/sg_inlines.h>
23#include <simgear/structure/exception.hxx>
24
27#include <Main/fg_props.hxx>
28#include <Airports/runways.hxx>
29#include <Airports/pavement.hxx>
31#include <Airports/dynamics.hxx>
33#include <Navaids/procedure.hxx>
34#include <Navaids/waypoint.hxx>
35#include <ATC/CommStation.hxx>
37#include <Navaids/navrecord.hxx>
41
42using std::vector;
43using std::pair;
44
45using namespace flightgear;
46
47/***************************************************************************
48 * FGAirport
49 ***************************************************************************/
50
51AirportCache FGAirport::airportCache;
52
54 const std::string &id,
55 const SGGeod& location,
56 const std::string &name,
57 bool has_metar,
58 Type aType,
59 SGPath sceneryPath):
60 FGPositioned(aGuid, aType, id, location),
61 _name(name),
62 _has_metar(has_metar),
63 _sceneryPath(std::move(sceneryPath)),
64 mTowerDataLoaded(false),
65 mHasTower(false),
66 mRunwaysLoaded(false),
67 mHelipadsLoaded(false),
68 mTaxiwaysLoaded(false),
69 mProceduresLoaded(false),
70 mThresholdDataLoaded(false),
71 mILSDataLoaded(false)
72{
73 mIsClosed = (name.find("[x]") != std::string::npos);
74}
75
76
80
82{
83 return type() == AIRPORT;
84}
85
87{
88 return type() == SEAPORT;
89}
90
92{
93 return type() == HELIPORT;
94}
95
97{
98 return _sceneryPath;
99}
100
101//------------------------------------------------------------------------------
102unsigned int FGAirport::numRunways() const
103{
104 loadRunways();
105 return mRunways.size();
106}
107
108//------------------------------------------------------------------------------
109unsigned int FGAirport::numHelipads() const
110{
111 loadHelipads();
112 return mHelipads.size();
113}
114
115//------------------------------------------------------------------------------
116FGRunwayRef FGAirport::getRunwayByIndex(unsigned int aIndex) const
117{
118 loadRunways();
119 return mRunways.at(aIndex);
120}
121
122//------------------------------------------------------------------------------
124{
125 loadHelipads();
126 return loadById<FGHelipad>(mHelipads, aIndex);
127}
128
129//------------------------------------------------------------------------------
131{
132 loadRunways();
133 FGRunwayMap map;
134
135 double minLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft");
136
137 for (auto rwy : mRunways) {
138 // ignore unusably short runways
139 // TODO: other methods don't check this...
140 if( rwy->lengthFt() >= minLengthFt )
141 map[ rwy->ident() ] = rwy;
142 }
143
144 return map;
145}
146
147//------------------------------------------------------------------------------
149{
150 loadHelipads();
151 FGHelipadMap map;
152
153 for (auto id : mHelipads) {
155 map[ rwy->ident() ] = rwy;
156 }
157
158 return map;
159}
160
161//------------------------------------------------------------------------------
162bool FGAirport::hasRunwayWithIdent(const std::string& aIdent) const
163{
164 loadRunways();
165 for (auto rwy : mRunways) {
166 if (rwy->ident() == aIdent) {
167 return true;
168 }
169 }
170
171 return false;
172}
173
174//------------------------------------------------------------------------------
175bool FGAirport::hasHelipadWithIdent(const std::string& aIdent) const
176{
179}
180
181//------------------------------------------------------------------------------
182FGRunwayRef FGAirport::getRunwayByIdent(const std::string& aIdent) const
183{
184 if (aIdent.empty())
185 return {};
186
187 loadRunways();
188 for (auto rwy : mRunways) {
189 if (rwy->ident() == aIdent) {
190 return rwy;
191 }
192 }
193
194 SG_LOG(SG_GENERAL, SG_ALERT, "no such runway '" << aIdent << "' at airport " << ident());
195 throw sg_range_exception("unknown runway " + aIdent + " at airport:" + ident(), "FGAirport::getRunwayByIdent");
196}
197
198//------------------------------------------------------------------------------
199FGHelipadRef FGAirport::getHelipadByIdent(const std::string& aIdent) const
200{
202 if (id == 0) {
203 SG_LOG(SG_GENERAL, SG_ALERT, "no such helipad '" << aIdent << "' at airport " << ident());
204 throw sg_range_exception("unknown helipad " + aIdent + " at airport:" + ident(), "FGAirport::getRunwayByIdent");
205 }
206
207 return loadById<FGHelipad>(id);
208}
209
210//------------------------------------------------------------------------------
212{
213 //FIXME must allign with FGAirportDynamics::getActiveRunway
214 loadRunways();
215
216 FGRunway* result = NULL;
217 double currentBestQuality = 0.0;
218
219 struct FindBestRunwayForHeadingParams fbrfhp;
220 if( NULL != parms ) fbrfhp = *parms;
221
222 SGPropertyNode_ptr searchNode = fgGetNode("/sim/airport/runways/search");
223 if( searchNode.valid() ) {
224 fbrfhp.lengthWeight = searchNode->getDoubleValue("length-weight", fbrfhp.lengthWeight );
225 fbrfhp.widthWeight = searchNode->getDoubleValue("width-weight", fbrfhp.widthWeight );
226 fbrfhp.surfaceWeight = searchNode->getDoubleValue("surface-weight", fbrfhp.surfaceWeight );
227 fbrfhp.deviationWeight = searchNode->getDoubleValue("deviation-weight", fbrfhp.deviationWeight );
228 fbrfhp.ilsWeight = searchNode->getDoubleValue("ils-weight", fbrfhp.ilsWeight );
229 }
230
231 for (auto rwy : mRunways) {
232 double good = rwy->score( fbrfhp.lengthWeight, fbrfhp.widthWeight, fbrfhp.surfaceWeight, fbrfhp.ilsWeight );
233 double dev = aHeading - rwy->headingDeg();
234 SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
235 double bad = fabs( fbrfhp.deviationWeight * dev) + 1e-20;
236 double quality = good / bad;
237
238 if (quality > currentBestQuality) {
239 currentBestQuality = quality;
240 result = rwy;
241 }
242 }
243
244 return result;
245}
246
247//------------------------------------------------------------------------------
249{
250 loadRunways();
251
252 FGRunway* result = NULL;
253 double currentLowestDev = 180.0;
254
255 for (auto rwy : mRunways) {
256 double inboundCourse = SGGeodesy::courseDeg(aPos, rwy->end());
257 double dev = inboundCourse - rwy->headingDeg();
258 SG_NORMALIZE_RANGE(dev, -180.0, 180.0);
259
260 dev = fabs(dev);
261 if (dev < currentLowestDev) { // new best match
262 currentLowestDev = dev;
263 result = rwy;
264 }
265 } // of runway iteration
266
267 return result;
268
269}
270
271//------------------------------------------------------------------------------
272bool FGAirport::hasHardRunwayOfLengthFt(double aLengthFt) const
273{
274 loadRunways();
275
276 for (auto rwy : mRunways) {
277 if (rwy->isHardSurface() && (rwy->lengthFt() >= aLengthFt)) {
278 return true; // we're done!
279 }
280 } // of runways iteration
281
282 return false;
283}
284
286{
287 FGRunwayRef r;
288 loadRunways();
289
290 for (auto rwy : mRunways) {
291 if (!r || (r->lengthFt() < rwy->lengthFt())) {
292 r = rwy;
293 }
294 } // of runways iteration
295
296 return r;
297}
298
299//------------------------------------------------------------------------------
301{
302 loadRunways();
303
304 return mRunways;
305}
306
307//------------------------------------------------------------------------------
309{
310 loadRunways();
311
312 FGRunwayList r;
313
314 for (auto rwy : mRunways) {
315 FGRunway* recip = rwy->reciprocalRunway();
316 if (recip) {
317 FGRunwayList::iterator it = std::find(r.begin(), r.end(), recip);
318 if (it != r.end()) {
319 continue; // reciprocal already in result set, don't include us
320 }
321 }
322
323 r.push_back(rwy);
324 }
325
326 return r;
327}
328
329//------------------------------------------------------------------------------
330unsigned int FGAirport::numTaxiways() const
331{
332 loadTaxiways();
333 return mTaxiways.size();
334}
335
336//------------------------------------------------------------------------------
338{
339 loadTaxiways();
340 return loadById<FGTaxiway>(mTaxiways, aIndex);
341}
342
343//------------------------------------------------------------------------------
345{
346 loadTaxiways();
347 return loadAllById<FGTaxiway>(mTaxiways);
348}
349
350//------------------------------------------------------------------------------
351unsigned int FGAirport::numPavements() const
352{
353 return mPavements.size();
354}
355
356//------------------------------------------------------------------------------
358{
359 return mPavements;
360}
361
363{
364 mPavements.push_back(pavement);
365}
366
367//------------------------------------------------------------------------------
368unsigned int FGAirport::numBoundary() const
369{
370 return mBoundary.size();
371}
372
373//------------------------------------------------------------------------------
375{
376 return mBoundary;
377}
378
380{
381 mBoundary.push_back(boundary);
382}
383
384//------------------------------------------------------------------------------
385unsigned int FGAirport::numLineFeatures() const
386{
387 return mLineFeatures.size();
388}
389
390//------------------------------------------------------------------------------
392{
393 return mLineFeatures;
394}
395
397{
398 mLineFeatures.push_back(linefeature);
399}
400
401//------------------------------------------------------------------------------
403{
404 auto envMgr = globals->get_subsystem<FGEnvironmentMgr>();
405
406 // This forces West-facing rwys to be used in no-wind situations
407 // which is consistent with Flightgear's initial setup.
408 double hdg = 270;
409
410 if (envMgr) {
411 // FIXME : this should use the weather at the airport, not the player's
412 // location.
413 const auto stationWeather = envMgr->getAircraftEnvironment();
414
415 double windSpeed = stationWeather->get_wind_speed_kt();
416 if (windSpeed > 0.0) {
417 hdg = stationWeather->get_wind_from_heading_deg();
418 }
419 }
420
421 return findBestRunwayForHeading(hdg);
422}
423
424//------------------------------------------------------------------------------
426 double aCuttofNm,
427 Filter* filter )
428{
429 AirportFilter aptFilter;
430 if( !filter )
431 filter = &aptFilter;
432
433 return static_pointer_cast<FGAirport>
434 (
435 FGPositioned::findClosest(aPos, aCuttofNm, filter)
436 );
437}
438
440 mMinLengthFt(minLengthFt)
441{
442 if (minLengthFt < 0.0) {
443 mMinLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft", 0.0);
444 }
445}
446
448{
449 return aApt->hasHardRunwayOfLengthFt(mMinLengthFt);
450}
451
452//------------------------------------------------------------------------------
455 _min_runway_length_ft( fgGetDouble("/sim/navdb/min-runway-length-ft", 0.0) )
456{
457
458}
459
460//------------------------------------------------------------------------------
462{
463 if( type == "heliport" ) _type = FGPositioned::HELIPORT;
464 else if( type == "seaport" ) _type = FGPositioned::SEAPORT;
465 else if( type == "airport" ) _type = FGPositioned::AIRPORT;
466 else return false;
467
468 return true;
469}
470
471//------------------------------------------------------------------------------
473{
474 FGAirport* apt = static_cast<FGAirport*>(pos);
475 if( (apt->type() == FGPositioned::AIRPORT)
477 )
478 return false;
479
480 return true;
481}
482
484{
485 airportCache.clear();
486}
487
488//------------------------------------------------------------------------------
489FGAirportRef FGAirport::findByIdent(const std::string& aIdent)
490{
491 AirportCache::iterator it = airportCache.find(aIdent);
492 if (it != airportCache.end())
493 return it->second;
494
495 PortsFilter filter;
496 FGAirportRef r = static_pointer_cast<FGAirport>
497 (
498 FGPositioned::findFirstWithIdent(aIdent, &filter)
499 );
500
501 // add airport to the cache (even when it's NULL, so we don't need to search in vain again)
502 airportCache[aIdent] = r;
503
504 // we don't warn here when r==NULL, let the caller do that
505 return r;
506}
507
508//------------------------------------------------------------------------------
509FGAirportRef FGAirport::getByIdent(const std::string& aIdent)
510{
511 FGAirportRef r = findByIdent(aIdent);
512 if (!r)
513 throw sg_range_exception("No such airport with ident: " + aIdent);
514 return r;
515}
516
517char** FGAirport::searchNamesAndIdents(const std::string& aFilter)
518{
520}
521
522// find basic airport location info from airport database
523const FGAirport *fgFindAirportID( const std::string& id)
524{
525 if ( id.empty() ) {
526 return NULL;
527 }
528
529 return FGAirport::findByIdent(id);
530}
531
532PositionedIDVec FGAirport::itemsOfType(FGPositioned::Type ty) const
533{
534 flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
535 return cache->airportItemsOfType(guid(), ty);
536}
537
538void FGAirport::loadRunways() const
539{
540 if (mRunwaysLoaded) {
541 return; // already loaded, great
542 }
543
544 loadSceneryDefinitions();
545
546 mRunwaysLoaded = true;
547 PositionedIDVec rwys(itemsOfType(FGPositioned::RUNWAY));
548 for (auto id : rwys) {
549 mRunways.push_back(loadById<FGRunway>(id));
550 }
551}
552
553void FGAirport::loadHelipads() const
554{
555 if (mHelipadsLoaded) {
556 return; // already loaded, great
557 }
558
559 mHelipadsLoaded = true;
560 mHelipads = itemsOfType(FGPositioned::HELIPAD);
561}
562
563void FGAirport::loadTaxiways() const
564{
565 if (mTaxiwaysLoaded) {
566 return; // already loaded, great
567 }
568
569 mTaxiwaysLoaded = true;
570 mTaxiways = itemsOfType(FGPositioned::TAXIWAY);
571}
572
573void FGAirport::loadProcedures() const
574{
575 if (mProceduresLoaded) {
576 return;
577 }
578
579 mProceduresLoaded = true;
580 SGPath path;
581 if (!XMLLoader::findAirportData(ident(), "procedures", path)) {
582 SG_LOG(SG_GENERAL, SG_INFO, "no procedures data available for " << ident());
583 return;
584 }
585
586 SG_LOG(SG_GENERAL, SG_INFO, ident() << ": loading procedures from " << path);
587 RouteBase::loadAirportProcedures(path, const_cast<FGAirport*>(this));
588}
589
590void FGAirport::loadRunwayRenames() const
591{
592 if (mRunwayRenamesLoaded) {
593 return;
594 }
595
596 SGPath path;
597 if (!XMLLoader::findAirportData(ident(), "runway_rename", path)) {
598 // No rename for airport; ignore
599 mRunwayRenamesLoaded = true;
600 return;
601 }
602
603 try {
604 SGPropertyNode_ptr rootNode = new SGPropertyNode;
605 readProperties(path, rootNode);
606 const_cast<FGAirport*>(this)->parseRunwayRenameData(rootNode);
607 mRunwayRenamesLoaded = true;
608 } catch (sg_exception& e) {
609 SG_LOG(SG_NAVAID, SG_WARN, ident() << "loading runway rename XML failed:" << e.getFormattedMessage());
610 }
611}
612
613void FGAirport::loadSceneryDefinitions() const
614{
615 if (mThresholdDataLoaded) {
616 return;
617 }
618
619 mThresholdDataLoaded = true;
620
621 SGPath path;
622 if (!XMLLoader::findAirportData(ident(), "threshold", path)) {
623 return; // no XML threshold data
624 }
625
626 try {
627 SGPropertyNode_ptr rootNode = new SGPropertyNode;
628 readProperties(path, rootNode);
629 const_cast<FGAirport*>(this)->readThresholdData(rootNode);
630 } catch (sg_exception& e) {
631 simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::TerraSync,
632 "Airport threshold data could not be loaded:" + e.getFormattedMessage(),
633 path);
634 }
635}
636
637void FGAirport::readThresholdData(SGPropertyNode* aRoot)
638{
639 SGPropertyNode* runway;
640 int runwayIndex = 0;
641 for (; (runway = aRoot->getChild("runway", runwayIndex)) != NULL; ++runwayIndex) {
642 SGPropertyNode* t0 = runway->getChild("threshold", 0),
643 *t1 = runway->getChild("threshold", 1);
644 if (!t0 || !t1) {
645 throw sg_io_exception("Mis-configured runway threshold data: exactly two thresholds must be defined",
646 sg_location{runway}, "FGAirport::readThresholdData", false);
647 }
648
649 processThreshold(t0);
650 processThreshold(t1);
651 } // of runways iteration
652}
653
654void FGAirport::processThreshold(SGPropertyNode* aThreshold)
655{
656 // first, let's identify the current runway
657 std::string rwyIdent(aThreshold->getStringValue("rwy"));
660
661 double lon = aThreshold->getDoubleValue("lon"),
662 lat = aThreshold->getDoubleValue("lat");
663 SGGeod newThreshold(SGGeod::fromDegM(lon, lat, elevationM()));
664
665 double newHeading = aThreshold->getDoubleValue("hdg-deg");
666 double newDisplacedThreshold = aThreshold->getDoubleValue("displ-m");
667 double newStopway = aThreshold->getDoubleValue("stopw-m");
668
669 if (id == 0) {
670 const auto runwayIdent = ident() + "/" + rwyIdent;
671 // enable this code when threshold.xml contains sufficient data to
672 // fully specify a new runway, *and* we figure out how to assign runtime
673 // Positioned IDs and insert temporary items into the spatial map.
674 throw sg_io_exception("Found runway not defined in global data:" + runwayIdent,
675 sg_location{aThreshold}, "FGAirport::processThreshold", false);
676
677#if 0
678 double newLength = 0.0, newWidth = 0.0;
679 int surfaceCode = 0;
680 FGRunway* rwy = new FGRunway(id, guid(), rwyIdent, newThreshold,
681 newHeading,
682 newLength, newWidth,
683 newDisplacedThreshold, newStopway,
684 surfaceCode);
685 // insert into the spatial map too
686 mRunways.push_back(rwy);
687#endif
688 } else {
689 FGRunway* rwy = loadById<FGRunway>(id);
690 rwy->updateThreshold(newThreshold, newHeading,
691 newDisplacedThreshold, newStopway);
692
693 }
694}
695
697{
698 validateTowerData();
699 return mTowerPosition;
700}
701
702void FGAirport::validateTowerData() const
703{
704 if (mTowerDataLoaded) {
705 return;
706 }
707
708 mTowerDataLoaded = true;
709
710// first, load data from the cache (apt.dat)
713 if (towers.empty()) {
714 mHasTower = false;
715 mTowerPosition = geod(); // use airport position
716
717 // offset the tower position away from the runway centerline, if
718 // airport has a single runway. Offset by eight times the runway width,
719 // an entirely guessed figure.
720 int runwayCount = numRunways();
721 if ((runwayCount > 0) && (runwayCount <= 2)) {
722 FGRunway* runway = getRunwayByIndex(0);
723 double hdg = runway->headingDeg() + 90;
724 mTowerPosition = SGGeodesy::direct(geod(), hdg, runway->widthM() * 8);
725 }
726
727 // increase tower elevation by 20 metres above the field elevation
728 mTowerPosition.setElevationM(geod().getElevationM() + 20.0);
729 } else {
730 FGPositionedRef tower = cache->loadById(towers.front());
731 mTowerPosition = tower->geod();
732 mHasTower = true;
733 }
734
735 SGPath path;
736 if (!XMLLoader::findAirportData(ident(), "twr", path)) {
737 return; // no XML tower data, base position is fine
738 }
739
740 try {
741 SGPropertyNode_ptr rootNode = new SGPropertyNode;
742 readProperties(path, rootNode);
743 const_cast<FGAirport*>(this)->readTowerData(rootNode);
744 mHasTower = true;
745 } catch (sg_exception& e){
746 SG_LOG(SG_NAVAID, SG_WARN, ident() << "loading twr XML failed:" << e.getFormattedMessage());
747 }
748}
749
750void FGAirport::readTowerData(SGPropertyNode* aRoot)
751{
752 SGPropertyNode* twrNode = aRoot->getChild("tower")->getChild("twr");
753 double lat = twrNode->getDoubleValue("lat"),
754 lon = twrNode->getDoubleValue("lon"),
755 elevM = twrNode->getDoubleValue("elev-m");
756// tower elevation is AGL, not AMSL. Since we don't want to depend on the
757// scenery for a precise terrain elevation, we use the field elevation
758// (this is also what the apt.dat code does)
759 double fieldElevationM = geod().getElevationM();
760 mTowerPosition = SGGeod::fromDegM(lon, lat, fieldElevationM + elevM);
761}
762
763void FGAirport::parseRunwayRenameData(SGPropertyNode* aRoot)
764{
765 SGPropertyNode* overrideNode = aRoot->getChild("runway-rename");
766 for (auto rnm : overrideNode->getChildren("runway")) {
767 const std::string oldIdent = rnm->getStringValue("old-ident");
768 const std::string newIdent = rnm->getStringValue("new-ident");
769 if (oldIdent.empty() || newIdent.empty()) {
770 SG_LOG(SG_NAVAID, SG_WARN, ident() << ": runway rename: Skipping bad runway rename entry");
771 continue;
772 }
773
774 if (!hasRunwayWithIdent(oldIdent)) {
775 SG_LOG(SG_NAVAID, SG_WARN, ident() << ": no old runway with ident:" << oldIdent);
776 continue;
777 }
778
779 _renamedRunways[newIdent] = oldIdent;
780 }
781}
782
783std::string FGAirport::findAPTRunwayForNewName(const std::string& newIdent) const
784{
785 loadRunwayRenames();
786
787 auto it = _renamedRunways.find(newIdent);
788 if (it == _renamedRunways.end())
789 return {};
790
791 return it->second;
792}
793
794
796{
797 if (mILSDataLoaded) {
798 return;
799 }
800
801 // to avoid re-entrancy on this code-path, ensure we set loaded
802 // immediately.
803 mILSDataLoaded = true;
804
805 SGPath path;
806 if (!XMLLoader::findAirportData(ident(), "ils", path)) {
807 return; // no XML ILS data
808 }
809
810 try {
811 SGPropertyNode_ptr rootNode = new SGPropertyNode;
812 readProperties(path, rootNode);
813 readILSData(rootNode);
814 } catch (sg_exception& e){
815 SG_LOG(SG_NAVAID, SG_WARN, ident() << "loading ils XML failed:" << e.getFormattedMessage());
816 }
817}
818
820{
821 validateTowerData();
822 return mHasTower;
823}
824
825void FGAirport::readILSData(SGPropertyNode* aRoot)
826{
828 // find the entry matching the runway
829 SGPropertyNode* runwayNode, *ilsNode;
830 for (int i=0; (runwayNode = aRoot->getChild("runway", i)) != NULL; ++i) {
831 for (int j=0; (ilsNode = runwayNode->getChild("ils", j)) != NULL; ++j) {
832 // must match on both nav-ident and runway ident, to support the following:
833 // - runways with multiple distinct ILS installations (KEWD, for example)
834 // - runways where both ends share the same nav ident (LFAT, for example)
835 PositionedID ils = cache->findILS(guid(), ilsNode->getStringValue("rwy"),
836 ilsNode->getStringValue("nav-id"));
837 if (ils == 0) {
838 SG_LOG(SG_GENERAL, SG_INFO, "reading ILS data for " << ident() <<
839 ", couldn't find runway/navaid for:" <<
840 ilsNode->getStringValue("rwy") << "/" <<
841 ilsNode->getStringValue("nav-id"));
842 continue;
843 }
844
845 double hdgDeg = ilsNode->getDoubleValue("hdg-deg"),
846 lon = ilsNode->getDoubleValue("lon"),
847 lat = ilsNode->getDoubleValue("lat"),
848 elevM = ilsNode->getDoubleValue("elev-m");
849
851 assert(nav.valid());
852 nav->updateFromXML(SGGeod::fromDegM(lon, lat, elevM), hdgDeg);
853 } // of ILS iteration
854 } // of runway iteration
855}
856
858{
859 mSIDs.push_back(aSid);
860}
861
863{
864 mSTARs.push_back(aStar);
865}
866
868{
869 mApproaches.push_back(aApp);
870}
871
872//------------------------------------------------------------------------------
873unsigned int FGAirport::numSIDs() const
874{
875 loadProcedures();
876 return mSIDs.size();
877}
878
879//------------------------------------------------------------------------------
880flightgear::SID* FGAirport::getSIDByIndex(unsigned int aIndex) const
881{
882 loadProcedures();
883 return mSIDs[aIndex];
884}
885
886//------------------------------------------------------------------------------
887flightgear::SID* FGAirport::findSIDWithIdent(const std::string& aIdent) const
888{
889 loadProcedures();
890 for (unsigned int i=0; i<mSIDs.size(); ++i) {
891 if (mSIDs[i]->ident() == aIdent) {
892 return mSIDs[i];
893 }
894 }
895
896 return NULL;
897}
898
899//------------------------------------------------------------------------------
901{
902 loadProcedures();
903 return flightgear::SIDList(mSIDs.begin(), mSIDs.end());
904}
905
906//------------------------------------------------------------------------------
907unsigned int FGAirport::numSTARs() const
908{
909 loadProcedures();
910 return mSTARs.size();
911}
912
913//------------------------------------------------------------------------------
914STAR* FGAirport::getSTARByIndex(unsigned int aIndex) const
915{
916 loadProcedures();
917 return mSTARs[aIndex];
918}
919
920//------------------------------------------------------------------------------
921STAR* FGAirport::findSTARWithIdent(const std::string& aIdent) const
922{
923 loadProcedures();
924 for (unsigned int i=0; i<mSTARs.size(); ++i) {
925 if (mSTARs[i]->ident() == aIdent) {
926 return mSTARs[i];
927 }
928 }
929
930 return NULL;
931}
932
933//------------------------------------------------------------------------------
935{
936 loadProcedures();
937 return STARList(mSTARs.begin(), mSTARs.end());
938}
939
940unsigned int FGAirport::numApproaches() const
941{
942 loadProcedures();
943 return mApproaches.size();
944}
945
946//------------------------------------------------------------------------------
947Approach* FGAirport::getApproachByIndex(unsigned int aIndex) const
948{
949 loadProcedures();
950 return mApproaches[aIndex];
951}
952
953//------------------------------------------------------------------------------
954Approach* FGAirport::findApproachWithIdent(const std::string& aIdent) const
955{
956 loadProcedures();
957 for (unsigned int i=0; i<mApproaches.size(); ++i) {
958 if (mApproaches[i]->ident() == aIdent) {
959 return mApproaches[i];
960 }
961 }
962
963 return NULL;
964}
965
966//------------------------------------------------------------------------------
968{
969 loadProcedures();
970 if( type == PROCEDURE_INVALID )
971 return ApproachList(mApproaches.begin(), mApproaches.end());
972
973 ApproachList ret;
974 for(size_t i = 0; i < mApproaches.size(); ++i)
975 {
976 if( mApproaches[i]->type() == type )
977 ret.push_back(mApproaches[i]);
978 }
979 return ret;
980}
981
984{
986 CommStationList result;
987 for (auto pos : cache->airportItemsOfType(guid(),
990 result.push_back( loadById<CommStation>(pos) );
991 }
992
993 return result;
994}
995
998{
1000 CommStationList result;
1001 for (auto pos : cache->airportItemsOfType(guid(), aTy)) {
1002 result.push_back( loadById<CommStation>(pos) );
1003 }
1004
1005 return result;
1006}
1007
1009{
1010public:
1012 _pos(pos),
1013 _sizeMetric(0)
1014 {
1015 assert(pos->type() == FGPositioned::AIRPORT);
1016 FGAirport* apt = static_cast<FGAirport*>(pos.get());
1017 for (auto rwy : apt->getRunwaysWithoutReciprocals()) {
1018 _sizeMetric += static_cast<int>(rwy->lengthFt());
1019 }
1020 }
1021
1022 bool operator<(const AirportWithSize& other) const
1023 {
1024 return _sizeMetric < other._sizeMetric;
1025 }
1026
1028 { return _pos; }
1029private:
1030 FGPositionedRef _pos;
1031 unsigned int _sizeMetric;
1032
1033};
1034
1036{
1037 std::vector<AirportWithSize> annotated;
1038 for (auto p : airportList) {
1039 annotated.push_back(AirportWithSize(p));
1040 }
1041 std::sort(annotated.begin(), annotated.end());
1042
1043 for (unsigned int i=0; i<annotated.size(); ++i) {
1044 airportList[i] = annotated[i].pos();
1045 }
1046}
1047
1052
1054{
1055 if (!_groundNetwork.get()) {
1056 _groundNetwork.reset(new FGGroundNetwork(const_cast<FGAirport*>(this)));
1057 XMLLoader::load(_groundNetwork.get());
1058 _groundNetwork->init();
1059 }
1060
1061 return _groundNetwork.get();
1062}
1063
1065{
1066 loadProcedures();
1067 for (auto sid : mSIDs) {
1068 auto trans = sid->findTransitionByEnroute(enroute);
1069 if (trans) {
1070 return trans;
1071 }
1072 }
1073 return nullptr;
1074}
1075
1076Transition *FGAirport::selectSIDByTransition(const FGRunway* runway, const std::string &aIdent) const
1077{
1078 loadProcedures();
1079 for (auto sid : mSIDs) {
1080 if (runway && !sid->isForRunway(runway))
1081 continue;
1082
1083 auto trans = sid->findTransitionByName(aIdent);
1084 if (trans) {
1085 return trans;
1086 }
1087 }
1088 return nullptr;
1089}
1090
1092{
1093 loadProcedures();
1094 for (auto star : mSTARs) {
1095 auto trans = star->findTransitionByEnroute(enroute);
1096 if (trans) {
1097 return trans;
1098 }
1099 }
1100 return nullptr;
1101}
1102
1103Transition *FGAirport::selectSTARByTransition(const FGRunway* runway, const std::string &aIdent) const
1104{
1105 loadProcedures();
1106 for (auto star : mSTARs) {
1107 if (runway && !star->isForRunway(runway))
1108 continue;
1109
1110 auto trans = star->findTransitionByName(aIdent);
1111 if (trans) {
1112 return trans;
1113 }
1114 }
1115 return nullptr;
1116}
1117
1118// get airport elevation
1119double fgGetAirportElev( const std::string& id )
1120{
1121 const FGAirport *a=fgFindAirportID( id);
1122 if (a) {
1123 return a->getElevation();
1124 } else {
1125 return -9999.0;
1126 }
1127}
1128
1129
1130// get airport position
1131SGGeod fgGetAirportPos( const std::string& id )
1132{
1133 const FGAirport *a = fgFindAirportID( id);
1134
1135 if (a) {
1136 return SGGeod::fromDegM(a->getLongitude(), a->getLatitude(), a->getElevation());
1137 } else {
1138 return SGGeod::fromDegM(0.0, 0.0, -9999.0);
1139 }
1140}
#define p(x)
#define i(x)
SGGeod fgGetAirportPos(const std::string &id)
Definition airport.cxx:1131
const FGAirport * fgFindAirportID(const std::string &id)
Definition airport.cxx:523
double fgGetAirportElev(const std::string &id)
Definition airport.cxx:1119
std::map< std::string, FGRunwayRef > FGRunwayMap
SGSharedPtr< FGTaxiway > FGTaxiwayRef
SGSharedPtr< FGAirportDynamics > FGAirportDynamicsRef
std::vector< FGRunwayRef > FGRunwayList
SGSharedPtr< FGHelipad > FGHelipadRef
SGSharedPtr< FGPavement > FGPavementRef
std::vector< FGPavementRef > FGPavementList
std::map< std::string, FGHelipadRef > FGHelipadMap
SGSharedPtr< FGAirport > FGAirportRef
SGSharedPtr< FGRunway > FGRunwayRef
std::vector< FGTaxiwayRef > FGTaxiwayList
bool operator<(const AirportWithSize &other) const
Definition airport.cxx:1022
FGPositionedRef pos() const
Definition airport.cxx:1027
AirportWithSize(FGPositionedRef pos)
Definition airport.cxx:1011
HardSurfaceFilter(double minLengthFt=-1)
Definition airport.cxx:439
virtual bool passAirport(FGAirport *aApt) const override
Definition airport.cxx:447
Filter which passes heliports and seaports in addition to airports.
Definition airport.hxx:209
FGPositioned::Type _type
Definition airport.hxx:247
bool fromTypeString(const std::string &type)
Construct from string containing type (airport, seaport or heliport)
Definition airport.cxx:461
virtual bool pass(FGPositioned *pos) const override
Over-rideable filter method.
Definition airport.cxx:472
FGAirportDynamicsRef getDynamics() const
Definition airport.cxx:1048
SGPath sceneryPath() const
Definition airport.cxx:96
unsigned int numRunways() const
Definition airport.cxx:102
std::string findAPTRunwayForNewName(const std::string &newIdent) const
Definition airport.cxx:783
flightgear::STAR * getSTARByIndex(unsigned int aIndex) const
Definition airport.cxx:914
FGRunwayRef findBestRunwayForHeading(double aHeading, struct FindBestRunwayForHeadingParams *parms=NULL) const
Definition airport.cxx:211
FGPavementList getLineFeatures() const
Definition airport.cxx:391
FGHelipadRef getHelipadByIndex(unsigned int aIndex) const
Definition airport.cxx:123
FGRunwayRef findBestRunwayForPos(const SGGeod &aPos) const
return the most likely target runway based on a position.
Definition airport.cxx:248
void validateILSData()
reload the ILS data from XML if required.
Definition airport.cxx:795
unsigned int numApproaches() const
Definition airport.cxx:940
unsigned int numSIDs() const
Definition airport.cxx:873
unsigned int numTaxiways() const
Definition airport.cxx:330
FGRunwayRef getRunwayByIndex(unsigned int aIndex) const
Definition airport.cxx:116
bool isAirport() const
Definition airport.cxx:81
FGRunwayList getRunwaysWithoutReciprocals() const
Retrieve all runways at the airport, but excluding the reciprocal runways.
Definition airport.cxx:308
double getElevation() const
Definition airport.hxx:61
double getLatitude() const
Definition airport.hxx:59
double getLongitude() const
Definition airport.hxx:57
FGRunwayRef getRunwayByIdent(const std::string &aIdent) const
Definition airport.cxx:182
void addLineFeature(FGPavementRef linefeature)
Definition airport.cxx:396
static void sortBySize(FGPositionedList &)
Sort an FGPositionedList of airports by size (number of runways + length) this is meant to prioritise...
Definition airport.cxx:1035
flightgear::Transition * selectSIDByEnrouteTransition(FGPositioned *enroute) const
Definition airport.cxx:1064
flightgear::CommStationList commStationsOfType(FGPositioned::Type aTy) const
Definition airport.cxx:997
bool isSeaport() const
Definition airport.cxx:86
bool hasHardRunwayOfLengthFt(double aLengthFt) const
Useful predicate for FMS/GPS/NAV displays and similar - check if this airport has a hard-surfaced run...
Definition airport.cxx:272
FGPavementList getPavements() const
Definition airport.cxx:357
static FGAirportRef findClosest(const SGGeod &aPos, double aCuttofNm, Filter *filter=NULL)
Syntactic wrapper around FGPositioned::findClosest - find the closest match for filter,...
Definition airport.cxx:425
unsigned int numLineFeatures() const
Definition airport.cxx:385
flightgear::Approach * getApproachByIndex(unsigned int aIndex) const
Definition airport.cxx:947
static void clearAirportsCache()
Definition airport.cxx:483
unsigned int numHelipads() const
Definition airport.cxx:109
flightgear::Transition * selectSTARByTransition(const FGRunway *runway, const std::string &aIdent) const
Definition airport.cxx:1103
static FGAirportRef findByIdent(const std::string &aIdent)
Helper to look up an FGAirport instance by unique ident.
Definition airport.cxx:489
flightgear::STAR * findSTARWithIdent(const std::string &aIdent) const
Definition airport.cxx:921
bool hasHelipadWithIdent(const std::string &aIdent) const
Definition airport.cxx:175
void addSID(flightgear::SID *aSid)
Definition airport.cxx:857
void addPavement(FGPavementRef pavement)
Definition airport.cxx:362
static char ** searchNamesAndIdents(const std::string &aFilter)
Specialised helper to implement the AirportList dialog.
Definition airport.cxx:517
virtual const std::string & name() const
Return the name of this positioned.
Definition airport.hxx:75
FGTaxiwayList getTaxiways() const
Definition airport.cxx:344
bool hasRunwayWithIdent(const std::string &aIdent) const
Definition airport.cxx:162
flightgear::ApproachList getApproaches(flightgear::ProcedureType type=flightgear::PROCEDURE_INVALID) const
Definition airport.cxx:967
flightgear::Transition * selectSTARByEnrouteTransition(FGPositioned *enroute) const
Definition airport.cxx:1091
FGPavementList getBoundary() const
Definition airport.cxx:374
unsigned int numPavements() const
Definition airport.cxx:351
FGRunwayRef longestRunway() const
Definition airport.cxx:285
void addSTAR(flightgear::STAR *aStar)
Definition airport.cxx:862
FGTaxiwayRef getTaxiwayByIndex(unsigned int aIndex) const
Definition airport.cxx:337
FGRunwayList getRunways() const
Retrieve all runways at the airport.
Definition airport.cxx:300
flightgear::CommStationList commStations() const
Definition airport.cxx:983
bool isHeliport() const
Definition airport.cxx:91
flightgear::STARList getSTARs() const
Definition airport.cxx:934
void addApproach(flightgear::Approach *aApp)
Definition airport.cxx:867
SGGeod getTowerLocation() const
Definition airport.cxx:696
FGAirport(PositionedID aGuid, const std::string &id, const SGGeod &location, const std::string &name, bool has_metar, Type aType, SGPath sceneryPath=SGPath())
Definition airport.cxx:53
flightgear::Approach * findApproachWithIdent(const std::string &aIdent) const
Definition airport.cxx:954
void addBoundary(FGPavementRef boundary)
Definition airport.cxx:379
FGRunwayMap getRunwayMap() const
Definition airport.cxx:130
unsigned int numBoundary() const
Definition airport.cxx:368
flightgear::SID * findSIDWithIdent(const std::string &aIdent) const
Definition airport.cxx:887
FGHelipadRef getHelipadByIdent(const std::string &aIdent) const
Definition airport.cxx:199
unsigned int numSTARs() const
Definition airport.cxx:907
flightgear::SID * getSIDByIndex(unsigned int aIndex) const
Definition airport.cxx:880
bool hasTower() const
Definition airport.cxx:819
FGRunwayRef getActiveRunwayForUsage() const
Definition airport.cxx:402
FGHelipadMap getHelipadMap() const
Definition airport.cxx:148
flightgear::Transition * selectSIDByTransition(const FGRunway *runway, const std::string &aIdent) const
Definition airport.cxx:1076
FGGroundNetwork * groundNetwork() const
Definition airport.cxx:1053
flightgear::SIDList getSIDs() const
Definition airport.cxx:900
static FGAirportRef getByIdent(const std::string &aIdent)
Helper to look up an FGAirport instance by unique ident.
Definition airport.cxx:509
Manage environment information.
Predicate class to support custom filtering of FGPositioned queries Default implementation of this pa...
FGPositioned(PositionedID aGuid, Type ty, const std::string &aIdent, const SGGeod &aPos)
friend class flightgear::NavDataCache
PositionedID guid() const
static std::vector< SGSharedPtr< T > > loadAllById(const PositionedIDVec &id_vec)
virtual const SGGeod & geod() const
static FGPositionedRef findFirstWithIdent(const std::string &aIdent, Filter *aFilter)
static SGSharedPtr< T > loadById(PositionedID id)
@ TOWER
an actual airport tower - not a radio comms facility!
double elevationM() const
Type type() const
const std::string & ident() const
static FGPositionedRef findClosest(const SGGeod &aPos, double aCutoffNm, Filter *aFilter=NULL)
Find the closest item to a position, which pass the specified filter A cutoff range in NM must be spe...
double headingDeg() const
Runway heading in degrees.
double lengthFt() const
double widthM() const
void updateThreshold(const SGGeod &newThreshold, double newHeading, double newDisplacedThreshold, double newStopway)
Definition runways.cxx:183
FGRunway * reciprocalRunway() const
Definition runways.cxx:115
static void load(FGRunwayPreference *p)
static bool findAirportData(const std::string &aICAO, const std::string &aFileName, SGPath &aPath)
Search the scenery for a file name of the form: I/C/A/ICAO.filename.xml and return the corresponding ...
static FGAirportDynamicsRef find(const std::string &icao)
Describe an approach procedure, including the missed approach segment.
PositionedID findILS(PositionedID airport, const std::string &runway, const std::string &navIdent)
Given an airport, and runway and ILS identifier, find the corresponding cache entry.
PositionedID airportItemWithIdent(PositionedID apt, FGPositioned::Type ty, const std::string &ident)
find the first match item of the specified type and ident, at an airport
FGPositionedRef loadById(PositionedID guid)
retrieve an FGPositioned from the cache.
static NavDataCache * instance()
char ** searchAirportNamesAndIdents(const std::string &aFilter)
Helper to implement the AirportSearch widget.
PositionedIDVec airportItemsOfType(PositionedID apt, FGPositioned::Type ty, FGPositioned::Type maxTy=FGPositioned::INVALID)
find all items of a specified type (or range of types) at an airport
static void loadAirportProcedures(const SGPath &aPath, FGAirport *aApt)
Definition route.cxx:757
Encapsulate a transition segment.
Definition procedure.hxx:70
FGGlobals * globals
Definition globals.cxx:142
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
Definition Addon.cxx:53
SGSharedPtr< FGPositioned > FGPositionedRef
Definition airways.cxx:49
std::vector< Approach * > ApproachList
std::vector< CommStationRef > CommStationList
std::vector< STAR * > STARList
std::vector< flightgear::SID * > SIDList
std::map< std::string, FGAirport * > AirportCache
@ PROCEDURE_INVALID
Definition procedure.hxx:37
SGSharedPtr< FGNavRecord > FGNavRecordRef
std::vector< PositionedID > PositionedIDVec
std::vector< FGPositionedRef > FGPositionedList
int64_t PositionedID
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
Definition proptest.cpp:30
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27