24#include <simgear/compiler.h>
25#include <simgear/constants.h>
26#include <simgear/debug/logstream.hxx>
27#include <simgear/io/iostreams/sgstream.hxx>
28#include <simgear/misc/sg_path.hxx>
29#include <simgear/misc/strutils.hxx>
30#include <simgear/structure/exception.hxx>
45namespace strutils = simgear::strutils;
55 SG_LOG(SG_GENERAL, SG_ALERT,
"unsupported type:" << aType);
56 throw sg_range_exception(
"Unsupported airport type",
"fptypeFromRobinType");
64 currentAirportPosID(0),
72 std::size_t bytesReadSoFar,
73 std::size_t totalSizeOfAllAptDatFiles)
75 const SGPath aptdb_file = sceneryLocation.
datPath;
76 string apt_dat = aptdb_file.utf8Str();
77 sg_gzifstream in(aptdb_file, std::ios_base::in | std::ios_base::binary,
true);
80 const std::string errMsg = simgear::strutils::error_string(errno);
81 SG_LOG(SG_GENERAL, SG_ALERT,
82 "Cannot open file '" << apt_dat <<
"': " << errMsg);
83 throw sg_io_exception(
"Cannot open file (" + errMsg +
")",
84 sg_location(aptdb_file));
89 unsigned int rowCode = 0;
90 unsigned int line_num = 0;
93 string currentAirportId;
99 bool skipAirport =
true;
102 while (line_num < 2 && std::getline(in, line)) {
108 std::string stripped_line = simgear::strutils::strip(line);
110 if (stripped_line !=
"I" && stripped_line !=
"A") {
111 std::string pb =
"invalid first line (neither 'I' nor 'A')";
112 SG_LOG(SG_GENERAL, SG_ALERT, aptdb_file <<
": " << pb);
113 throw sg_format_exception(
"cannot parse '" + apt_dat +
"': " + pb,
117 vector<string> fields(strutils::split(line, 0, 1));
119 if (fields.empty()) {
120 string errMsg =
"unable to parse format version: empty line";
121 SG_LOG(SG_GENERAL, SG_ALERT, apt_dat <<
": " << errMsg);
122 throw sg_format_exception(
"cannot parse '" + apt_dat +
"': " + errMsg,
125 unsigned int aptDatFormatVersion =
126 strutils::readNonNegativeInt<unsigned int>(fields[0]);
127 SG_LOG(SG_GENERAL, SG_INFO,
128 "apt.dat format version (" << apt_dat <<
"): " << aptDatFormatVersion);
133 throwExceptionIfStreamError(in, aptdb_file);
135 while (std::getline(in, line)) {
139 if (isBlankOrCommentLine(line))
142 if ((line_num % 100) == 0) {
144 unsigned int percent = ((bytesReadSoFar + in.approxOffset()) * 100) / totalSizeOfAllAptDatFiles;
145 cache->setRebuildPhaseProgress(
150 rowCode =
atoi(line.c_str());
155 vector<string> tokens(simgear::strutils::split(line));
156 if (tokens.size() < 6) {
157 SG_LOG(SG_GENERAL, SG_WARN,
158 apt_dat <<
":" << line_num <<
": invalid airport header "
159 "(at least 6 fields are required)");
164 currentAirportId = tokens[4];
167 std::pair<AirportInfoMapType::iterator, bool>
168 insertRetval = airportInfoMap.insert(
169 AirportInfoMapType::value_type(currentAirportId, RawAirportInfo()));
170 skipAirport = !insertRetval.second;
173 SG_LOG(SG_GENERAL, SG_INFO,
174 apt_dat <<
":" << line_num <<
": skipping airport " << currentAirportId <<
" (already defined earlier)");
177 RawAirportInfo& airportInfo = insertRetval.first->second;
178 airportInfo.file = aptdb_file;
179 airportInfo.sceneryPath = sceneryLocation.
sceneryPath;
180 airportInfo.rowCode = rowCode;
181 airportInfo.firstLineNum = line_num;
182 airportInfo.firstLineTokens = std::move(tokens);
184 }
else if (rowCode == 99) {
185 SG_LOG(SG_GENERAL, SG_DEBUG,
186 apt_dat <<
":" << line_num <<
": code 99 found "
187 "(normally at end of file)");
188 }
else if (!skipAirport) {
191 airportInfoMap[currentAirportId].otherLines.emplace_back(
192 line_num, rowCode, line);
196 throwExceptionIfStreamError(in, aptdb_file);
201 AirportInfoMapType::size_type nbLoadedAirports = 0;
202 AirportInfoMapType::size_type nbAirports = airportInfoMap.size();
205 for (AirportInfoMapType::const_iterator it = airportInfoMap.begin();
206 it != airportInfoMap.end(); ++it) {
208 const SGPath aptDat = it->second.file;
211 last_apt_id = it->first;
212 RawAirportInfo rawinfo = it->second;
214 loadAirport(aptDat, last_apt_id, &rawinfo);
217 if ((nbLoadedAirports % 300) == 0) {
219 unsigned int percent = nbLoadedAirports * 100 / nbAirports;
225 SG_LOG(SG_GENERAL, SG_INFO,
226 "Loaded data for " << nbLoadedAirports <<
" airports");
232 std::size_t bytesReadSoFar = 10;
233 std::size_t totalSizeOfAllAptDatFiles = 100;
235 readAptDatFile(sceneryLocation, bytesReadSoFar, totalSizeOfAllAptDatFiles);
237 RawAirportInfo rawInfo = airportInfoMap[id];
238 return loadAirport(sceneryLocation.
datPath,
id, &rawInfo,
true);
243 return ((code >= 50) && (code <= 56)) || ((code >= 1050) && (code <= 1056));
246const FGAirport* APTLoader::loadAirport(
const SGPath& aptDatFile,
const std::string& airportID, RawAirportInfo* airport_info,
bool createFGAirport)
250 parseAirportLine(airport_info->rowCode, airport_info->firstLineTokens,
251 airport_info->sceneryPath);
252 const LinesList& lines = airport_info->otherLines;
254 const string aptDat = aptDatFile.utf8Str();
255 NodeBlock current_block = None;
258 for (LinesList::const_iterator linesIt = lines.begin();
259 linesIt != lines.end(); ++linesIt) {
261 unsigned int rowCode = linesIt->rowCode;
264 parseRunwayLine810(aptDat, linesIt->number,
265 simgear::strutils::split(linesIt->str));
266 }
else if (rowCode == 100) {
267 parseRunwayLine850(aptDat, linesIt->number,
268 simgear::strutils::split(linesIt->str));
269 }
else if (rowCode == 101) {
270 parseWaterRunwayLine850(aptDat, linesIt->number,
271 simgear::strutils::split(linesIt->str));
272 }
else if (rowCode == 102) {
273 parseHelipadLine850(aptDat, linesIt->number,
274 simgear::strutils::split(linesIt->str));
275 }
else if (rowCode == 18) {
277 }
else if (rowCode == 14) {
278 parseViewpointLine(aptDat, linesIt->number,
279 simgear::strutils::split(linesIt->str));
280 }
else if (rowCode == 19) {
282 }
else if (rowCode == 20) {
284 }
else if (rowCode == 21) {
286 }
else if (rowCode == 15) {
288 }
else if (rowCode == 0) {
291 parseCommLine(aptDat, linesIt->number, rowCode,
292 simgear::strutils::split(linesIt->str));
293 }
else if (rowCode == 110) {
294 current_block = Pavement;
295 parsePavementLine850(simgear::strutils::split(linesIt->str, 0, 4));
296 }
else if (rowCode >= 111 && rowCode <= 116) {
297 switch (current_block) {
299 parseNodeLine850(&pavements, aptDat, linesIt->number, rowCode,
300 simgear::strutils::split(linesIt->str));
302 case AirportBoundary:
303 parseNodeLine850(&airport_boundary, aptDat, linesIt->number, rowCode,
304 simgear::strutils::split(linesIt->str));
307 parseNodeLine850(&linear_feature, aptDat, linesIt->number, rowCode,
308 simgear::strutils::split(linesIt->str));
312 std::ostringstream oss;
313 string cleanedLine = cleanLine(linesIt->str);
314 oss << aptDat <<
":" << linesIt->number <<
": unexpected row code " << rowCode;
315 SG_LOG(SG_GENERAL, SG_ALERT, oss.str() <<
" (" << cleanedLine <<
")");
316 throw sg_format_exception(oss.str(), cleanedLine);
319 }
else if (rowCode == 120) {
320 current_block = LinearFeature;
321 }
else if (rowCode == 130) {
322 current_block = AirportBoundary;
323 }
else if (rowCode >= 1000) {
326 std::ostringstream oss;
327 string cleanedLine = cleanLine(linesIt->str);
328 oss << aptDat <<
":" << linesIt->number <<
": unknown row code " << rowCode;
329 SG_LOG(SG_GENERAL, SG_DEV_ALERT, oss.str() <<
" (" << cleanedLine <<
")");
333 finishAirport(aptDat);
335 if (createFGAirport) {
344 airport_boundary.begin(),
345 airport_boundary.end(),
349 linear_feature.begin(),
350 linear_feature.end(),
354 airport_boundary.clear();
355 linear_feature.clear();
368bool APTLoader::isBlankOrCommentLine(
const std::string& line)
370 size_t pos = line.find_first_not_of(
" \t");
371 return (pos == std::string::npos ||
373 line.find(
"##", pos) == pos);
376std::string APTLoader::cleanLine(
const std::string& line)
378 std::string res = line;
382 for (std::string::reverse_iterator it = res.rbegin();
383 it != res.rend() && *it ==
'\r';
385 it = std::string::reverse_iterator(res.erase((it + 1).base()));
391void APTLoader::throwExceptionIfStreamError(
const sg_gzifstream& input_stream,
394 if (input_stream.bad()) {
395 const std::string errMsg = simgear::strutils::error_string(errno);
397 SG_LOG(SG_NAVAID, SG_ALERT,
398 "Error while reading '" << path.utf8Str() <<
"': " << errMsg);
399 throw sg_io_exception(
"APTLoader: error reading file (" + errMsg +
")",
404void APTLoader::finishAirport(
const string& aptDat)
406 if (currentAirportPosID == 0) {
411 currentAirportPosID = 0;
412 SG_LOG(SG_GENERAL, SG_ALERT,
"Error in '" << aptDat <<
"': no runways for " << last_apt_id <<
", skipping.");
416 double lat = rwy_lat_accum / (double)rwy_count;
417 double lon = rwy_lon_accum / (double)rwy_count;
419 SGGeod pos(SGGeod::fromDegFt(lon, lat, last_apt_elev));
420 cache->updatePosition(currentAirportPosID, pos);
422 currentAirportPosID = 0;
427void APTLoader::parseAirportLine(
unsigned int rowCode,
428 const vector<string>& token,
429 const SGPath& sceneryPath)
432 vector<string>::size_type lastIndex = token.size() - 1;
433 const string& id(token[4]);
434 double elev =
atof(token[1].c_str());
435 last_apt_elev = elev;
439 for (vector<string>::size_type
i = 5;
i < lastIndex; ++
i) {
440 name += token[
i] +
" ";
442 name += token[lastIndex];
450 id,
name, sceneryPath);
453void APTLoader::parseRunwayLine810(
const string& aptDat,
unsigned int lineNum,
454 const vector<string>& token)
456 if (token.size() < 11) {
457 SG_LOG(SG_GENERAL, SG_WARN,
458 aptDat <<
":" << lineNum <<
": invalid v810 runway line "
459 <<
"(row code 10): at least 11 fields are required");
463 double lat =
atof(token[1].c_str());
464 double lon =
atof(token[2].c_str());
465 rwy_lat_accum += lat;
466 rwy_lon_accum += lon;
469 const string& rwy_no(token[3]);
471 double heading =
atof(token[4].c_str());
472 double length =
atoi(token[5].c_str());
473 double width =
atoi(token[8].c_str());
474 length *= SG_FEET_TO_METER;
475 width *= SG_FEET_TO_METER;
478 SGGeod pos_1 = SGGeodesy::direct(SGGeod::fromDegFt(lon, lat, last_apt_elev),
479 heading, -length / 2);
481 last_rwy_heading = heading;
483 int surface_code =
atoi(token[10].c_str());
485 if (rwy_no[0] ==
'x') {
488 heading, length, width, 0.0, 0.0, surface_code);
489 }
else if (rwy_no[0] ==
'H') {
490 SGGeod pos(SGGeod::fromDegFt(lon, lat, last_apt_elev));
492 heading, length, width, 0.0, 0.0, surface_code);
495 string rwy_displ_threshold = token[6];
496 vector<string> displ = simgear::strutils::split(rwy_displ_threshold,
".");
497 double displ_thresh1 =
atof(displ[0].c_str());
498 double displ_thresh2 =
atof(displ[1].c_str());
499 displ_thresh1 *= SG_FEET_TO_METER;
500 displ_thresh2 *= SG_FEET_TO_METER;
502 string rwy_stopway = token[7];
503 vector<string> stop = simgear::strutils::split(rwy_stopway,
".");
504 double stopway1 =
atof(stop[0].c_str());
505 double stopway2 =
atof(stop[1].c_str());
506 stopway1 *= SG_FEET_TO_METER;
507 stopway2 *= SG_FEET_TO_METER;
509 SGGeod pos_2 = SGGeodesy::direct(pos_1, heading, length);
512 currentAirportPosID, heading, length,
513 width, displ_thresh1, stopway1,
520 SGMiscd::normalizePeriodic(0, 360, heading + 180.0),
521 length, width, displ_thresh2, stopway2,
524 cache->setRunwayReciprocal(rwy, reciprocal);
528void APTLoader::parseRunwayLine850(
const string& aptDat,
unsigned int lineNum,
529 const vector<string>& token)
531 if (token.size() < 26) {
532 SG_LOG(SG_GENERAL, SG_WARN,
533 aptDat <<
":" << lineNum <<
": invalid v850 runway line "
534 <<
"(row code 100): at least 26 fields are required");
538 double width =
atof(token[1].c_str());
539 int surface_code =
atoi(token[2].c_str());
540 int shoulder_code =
atoi(token[3].c_str());
541 float smoothness =
atof(token[4].c_str());
542 int center_lights =
atoi(token[5].c_str());
543 int edge_lights =
atoi(token[6].c_str());
544 int distance_remaining =
atoi(token[7].c_str());
546 double lat_1 =
atof(token[9].c_str());
547 double lon_1 =
atof(token[10].c_str());
548 SGGeod pos_1(SGGeod::fromDegFt(lon_1, lat_1, 0.0));
549 rwy_lat_accum += lat_1;
550 rwy_lon_accum += lon_1;
553 double lat_2 =
atof(token[18].c_str());
554 double lon_2 =
atof(token[19].c_str());
555 SGGeod pos_2(SGGeod::fromDegFt(lon_2, lat_2, 0.0));
556 rwy_lat_accum += lat_2;
557 rwy_lon_accum += lon_2;
560 double length, heading_1, heading_2;
561 SGGeodesy::inverse(pos_1, pos_2, heading_1, heading_2, length);
563 last_rwy_heading = heading_1;
565 const string& rwy_no_1(token[8]);
566 const string& rwy_no_2(token[17]);
567 if (rwy_no_1.empty() || rwy_no_2.empty())
570 double displ_thresh1 =
atof(token[11].c_str());
571 double displ_thresh2 =
atof(token[20].c_str());
573 double stopway1 =
atof(token[12].c_str());
574 double stopway2 =
atof(token[21].c_str());
576 int markings1 =
atoi(token[13].c_str());
577 int markings2 =
atoi(token[22].c_str());
579 int approach1 =
atoi(token[14].c_str());
580 int approach2 =
atoi(token[23].c_str());
582 int tdz1 =
atoi(token[15].c_str());
583 int tdz2 =
atoi(token[24].c_str());
585 int reil1 =
atoi(token[16].c_str());
586 int reil2 =
atoi(token[25].c_str());
589 currentAirportPosID, heading_1, length,
590 width, displ_thresh1, stopway1, markings1,
591 approach1, tdz1, reil1,
592 surface_code, shoulder_code, smoothness,
593 center_lights, edge_lights, distance_remaining);
598 currentAirportPosID, heading_2, length,
599 width, displ_thresh2, stopway2, markings2,
600 approach2, tdz2, reil2,
601 surface_code, shoulder_code, smoothness,
602 center_lights, edge_lights, distance_remaining);
604 cache->setRunwayReciprocal(rwy, reciprocal);
607void APTLoader::parseWaterRunwayLine850(
const string& aptDat,
608 unsigned int lineNum,
609 const vector<string>& token)
611 if (token.size() < 9) {
612 SG_LOG(SG_GENERAL, SG_WARN,
613 aptDat <<
":" << lineNum <<
": invalid v850 water runway line "
614 <<
"(row code 101): at least 9 fields are required");
618 double width =
atof(token[1].c_str());
620 double lat_1 =
atof(token[4].c_str());
621 double lon_1 =
atof(token[5].c_str());
622 SGGeod pos_1(SGGeod::fromDegFt(lon_1, lat_1, 0.0));
623 rwy_lat_accum += lat_1;
624 rwy_lon_accum += lon_1;
627 double lat_2 =
atof(token[7].c_str());
628 double lon_2 =
atof(token[8].c_str());
629 SGGeod pos_2(SGGeod::fromDegFt(lon_2, lat_2, 0.0));
630 rwy_lat_accum += lat_2;
631 rwy_lon_accum += lon_2;
634 double length, heading_1, heading_2;
635 SGGeodesy::inverse(pos_1, pos_2, heading_1, heading_2, length);
637 last_rwy_heading = heading_1;
639 const string& rwy_no_1(token[3]);
640 const string& rwy_no_2(token[6]);
645 currentAirportPosID, heading_1, length,
646 width, 0.0, 0.0, 0, 0, 0, 0, 13, 0, 1.0, 0, 1, 0);
651 currentAirportPosID, heading_2, length,
652 width, 0.0, 0.0, 13);
654 cache->setRunwayReciprocal(rwy, reciprocal);
657void APTLoader::parseHelipadLine850(
const string& aptDat,
unsigned int lineNum,
658 const vector<string>& token)
660 if (token.size() < 12) {
661 SG_LOG(SG_GENERAL, SG_WARN,
662 aptDat <<
":" << lineNum <<
": invalid v850 helipad line "
663 <<
"(row code 102): at least 12 fields are required");
667 double length =
atof(token[5].c_str());
668 double width =
atof(token[6].c_str());
670 double lat =
atof(token[2].c_str());
671 double lon =
atof(token[3].c_str());
672 SGGeod pos(SGGeod::fromDegFt(lon, lat, 0.0));
673 rwy_lat_accum += lat;
674 rwy_lon_accum += lon;
677 double heading =
atof(token[4].c_str());
679 last_rwy_heading = heading;
681 const string& rwy_no(token[1]);
682 int surface_code =
atoi(token[7].c_str());
683 int markings =
atoi(token[8].c_str());
684 int shoulder_code =
atoi(token[9].c_str());
685 float smoothness =
atof(token[10].c_str());
686 int edge_lights =
atoi(token[11].c_str());
689 currentAirportPosID, heading, length,
690 width, 0.0, 0.0, markings, 0, 0, 0,
691 surface_code, shoulder_code, smoothness, 0, edge_lights, 0);
694void APTLoader::parseViewpointLine(
const string& aptDat,
unsigned int lineNum,
695 const vector<string>& token)
697 if (token.size() < 5) {
698 SG_LOG(SG_GENERAL, SG_WARN,
699 aptDat <<
":" << lineNum <<
": invalid viewpoint line "
700 "(row code 14): at least 5 fields are required");
702 double lat =
atof(token[1].c_str());
703 double lon =
atof(token[2].c_str());
704 double elev =
atof(token[3].c_str());
705 tower = SGGeod::fromDegFt(lon, lat, elev + last_apt_elev);
706 cache->insertTower(currentAirportPosID, tower);
710void APTLoader::parsePavementLine850(
const vector<string>& token)
712 if (token.size() >= 5) {
713 pavement_ident = token[4];
714 if (!pavement_ident.empty() &&
715 pavement_ident[pavement_ident.size() - 1] ==
'\r')
716 pavement_ident.erase(pavement_ident.size() - 1);
718 pavement_ident =
"xx";
722void APTLoader::parseNodeLine850(NodeList* nodelist,
723 const string& aptDat,
724 unsigned int lineNum,
int rowCode,
725 const vector<string>& token)
727 static const unsigned int minNbTokens[] = {3, 5, 3, 5, 3, 5};
728 assert(111 <= rowCode && rowCode <= 116);
730 if (token.size() < minNbTokens[rowCode - 111]) {
731 SG_LOG(SG_GENERAL, SG_WARN,
732 aptDat <<
":" << lineNum <<
": invalid v850 node line "
733 <<
"(row code " << rowCode <<
"): at least " << minNbTokens[rowCode - 111] <<
" fields are required");
737 double lat =
atof(token[1].c_str());
738 double lon =
atof(token[2].c_str());
739 SGGeod pos(SGGeod::fromDegFt(lon, lat, 0.0));
742 if ((!pavement_ident.empty()) || (nodelist->size() == 0)) {
743 pvt =
new FGPavement(0, pavement_ident, pos);
744 nodelist->push_back(pvt);
747 pvt = nodelist->back();
755 if ((rowCode < 115) && (token.size() == (minNbTokens[rowCode - 111] + 1))) {
757 paintCode =
atoi(token[minNbTokens[rowCode - 111]].c_str());
760 if ((rowCode < 115) && (token.size() == (minNbTokens[rowCode - 111] + 2))) {
762 paintCode =
atoi(token[minNbTokens[rowCode - 111] - 1].c_str());
763 lightCode =
atoi(token[minNbTokens[rowCode - 111]].c_str());
766 if ((rowCode == 112) || (rowCode == 114) || (rowCode == 116)) {
767 double lat_b =
atof(token[3].c_str());
768 double lon_b =
atof(token[4].c_str());
769 SGGeod pos_b(SGGeod::fromDegFt(lon_b, lat_b, 0.0));
770 pvt->
addBezierNode(pos, pos_b, (rowCode == 114) || (rowCode == 116), (rowCode == 114), paintCode, lightCode);
772 pvt->
addNode(pos, (rowCode == 113) || (rowCode == 115), (rowCode == 113), paintCode, lightCode);
776void APTLoader::parseCommLine(
const string& aptDat,
777 unsigned int lineNum,
unsigned int rowCode,
778 const vector<string>& token)
780 if (token.size() < 3) {
781 SG_LOG(SG_GENERAL, SG_WARN,
782 aptDat <<
":" << lineNum <<
": invalid Comm Frequency line "
783 <<
"(row code " << rowCode <<
"): at least 3 fields are required");
785 }
else if (rwy_count <= 0) {
786 SG_LOG(SG_GENERAL, SG_ALERT,
787 aptDat <<
":" << lineNum <<
": no runways, skipping Comm "
788 <<
"Frequency line for " << last_apt_id);
794 SGGeod pos = SGGeod::fromDegFt(rwy_lon_accum / (
double)rwy_count,
795 rwy_lat_accum / (
double)rwy_count,
798 const bool isAPT1000Code = rowCode > 1000;
804 int freqKhz = std::stoi(token[1]);
806 const int channel = freqKhz % 25;
807 if (channel != 0 && channel != 5 && channel != 10 && channel != 15) {
808 SG_LOG(SG_GENERAL, SG_ALERT,
809 aptDat <<
":" << lineNum <<
": skipping invalid 8.333 kHz Frequency " << freqKhz <<
" for " << last_apt_id);
814 const int remainder = freqKhz % 100;
816 if (remainder == 20 || remainder == 70) {
821 SG_LOG(SG_GENERAL, SG_ALERT,
822 aptDat <<
":" << lineNum <<
": skipping invalid 25 kHz Frequency " << freqKhz <<
" for " << last_apt_id);
827 if (freqKhz < 118000 || freqKhz >= 137000) {
828 SG_LOG(SG_GENERAL, SG_ALERT,
829 aptDat <<
":" << lineNum <<
": skipping out of range Frequency " << freqKhz <<
" for " << last_apt_id);
839 for (
size_t i = 2;
i < token.size(); ++
i) {
840 if (token[
i] ==
"ATIS") {
854 throw sg_range_exception(
"unsupported apt.dat comm station type");
859 string name = token[2];
860 for (
size_t i = 3;
i < token.size(); ++
i)
861 name +=
' ' + token[
i];
863 cache->insertCommStation(ty,
name, pos, freqKhz, rangeNm,
864 currentAirportPosID);
870 sg_gzifstream metar_in(metar_file);
871 if (!metar_in.is_open()) {
872 SG_LOG(SG_GENERAL, SG_ALERT,
"Cannot open file: " << metar_file);
881 if (ident ==
"#" || ident ==
"//") {
SGSharedPtr< FGPavement > FGPavementRef
SGSharedPtr< FGAirport > FGAirportRef
static FGPositioned::Type fptypeFromRobinType(unsigned int aType)
static FGAirportRef findByIdent(const std::string &aIdent)
Helper to look up an FGAirport instance by unique ident.
void addNode(const SGGeod &aPos, bool aClose=false, bool aLoop=false, int paintCode=0, int lightCode=0)
void addBezierNode(const SGGeod &aPos, const SGGeod &aCtrlPt, bool aClose=false, bool aLoop=false, int paintCode=0, int lightCode=0)
static std::string reverseIdent(const std::string &aRunayIdent)
given a runway identifier (06, 18L, 31R) compute the identifier for the reciprocal heading runway (24...
const FGAirport * loadAirportFromFile(const std::string &id, const NavDataCache::SceneryLocation &sceneryLocation)
void readAptDatFile(const NavDataCache::SceneryLocation &sceneryLocation, std::size_t bytesReadSoFar, std::size_t totalSizeOfAllAptDatFiles)
@ REBUILD_LOADING_AIRPORTS
@ REBUILD_READING_APT_DAT_FILES
void setAirportMetar(const std::string &icao, bool hasMetar)
update the metar flag associated with an airport
static NavDataCache * instance()
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
bool metarDataLoad(const SGPath &metar_file)
static bool isCommLine(const int code)
static double atof(const string &str)
static int atoi(const string &str)