#!/usr/bin/perl -w # NAME: solve.pl # AIM: Given a position, decide the track to take to best arrive at a 'circuit' # At present only fixed YGIL positions used, but added reading of apt.dat.gz to # find an ICAO, and should be able to do it for any airport # Needs LOTS of work to be fully 'generic' # Also an example of using ImageMagik 'convert' to generate a GIF image of the circuit. # Presently ONLY a DEBUG ON version! # Also note gzip reports a bloken pipe when closed before all lines read, but # the warning can be ignored. # 2020-01-27 - review - SEEMS BROKEN, using load, find apt!!! great GRAF gen, tempgraf.bat... # 2011-07-21 - initial cut (circa) # also see gencircuit.pl maybe some continuation... use strict; use warnings; use File::Basename; # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] ) use Cwd; use IO::Socket; use Term::ReadKey; use Time::HiRes qw( usleep gettimeofday tv_interval ); use Math::Trig; my $perl_dir = 'C:\GTools\perl'; unshift(@INC, $perl_dir); require 'lib_utils.pl' or die "Unable to load 'lib_utils.pl'! Check location and \@INC content.\n"; require 'fg_wsg84.pl' or die "Unable to load fg_wsg84.pl ...\n"; # NOT USED require "Bucket2.pm" or die "Unable to load Bucket2.pm ...\n"; # =============== # log file stuff our ($LF); my $pgmname = $0; if ($pgmname =~ /(\\|\/)/) { my @tmpsp = split(/(\\|\/)/,$pgmname); $pgmname = $tmpsp[-1]; } my $outfile = $perl_dir."\\temp.$pgmname.txt"; open_log($outfile); ### constants my $VER = "0.0.3 2020-01-27"; ###my $VER = "0.0.2 2014-11-29"; ###my $VER = "0.0.1 2010-09-11"; my $SGD_PI = 3.1415926535; my $SGD_DEGREES_TO_RADIANS = $SGD_PI / 180.0; my $SGD_RADIANS_TO_DEGREES = 180.0 / $SGD_PI; my $DEF_GS = 3; my $ATAN3 = atan( $DEF_GS * $SGD_DEGREES_TO_RADIANS ); # /** Feet to Meters */ my $SG_FEET_TO_METER = 0.3048; # /** Meters to Feet */ my $SG_METER_TO_FEET = 3.28083989501312335958; my $SG_NM_TO_METER = 1852; my $SG_METER_TO_NM = 0.0005399568034557235; # user variables my $load_log = 0; my $in_file = ''; my $bad_latlon = 200; my $in_lat = $bad_latlon; my $in_lon = $bad_latlon; my $graf_file = "tempgraf.gif"; my $graf_bat = $perl_dir."\\tempgraf.bat"; my $apt_icao = 'YGIL'; my $debug_on = 1; my $def_latlon = "-31.6,148.6"; my $def_lat2 = "-31.703"; my $def_lon2 = "148.6744"; my $def_lat3 = "-31.8145"; my $def_lon3 = "148.6374"; my $def_lat4 = "-31.756"; my $def_lon4 = "148.559"; my $def_lat5 = "-31.698961"; my $def_lon5 = "148.627853"; my $def_lat6 = "-31.701298"; my $def_lon6 = "148.6045"; my $stand_glide_degs = 3; # degrees my $stand_patt_alt = 1000; # feet my $stand_cross_nm = 2.1; # nm, but this will depend on the aircraft ### program variables my @warnings = (); my $cwd = cwd(); my $os = $^O; my @my_airport = (); my $a_gil_lat = -31.697287500; my $a_gil_lon = 148.636942500; my $a_dub_lat = -32.2174865; my $a_dub_lon = 148.57727; # rough Gil circuit my $tl_lat = -31.684063; my $tl_lon = 148.614120; my $bl_lat = -31.723495; my $bl_lon = 148.633003; my $br_lat = -31.716778; my $br_lon = 148.666992; my $tr_lat = -31.672960; my $tr_lon = 148.649139; my $use_pattern = 1; # adjust the above values to the computed circuit my $add_text_count = 1; # add text count my $try_dash_line = 1; my $switch_circuit = 0; # try the OTHER circuit 15 (def = 33) # my $YSDU_metar = "2011/07/22 14:00 YSDU 221400Z AUTO 15010KT 9999 // NCD 06/04 Q1020"; my $WIND_DIR_ABB = 'SSE'; my $WIND_DIR_DEG = 150; my $WIND_DIR_ENG = 'South/Southeast'; my $WIND_KTS = 8; # kt 9.2 mph # RUNWAY ARRAY OFFSETS my $RW_LEN = 0; my $RW_HDG = 1; my $RW_REV = 2; my $RW_TT1 = 3; my $RW_TT2 = 4; my $RW_CLAT = 5; my $RW_CLON = 6; my $RW_LLAT = 7; my $RW_LLON = 8; my $RW_RLAT = 9; my $RW_RLON = 10; my $RW_DONE = 11; # Len Hdg Rev Title RTit Ctr Lat Ctr Lon # 0 1 2 3 4 5 6 7 8 9 10 11 my @gil_patt = (); ##my @gil_rwys = ( [4204, 162.0, 0, '15', '33', -31.696928, 148.636404, 0, 0, 0, 0, 0 ] ); my @gil_rwys = ( [3984, 162.22, 0, '15', '33', -31.69656323, 148.6363057, 0, 0, 0, 0, 0 ] ); #my @gil_navs = ( ["", 0 ] ); my @gil_navs = (); #my @gil_rwys = ( [162.0, 4204], [93.0, 1902] ); my @dub_patt = ( [ ] ); my @dub_rwys = ( [5600, 53.61, 0, '05', '23', -32.218265, 148.576145, 0, 0, 0, 0, 0 ] ); my @dub_navs = ( ["VOR", 114.4], ["NDB", 251] ); my $OL_LAT = 0; my $OL_LON = 1; my $OL_NAV = 2; my $OL_RWY = 3; my $OL_PAT = 4; my %apt_locations = ( # key 0 1 2 3 4 # ICAO => [ Center LAT, LON NAVAIDS RUNWAYS CIRCUIT ] 'YGIL' => [$a_gil_lat, $a_gil_lon, \@gil_navs, \@gil_rwys, \@gil_patt ], 'YSDU' => [$a_dub_lat, $a_dub_lon, \@dub_navs, \@dub_rwys, \@dub_patt ] ); sub get_locations() { return \%apt_locations; } # DEBUG my $dbg_01 = 0; # show pattern values my $dbg_02 = 0; 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" ); } } 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 in_world_range($$) { my ($lat,$lon) = @_; if (($lat < -90) || ($lat > 90) || ($lon < -180) || ($lon > 180) ) { return 0; } return 1; } # from : http://compsci.ca/v3/viewtopic.php?t=6034 # if a point P is inside triangle ABC, then # Area PAB+Area PBC +Area PAC=Area ABC # notice that if P is on the edge of AB, BC, or CA, the above hold. # But effectively, one of the area PAB, PBC, PAC is 0 (so just make sure you check that). # if P is outside, the above equality does NOT hold... # for example, if A=(x1,y1) B=(x2,y2), C=(x3,y3) # Area= abs(x1*y2 + x2*y3 + x3*y1 - x1*y3 - x3*y2 -x2*y1 ) / 2 sub Triangle_Area($$$$$$) { my ($x1,$y1,$x2,$y2,$x3,$y3) = @_; return abs($x1*$y2 + $x2*$y3 + $x3*$y1 - $x1*$y3 - $x3*$y2 - $x2*$y1) / 2; } # Lots of stuff at : http://xlinux.nist.gov/dads/ # from : http://www.blackpawn.com/texts/pointinpoly/default.html # Point in triangle test # A common way to check if a point is in a triangle is to find # the vectors connecting the point to each of the triangle's # three vertices and sum the angles between those vectors. # If the sum of the angles is 2*pi then the point is inside the triangle, otherwise it is not. # CrossProduct - A x B = A B sin(&) # http://tutorial.math.lamar.edu/Classes/CalcII/CrossProduct.aspx # Two vector: A = (a1,a2,a3), B = (b1,b2,b3) # A X B = (a2b3 - a3b2, a3b1 - a1b3, a1b2 - a2b1) # from : http://schools-wikipedia.org/wp/c/Cross_product.htm # Consider two vectors, a = (1,2,3) and b = (4,5,6). # The cross product a × b is # a × b = (1,2,3) × (4,5,6) = ((2 × 6 - 3 × 5),-(1 × 6 - 3 × 4),+(1 × 5 - 2 × 4)) = (-3,6,-3). # Consider two vectors, a = (3,0,0) and b = (0,2,0). The cross product a × b is # a × b = (3,0,0) × (0,2,0) = ((0 × 0 - 0 × 2), (0 × 0 - 3 × 0), (3 × 2 - 0 × 0)) = (0,0,6). # from : http://msdn.microsoft.com/en-us/library/system.windows.vector.crossproduct.aspx # double CrossProduct( Vector & v1, Vector & v2 ) { # return (v1.X * v2.Y) - (v1.Y * v2.X); } # If the value of a1b2 - a2b1 is positive, then it points OUT of the page; # if its value is negative, then it points INTO the page. # from : http://web.mit.edu/wwmath/vectorc/3d/crossp.html #(v1, v2, v3) × (w1, w2, w3) = ( v2 w3 - v3 w2, v3 w1 - v1 w3, v1 w2 - v2 w1 ) # from : http://www.blackpawn.com/texts/pointinpoly/default.html # function SameSide(p1,p2, a,b) # cp1 = CrossProduct(b-a, p1-a) # cp2 = CrossProduct(b-a, p2-a) # if DotProduct(cp1, cp2) >= 0 then return true # else return false # function PointInTriangle(p, a,b,c) # if SameSide(p,a, b,c) and SameSide(p,b, a,c) # and SameSide(p,c, a,b) then return true # else return false # Barycentric Technique # With that in mind we can now describe any point on the plane as # P = A + u * (C - A) + v * (B - A) #Notice now that if u or v < 0 then we've walked in the wrong direction and must be outside the triangle. Also if u or v > 1 then we've walked too far in a direction and are outside the triangle. Finally if u + v > 1 then we've crossed the edge BC again leaving the triangle. #Given u and v we can easily calculate the point P with the above equation, but how can we go in the reverse direction and calculate u and v from a given point P? Time for some math! # P = A + u * (C - A) + v * (B - A) // Original equation # (P - A) = u * (C - A) + v * (B - A) // Subtract A from both sides # v2 = u * v0 + v * v1 // Substitute v0, v1, v2 for less writing # // We have two unknowns (u and v) so we need two equations to solve # // for them. Dot both sides by v0 to get one and dot both sides by # // v1 to get a second. # (v2) . v0 = (u * v0 + v * v1) . v0 # (v2) . v1 = (u * v0 + v * v1) . v1 # // Distribute v0 and v1 # v2 . v0 = u * (v0 . v0) + v * (v1 . v0) # v2 . v1 = u * (v0 . v1) + v * (v1 . v1) # // Now we have two equations and two unknowns and can solve one # // equation for one variable and substitute into the other. Or # // if you're lazy like me, fire up Mathematica and save yourself # // some handwriting. # Solve[v2.v0 == {u(v0.v0) + v(v1.v0), v2.v1 == u(v0.v1) + v(v1.v1)}, {u, v}] # u = ((v1.v1)(v2.v0)-(v1.v0)(v2.v1)) / ((v0.v0)(v1.v1) - (v0.v1)(v1.v0)) # v = ((v0.v0)(v2.v1)-(v0.v1)(v2.v0)) / ((v0.v0)(v1.v1) - (v0.v1)(v1.v0)) # OR # // Compute vectors # v0 = C - A # v1 = B - A # v2 = P - A #// Compute dot products # dot00 = dot(v0, v0) # dot01 = dot(v0, v1) # dot02 = dot(v0, v2) # dot11 = dot(v1, v1) # dot12 = dot(v1, v2) #// Compute barycentric coordinates #invDenom = 1 / (dot00 * dot11 - dot01 * dot01) # u = (dot11 * dot02 - dot01 * dot12) * invDenom # v = (dot00 * dot12 - dot01 * dot02) * invDenom # // Check if point is in triangle # return (u > 0) && (v > 0) && (u + v < 1) # from : http://mathforum.org/library/drmath/view/54735.html #How can I know whether a point P(x,y) is in a triangle with vertices #P1(x1,y1), P2(x2,y2), and P3(x3,y3)? #There are two possibilities: # 1. P is INSIDE or ON, # 2. P is OUTSIDE triangle P1P2P3. #If you draw a sketch and consider triangles, # PP1P2, PP2P3, PP3P1 #You will notice the following relationship in their areas. #If P is ON or INSIDE, # A(PP1P2) + A(PP2P3) + A(PP3P1) = A(P1P2P3) #If P is OUTSIDE then, # A(PP1P2) + A(PP2P3) + A(PP3P1) > A(P1P2P3) #Area of a triangle can be found using the determinant: # A = abs(1/2 * |x1 y1 1| ) # |x2 y2 1| # |x3 y3 1| #You can write a function in C++ that accepts the three points #(as Classes) or their coordinates and returns the area. #Finally if you have to distinguish between ON and INSIDE, put the point #in the equations of the three lines that form the sides. If one of them #is satisfied, P is ON a side. If two are satisfied, P is a vertex of #the triangle. sub Point_Inside_Triange($$$$$$$$) { my ($px,$py,$x1,$y1,$x2,$y2,$x3,$y3) = @_; my $a1 = Triangle_Area($px,$py,$x1,$y1,$x2,$y2); my $a2 = Triangle_Area($px,$py,$x2,$y2,$x3,$y3); my $a3 = Triangle_Area($px,$py,$x3,$y3,$x1,$y1); my $at = Triangle_Area($x1,$y1,$x2,$y2,$x3,$y3); my $sum = $a1 + $a2 + $a3; my $diff = abs($at - $sum); # my $minlat = 200; # my $maxlat = -200; # my $minlon = 200; # my $maxlon = -200; # $minlat = $x1 if ($x1 < $minlat); # $minlat = $x2 if ($x2 < $minlat); # $minlat = $x3 if ($x3 < $minlat); # $maxlat = $x1 if ($x1 > $maxlat); # $maxlat = $x2 if ($x2 > $maxlat); # $maxlat = $x3 if ($x3 > $maxlat); # $minlon = $y1 if ($y1 < $minlon); # $minlon = $y2 if ($y2 < $minlon); # $minlon = $y3 if ($y3 < $minlon); # $maxlon = $y1 if ($y1 > $maxlon); # $maxlon = $y2 if ($y2 > $maxlon); # $maxlon = $y3 if ($y3 > $maxlon); # prt("Bound: TL $maxlat,$minlon, BR $minlat,$maxlon\n"); # if (($px >= $minlat)&&($px <= $maxlat)&&($py >= $minlon)&&($py <= $maxlon)) { # prt("Point $px,$py is in bounding box!\n"); # } else { # prt("Point $px,$py is NOT in bounding box!\n"); # } # prt("Areas: if (($a1 + $a2 + $a3) > $at) \n"); # prt("Total $sum, diff $diff\n"); #if (($a1 + $a2 + $a3) > $at) { return 0; } # THIS FAILED in some cases return 1 if ($diff < 1.0E-010); # take this SMALL value as EQUAL !!! return 0; } # ============================================================ # # SimGear Services, rendered in perl # ============================================================ # # dot(const SGVec3& v1, const SGVec3& v2) # { return v1(0)*v2(0) + v1(1)*v2(1) + v1(2)*v2(2); } # Given 2 Vectors3, return the dot product sub scalar_dot_product($$) { my ($rv1,$rv2) = @_; return ${$rv1}[0] * ${$rv2}[0] + ${$rv1}[1] * ${$rv2}[1] + ${$rv1}[2] * ${$rv2}[2]; } # In dimension 2, the dot product of vectors [a,b] and [c,d] is ac + bd sub scalar_dot_product2($$$$) { my ($v1x,$v1y,$v2x,$v2y) = @_; return ($v1x * $v2x) + ($v1y * $v2y); } # The euclidean norm of the vector, that is what most people call length # norm(const SGVec3& v) # { return sqrt(dot(v, v)); } # Given a Vector3, return length sub norm_vector_length($) { my ($rv) = @_; return sqrt(scalar_dot_product($rv, $rv)); } sub norm_vector_length2($$) { my ($vx,$vy) = @_; return sqrt(scalar_dot_product2($vx, $vy, $vx, $vy)); } 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 set_int_stg($) { my $r = shift; ${$r} = int(${$r} + 0.5); } sub set_int_dist_stg5($) { my $r = shift; set_int_stg($r); my $r5 = sprintf("%5d",${$r}); ${$r} = $r5; } sub set_hdg_stg($) { my ($rh) = @_; my $hdg = ${$rh}; $hdg = 360 if ($hdg == 0); # always replace 000 with 360 ;=)) $hdg = sprintf("%03d",int($hdg+0.5)); ${$rh} = $hdg; } sub set_lat_stg($) { my ($rl) = @_; ${$rl} = sprintf("%2.7f",${$rl}); } sub set_lon_stg($) { my ($rl) = @_; ${$rl} = sprintf("%3.7f",${$rl}); } sub set_decimal1_stg($) { my $r = shift; ${$r} = int((${$r} + 0.05) * 10) / 10; ${$r} = "0.0" if (${$r} == 0); ${$r} .= ".0" if !(${$r} =~ /\./); } sub get_dist_stg_nm($) { my ($dist) = @_; my $nm = $dist * $SG_METER_TO_NM; set_decimal1_stg(\$nm); $nm .= "nm"; return $nm; } sub normalised_hdg($) { my $hdg = shift; $hdg += 360 if ($hdg < 0); $hdg -= 360 if ($hdg >= 360); return $hdg; } sub set_hdg_stg3($) { my $r = shift; set_int_stg($r); my $r3 = sprintf("%3d",${$r}); ${$r} = $r3; } # ============================================ sub show_distance_heading($$$$) { my ($lat1,$lon1,$lat2,$lon2) = @_; my ($az1,$az2,$dist); fg_geo_inverse_wgs_84 ($lat1,$lon1,$lat2,$lon2,\$az1,\$az2,\$dist); $dist = get_dist_stg_nm($dist); set_hdg_stg(\$az1); prt("Is $dist, on heading $az1\n"); } sub show_rw_patt($$) { my ($key,$rpatts) = @_; my $cnt = scalar @{$rpatts}; prt("Display of $cnt patterns/circuits for $key...\n"); my ($i,$lat1,$lon1,$lat2,$lon2,$j); for ($i = 0; $i < $cnt; $i++) { for ($j = 0; $j < 8; $j += 2) { $lat1 = ${$rpatts}[$i][$j+0]; $lon1 = ${$rpatts}[$i][$j+1]; if ($j == 6) { $lat2 = ${$rpatts}[$i][0]; $lon2 = ${$rpatts}[$i][1]; } else { $lat2 = ${$rpatts}[$i][$j+2]; $lon2 = ${$rpatts}[$i][$j+3]; } prt("$i:$j: $lat1,$lon1 $lat2,$lon2\n"); show_distance_heading($lat1,$lon1,$lat2,$lon2); } } } sub set_runway_ends_and_patt($$$$) { my ($rrwys,$i,$key,$rpatts) = @_; # set ENDS of runway my $rlen = ${$rrwys}[$i][$RW_LEN]; my $rhdg = ${$rrwys}[$i][$RW_HDG]; my $clat = ${$rrwys}[$i][$RW_CLAT]; my $clon = ${$rrwys}[$i][$RW_CLON]; my $rty1 = ${$rrwys}[$i][$RW_TT1]; my $rty2 = ${$rrwys}[$i][$RW_TT2]; my $rwlen2 = ($rlen * $SG_FEET_TO_METER) / 2; my ($elat1,$elon1,$eaz1,$elat2,$elon2,$eaz2); my $hdgr = $rhdg + 180; $hdgr -= 360 if ($hdgr >= 360); ${$rrwys}[$i][$RW_REV] = $hdgr; fg_geo_direct_wgs_84( $clat, $clon, $rhdg, $rwlen2, \$elat1, \$elon1, \$eaz1 ); fg_geo_direct_wgs_84( $clat, $clon, $hdgr, $rwlen2, \$elat2, \$elon2, \$eaz2 ); ${$rrwys}[$i][$RW_LLAT] = $elat1; ${$rrwys}[$i][$RW_LLON] = $elon1; ${$rrwys}[$i][$RW_RLAT] = $elat2; ${$rrwys}[$i][$RW_RLON] = $elon2; ${$rrwys}[$i][$RW_DONE] = $i + 1; my ($az1,$az2,$dist); fg_geo_inverse_wgs_84 ($elat1,$elon1,$elat2,$elon2,\$az1,\$az2,\$dist); $dist = $dist * $SG_METER_TO_FEET; set_int_stg(\$az1); set_int_stg(\$az2); set_int_stg(\$dist); # init: YSDU: 23: -32.2136987804606,148.583432501246 05: -32.2228307960945,148.568856770273 234 5600 54 vs 53.61 5600 # init: YGIL: 33: -31.7024233216057,148.638492502638 15: -31.6914326394609,148.634315743548 342 4204 162 vs 162 4204 #prt("init: $key: $rty2: $elat1,$elon1 $az1 $rty1: $elat2,$elon2 $az1 $dist $az2 vs $rhdg $rlen\n"); prt("init:$i: $key: $rty2:$az1: $elat1,$elon1 $rty1:$az2: $elat2,$elon2\n"); # We have the RUNWAY ends - now extend out to first turn to crosswind leg, and turn to final # but by how MUCH - ok decide from runway end, out to where it is a 3 degree glide from 1000 feet $dist = ($stand_patt_alt * $SG_FEET_TO_METER) / tan($stand_glide_degs * $SGD_DEGREES_TO_RADIANS); my ($plat11,$plon11,$plat12,$plon12,$plat13,$plon13,$paz1); my ($plat21,$plon21,$plat22,$plon22,$plat23,$plon23,$paz2); my ($hdg1L,$hdg1R,$crossd); fg_geo_direct_wgs_84( $clat, $clon, $rhdg, $rwlen2+$dist, \$plat11, \$plon11, \$paz1 ); fg_geo_direct_wgs_84( $clat, $clon, $hdgr, $rwlen2+$dist, \$plat21, \$plon21, \$paz2 ); $hdg1L = normalised_hdg($rhdg - 90); $hdg1R = normalised_hdg($rhdg + 90); $crossd = $stand_cross_nm * $SG_NM_TO_METER; # ON $rhdg to $elat1, $elon1 to ... turn point, go LEFT and to get NEXT points, this end fg_geo_direct_wgs_84( $plat11, $plon11, $hdg1L, $crossd, \$plat12, \$plon12, \$paz1 ); fg_geo_direct_wgs_84( $plat21, $plon21, $hdg1L, $crossd, \$plat13, \$plon13, \$paz1 ); # from the turn point, go LEFT and RIGHT to get NEXT points, this other end fg_geo_direct_wgs_84( $plat21, $plon21, $hdg1R, $crossd, \$plat22, \$plon22, \$paz2 ); fg_geo_direct_wgs_84( $plat11, $plon11, $hdg1R, $crossd, \$plat23, \$plon23, \$paz2 ); if ($use_pattern && ($key eq 'YGIL')) { if ($switch_circuit) { # At YGIL, this is a 15 circuit (the prevailing wind! SSE... $tl_lat = $plat12; $tl_lon = $plon12; $bl_lat = $plat13; $bl_lon = $plon13; $br_lat = $plat21; $br_lon = $plon21; $tr_lat = $plat11; $tr_lon = $plon11; } else { # At YGIL, this is a 33 circuit $tl_lat = $plat22; #-31.684063; $tl_lon = $plon22; #148.614120; $bl_lat = $plat23; #-31.723495; $bl_lon = $plon23; #148.633003; $br_lat = $plat11; #-31.716778; $br_lon = $plon11; #148.666992; $tr_lat = $plat21; #-31.672960; $tr_lon = $plon21; #148.649139; } prt("Set pattern as the rectangle...\n"); } if ($dbg_01) { # now we have 4 points, either side of the runway prt("On $rhdg, at $plat11,$plon11 turn $hdg1L to $plat12,$plon12\n"); show_distance_heading($plat11,$plon11,$plat12,$plon12); # This is the LONG downwind side 12 to 13 prt("On $hdg1L at $plat12,$plon12, turn $hdgr to $plat13,$plon13\n"); show_distance_heading($plat12,$plon12,$plat13,$plon13); prt("On $hdgr at $plat13,$plon13 turn $hdg1R to $plat21,$plon21\n"); show_distance_heading($plat13,$plon13,$plat21,$plon21); prt("On $hdg1R at $plat21,$plon21 turn $rhdg to $elat1,$elon1\n"); show_distance_heading($plat21,$plon21,$elat1,$elon2); prt("\n"); #E.G. for YGIL - TO 15 #On 162, at -31.7523059488988,148.65746239832 turn 72 to -31.7414611993009,148.696497359091 #Is 2.1nm, on heading 72 #On 72 at -31.7414611993009,148.696497359091, turn 342 to -31.630701184372,148.654359238954 #Is 7.0nm, on heading 342 #On 342 at -31.630701184372,148.654359238954 turn 252 to -31.6415460967825,148.615370603846 #Is 2.1nm, on heading 252 #On 252 at -31.6415460967825,148.615370603846 turn 162 to -31.7024233216057,148.638492502638 #Is 3.8nm, on heading 165 prt("On $hdgr at $plat21,$plon21 turn $hdg1R to $plat22,$plon22\n"); show_distance_heading($plat21,$plon21,$plat22,$plon22); # This is the LONG downwind side 22 to 23 prt("On $hdg1R at $plat22,$plon22 turn $rhdg to $plat23,$plon23\n"); show_distance_heading($plat22,$plon22,$plat23,$plon23); prt("On $rhdg at $plat23,$plon23 turn $hdg1L to $plat11,$plon11\n"); show_distance_heading($plat23,$plon23,$plat11,$plon11); prt("On $hdg1L at $plat11,$plon11 turn $hdgr to $elat2,$elon2\n"); show_distance_heading($plat11,$plon11,$elat2,$elon2); prt("\n"); #E.G. for YGIL, TO 33 #On 342 at -31.6415460967825,148.615370603846 turn 252 to -31.6523790808638,148.576372922039 #Is 2.1nm, on heading 252 #On 252 at -31.6523790808638,148.576372922039 turn 162 to -31.7631387187979,148.618418340898 #Is 7.0nm, on heading 162 #On 162 at -31.7631387187979,148.618418340898 turn 72 to -31.7523059488988,148.65746239832 #Is 2.1nm, on heading 72 #On 72 at -31.7523059488988,148.65746239832 turn 342 to -31.6914326394609,148.634315743548 #Is 3.8nm, on heading 342 } @{$rpatts} = (); # add notional RIGHT side circuit first push(@{$rpatts}, [$plat11,$plon11,$plat12,$plon12,$plat13,$plon13,$plat21,$plon21,$clat,$clon,$rlen,$rhdg]); # then notional LEFT size circuit push(@{$rpatts}, [$plat21,$plon21,$plat22,$plon22,$plat23,$plon23,$plat11,$plon11,$clat,$clon,$rlen,$hdgr]); } sub init_runway_array() { my $rl = get_locations(); my ($key,$i,$cnt,$rrwys,$rpatts); foreach $key (keys %{$rl}) { $rrwys = ${$rl}{$key}[$OL_RWY]; $rpatts = ${$rl}{$key}[$OL_PAT]; $cnt = scalar @{$rrwys}; for ($i = 0; $i < $cnt; $i++) { set_runway_ends_and_patt($rrwys,$i,$key,$rpatts); } } if ($dbg_02) { foreach $key (keys %{$rl}) { $rpatts = ${$rl}{$key}[$OL_PAT]; show_rw_patt($key,$rpatts); } } # pgm_exit(1,"Temp exit"); } sub get_runways_and_pattern($$) { my ($rh,$key) = @_; my $rl = get_locations(); my ($rrwys,$rpatts); if (defined ${$rl}{$key}) { $rrwys = ${$rl}{$key}[$OL_RWY]; $rpatts = ${$rl}{$key}[$OL_PAT]; ${$rh}{'runways'} = $rrwys; ${$rh}{'pattern'} = $rpatts; ${$rh}{'airport'} = $key; } else { pgm_exit(1,"ERROR: Key [$key] NOT in locations!\n"); } } # ============================================ sub set_circuit_values($$) { my ($rh,$show) = @_; my ($az1,$az2,$dist); if ($show) { #prt("Set, show circuit...\nTL ".${$rh}{'tl_lat'}.",".${$rh}{'tl_lon'}."\nBL ". # ${$rh}{'bl_lat'}.",".${$rh}{'bl_lon'}."\nBR ". # ${$rh}{'br_lat'}.",".${$rh}{'br_lon'}."\nTR ". # ${$rh}{'tr_lat'}.",".${$rh}{'tr_lon'}."\n"); my ($tllat,$tllon,$bllat,$bllon,$brlat,$brlon,$trlat,$trlon); $tllat = ${$rh}{'tl_lat'}; $tllon = ${$rh}{'tl_lon'}; $bllat = ${$rh}{'bl_lat'}; $bllon = ${$rh}{'bl_lon'}; $brlat = ${$rh}{'br_lat'}; $brlon = ${$rh}{'br_lon'}; $trlat = ${$rh}{'tr_lat'}; $trlon = ${$rh}{'tr_lon'}; set_lat_stg(\$tllat); set_lat_stg(\$bllat); set_lat_stg(\$brlat); set_lat_stg(\$trlat); set_lon_stg(\$tllon); set_lon_stg(\$bllon); set_lon_stg(\$brlon); set_lon_stg(\$trlon); prt("Set, show circuit...\nTL $tllat,$tllon\nBL ". "$bllat,$bllon\nBR ". "$brlat,$brlon\nTR ". "$trlat,$trlon\n"); } # anti-clockwise around circuit fg_geo_inverse_wgs_84 (${$rh}{'tl_lat'},${$rh}{'tl_lon'},${$rh}{'bl_lat'},${$rh}{'bl_lon'},\$az1,\$az2,\$dist); ${$rh}{'l1_az1'} = $az1; ${$rh}{'l1_az2'} = $az2; ${$rh}{'l1_dist'} = $dist; if ($show) { set_int_dist_stg5(\$dist); set_hdg_stg3(\$az1); prt("l1 $dist m, on $az1 (tl2bl)\n"); } fg_geo_inverse_wgs_84 (${$rh}{'bl_lat'},${$rh}{'bl_lon'},${$rh}{'br_lat'},${$rh}{'br_lon'},\$az1,\$az2,\$dist); ${$rh}{'l2_az1'} = $az1; ${$rh}{'l2_az2'} = $az2; ${$rh}{'l2_dist'} = $dist; if ($show) { set_int_dist_stg5(\$dist); set_hdg_stg3(\$az1); prt("l2 $dist m, on $az1 (bl2br)\n"); } fg_geo_inverse_wgs_84 (${$rh}{'br_lat'},${$rh}{'br_lon'},${$rh}{'tr_lat'},${$rh}{'tr_lon'},\$az1,\$az2,\$dist); ${$rh}{'l3_az1'} = $az1; ${$rh}{'l3_az2'} = $az2; ${$rh}{'l3_dist'} = $dist; if ($show) { set_int_dist_stg5(\$dist); set_hdg_stg3(\$az1); prt("l3 $dist m, on $az1 (br2tr)\n"); } fg_geo_inverse_wgs_84 (${$rh}{'tr_lat'},${$rh}{'tr_lon'},${$rh}{'tl_lat'},${$rh}{'tl_lon'},\$az1,\$az2,\$dist); ${$rh}{'l4_az1'} = $az1; ${$rh}{'l4_az2'} = $az2; ${$rh}{'l4_dist'} = $dist; if ($show) { set_int_dist_stg5(\$dist); set_hdg_stg3(\$az1); prt("l4 $dist m, on $az1 (tr2tl)\n"); } } sub set_most_values_plus($$$$$$) { my ($rh,$show,$u_lat,$u_lon,$t_lat,$t_lon) = @_; if (!defined ${$rh}{'user_points'}) { ${$rh}{'user_points'} = []; } my $ru = ${$rh}{'user_points'}; push(@{$ru}, [$u_lat,$u_lon,$t_lat,$t_lon]); } sub set_min_max($$$$$$) { my ($rmaxlat,$rminlat,$rmaxlon,$rminlon,$lat,$lon) = @_; ${$rmaxlat} = $lat if ($lat > ${$rmaxlat}); ${$rminlat} = $lat if ($lat < ${$rminlat}); ${$rmaxlon} = $lon if ($lon > ${$rmaxlon}); ${$rminlon} = $lon if ($lon < ${$rminlon}); } sub add_img_arrow($$$$) { my ($rh,$clat,$clon,$az) = @_; my ($w_ind1,$h_ind1); my ($maxlat,$minlat,$maxlon,$minlon); my ($w_dpp,$h_dpp); my ($sqwid,$sqhgt,$rmsg); #my $adj = 20; # works for some, not others??? my $adj = 0; $rmsg = ${$rh}{'rmsg'}; $maxlat = ${$rh}{'max_lat'}; $minlat = ${$rh}{'min_lat'}; $maxlon = ${$rh}{'max_lon'}; $minlon = ${$rh}{'min_lon'}; $w_dpp = ${$rh}{'w_dpp'}; $h_dpp = ${$rh}{'h_dpp'}; $sqwid = ${$rh}{'sq_wid_adj'}; $sqhgt = ${$rh}{'sq_hgt_adj'}; $w_ind1 = int((($clon - $minlon) * $w_dpp) + 0.5); # get degrees/pixels from left edge $h_ind1 = int((($clat - $minlat) * $h_dpp) + 0.5); # get degrees/pixels from bottom edge $h_ind1 = $sqhgt - $h_ind1; $h_ind1 += 1 if ($h_ind1 == 0); $w_ind1 += 1 if ($w_ind1 == 0); $h_ind1 -= 1 if ($h_ind1 == $sqhgt); $w_ind1 -= 1 if ($w_ind1 == $sqwid); # Note that the direction of rotate is clock-wise. # This may seem illogical mathematically, until you realise # that the image coordinate system is relative to the top-left # of the image instead of the mathematical norm of the bottom-left. # The result is the angle of rotation is the oppisite of what you may # logically expect. This is important to keep in mind when dealing # with any form of image rotation, compared to a mathematical rotation. my $rot = normalised_hdg(int($az+0.5) - (90+$adj)); my $arrow_head = "path 'M 0,0 l -15,-5 +5,+5 -5,+5 +15,-5 z'"; my $msg = "-draw \"push graphic-context stroke blue fill skyblue translate $w_ind1,$h_ind1 rotate $rot "; $msg .= "$arrow_head pop graphic-context\" "; ${$rmsg} .= $msg; } sub add_img_circle($$$) { my ($rh,$clat,$clon) = @_; my $rmsg = ${$rh}{'rmsg'}; my ($w_ind1,$h_ind1); my ($maxlat,$minlat,$maxlon,$minlon); my ($w_dpp,$h_dpp); my ($sqwid,$sqhgt); $maxlat = ${$rh}{'max_lat'}; $minlat = ${$rh}{'min_lat'}; $maxlon = ${$rh}{'max_lon'}; $minlon = ${$rh}{'min_lon'}; $w_dpp = ${$rh}{'w_dpp'}; $h_dpp = ${$rh}{'h_dpp'}; $sqwid = ${$rh}{'sq_wid_adj'}; $sqhgt = ${$rh}{'sq_hgt_adj'}; $w_ind1 = int((($clon - $minlon) * $w_dpp) + 0.5); # get degrees/pixels from left edge $h_ind1 = int((($clat - $minlat) * $h_dpp) + 0.5); # get degrees/pixels from bottom edge $h_ind1 = $sqhgt - $h_ind1; $h_ind1 += 1 if ($h_ind1 == 0); $w_ind1 += 1 if ($w_ind1 == 0); $h_ind1 -= 1 if ($h_ind1 == $sqhgt); $w_ind1 -= 1 if ($w_ind1 == $sqwid); ${$rmsg} .= "-draw \"circle ".($w_ind1-1).",$h_ind1 ".($w_ind1+1).",$h_ind1\" "; } sub add_arrow_to_center($$$$$) { my ($rh,$plat12,$plon12,$plat13,$plon13) = @_; my ($clat,$clon,$az1,$az2,$dist); # get heading and distance fg_geo_inverse_wgs_84($plat12,$plon12,$plat13,$plon13,\$az1,\$az2,\$dist); # get center point fg_geo_direct_wgs_84($plat12,$plon12,$az1,$dist/2,\$clat,\$clon,\$az2); add_img_circle($rh,$clat,$clon); add_img_arrow($rh,$clat,$clon,$az1); } sub add_img_line($$$$$$) { my ($rh,$lat1,$lon1,$lat2,$lon2,$type) = @_; my ($w_ind1,$h_ind1,$w_ind2,$h_ind2); my ($maxlat,$minlat,$maxlon,$minlon); my ($w_dpp,$h_dpp); my ($sqwid,$sqhgt,$draw); my $rmsg = ${$rh}{'rmsg'}; $maxlat = ${$rh}{'max_lat'}; $minlat = ${$rh}{'min_lat'}; $maxlon = ${$rh}{'max_lon'}; $minlon = ${$rh}{'min_lon'}; $w_dpp = ${$rh}{'w_dpp'}; $h_dpp = ${$rh}{'h_dpp'}; $sqwid = ${$rh}{'sq_wid_adj'}; $sqhgt = ${$rh}{'sq_hgt_adj'}; $w_ind1 = int((($lon1 - $minlon) * $w_dpp) + 0.5); # get degrees/pixels from left edge $h_ind1 = int((($lat1 - $minlat) * $h_dpp) + 0.5); # get degrees/pixels from bottom edge $h_ind1 = $sqhgt - $h_ind1; $h_ind1 += 1 if ($h_ind1 == 0); $w_ind1 += 1 if ($w_ind1 == 0); $h_ind1 -= 1 if ($h_ind1 == $sqhgt); $w_ind1 -= 1 if ($w_ind1 == $sqwid); # ${$rmsg} .= "-draw \"circle ".($w_ind1-1).",$h_ind1 ".($w_ind1+1).",$h_ind1\" "; $w_ind2 = int((($lon2 - $minlon) * $w_dpp) + 0.5); # get degrees/pixels from left edge $h_ind2 = int((($lat2 - $minlat) * $h_dpp) + 0.5); # get degrees/pixels from bottom edge $h_ind2 = $sqhgt - $h_ind2; #$w_ind2 += 1 if ($w_ind2 == 0); $h_ind2 += 1 if ($h_ind2 == 0); $w_ind2 += 1 if ($w_ind2 == 0); $h_ind2 -= 1 if ($h_ind2 == $sqhgt); $w_ind2 -= 1 if ($w_ind2 == $sqwid); # ${$rmsg} .= "-draw \"circle ".($w_ind2-1).",$h_ind2 ".($w_ind2+1).",$h_ind2\" "; # -draw "stroke-dasharray 5 3 path 'M 10,20 L 90,20'" #if ($try_dash_line) { if ($type == 1) { $draw = "-draw \"stroke-dasharray 5 3 path 'M $w_ind1,$h_ind1 L $w_ind2,$h_ind2'\" "; } else { $draw = "-draw \"line $w_ind1,$h_ind1 $w_ind2,$h_ind2\" "; } ${$rmsg} .= $draw; } sub draw_text_at_pos($$$$$) { my ($rh,$x_txt,$y_txt,$text,$flag) = @_; my $rmsg = ${$rh}{'rmsg'}; # -draw "text 25,65 'Anthony'" my $msg = "-draw \"text $x_txt,$y_txt '$text'\" "; ${$rh}{'x_txt'} = $x_txt; ${$rh}{'y_txt'} = $y_txt; ${$rmsg} .= $msg; } sub draw_text_at_latlon($$$$$) { my ($rh,$lat,$lon,$text,$flag) = @_; my ($w_ind1,$h_ind1); my ($maxlat,$minlat,$maxlon,$minlon); my ($w_dpp,$h_dpp); my ($sqwid,$sqhgt,$draw); my ($x_txt,$y_txt); my $len = length($text); return if ($len == 0); my $w_adj = $len * 3; my $h_adj = 5; $maxlat = ${$rh}{'max_lat'}; $minlat = ${$rh}{'min_lat'}; $maxlon = ${$rh}{'max_lon'}; $minlon = ${$rh}{'min_lon'}; $w_dpp = ${$rh}{'w_dpp'}; $h_dpp = ${$rh}{'h_dpp'}; $sqwid = ${$rh}{'sq_wid_adj'}; $sqhgt = ${$rh}{'sq_hgt_adj'}; $w_ind1 = int((($lon - $minlon) * $w_dpp) + 0.5); # get degrees/pixels from left edge $h_ind1 = int((($lat - $minlat) * $h_dpp) + 0.5); # get degrees/pixels from bottom edge $h_ind1 = $sqhgt - $h_ind1; $x_txt = $w_ind1 + 3; $y_txt = $h_ind1 + 4; # modify the position to within the page $x_txt += 3 if (($x_txt - 3) <= 0); $x_txt -= 3 if (($x_txt + 3) >= $sqwid); $y_txt -= 5 if (($y_txt + 5) >= $sqhgt); $y_txt += 5 if (($y_txt - 5) <= 0); draw_text_at_pos($rh,$x_txt,$y_txt,$text,$flag); } sub paint_user_points($$) { my ($rh,$show) = @_; my ($tllat,$tllon,$bllat,$bllon,$brlat,$brlon,$trlat,$trlon); $tllat = ${$rh}{'tl_lat'}; $tllon = ${$rh}{'tl_lon'}; $bllat = ${$rh}{'bl_lat'}; $bllon = ${$rh}{'bl_lon'}; $brlat = ${$rh}{'br_lat'}; $brlon = ${$rh}{'br_lon'}; $trlat = ${$rh}{'tr_lat'}; $trlon = ${$rh}{'tr_lon'}; my ($minlat,$maxlat,$minlon,$maxlon); my ($latdegs,$londegs,$sqwid,$sqhgt); $minlat = $bad_latlon; $maxlat = -$bad_latlon; $minlon = $bad_latlon; $maxlon = -$bad_latlon; my ($u_lat,$u_lon,$t_lat,$t_lon,$i,$cnt,$ru,$clat,$clon); # TEST SET OF 4, PLUS USRE'S POSITION $maxlat = $tllat if ($tllat > $maxlat); $maxlat = $bllat if ($bllat > $maxlat); $maxlat = $brlat if ($brlat > $maxlat); $maxlat = $trlat if ($trlat > $maxlat); $minlat = $tllat if ($tllat < $minlat); $minlat = $bllat if ($bllat < $minlat); $minlat = $brlat if ($brlat < $minlat); $minlat = $trlat if ($trlat < $minlat); $maxlon = $tllon if ($tllon > $maxlon); $maxlon = $bllon if ($bllon > $maxlon); $maxlon = $brlon if ($brlon > $maxlon); $maxlon = $trlon if ($trlon > $maxlon); $minlon = $tllon if ($tllon < $minlon); $minlon = $bllon if ($bllon < $minlon); $minlon = $brlon if ($brlon < $minlon); $minlon = $trlon if ($trlon < $minlon); # expand by User points if (defined ${$rh}{'user_points'}) { $ru = ${$rh}{'user_points'}; $cnt = scalar @{$ru}; for ($i = 0; $i < $cnt; $i++) { $u_lat = ${$ru}[$i][0]; $u_lon = ${$ru}[$i][1]; $t_lat = ${$ru}[$i][2]; $t_lon = ${$ru}[$i][3]; $maxlat = $u_lat if ($u_lat > $maxlat); $minlat = $u_lat if ($u_lat < $minlat); $maxlon = $u_lon if ($u_lon > $maxlon); $minlon = $u_lon if ($u_lon < $minlon); } } my $key = ''; my $rcnt = 0; my $pcnt = 0; my ($rrwys,$rpatts); my ($elat1,$elon1,$elat2,$elon2); my ($plat11,$plon11,$plat12,$plon12,$plat13,$plon13,$plat21,$plon21); #my ($plat21,$plon21,$plat22,$plon22,$plat23,$plon23,$plat11,$plon11); if ((defined ${$rh}{'runways'})&&(defined ${$rh}{'pattern'})&&(defined ${$rh}{'airport'})) { $rrwys = ${$rh}{'runways'}; $rpatts = ${$rh}{'pattern'}; $key = ${$rh}{'airport'}; $rcnt = scalar @{$rrwys}; $pcnt = scalar @{$rpatts}; prt("Adding $rcnt runways, and $pcnt patterns...\n"); for ($i = 0; $i < $rcnt; $i++) { $elat1 = ${$rrwys}[$i][$RW_LLAT]; $elon1 = ${$rrwys}[$i][$RW_LLON]; $elat2 = ${$rrwys}[$i][$RW_RLAT]; $elon2 = ${$rrwys}[$i][$RW_RLON]; set_min_max(\$maxlat,\$minlat,\$maxlon,\$minlon,$elat1,$elon1); set_min_max(\$maxlat,\$minlat,\$maxlon,\$minlon,$elat2,$elon2); } for ($i = 0; $i < $pcnt; $i++) { # 0 1 2 3 4 5 6 7 # push(@{$rpatts}, [$plat11,$plon11,$plat12,$plon12,$plat13,$plon13,$plat21,$plon21,$clat,$clon,$rlen,$rhdg]); # push(@{$rpatts}, [$plat21,$plon21,$plat22,$plon22,$plat23,$plon23,$plat11,$plon11,$clat,$clon,$rlen,$hdgr]); $plat11 = ${$rpatts}[$i][0]; $plon11 = ${$rpatts}[$i][1]; $plat12 = ${$rpatts}[$i][2]; $plon12 = ${$rpatts}[$i][3]; $plat13 = ${$rpatts}[$i][4]; $plon13 = ${$rpatts}[$i][5]; $plat21 = ${$rpatts}[$i][6]; $plon21 = ${$rpatts}[$i][7]; set_min_max(\$maxlat,\$minlat,\$maxlon,\$minlon,$plat11,$plon11); set_min_max(\$maxlat,\$minlat,\$maxlon,\$minlon,$plat12,$plon12); set_min_max(\$maxlat,\$minlat,\$maxlon,\$minlon,$plat13,$plon13); set_min_max(\$maxlat,\$minlat,\$maxlon,\$minlon,$plat21,$plon21); } } ${$rh}{'max_lat'} = $maxlat; ${$rh}{'min_lat'} = $minlat; ${$rh}{'max_lon'} = $maxlon; ${$rh}{'min_lon'} = $minlon; my $lon_factor = 2; $latdegs = $maxlat - $minlat; $londegs = ($maxlon - $minlon) * $lon_factor; ${$rh}{'lon_degs'} = $londegs; ${$rh}{'lat_degs'} = $latdegs; $sqhgt = int(($latdegs * 10000) + 0.5); $sqwid = int(($londegs * 10000) + 0.5); ${$rh}{'sq_wid'} = $sqwid; ${$rh}{'sq_hgt'} = $sqhgt; # scale the image DOWN/up my $targ_wid = 600; my $ratio = $sqwid / $sqhgt; #if (($sqwid > $targ_wid) || ($sqhgt > $targ_wid)) { if ($ratio > 1) { # width > height $sqwid = $targ_wid; # set target width $sqhgt = int($targ_wid / $ratio); # and calculate NEW height } else { # height > width = set target height, adjust width accordingly $sqwid = int($targ_wid * $ratio); # calculate width $sqhgt = $targ_wid; # and set target width } #} ${$rh}{'sq_wid_adj'} = $sqwid; ${$rh}{'sq_hgt_adj'} = $sqhgt; my ($w_dpp,$h_dpp,$w_ind1,$h_ind1,$w_ind2,$h_ind2,$w_ind3,$h_ind3,$w_ind4,$h_ind4,$msg); my ($h_indu,$w_indu,$h_indt,$w_indt); my ($x_txt,$y_txt,$txt); $w_dpp = ($sqwid / $londegs) * $lon_factor; $h_dpp = $sqhgt / $latdegs; ${$rh}{'w_dpp'} = $w_dpp; ${$rh}{'h_dpp'} = $h_dpp; ${$rh}{'rmsg'} = \$msg; # Imagmagick colors - http://www.imagemagick.org/script/color.php #$msg = "convert -size ${sqwid}x${sqhgt} xc:wheat +antialias -fill white "; $msg = "convert -size ${sqwid}x${sqhgt} xc:wheat -fill white "; # -stroke Gold -draw "path 'M 20,70 A 1,1 0 0,1 80,50'" \ # -stroke DodgerBlue -draw "line 30,10 50,80" \ # a dasshed line -draw "stroke-dasharray 5 3 path 'M 10,20 L 90,20'" # -stroke Red -draw "circle 80,60 82,60" test.gif # -draw 'circle 50,30 50,55' - centered on point 50,30, ??? #$msg .= "-font Courier -pointsize 12 -strokewidth 0.5 "; # As such with a default "-density" of 72dpi (at which 1 point = 1 pixel) # a 12 point font should have 12 pixels separation between the # baselines to two lines of text. $msg .= "-pointsize 12 -strokewidth 0.5 "; if (length($key) && $rcnt) { if ($pcnt) { ${$rh}{'-stroke'} = "SlateGray"; $msg .= "-stroke SlateGray "; for ($i = 0; $i < $pcnt; $i++) { # 0 1 2 3 4 5 6 7 8 9 # push(@{$rpatts}, [$plat11,$plon11,$plat12,$plon12,$plat13,$plon13,$plat21,$plon21,$clat,$clon]); # push(@{$rpatts}, [$plat21,$plon21,$plat22,$plon22,$plat23,$plon23,$plat11,$plon11,$clat,$clon]); # YGIL LAYOUT # 22 ----- 21 ----- 13 # | | # | 15 | # | | | # | 33 | # | | # 23 ----- 11 ----- 12 $plat11 = ${$rpatts}[$i][0]; $plon11 = ${$rpatts}[$i][1]; $plat12 = ${$rpatts}[$i][2]; $plon12 = ${$rpatts}[$i][3]; $plat13 = ${$rpatts}[$i][4]; $plon13 = ${$rpatts}[$i][5]; $plat21 = ${$rpatts}[$i][6]; $plon21 = ${$rpatts}[$i][7]; $clat = ${$rpatts}[$i][8]; $clon = ${$rpatts}[$i][9]; add_img_line($rh,$plat11,$plon11,$plat12,$plon12,1); add_img_line($rh,$plat12,$plon12,$plat13,$plon13,1); add_img_line($rh,$plat13,$plon13,$plat21,$plon21,1); add_img_line($rh,$plat21,$plon21,$plat11,$plon11,1); if ($switch_circuit && ($i == 0)) { add_arrow_to_center($rh,$plat11,$plon11,$plat12,$plon12); add_arrow_to_center($rh,$plat12,$plon12,$plat13,$plon13); add_arrow_to_center($rh,$plat13,$plon13,$plat21,$plon21); add_arrow_to_center($rh,$clat,$clon,$plat11,$plon11); add_arrow_to_center($rh,$plat21,$plon21,$clat,$clon); } elsif (!$switch_circuit && ($i == 1)) { add_arrow_to_center($rh,$plat11,$plon11,$plat12,$plon12); add_arrow_to_center($rh,$plat12,$plon12,$plat13,$plon13); add_arrow_to_center($rh,$plat13,$plon13,$plat21,$plon21); add_arrow_to_center($rh,$plat21,$plon21,$clat,$clon); add_arrow_to_center($rh,$clat,$clon,$plat11,$plon11); } } } # DRAW THE RUNWAY # set color $msg .= "-stroke blue "; for ($i = 0; $i < $rcnt; $i++) { $elat1 = ${$rrwys}[$i][$RW_LLAT]; $elon1 = ${$rrwys}[$i][$RW_LLON]; $elat2 = ${$rrwys}[$i][$RW_RLAT]; $elon2 = ${$rrwys}[$i][$RW_RLON]; $w_ind1 = int((($elon1 - $minlon) * $w_dpp) + 0.5); # get degrees/pixels from left edge $h_ind1 = int((($elat1 - $minlat) * $h_dpp) + 0.5); # get degrees/pixels from bottom edge $h_ind1 = $sqhgt - $h_ind1; $h_ind1 += 1 if ($h_ind1 == 0); $w_ind1 += 1 if ($w_ind1 == 0); $h_ind1 -= 1 if ($h_ind1 == $sqhgt); $w_ind1 -= 1 if ($w_ind1 == $sqwid); $msg .= "-draw \"circle ".($w_ind1-1).",$h_ind1 ".($w_ind1+1).",$h_ind1\" "; $w_ind2 = int((($elon2 - $minlon) * $w_dpp) + 0.5); # get degrees/pixels from left edge $h_ind2 = int((($elat2 - $minlat) * $h_dpp) + 0.5); # get degrees/pixels from bottom edge $h_ind2 = $sqhgt - $h_ind2; #$w_ind2 += 1 if ($w_ind2 == 0); $h_ind2 += 1 if ($h_ind2 == 0); $w_ind2 += 1 if ($w_ind2 == 0); $h_ind2 -= 1 if ($h_ind2 == $sqhgt); $w_ind2 -= 1 if ($w_ind2 == $sqwid); $msg .= "-draw \"circle ".($w_ind2-1).",$h_ind2 ".($w_ind2+1).",$h_ind2\" "; $msg .= "-draw \"line $w_ind1,$h_ind1 $w_ind2,$h_ind2\" "; my $clat = ${$rrwys}[$i][$RW_CLAT]; my $clon = ${$rrwys}[$i][$RW_CLON]; draw_text_at_latlon($rh,$clat,$clon,"YGIL",0); } } # set color $msg .= "-stroke black "; $x_txt = $sqwid - 100; #$y_txt = 12; $y_txt = 20; draw_text_at_pos($rh,$x_txt,$y_txt,"Wind: SSE 8Kt",0); # tllat,$tllon on square $w_ind1 = int((($tllon - $minlon) * $w_dpp) + 0.5); # get degrees/pixels from left edge $h_ind1 = int((($tllat - $minlat) * $h_dpp) + 0.5); # get degrees/pixels from bottom edge $h_ind1 = $sqhgt - $h_ind1; $h_ind1 += 1 if ($h_ind1 == 0); $w_ind1 += 1 if ($w_ind1 == 0); $h_ind1 -= 1 if ($h_ind1 == $sqhgt); $w_ind1 -= 1 if ($w_ind1 == $sqwid); $msg .= "-draw \"circle ".($w_ind1-1).",$h_ind1 ".($w_ind1+1).",$h_ind1\" "; # bllat,$bllon on square $w_ind2 = int((($bllon - $minlon) * $w_dpp) + 0.5); # get degrees/pixels from left edge $h_ind2 = int((($bllat - $minlat) * $h_dpp) + 0.5); # get degrees/pixels from bottom edge $h_ind2 = $sqhgt - $h_ind2; #$w_ind2 += 1 if ($w_ind2 == 0); $h_ind2 += 1 if ($h_ind2 == 0); $w_ind2 += 1 if ($w_ind2 == 0); $h_ind2 -= 1 if ($h_ind2 == $sqhgt); $w_ind2 -= 1 if ($w_ind2 == $sqwid); $msg .= "-draw \"circle ".($w_ind2-1).",$h_ind2 ".($w_ind2+1).",$h_ind2\" "; # brlat,$brlon on square $w_ind3 = int((($brlon - $minlon) * $w_dpp) + 0.5); # get degrees/pixels from left edge $h_ind3 = int((($brlat - $minlat) * $h_dpp) + 0.5); # get degrees/pixels from bottom edge $h_ind3 = $sqhgt - $h_ind3; #$w_ind3 += 1 if ($w_ind3 == 0); $h_ind3 += 1 if ($h_ind3 == 0); $w_ind3 += 1 if ($w_ind3 == 0); $h_ind3 -= 1 if ($h_ind3 == $sqhgt); $w_ind3 -= 1 if ($w_ind3 == $sqwid); $msg .= "-draw \"circle ".($w_ind3-1).",$h_ind3 ".($w_ind3+1).",$h_ind3\" "; # trlat,$trlon on square $w_ind4 = int((($trlon - $minlon) * $w_dpp) + 0.5); # get degrees/pixels from left edge $h_ind4 = int((($trlat - $minlat) * $h_dpp) + 0.5); # get degrees/pixels from bottom edge $h_ind4 = $sqhgt - $h_ind4; #$w_ind4 += 1 if ($w_ind4 == 0); $h_ind4 += 1 if ($h_ind4 == 0); $w_ind4 += 1 if ($w_ind4 == 0); $h_ind4 -= 1 if ($h_ind4 == $sqhgt); $w_ind4 -= 1 if ($w_ind4 == $sqwid); $msg .= "-draw \"circle ".($w_ind4-1).",$h_ind4 ".($w_ind4+1).",$h_ind4\" "; if (!$use_pattern) { $msg .= "-draw \"line $w_ind1,$h_ind1 $w_ind2,$h_ind2\" "; $msg .= "-draw \"line $w_ind2,$h_ind2 $w_ind3,$h_ind3\" "; $msg .= "-draw \"line $w_ind3,$h_ind3 $w_ind4,$h_ind4\" "; $msg .= "-draw \"line $w_ind4,$h_ind4 $w_ind1,$h_ind1\" "; } if (defined ${$rh}{'user_points'}) { $ru = ${$rh}{'user_points'}; $cnt = scalar @{$ru}; # u_lat,$u_lon on square for ($i = 0; $i < $cnt; $i++) { $u_lat = ${$ru}[$i][0]; $u_lon = ${$ru}[$i][1]; $t_lat = ${$ru}[$i][2]; $t_lon = ${$ru}[$i][3]; $w_indu = int((($u_lon - $minlon) * $w_dpp) + 0.5); # get degrees/pixels from left edge $h_indu = int((($u_lat - $minlat) * $h_dpp) + 0.5); # get degrees/pixels from bottom edge $h_indu = $sqhgt - $h_indu; $h_indu += 1 if ($h_indu == 0); $w_indu += 1 if ($w_indu == 0); $h_indu -= 1 if ($h_indu == $sqhgt); $w_indu -= 1 if ($w_indu == $sqwid); $msg .= "-stroke blue "; $msg .= "-draw \"circle ".($w_indu-1).",$h_indu ".($w_indu+1).",$h_indu\" "; if ($add_text_count) { # -draw "text 25,65 'Anthony'" # this point is the lower left of the text $txt = $i + 1; $x_txt = $w_indu + 3; $y_txt = $h_indu + 4; # modify the position to within the page $x_txt += 3 if (($x_txt - 3) <= 0); $x_txt -= 3 if (($x_txt + 3) >= $sqwid); $y_txt -= 5 if (($y_txt + 5) >= $sqhgt); $y_txt += 5 if (($y_txt - 5) <= 0); $msg .= "-draw \"text $x_txt,$y_txt '$txt'\" "; } # t_lat,$t_lon on square $w_indt = int((($t_lon - $minlon) * $w_dpp) + 0.5); # get degrees/pixels from left edge $h_indt = int((($t_lat - $minlat) * $h_dpp) + 0.5); # get degrees/pixels from bottom edge $h_indt = $sqhgt - $h_indt; $h_indt += 1 if ($h_indt == 0); $w_indt += 1 if ($w_indt == 0); $h_indt -= 1 if ($h_indt == $sqhgt); $w_indt -= 1 if ($w_indt == $sqwid); $msg .= "-draw \"circle ".($w_indu-1).",$h_indu ".($w_indu+1).",$h_indu\" "; # $msg .= "-stroke red -draw \"line $w_indu,$h_indu $w_indt,$h_indt\" "; $msg .= "-stroke red "; #add_img_line($rh,$w_indu,$h_indu,$w_indt,$h_indt); add_img_line($rh,$u_lat,$u_lon,$t_lat,$t_lon,0); } } $msg .= "$graf_file\n"; $msg .= "imdisplay $graf_file\n"; $msg .= "start $graf_file\n"; write2file($msg,$graf_bat); prt("Written $graf_bat\n"); if ($show) { set_lat_stg(\$maxlat); set_lat_stg(\$minlat); set_lon_stg(\$maxlon); set_lon_stg(\$minlon); prt("Square wid=$londegs hgt=$latdegs ${sqwid}X${sqhgt}\n"); prt("TL $maxlat,$minlon\n"); prt("BL $minlat,$minlon\n"); prt("BR $minlat,$maxlon\n"); prt("TR $maxlat,$maxlon\n"); } } sub get_circuit_hash() { my %h = (); $h{'tl_lat'} = $tl_lat; $h{'tl_lon'} = $tl_lon; $h{'bl_lat'} = $bl_lat; $h{'bl_lon'} = $bl_lon; $h{'br_lat'} = $br_lat; $h{'br_lon'} = $br_lon; $h{'tr_lat'} = $tr_lat; $h{'tr_lon'} = $tr_lon; set_circuit_values(\%h,1); #set_most_values(\%h,1); return \%h; } sub get_mid_tl2bl($$) { my ($rlat,$rlon) = @_; my ($az1,$az2,$dist); fg_geo_inverse_wgs_84 ($tl_lat,$tl_lon,$bl_lat,$bl_lon,\$az1,\$az2,\$dist); my $dist2 = $dist / 2; # get the center of this line my ($clat,$clon); #fg_geo_direct_wgs_84( $tl_lat, $tl_lon, $az1, $dist2, $rlat, $rlon, \$az2 ); fg_geo_direct_wgs_84( $tl_lat, $tl_lon, $az1, $dist2, \$clat, \$clon, \$az2 ); $az2 = normalised_hdg($az1 + 90); # turn 90 degrees, and get a point fg_geo_direct_wgs_84( $clat, $clon, $az2, $dist2, $rlat, $rlon, \$az1 ); } sub get_mid_bl2br($$) { my ($rlat,$rlon) = @_; my ($az1,$az2,$dist); fg_geo_inverse_wgs_84 ($bl_lat,$bl_lon,$br_lat,$br_lon,\$az1,\$az2,\$dist); my $dist2 = $dist / 2; fg_geo_direct_wgs_84( $bl_lat, $bl_lon, $az1, $dist2, $rlat, $rlon, \$az2 ); } sub get_mid_bl2br2($$) { my ($rlat,$rlon) = @_; my ($az1,$az2,$dist); fg_geo_inverse_wgs_84 ($bl_lat,$bl_lon,$br_lat,$br_lon,\$az1,\$az2,\$dist); my $dist2 = $dist / 2; my ($clat,$clon); fg_geo_direct_wgs_84( $bl_lat, $bl_lon, $az1, $dist2, \$clat, \$clon, \$az2 ); $az2 = normalised_hdg($az1 + 90); # turn 90 degrees, and get a point fg_geo_direct_wgs_84( $clat, $clon, $az2, $dist2, $rlat, $rlon, \$az1 ); } sub set_distances_bearings($$$$) { my ($rh,$lat,$lon,$msg) = @_; ${$rh}{'usr_lat'} = $lat; ${$rh}{'usr_lon'} = $lon; ${$rh}{'usr_msg'} = $msg; my ($tlat,$tlon); my ($az1,$az2,$dist); $msg = ''; $tlat = ${$rh}{'tl_lat'}; # = -31.684063; $tlon = ${$rh}{'tl_lon'}; # = 148.614120; fg_geo_inverse_wgs_84 ($lat,$lon,$tlat,$tlon,\$az1,\$az2,\$dist); ${$rh}{'tl_az1'} = $az1; ${$rh}{'tl_az2'} = $az2; ${$rh}{'tl_dist'} = $dist; set_int_dist_stg5(\$dist); $msg .= "TL $dist "; $tlat = ${$rh}{'bl_lat'}; # = -31.723495; $tlon = ${$rh}{'bl_lon'}; # = 148.633003; fg_geo_inverse_wgs_84 ($lat,$lon,$tlat,$tlon,\$az1,\$az2,\$dist); ${$rh}{'bl_az1'} = $az1; ${$rh}{'bl_az2'} = $az2; ${$rh}{'bl_dist'} = $dist; set_int_dist_stg5(\$dist); $msg .= "BL $dist "; $tlat = ${$rh}{'br_lat'}; # = -31.716778; $tlon = ${$rh}{'br_lon'}; # = 148.666992; fg_geo_inverse_wgs_84 ($lat,$lon,$tlat,$tlon,\$az1,\$az2,\$dist); ${$rh}{'br_az1'} = $az1; # from 'test' to BR point ${$rh}{'br_az2'} = $az2; ${$rh}{'br_dist'} = $dist; set_int_dist_stg5(\$dist); $msg .= "BR $dist "; $tlat = ${$rh}{'tr_lat'}; # = -31.672960; $tlon = ${$rh}{'tr_lon'}; # = 148.649139; fg_geo_inverse_wgs_84 ($lat,$lon,$tlat,$tlon,\$az1,\$az2,\$dist); ${$rh}{'tr_az1'} = $az1; ${$rh}{'tr_az2'} = $az2; ${$rh}{'tr_dist'} = $dist; set_int_dist_stg5(\$dist); $msg .= "TR $dist "; prt("Distances: $msg\n"); } sub dist_less_or_equal5($$) { my ($dist,$min_dist) = @_; return 1 if ($dist <= $min_dist); my $d5 = $dist * 0.95; return 2 if ($d5 < $min_dist); return 0; } sub point_in_circuit($$$$) { my ($rh,$lat,$lon,$rres) = @_; my ($tl_lat,$tl_lon,$bl_lat,$bl_lon,$br_lat,$br_lon,$tr_lat,$tr_lon); my $ret = 0; $tl_lat = ${$rh}{'tl_lat'}; $tl_lon = ${$rh}{'tl_lon'}; $bl_lat = ${$rh}{'bl_lat'}; $bl_lon = ${$rh}{'bl_lon'}; $br_lat = ${$rh}{'br_lat'}; $br_lon = ${$rh}{'br_lon'}; $tr_lat = ${$rh}{'tr_lat'}; $tr_lon = ${$rh}{'tr_lon'}; my $res = ''; if (Point_Inside_Triange($lat,$lon,$tl_lat,$tl_lon, $bl_lat,$bl_lon, $br_lat,$br_lon)) { $res = "in circuit (1st tri)"; $ret = 1; } elsif (Point_Inside_Triange($lat,$lon,$tl_lat,$tl_lon, $tr_lat,$tr_lon, $br_lat,$br_lon)) { $res = "in curcuit (2nd tri)"; $ret = 1; } else { $res = "NOT in circuit."; } ${$rres} = $res; return $ret; } sub process_lat_lon($$$$) { my ($rh,$lat,$lon,$msg) = @_; my $res = ''; my $ptinc = point_in_circuit($rh,$lat,$lon,\$res); prt("\nSolving lat=$lat, lon=$lon $msg, $res \n"); # get distances, and headings... my ($tlat,$tlon,$az1,$az2,$dist); set_distances_bearings($rh,$lat,$lon,$msg); # 1: get closest point, on circuit # 1 Mile = 1609.344 Meters. my $min_dist = 12000 * 1700; my $hdto = 'unsolved'; my $targ_lon = $bad_latlon; my $targ_lat = $bad_latlon; my $targ_dist = 1000000000; my ($hdg); $tlat = ${$rh}{'tl_lat'}; # = -31.684063; $tlon = ${$rh}{'tl_lon'}; # = 148.614120; $az1 = ${$rh}{'tl_az1'}; $az2 = ${$rh}{'tl_az2'}; $dist = ${$rh}{'tl_dist'}; $res = 0; #if ($dist < $min_dist) { $min_dist = $dist; $hdto = "to top left ($res)"; $hdg = $az1; $targ_lat = $tlat; $targ_lon = $tlon; $targ_dist = $dist; #} $tlat = ${$rh}{'bl_lat'}; # = -31.684063; $tlon = ${$rh}{'bl_lon'}; # = 148.614120; $az1 = ${$rh}{'bl_az1'}; $az2 = ${$rh}{'bl_az2'}; $dist = ${$rh}{'bl_dist'}; #if ($dist <= $min_dist) { $res = dist_less_or_equal5($dist,$min_dist); if ($res) { $min_dist = $dist; $hdto = "to bottom left ($res)"; $hdg = $az1; $targ_lat = $tlat; $targ_lon = $tlon; $targ_dist = $dist; } $tlat = ${$rh}{'br_lat'}; # = -31.684063; $tlon = ${$rh}{'br_lon'}; # = 148.614120; $az1 = ${$rh}{'br_az1'}; $az2 = ${$rh}{'br_az2'}; $dist = ${$rh}{'br_dist'}; #if ($dist < $min_dist) { $res = dist_less_or_equal5($dist,$min_dist); if ($res) { $min_dist = $dist; $hdto = "to bottom right ($res)"; $hdg = $az1; $targ_lat = $tlat; $targ_lon = $tlon; $targ_dist = $dist; } $tlat = ${$rh}{'tr_lat'}; # = -31.684063; $tlon = ${$rh}{'tr_lon'}; # = 148.614120; $az1 = ${$rh}{'tr_az1'}; $az2 = ${$rh}{'tr_az2'}; $dist = ${$rh}{'tr_dist'}; #if ($dist <= $min_dist) { $res = dist_less_or_equal5($dist,$min_dist); if ($res) { $min_dist = $dist; $hdto = "to top right ($res)"; $hdg = $az1; $targ_lat = $tlat; $targ_lon = $tlon; $targ_dist = $dist; } $tlat = ${$rh}{'tl_lat'}; # = -31.684063; $tlon = ${$rh}{'tl_lon'}; # = 148.614120; $az1 = ${$rh}{'tl_az1'}; $az2 = ${$rh}{'tl_az2'}; $dist = ${$rh}{'tl_dist'}; $res = dist_less_or_equal5($dist,$min_dist); if ($res) { $min_dist = $dist; $hdto = "to top left ($res)"; $hdg = $az1; $targ_lat = $tlat; $targ_lon = $tlon; $targ_dist = $dist; } if ($ptinc) { # if in the circuit, always head for the end of downwind, before turn cross $tlat = ${$rh}{'bl_lat'}; # = -31.684063; $tlon = ${$rh}{'bl_lon'}; # = 148.614120; $az1 = ${$rh}{'bl_az1'}; $az2 = ${$rh}{'bl_az2'}; $dist = ${$rh}{'bl_dist'}; $hdto = "in circuit ($ptinc)"; $hdg = $az1; $targ_lat = $tlat; $targ_lon = $tlon; $targ_dist = $dist; } set_most_values_plus($rh,1,$lat,$lon,$targ_lat,$targ_lon); # for display set_hdg_stg3(\$hdg); set_int_dist_stg5(\$targ_dist); set_lat_stg(\$targ_lat); set_lon_stg(\$targ_lon); prt("\nHeading: $hdto, hdg=$hdg, to $targ_lat,$targ_lon, at $targ_dist m.\n"); } sub is_in_circuit($$$$$) { my ($rh,$lat,$lon,$msg,$i) = @_; # $tl_lat,$tl_lon, $bl_lat, $bl_lon, $br_lat, $br_lon, $tr_lat, $tr_lon # Into two triangles # 1 - $tl_lat,$tl_lon, $bl_lat,$bl_lon, $br_lat,$br_lon # 2 - $tl_lat,$tl_lon, $tr_lat,$tr_lon, $br_lat,$br_lon #sub Point_Inside_Triange($$$$$$$$) { # my ($px,$py,$x1,$y1,$x2,$y2,$x3,$y3) = @_; if (Point_Inside_Triange($lat,$lon,$tl_lat,$tl_lon, $bl_lat,$bl_lon, $br_lat,$br_lon)) { prt("Point $i inside first triangle\n"); } elsif (Point_Inside_Triange($lat,$lon,$tl_lat,$tl_lon, $tr_lat,$tr_lon, $br_lat,$br_lon)) { prt("Point $i inside second triangle\n"); } else { prt("Point $i NOT in circuit\n"); } } # Airport Line. eg '1 5355 1 0 KABQ Albuquerque Intl Sunport' # 0 1 - this as an airport header line. 16 is a seaplane/floatplane base, 17 a heliport. # 1 5355 - Airport elevation (in feet above MSL). # 2 1 - Airport has a control tower (1=yes, 0=no). # 3 0 - Display X-Plane’s default airport buildings (1=yes, 0=no). # 4 KABQ - Identifying code for the airport (the ICAO code, if one exists). # 5+Albuquerque Intl Sunport - Airport name. sub find_apt($) { my $icao = shift; my $t1 = [gettimeofday]; my $aptdat = "X:\\fgdata\\Airports\\apt.dat.gz"; if (! -f $aptdat) { prt("Failed to 'stat' file '$aptdat'!\n"); pgm_exit(1,""); } prt("Processing file $aptdat... for ICAO $icao... moment...\n"); if (!open(INF,"gzip -cdq $aptdat|")) { prt("Failed to 'open' file '$aptdat'!\n"); pgm_exit(1,""); } my ($line,@arr,$type,$len); my $ver = 0; my $lncnt = 0; while ($line = ) { $lncnt++; chomp $line; $line = trim_all($line); if ($line =~ /^(\d+)\s+Version/) { $ver = $1; last; } } my $fnd = 0; if ($ver) { while ($line = ) { $lncnt++; 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)) && ($arr[4] eq $icao) ) { # if (($type == 1) && ($arr[4] eq $icao)) my @a = @arr; push(@my_airport,\@a); while ($line = ) { $lncnt++; 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)); my @a2 = @arr; push(@my_airport,\@a2); } $fnd = $lncnt; last; } } } else { pgm_exit(1,"Version NOT found in $aptdat!\n"); } while ($line = ) { $lncnt++; } my $elap = secs_HHMMSS( tv_interval( $t1, [gettimeofday] ) ); $line = scalar @my_airport; if ($fnd) { prt("Found apt $icao, $line components, at line $fnd of $lncnt lines... in $elap\n"); } else { pgm_exit(1,"ICAO $icao NOT FOUND in $aptdat!\n"); } close INF; } # @runways reference # 0 1=lat 2=lon 3=s 4=hdg 5=len 6=offsets 7=stopway 8=wid 9=lights 10=surf 11 12 13 14 15 # 10 36.962213 127.031071 14x 131.52 8208 1595.0620 0000.0000 150 321321 1 0 3 0.25 0 0300.0300 # 11=shoulder 12=marks 13=smooth 14=signs 15=GS angles # 0 3 0.25 0 0300.0300 # ==================================================== # Land Runway # 0 - 100 - Row code for a land runway (the most common) 100 # 1 - 29.87 - Width of runway in metres Two decimal places recommended. Must be >= 1.00 # 2 - 3 - Code defining the surface type (concrete, asphalt, etc) Integer value for a Surface Type Code # 3 - 0 - Code defining a runway shoulder surface type 0=no shoulder, 1=asphalt shoulder, 2=concrete shoulder # 4 - 0.15 - Runway smoothness (not used by X-Plane yet) 0.00 (smooth) to 1.00 (very rough). Default is 0.25 # 5 - 0 - Runway centre-line lights 0=no centerline lights, 1=centre line lights # 6 - 0 - Runway edge lighting (also implies threshold lights) 0=no edge lights, 2=medium intensity edge lights # 7 - 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 # 8 - 13L - Runway number (eg. 31R, 02). Leading zeros are required. Two to three characters. Valid suffixes: L, R or C (or blank) # 9 - 47.53801700 - Latitude of runway threshold (on runway centerline) in decimal degrees Eight decimal places supported # 10 - -122.30746100 - Longitude of runway threshold (on runway centerline) in decimal degrees Eight decimal places supported # 11 - 73.15 - Length of displaced threshold in metres (this is included in implied runway length) Two decimal places (metres). Default is 0.00 # 12 - 0.00 - Length of overrun/blast-pad in metres (not included in implied runway length) Two decimal places (metres). Default is 0.00 # 13 - 2 - Code for runway markings (Visual, non-precision, precision) Integer value for Runway Marking Code # 14 - 0 - Code for approach lighting for this runway end Integer value for Approach Lighting Code # 15 - 0 - Flag for runway touchdown zone (TDZ) lighting 0=no TDZ lighting, 1=TDZ lighting # 16 - 1 - Code for Runway End Identifier Lights (REIL) 0=no REIL, 1=omni-directional REIL, 2=unidirectional REIL # 17 - 31R # 18 - 47.52919200 # 19 - -122.30000000 # 20 - 110.95 # 21 - 0.00 # 22 - 2 # 23 - 0 # 24 - 0 # 25 - 1 sub find_runway() { my ($ra,$type,$clat,$clon,$cnt,$len,$width,$hdg,$tmp,$hdgr); my ($rwy1,$lat1,$lon1); my ($rwy2,$lat2,$lon2); my ($az1,$az2,$dist,$rwlen2); my ($isrwy,$dhdg); my ($alt,$icao,$name); my ($tlat,$tlon,$alat,$alon); $cnt = scalar @my_airport; prt("Choose runway, from $cnt apt components...\n"); my @runways = (); $tlat = 0; $tlon = 0; my $gothdr = 0; foreach $ra (@my_airport) { $cnt = scalar @{$ra}; $type = ${$ra}[0]; $isrwy = 0; if (($type == 1)||($type == 16)||($type == 17)) { # @arr2 = split(/\s+/,$apt); $alt = ${$ra}[1]; # Airport (general) ALTITUDE AMSL # $actl = $arr2[2]; # control tower # $abld = $arr2[3]; # buildings $icao = ${$ra}[4]; # ICAO $name = join(' ', splice(@{$ra},5)); # Name $gothdr = 1; } elsif ($type == 10) { $rwy1 = ${$ra}[3]; # xxx just a taxiway next if ($rwy1 eq 'xxx'); $clat = ${$ra}[1]; $clon = ${$ra}[2]; $rwy1 = ${$ra}[3]; # xxx just a taxiway $hdg = ${$ra}[4]; # 4=hdg $len = ${$ra}[5]; # 5=len $dist = $len * $SG_FEET_TO_METER; $rwlen2 = $dist / 2; my $hdgr = $hdg + 180; $hdgr -= 360 if ($hdgr >= 360); fg_geo_direct_wgs_84( $clat, $clon, $hdg, $rwlen2, \$lat1, \$lon1, \$az1 ); fg_geo_direct_wgs_84( $clat, $clon, $hdgr, $rwlen2, \$lat2, \$lon2, \$az2 ); $rwy1 =~ s/x+$//; # REMOVE any TRAILING 'x', but may have 'L', 'R', 'C', 'S' appended if ($rwy1 =~ /^\d+$/) { ### $rwy2 = $rwy1 * 10; # 2010-12-15 - get opposite end numbers $rwy2 = ($rwy1 > 18) ? $rwy1 - 18 : $rwy1 + 18; $rwy2 = '36' if ($rwy2 == 0); } else { $rwy2 = $rwy1; $rwy2 =~ /(..)(.)/; $tmp = ($1 > 18) ? $1 - 18 : $1 + 18; $tmp = '0'.$tmp if ($tmp < 10); $tmp .= 'L' if ($2 eq 'R'); $tmp .= 'R' if ($2 eq 'L'); $tmp .= 'C' if ($2 eq 'C'); $rwy2 = $tmp; # get opp heading, but may NOT be per numbers } $isrwy = 1; } elsif ($type == 100) { $rwy1 = ${$ra}[8]; # 8 - 13L - Runway number (eg. 31R, 02). Leading zeros are required. Two to three characters. Valid suffixes: L, R or C (or blank) $lat1 = ${$ra}[9]; $lon1 = ${$ra}[10]; $rwy2 = ${$ra}[17]; $lat2 = ${$ra}[18]; $lon2 = ${$ra}[19]; # get LENGTH fg_geo_inverse_wgs_84 ($lat1,$lon1,$lat2,$lon2,\$az1,\$az2,\$dist); $len = $SG_METER_TO_FEET * $dist; $rwlen2 = $len / 2; $hdg = $az1; $hdgr = $az2; # get CENTER fg_geo_direct_wgs_84( $lat1, $lon1, $hdg, $rwlen2, \$clat, \$clon, \$az2 ); $isrwy = 1; } if ($isrwy) { my @a = (); $a[$RW_LEN] = $len; # lenght (feet) $a[$RW_HDG] = $hdg; $a[$RW_REV] = $hdgr; $a[$RW_TT1] = $rwy1; $a[$RW_TT2] = $rwy2; $a[$RW_CLAT] = $clat; $a[$RW_CLON] = $clon; $a[$RW_LLAT] = $lat1; $a[$RW_LLON] = $lon1; $a[$RW_RLAT] = $lat2; $a[$RW_RLON] = $lat2; $a[$RW_DONE] = 0; $tlat += $clat; $tlon += $clon; push(@runways,\@a); } } $cnt = scalar @runways; if (!$cnt) { pgm_exit(1,"Did NOT find any runways!\n"); } if (!$gothdr) { pgm_exit(1,"Did NOT find airport header line!\n"); } $alat = $tlat / $cnt; $alon = $tlon / $cnt; prt("Found $cnt runways at $icao, $name, ctr $alat,$alon...\n"); my $off = -1; my $max_len = 0; $cnt = 0; foreach $ra (@runways) { $len = ${$ra}[$RW_LEN]; if ($cnt == 0) { $off = $cnt; $max_len = $len; } elsif ($len > $max_len) { $off = $cnt; $max_len = $len; } $cnt++; } # get chosen ref array $ra = $runways[$off]; $len = ${$ra}[$RW_LEN]; # lenght (feet) $hdg = ${$ra}[$RW_HDG]; $hdgr = ${$ra}[$RW_REV]; $rwy1 = ${$ra}[$RW_TT1]; $rwy2 = ${$ra}[$RW_TT2]; $clat = ${$ra}[$RW_CLAT]; $clon = ${$ra}[$RW_CLON]; # $a[$RW_LLAT] = $lat1; # $a[$RW_LLON] = $lon1; # $a[$RW_RLAT] = $lat2; # $a[$RW_RLON] = $lat2; # $a[$RW_DONE] = 0; $dhdg = $hdg; set_int_stg(\$len); set_hdg_stg(\$hdg); set_hdg_stg(\$hdgr); set_int_stg(\$alt); set_decimal1_stg(\$dhdg); prt("At apt $icao, $name, alt $alt, ctr $alat,$alon\n"); prt("Chosen rwy $rwy1, len $len ft, hdg $hdg ($dhdg), ctr $clat,$clon\n"); prt("Opposite rwy $rwy2, len $len ft, hdg $hdgr\n"); my @naids = (); my @circ = (); my @a2 = ( $alat, $alon, \@naids, $ra, \@circ ); my $refa = \@a2; # [ $alat, $alon, \@naids, $ra, \@circ ]; my $rl = get_locations(); # key 0 1 2 3 4 # ICAO => [ Center LAT, LON NAVAIDS RUNWAYS CIRCUIT ] if (defined ${$rl}{$icao}) { prt("Updating existing entry...\n"); my $pra = ${$rl}{$icao}; my $plat = ${$pra}[0]; my $plon = ${$pra}[1]; my $prwys = ${$pra}[3]; my $prcnt = scalar @{$prwys}; my ($plen,$phdg,$prwy1,$pclat,$pclon,$paz1,$paz2,$pdist); $plen = ${$prwys}[0][$RW_LEN]; # lenght (feet) $phdg = ${$prwys}[0][$RW_HDG]; ###$hdgr = ${$prwys}[0][$RW_REV]; $prwy1 = ${$prwys}[0][$RW_TT1]; ##$rwy2 = ${$prwys}[0][$RW_TT2]; $pclat = ${$prwys}[0][$RW_CLAT]; $pclon = ${$prwys}[0][$RW_CLON]; fg_geo_inverse_wgs_84 ($clat,$clon,$pclat,$pclon,\$paz1,\$paz2,\$pdist); $pdist = get_dist_stg_nm($pdist); prt("Was $prwy1, len $plen, hdg $phdg, $pclat,$pclon, cnt $prcnt ($pdist)\n"); ${$rl}{$icao} = $refa; } else { prt("Adding a NEW entry...\n"); ${$rl}{$icao} = $refa; } #pgm_exit(1,"TEMP EXIT\n"); } ######################################### ### MAIN ### #find_apt($apt_icao); # "YGIL" #find_runway(); parse_args(@ARGV); init_runway_array(); my $ref_hash = get_circuit_hash(); get_runways_and_pattern($ref_hash,'YGIL'); ### is_in_circuit($ref_hash,$in_lat,$in_lon,"User input.",1); # 1 process_lat_lon($ref_hash,$in_lat,$in_lon,"1: User input"); # 1 if ($debug_on) { get_mid_tl2bl(\$in_lat,\$in_lon); process_lat_lon($ref_hash,$in_lat,$in_lon,"2: Mid tl2bl test point"); # 2 #get_mid_bl2br(\$in_lat,\$in_lon); get_mid_bl2br2(\$in_lat,\$in_lon); process_lat_lon($ref_hash,$in_lat,$in_lon,"3: Mid bl2br test point"); # 3 process_lat_lon($ref_hash,$def_lat2,$def_lon2,"4: 4th test point"); # 4 process_lat_lon($ref_hash,$def_lat3,$def_lon3,"5: 5th test point"); # 5 process_lat_lon($ref_hash,$def_lat4,$def_lon4,"6: 6th test point"); # 6 process_lat_lon($ref_hash,$def_lat5,$def_lon5,"7: 7th test point"); # 7 process_lat_lon($ref_hash,$def_lat6,$def_lon6,"8: 8th test point"); # 8 } # create the image of all the points paint_user_points($ref_hash,1); pgm_exit(0,""); ######################################## sub give_help { prt("$pgmname: version $VER\n"); prt("Usage: $pgmname [options] lat,lon\n"); prt("Options:\n"); prt(" --help (-h or -?) = This help, and exit 0.\n"); } sub need_arg { my ($arg,@av) = @_; pgm_exit(1,"ERROR: [$arg] must have following argument!\n") if (!@av); } sub parse_args { my (@av) = @_; my ($arg,$sarg,@arr,$lat,$lon); while (@av) { $arg = $av[0]; 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)"); } else { pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n"); } } else { @arr = split(",",$arg); if (scalar @arr == 2) { $lat = $arr[0]; $lon = $arr[1]; } else { pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n"); } } shift @av; } if ((($in_lat == $bad_latlon)||($in_lon == $bad_latlon)) && $debug_on) { @arr = split(",",$def_latlon); if (scalar @arr == 2) { $lat = $arr[0]; $lon = $arr[1]; if (in_world_range($lat,$lon)) { $in_lat = $lat; $in_lon = $lon; prt("Set lat=$in_lat, lon=$in_lon\n"); } else { pgm_exit(1,"ERROR: lat=$lat, lon=$lon OUT OF WORLD RANGE!\n"); } } else { pgm_exit(1,"ERROR: Default is invlalid\n"); } } if (($in_lat == $bad_latlon)||($in_lon == $bad_latlon)) { pgm_exit(1,"ERROR: No lat,lon found in command!\n"); } } sub get_v1000_spec() { my $txt = <= 1.00 2 - 3 - Code defining the surface type (concrete, asphalt, etc) Integer value for a Surface Type Code 3 - 0 - Code defining a runway shoulder surface type 0=no shoulder, 1=asphalt shoulder, 2=concrete shoulder 4 - 0.15 - Runway smoothness (not used by X-Plane yet) 0.00 (smooth) to 1.00 (very rough). Default is 0.25 5 - 0 - Runway centre-line lights 0=no centerline lights, 1=centre line lights 6 - 0 - Runway edge lighting (also implies threshold lights) 0=no edge lights, 2=medium intensity edge lights 7 - 1 - Auto-generate distance-remaining signs (turn off if created manually) 0=no auto signs, 1=auto-generate signs Airport Line. eg '1 5355 1 0 KABQ Albuquerque Intl Sunport' 1 - this as an airport header line. 16 is a seaplane/floatplane base, 17 a heliport. 5355 - Airport elevation (in feet above MSL). 1 - Airport has a control tower (1=yes, 0=no). 0 - Display X-Plane’s default airport buildings (1=yes, 0=no). KABQ - Identifying code for the airport (the ICAO code, if one exists). Albuquerque Intl Sunport - Airport name. EOF return $txt; } # eof - solve.pl