Ensure non-US countries with less airport codes get sufficient choices
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Ask Bjørn Hansen 2022-05-01 12:20:23 -07:00
parent 7f0c60b392
commit ba0a727150
3 changed files with 131 additions and 60 deletions

View File

@ -24,6 +24,10 @@ type Airport struct {
data *alphafoxtrot.Airport data *alphafoxtrot.Airport
} }
type Finder struct {
f *alphafoxtrot.AirportFinder
}
func main() { func main() {
var dataDir = flag.String("data-dir", "./data", "Data cache directory") var dataDir = flag.String("data-dir", "./data", "Data cache directory")
@ -32,7 +36,9 @@ func main() {
validateData(*dataDir) validateData(*dataDir)
finder := alphafoxtrot.NewAirportFinder() finder := &Finder{
f: alphafoxtrot.NewAirportFinder(),
}
// LoadOptions come with preset filepaths // LoadOptions come with preset filepaths
options := alphafoxtrot.PresetLoadOptions(*dataDir) options := alphafoxtrot.PresetLoadOptions(*dataDir)
@ -41,10 +47,47 @@ func main() {
filter := alphafoxtrot.AirportTypeRunways filter := alphafoxtrot.AirportTypeRunways
// Load the data into memory // 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) 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 := echo.New()
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
@ -63,13 +106,15 @@ func main() {
radiusString := c.QueryParam("radius") radiusString := c.QueryParam("radius")
var radiusKM int var radiusKM float64
if len(radiusString) > 0 { if len(radiusString) > 0 {
radiusKM, _ = strconv.Atoi(radiusString) radiusKM, _ = strconv.ParseFloat(radiusString, 64)
} }
if radiusKM < 10 { if radiusKM < 150 {
radiusKM = 100 radiusKM = 150
} else {
radiusKM = radiusKM * 1.5
} }
latitude, longitude, err := getLatLng(c) latitude, longitude, err := getLatLng(c)
@ -77,64 +122,82 @@ func main() {
return c.String(http.StatusBadRequest, fmt.Sprintf("invalid lat or lng: %s", err)) return c.String(http.StatusBadRequest, fmt.Sprintf("invalid lat or lng: %s", err))
} }
ipLocation := s2.LatLngFromDegrees(latitude, longitude) airports, err := finder.GetAirports(countryISOCode, radiusKM, latitude, longitude)
if err != nil {
maxResults := 100 return err
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)
} }
llCache := map[int]s2.LatLng{} return c.JSON(http.StatusOK, airports)
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)
}) })
e.Logger.Fatal(e.Start(":8000")) 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) { func validateData(dataDir string) {
downloadFiles := false downloadFiles := false
for _, filename := range alphafoxtrot.OurAirportsFiles { for _, filename := range alphafoxtrot.OurAirportsFiles {

8
go.mod
View File

@ -16,9 +16,9 @@ require (
github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-isatty v0.0.14 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect github.com/valyala/fasttemplate v1.2.1 // indirect
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921 // indirect golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f // indirect
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 // indirect golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f // indirect golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect
golang.org/x/text v0.3.7 // 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
) )

8
go.sum
View File

@ -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-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 h1:iU7T1X1J6yxDr0rda54sWGkHgOp5XJrqm79gcNlC2VM=
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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 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-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-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-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/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-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 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-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 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 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 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-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 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs=
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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/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-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=