From ba0a72715024fdcd916549ac48dc2b85e3c12043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ask=20Bj=C3=B8rn=20Hansen?= Date: Sun, 1 May 2022 12:20:23 -0700 Subject: [PATCH] Ensure non-US countries with less airport codes get sufficient choices --- airports.go | 175 +++++++++++++++++++++++++++++++++++----------------- go.mod | 8 +-- go.sum | 8 +++ 3 files changed, 131 insertions(+), 60 deletions(-) diff --git a/airports.go b/airports.go index 7234e5f..214f0de 100644 --- a/airports.go +++ b/airports.go @@ -24,6 +24,10 @@ type Airport struct { data *alphafoxtrot.Airport } +type Finder struct { + f *alphafoxtrot.AirportFinder +} + func main() { var dataDir = flag.String("data-dir", "./data", "Data cache directory") @@ -32,7 +36,9 @@ func main() { validateData(*dataDir) - finder := alphafoxtrot.NewAirportFinder() + finder := &Finder{ + f: alphafoxtrot.NewAirportFinder(), + } // LoadOptions come with preset filepaths options := alphafoxtrot.PresetLoadOptions(*dataDir) @@ -41,10 +47,47 @@ func main() { filter := alphafoxtrot.AirportTypeRunways // Load the data into memory - if err := finder.Load(options, filter); len(err) > 0 { + if err := finder.f.Load(options, filter); len(err) > 0 { log.Println("errors:", err) } + if args := flag.Args(); len(args) > 0 { + if len(args) != 4 { + fmt.Printf("[cc] [lat] [lng] [radius]\n") + os.Exit(2) + } + cc := strings.ToUpper(args[0]) + + latitude, err := strconv.ParseFloat(args[1], 64) + if err != nil { + fmt.Printf("could not parse %s as latitude: %s", args[1], err) + os.Exit(2) + } + + longitude, err := strconv.ParseFloat(args[2], 64) + if err != nil { + fmt.Printf("could not parse %s as longitude: %s", args[2], err) + os.Exit(2) + } + + radiusKM, err := strconv.ParseFloat(args[3], 64) + if err != nil { + fmt.Printf("could not parse %s as radius: %s", args[3], err) + os.Exit(2) + } + + airports, err := finder.GetAirports(cc, radiusKM, latitude, longitude) + if err != nil { + fmt.Printf("GetAirports error: %s\n", err) + } + + for _, a := range airports { + fmt.Printf("%s\t%s (%0f)\n", a.Code, a.Name, a.Distance) + } + + os.Exit(0) + } + e := echo.New() e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ @@ -63,13 +106,15 @@ func main() { radiusString := c.QueryParam("radius") - var radiusKM int + var radiusKM float64 if len(radiusString) > 0 { - radiusKM, _ = strconv.Atoi(radiusString) + radiusKM, _ = strconv.ParseFloat(radiusString, 64) } - if radiusKM < 10 { - radiusKM = 100 + if radiusKM < 150 { + radiusKM = 150 + } else { + radiusKM = radiusKM * 1.5 } latitude, longitude, err := getLatLng(c) @@ -77,64 +122,82 @@ func main() { return c.String(http.StatusBadRequest, fmt.Sprintf("invalid lat or lng: %s", err)) } - ipLocation := s2.LatLngFromDegrees(latitude, longitude) - - maxResults := 100 - radiusInMeters := float64(radiusKM) * 1000 - airportsRaw := finder.FindNearestAirportsByCountry(countryISOCode, latitude, longitude, radiusInMeters, maxResults, filter) - - airports := []*alphafoxtrot.Airport{} - for _, ap := range airportsRaw { - if len(ap.IATACode) == 0 { - continue - } - airports = append(airports, ap) + airports, err := finder.GetAirports(countryISOCode, radiusKM, latitude, longitude) + if err != nil { + return err } - llCache := map[int]s2.LatLng{} - for i, ap := range airports { - ll := s2.LatLngFromDegrees(ap.LatitudeDeg, ap.LongitudeDeg) - llCache[i] = ll - } - - r := []*Airport{} - - for i, airport := range airports { - // fmt.Printf("%d %s: %+v\n", i, airport.Name, airport) - - code := strings.ToLower(airport.Country.ISOCode + airport.IATACode) - - distance := float64(ipLocation.Distance(llCache[i])) * 6371.01 - - a := &Airport{ - Name: airport.Name, - Code: code, - Distance: distance, - data: airport, - } - r = append(r, a) - } - - sort.Slice(r, func(i, j int) bool { - if r[i].data.Type == r[j].data.Type { - return r[i].Distance < r[j].Distance - } - return airports[i].Type < airports[j].Type - }) - - if len(r) > 10 { - r = r[0:10] - } - - fmt.Printf("got %d airports, filtered to %d, returning %d\n", len(airportsRaw), len(airports), len(r)) - - return c.JSON(http.StatusOK, r) + return c.JSON(http.StatusOK, airports) }) e.Logger.Fatal(e.Start(":8000")) } +func (f *Finder) GetAirports(cc string, radiusKM, latitude, longitude float64) ([]*Airport, error) { + + log.Printf("GetAirports(%s %.2f %.4f %.4f)", cc, radiusKM, latitude, longitude) + + ipLocation := s2.LatLngFromDegrees(latitude, longitude) + + maxResults := 500 // some countries have a lot of airports without local codes + radiusInMeters := radiusKM * 1000 + airportsRaw := f.f.FindNearestAirportsByCountry( + cc, + latitude, longitude, + radiusInMeters, + maxResults, + alphafoxtrot.AirportTypeAll, // filtered at load time + ) + + airports := []*alphafoxtrot.Airport{} + for _, ap := range airportsRaw { + if len(ap.IATACode) == 0 { + continue + } + airports = append(airports, ap) + } + + llCache := map[int]s2.LatLng{} + for i, ap := range airports { + ll := s2.LatLngFromDegrees(ap.LatitudeDeg, ap.LongitudeDeg) + llCache[i] = ll + } + + r := []*Airport{} + + for i, airport := range airports { + fmt.Printf("%d %s: %+v\n", i, airport.Name, airport) + + code := strings.ToLower(airport.Country.ISOCode + airport.IATACode) + + distance := float64(ipLocation.Distance(llCache[i])) * 6371.01 + + a := &Airport{ + Name: airport.Name, + Code: code, + Distance: distance, + data: airport, + } + r = append(r, a) + } + + sort.Slice(r, func(i, j int) bool { + if r[i].data.Type == r[j].data.Type { + return r[i].Distance < r[j].Distance + } + return airports[i].Type < airports[j].Type + }) + + if len(r) > 15 { + r = r[0:15] + } + + fmt.Printf("got %d airports, filtered to %d, returning %d\n", len(airportsRaw), len(airports), len(r)) + + return r, nil +} + func validateData(dataDir string) { downloadFiles := false for _, filename := range alphafoxtrot.OurAirportsFiles { diff --git a/go.mod b/go.mod index 74bf1d8..eea7c9c 100644 --- a/go.mod +++ b/go.mod @@ -16,9 +16,9 @@ require ( github.com/mattn/go-isatty v0.0.14 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.1 // indirect - golang.org/x/crypto v0.0.0-20220408190544-5352b0902921 // indirect - golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 // indirect - golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f // indirect + golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f // indirect + golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect + golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect golang.org/x/text v0.3.7 // indirect - golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect + golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect ) diff --git a/go.sum b/go.sum index 5f3bca3..3a98909 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,12 @@ golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNy golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220408190544-5352b0902921 h1:iU7T1X1J6yxDr0rda54sWGkHgOp5XJrqm79gcNlC2VM= golang.org/x/crypto v0.0.0-20220408190544-5352b0902921/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc= +golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 h1:EN5+DfgmRMvRUrMGERW2gQl3Vc+Z7ZMnI/xdEpPSf0c= golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -40,12 +44,16 @@ golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12 h1:QyVthZKMsyaQwBTJE04jdNN0P golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= +golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=