package main import ( "context" "encoding/json" "fmt" "log" "net/http" "strconv" "strings" "github.com/caarlos0/env" "googlemaps.github.io/maps" ) type config struct { MapsApiKey string `env:"MAPS_API_KEY"` Port int `env:"PORT" envDefault:"80"` } type location struct { Lat float64 `json:"latitude"` Long float64 `json:"longitude"` } type address struct { Address string `json:"address"` } type distance struct { Text string `json:"text"` Value int `json:"value"` } type duration struct { Text string `json:"text"` Value float64 `json:"value"` } type destination struct { Destination string `json:"destination"` Distance distance `json:"distance"` Duration duration `json:"duration"` } type origin struct { Origin string `json:"origin"` Destinations []destination `json:"destinations"` } type distanceResponse struct { Origins []origin `json:"origins"` } func main() { cfg := config{} err := env.Parse(&cfg) if err != nil { fmt.Printf("%+v\n", err) } fmt.Printf("%+v\n", cfg) client, err := maps.NewClient(maps.WithAPIKey(cfg.MapsApiKey)) if err != nil { log.Fatalf("fatal error: %s", err) } http.HandleFunc("/latlong/", makeHandler(handleLatLongRequest, client)) http.HandleFunc("/address/", makeHandler(handleAddressRequest, client)) http.HandleFunc("/distance/", makeHandler(handleDistanceMatrixRequest, client)) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", cfg.Port), nil)) } func makeHandler(fn func(http.ResponseWriter, *http.Request, *maps.Client), client *maps.Client) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { fn(w, r, client) } } func handleDistanceMatrixRequest(w http.ResponseWriter, r *http.Request, client *maps.Client) { origins := strings.Split(r.URL.Query().Get("origins"), "|") destinationsString := r.URL.Query().Get("destinations") if destinationsString == "" { x := make([]origin, len(origins)) for i, o := range origins { x[i] = origin{Origin: o} } writeResponse(distanceResponse{Origins: x}, w) return } destinations := strings.Split(destinationsString, "|") req := &maps.DistanceMatrixRequest{ Origins: origins, Destinations: destinations, Language: "sv", Units: maps.UnitsMetric, } if result, err := client.DistanceMatrix(context.Background(), req); err != nil { log.Fatalf("matrix fatal error: %s", err) w.WriteHeader(400) } else { res := distanceResponse{} for i, r := range result.Rows { var dests []destination for j, e := range r.Elements { dests = append(dests, destination{ destinations[j], distance{ Text: e.Distance.HumanReadable, Value: e.Distance.Meters, }, duration{ e.Duration.String(), e.Duration.Minutes(), }, }) } res.Origins = append(res.Origins, origin{origins[i], dests}) } writeResponse(res, w) } } func writeResponse(res distanceResponse, w http.ResponseWriter) { if response, err := json.Marshal(res); err != nil { log.Fatalf("fatal error: %s", err) w.WriteHeader(404) } else { _, _ = w.Write(response) } } func handleAddressRequest(w http.ResponseWriter, r *http.Request, client *maps.Client) { p := strings.Split(r.URL.Path[len("/address/"):], ",") lat, _ := strconv.ParseFloat(p[0], 64) lng, _ := strconv.ParseFloat(p[1], 64) req := &maps.GeocodingRequest{ LatLng: &maps.LatLng{Lat: lat, Lng: lng}, } if result, err := client.Geocode(context.Background(), req); err != nil { log.Fatalf("fatal error: %s", err) w.WriteHeader(400) } else { if len(result) > 0 { l := address{ Address: result[0].FormattedAddress, } if response, err := json.Marshal(l); err != nil { log.Fatalf("fatal error: %s", err) w.WriteHeader(404) } else { _, _ = w.Write(response) } } } } func handleLatLongRequest(w http.ResponseWriter, r *http.Request, client *maps.Client) { req := &maps.GeocodingRequest{ Address: r.URL.Path[len("/latlong/"):], } if result, err := client.Geocode(context.Background(), req); err != nil { w.WriteHeader(400) } else { if len(result) > 0 { l := location{ Lat: result[0].Geometry.Location.Lat, Long: result[0].Geometry.Location.Lng, } if response, err := json.Marshal(l); err != nil { w.WriteHeader(500) } else { _, _ = w.Write(response) } } else { w.WriteHeader(404) } } }