package handler import ( "context" "strings" "github.com/micro/micro/v3/service/errors" "github.com/micro/micro/v3/service/logger" "googlemaps.github.io/maps" pb "github.com/micro/services/geocoding/proto" ) var ( ErrDownstream = errors.InternalServerError("MAP_ERROR", "Unable to connect to map provider") ErrNoResults = errors.BadRequest("NO_RESULTS", "Unable to geocode address, no results found") ErrMissingLatitude = errors.BadRequest("MISSING_LATITUDE", "Missing latitude") ErrMissingLongitude = errors.BadRequest("MISSING_LONGITUDE", "Missing longitude") ) type Geocoding struct { Maps *maps.Client } func (g *Geocoding) Lookup(ctx context.Context, req *pb.LookupRequest, rsp *pb.LookupResponse) error { // query google maps results, err := g.Maps.Geocode(ctx, &maps.GeocodingRequest{Address: toString(req)}) if err != nil { logger.Errorf("Error geocoding: %v", err) return ErrDownstream } if len(results) == 0 { return ErrNoResults } rsp.Address = new(pb.Address) rsp.Location = new(pb.Location) // return the result serializeResult(results[0], rsp.Address, rsp.Location) return nil } // Reverse geocode an address func (g *Geocoding) Reverse(ctx context.Context, req *pb.ReverseRequest, rsp *pb.ReverseResponse) error { // validate the request if req.Latitude == 0.0 { return ErrMissingLatitude } if req.Longitude == 0.0 { return ErrMissingLongitude } // query google maps results, err := g.Maps.ReverseGeocode(ctx, &maps.GeocodingRequest{ LatLng: &maps.LatLng{Lat: req.Latitude, Lng: req.Longitude}, }) if err != nil { logger.Errorf("Error geocoding: %v", err) return ErrDownstream } if len(results) == 0 { return ErrNoResults } rsp.Address = new(pb.Address) rsp.Location = new(pb.Location) // return the result serializeResult(results[0], rsp.Address, rsp.Location) return nil } func toString(l *pb.LookupRequest) string { var comps []string for _, c := range []string{l.Address, l.City, l.Postcode, l.Country} { t := strings.TrimSpace(c) if len(t) > 0 { comps = append(comps, t) } } return strings.Join(comps, ", ") } func serializeResult(r maps.GeocodingResult, a *pb.Address, l *pb.Location) { var street, number string for _, c := range r.AddressComponents { for _, t := range c.Types { switch t { case "street_number": number = c.LongName case "route": street = c.LongName case "neighborhood": a.LineTwo = c.LongName case "country": a.Country = c.LongName case "postal_code": a.Postcode = c.LongName case "postal_town": a.City = c.LongName } } } a.LineOne = strings.Join([]string{number, street}, " ") l.Latitude = r.Geometry.Location.Lat l.Longitude = r.Geometry.Location.Lng }