Distance and Bearing
use Text::CSV;
constant $EARTH_RADIUS_IN_NAUTICAL_MILES = 6372.8 / 1.852;
sub degrees ( Real $rad ) { $rad / tau * 360 }
sub radians ( Real $deg ) { $deg * tau / 360 }
sub haversine ( Real $x ) { ($x / 2).sin.² }
sub arc_haver ( Real $x ) { $x.sqrt.asin * 2 }
sub great_circle_distance ( \φ1, \λ1, \φ2, \λ2 ) {
# https://en.wikipedia.org/wiki/Haversine_formula
# latitude (represented by φ) and longitude (represented by λ)
# hav(θ) = hav(φ₂ − φ₁) + cos(φ₁) cos(φ₂) hav(λ₂ − λ₁)
arc_haver(
haversine(φ2 - φ1)
+ haversine(λ2 - λ1) * cos(φ1) * cos(φ2)
);
}
sub great_circle_bearing ( \φ1, \λ1, \φ2, \λ2 ) {
atan2( φ2.cos * (λ2 - λ1).sin,
φ1.cos * φ2.sin - φ1.sin * φ2.cos * (λ2 - λ1).cos,
);
}
sub distance_and_bearing ( $lat1, $lon1, $lat2, $lon2 ) {
my @ll = map &radians, $lat1, $lon1, $lat2, $lon2;
my $dist = great_circle_distance(|@ll);
my $theta = great_circle_bearing( |@ll);
return $dist * $EARTH_RADIUS_IN_NAUTICAL_MILES,
degrees( $theta + (tau if $theta < 0) )
}
sub find_nearest_airports ( $latitude, $longitude, $csv_path ) {
my &d_and_b_from_here = &distance_and_bearing.assuming($latitude, $longitude);
my @airports = csv(
in => $csv_path,
headers => [<ID Name City Country IATA ICAO Latitude Longitude>],
);
for @airports -> %row {
%row<Distance Bearing> = d_and_b_from_here( +%row<Latitude>, +%row<Longitude> );
}
return @airports.sort(*.<Distance>);
}
sub MAIN ( $lat = 51.514669, $long = 2.198581, $wanted = 20, $csv = 'airports.dat' ) {
printf "%7s\t%7s\t%-7s\t%-15s\t%s\n", <Dist Bear ICAO Country Name>;
for find_nearest_airports($lat, $long, $csv).head($wanted) {
printf "%7.1f\t %03d\t%-7s\t%-15s\t%s\n",
.<Distance Bearing ICAO Country Name>;
}
}
Output:
Dist Bear ICAO Country Name
30.7 146 EBFN Belgium Koksijde Air Base
31.3 127 EBOS Belgium Ostend-Bruges International Airport
33.6 252 EGMH United Kingdom Kent International Airport
34.4 195 LFAC France Calais-Dunkerque Airport
42.6 105 EBKW Belgium Westkapelle heliport
51.6 240 EGMK United Kingdom Lympne Airport
52.8 114 EBUL Belgium Ursel Air Base
56.2 274 EGMC United Kingdom Southend Airport
56.4 162 LFQT France Merville-Calonne Airport
56.5 137 EBKT Belgium Wevelgem Airport
57.3 089 EHMZ Netherlands Midden-Zeeland Airport
58.0 235 EGMD United Kingdom Lydd Airport
59.0 309 EGUW United Kingdom RAF Wattisham
59.3 339 EGSM United Kingdom Beccles Airport
59.7 146 LFQO France Lille/Marcq-en-Baroeul Airport
62.2 250 EGKH United Kingdom Lashenden (Headcorn) Airfield
63.7 200 LFAT France Le Touquet-Côte d'Opale Airport
64.2 261 EGTO United Kingdom Rochester Airport
66.3 149 LFQQ France Lille-Lesquin Airport
68.4 271 EGMT United Kingdom Thurrock Airfield
Last updated