Geocoding Service (#35)

This commit is contained in:
ben-toogood
2021-01-08 14:08:19 +00:00
committed by GitHub
parent de2c437c41
commit 39e0f94152
12 changed files with 884 additions and 0 deletions

2
geocoding/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
geocoding

3
geocoding/Dockerfile Normal file
View File

@@ -0,0 +1,3 @@
FROM alpine
ADD geocoding /geocoding
ENTRYPOINT [ "/geocoding" ]

22
geocoding/Makefile Normal file
View File

@@ -0,0 +1,22 @@
GOPATH:=$(shell go env GOPATH)
.PHONY: init
init:
go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/protoc-gen-go
go get github.com/micro/micro/v3/cmd/protoc-gen-micro
.PHONY: proto
proto:
protoc --proto_path=. --micro_out=. --go_out=:. proto/geocoding.proto
.PHONY: build
build:
go build -o geocoding *.go
.PHONY: test
test:
go test -v ./... -cover
.PHONY: docker
docker:
docker build . -t geocoding:latest

2
geocoding/generate.go Normal file
View File

@@ -0,0 +1,2 @@
package main
//go:generate make proto

View File

@@ -0,0 +1,104 @@
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
}
// Geocode an address
func (g *Geocoding) Geocode(ctx context.Context, req *pb.Address, rsp *pb.Address) 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
}
// return the result
serializeResult(results[0], rsp)
return nil
}
// Reverse geocode an address
func (g *Geocoding) Reverse(ctx context.Context, req *pb.Coordinates, rsp *pb.Address) error {
// validate the request
if req.Latitude == nil {
return ErrMissingLatitude
}
if req.Longitude == nil {
return ErrMissingLongitude
}
// query google maps
results, err := g.Maps.ReverseGeocode(ctx, &maps.GeocodingRequest{
LatLng: &maps.LatLng{Lat: req.Latitude.Value, Lng: req.Longitude.Value},
})
if err != nil {
logger.Errorf("Error geocoding: %v", err)
return ErrDownstream
}
if len(results) == 0 {
return ErrNoResults
}
// return the result
serializeResult(results[0], rsp)
return nil
}
func toString(a *pb.Address) string {
var comps []string
for _, c := range []string{a.LineOne, a.LineTwo, a.City, a.Postcode, a.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) {
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}, " ")
a.Latitude = r.Geometry.Location.Lat
a.Longitude = r.Geometry.Location.Lng
}

View File

@@ -0,0 +1,261 @@
package handler_test
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/types/known/wrapperspb"
"googlemaps.github.io/maps"
"github.com/micro/services/geocoding/handler"
pb "github.com/micro/services/geocoding/proto"
)
const (
validReponse = `{
"results":[
{
"address_components":[
{
"long_name":"160",
"types":[
"street_number"
]
},
{
"long_name":"Grays Inn Road",
"types":[
"route"
]
},
{
"long_name":"Holborn",
"types":[
"neighborhood"
]
},
{
"long_name":"Santa Clara County",
"types":[
"administrative_area_level_2",
"political"
]
},
{
"long_name":"London",
"types":[
"political_town"
]
},
{
"long_name":"United Kingdom",
"types":[
"country",
"political"
]
},
{
"long_name":"WC1X 8ED",
"types":[
"postal_code"
]
}
],
"geometry":{
"location":{
"lat":51.522214,
"lng":-0.113565
}
},
"partial_math":false,
"place_id":"ChIJ2eUgeAK6j4ARbn5u_wAGqWA",
"types":[
"street_address"
]
}
],
"status":"OK"
}`
noResultsReponse = `{
"results": [],
"status": "OK"
}`
)
func TestGeocoding(t *testing.T) {
tt := []struct {
Name string
ResponseBody string
ResponseCode int
MapQuery string
Error error
Address *pb.Address
Result *pb.Address
}{
{
Name: "Invalid address",
ResponseBody: noResultsReponse,
ResponseCode: http.StatusOK,
Address: &pb.Address{
LineOne: "Foobar Street",
},
Error: handler.ErrNoResults,
MapQuery: "Foobar Street",
},
{
Name: "Valid address",
ResponseBody: validReponse,
ResponseCode: http.StatusOK,
Address: &pb.Address{
LineOne: "160 Grays Inn Road",
LineTwo: "Holborn",
Postcode: "wc1x8ed",
Country: "United Kingdom",
},
MapQuery: "160 Grays Inn Road, Holborn, wc1x8ed, United Kingdom",
},
{
Name: "Maps error",
ResponseCode: http.StatusInternalServerError,
Address: &pb.Address{
LineOne: "Foobar Street",
},
Error: handler.ErrDownstream,
ResponseBody: "{}",
MapQuery: "Foobar Street",
},
}
for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
var query string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
query = r.URL.Query().Get("address")
w.WriteHeader(tc.ResponseCode)
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
fmt.Fprintln(w, tc.ResponseBody)
}))
defer server.Close()
m, err := maps.NewClient(maps.WithBaseURL(server.URL), maps.WithAPIKey("shh"))
if err != nil {
t.Fatal(err)
}
h := &handler.Geocoding{Maps: m}
var rsp pb.Address
err = h.Geocode(context.TODO(), tc.Address, &rsp)
assert.Equal(t, tc.MapQuery, query)
assert.Equal(t, tc.Error, err)
if tc.Result != nil {
assert.Equal(t, tc.Result.LineOne, rsp.LineOne)
assert.Equal(t, tc.Result.LineTwo, rsp.LineTwo)
assert.Equal(t, tc.Result.City, rsp.City)
assert.Equal(t, tc.Result.Country, rsp.Country)
assert.Equal(t, tc.Result.Postcode, rsp.Postcode)
}
})
}
}
func TestReverseGeocoding(t *testing.T) {
tt := []struct {
Name string
ResponseBody string
ResponseCode int
Error error
Latitude *wrapperspb.DoubleValue
Longitude *wrapperspb.DoubleValue
Result *pb.Address
}{
{
Name: "Missing longitude",
Latitude: &wrapperspb.DoubleValue{Value: 51.522214},
Error: handler.ErrMissingLongitude,
},
{
Name: "Missing latitude",
Longitude: &wrapperspb.DoubleValue{Value: -0.113565},
Error: handler.ErrMissingLatitude,
},
{
Name: "Invalid address",
ResponseBody: noResultsReponse,
ResponseCode: http.StatusOK,
Latitude: &wrapperspb.DoubleValue{Value: 999.999999},
Longitude: &wrapperspb.DoubleValue{Value: 999.999999},
Error: handler.ErrNoResults,
},
{
Name: "Valid address",
ResponseBody: validReponse,
ResponseCode: http.StatusOK,
Latitude: &wrapperspb.DoubleValue{Value: 51.522214},
Longitude: &wrapperspb.DoubleValue{Value: -0.113565},
Result: &pb.Address{
LineOne: "160 Grays Inn Road",
LineTwo: "Holborn",
Postcode: "WC1X 8ED",
Country: "United Kingdom",
},
},
{
Name: "Maps error",
Latitude: &wrapperspb.DoubleValue{Value: 51.522214},
Longitude: &wrapperspb.DoubleValue{Value: -0.113565},
ResponseCode: http.StatusInternalServerError,
Error: handler.ErrDownstream,
ResponseBody: "{}",
},
}
for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
var lat, lng string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if coords := strings.Split(string(r.URL.Query().Get("latlng")), ","); len(coords) == 2 {
lat = coords[0]
lng = coords[1]
}
w.WriteHeader(tc.ResponseCode)
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
fmt.Fprintln(w, tc.ResponseBody)
}))
defer server.Close()
m, err := maps.NewClient(maps.WithBaseURL(server.URL), maps.WithAPIKey("shh"))
if err != nil {
t.Fatal(err)
}
h := &handler.Geocoding{Maps: m}
var rsp pb.Address
err = h.Reverse(context.TODO(), &pb.Coordinates{
Latitude: tc.Latitude, Longitude: tc.Longitude,
}, &rsp)
assert.Equal(t, tc.Error, err)
if tc.Latitude != nil && tc.Longitude != nil {
assert.Equal(t, fmt.Sprintf("%f", tc.Latitude.Value), lat)
assert.Equal(t, fmt.Sprintf("%f", tc.Longitude.Value), lng)
}
if tc.Result != nil {
assert.Equal(t, tc.Result.LineOne, rsp.LineOne)
assert.Equal(t, tc.Result.LineTwo, rsp.LineTwo)
assert.Equal(t, tc.Result.City, rsp.City)
assert.Equal(t, tc.Result.Country, rsp.Country)
assert.Equal(t, tc.Result.Postcode, rsp.Postcode)
}
})
}
}

41
geocoding/main.go Normal file
View File

@@ -0,0 +1,41 @@
package main
import (
"github.com/micro/services/geocoding/handler"
pb "github.com/micro/services/geocoding/proto"
"github.com/micro/micro/v3/service"
"github.com/micro/micro/v3/service/config"
"github.com/micro/micro/v3/service/logger"
"googlemaps.github.io/maps"
)
func main() {
// Create service
srv := service.New(
service.Name("geocoding"),
service.Version("latest"),
)
// Setup google maps
c, err := config.Get("google.apikey")
if err != nil {
logger.Fatalf("Error loading config: %v", err)
}
apiKey := c.String("")
if len(apiKey) == 0 {
logger.Fatalf("Missing required config: google.apikey")
}
m, err := maps.NewClient(maps.WithAPIKey(apiKey))
if err != nil {
logger.Fatalf("Error configuring google maps client: %v", err)
}
// Register handler
pb.RegisterGeocodingHandler(srv.Server(), &handler.Geocoding{Maps: m})
// Run service
if err := srv.Run(); err != nil {
logger.Fatal(err)
}
}

View File

@@ -0,0 +1,299 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.23.0
// protoc v3.13.0
// source: proto/geocoding.proto
package geocoding
import (
proto "github.com/golang/protobuf/proto"
wrappers "github.com/golang/protobuf/ptypes/wrappers"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Address struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
LineOne string `protobuf:"bytes,1,opt,name=line_one,json=lineOne,proto3" json:"line_one,omitempty"`
LineTwo string `protobuf:"bytes,2,opt,name=line_two,json=lineTwo,proto3" json:"line_two,omitempty"`
City string `protobuf:"bytes,3,opt,name=city,proto3" json:"city,omitempty"`
Country string `protobuf:"bytes,4,opt,name=country,proto3" json:"country,omitempty"`
Postcode string `protobuf:"bytes,5,opt,name=postcode,proto3" json:"postcode,omitempty"`
Latitude float64 `protobuf:"fixed64,6,opt,name=latitude,proto3" json:"latitude,omitempty"`
Longitude float64 `protobuf:"fixed64,7,opt,name=longitude,proto3" json:"longitude,omitempty"`
}
func (x *Address) Reset() {
*x = Address{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_geocoding_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Address) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Address) ProtoMessage() {}
func (x *Address) ProtoReflect() protoreflect.Message {
mi := &file_proto_geocoding_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Address.ProtoReflect.Descriptor instead.
func (*Address) Descriptor() ([]byte, []int) {
return file_proto_geocoding_proto_rawDescGZIP(), []int{0}
}
func (x *Address) GetLineOne() string {
if x != nil {
return x.LineOne
}
return ""
}
func (x *Address) GetLineTwo() string {
if x != nil {
return x.LineTwo
}
return ""
}
func (x *Address) GetCity() string {
if x != nil {
return x.City
}
return ""
}
func (x *Address) GetCountry() string {
if x != nil {
return x.Country
}
return ""
}
func (x *Address) GetPostcode() string {
if x != nil {
return x.Postcode
}
return ""
}
func (x *Address) GetLatitude() float64 {
if x != nil {
return x.Latitude
}
return 0
}
func (x *Address) GetLongitude() float64 {
if x != nil {
return x.Longitude
}
return 0
}
type Coordinates struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Latitude *wrappers.DoubleValue `protobuf:"bytes,1,opt,name=latitude,proto3" json:"latitude,omitempty"`
Longitude *wrappers.DoubleValue `protobuf:"bytes,2,opt,name=longitude,proto3" json:"longitude,omitempty"`
}
func (x *Coordinates) Reset() {
*x = Coordinates{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_geocoding_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Coordinates) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Coordinates) ProtoMessage() {}
func (x *Coordinates) ProtoReflect() protoreflect.Message {
mi := &file_proto_geocoding_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Coordinates.ProtoReflect.Descriptor instead.
func (*Coordinates) Descriptor() ([]byte, []int) {
return file_proto_geocoding_proto_rawDescGZIP(), []int{1}
}
func (x *Coordinates) GetLatitude() *wrappers.DoubleValue {
if x != nil {
return x.Latitude
}
return nil
}
func (x *Coordinates) GetLongitude() *wrappers.DoubleValue {
if x != nil {
return x.Longitude
}
return nil
}
var File_proto_geocoding_proto protoreflect.FileDescriptor
var file_proto_geocoding_proto_rawDesc = []byte{
0x0a, 0x15, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6f, 0x63, 0x6f, 0x64, 0x69, 0x6e,
0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x67, 0x65, 0x6f, 0x63, 0x6f, 0x64, 0x69,
0x6e, 0x67, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x22, 0xc3, 0x01, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x19,
0x0a, 0x08, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x6f, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x07, 0x6c, 0x69, 0x6e, 0x65, 0x4f, 0x6e, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x69, 0x6e,
0x65, 0x5f, 0x74, 0x77, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x69, 0x6e,
0x65, 0x54, 0x77, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x63, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x74, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x05,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x74, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x1a,
0x0a, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01,
0x52, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6c, 0x6f,
0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x6c,
0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x22, 0x83, 0x01, 0x0a, 0x0b, 0x43, 0x6f, 0x6f,
0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x08, 0x6c, 0x61, 0x74, 0x69,
0x74, 0x75, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75,
0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75,
0x64, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61,
0x6c, 0x75, 0x65, 0x52, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x32, 0x79,
0x0a, 0x09, 0x47, 0x65, 0x6f, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x33, 0x0a, 0x07, 0x47,
0x65, 0x6f, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x2e, 0x67, 0x65, 0x6f, 0x63, 0x6f, 0x64, 0x69,
0x6e, 0x67, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x1a, 0x12, 0x2e, 0x67, 0x65, 0x6f,
0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x00,
0x12, 0x37, 0x0a, 0x07, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x65,
0x6f, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61,
0x74, 0x65, 0x73, 0x1a, 0x12, 0x2e, 0x67, 0x65, 0x6f, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x2e,
0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
file_proto_geocoding_proto_rawDescOnce sync.Once
file_proto_geocoding_proto_rawDescData = file_proto_geocoding_proto_rawDesc
)
func file_proto_geocoding_proto_rawDescGZIP() []byte {
file_proto_geocoding_proto_rawDescOnce.Do(func() {
file_proto_geocoding_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_geocoding_proto_rawDescData)
})
return file_proto_geocoding_proto_rawDescData
}
var file_proto_geocoding_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_proto_geocoding_proto_goTypes = []interface{}{
(*Address)(nil), // 0: geocoding.Address
(*Coordinates)(nil), // 1: geocoding.Coordinates
(*wrappers.DoubleValue)(nil), // 2: google.protobuf.DoubleValue
}
var file_proto_geocoding_proto_depIdxs = []int32{
2, // 0: geocoding.Coordinates.latitude:type_name -> google.protobuf.DoubleValue
2, // 1: geocoding.Coordinates.longitude:type_name -> google.protobuf.DoubleValue
0, // 2: geocoding.Geocoding.Geocode:input_type -> geocoding.Address
1, // 3: geocoding.Geocoding.Reverse:input_type -> geocoding.Coordinates
0, // 4: geocoding.Geocoding.Geocode:output_type -> geocoding.Address
0, // 5: geocoding.Geocoding.Reverse:output_type -> geocoding.Address
4, // [4:6] is the sub-list for method output_type
2, // [2:4] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_proto_geocoding_proto_init() }
func file_proto_geocoding_proto_init() {
if File_proto_geocoding_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_proto_geocoding_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Address); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proto_geocoding_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Coordinates); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proto_geocoding_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_proto_geocoding_proto_goTypes,
DependencyIndexes: file_proto_geocoding_proto_depIdxs,
MessageInfos: file_proto_geocoding_proto_msgTypes,
}.Build()
File_proto_geocoding_proto = out.File
file_proto_geocoding_proto_rawDesc = nil
file_proto_geocoding_proto_goTypes = nil
file_proto_geocoding_proto_depIdxs = nil
}

View File

@@ -0,0 +1,115 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: proto/geocoding.proto
package geocoding
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
_ "github.com/golang/protobuf/ptypes/wrappers"
math "math"
)
import (
context "context"
api "github.com/micro/micro/v3/service/api"
client "github.com/micro/micro/v3/service/client"
server "github.com/micro/micro/v3/service/server"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Reference imports to suppress errors if they are not otherwise used.
var _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option
// Api Endpoints for Geocoding service
func NewGeocodingEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// Client API for Geocoding service
type GeocodingService interface {
// Geocode an address, the result will be the normalized address which contains coordinates
Geocode(ctx context.Context, in *Address, opts ...client.CallOption) (*Address, error)
// Reverse geocode coordinates to an address
Reverse(ctx context.Context, in *Coordinates, opts ...client.CallOption) (*Address, error)
}
type geocodingService struct {
c client.Client
name string
}
func NewGeocodingService(name string, c client.Client) GeocodingService {
return &geocodingService{
c: c,
name: name,
}
}
func (c *geocodingService) Geocode(ctx context.Context, in *Address, opts ...client.CallOption) (*Address, error) {
req := c.c.NewRequest(c.name, "Geocoding.Geocode", in)
out := new(Address)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *geocodingService) Reverse(ctx context.Context, in *Coordinates, opts ...client.CallOption) (*Address, error) {
req := c.c.NewRequest(c.name, "Geocoding.Reverse", in)
out := new(Address)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Geocoding service
type GeocodingHandler interface {
// Geocode an address, the result will be the normalized address which contains coordinates
Geocode(context.Context, *Address, *Address) error
// Reverse geocode coordinates to an address
Reverse(context.Context, *Coordinates, *Address) error
}
func RegisterGeocodingHandler(s server.Server, hdlr GeocodingHandler, opts ...server.HandlerOption) error {
type geocoding interface {
Geocode(ctx context.Context, in *Address, out *Address) error
Reverse(ctx context.Context, in *Coordinates, out *Address) error
}
type Geocoding struct {
geocoding
}
h := &geocodingHandler{hdlr}
return s.Handle(s.NewHandler(&Geocoding{h}, opts...))
}
type geocodingHandler struct {
GeocodingHandler
}
func (h *geocodingHandler) Geocode(ctx context.Context, in *Address, out *Address) error {
return h.GeocodingHandler.Geocode(ctx, in, out)
}
func (h *geocodingHandler) Reverse(ctx context.Context, in *Coordinates, out *Address) error {
return h.GeocodingHandler.Reverse(ctx, in, out)
}

View File

@@ -0,0 +1,27 @@
syntax = "proto3";
package geocoding;
import "google/protobuf/wrappers.proto";
service Geocoding {
// Geocode an address, the result will be the normalized address which contains coordinates
rpc Geocode(Address) returns (Address) {};
// Reverse geocode coordinates to an address
rpc Reverse(Coordinates) returns (Address) {};
}
message Address {
string line_one = 1;
string line_two = 2;
string city = 3;
string country = 4;
string postcode = 5;
double latitude = 6;
double longitude = 7;
}
message Coordinates {
google.protobuf.DoubleValue latitude = 1;
google.protobuf.DoubleValue longitude = 2;
}

1
go.mod
View File

@@ -20,6 +20,7 @@ require (
google.golang.org/genproto v0.0.0-20201001141541-efaab9d3c4f7 // indirect google.golang.org/genproto v0.0.0-20201001141541-efaab9d3c4f7 // indirect
google.golang.org/grpc v1.32.0 // indirect google.golang.org/grpc v1.32.0 // indirect
google.golang.org/protobuf v1.25.0 google.golang.org/protobuf v1.25.0
googlemaps.github.io/maps v1.3.1
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
gorm.io/driver/postgres v1.0.6 gorm.io/driver/postgres v1.0.6
gorm.io/gorm v1.20.9 gorm.io/gorm v1.20.9

7
go.sum
View File

@@ -404,6 +404,7 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ= github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
@@ -455,6 +456,7 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -622,6 +624,8 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@@ -743,9 +747,12 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
googlemaps.github.io/maps v1.3.1 h1:VYFiLFgZyDVFYjPKLedOWxjmrwuaJFAc4EhqGNZfX40=
googlemaps.github.io/maps v1.3.1/go.mod h1:cCq0JKYAnnCRSdiaBi7Ex9CW15uxIAk7oPi8V/xEh6s=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
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/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=