locationcode/airports.go
Ask Bjørn Hansen e926800f44
All checks were successful
continuous-integration/drone/push Build is passing
First version
2022-04-08 01:49:18 -07:00

160 lines
3.5 KiB
Go

package main
import (
"flag"
"fmt"
"log"
"net/http"
"os"
"path"
"sort"
"strconv"
"strings"
"github.com/golang/geo/s2"
alphafoxtrot "github.com/grumpypixel/go-airport-finder"
"github.com/labstack/echo/v4"
)
type Airport struct {
Name string
Code string
Distance float64
data *alphafoxtrot.Airport
}
func main() {
var dataDir = flag.String("data-dir", "./data", "Data cache directory")
flag.Parse()
validateData(*dataDir)
finder := alphafoxtrot.NewAirportFinder()
// LoadOptions come with preset filepaths
options := alphafoxtrot.PresetLoadOptions(*dataDir)
// filter := alphafoxtrot.AirportTypeLarge | alphafoxtrot.AirportTypeMedium
filter := alphafoxtrot.AirportTypeRunways
// Load the data into memory
if err := finder.Load(options, filter); len(err) > 0 {
log.Println("errors:", err)
}
e := echo.New()
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "location code service")
})
e.GET("/v1/code", func(c echo.Context) error {
countryISOCode := c.QueryParam("cc")
countryISOCode = strings.ToUpper(countryISOCode)
radiusString := c.QueryParam("radius")
var radiusKM int
if len(radiusString) > 0 {
radiusKM, _ = strconv.Atoi(radiusString)
}
if radiusKM < 10 {
radiusKM = 100
}
latitude, longitude, err := getLatLng(c)
if err != nil {
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)
}
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)
})
e.Logger.Fatal(e.Start(":8000"))
}
func validateData(dataDir string) {
downloadFiles := false
for _, filename := range alphafoxtrot.OurAirportsFiles {
filepath := path.Join(dataDir, filename)
if _, err := os.Stat(filepath); os.IsNotExist(err) {
downloadFiles = true
break
}
}
if downloadFiles {
fmt.Println("Downloading CSV files from OurAirports.com...")
alphafoxtrot.DownloadDatabase(dataDir)
}
}
func getLatLng(c echo.Context) (float64, float64, error) {
latitudeStr := c.QueryParam("lat") // 37.3793
longitudeStr := c.QueryParam("lng") // -122.12
latitude, err := strconv.ParseFloat(latitudeStr, 64)
if err != nil {
return 0, 0, err
}
longitude, err := strconv.ParseFloat(longitudeStr, 64)
if err != nil {
return 0, 0, err
}
return latitude, longitude, nil
}