Make route guide implementation more go idiomatic

This commit is contained in:
Daniel Wang 2015-02-25 12:08:12 -08:00
parent a94c062d9b
commit 8fd7702f97
5 changed files with 128 additions and 106 deletions

View File

@ -2,7 +2,7 @@
The route guide server and client demonstrates how to use grpc go libraries to The route guide server and client demonstrates how to use grpc go libraries to
perform unary, client streaming, server streaming and full duplex RPCs. perform unary, client streaming, server streaming and full duplex RPCs.
See the definition of the route guide service in route_guide.proto. See the definition of the route guide service in proto/route_guide.proto.
# Run the sample code # Run the sample code
To compile and run the server, assuming you are in the root of the route_guide To compile and run the server, assuming you are in the root of the route_guide
@ -18,8 +18,8 @@ Likewise, to run the client:
The server and client both take optional command line flags. For example, the The server and client both take optional command line flags. For example, the
client and server run without TLS by default. To enable TSL: client and server run without TLS by default. To enable TSL:
`go run server/server.go -use_tls=true` `go run server/server.go -tls=true`
and and
`go run client/client.go -use_tls=true` `go run client/client.go -tls=true`

View File

@ -31,6 +31,10 @@
* *
*/ */
// Package main implements a simple grpc client that demonstrates how to use grpc go libraries
// to perform unary, client streaming, server streaming and full duplex RPCs.
//
// It interacts with the route guide service whose definition can be found in proto/route_guide.proto.
package main package main
import ( import (
@ -38,64 +42,59 @@ import (
"io" "io"
"log" "log"
"math/rand" "math/rand"
"net"
"strconv"
"time" "time"
"golang.org/x/net/context" "golang.org/x/net/context"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
pb "google.golang.org/grpc/examples/route_guide" pb "google.golang.org/grpc/examples/route_guide/proto"
) )
var ( var (
useTLS = flag.Bool("use_tls", false, "Connection uses TLS if true, else plain TCP") tls = flag.Bool("use_tls", false, "Connection uses TLS if true, else plain TCP")
caFile = flag.String("tls_ca_file", "testdata/ca.pem", "The file containning the CA root cert file") caFile = flag.String("tls_ca_file", "testdata/ca.pem", "The file containning the CA root cert file")
serverHost = flag.String("server_host", "127.0.0.1", "The server host name") serverAddr = flag.String("server_addr", "127.0.0.1:10000", "The server address in the format of host:port")
serverPort = flag.Int("server_port", 10000, "The server port number")
tlsServerName = flag.String("tls_server_name", "x.test.youtube.com", "The server name use to verify the hostname returned by TLS handshake") tlsServerName = flag.String("tls_server_name", "x.test.youtube.com", "The server name use to verify the hostname returned by TLS handshake")
) )
// doGetFeature gets the feature for the given point. // printFeature gets the feature for the given point.
func doGetFeature(client pb.RouteGuideClient, point *pb.Point) { func printFeature(client pb.RouteGuideClient, point *pb.Point) {
log.Printf("Getting feature for point (%d, %d)", point.Latitude, point.Longitude) log.Printf("Getting feature for point (%d, %d)", point.Latitude, point.Longitude)
reply, err := client.GetFeature(context.Background(), point) feature, err := client.GetFeature(context.Background(), point)
if err != nil { if err != nil {
log.Fatalf("%v.GetFeatures(_) = _, %v: ", client, err) log.Fatalf("%v.GetFeatures(_) = _, %v: ", client, err)
return return
} }
log.Println(reply) log.Println(feature)
} }
// doListFeatures lists all the features within the given bounding Rectangle. // printFeatures lists all the features within the given bounding Rectangle.
func doListFeatures(client pb.RouteGuideClient, rect *pb.Rectangle) { func printFeatures(client pb.RouteGuideClient, rect *pb.Rectangle) {
log.Printf("Looking for features within %v", rect) log.Printf("Looking for features within %v", rect)
stream, err := client.ListFeatures(context.Background(), rect) stream, err := client.ListFeatures(context.Background(), rect)
if err != nil { if err != nil {
log.Fatalf("%v.ListFeatures(_) = _, %v", client, err) log.Fatalf("%v.ListFeatures(_) = _, %v", client, err)
} }
var rpcStatus error
for { for {
reply, err := stream.Recv() feature, err := stream.Recv()
if err != nil { if err == io.EOF {
rpcStatus = err
break break
} }
log.Println(reply) if err != nil {
} log.Fatalf("%v.ListFeatures(_) = _, %v", client, err)
if rpcStatus != io.EOF { }
log.Fatalf("%v.ListFeatures(_) = _, %v", client, err) log.Println(feature)
} }
} }
// doRecordRoute sends a sequence of points to server and expects to get a RouteSummary from server. // runRecordRoute sends a sequence of points to server and expects to get a RouteSummary from server.
func doRecordRoute(client pb.RouteGuideClient) { func runRecordRoute(client pb.RouteGuideClient) {
// Create a random number of random points // Create a random number of random points
rand.Seed(time.Now().UnixNano()) r := rand.New(rand.NewSource(time.Now().UnixNano()))
pointCount := rand.Int31n(100) + 2 // Tranverse at least two points pointCount := int(r.Int31n(100)) + 2 // Traverse at least two points
points := make([]*pb.Point, pointCount) var points []*pb.Point
for i, _ := range points { for i := 0; i < pointCount; i++ {
points[i] = randomPoint() points = append(points, randomPoint(r))
} }
log.Printf("Traversing %d points.", len(points)) log.Printf("Traversing %d points.", len(points))
stream, err := client.RecordRoute(context.Background()) stream, err := client.RecordRoute(context.Background())
@ -111,58 +110,57 @@ func doRecordRoute(client pb.RouteGuideClient) {
if err != nil { if err != nil {
log.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil) log.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)
} }
log.Printf("Route summary: %v\n", reply) log.Printf("Route summary: %v", reply)
} }
// doRouteChat receives a sequence of route notes, while sending notes for various locations. // runRouteChat receives a sequence of route notes, while sending notes for various locations.
func doRouteChat(client pb.RouteGuideClient) { func runRouteChat(client pb.RouteGuideClient) {
notes := []*pb.RouteNote{ notes := []*pb.RouteNote{
&pb.RouteNote{&pb.Point{0, 1}, "First message"}, {&pb.Point{0, 1}, "First message"},
&pb.RouteNote{&pb.Point{0, 2}, "Second message"}, {&pb.Point{0, 2}, "Second message"},
&pb.RouteNote{&pb.Point{0, 3}, "Third message"}, {&pb.Point{0, 3}, "Third message"},
&pb.RouteNote{&pb.Point{0, 1}, "Fourth message"}, {&pb.Point{0, 1}, "Fourth message"},
&pb.RouteNote{&pb.Point{0, 2}, "Fifth message"}, {&pb.Point{0, 2}, "Fifth message"},
&pb.RouteNote{&pb.Point{0, 3}, "Sixth message"}, {&pb.Point{0, 3}, "Sixth message"},
} }
stream, err := client.RouteChat(context.Background()) stream, err := client.RouteChat(context.Background())
if err != nil { if err != nil {
log.Fatalf("%v.RouteChat(_) = _, %v", client, err) log.Fatalf("%v.RouteChat(_) = _, %v", client, err)
} }
c := make(chan int) waitc := make(chan int)
go func() { go func() {
for { for {
in, err := stream.Recv() in, err := stream.Recv()
if err == io.EOF { if err == io.EOF {
// read done. // read done.
c <- 1 waitc <- 1
return return
} }
if err != nil { if err != nil {
log.Fatalf("Failed to receive a note : %v\n", err) log.Fatalf("Failed to receive a note : %v", err)
} }
log.Printf("Got message %s at point(%d, %d)\n", in.Message, in.Location.Latitude, in.Location.Longitude) log.Printf("Got message %s at point(%d, %d)", in.Message, in.Location.Latitude, in.Location.Longitude)
} }
}() }()
for _, note := range notes { for _, note := range notes {
if err := stream.Send(note); err != nil { if err := stream.Send(note); err != nil {
log.Fatalf("Failed to send a note: %v\n", err) log.Fatalf("Failed to send a note: %v", err)
} }
} }
stream.CloseSend() stream.CloseSend()
<-c <-waitc
} }
func randomPoint() *pb.Point { func randomPoint(r *rand.Rand) *pb.Point {
lat := (rand.Int31n(180) - 90) * 1e7 lat := (r.Int31n(180) - 90) * 1e7
long := (rand.Int31n(360) - 180) * 1e7 long := (r.Int31n(360) - 180) * 1e7
return &pb.Point{lat, long} return &pb.Point{lat, long}
} }
func main() { func main() {
flag.Parse() flag.Parse()
serverAddr := net.JoinHostPort(*serverHost, strconv.Itoa(*serverPort))
var opts []grpc.DialOption var opts []grpc.DialOption
if *useTLS { if *tls {
var sn string var sn string
if *tlsServerName != "" { if *tlsServerName != "" {
sn = *tlsServerName sn = *tlsServerName
@ -179,7 +177,7 @@ func main() {
} }
opts = append(opts, grpc.WithClientTLS(creds)) opts = append(opts, grpc.WithClientTLS(creds))
} }
conn, err := grpc.Dial(serverAddr, opts...) conn, err := grpc.Dial(*serverAddr, opts...)
if err != nil { if err != nil {
log.Fatalf("fail to dial: %v", err) log.Fatalf("fail to dial: %v", err)
} }
@ -187,17 +185,17 @@ func main() {
client := pb.NewRouteGuideClient(conn) client := pb.NewRouteGuideClient(conn)
// Looking for a valid feature // Looking for a valid feature
doGetFeature(client, &pb.Point{409146138, -746188906}) printFeature(client, &pb.Point{409146138, -746188906})
// Feature missing. // Feature missing.
doGetFeature(client, &pb.Point{0, 0}) printFeature(client, &pb.Point{0, 0})
// Looking for features between 40, -75 and 42, -73. // Looking for features between 40, -75 and 42, -73.
doListFeatures(client, &pb.Rectangle{&pb.Point{400000000, -750000000}, &pb.Point{420000000, -730000000}}) printFeatures(client, &pb.Rectangle{&pb.Point{400000000, -750000000}, &pb.Point{420000000, -730000000}})
// RecordRoute // RecordRoute
doRecordRoute(client) runRecordRoute(client)
// RouteChat // RouteChat
doRouteChat(client) runRouteChat(client)
} }

View File

@ -1,12 +1,12 @@
// Code generated by protoc-gen-go. // Code generated by protoc-gen-go.
// source: route_guide.proto // source: proto/route_guide.proto
// DO NOT EDIT! // DO NOT EDIT!
/* /*
Package route_guide is a generated protocol buffer package. Package proto is a generated protocol buffer package.
It is generated from these files: It is generated from these files:
route_guide.proto proto/route_guide.proto
It has these top-level messages: It has these top-level messages:
Point Point
@ -15,9 +15,9 @@ It has these top-level messages:
RouteNote RouteNote
RouteSummary RouteSummary
*/ */
package route_guide package proto
import proto "github.com/golang/protobuf/proto" import proto1 "github.com/golang/protobuf/proto"
import ( import (
context "golang.org/x/net/context" context "golang.org/x/net/context"
@ -29,7 +29,7 @@ var _ context.Context
var _ grpc.ClientConn var _ grpc.ClientConn
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal var _ = proto1.Marshal
// Points are represented as latitude-longitude pairs in the E7 representation // Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer). // (degrees multiplied by 10**7 and rounded to the nearest integer).
@ -41,7 +41,7 @@ type Point struct {
} }
func (m *Point) Reset() { *m = Point{} } func (m *Point) Reset() { *m = Point{} }
func (m *Point) String() string { return proto.CompactTextString(m) } func (m *Point) String() string { return proto1.CompactTextString(m) }
func (*Point) ProtoMessage() {} func (*Point) ProtoMessage() {}
// A latitude-longitude rectangle, represented as two diagonally opposite // A latitude-longitude rectangle, represented as two diagonally opposite
@ -54,7 +54,7 @@ type Rectangle struct {
} }
func (m *Rectangle) Reset() { *m = Rectangle{} } func (m *Rectangle) Reset() { *m = Rectangle{} }
func (m *Rectangle) String() string { return proto.CompactTextString(m) } func (m *Rectangle) String() string { return proto1.CompactTextString(m) }
func (*Rectangle) ProtoMessage() {} func (*Rectangle) ProtoMessage() {}
func (m *Rectangle) GetLo() *Point { func (m *Rectangle) GetLo() *Point {
@ -82,7 +82,7 @@ type Feature struct {
} }
func (m *Feature) Reset() { *m = Feature{} } func (m *Feature) Reset() { *m = Feature{} }
func (m *Feature) String() string { return proto.CompactTextString(m) } func (m *Feature) String() string { return proto1.CompactTextString(m) }
func (*Feature) ProtoMessage() {} func (*Feature) ProtoMessage() {}
func (m *Feature) GetLocation() *Point { func (m *Feature) GetLocation() *Point {
@ -101,7 +101,7 @@ type RouteNote struct {
} }
func (m *RouteNote) Reset() { *m = RouteNote{} } func (m *RouteNote) Reset() { *m = RouteNote{} }
func (m *RouteNote) String() string { return proto.CompactTextString(m) } func (m *RouteNote) String() string { return proto1.CompactTextString(m) }
func (*RouteNote) ProtoMessage() {} func (*RouteNote) ProtoMessage() {}
func (m *RouteNote) GetLocation() *Point { func (m *RouteNote) GetLocation() *Point {
@ -128,7 +128,7 @@ type RouteSummary struct {
} }
func (m *RouteSummary) Reset() { *m = RouteSummary{} } func (m *RouteSummary) Reset() { *m = RouteSummary{} }
func (m *RouteSummary) String() string { return proto.CompactTextString(m) } func (m *RouteSummary) String() string { return proto1.CompactTextString(m) }
func (*RouteSummary) ProtoMessage() {} func (*RouteSummary) ProtoMessage() {}
func init() { func init() {
@ -170,7 +170,7 @@ func NewRouteGuideClient(cc *grpc.ClientConn) RouteGuideClient {
func (c *routeGuideClient) GetFeature(ctx context.Context, in *Point, opts ...grpc.CallOption) (*Feature, error) { func (c *routeGuideClient) GetFeature(ctx context.Context, in *Point, opts ...grpc.CallOption) (*Feature, error) {
out := new(Feature) out := new(Feature)
err := grpc.Invoke(ctx, "/route_guide.RouteGuide/GetFeature", in, out, c.cc, opts...) err := grpc.Invoke(ctx, "/proto.RouteGuide/GetFeature", in, out, c.cc, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -178,7 +178,7 @@ func (c *routeGuideClient) GetFeature(ctx context.Context, in *Point, opts ...gr
} }
func (c *routeGuideClient) ListFeatures(ctx context.Context, in *Rectangle, opts ...grpc.CallOption) (RouteGuide_ListFeaturesClient, error) { func (c *routeGuideClient) ListFeatures(ctx context.Context, in *Rectangle, opts ...grpc.CallOption) (RouteGuide_ListFeaturesClient, error) {
stream, err := grpc.NewClientStream(ctx, &_RouteGuide_serviceDesc.Streams[0], c.cc, "/route_guide.RouteGuide/ListFeatures", opts...) stream, err := grpc.NewClientStream(ctx, &_RouteGuide_serviceDesc.Streams[0], c.cc, "/proto.RouteGuide/ListFeatures", opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -210,7 +210,7 @@ func (x *routeGuideListFeaturesClient) Recv() (*Feature, error) {
} }
func (c *routeGuideClient) RecordRoute(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RecordRouteClient, error) { func (c *routeGuideClient) RecordRoute(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RecordRouteClient, error) {
stream, err := grpc.NewClientStream(ctx, &_RouteGuide_serviceDesc.Streams[1], c.cc, "/route_guide.RouteGuide/RecordRoute", opts...) stream, err := grpc.NewClientStream(ctx, &_RouteGuide_serviceDesc.Streams[1], c.cc, "/proto.RouteGuide/RecordRoute", opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -244,7 +244,7 @@ func (x *routeGuideRecordRouteClient) CloseAndRecv() (*RouteSummary, error) {
} }
func (c *routeGuideClient) RouteChat(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RouteChatClient, error) { func (c *routeGuideClient) RouteChat(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RouteChatClient, error) {
stream, err := grpc.NewClientStream(ctx, &_RouteGuide_serviceDesc.Streams[2], c.cc, "/route_guide.RouteGuide/RouteChat", opts...) stream, err := grpc.NewClientStream(ctx, &_RouteGuide_serviceDesc.Streams[2], c.cc, "/proto.RouteGuide/RouteChat", opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -304,9 +304,9 @@ func RegisterRouteGuideServer(s *grpc.Server, srv RouteGuideServer) {
s.RegisterService(&_RouteGuide_serviceDesc, srv) s.RegisterService(&_RouteGuide_serviceDesc, srv)
} }
func _RouteGuide_GetFeature_Handler(srv interface{}, ctx context.Context, buf []byte) (proto.Message, error) { func _RouteGuide_GetFeature_Handler(srv interface{}, ctx context.Context, buf []byte) (proto1.Message, error) {
in := new(Point) in := new(Point)
if err := proto.Unmarshal(buf, in); err != nil { if err := proto1.Unmarshal(buf, in); err != nil {
return nil, err return nil, err
} }
out, err := srv.(RouteGuideServer).GetFeature(ctx, in) out, err := srv.(RouteGuideServer).GetFeature(ctx, in)
@ -390,7 +390,7 @@ func (x *routeGuideRouteChatServer) Recv() (*RouteNote, error) {
} }
var _RouteGuide_serviceDesc = grpc.ServiceDesc{ var _RouteGuide_serviceDesc = grpc.ServiceDesc{
ServiceName: "route_guide.RouteGuide", ServiceName: "proto.RouteGuide",
HandlerType: (*RouteGuideServer)(nil), HandlerType: (*RouteGuideServer)(nil),
Methods: []grpc.MethodDesc{ Methods: []grpc.MethodDesc{
{ {

View File

@ -29,13 +29,16 @@
syntax = "proto3"; syntax = "proto3";
package route_guide; package proto;
// Interface exported by the server. // Interface exported by the server.
service RouteGuide { service RouteGuide {
// A simple RPC. // A simple RPC.
// //
// Obtains the feature at a given position. // Obtains the feature at a given position.
//
// If no feature is found for the given point, a feature with an empty name
// should be returned.
rpc GetFeature(Point) returns (Feature) {} rpc GetFeature(Point) returns (Feature) {}
// A server-to-client streaming RPC. // A server-to-client streaming RPC.

View File

@ -31,11 +31,16 @@
* *
*/ */
// Package main implements a simple grpc server that demonstrates how to use grpc go libraries
// to perform unary, client streaming, server streaming and full duplex RPCs.
//
// It implements the route guide service whose definition can be found in proto/route_guide.proto.
package main package main
import ( import (
"encoding/json" "encoding/json"
"flag" "flag"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
@ -49,11 +54,13 @@ import (
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
pb "google.golang.org/grpc/examples/route_guide" proto "github.com/golang/protobuf/proto"
pb "google.golang.org/grpc/examples/route_guide/proto"
) )
var ( var (
useTLS = flag.Bool("use_tls", false, "Connection uses TLS if true, else plain TCP") tls = flag.Bool("use_tls", false, "Connection uses TLS if true, else plain TCP")
certFile = flag.String("tls_cert_file", "testdata/server1.pem", "The TLS cert file") certFile = flag.String("tls_cert_file", "testdata/server1.pem", "The TLS cert file")
keyFile = flag.String("tls_key_file", "testdata/server1.key", "The TLS key file") keyFile = flag.String("tls_key_file", "testdata/server1.key", "The TLS key file")
jsonDBFile = flag.String("route_guide_db", "testdata/route_guide_db.json", "A json file containing a list of features") jsonDBFile = flag.String("route_guide_db", "testdata/route_guide_db.json", "A json file containing a list of features")
@ -61,15 +68,15 @@ var (
) )
type routeGuideServer struct { type routeGuideServer struct {
savedFeatures []pb.Feature savedFeatures []*pb.Feature
routeNotes map[pb.Point][]*pb.RouteNote routeNotes map[string][]*pb.RouteNote
} }
// GetFeature returns the feature at the given point. // GetFeature returns the feature at the given point.
func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) { func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) {
for _, feature := range s.savedFeatures { for _, feature := range s.savedFeatures {
if *(feature.Location) == *point { if proto.Equal(feature.Location, point) {
return &feature, nil return feature, nil
} }
} }
// No feature was found, return an unnamed feature // No feature was found, return an unnamed feature
@ -78,16 +85,9 @@ func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb
// ListFeatures lists all features comtained within the given bounding Rectangle. // ListFeatures lists all features comtained within the given bounding Rectangle.
func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error { func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error {
left := math.Min(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude))
right := math.Max(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude))
top := math.Max(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
bottom := math.Min(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
for _, feature := range s.savedFeatures { for _, feature := range s.savedFeatures {
if float64(feature.Location.Longitude) >= left && if inRange(feature.Location, rect) {
float64(feature.Location.Longitude) <= right && if err := stream.Send(feature); err != nil {
float64(feature.Location.Latitude) >= bottom &&
float64(feature.Location.Latitude) <= top {
if err := stream.Send(&feature); err != nil {
return err return err
} }
} }
@ -120,7 +120,7 @@ func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) e
} }
pointCount++ pointCount++
for _, feature := range s.savedFeatures { for _, feature := range s.savedFeatures {
if *(feature.Location) == *point { if proto.Equal(feature.Location, point) {
featureCount++ featureCount++
} }
} }
@ -142,14 +142,14 @@ func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error
if err != nil { if err != nil {
return err return err
} }
point := *(in.Location) key := serialize(in.Location)
if _, present := s.routeNotes[point]; !present { if _, present := s.routeNotes[key]; !present {
s.routeNotes[point] = []*pb.RouteNote{in} s.routeNotes[key] = []*pb.RouteNote{in}
} else { } else {
s.routeNotes[point] = append(s.routeNotes[point], in) s.routeNotes[key] = append(s.routeNotes[key], in)
} }
for _, note := range s.routeNotes[point] { for _, note := range s.routeNotes[key] {
if err := stream.Send(note); err != nil { if err := stream.Send(note); err != nil {
return err return err
} }
@ -161,10 +161,10 @@ func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error
func (s *routeGuideServer) loadFeatures(filePath string) { func (s *routeGuideServer) loadFeatures(filePath string) {
file, err := ioutil.ReadFile(filePath) file, err := ioutil.ReadFile(filePath)
if err != nil { if err != nil {
log.Fatal("Failed to load default features: %v\n", err) log.Fatal("Failed to load default features: %v", err)
} }
if err := json.Unmarshal(file, &(s.savedFeatures)); err != nil { if err := json.Unmarshal(file, &(s.savedFeatures)); err != nil {
log.Fatal("Failed to load default features: %v\n", err) log.Fatal("Failed to load default features: %v", err)
} }
} }
@ -172,17 +172,19 @@ func toRadians(num float64) float64 {
return num * math.Pi / float64(180) return num * math.Pi / float64(180)
} }
// calcDistance calculates the distance between two points using the "haversine" formula.
// This code was taken from http://www.movable-type.co.uk/scripts/latlong.html.
func calcDistance(p1 *pb.Point, p2 *pb.Point) int32 { func calcDistance(p1 *pb.Point, p2 *pb.Point) int32 {
const COORD_FACTOR float64 = 1e7 const CordFactor float64 = 1e7
const R float64 = float64(6371000) // metres const R float64 = float64(6371000) // metres
lat1 := float64(p1.Latitude) / COORD_FACTOR lat1 := float64(p1.Latitude) / CordFactor
lat2 := float64(p2.Latitude) / COORD_FACTOR lat2 := float64(p2.Latitude) / CordFactor
lon1 := float64(p1.Longitude) / COORD_FACTOR lng1 := float64(p1.Longitude) / CordFactor
lon2 := float64(p2.Longitude) / COORD_FACTOR lng2 := float64(p2.Longitude) / CordFactor
φ1 := toRadians(lat1) φ1 := toRadians(lat1)
φ2 := toRadians(lat2) φ2 := toRadians(lat2)
Δφ := toRadians(lat2 - lat1) Δφ := toRadians(lat2 - lat1)
Δλ := toRadians(lon2 - lon1) Δλ := toRadians(lng2 - lng1)
a := math.Sin(Δφ/2)*math.Sin(Δφ/2) + a := math.Sin(Δφ/2)*math.Sin(Δφ/2) +
math.Cos(φ1)*math.Cos(φ2)* math.Cos(φ1)*math.Cos(φ2)*
@ -193,10 +195,29 @@ func calcDistance(p1 *pb.Point, p2 *pb.Point) int32 {
return int32(distance) return int32(distance)
} }
func inRange(point *pb.Point, rect *pb.Rectangle) bool {
left := math.Min(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude))
right := math.Max(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude))
top := math.Max(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
bottom := math.Min(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
if float64(point.Longitude) >= left &&
float64(point.Longitude) <= right &&
float64(point.Latitude) >= bottom &&
float64(point.Latitude) <= top {
return true
}
return false
}
func serialize(point *pb.Point) string {
return fmt.Sprintf("%d %d", point.Latitude, point.Longitude)
}
func newServer() *routeGuideServer { func newServer() *routeGuideServer {
s := new(routeGuideServer) s := new(routeGuideServer)
s.loadFeatures(*jsonDBFile) s.loadFeatures(*jsonDBFile)
s.routeNotes = make(map[pb.Point][]*pb.RouteNote, 0) s.routeNotes = make(map[string][]*pb.RouteNote, 0)
return s return s
} }
@ -209,7 +230,7 @@ func main() {
} }
grpcServer := grpc.NewServer() grpcServer := grpc.NewServer()
pb.RegisterRouteGuideServer(grpcServer, newServer()) pb.RegisterRouteGuideServer(grpcServer, newServer())
if *useTLS { if *tls {
creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile) creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
if err != nil { if err != nil {
log.Fatalf("Failed to generate credentials %v", err) log.Fatalf("Failed to generate credentials %v", err)