diff --git a/.gitignore b/.gitignore index 8fcff32..ac1d5a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ data -locationcode +/locationcode diff --git a/airports.go b/airports.go index b293559..6a22caf 100644 --- a/airports.go +++ b/airports.go @@ -10,7 +10,6 @@ import ( "os" "os/signal" "path" - "sort" "strconv" "strings" "syscall" @@ -24,14 +23,9 @@ import ( "go.ntppool.org/common/tracing" "go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho" "golang.org/x/sync/errgroup" -) -type Airport struct { - Name string - Code string - Distance float64 - data *alphafoxtrot.Airport -} + "go.askask.com/locationcode/types" +) type Finder struct { f *alphafoxtrot.AirportFinder @@ -191,7 +185,7 @@ func main() { } } -func (f *Finder) GetAirports(ctx context.Context, cc string, radiusKM, latitude, longitude float64) ([]*Airport, error) { +func (f *Finder) GetAirports(ctx context.Context, cc string, radiusKM, latitude, longitude float64) ([]*types.Airport, error) { log := logger.FromContext(ctx) log.InfoContext(ctx, fmt.Sprintf("GetAirports(%s %.2f %.4f %.4f)", cc, radiusKM, latitude, longitude)) @@ -215,39 +209,24 @@ func (f *Finder) GetAirports(ctx context.Context, cc string, radiusKM, latitude, 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 := []*types.Airport{} - r := []*Airport{} + for _, airport := range airports { + // fmt.Printf("%d %s: %+v\n", i, airport.Name, airport) - for i, airport := range airports { - fmt.Printf("%d %s: %+v\n", i, airport.Name, airport) + a := types.NewAirport(airport) - code := strings.ToLower(airport.Country.ISOCode + airport.IATACode) + ll := s2.LatLngFromDegrees(airport.LatitudeDeg, airport.LongitudeDeg) + a.Distance = float64(ipLocation.Distance(ll)) * 6371.01 - 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 - }) + types.SortAirports(r) - if len(r) > 15 { - r = r[0:15] + maxReturns := 20 + if len(r) > maxReturns { + r = r[0:maxReturns] } log.InfoContext(ctx, "got airports", "count", len(airportsRaw), "filtered", len(airports), "returning", len(r)) diff --git a/client/locationcode/locationcodeclient.go b/client/locationcode/locationcodeclient.go new file mode 100644 index 0000000..83b37ee --- /dev/null +++ b/client/locationcode/locationcodeclient.go @@ -0,0 +1,85 @@ +package locationcode + +import ( + "context" + "encoding/json" + "fmt" + "net" + "net/http" + "net/url" + "os" + "time" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + + "go.askask.com/locationcode/types" +) + +var client http.Client + +func init() { + netTransport := &http.Transport{ + Dial: (&net.Dialer{ + Timeout: 5 * time.Second, + }).Dial, + TLSHandshakeTimeout: 5 * time.Second, + } + client = http.Client{ + Timeout: time.Second * 10, + Transport: netTransport, + } +} + +func GetAirports(ctx context.Context, countryCode string, lat, lng float64, radiusKM float64) ([]*types.Airport, error) { + ctx, span := otel.Tracer("locationcode").Start(ctx, "locationcode.GetAirports") + defer span.End() + + baseURL := os.Getenv("locationcode_service") + if len(baseURL) == 0 { + return nil, fmt.Errorf("locationcode_service not configured") + } + + q := url.Values{} + q.Set("cc", countryCode) + if radiusKM > 0 { + q.Set("radius", fmt.Sprintf("%f", radiusKM)) + } + q.Set("lat", fmt.Sprintf("%f", lat)) + q.Set("lng", fmt.Sprintf("%f", lng)) + + reqURL, err := url.Parse(fmt.Sprintf("http://%s/v1/code?%s", baseURL, q.Encode())) + if err != nil { + span.RecordError(err) + return nil, err + } + + span.SetAttributes(attribute.String("url", reqURL.String())) + + req, err := http.NewRequestWithContext(ctx, "GET", reqURL.String(), nil) + if err != nil { + span.RecordError(err) + return nil, err + } + + resp, err := client.Do(req) + if err != nil { + span.RecordError(err) + return nil, err + } + defer resp.Body.Close() + + // io.Copy(os.Stdout, resp.Body) + // return nil, nil + + dec := json.NewDecoder(resp.Body) + + airports := []*types.Airport{} + err = dec.Decode(&airports) + if err != nil { + span.RecordError(err) + return nil, err + } + + return airports, nil +} diff --git a/go.mod b/go.mod index 2fafcb5..23fb355 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/samber/slog-echo v1.15.0 go.ntppool.org/common v0.3.1 go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.58.0 + go.opentelemetry.io/otel v1.33.0 golang.org/x/sync v0.10.0 ) @@ -44,7 +45,6 @@ require ( go.opentelemetry.io/contrib/bridges/otelslog v0.8.0 // indirect go.opentelemetry.io/contrib/bridges/prometheus v0.58.0 // indirect go.opentelemetry.io/contrib/exporters/autoexport v0.58.0 // indirect - go.opentelemetry.io/otel v1.33.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.9.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.9.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0 // indirect diff --git a/types/airport_type.go b/types/airport_type.go new file mode 100644 index 0000000..416fa85 --- /dev/null +++ b/types/airport_type.go @@ -0,0 +1,57 @@ +package types + +import ( + "sort" + "strings" + + alphafoxtrot "github.com/grumpypixel/go-airport-finder" +) + +type Airport struct { + Name string + Code string + Distance float64 + Type string + + data *alphafoxtrot.Airport +} + +func NewAirport(airport *alphafoxtrot.Airport) *Airport { + code := strings.ToLower(airport.Country.ISOCode + airport.IATACode) + + a := &Airport{ + Name: airport.Name, + Code: code, + Type: airport.Type, + data: airport, + } + + return a +} + +func (a *Airport) String() string { + return a.Name +} + +func UniqAirports(r []*Airport) []*Airport { + seen := make(map[string]struct{}) + j := 0 + for _, v := range r { + if _, ok := seen[v.Code]; ok { + continue + } + seen[v.Code] = struct{}{} + r[j] = v + j++ + } + return r[:j] +} + +func SortAirports(r []*Airport) { + 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 r[i].data.Type < r[j].data.Type + }) +}