#!/usr/bin/perl -w # NAME: aptextract.pl # AIM: Given an ICAO, load apt.dat.gz, and extract that airport to the out file # 2018-07-29 - Show command line in xg file # 2018-06-09 - small enhancments # 07/12/2014 geoff mclane http://geoffair.net/mperl use strict; use warnings; use File::Basename; # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] ) use Time::HiRes qw( gettimeofday tv_interval ); use Cwd; my $os = $^O; my $perl_dir = '/home/geoff/bin'; my $PATH_SEP = '/'; my $temp_dir = '/tmp'; if ($os =~ /win/i) { $perl_dir = 'C:\GTools\perl'; $temp_dir = $perl_dir; $PATH_SEP = "\\"; } unshift(@INC, $perl_dir); require 'lib_utils.pl' or die "Unable to load 'lib_utils.pl' Check paths in \@INC...\n"; require 'fg_wsg84.pl' or die "Unable to load fg_wsg84.pl ...\n"; # log file stuff our ($LF); my $pgmname = $0; if ($pgmname =~ /(\\|\/)/) { my @tmpsp = split(/(\\|\/)/,$pgmname); $pgmname = $tmpsp[-1]; } my $outfile = $temp_dir.$PATH_SEP."temp.$pgmname.txt"; open_log($outfile); # user variables my $VERS = "0.0.6 2018-07-29"; ###my $VERS = "0.0.5 2018-06-09"; ###my $VERS = "0.0.4 2014-12-25"; ###my $VERS = "0.0.3 2014-12-07"; my $load_log = 0; my $in_icao = ''; my $verbosity = 0; my $out_file = ''; ### my $fgroot = "X:\\fgdata"; my $aptdat = "X:\\fgdata\\Airports\\apt.dat.gz"; my $cmpdat = ''; my $writesigns = 0; my $xg_out = ''; my $add_parkpos = 0; my $def_rad = 30; my $add_names = 1; my $at_head = 0; my $add_info = 0; my $box_color = 'blue'; my $hdg_color = 'white'; # ### DEBUG ### my $debug_on = 0; # from : http://gateway.x-plane.com/artist/page/193 # EDFB_Scenery_Pack.zip # EDFH_Scenery_Pack.zip # EDFL_Scenery_Pack.zip # EDFQ_Scenery_Pack.zip # from : http://gateway.x-plane.com/artist/page/25 # ZSPD_Scenery_Pack.zip # 08R_Scenery_Pack.zip #my $def_file = 'EDFB'; #my $def_cmp = 'C:\Users\user\Downloads\EDFB_Scenery_Pack\Earth nav data\apt.dat'; #my $def_file = 'EDFH'; #my $def_cmp = 'C:\Users\user\Downloads\EDFH_Scenery_Pack\Earth nav data\apt.dat'; #my $def_file = 'EDFL'; #my $def_cmp = 'C:\Users\user\Downloads\EDFL_Scenery_Pack\Earth nav data\apt.dat'; #my $def_file = 'EDFQ'; #my $def_cmp = 'C:\Users\user\Downloads\EDFQ_Scenery_Pack\Earth nav data\apt.dat'; #my $def_file = 'ZSPD'; #my $def_cmp = 'D:\FG\xplane\ZSPD_Scenery_Pack\Earth nav data\apt.dat'; #my $def_file = '08R'; #my $def_cmp = 'C:\Users\user\Downloads\08R_Scenery_Pack\Earth nav data\apt.dat'; my $def_file = 'KSFO'; my $def_cmp = 'D:\FG\xplane\KSFO_Scenery_Pack\Earth nav data\apt.dat'; ### program variables my @warnings = (); my $cwd = cwd(); my ($bgntm); my @apt_lines = (); my @my_apt = (); my $cmd_line = ''; # constants #/** Feet to Meters */ my $FEET_TO_METER = 0.3048; sub VERB1() { return $verbosity >= 1; } sub VERB2() { return $verbosity >= 2; } sub VERB5() { return $verbosity >= 5; } sub VERB9() { return $verbosity >= 9; } sub show_warnings($) { my ($val) = @_; if (@warnings) { prt( "\nGot ".scalar @warnings." WARNINGS...\n" ); foreach my $itm (@warnings) { prt("$itm\n"); } prt("\n"); } else { prt( "\nNo warnings issued.\n\n" ) if (VERB9()); } } sub pgm_exit($$) { my ($val,$msg) = @_; if (length($msg)) { $msg .= "\n" if (!($msg =~ /\n$/)); prt($msg); } show_warnings($val); close_log($outfile,$load_log); exit($val); } sub prtw($) { my ($tx) = shift; $tx =~ s/\n$//; prt("$tx\n"); push(@warnings,$tx); } sub process_in_file($) { my ($inf) = @_; if (! open INF, "<$inf") { pgm_exit(1,"ERROR: Unable to open file [$inf]\n"); } my @lines = ; close INF; my $lncnt = scalar @lines; prt("Processing $lncnt lines, from [$inf]...\n"); my ($line,$inc,$lnn); $lnn = 0; foreach $line (@lines) { chomp $line; $lnn++; if ($line =~ /\s*#\s*include\s+(.+)$/) { $inc = $1; prt("$lnn: $inc\n"); } } } sub load_apt_dat() { $bgntm = [gettimeofday]; if ($aptdat =~ /\.gz$/i) { if (!open(INF,"gzip -cdq $aptdat|")) { pgm_exit(1,"Failed to 'open' file '$aptdat'!\n"); } } else { if (!open( INF, "<$aptdat") ) { pgm_exit(1,"Error: Unable to open $aptdat\n"); } } @apt_lines = ; close INF; my $cnt = scalar @apt_lines; my $elap = secs_HHMMSS( tv_interval( $bgntm, [gettimeofday] ) ); prt("Loaded $cnt lines from $aptdat... in $elap\n"); } sub init_bounds($) { my $ra = shift; push(@{$ra},400); # 0 min_lon push(@{$ra},400); # 1 min_lat push(@{$ra},-400); # 2 max_lon push(@{$ra},-400); # 3 max_lat } sub set_bounds($$$) { my ($ra,$lat,$lon) = @_; ${$ra}[0] = $lon if ($lon < ${$ra}[0]); ${$ra}[1] = $lat if ($lat < ${$ra}[1]); ${$ra}[2] = $lon if ($lon > ${$ra}[2]); ${$ra}[3] = $lat if ($lat > ${$ra}[3]); } sub valid_bounds($) { return 1; } sub get_bounds($) { my $ra = shift; my $xg = "# add min/max bbox ${$ra}[0],${$ra}[1],${$ra}[2],${$ra}[3]\n"; $xg .= "color gray\n"; $xg .= "${$ra}[0] ${$ra}[1]\n"; # min_lon min_lat $xg .= "${$ra}[0] ${$ra}[3]\n"; # min_lon max_lat $xg .= "${$ra}[2] ${$ra}[3]\n"; # max_lon max_lat $xg .= "${$ra}[2] ${$ra}[1]\n"; # max_lon min_lat $xg .= "${$ra}[0] ${$ra}[1]\n"; # min_lon min_lat $xg .= "NEXT\n"; return $xg; } my $typ100 = 0; # Runway my $typ101 = 1; # Water runway my $typ102 = 2; # Helipad my $typ110 = 3; # Pavement (taxiway or ramp) header Must form a closed loop my $typ120 = 4; # Linear feature (painted line or light string) header Can form closed loop or simple string my $typ130 = 5; # Airport boundary header Must form a closed loop my $typ111 = 6; # Node All nodes can also include a “style” (line or lights) my $typ112 = 7; # Node with Bezier control point Bezier control points define smooth curves my $typ113 = 8; # Node with implicit close of loop Implied join to first node in chain my $typ114 = 9; # Node with Bezier control point, with implicit close of loop Implied join to first node in chain my $typ115 = 10; # Node terminating a string (no close loop) No “styles” used my $typ116 = 11; # Node with Bezier control point, terminating a string (no close loop) No “styles” used my $typ14 = 12; # Airport viewpoint One or none for each airport my $typ15 = 13; # Aeroplane startup location *** Convert these to new row code 1300 *** my $typ18 = 14; # Airport light beacon One or none for each airport my $typ19 = 15; # Windsock Zero, one or many for each airport my $typ20 = 16; # Taxiway sign (inc. runway distance-remaining signs) Zero, one or many for each airport my $typ21 = 17; # Lighting object (VASI, PAPI, Wig-Wag, etc.) Zero, one or many for each airport my $typ1000 = 18; # Airport traffic flow Zero, one or many for an airport. Used if following rules met # (rules of same type use ‘or’ logic, rules of a different type use # ‘and’ logic). First flow to pass all rules is used. my $typ1001 = 19; # Traffic flow wind rule Zero, one or many for a flow. Multiple rules use ‘or’ logic. my $typ1002 = 20; # Traffic flow minimum ceiling rule Zero or one rule for each flow my $typ1003 = 21; # Traffic flow minimum visibility rule Zero or one rule for each flow my $typ1004 = 22; # Traffic flow time rule Zero, one or many for a flow. Multiple rules use ‘or’ logic. my $typ1100 = 23; # Runway-in-use arrival/departure constraints First constraint met is used. Sequence matters! my $typ1101 = 24; # VFR traffic pattern Zero or one pattern for each traffic flow my $typ1200 = 25; # Header indicating that taxi route network data follows my $typ1201 = 26; # Taxi route network node Sequencing is arbitrary. Must be part of one or more edges. my $typ1202 = 27; # Taxi route network edge Must connect two nodes my $typ1204 = 28; # Taxi route edge active zone Can refer to up to 4 runway ends my $typ1300 = 29; # Airport location (deprecates code 15) Not explicitly connected to taxi route network # 50 – 56 Communication frequencies Zero, one or many for each airport my $typ50 = 30; my $typ51 = 31; my $typ52 = 32; my $typ53 = 33; my $typ54 = 34; my $typ55 = 35; my $typ56 = 36; my $typ1 = 37; my $typ16 = 38; my $typ17 = 39; my $typ10 = 40; my $typmax = 41; my %typ_text = ( $typ100 => '100 Runways', $typ101 => '101 Water runways', $typ102 => '102 Helipads', $typ110 => '110 Pavement (taxiway or ramp) header closed loop', $typ120 => '120 Linear feature', # (painted line or light string) header Can form closed loop or simple string $typ130 => '130 Airport boundary header', # Must form a closed loop $typ111 => '111 Node style', # All nodes can also include a “style” (line or lights) $typ112 => '112 Node with Bezier', # control point Bezier control points define smooth curves $typ113 => '113 Node with implicit close of loop', # Implied join to first node in chain $typ114 => '114 Node with Bezier close', # control point, with implicit close of loop Implied join to first node in chain $typ115 => '115 Node terminating a string', # (no close loop) No “styles” used $typ116 => '116 Node with Bezier control point', # terminating a string (no close loop) No “styles” used $typ14 => '14 Airport viewpoint', # One or none for each airport $typ15 => '15 Startup location', # *** Convert these to new row code 1300 *** $typ18 => '18 Light beacon', # One or none for each airport $typ19 => '19 Windsocks', # Zero, one or many for each airport $typ20 => '20 Taxiway sign', # (inc. runway distance-remaining signs) Zero, one or many for each airport $typ21 => '21 Lighting objects', # (VASI, PAPI, Wig-Wag, etc.) Zero, one or many for each airport $typ1000 => '1000 Traffic flow', # Zero, one or many for an airport. Used if following rules met $typ1001 => '1001 Traffic flow wind', # rule Zero, one or many for a flow. Multiple rules use ‘or’ logic. $typ1002 => '1002 Traffic flow minimum ceiling', # rule Zero or one rule for each flow $typ1003 => '1003 Traffic flow minimum visibility', # rule Zero or one rule for each flow $typ1004 => '1004 Traffic flow time', # rule Zero, one or many for a flow. Multiple rules use ‘or’ logic. $typ1100 => '1100 Runway-in-use', # arrival/departure constraints First constraint met is used. Sequence matters! $typ1101 => '1101 VFR traffic pattern', # Zero or one pattern for each traffic flow $typ1200 => '1200 Taxi route', # Header indicating that taxi route network data follows $typ1201 => '1201 Taxi route network', # node Sequencing is arbitrary. Must be part of one or more edges. $typ1202 => '1202 Taxi route network edge', # Must connect two nodes $typ1204 => '1204 Taxi route edge active', # zone Can refer to up to 4 runway ends $typ1300 => '1300 Airport location', # (deprecates code 15) Not explicitly connected to taxi route network $typ50 => '50 ATIS', $typ51 => '51 Unicom', $typ52 => '52 CLD', $typ53 => '53 GND', $typ54 => '54 TWR', $typ55 => '55 APP', $typ56 => '56 DEP', $typ1 => '1 Airport', $typ16 => '16 Water-port', $typ17 => '17 Heliport', $typ10 => '10 Runways810', $typmax => '0 LAST' ); sub set_type_count($$$) { my ($type,$rtc,$line) = @_; my $index = -1; if ($type == 100) { $index = $typ100; } elsif ($type == 101) { $index = $typ101; } elsif ($type == 102) { $index = $typ102; } elsif ($type == 110) { $index = $typ110; } elsif ($type == 120) { $index = $typ120; } elsif ($type == 130) { $index = $typ130; } elsif ($type == 111) { $index = $typ111; } elsif ($type == 112) { $index = $typ112; } elsif ($type == 113) { $index = $typ113; } elsif ($type == 114) { $index = $typ114; } elsif ($type == 115) { $index = $typ115; } elsif ($type == 116) { $index = $typ116; } elsif ($type == 14) { $index = $typ14; } elsif ($type == 15) { $index = $typ15; } elsif ($type == 18) { $index = $typ18; } elsif ($type == 19) { $index = $typ19; } elsif ($type == 20) { $index = $typ20; } elsif ($type == 21) { $index = $typ21; } elsif ($type == 1000) { $index = $typ1000; } elsif ($type == 1001) { $index = $typ1001; } elsif ($type == 1002) { $index = $typ1002; } elsif ($type == 1003) { $index = $typ1003; } elsif ($type == 1004) { $index = $typ1004; } elsif ($type == 1100) { $index = $typ1100; } elsif ($type == 1101) { $index = $typ1101; } elsif ($type == 1200) { $index = $typ1200; } elsif ($type == 1201) { $index = $typ1201; } elsif ($type == 1202) { $index = $typ1202; } elsif ($type == 1204) { $index = $typ1204; } elsif ($type == 1300) { $index = $typ1300; } elsif ($type == 50) { $index = $typ50; } elsif ($type == 51) { $index = $typ51; } elsif ($type == 52) { $index = $typ52; } elsif ($type == 53) { $index = $typ53; } elsif ($type == 54) { $index = $typ54; } elsif ($type == 55) { $index = $typ55; } elsif ($type == 56) { $index = $typ56; } elsif ($type == 1) { $index = $typ1; } elsif ($type == 16) { $index = $typ16; } elsif ($type == 17) { $index = $typ17; } elsif ($type == 10) { $index = $typ10; } else { pgm_exit(1,"Unprocessed type $type\nline: $line\n*** FIX ME ***\n"); } ${$rtc}[$index]++; return $index; } # get the runway box, and set bound sub rwy_xg_stg($$$$$$$) { my ($elat1,$elon1,$elat2,$elon2,$widm,$colr,$rb) = @_; my $hwidm = $widm / 2; my ($az1,$az2,$s,$az3,$az4); my ($lon1,$lon2,$lon3,$lon4,$lat1,$lat2,$lat3,$lat4); my $xg = "color $colr\n"; my $res = fg_geo_inverse_wgs_84($elat1,$elon1,$elat2,$elon2,\$az1,\$az2,\$s); $az3 = $az1 + 90; $az3 -= 360 if ($az3 >= 360); $az4 = $az1 - 90; $az4 += 360 if ($az4 < 0); $res = fg_geo_direct_wgs_84($elat1,$elon1, $az3, $hwidm, \$lat1, \$lon1, \$az2); set_bounds($rb,$lat1,$lon1); $xg .= "$lon1 $lat1\n"; $res = fg_geo_direct_wgs_84($elat1,$elon1, $az4, $hwidm, \$lat2, \$lon2, \$az2); set_bounds($rb,$lat2,$lon2); $xg .= "$lon2 $lat2\n"; $res = fg_geo_direct_wgs_84($elat2,$elon2, $az4, $hwidm, \$lat3, \$lon3, \$az2); set_bounds($rb,$lat3,$lon3); $xg .= "$lon3 $lat3\n"; $res = fg_geo_direct_wgs_84($elat2,$elon2, $az3, $hwidm, \$lat4, \$lon4, \$az2); set_bounds($rb,$lat4,$lon4); $xg .= "$lon4 $lat4\n"; $xg .= "$lon1 $lat1\n"; $xg .= "NEXT\n"; #### prt($xg); return $xg; } sub trim_sign($) { my $txt = shift; my $sign = ''; my $len = length($txt); my ($i,$ch); for ($i = 0; $i < $len; $i++) { $ch = substr($txt,$i,1); if ($ch eq '{') { $i++; for (; $i < $len; $i++) { $ch = substr($txt,$i,1); last if ($ch eq '}'); } } else { if ($ch eq '_') { $sign .= ' '; } else { $sign .= $ch; } } } return $sign; } sub get_rectange($$$$$) { my ($clat,$clon,$hdg,$rad,$name) = @_; my ($res); my ($lat1, $lon1, $az1); my ($lat2, $lon2, $az2); my $hrad = $rad / 2.0; my $rhdg = $hdg + 180.0; if ($rhdg >= 360.0) { $rhdg -= 360.0; } # // get tow ends using heading and reciprocal $res = fg_geo_direct_wgs_84( $clat, $clon, $hdg, $hrad, \$lat1, \$lon1, \$az1 ); $res = fg_geo_direct_wgs_84( $clat, $clon, $rhdg, $hrad, \$lat2, \$lon2, \$az2 ); #// setup heading to 'right' and 'left' my $hdg1 = $hdg + 90.0; if ($hdg1 >= 360.0) { $hdg1 -= 360.0; } my $hdg2 = $hdg - 90.0; if ($hdg2 < 0.0) { $hdg2 += 360.0; } my ($lata, $lona, $aza); my ($latb, $lonb, $azb); my ($latc, $lonc, $azc); my ($latd, $lond, $azd); #// generate the four corners $res = fg_geo_direct_wgs_84( $lat1, $lon1, $hdg1, $hrad, \$lata, \$lona, \$aza ); $res = fg_geo_direct_wgs_84( $lat1, $lon1, $hdg2, $hrad, \$latb, \$lonb, \$azb ); $res = fg_geo_direct_wgs_84( $lat2, $lon2, $hdg1, $hrad, \$latd, \$lond, \$azd ); $res = fg_geo_direct_wgs_84( $lat2, $lon2, $hdg2, $hrad, \$latc, \$lonc, \$azc ); #if 0 // 000000000000000000000000000000000000000000000000000000 # // fill out the boundary # add_bbox( pbb, lata, lona ); # add_bbox( pbb, latb, lonb ); # add_bbox( pbb, latc, lonc ); # add_bbox( pbb, latd, lond ); # add_bbox( pbb, lat1, lon1 ); # add_bbox( pbb, lat2, lon2 ); #endif // 00000000000000000000000000000000000000000000000000000 #// generate the Xgraph stream my $xg = ''; #// ******************************************************************* #// seems default precision is quite small - with very BAD results #xg << std::setprecision(def_precision); #// ******************************************************************* if ($name && $add_names && length($name)) { if ($at_head) { $xg .= "anno $lon1 $lat1"; } else { $xg .= "anno $clon $clat"; } $xg .= " $name"; if ($add_info) { $xg .= " r=$rad h=$hdg"; } $xg .= "\n"; } $xg .= "color $box_color\n"; $xg .= "$lona $lata\n"; $xg .= "$lonb $latb\n"; $xg .= "$lonc $latc\n"; $xg .= "$lond $latd\n"; $xg .= "$lona $lata\n"; $xg .= "NEXT\n"; $xg .= "color $hdg_color\n"; $xg .= "$lon1 $lat1\n"; $xg .= "$lon2 $lat2\n"; $xg .= "NEXT\n"; return $xg; } # found our ICAO, and collected all the lines, to the next airport # note a Helipad # 17 231 0 1 HI09 [H] Hon Municipalcipal Bldg # 102 H1 21.30433555 -157.85586778 0.00 10.67 10.67 2 2 0 0.25 0 # 19 21.30447818 -157.85571468 1 WS sub write_xg_file($) { my $ara = shift; # \@my_apt; my $max = scalar @{$ara}; my ($i,$ra,$typ,$alat,$alon,$icao,$name,$aalt); my ($elat1,$elon1,$elat2,$elon2,$az1,$az2,$s); my ($rwid,$surf,$rwy1,$rwy2,$res,$line,$cnt); my ($rlat,$rlon,$hdg,$hdgr,$eaz1,$eaz2,$colr,$rlen); my ($bgn,$end,$dir,$uid,$rra,$ptyp); my ($ralen,$j); my $gotvp = 0; my $gotnm = 0; my $xg = "# Generated by $pgmname on ".lu_get_YYYYMMDD_hhmmss_UTC(time())." UTC\n"; $xg .= "# with command: $cmd_line\n"; my %routing = (); my $glat = 0; my $glon = 0; my $rwycnt = 0; my $helicnt = 0; my @bounds = (); init_bounds(\@bounds); $typ = 0; for ($i = 0; $i < $max; $i++) { $ra = ${$ara}[$i]; $ptyp = $typ; # previous 'type' $typ = ${$ra}[0]; if (($typ == 1)||($typ == 16)||($typ == 17)) { $aalt = ${$ra}[1]; # Airport (general) ALTITUDE AMSL $icao = ${$ra}[4]; $name = join(' ', splice(@{$ra},5)); # Name $gotnm = 1; } elsif ($typ == 14) { # Airport viewpoint One or none for each airport if ($gotvp) { prtw("WARNING: Got VP $alat,$alon, NOW ".${$ra}[1].",".${$ra}[2]."?\n"); } $alat = ${$ra}[1]; # 47.53900921 Latitude of viewport in decimal degrees Eight decimal places supported $alon = ${$ra}[2]; # -122.30868700 Longitude of viewport in decimal degrees Eight decimal places supported set_bounds(\@bounds,$alat,$alon); $gotvp = 1; } elsif ($typ == 1201) { # 1201 Taxi routing node All nodes must be used in at least one edge # 1201 31.15839433 121.81188494 both 0 N_start $elat1 = ${$ra}[1]; $elon1 = ${$ra}[2]; set_bounds(\@bounds,$elat1,$elon1); $dir = ${$ra}[3]; $uid = ${$ra}[4]; if (defined $routing{$uid}) { prtw("WARNING: 1201 not unique ID $uid\n"); } $routing{$uid} = [$elat1,$elon1,$dir]; } elsif ($typ == 10) { $rlat = ${$ra}[1]; $rlon = ${$ra}[2]; $rwy1 = ${$ra}[3]; next if ($rwy1 eq 'xxx'); $rwycnt++; $glat += $rlat; $glon += $rlon; } elsif ($typ == 100) { $elat1 = ${$ra}[9]; $elon1 = ${$ra}[10]; $elat2 = ${$ra}[18]; $elon2 = ${$ra}[19]; $rwycnt++; $glat += ($elat1 + $elon1) / 2; $glon += ($elon1 + $elon2) / 2; } elsif ($typ == 102) { # 0 1 2 3 # 102 H1 21.30433555 -157.85586778 0.00 10.67 10.67 2 2 0 0.25 0 $helicnt++; $alat = ${$ra}[2]; # 47.53900921 Latitude of $alon = ${$ra}[3]; # -122.30868700 Longitude of set_bounds(\@bounds,$alat,$alon); } } if ($gotvp) { prt("Got airport viewport at $alat,$alon\n"); } elsif ($rwycnt) { $alat = $glat / $rwycnt; $alon = $glon / $rwycnt; $gotvp = 1; prt("Got airport av rwy ctr at $alat,$alon\n"); } elsif ($helicnt) { # lat and lon already set prt("Got heliport at $alat,$alon\n"); $gotvp = 1; } else { prtw("WARNING: No viewport and no runways (10,100)! TODO: Use something else!\n"); } for ($i = 0; $i < $max; $i++) { $ra = ${$ara}[$i]; $ralen = scalar @{$ra}; $typ = ${$ra}[0]; if (($typ == 1)||($typ == 16)||($typ == 17)) { $aalt = ${$ra}[1]; # Airport (general) ALTITUDE AMSL $icao = ${$ra}[4]; $name = join(' ', splice(@{$ra},5)); # Name if ($gotvp) { $line = "anno $alon $alat $icao $aalt $name\n"; $xg .= $line; prt($line); } } elsif ($typ == 10) { # previous 810 type runway and taxi way # 0 1 2 3 4 5 # 10 36.962213 127.031071 14x 131.52 8208 1595.0620 0000.0000 150 321321 1 0 3 0.25 0 0300.0300 # 10 36.969145 127.020106 xxx 221.51 329 0.0 0.0 75 161161 1 0 0 0.25 0 $rlat = ${$ra}[1]; $rlon = ${$ra}[2]; $rwy1 = ${$ra}[3]; $hdg = ${$ra}[4]; $rlen = (${$ra}[5] * $FEET_TO_METER); # length, in feet to meters $rwid = (${$ra}[8] * $FEET_TO_METER); # Runway or taxiway segment width in feet to meters $hdgr = $hdg + 180; $hdgr -= 360 if ($hdgr >= 360); $res = fg_geo_direct_wgs_84( $rlat, $rlon, $hdg , ($rlen / 2), \$elat1, \$elon1, \$eaz1 ); $res = fg_geo_direct_wgs_84( $rlat, $rlon, $hdgr, ($rlen / 2), \$elat2, \$elon2, \$eaz2 ); $colr = 'red'; if ($rwy1 eq 'xxx') { $colr = 'gray'; } set_bounds(\@bounds,$elat1,$elon1); set_bounds(\@bounds,$elat2,$elon2); # ======================================================= $xg .= rwy_xg_stg($elat1,$elon1,$elat2,$elon2,$rwid,$colr,\@bounds); # ======================================================= } elsif ($typ == 100) { $rwid = ${$ra}[1]; # WIDTH in meters? NOT SHOWN $surf = ${$ra}[2]; # add surface type $rwy1 = ${$ra}[8]; $elat1 = ${$ra}[9]; $elon1 = ${$ra}[10]; $rwy2 = ${$ra}[17]; $elat2 = ${$ra}[18]; $elon2 = ${$ra}[19]; set_bounds(\@bounds,$elat1,$elon1); set_bounds(\@bounds,$elat2,$elon2); ###$res = fg_geo_inverse_wgs_84 ($elat1,$elon1,$elat2,$elon2,\$az1,\$az2,\$s); # ======================================================= $xg .= rwy_xg_stg($elat1,$elon1,$elat2,$elon2,$rwid,'red',\@bounds); # ======================================================= } elsif ($typ == 102) { #102 Row code for a helipad #H1 Designator for a helipad. Must be unique at an airport. Usually “H” suffixed by an integer (eg. “H1”, “H3”) $rlat = ${$ra}[2]; #47.53918248 Latitude of helipad centre in decimal degrees Eight decimal places supported $rlon = ${$ra}[3]; #-122.30722302 Longitude of helipad centre in decimal degrees Eight decimal places supported $hdg = ${$ra}[4]; #2.00 Orientation (true heading) of helipad in degrees Two decimal places recommended $rwid = ${$ra}[5]; #10.06 Helipad length in metres Two decimal places recommended (metres), must be >=1.00 $rlen = ${$ra}[6]; #10.06 Helipad width in metres Two decimal places recommended (metres), must be >= 1.00 #1 Helipad surface code Integer value for a Surface Type Code (see below) #0 Helipad markings 0 (other values not yet supported) #0 Code defining a helipad shoulder surface type 0=no shoulder, 1=asphalt shoulder, 2=concrete shoulder #0.25 Helipad smoothness (not used by X-Plane yet) 0.00 (smooth) to 1.00 (very rough). Default is 0.25 #0 Helipad edge lighting 0=no edge lights, 1=yellow edge lights $res = fg_geo_direct_wgs_84( $rlat, $rlon, $hdg , ($rlen / 2), \$elat1, \$elon1, \$eaz1 ); $hdgr = $hdg + 180; $hdgr -= 360 if ($hdgr >= 360); $res = fg_geo_direct_wgs_84( $rlat, $rlon, $hdgr , ($rlen / 2), \$elat2, \$elon2, \$eaz2 ); set_bounds(\@bounds,$elat1,$elon1); set_bounds(\@bounds,$elat2,$elon2); # ======================================================= $xg .= rwy_xg_stg($elat1,$elon1,$elat2,$elon2,$rwid,"green",\@bounds); # ======================================================= } elsif ($typ == 130) { $i++; # 47.53752190 [All nodes] Latitude of node in decimal degrees Eight decimal places supported # -122.30826710 [All nodes] Longitude of node in decimal degrees Eight decimal places supported $xg .= "color green\n"; $cnt = 0; for (; $i < $max; $i++) { $ra = ${$ara}[$i]; $typ = ${$ra}[0]; $elat1 = ${$ra}[1]; $elon1 = ${$ra}[2]; set_bounds(\@bounds,$elat1,$elon1); if ($cnt == 0) { $elat2 = $elat1; $elon2 = $elon1; } if ($typ == 111) { $xg .= "$elon1 $elat1\n"; } elsif ($typ == 113) { $xg .= "$elon1 $elat1\n"; last; } elsif (($typ == 112)||($typ == 114)||($typ == 116)) { # 47.53757385 [112, 114, 116 only] Latitude of Bezier control point in decimal degrees Eight decimal places supported. Ignore for 111, 113, 115 # -122.30824831 [112, 114, 116 only] Latitude of Bezier control point in decimal degrees Eight decimal places supported. Ignore for 111, 113, 115 $xg .= "$elon1 $elat1\n"; $elat1 = ${$ra}[3]; $elon1 = ${$ra}[4]; $xg .= "$elon1 $elat1\n"; set_bounds(\@bounds,$elat1,$elon1); last if ($typ == 114); } else { pgm_exit(1,"Type $typ: Other than 111 or 113 in airport bounds! *** FIX ME ***\n"); } $cnt++; } $xg .= "$elon2 $elat2\n"; $xg .= "NEXT\n"; } elsif ($typ == 110) { $i++; $xg .= "color gray\n"; $cnt = 0; for (; $i < $max; $i++) { $ra = ${$ara}[$i]; $typ = ${$ra}[0]; $elat1 = ${$ra}[1]; $elon1 = ${$ra}[2]; set_bounds(\@bounds,$elat1,$elon1); if ($cnt == 0) { $elat2 = $elat1; $elon2 = $elon1; } if ($typ == 130) { $i--; # back up to this last; } if ($typ == 111) { $xg .= "$elon1 $elat1\n"; } elsif (($typ == 112)||($typ == 114)||($typ == 116)) { # 47.53757385 [112, 114, 116 only] Latitude of Bezier control point in decimal degrees Eight decimal places supported. Ignore for 111, 113, 115 # -122.30824831 [112, 114, 116 only] Latitude of Bezier control point in decimal degrees Eight decimal places supported. Ignore for 111, 113, 115 $xg .= "$elon1 $elat1\n"; $elat1 = ${$ra}[3]; $elon1 = ${$ra}[4]; $xg .= "$elon1 $elat1\n"; set_bounds(\@bounds,$elat1,$elon1); last if ($typ == 114); } elsif ($typ == 112) { $xg .= "$elon1 $elat1\n"; } elsif ($typ == 113) { $xg .= "$elon1 $elat1\n"; last; } else { $line = join(" ",@{$ra}); pgm_exit(1,"Type $typ: line : $line! *** FIX ME ***\n"); } $cnt++; } $xg .= "$elon2 $elat2\n"; $xg .= "NEXT\n"; } elsif ($typ == 20) { # 0 1 2 3 4 5 6 # 20 50.34385470 008.87837095 358.5000 0 2 {@Y}{^l}A # 20 50.34074168 008.87848957 178.5000 0 2 {@Y}{^r}C{@@}C{^l} # 20 50.34048600 008.87849905 358.5000 0 2 {@Y}{^l}C{@@}C{^r} # 20 50.33713988 008.87862637 178.5000 0 2 {@Y}{^r}D{@@}D{^l} # 20 50.33693452 008.87863396 358.5000 0 2 {@Y}{^l}D{@@}D{^r} # 20 50.33314446 008.87877832 180.0000 0 2 {@Y}{^r}A $elat1 = ${$ra}[1]; $elon1 = ${$ra}[2]; set_bounds(\@bounds,$elat1,$elon1); $name = join(' ', splice(@{$ra},6)); # Letters $name = trim_sign($name); $xg .= "anno $elon1 $elat1 $name\n" if (length($name)); } elsif ($typ == 1200) { # readability only } elsif ($typ == 1201) { # 1201 Taxi routing node All nodes must be used in at least one edge # 1201 31.15839433 121.81188494 both 0 N_start $elat1 = ${$ra}[1]; $elon1 = ${$ra}[2]; set_bounds(\@bounds,$elat1,$elon1); $dir = ${$ra}[3]; $uid = ${$ra}[4]; } elsif ($typ == 1202) { # 1202 Taxi routing edge Segment in taxi routing network # 1202 270 255 twoway taxiway N $bgn = ${$ra}[1]; $end = ${$ra}[2]; if (defined $routing{$bgn} && defined $routing{$end}) { $rra = $routing{$bgn}; $elat1 = ${$rra}[0]; $elon1 = ${$rra}[1]; $rra = $routing{$end}; $elat2 = ${$rra}[0]; $elon2 = ${$rra}[1]; $xg .= "color blue\n"; $xg .= "$elon1 $elat1\n"; $xg .= "$elon2 $elat2\n"; $xg .= "NEXT\n"; } else { prtw("WARNING: Begin $bgn, or end $end NOT defined!\n"); } } elsif ($typ == 1204) { # 1204 Edge active zone Identifies an edge as in a runway active zone. # arrival Active zone classification “arrival” or “departure” or “ils” # 16L,16C Runway(s) to which active zone refers Comma-separated list up to 4 runway identifies $uid = ${$ra}[1]; # classification $rwy1 = ${$ra}[2]; # runways } elsif ($typ == 1300) { # 1300 37.61743710 -122.39173209 30.00 gate heavy|jets G93 # NB: SEEMS BADLY DOCUMENTED BELOW!!!! # 1300 Taxi location Start or end point for taxi routes. Not linked to taxi routing network by edges (row code 1202) # 0 1300 Row code for taxi route start/end point 1300 # 1 47.43931757 Latitude of location in decimal degrees Eight decimal places supported # 2 -122.29806851 Longitude of location object in decimal degrees Eight decimal places supported # 3 88.78 Heading (true) of airplane positioned at this location Decimal degrees, true heading # 4 gate Type of location “gate”, “hangar”, “misc” or “tie-down” # 5 jets|turboprops Airplane types to that can use this location Pipe-separated list (“|”). Can include “heavy”, ”jets”, # “turboprops”, “props” and “helos” (or just “all” for all types) # 6 G93 Name $elat1 = ${$ra}[1]; $elon1 = ${$ra}[2]; $hdg = ${$ra}[3]; $uid = ${$ra}[4]; # Type of location “gate”, “hangar”, “misc” or “tie-down” $rwy2 = ${$ra}[5]; $name = ${$ra}[6]; $xg .= get_rectange( $elat1, $elon1, $hdg, $def_rad, $name ) if ($add_parkpos); if (VERB9()) { $line = "$typ"; $ralen = scalar @{$ra}; for ($j = 1; $j < $ralen; $j++) { $line .= " ".${$ra}[$j]; } prt("$line\n"); } set_bounds(\@bounds,$elat1,$elon1); } elsif ($typ == 15) { # 15 Startup location Startup locations for airplanes at an airport Should be converted to new row code 1300 # 0 15 Row code for a startup location 15 # 1 47.52926674 Latitude of startup location in decimal degrees Eight decimal places supported # 2 -122.29919589 Longitude of startup location in decimal degrees Eight decimal places supported # 3 304.16 Heading (true) of an aeroplane when positioned at startup location Two decimal places recommended # 4 A6 Run Up Name of startup location (list will be displayed in X-Plane for each airport) Short descriptive text string – ten characters or less $elat1 = ${$ra}[1]; $elon1 = ${$ra}[2]; $hdg = ${$ra}[3]; $name = ${$ra}[4]; # $name = join(' ', splice(@{$ra},4)); # Name $xg .= get_rectange( $elat1, $elon1, $hdg, $def_rad, $name ) if ($add_parkpos); if (VERB9()) { $line = "$typ"; $ralen = scalar @{$ra}; for ($j = 1; $j < $ralen; $j++) { $line .= " ".${$ra}[$j]; } prt("$line\n"); } set_bounds(\@bounds,$elat1,$elon1); } } $xg = get_bounds(\@bounds).$xg; if (length($xg_out) == 0) { $xg_out = $temp_dir.$PATH_SEP."temp.$icao.xg"; } rename_2_old_bak($xg_out); # rename any previous write2file($xg,$xg_out); prt("Airport XG written to $xg_out\n"); } sub find_icao($) { my $icao = shift; my ($line,$type,@arr,$len,$ver,$i,$cnt,$ra,$txt,$index); $ver = 0; $cnt = scalar @apt_lines; my @typ_counts = (); for ($i = 0; $i < $typmax; $i++) { push(@typ_counts,0); } my $rtc = \@typ_counts; for ($i = 0; $i < $cnt; $i++) { $line = $apt_lines[$i]; chomp $line; $line = trim_all($line); $len = length($line); if ($i == 1) { if ($line =~ /^(\d+)\s*/) { $ver = $1; } $i++; last; } } if ($ver == 0) { prtw("WARNING: Did NOT get a version from $aptdat\n"); } for (; $i < $cnt; $i++) { $line = $apt_lines[$i]; chomp $line; $line = trim_all($line); $len = length($line); next if ($len == 0); @arr = split(/\s+/,$line); $type = $arr[0]; last if ($type == 99); if (($type == 1)||($type == 16)||($type == 17)) { if ($icao eq $arr[4]) { # found our ICAO $index = set_type_count($type,$rtc,$line); my @a = @arr; push(@my_apt,\@a); $i++; for (; $i < $cnt; $i++) { $line = $apt_lines[$i]; chomp $line; $line = trim_all($line); $len = length($line); next if ($len == 0); @arr = split(/\s+/,$line); $type = $arr[0]; last if ($type == 99); last if (($type == 1)||($type == 16)||($type == 17)); $index = set_type_count($type,$rtc,$line); my @a2 = @arr; push(@my_apt,\@a2); } last; } } } if (length($out_file) == 0) { $out_file = $temp_dir.$PATH_SEP."temp.$icao.dat" } $cnt = scalar @my_apt; prt("Found $icao, $cnt items... to be written to $out_file\n"); $txt = ''; foreach $ra (@my_apt) { $line = join(" ",@{$ra}); $txt .= "$line\n"; } rename_2_old_bak($out_file); # rename any previous write2file($txt,$out_file); prt("Extract of $icao written to $out_file\n"); write_xg_file(\@my_apt); ########################################################### for ($i = 0; $i < $typmax; $i++) { $cnt = ${$rtc}[$i]; $txt = $typ_text{$i}; prt("$txt $cnt\n") if ($cnt); } ################################################################################# ### if (length($cmpdat)) { my ($cnt2); my @typ_count2 = (); for ($i = 0; $i < $typmax; $i++) { push(@typ_count2,0); } my $rtc2 = \@typ_count2; if ($cmpdat =~ /\.gz$/i) { if (!open(INF,"gzip -cdq $cmpdat|")) { pgm_exit(1,"Failed to 'open' file '$cmpdat'!\n"); } } else { if (!open( INF, "<$cmpdat") ) { pgm_exit(1,"Error: Unable to open $cmpdat\n"); } } my @cmp = ; close INF; $cnt = scalar @cmp; prt("Got $cnt lines to compare... from $cmpdat\n"); my @cmp_apt = (); $ver = 0; for ($i = 0; $i < $cnt; $i++) { $line = $cmp[$i]; chomp $line; $line = trim_all($line); $len = length($line); next if ($len == 0); if ($line =~ /^(\d+)\s+Generated/i) { $ver = $1; #prt("Version $ver\n"); prt("$line\n"); next; } elsif ($line =~ /^(\d+)\s+Version/i) { $ver = $1; #prt("Version $ver\n"); prt("$line\n"); next; } next if ($ver == 0); @arr = split(/\s+/,$line); $type = $arr[0]; last if ($type == 99); #$index = set_type_count($type,$rtc2,$line); #my @a = @arr; #push(@cmp_apt,\@a); if (($type == 1)||($type == 16)||($type == 17)) { if ($icao eq $arr[4]) { $index = set_type_count($type,$rtc2,$line); my @a = @arr; push(@cmp_apt,\@a); $i++; for (; $i < $cnt; $i++) { $line = $cmp[$i]; chomp $line; $line = trim_all($line); $len = length($line); next if ($len == 0); @arr = split(/\s+/,$line); $type = $arr[0]; last if ($type == 99); last if (($type == 1)||($type == 16)||($type == 17)); $index = set_type_count($type,$rtc2,$line); my @a2 = @arr; push(@cmp_apt,\@a2); } last; } } } $cnt = scalar @cmp_apt; if ($ver == 0) { prtw("WARNING: Failed to find 'version' line in $cmpdat file!\n"); } elsif ($cnt == 0) { prtw("WARNING: Failed to find $icao in $cmpdat file!\n"); } else { prt("Got $cnt $icao apt lines from $cmpdat\n"); for ($i = 0; $i < $typmax; $i++) { $cnt = ${$rtc}[$i]; $cnt2 = ${$rtc2}[$i]; $txt = $typ_text{$i}; prt("$txt $cnt vs $cnt2\n") if ($cnt || $cnt2); } $xg_out = $temp_dir.$PATH_SEP."temp.$icao.comp.xg"; write_xg_file(\@cmp_apt); prt("Done compare...\n"); } } } sub mycmp_acend_n1 { return -1 if (${$a}[1] < ${$b}[1]); return 1 if (${$a}[1] > ${$b}[1]); return 0; } sub list_apts_w_taxi() { my ($line,$type,@arr,$len,$ver,$i,$cnt,$ra,$txt,$icao,$aptcnt,$atyp); $ver = 0; $cnt = scalar @apt_lines; my %apts = (); $aptcnt = 0; for ($i = 0; $i < $cnt; $i++) { $line = $apt_lines[$i]; chomp $line; $line = trim_all($line); $len = length($line); next if ($len == 0); if ($line =~ /^(\d+)\s+Version/) { $ver = $1; #1234567890123456789012345678901234567890123456789 #1000 Version - data cycle 2013.10, build 20131335 prt(substr($line,0,49)."\n"); #prt("Version $ver\n"); next; } next if ($ver == 0); @arr = split(/\s+/,$line); $type = $arr[0]; last if ($type == 99); if (($type == 1)||($type == 16)||($type == 17)) { $aptcnt++; # land airports $icao = $arr[4]; $atyp = $type; } elsif ($type == 20) { if (defined $apts{$icao}) { $apts{$icao}++; } else { $apts{$icao} = 1; } } } @arr = sort keys %apts; $cnt = scalar @arr; prt("Found $aptcnt airports (1,16,17), $cnt with signs...\n"); $txt = "Found $aptcnt airports (1,16,17), $cnt with signs...\n"; my @signs = (); foreach $icao (@arr) { $cnt = $apts{$icao}; push(@signs,[$icao,$cnt]); ##prt("$icao $cnt\n"); } @signs = sort mycmp_acend_n1 @signs; prt("List ordered by sign count...\n"); foreach $ra (@signs) { $line = join("\t",@{$ra}); $txt .= "$line\n"; prt("$line\n"); } if (length($out_file) == 0) { $out_file = $temp_dir.$PATH_SEP."tempsigns.txt" } rename_2_old_bak($out_file); # rename any previous write2file($txt,$out_file); prt("List written to $out_file\n"); #$load_log = 1; #pgm_exit(1,"TEMP EXIT\n"); } ######################################### ### MAIN ### parse_args(@ARGV); load_apt_dat(); if ($writesigns) { list_apts_w_taxi(); } else { find_icao($in_icao); } pgm_exit(0,""); ######################################## sub need_arg { my ($arg,@av) = @_; pgm_exit(1,"ERROR: [$arg] must have a following argument!\n") if (!@av); } sub parse_args { my (@av) = @_; my ($arg,$sarg); my $verb = VERB2(); while (@av) { $arg = $av[0]; $cmd_line .= ' ' if (length($cmd_line)); $cmd_line .= $arg; if ($arg =~ /^-/) { $sarg = substr($arg,1); $sarg = substr($sarg,1) while ($sarg =~ /^-/); if (($sarg =~ /^h/i)||($sarg eq '?')) { give_help(); pgm_exit(0,"Help exit(0)"); } elsif ($sarg =~ /^v/) { if ($sarg =~ /^v.*(\d+)$/) { $verbosity = $1; } else { while ($sarg =~ /^v/) { $verbosity++; $sarg = substr($sarg,1); } } $verb = VERB2(); prt("Verbosity = $verbosity\n") if ($verb); } elsif ($sarg =~ /^l/) { if ($sarg =~ /^ll/) { $load_log = 2; } else { $load_log = 1; } prt("Set to load log at end. ($load_log)\n") if ($verb); } elsif ($sarg =~ /^o/) { need_arg(@av); shift @av; $sarg = $av[0]; $out_file = $sarg; prt("Set out file to [$out_file].\n") if ($verb); $cmd_line .= " $sarg"; } elsif ($sarg =~ /^x/) { need_arg(@av); shift @av; $sarg = $av[0]; $xg_out = $sarg; prt("Set xgraph out file to [$xg_out].\n") if ($verb); $cmd_line .= " $sarg"; } elsif ($sarg =~ /^p/) { $add_parkpos = 1; prt("Set to add parkpos to xgraph out file.\n") if ($verb); } elsif ($sarg =~ /^a/) { need_arg(@av); shift @av; $sarg = $av[0]; $aptdat = $sarg; if (-f $aptdat) { prt("Set apt.dat to [$aptdat].\n") if ($verb); } else { pgm_exit(1,"ERROR: Unable to 'stat' new $aptdat\n"); } $cmd_line .= " $sarg"; } elsif ($sarg =~ /^c/) { need_arg(@av); shift @av; $sarg = $av[0]; $cmpdat = $sarg; if (-f $cmpdat) { prt("Set compare dat to [$cmpdat].\n") if ($verb); } else { pgm_exit(1,"ERROR: Unable to 'stat' compare $cmpdat\n"); } $cmd_line .= " $sarg"; } elsif ($sarg =~ /^s/) { $writesigns = 1; } else { pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n"); } } else { $in_icao = $arg; prt("Set input to [$in_icao]\n") if ($verb); } shift @av; } if ($debug_on) { prtw("WARNING: DEBUG is ON!\n"); if ($debug_on == 1) { # $verbosity = 9; $add_parkpos = 1; # $load_log = 1; if (length($in_icao) == 0) { $in_icao = $def_file; prt("Set DEFAULT input to [$in_icao]\n"); } if ((length($cmpdat) == 0) && length($def_cmp) && (-f $def_cmp)) { $cmpdat = $def_cmp; prt("Set DEFAULT compare to [$cmpdat]\n"); } } else { $writesigns = 1; prt("Set DEFAULT to write signs\n"); } } if ((length($in_icao) == 0) && ($writesigns == 0)) { pgm_exit(1,"ERROR: No ICAO nor --signs found in command!\n"); } elsif ($writesigns && length($in_icao)) { pgm_exit(1,"Got BOTH an icao $in_icao, AND --signs!\nCan only do one or the other!\n"); } if (! -f $aptdat) { my $tmp = "F:/fgdata/Airports/apt.dat.gz"; if (-f $tmp) { prt("DEFAULT $aptdat failed. Using '$tmp'...\n"); $aptdat = $tmp; } else { pgm_exit(1,"ERROR: Unable to find in file [$aptdat]! Check name, location...\n"); } } } sub give_help { prt("$pgmname: version $VERS\n"); prt("Usage: $pgmname [options] ICAO\n"); prt("Options:\n"); prt(" --help (-h or -?) = This help, and exit 0.\n"); prt(" --verb[n] (-v) = Bump [or set] verbosity. (def=$verbosity)\n"); prt(" --load (-l) = Load LOG at end. ($outfile)\n"); prt(" --out (-o) = Write output to this file.\n"); prt(" --apt (-a) = Given name of apt.dat.gz to use.\n"); prt(" Default is '$aptdat'. ".(( -f $aptdat ) ? "ok" : "NOT FOUND!")."\n"); prt(" --comp (-c) = Compare extract to this file.\n"); prt(" --signs (-s) = Generate a list of airports with signs.\n"); prt(" --xg (-x) = Set xgraph output file name.\n"); if (length($xg_out) == 0) { $xg_out = $temp_dir.$PATH_SEP."temp.xg"; } prt(" Default xg out is '$xg_out'\n"); prt(" --parkpos (-p) = Add parking positions to xgraph output.\n"); } sub apt1000_spec() { my $txt = <= 1.00 1 Code defining the surface type (concrete, asphalt, etc) Integer value for a Surface Type Code (see below) 0 Code defining a runway shoulder surface type 0=no shoulder, 1=asphalt shoulder, 2=concrete shoulder 0.15 Runway smoothness (not used by X-Plane yet) 0.00 (smooth) to 1.00 (very rough). Default is 0.25 0 Runway centre-line lights 0=no centerline lights, 1=centre line lights 2 Runway edge lighting (also implies threshold lights) 0=no edge lights, 2=medium intensity edge lights 1 Auto-generate distance-remaining signs (turn off if created manually) 0=no auto signs, 1=auto-generate signs The following fields are repeated for each end of the runway 13L Runway number (eg. “31R”, “02”). Leading zeros are required. Two to three characters. Valid suffixes: “L”, “R” or “C” (or blank) 47.53801700 Latitude of runway end (on runway centerline) in decimal degrees Eight decimal places supported -122.307461 Longitude of runway end (on runway centerline) in decimal degrees Eight decimal places supported 73.15 Length of displaced threshold in metres (this is included in implied runway length) A displaced threshold will always be inside (between) the two runway ends Two decimal places (metres). Default is 0.00 0.00 Length of overrun/blast-pad in metres (not included in implied runway length) Two decimal places (metres). Default is 0.00 2 Code for runway markings (Visual, non-precision, precision) Integer value for Runway Marking Code (see below) 0 Code for approach lighting for this runway end Integer value for Approach Lighting Code (see below) 0 Flag for runway touchdown zone (TDZ) lighting 0=no TDZ lighting, 1=TDZ lighting 1 Code for Runway End Identifier Lights (REIL) 0=no REIL, 1=omni-directional REIL, 2=unidirectional REIL 101 Water runway 101 Row code for a water runway 101 49 Width of runway in metres Two decimal places recommended. Must be >= 1.00 1 Flag for perimeter buoys 0=no buoys, 1=render buoys The following fields are repeated for each end of the water runway 08 Runway number. Not rendered in X-Plane (it’s on water!) Valid suffixes are “L”, “R” or “C” (or blank) 35.04420911 Latitude of runway end (on runway centerline) in decimal degrees Eight decimal places supported -106.59855711 Longitude of runway end (on runway centerline) in decimal degrees Eight decimal places supported 102 Helipad 102 Row code for a helipad 101 H1 Designator for a helipad. Must be unique at an airport. Usually “H” suffixed by an integer (eg. “H1”, “H3”) 47.53918248 Latitude of helipad centre in decimal degrees Eight decimal places supported -122.30722302 Longitude of helipad centre in decimal degrees Eight decimal places supported 2.00 Orientation (true heading) of helipad in degrees Two decimal places recommended 10.06 Helipad length in metres Two decimal places recommended (metres), must be >=1.00 10.06 Helipad width in metres Two decimal places recommended (metres), must be >= 1.00 1 Helipad surface code Integer value for a Surface Type Code (see below) 0 Helipad markings 0 (other values not yet supported) 0 Code defining a helipad shoulder surface type 0=no shoulder, 1=asphalt shoulder, 2=concrete shoulder 0.25 Helipad smoothness (not used by X-Plane yet) 0.00 (smooth) to 1.00 (very rough). Default is 0.25 0 Helipad edge lighting 0=no edge lights, 1=yellow edge lights 110 Pavement (taxiways) Defines an arbitrary pavement shape 110 Row code for a pavement chunk header (must be followed by a set of nodes) 110 1 Code defining the surface type (concrete, asphalt, etc) Integer value for a Surface Type Code (see below) 0.25 Runway smoothness (not used by X-Plane yet) 0.00 (smooth) to 1.00 (very rough). Default is 0.25 150.29 Orientation (true degrees) of pavement texture ‘grain’ Two decimal places recommended A2 Exit Description of pavement chunk (not used by X-Plane) Text string 120 Linear feature Painted surface markings & light strings 130 Airport boundary Boundary for future terrain ‘flattening’ 120 Row code for a linear feature or airport boundary 120 or 130 Line B1 Description of feature or boundary (not used by X-Plane) Text string 111 Node Node (plain) 112 Node Node with Bezier control point 113 Node Node (close loop), to close boundary 114 Node Node (close loop) with Bezier control point 115 Node Node (end) to terminate a line 116 Node Node (end) with Bezier control point 112 Row code for a node. First node must follow an appropriate header row 111 thru 116 47.53752190 [All nodes] Latitude of node in decimal degrees Eight decimal places supported -122.30826710 [All nodes] Longitude of node in decimal degrees Eight decimal places supported 47.53757385 [112, 114, 116 only] Latitude of Bezier control point in decimal degrees Eight decimal places supported. Ignore for 111, 113, 115 -122.30824831 [112, 114, 116 only] Latitude of Bezier control point in decimal degrees Eight decimal places supported. Ignore for 111, 113, 115 3 [Not for 115 or 116] Code for painted line type on line segment starting at this node Integer Line Type Code (see below). Not for 115 or 116 102 [Not for 115 or 116] Code for lighting on line segment starting at this node Integer Line Type Code (see below). Not for 115 or 116 14 Viewpoint Maximum of one viewpoint for each airport 14 Row code for a viewpoint 14 47.52917900 Latitude of viewpoint in decimal degrees Eight decimal places supported -122.30434900 Longitude of viewpoint in decimal degrees Eight decimal places supported 100 Height (in feet) of viewpoint above ground level Integer 0 Code deprecated. Use ‘0’ 0 ATC Tower Name of viewpoint (not used by X-Plane) Descriptive text string (optional) 15 Startup location Startup locations for airplanes at an airport Should be converted to new row code 1300 15 Row code for a startup location 15 47.52926674 Latitude of startup location in decimal degrees Eight decimal places supported -122.29919589 Longitude of startup location in decimal degrees Eight decimal places supported 304.16 Heading (true) of an aeroplane when positioned at startup location Two decimal places recommended A6 Run Up Name of startup location (list will be displayed in X-Plane for each airport) Short descriptive text string – ten characters or less 18 Light beacon Maximum of one beacon for each airport 18 Row code for an airport light beacon 18 47.52920400 Latitude of beacon in decimal degrees Eight decimal places supported -122.30412800 Longitude of beacon in decimal degrees Eight decimal places supported 1 Code for type of light beacon. Determines colors of beacon. Integer Beacon Type Code (see below) BCN Name of viewpoint (not used by X-Plane) Descriptive text string (optional) 19 Windsock Multiple windsocks permitted for each airport 19 Row code for a windsock 19 47.53900921 Latitude of windsock in decimal degrees Eight decimal places supported -122.30868700 Longitude of windsock in decimal degrees Eight decimal places supported 1 Flag for windsock lighting 0=unlit, 1=illuminated WS Name of viewpoint (not used by X-Plane) Descriptive text string (optional) 20 Signs Taxiway signs or runway distance-remaining signs 20 Row code for a sign 20 47.54099177 Latitude of sign in decimal degrees Eight decimal places supported -122.31031317 Longitude of sign in decimal degrees Eight decimal places supported 235.71 Orientation of sign in true degrees (heading of someone looking at sign’s front) Two decimal places recommended 0 Reserved for future use. Ignore. 0 2 Code for sign size Integer Sign Size Code (see below) 31R-13L Text to be rendered on sign front and/or back Text string formatted by Sign Text Definition (see below) 21 Lighting objects VASI, PAPI, wig-wags, etc. 21 Row code for a lighting object 21 47.53666659 Latitude of lighting object in decimal degrees Eight decimal places supported -122.30585255 Longitude of lighting object in decimal degrees Eight decimal places supported 2 Code for type of lighting object Integer Lighting Object Code (see below) 150.28 Orientation of lighting object in true degrees (looking toward object) Two decimal places recommended 3.30 Visual glideslope angle in degrees Two decimal places. 0.00 if not required. Default is 3.00 13L Associated runway number (required for VASI/PAPI, etc) One to three characters PAPI-2L Description of lighting object (not used by X-Plane Short text string (optional) 1000 Traffic flow Arrival and departure traffic flows 1000 Row code for an arrival/departure traffic flow 1000 Calm and south flows Traffic flow name Descriptive name (max 50 characters) 1001 Traffic flow wind rule Zero or multiple wind rules permitted per flow 1001 Row code for a traffic flow wind rule 1001 KSEA METAR reporting station (may be a remote airport, eg KSEA for KBFI) ICAO code, up to 6 characters 000 Wind direction minimum (magnetic) 000 - 359 359 Wind direction maximum (magnetic) 000 - 359 5 Maximum wind speed. Use 999 for ‘all’ wind speeds. 0 - 999 1002 Traffic flow ceiling rule Zero or one ceiling rule permitted per flow 1002 Row code for a traffic flow ceiling rule 1002 KSEA METAR reporting station (may be a remote airport, eg KSEA for KBFI) ICAO code, up to 6 characters 0 Minimum reported ceiling in feet AGL at reporting station Positive integer 1003 Traffic flow visibility rule Zero or one visibility rule permitted per flow 1003 Row code for a traffic flow visibility rule 1003 KSEA METAR reporting station (may be a remote airport, eg KSEA for KBFI) ICAO code, up to 6 characters 0 Minimum reported visibility in statute miles Float (eg. “1.5”) 1004 Traffic time rule Zero or multiple time rules permitted per flow 1004 Row code for a traffic flow time rule 1004 0000 UTC time from which rule is valid 0000 - 2400 2400 UTC time at which rule ends 0000 - 2400 1100 Runway-in-use rule Multiple rules for each flow. First to ‘pass’ is used 1100 Row code for a runway-in-use rule 1100 34C Runway end identifier Two to three characters. Valid suffixes: “L”, “R” or “C” (or blank) 11920 Arrival or departure frequency Five digit integer, rounded DOWN where necessary arrivals Rule type (arrivals, departures) Pipe separated list (“|”). ‘arrivals’ and/or ‘departures’ jets|turboprops Airplane types to which rule applies Pipe-separated list (“|”). Can include “heavy”, ”jets”, “turboprops”, “props” and “helos” 181359 Heading range. Not used for arrivals. 000000 - 359359 341341 Initial departure heading range. Not used for arrivals. 000000 - 359359 Arrival 34C Rule name Descriptive name (max 50 characters) 1101 VFR pattern rule Zero or one VFR pattern rule permitted per flow 1101 Row code for a VFR traffic pattern 1101 34L Runway end identifier Two to three characters. Valid suffixes: “L”, “R” or “C” (or blank) left VFR traffic pattern direction “left” or “right” 1200 Taxi routing network (for readability only) 1201 Taxi routing node All nodes must be used in at least one edge 1201 Row code for taxi routing network node 1201 47.53752190 Latitude of node in decimal degrees Eight decimal places supported -122.30826710 Longitude of node in decimal degrees Eight decimal places supported both Usage of node in network (begin or end a taxi path, or both) “dest”, “init”, “both” or “junc” 5416 Node identifier (user-defined) Integer. Must be unique within scope of an airport. A_start Node name. Not currently used. String (max 16 characters) 1202 Taxi routing edge Segment in taxi routing network 1202 Row code for taxi routing network edge 1202 5416 Node identifier for start of edge Integer. Must refer to valid node (row code ‘1201’) 5417 Node identifier for end of edge Integer. Must refer to valid node (row code ‘1201’) twoway Edge can be used in both directions “twoway” or “oneway” taxiway Node is on a regular taxiway. If on “runway” a clearance is needed from ATC “taxiway” or “runway” A Taxiway identifier. Used to build ATC taxi clearances (eg. “.. .taxi via A, T, Q”) String. Taxiway or runway identifier (eg. “A” or “16L/34R”) 1204 Edge active zone Identifies an edge as in a runway active zone. 1204 Row code for an edge entering a runway active zone 1204 arrival Active zone classification “arrival” or “departure” or “ils” 16L,16C Runway(s) to which active zone refers Comma-separated list up to 4 runway identifies 1300 Taxi location Start or end point for taxi routes. Not linked to taxi routing network by edges (row code 1202) 1300 Row code for taxi route start/end point 1300 arrival Active zone classification for which ATC clearance may be needed to proceed “arrival” or “departure” or “ils” 47.43931757 Latitude of location in decimal degrees Eight decimal places supported -122.29806851 Longitude of location object in decimal degrees Eight decimal places supported 88.78 Heading (true) of airplane positioned at this location Decimal degrees, true heading gate Type of location “gate”, “hangar”, “misc” or “tie-down” jets|turboprops Airplane types to that can use this location Pipe-separated list (“|”). Can include “heavy”, ”jets”, “turboprops”, “props” and “helos” (or just “all” for all types) 50 ATC – Recorded AWOS, ASOS or ATIS 51 ATC – Unicom Unicom (US), CTAF (US), Radio (UK) 52 ATC – CLD Clearance Delivery 53 ATC – GND Ground 54 ATC – TWR Tower 55 ATC – APP Approach 56 ATC - DEP Departure 51 Row code for an ATC COM frequency 50 thru 56 (see above) 12775 Frequency in MHz x 100 (eg. use “12322” for 123.225MHz) Five digit integer, rounded DOWN where necessary ATIS Descriptive name (displayed on X-Plane charts) Short text string (recommend less than 10 characters) For Version 810 Runway or taxiway at an airport. 10 0 Identifies this as a data line for a runway or taxiway segment. 35.044209 1 Latitude (in decimal degrees) of runway or taxiway segment center. -106.598557 2 Longitude (in decimal degrees) of runway or taxiway segment center. 08x 3 Runway number (eg “25x” or “24R”). If there is no runway suffix (eg. “L”, “R”, “C” or "S"), then an “x” is used. “xxx” identifies the entry as a taxiway. Helipads at the same airport are numbered sequentially as "H1x", H2x". 90.439 4 True (not magnetic) heading of the runway in degrees. Must be between 0.00 and 360.00. 13749 5 Runway or taxiway segment length in feet. 1000.0000 6 Length of displaced threshold (1,000 feet) for runway 08 and for the reciprocal runway 26 (0 feet). The length of the reciprocal runway’s displaced threshold is expressed as the fractional part of this number. Take the runway 26 displaced threshold length (in feet) and divide it by 10,000, then add it to the displaced threshold length for runway 08. For example, for displaced threshold lengths of 543 feet and 1234 feet, the code would be 543.1234. Note that the displaced threshold length is included in the overall runway length but that the stopway length is excluded from the overall runway length. This code should be 0.0000 for taxiway segments. FYI, the displaced threshold is usually marked (in the real world) with long white arrows pointing toward the threshold. The displaced threshold is not available for use by aeroplanes landing, but may be used for take-off (in practice, if you use these last few feet of the runway for take-off, you are probably in serious trouble!). 0.1000 7 Length of stopway/blastpad/over-run at the approach end of runway 08 (0 feet) and for runway 26 (1,000 feet), using the same coding structure defined above. FYI, in the real world the stopway/blastpad/over-run is usually marked with large yellow chevrons, and aeroplane movements are not permitted. 150 8 Runway or taxiway segment width in feet. 252231 9 Runway or taxiway segment lighting codes. The first three digits ("252") define the lighting for the runway as seen when approached from the direction implied by the runway number (08 in our example). The final three ("231") define the lighting for the runway as seen when approached from the opposite end (26 in our example). In order, these codes represent: Runway end “A” (08): Visual approach path (VASI / PAPI etc.) lighting. Here, code 2 corresponds to a VASI. Runway end “A” (08): Runway lighting. Here, code 5 corresponds to TDZ lighting, which also implies centre-line lighting, REIL and edge lighting. Runway end “A” (08): Approach lighting. Here, code 2 corresponds to SSALS. Other runway end (26): Visual approach path (VASI / PAPI etc.) lighting. Here, code 2 corresponds to a VASI. Other runway end (26): Runway lighting. Here, code 3 corresponds to REIL, which also implies edge lighting. Other runway end (26): Approach lighting. Here, code 1 implies no approach lighting. 02 10 Runway or taxiway surface code for the runway or taxiway segment. The leading zero is optional - but I always use it to keep all the columns neatly lined up. 0 11 Runway shoulder code. These are only available in file version 701 and later. Here, code 0 implies that there is no runway shoulder. 3 12 Runway markings (the white painted markings on the surface of the runway. Here, code 3 implies precision runway markings (ie. there is an associated precision approach for the runway, either an ILS or MLS). 0.25 13 Runway smoothness. Used to cause bumps when taxying or rolling along the runway in X-Plane. It is on a scale of 0.0 to 1.0, with 0.0 being very smooth, and 1.0 being very, very rough. X-Plane determines a baseline smoothness based upon the runway surface type, and then uses this factor to determine the 'quality' of the runway surface. The default value is 0.25. 1 14 Runway has 'distance remaining' signs (0=no signs, 1=show signs). These are the white letters on a black background on little illuminated signs along a runway, indicating the number of thousands of feet of usable runway that remain. They are inappropriate at small airports or on most dirt, gravel or grass runways. 0300.0350 15 NEW for file version 810: Visual glideslope angle for the VASI or PAPI at each end of the runway (3.00 degrees for runway 08 and 3.50 degrees for runway 26). The angle for runway 08 is the whole part of this number divided by 100 (so "0300" becomes 3.00 degrees) and the angle for the reciprocal runway (26) is the fractional part of this number multiplied by 100 (so "0.0350" becomes 3.50 degrees). This data is required for runways, but is NOT necessary for taxiways. EOF return $txt } # eof - template.pl