1 | package main
|
2 |
|
3 | import (
|
4 | "encoding/csv"
|
5 | "fmt"
|
6 | "io"
|
7 | "math"
|
8 | "os"
|
9 | "strconv"
|
10 | "strings"
|
11 | )
|
12 |
|
13 | const d2r = (math.Pi / 180.0)
|
14 |
|
15 | // Haversine calculates linear distance between two pairs
|
16 | func Haversine(start, dest [2]float64) float64 {
|
17 | dlong := (dest[1] - start[1]) * d2r
|
18 | dlat := (dest[0] - start[0]) * d2r
|
19 | a := math.Pow(math.Sin(dlat/2.0), 2) + math.Cos(start[0]*d2r)*math.Cos(dest[0]*d2r)*math.Pow(math.Sin(dlong/2.0), 2)
|
20 | c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
|
21 | distance := 6367 * c
|
22 | return distance
|
23 | }
|
24 |
|
25 | func GetDistance(start, dest Airport) float64 {
|
26 | return Haversine([2]float64{start.Latitude, start.Longitude}, [2]float64{dest.Latitude, dest.Longitude})
|
27 | }
|
28 |
|
29 | func NearBy(threshold float64, start Airport, airports AirportMap) []Airport {
|
30 | nearby := []Airport{}
|
31 | for _, dest := range airports {
|
32 | if GetDistance(start, dest) <= threshold {
|
33 | if dest.Code != start.Code {
|
34 | nearby = append(nearby, dest)
|
35 | }
|
36 | }
|
37 | }
|
38 | return nearby
|
39 | }
|
40 |
|
41 | func FindRoutes(threshold float64, airports map[string]Airport, departure, arrival Airport) ([]Airport, []Airport) {
|
42 | return NearBy(threshold, departure, airports), NearBy(threshold, arrival, airports)
|
43 | }
|
44 |
|
45 | // icao_code,iata_code,name,city,country,lat_deg,lat_min,lat_sec,lat_dir,lon_deg,lon_min,lon_sec,lon_dir,altitude,lat_decimal,lon_decimal
|
46 | // AYGA:GKA:GOROKA:GOROKA:PAPUA NEW GUINEA:006:004:054:S:145:023:030:E:01610:-6.082:145.392
|
47 | func parseAirport(row []string) *Airport {
|
48 | if len(row) != 16 {
|
49 | return nil
|
50 | }
|
51 | if row[1] == "N/A" {
|
52 | return nil
|
53 | }
|
54 | latitude, _ := strconv.ParseFloat(row[14], 64)
|
55 | longitude, _ := strconv.ParseFloat(row[15], 64)
|
56 | if latitude == 0 || longitude == 0 {
|
57 | return nil
|
58 | }
|
59 | airport := &Airport{
|
60 | Code: row[1],
|
61 | Name: row[2],
|
62 | City: row[3],
|
63 | Country: row[4],
|
64 | Latitude: latitude,
|
65 | Longitude: longitude,
|
66 | }
|
67 | return airport
|
68 | }
|
69 |
|
70 | //2B,410,AER,2965,KZN,2990,,0,CR2
|
71 | func parseRoute(airports AirportMap, row []string) *Route {
|
72 | if len(row) != 9 {
|
73 | return nil
|
74 | }
|
75 | start, end := row[2], row[4]
|
76 | if airports.HasCode(start) && airports.HasCode(end) {
|
77 | if start != end {
|
78 | return &Route{
|
79 | to: airports.Airport(end),
|
80 | from: airports.Airport(start),
|
81 | }
|
82 | } else {
|
83 | return nil
|
84 | }
|
85 | } else {
|
86 | return nil
|
87 | }
|
88 | }
|
89 |
|
90 | func Airports() AirportMap {
|
91 | airports := map[string]Airport{}
|
92 | reader := csv.NewReader(strings.NewReader(airportData))
|
93 | reader.Comma = ':'
|
94 | for {
|
95 | row, err := reader.Read()
|
96 | if err == io.EOF {
|
97 | return airports
|
98 | }
|
99 | airport := parseAirport(row)
|
100 | if airport != nil {
|
101 | airports[airport.Code] = *airport
|
102 | }
|
103 | }
|
104 | }
|
105 |
|
106 | func Routes(airports AirportMap) []Route {
|
107 | var routes []Route
|
108 | reader := csv.NewReader(strings.NewReader(routeData))
|
109 | reader.Comma = ','
|
110 | for {
|
111 | row, err := reader.Read()
|
112 | if err == io.EOF {
|
113 | return routes
|
114 | }
|
115 | route := parseRoute(airports, row)
|
116 | if route != nil {
|
117 | routes = append(routes, *route)
|
118 | }
|
119 | }
|
120 | }
|
121 |
|
122 | func maybe(err error) {
|
123 | if err != nil {
|
124 | fmt.Printf("Error: %s\n", err)
|
125 | os.Exit(1)
|
126 | }
|
127 | }
|