Distance and Bearing
It is very important in aviation to have knowledge of the nearby airports at any time in flight.
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
You are encouraged to solve this task according to the task description, using any language you may know.
- Task
Determine the distance and bearing from an Airplane to the 20 nearest Airports whenever requested. Use the non-commercial data from openflights.org airports.dat as reference.
A request comes from an airplane at position ( latitude, longitude ): ( 51.514669, 2.198581 ).
Your report should contain the following information from table airports.dat (column shown in brackets):
Name(2), Country(4), ICAO(6), Distance and Bearing calculated from Latitude(7) and Longitude(8).
Distance is measured in nautical miles (NM). Resolution is 0.1 NM.
Bearing is measured in degrees (°). 0° = 360° = north then clockwise 90° = east, 180° = south, 270° = west. Resolution is 1°.
- See
-
- openflights.org/data: Airport, airline and route data
- Movable Type Scripts: Calculate distance, bearing and more between Latitude/Longitude points
J
Implementation:
DAT=: {{ require'web/gethttp csv'
if.0=#$fread F=.'airports.dat' do.
(gethttp y) fwrite F
end.
readcsv F
}}'https://raw.githubusercontent.com/jpatokal/openflights/master/data/airports.dat'
LATLONG=: _".&>6 7{"1 DAT
fmtm=: {{
9!:7]9 1 1#' -'
r=._1 _1}.1 1}.":boxopen y
~.r[9!:7]9 1 1#'+|-'
}}
AIRPORTS=: 1 3 5{"1 DAT
deg=: %&180p_1 NB. convert from degrees
R=: 21600 % 2p1 NB. radius of spherical earth in nautical miles
dist=: {{R*2*_1 o.1<.%:(*:1 o.0.5*x-y)p.x*&(2 o.{.)y}}°
bear=: {{
re=. (2 o.{.y)*1 o.dlon=.y-&{:x
2p1|1r2p1-{:*.re j. ((*|.)/+.^j.x,&{.y) p.-2 o.dlon
}}&.deg
NB. round y to nearest whole number under multiplying by x
pkg=: {{ <"0 (<.0.5+x*y)%x }}
LABELS=: ;:'Airport Country ICAO Distance Bearing'
nearest=: {{
ndx=. 20{./: d=. y dist"1 LATLONG NB. 20 nearest airports
fmtm LABELS,(ndx{AIRPORTS),"1 (10 pkg ndx{d),.1 pkg y bear"1 ndx{LATLONG
}}
For this task, we assume that the earth is a sphere rather than an oblate ellipsoid. Also, we report the initial bearing (at the requested location). For the task example, these issues are irrelevant (the distances involved are too small for those details to be significant), but they might become significant if code from this implementation was used in other contexts.
Task example:
nearest 51.514669 2.198581 Airport Country ICAO Distance Bearing ----------------------------------- -------------- ---- -------- ------- Koksijde Air Base Belgium EBFN 30.6 146 Ostend-Bruges International Airport Belgium EBOS 31.3 127 Kent International Airport United Kingdom EGMH 33.5 252 Calais-Dunkerque Airport France LFAC 34.4 196 Westkapelle heliport Belgium EBKW 42.5 105 Lympne Airport United Kingdom EGMK 51.6 240 Ursel Air Base Belgium EBUL 52.8 114 Southend Airport United Kingdom EGMC 56.2 274 Merville-Calonne Airport France LFQT 56.3 163 Wevelgem Airport Belgium EBKT 56.4 137 Midden-Zeeland Airport Netherlands EHMZ 57.2 90 Lydd Airport United Kingdom EGMD 58 235 RAF Wattisham United Kingdom EGUW 58.9 309 Beccles Airport United Kingdom EGSM 59.3 339 Lille/Marcq-en-Baroeul Airport France LFQO 59.6 146 Lashenden (Headcorn) Airfield United Kingdom EGKH 62.2 250 Le Touquet-Côte d'Opale Airport France LFAT 63.7 200 Rochester Airport United Kingdom EGTO 64.2 262 Lille-Lesquin Airport France LFQQ 66.2 149 Thurrock Airfield United Kingdom EGMT 68.4 272
Java
Author Sjharper79 (talk)
Description
This java program contains two classes. One class holds the airport information, and the other class does all of the work to perform calculations and print results. Some of the logic adapted from J.
Implementation
// The Airport class holds each airport object
package distanceAndBearing;
public class Airport {
private String airport;
private String country;
private String icao;
private double lat;
private double lon;
public String getAirportName() { return this.airport; }
public void setAirportName(String airport) { this.airport = airport; }
public String getCountry() { return this.country; }
public void setCountry(String country) { this.country = country; }
public String getIcao() { return this.icao; }
public void setIcao(String icao) { this.icao = icao; }
public double getLat() { return this.lat; }
public void setLat(double lat) { this.lat = lat; }
public double getLon() { return this.lon; }
public void setLon(double lon) { this.lon = lon; }
@Override
public String toString() {return "Airport: " + getAirportName() + ": ICAO: " + getIcao();}
}
// The DistanceAndBearing class does all the work.
package distanceAndBearing;
import java.io.File;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.stream.Collectors;
public class DistanceAndBearing {
private final double earthRadius = 6371;
private File datFile;
private List<Airport> airports;
public DistanceAndBearing() { this.airports = new ArrayList<Airport>(); }
public boolean readFile(String filename) {
this.datFile = new File(filename);
try {
Scanner fileScanner = new Scanner(datFile);
String line;
while (fileScanner.hasNextLine()) {
line = fileScanner.nextLine();
line = line.replace(", ", "; "); // There are some commas in airport names in the dat
// file
line = line.replace(",\",\"", "\",\""); // There are some airport names that
// end in a comma in the dat file
String[] parts = line.split(",");
Airport airport = new Airport();
airport.setAirportName(parts[1].replace("\"", "")); // Remove the double quotes from the string
airport.setCountry(parts[3].replace("\"", "")); // Remove the double quotes from the string
airport.setIcao(parts[5].replace("\"", "")); // Remove the double quotes from the string
airport.setLat(Double.valueOf(parts[6]));
airport.setLon(Double.valueOf(parts[7]));
this.airports.add(airport);
}
fileScanner.close();
return true; // Return true if the read was successful
} catch (Exception e) {
e.printStackTrace();
return false; // Return false if the read was not successful
}}
public double[] calculate(double lat1, double lon1, double lat2, double lon2) {
double[] results = new double[2];
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lon2 - lon1);
double rlat1 = Math.toRadians(lat1);
double rlat2 = Math.toRadians(lat2);
double a = Math.pow(Math.sin(dLat / 2), 2)
+ Math.pow(Math.sin(dLon / 2), 2) * Math.cos(rlat1) * Math.cos(rlat2);
double c = 2 * Math.asin(Math.sqrt(a));
double distance = earthRadius * c;
DecimalFormat df = new DecimalFormat("#0.00");
distance = Double.valueOf(df.format(distance));
results[0] = distance;
double X = Math.cos(rlat2) * Math.sin(dLon);
double Y = Math.cos(rlat1) * Math.sin(rlat2) - Math.sin(rlat1) * Math.cos(rlat2) * Math.cos(dLon);
double heading = Math.atan2(X, Y);
heading = Math.toDegrees(heading);
results[1] = heading;
return results;
}
public Airport searchByName(final String name) {
Airport airport = new Airport();
List<Airport> results = this.airports.stream().filter(ap -> ap.getAirportName().contains(name))
.collect(Collectors.toList());
airport = results.get(0);
return airport;
}
public List<Airport> findClosestAirports(double lat, double lon) {
// TODO Auto-generated method stub
Map<Double, Airport> airportDistances = new HashMap<>();
Map<Double, Airport> airportHeading = new HashMap<>();
List<Airport> closestAirports = new ArrayList<Airport>();
// This loop finds the distance and heading for every airport and saves them
// into two separate Maps
for (Airport ap : this.airports) {
double[] result = calculate(lat, lon, ap.getLat(), ap.getLon());
airportDistances.put(result[0], ap);
airportHeading.put(result[1], ap);
}
// Get the keyset from the distance map and sort it.
ArrayList<Double> distances = new ArrayList<>(airportDistances.keySet());
Collections.sort(distances);
// Get the first 20 airports by finding the value in the distance map for
// each distance in the sorted Arraylist. Then get the airport name, and
// use that to search for the airport in the airports List.
// Save that into a new List
for (int i = 0; i < 20; i++) { closestAirports.add(searchByName((airportDistances.get(distances.get(i)).getAirportName())));}
// Find the distance and heading for each of the top 20 airports.
Map<String, Double> distanceMap = new HashMap<>();
for (Double d : airportDistances.keySet()) { distanceMap.put(airportDistances.get(d).getAirportName(), d);}
Map<String, Double> headingMap = new HashMap<>();
for (Double d : airportHeading.keySet()) {
double d2 = d;
if(d2<0){d2+=360'}
headingMap.put(airportHeading.get(d).getAirportName(), d2); }
// Print the results.
System.out.printf("%-4s %-40s %-25s %-6s %12s %15s\n", "Num", "Airport", "Country", "ICAO", "Distance", "Bearing");
System.out.println("-----------------------------------------------------------------------------------------------------------");
int i = 0;
for (Airport a : closestAirports) {
System.out.printf("%-4s %-40s %-25s %-6s %12.1f %15.0f\n", ++i, a.getAirportName(), a.getCountry(), a.getIcao(), distanceMap.get(a.getAirportName())*0.5399568, headingMap.get(a.getAirportName()));
}
return closestAirports;
}
}
Usage
import distanceAndBearing.DistanceAndBearing;
public class MyClass {
public static void main(String[] args) {
DistanceAndBearing dandb = new DistanceAndBearing();
dandb.readFile("airports.txt");
dandb.findClosestAirports(51.514669,2.198581);
}
}
Output when lat = 51.514669 and lon = 2.198581
Num Airport Country ICAO Distance Bearing ----------------------------------------------------------------------------------------------------------- 1 Koksijde Air Base Belgium EBFN 30.7 146 2 Ostend-Bruges International Airport Belgium EBOS 31.3 127 3 Kent International Airport United Kingdom EGMH 33.5 252 4 Calais-Dunkerque Airport France LFAC 34.4 196 5 Westkapelle heliport Belgium EBKW 42.6 105 6 Lympne Airport United Kingdom EGMK 51.6 240 7 Ursel Air Base Belgium EBUL 52.8 114 8 Southend Airport United Kingdom EGMC 56.2 274 9 Merville-Calonne Airport France LFQT 56.4 163 10 Wevelgem Airport Belgium EBKT 56.5 137 11 Midden-Zeeland Airport Netherlands EHMZ 57.3 90 12 Lydd Airport United Kingdom EGMD 58.0 235 13 RAF Wattisham United Kingdom EGUW 59.0 309 14 Beccles Airport United Kingdom EGSM 59.3 339 15 Lille/Marcq-en-Baroeul Airport France LFQO 59.7 146 16 Lashenden (Headcorn) Airfield United Kingdom EGKH 62.2 250 17 Le Touquet-Côte d'Opale Airport France LFAT 63.7 200 18 Rochester Airport United Kingdom EGTO 64.2 262 19 Lille-Lesquin Airport France LFQQ 66.2 149 20 Thurrock Airfield United Kingdom EGMT 68.4 272
jq
Adapted from Wren
Works with gojq, the Go implementation of jq.
jq does not natively support the complexities of CSV, so to focus on the task at hand, this entry assumes that the data file is in TSV format. However, a jq program for converting CSV to JSON arrays may be found here on RC at Convert_CSV_records_to_TSV; it has been tested against the airlines.dat file.
def pi: 1|atan * 4;
# Read a TSV file one line at a time,
# and emit a stream of arrays, one array per line:
def readTSV: [inputs | sub(" *$";"") / "\t" ];
def airports:
[readTSV
| {airportID: (.[0] | tonumber),
name : .[1],
city : .[2],
country : .[3],
iata : .[4],
icao : .[5],
latitude : (.[6] | tonumber),
longitude : (.[7] | tonumber),
altitude : (.[8] | tonumber),
timezone : .[9],
dst : .[10],
tzOlson : .[11],
type : .[12],
source : .[13]} ];
def calculateDistance(lat1; lon1; lat2; lon2; units):
if (lat1 == lat2 and lon1 == lon2) then 0
else pi as $pi
| ($pi * lat1 / 180) as $radlat1
| ($pi * lat2 / 180) as $radlat2
| (lon1 - lon2) as $theta
| ($pi * $theta / 180) as $radtheta
| {}
| .dist = ($radlat1|sin) * ($radlat2|sin) +
($radlat1|cos) * ($radlat2|cos) * ($radtheta|cos)
| if .dist > 1 then .dist = 1 else . end
| .dist |= (acos * 180 / $pi) * 60 * 1.1515576 # distance in statute miles
| if (units == "K") then .dist *= 1.609344 # distance in kilometers
elif (units == "N") then .dist *= 0.868976 # distance in nautical miles
else .
end
| .dist
end ;
def calculateBearing(lat1; lon1; lat2; lon2):
if (lat1 == lat2 and lon1 == lon2) then 0
else pi as $pi
| ($pi * lat1 / 180) as $radlat1
| ($pi * lat2 / 180) as $radlat2
| ($pi * (lon2 - lon1) / 180) as $raddlon
| ( ($raddlon|sin) * ($radlat2|cos) ) as $y
| (($radlat1|cos) * ($radlat2|sin) -
($radlat1|sin) * ($radlat2|cos) * ($raddlon|cos)) as $x
| ((atan2($y;$x) * 180 / $pi) + 360) % 360
end;
# request from airplane at position $X; $Y
def request($X; $Y):
airports as $airports
| reduce $airports[] as $a ([];
((calculateDistance($X; $Y; $a.latitude; $a.longitude; "N") * 10 | round)
/ 10) as $dist
| (calculateBearing($X; $Y; $a.latitude; $a.longitude)|round ) as $bear
| . + [ [$a.name, $a.country, $a.icao, $dist, $bear] ] ) ;
def lpad($len): tostring | ($len - length) as $l | (" " * $l)[:$l] + .;
def fmt:
def l($n): lpad($n);
"\(.[0] |l(36)) | \(.[1]|l(14)) | \(.[2]|l(4)) |\(.[3]|l(15)) |\(.[4]|l(10))";
# Example:
def display($n):
"Closest \($n) destinations relative to position \(.[0]), \(.[1]):",
"",
" Name | Country | ICAO | Distance in NM | Bearing in ° ",
"-------------------------------------+----------------+------+----------------+--------------",
(request(.[0]; .[1])
| sort_by( .[3] )
| .[0:$n][] | fmt ) ;
[51.514669, 2.198581] | display(20)
''Invocation'': jq -nrRf distance-and-bearing.jq airports.tsv
{{output}}
<pre>
Closest 20 destinations relative to position 51.514669, 2.198581:
Name | Country | ICAO | Distance in NM | Bearing in °
-------------------------------------+----------------+------+----------------+--------------
Koksijde Air Base | Belgium | EBFN | 30.7 | 146
Ostend-Bruges International Airport | Belgium | EBOS | 31.3 | 127
Kent International Airport | United Kingdom | EGMH | 33.5 | 252
Calais-Dunkerque Airport | France | LFAC | 34.4 | 195
Westkapelle heliport | Belgium | EBKW | 42.6 | 105
Lympne Airport | United Kingdom | EGMK | 51.6 | 240
Ursel Air Base | Belgium | EBUL | 52.8 | 114
Southend Airport | United Kingdom | EGMC | 56.2 | 274
Merville-Calonne Airport | France | LFQT | 56.4 | 162
Wevelgem Airport | Belgium | EBKT | 56.5 | 137
Midden-Zeeland Airport | Netherlands | EHMZ | 57.3 | 89
Lydd Airport | United Kingdom | EGMD | 58 | 235
RAF Wattisham | United Kingdom | EGUW | 59 | 309
Beccles Airport | United Kingdom | EGSM | 59.3 | 339
Lille/Marcq-en-Baroeul Airport | France | LFQO | 59.7 | 146
Lashenden (Headcorn) Airfield | United Kingdom | EGKH | 62.2 | 250
Le Touquet-Côte d'Opale Airport | France | LFAT | 63.7 | 200
Rochester Airport | United Kingdom | EGTO | 64.2 | 261
Lille-Lesquin Airport | France | LFQQ | 66.2 | 149
Thurrock Airfield | United Kingdom | EGMT | 68.4 | 271
</pre>
=={{header|Julia}}==
<syntaxhighlight lang="julia">using DataFrames
using CSV
const EARTH_RADIUS_KM = 6372.8
const TASK_CONVERT_NM = 0.0094174
const AIRPORT_DATA_FILE = "airports.dat.txt"
const QUERY = (latitude = 51.514669, longitude = 2.198581)
"""
Given two latitude, longitude pairs in degrees for two points on the Earth,
get distance (nautical miles) and initial direction of travel (degrees)
for travel from lat1, lon1 to lat2, lon2
"""
function haversine(lat1, lon1, lat2, lon2)
dlat = lat2 - lat1
dlon = lon2 - lon1
a = sind(dlat / 2)^2 + cosd(lat1) * cosd(lat2) * sind(dlon / 2)^2
c = 2.0 * asind(sqrt(a))
theta = atand(sind(dlon) * cosd(lat2),
cosd(lat1) * sind(lat2) - sind(lat1) * cosd(lat2) * cosd(dlon))
theta = (theta + 360) % 360
return EARTH_RADIUS_KM * c * TASK_CONVERT_NM, theta
end
""" Given latitude and longitude, find `wanted` closest airports in database file csv. """
function find_nearest_airports(latitude, longitude, wanted = 20, csv = AIRPORT_DATA_FILE)
airports = DataFrame(CSV.File(csv))[:, [2, 4, 6, 7, 8]]
airports[!, "d"] .= 0.0
airports[!, "b"] .= 0
rename!(airports, [:Name, :Country, :ICAO, :Latitude_deg, :Longitude_deg, :Distance, :Bearing])
for r in eachrow(airports)
distance, bearing = haversine(latitude, longitude, r.Latitude_deg, r.Longitude_deg)
r.Distance, r.Bearing = round(distance, digits = 1), Int(round(bearing))
end
sort!(airports, :Distance)
return airports[1:wanted, [:Name, :Country, :ICAO, :Distance, :Bearing]]
end
println(find_nearest_airports(QUERY.latitude, QUERY.longitude))
- Output:
20×5 DataFrame Row │ Name Country ICAO Distance Bearing │ String String String7 Float64 Int64 ─────┼─────────────────────────────────────────────────────────────────────────────── 1 │ Koksijde Air Base Belgium EBFN 30.6 146 2 │ Ostend-Bruges International Airp… Belgium EBOS 31.3 127 3 │ Kent International Airport United Kingdom EGMH 33.5 252 4 │ Calais-Dunkerque Airport France LFAC 34.4 196 5 │ Westkapelle heliport Belgium EBKW 42.6 105 6 │ Lympne Airport United Kingdom EGMK 51.6 240 7 │ Ursel Air Base Belgium EBUL 52.8 114 8 │ Southend Airport United Kingdom EGMC 56.2 274 9 │ Merville-Calonne Airport France LFQT 56.3 163 10 │ Wevelgem Airport Belgium EBKT 56.4 137 11 │ Midden-Zeeland Airport Netherlands EHMZ 57.2 90 12 │ Lydd Airport United Kingdom EGMD 58.0 235 13 │ RAF Wattisham United Kingdom EGUW 59.0 309 14 │ Beccles Airport United Kingdom EGSM 59.3 339 15 │ Lille/Marcq-en-Baroeul Airport France LFQO 59.7 146 16 │ Lashenden (Headcorn) Airfield United Kingdom EGKH 62.2 250 17 │ Le Touquet-Côte d'Opale Airport France LFAT 63.7 200 18 │ Rochester Airport United Kingdom EGTO 64.2 262 19 │ Lille-Lesquin Airport France LFQQ 66.2 149 20 │ Thurrock Airfield United Kingdom EGMT 68.4 272
PARI/GP
/* copy CSV aiports.dat from URL */
csv=externstr("curl 'https://raw.githubusercontent.com/jpatokal/openflights/master/data/airports.dat'");
/* We want an easy convert from CSV to matrix. */
/* Remove all those backslashes in airports.dat */
{
for(i = 1 , #csv, tmp = Vecsmall(csv[i]);
for(j = 1 , #tmp, if(tmp[j]==92, tmp[j]=78));
csv[i] = strchr(tmp)
);
};
{
convert(csv,mat)=
write1(mat,"[");
for(i=1, #csv-1,
write1(mat,csv[i]); write1(mat,";")
);
write1(mat,csv[#csv]); write(mat,"]")
};
convert(csv,airports);
M = read(airports);
{
distance(lat1, lon1, lat2, lon2, unit = "mi") =
my(dist = 0, rlat1, rlat2, rtheta);
if(lat1!=lat2 || lon1!=lon2,
rlat1 = lat1*Pi/180; rlat2 = lat2*Pi/180;
rtheta = (lon1-lon2)*Pi/180;
dist = sin(rlat1)*sin(rlat2) + cos(rlat1)*cos(rlat2)*cos(rtheta);
dist = acos(min(dist,1))*180/Pi*60*1.1515576;
if(unit=="Km", dist*= 1.609344, unit=="NM", dist*= 0.868976)
);dist
};
{
bearing(lat1, lon1, lat2, lon2)=
my(bear, rlat1, rlat2, rdlon, x, y);
if (lat1!=lat2 || lon1!=lon2,
rlat1 = lat1*Pi/180; rlat2 = lat2*Pi/180;
rdlon = (lon2-lon1)*Pi/180;
y = sin(rdlon)*cos(rlat2);
x = cos(rlat1)*sin(rlat2) - sin(rlat1)*cos(rlat2)*cos(rdlon);
/* Pari/GP has no built-in atan2() function. */
if( x>0, bear=atan(y/x), x<0 && y>0, bear=atan(y/x)+Pi,
x<0 && y==0, bear=Pi, x<0 && y<0, bear=atan(y/x)-Pi,
x==0 && y>0, bear=Pi/2, x==0 && y<0, bear=-Pi/2);
bear = (bear*180/Pi+360)%360;
);bear
};
{
request(lat, lon, r = 20 , unit = "NM")=
my(D = vector(#M[,1]), R = matrix(r+1, 5));
R[1,1] = "NAME"; R[1,2] = "COUNTRY"; R[1,3] = "ICAO"; R[1,4] = "DISTANCE"; R[1,5] = "BEARING";
printf("%-38s\t%-16s\t%-4s\t%s\t%s\n", R[1,1], R[1,2], R[1,3], R[1,4], R[1,5]);
for(i = 1, #M[,1],
D[i] = [distance(lat, lon, M[i,7], M[i,8], unit) , i];
);
D=vecsort(D);
for(i = 1, r,
R[i+1,1] = M[D[i][2],2]; R[i+1,2] = M[D[i][2],4]; R[i+1,3] = M[D[i][2],6];
R[i+1,4] = D[i][1]; R[i+1,5] = bearing(lat, lon, M[D[i][2],7], M[D[i][2],8]);
printf("%-38s\t%-16s\t%-4s\t%8.1f\t%7.0f\n", R[i+1,1], R[i+1,2], R[i+1,3], R[i+1,4], R[i+1,5]);
);
}
request(51.514669, 2.198581)
- Output:
NAME COUNTRY ICAO DISTANCE BEARING Koksijde Air Base Belgium EBFN 30.7 146 Ostend-Bruges International Airport Belgium EBOS 31.3 127 Kent International Airport United Kingdom EGMH 33.5 252 Calais-Dunkerque Airport France LFAC 34.4 196 Westkapelle heliport Belgium EBKW 42.6 105 Lympne Airport United Kingdom EGMK 51.6 240 Ursel Air Base Belgium EBUL 52.8 114 Southend Airport United Kingdom EGMC 56.2 274 Merville-Calonne Airport France LFQT 56.4 163 Wevelgem Airport Belgium EBKT 56.5 137 Midden-Zeeland Airport Netherlands EHMZ 57.3 90 Lydd Airport United Kingdom EGMD 58.0 235 RAF Wattisham United Kingdom EGUW 59.0 309 Beccles Airport United Kingdom EGSM 59.3 339 Lille/Marcq-en-Baroeul Airport France LFQO 59.7 146 Lashenden (Headcorn) Airfield United Kingdom EGKH 62.2 250 Le Touquet-Côte d'Opale Airport France LFAT 63.7 200 Rochester Airport United Kingdom EGTO 64.2 262 Lille-Lesquin Airport France LFQQ 66.2 149 Thurrock Airfield United Kingdom EGMT 68.4 272 cpu time = 183 ms, real time = 183 ms.
Phix
without js -- file i/o enum Airport_ID, Name, City, Country, IATA, ICAO, Latitude, Longitude, Altitude, Timezone, DST, Tz_Olson, Type, Source function tabulate(sequence s) s = split(trim(s),"\n") for i,ri in s do ri = split(ri,',') if length(ri)!=14 then ri[2] &= ","&ri[3] ri[3..3] = {} end if for j in {Airport_ID, Latitude, Longitude, Altitude} do ri[j] = to_number(ri[j]) end for for j in {Name,City,Country,ICAO} do ri[j] = trim(ri[j],`"`) end for s[i] = ri end for return s end function constant airports = tabulate(get_text("airports.dat")) function distance(atom lat1, lon1, lat2, lon2, integer units) atom dist = 0 if lat1!=lat2 or lon1!=lon2 then atom radlat1 = lat1*PI/180, radlat2 = lat2*PI/180, radtheta = (lon1-lon2)*PI/180 dist = sin(radlat1)*sin(radlat2) + cos(radlat1)*cos(radlat2)*cos(radtheta) dist = arccos(min(dist,1))*180/PI * 60*1.1515576 -- in Statute Miles if units = 'K' then dist *= 1.609344 end if -- in Kilometres if units = 'N' then dist *= 0.868976 end if -- in Nautical Miles end if return dist end function function bearing(atom lat1, lon1, lat2, lon2) atom bear = NULL if lat1!=lat2 or lon1!=lon2 then atom radlat1 = lat1*PI/180, radlat2 = lat2*PI/180, raddlon = (lon2-lon1)*PI/180, y = sin(raddlon)*cos(radlat2), x = cos(radlat1)*sin(radlat2) - sin(radlat1)*cos(radlat2)*cos(raddlon) bear = remainder(atan2(y, x)*180/PI+360,360) end if return bear; end function procedure query(atom lat,lon, integer rows=20) sequence r = {} for a in airports do atom lat2 = a[Latitude], lon2 = a[Longitude], dist = round(distance(lat,lon,lat2,lon2,'N'),10), bear = round(bearing(lat,lon,lat2,lon2)) r = append(r,{a[Name],a[Country],a[ICAO],dist,bear}) end for r = sort_columns(r,{4})[1..rows] printf(1," Name | Country | ICAO | Distance in NM | Bearing in ° \n") printf(1,"-------------------------------------+----------------+------+----------------+--------------\n") printf(1,"%s\n",{join(r,"\n",fmt:=" %-36s| %-15s| %4s | %4.1f | %3d")}) printf(1,"(%d rows)\n",rows) end procedure query(51.514669, 2.198581)
- Output:
Name | Country | ICAO | Distance in NM | Bearing in ° -------------------------------------+----------------+------+----------------+-------------- Koksijde Air Base | Belgium | EBFN | 30.7 | 146 Ostend-Bruges International Airport | Belgium | EBOS | 31.3 | 127 Kent International Airport | United Kingdom | EGMH | 33.5 | 252 Calais-Dunkerque Airport | France | LFAC | 34.4 | 196 Westkapelle heliport | Belgium | EBKW | 42.6 | 105 Lympne Airport | United Kingdom | EGMK | 51.6 | 240 Ursel Air Base | Belgium | EBUL | 52.8 | 114 Southend Airport | United Kingdom | EGMC | 56.2 | 274 Merville-Calonne Airport | France | LFQT | 56.4 | 163 Wevelgem Airport | Belgium | EBKT | 56.5 | 137 Midden-Zeeland Airport | Netherlands | EHMZ | 57.3 | 90 Lydd Airport | United Kingdom | EGMD | 58.0 | 235 RAF Wattisham | United Kingdom | EGUW | 59.0 | 309 Beccles Airport | United Kingdom | EGSM | 59.3 | 339 Lille/Marcq-en-Baroeul Airport | France | LFQO | 59.7 | 146 Lashenden (Headcorn) Airfield | United Kingdom | EGKH | 62.2 | 250 Le Touquet-Côte d'Opale Airport | France | LFAT | 63.7 | 200 Rochester Airport | United Kingdom | EGTO | 64.2 | 262 Lille-Lesquin Airport | France | LFQQ | 66.2 | 149 Thurrock Airfield | United Kingdom | EGMT | 68.4 | 272 (20 rows)
Python
''' Rosetta Code task Distance_and_Bearing '''
from math import radians, degrees, sin, cos, asin, atan2, sqrt
from pandas import read_csv
EARTH_RADIUS_KM = 6372.8
TASK_CONVERT_NM = 0.0094174
AIRPORT_DATA_FILE = 'airports.dat.txt'
QUERY_LATITUDE, QUERY_LONGITUDE = 51.514669, 2.198581
def haversine(lat1, lon1, lat2, lon2):
'''
Given two latitude, longitude pairs in degrees for two points on the Earth,
get distance (nautical miles) and initial direction of travel (degrees)
for travel from lat1, lon1 to lat2, lon2
'''
rlat1, rlon1, rlat2, rlon2 = [radians(x) for x in [lat1, lon1, lat2, lon2]]
dlat = rlat2 - rlat1
dlon = rlon2 - rlon1
arc = sin(dlat / 2) ** 2 + cos(rlat1) * cos(rlat2) * sin(dlon / 2) ** 2
clen = 2.0 * degrees(asin(sqrt(arc)))
theta = atan2(sin(dlon) * cos(rlat2),
cos(rlat1) * sin(rlat2) - sin(rlat1) * cos(rlat2) * cos(dlon))
theta = (degrees(theta) + 360) % 360
return EARTH_RADIUS_KM * clen * TASK_CONVERT_NM, theta
def find_nearest_airports(latitude, longitude, wanted=20, csv=AIRPORT_DATA_FILE):
''' Given latitude and longitude, find `wanted` closest airports in database file csv. '''
airports = read_csv(csv, header=None, usecols=[1, 3, 5, 6, 7], names=[
'Name', 'Country', 'ICAO', 'Latitude', 'Longitude'])
airports['Distance'] = 0.0
airports['Bearing'] = 0
for (idx, row) in enumerate(airports.itertuples()):
distance, bearing = haversine(
latitude, longitude, row.Latitude, row.Longitude)
airports.at[idx, 'Distance'] = round(distance, ndigits=1)
airports.at[idx, 'Bearing'] = int(round(bearing))
airports.sort_values(by=['Distance'], ignore_index=True, inplace=True)
return airports.loc[0:wanted-1, ['Name', 'Country', 'ICAO', 'Distance', 'Bearing']]
print(find_nearest_airports(QUERY_LATITUDE, QUERY_LONGITUDE))
- Output:
Name Country ICAO Distance Bearing 0 Koksijde Air Base Belgium EBFN 30.7 146 1 Ostend-Bruges International Airport Belgium EBOS 31.3 127 2 Kent International Airport United Kingdom EGMH 33.5 252 3 Calais-Dunkerque Airport France LFAC 34.4 196 4 Westkapelle heliport Belgium EBKW 42.6 105 5 Lympne Airport United Kingdom EGMK 51.6 240 6 Ursel Air Base Belgium EBUL 52.8 114 7 Southend Airport United Kingdom EGMC 56.2 274 8 Merville-Calonne Airport France LFQT 56.4 163 9 Wevelgem Airport Belgium EBKT 56.5 137 10 Midden-Zeeland Airport Netherlands EHMZ 57.3 90 11 Lydd Airport United Kingdom EGMD 58.0 235 12 RAF Wattisham United Kingdom EGUW 59.0 309 13 Beccles Airport United Kingdom EGSM 59.3 339 14 Lille/Marcq-en-Baroeul Airport France LFQO 59.7 146 15 Lashenden (Headcorn) Airfield United Kingdom EGKH 62.2 250 16 Le Touquet-Côte d'Opale Airport France LFAT 63.7 200 17 Rochester Airport United Kingdom EGTO 64.2 262 18 Lille-Lesquin Airport France LFQQ 66.2 149 19 Thurrock Airfield United Kingdom EGMT 68.4 272
Raku
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
SQL /PostgreSQL
Create table and copy from URL.
-- create table airports with 14 columns
CREATE TABLE airports (
Airport_ID serial PRIMARY KEY,
Name VARCHAR NOT NULL,
City VARCHAR,
Country VARCHAR NOT NULL,
IATA VARCHAR,
ICAO VARCHAR,
Latitude double precision NOT NULL,
Longitude double precision NOT NULL,
Altitude SMALLINT,
Timezone VARCHAR,
DST VARCHAR,
Tz_Olson VARCHAR,
Type VARCHAR,
Source VARCHAR
);
-- copy CSV airports.dat from URL
COPY airports FROM
PROGRAM 'curl "https://raw.githubusercontent.com/jpatokal/openflights/master/data/airports.dat"'
WITH (FORMAT csv);
Functions for distance and bearing.
-- calculate distance
CREATE OR REPLACE FUNCTION calculate_distance(lat1 float, lon1 float, lat2 float, lon2 float, units varchar)
RETURNS numeric AS $dist$
DECLARE
dist float = 0;
radlat1 float;
radlat2 float;
theta float;
radtheta float;
BEGIN
IF lat1 = lat2 AND lon1 = lon2
THEN RETURN dist;
ELSE
radlat1 = pi() * lat1 / 180;
radlat2 = pi() * lat2 / 180;
theta = lon1 - lon2;
radtheta = pi() * theta / 180;
dist = sin(radlat1) * sin(radlat2) + cos(radlat1) * cos(radlat2) * cos(radtheta);
IF dist > 1 THEN dist = 1; END IF;
dist = acos(dist);
dist = dist * 180 / pi();
-- Distance in Statute Miles
dist = dist * 60 * 1.1515576;
-- Distance in Kilometres
IF units = 'K' THEN dist = dist * 1.609344; END IF;
-- Distance in Nautical Miles
IF units = 'N' THEN dist = dist * 0.868976; END IF;
dist = dist::numeric;
RETURN dist;
END IF;
END;
$dist$ LANGUAGE plpgsql;
-- calculate bearing
CREATE OR REPLACE FUNCTION calculate_bearing(lat1 float, lon1 float, lat2 float, lon2 float)
RETURNS numeric AS $bear$
DECLARE
bear float = NULL;
radlat1 float;
radlat2 float;
raddlon float;
y float;
x float;
BEGIN
IF lat1 = lat2 AND lon1 = lon2
THEN RETURN bear;
ELSE
radlat1 = pi() * lat1 / 180;
radlat2 = pi() * lat2 / 180;
raddlon = pi() * (lon2 - lon1) / 180;
y = sin(raddlon) * cos(radlat2);
x = cos(radlat1) * sin(radlat2) - sin(radlat1) * cos(radlat2) * cos(raddlon);
bear = atan2(y, x) * 180 / pi();
bear = (bear::numeric + 360) % 360;
RETURN bear;
END IF;
END;
$bear$ LANGUAGE plpgsql;
Request from airplane at position ( 51.514669, 2.198581 ).
Select
Name "Name",
Country "Country",
ICAO "ICAO",
ROUND(calculate_distance(51.514669, 2.198581, Latitude, Longitude, 'N'), 1) "Distance in NM",
ROUND(calculate_bearing(51.514669, 2.198581, Latitude, Longitude), 0) "Bearing in °"
From
airports
ORDER BY "Distance in NM"
LIMIT 20;
- Output:
Name | Country | ICAO | Distance in NM | Bearing in ° -------------------------------------+----------------+------+----------------+-------------- Koksijde Air Base | Belgium | EBFN | 30.7 | 146 Ostend-Bruges International Airport | Belgium | EBOS | 31.3 | 127 Kent International Airport | United Kingdom | EGMH | 33.5 | 252 Calais-Dunkerque Airport | France | LFAC | 34.4 | 196 Westkapelle heliport | Belgium | EBKW | 42.6 | 105 Lympne Airport | United Kingdom | EGMK | 51.6 | 240 Ursel Air Base | Belgium | EBUL | 52.8 | 114 Southend Airport | United Kingdom | EGMC | 56.2 | 274 Merville-Calonne Airport | France | LFQT | 56.4 | 163 Wevelgem Airport | Belgium | EBKT | 56.5 | 137 Midden-Zeeland Airport | Netherlands | EHMZ | 57.3 | 90 Lydd Airport | United Kingdom | EGMD | 58.0 | 235 RAF Wattisham | United Kingdom | EGUW | 59.0 | 309 Beccles Airport | United Kingdom | EGSM | 59.3 | 339 Lille/Marcq-en-Baroeul Airport | France | LFQO | 59.7 | 146 Lashenden (Headcorn) Airfield | United Kingdom | EGKH | 62.2 | 250 Le Touquet-Côte d'Opale Airport | France | LFAT | 63.7 | 200 Rochester Airport | United Kingdom | EGTO | 64.2 | 262 Lille-Lesquin Airport | France | LFQQ | 66.2 | 149 Thurrock Airfield | United Kingdom | EGMT | 68.4 | 272 (20 rows)
Wren
However, rather than use Wren-sql, we program it so that it can be run from Wren-CLI. Runtime is about 0.24 seconds.
import "io" for File
import "./dynamic" for Tuple
import "./str" for Str
import "./fmt" for Fmt
var airportFields = [
"airportID",
"name",
"city",
"country",
"iata",
"icao",
"latitude",
"longitude",
"altitude",
"timezone",
"dst",
"tzOlson",
"type",
"source"
]
var Airport = Tuple.create("Airport", airportFields)
var fileName = "airports.dat" // local copy
var lines = File.read(fileName).trimEnd().split("\n")
var lc = lines.count
var airports = List.filled(lc, null)
for (i in 0...lc) {
var fields = lines[i].split(",").map { |f| f.replace("\"", "") }.toList
if (fields.count > 14) { // there are field(s) with embedded comma(s)
fields = Str.splitCsv(lines[i])
}
var airportID = Num.fromString(fields[0])
var name = fields[1]
var city = fields[2]
var country = fields[3]
var iata = fields[4]
var icao = fields[5]
var latitude = Num.fromString(fields[6])
var longitude = Num.fromString(fields[7])
var altitude = Num.fromString(fields[8])
var timezone = fields[9]
var dst = fields[10]
var tzOlson = fields[11]
var type = fields[12]
var source = fields[13]
airports[i] = Airport.new(airportID, name, city, country, iata, icao, latitude, longitude,
altitude, timezone, dst, tzOlson, type, source)
}
var calculateDistance = Fn.new { |lat1, lon1, lat2, lon2, units|
if (lat1 == lat2 && lon1 == lon2) return 0
var radlat1 = Num.pi * lat1 / 180
var radlat2 = Num.pi * lat2 / 180
var theta = lon1 - lon2
var radtheta = Num.pi * theta / 180
var dist = radlat1.sin * radlat2.sin + radlat1.cos * radlat2.cos * radtheta.cos
if (dist > 1) dist = 1
dist = dist.acos * 180 / Num.pi * 60 * 1.1515576 // distance in statute miles
if (units == "K") dist = dist * 1.609344 // distance in kilometers
if (units == "N") dist = dist * 0.868976 // distance in nautical miles
return dist
}
var calculateBearing = Fn.new { |lat1, lon1, lat2, lon2|
if (lat1 == lat2 && lon1 == lon2) return 0
var radlat1 = Num.pi * lat1 / 180
var radlat2 = Num.pi * lat2 / 180
var raddlon = Num.pi * (lon2 - lon1) / 180
var y = raddlon.sin * radlat2.cos
var x = radlat1.cos * radlat2.sin - radlat1.sin * radlat2.cos * raddlon.cos
var bear = y.atan(x) * 180 / Num.pi
return (bear + 360) % 360
}
// request from airplane at position (51.514669, 2.198581)
var query = List.filled(airports.count, null)
for (i in 0...airports.count) {
var a = airports[i]
var dist = calculateDistance.call(51.514669, 2.198581, a.latitude, a.longitude, "N")
dist = (dist * 10).round / 10
var bear = calculateBearing.call(51.514669, 2.198581, a.latitude, a.longitude).round
query[i] = [a.name, a.country, a.icao, dist, bear]
}
query.sort { |q1, q2| q1[3] < q2[3] }
System.print(" Name | Country | ICAO | Distance in NM | Bearing in ° ")
System.print("-------------------------------------+----------------+------+----------------+--------------")
var fmt = " $-36s| $-15s| $4s | $4.1f | $3d"
for (i in 0..19) Fmt.lprint(fmt, query[i])
System.print("(20 rows)")
- Output:
Name | Country | ICAO | Distance in NM | Bearing in ° -------------------------------------+----------------+------+----------------+-------------- Koksijde Air Base | Belgium | EBFN | 30.7 | 146 Ostend-Bruges International Airport | Belgium | EBOS | 31.3 | 127 Kent International Airport | United Kingdom | EGMH | 33.5 | 252 Calais-Dunkerque Airport | France | LFAC | 34.4 | 196 Westkapelle heliport | Belgium | EBKW | 42.6 | 105 Lympne Airport | United Kingdom | EGMK | 51.6 | 240 Ursel Air Base | Belgium | EBUL | 52.8 | 114 Southend Airport | United Kingdom | EGMC | 56.2 | 274 Merville-Calonne Airport | France | LFQT | 56.4 | 163 Wevelgem Airport | Belgium | EBKT | 56.5 | 137 Midden-Zeeland Airport | Netherlands | EHMZ | 57.3 | 90 Lydd Airport | United Kingdom | EGMD | 58.0 | 235 RAF Wattisham | United Kingdom | EGUW | 59.0 | 309 Beccles Airport | United Kingdom | EGSM | 59.3 | 339 Lille/Marcq-en-Baroeul Airport | France | LFQO | 59.7 | 146 Lashenden (Headcorn) Airfield | United Kingdom | EGKH | 62.2 | 250 Le Touquet-Côte d'Opale Airport | France | LFAT | 63.7 | 200 Rochester Airport | United Kingdom | EGTO | 64.2 | 262 Lille-Lesquin Airport | France | LFQQ | 66.2 | 149 Thurrock Airfield | United Kingdom | EGMT | 68.4 | 272 (20 rows)