polishing route_guide example

pull/3207/head
Jan Tattermusch 9 years ago
parent 97eaec1cdc
commit 121c4d1221
  1. 61
      examples/csharp/route_guide/RouteGuide/RouteGuideUtil.cs
  2. 86
      examples/csharp/route_guide/RouteGuideClient/Program.cs
  3. 14
      examples/csharp/route_guide/RouteGuideServer/Program.cs
  4. 127
      examples/csharp/route_guide/RouteGuideServer/RouteGuideImpl.cs

@ -9,6 +9,9 @@ using System.Threading.Tasks;
namespace Examples
{
/// <summary>
/// Utility methods for the route guide example.
/// </summary>
public static class RouteGuideUtil
{
public const string DefaultFeaturesFile = "route_guide_db.json";
@ -18,21 +21,63 @@ namespace Examples
/// <summary>
/// Indicates whether the given feature exists (i.e. has a valid name).
/// </summary>
public static bool Exists(Feature feature)
public static bool Exists(this Feature feature)
{
return feature != null && (feature.Name.Length != 0);
}
public static double GetLatitude(Point point)
public static double GetLatitude(this Point point)
{
return point.Latitude / CoordFactor;
}
public static double GetLongitude(Point point)
public static double GetLongitude(this Point point)
{
return point.Longitude / CoordFactor;
}
/// <summary>
/// Calculate the distance between two points using the "haversine" formula.
/// This code was taken from http://www.movable-type.co.uk/scripts/latlong.html.
/// </summary>
/// <param name="start">the starting point</param>
/// <param name="end">the end point</param>
/// <returns>the distance between the points in meters</returns>
public static double GetDistance(this Point start, Point end)
{
double lat1 = start.GetLatitude();
double lat2 = end.GetLatitude();
double lon1 = start.GetLongitude();
double lon2 = end.GetLongitude();
int r = 6371000; // metres
double phi1 = ToRadians(lat1);
double phi2 = ToRadians(lat2);
double deltaPhi = ToRadians(lat2 - lat1);
double deltaLambda = ToRadians(lon2 - lon1);
double a = Math.Sin(deltaPhi / 2) * Math.Sin(deltaPhi / 2) + Math.Cos(phi1) * Math.Cos(phi2) * Math.Sin(deltaLambda / 2) * Math.Sin(deltaLambda / 2);
double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
return r * c;
}
/// <summary>
/// Returns <c>true</c> if rectangular area contains given point.
/// </summary>
public static bool Contains(this Rectangle rectangle, Point point)
{
int left = Math.Min(rectangle.Lo.Longitude, rectangle.Hi.Longitude);
int right = Math.Max(rectangle.Lo.Longitude, rectangle.Hi.Longitude);
int top = Math.Max(rectangle.Lo.Latitude, rectangle.Hi.Latitude);
int bottom = Math.Min(rectangle.Lo.Latitude, rectangle.Hi.Latitude);
return (point.Longitude >= left && point.Longitude <= right && point.Latitude >= bottom && point.Latitude <= top);
}
private static double ToRadians(double val)
{
return (Math.PI / 180) * val;
}
/// <summary>
/// Parses features from a JSON file.
/// </summary>
@ -41,13 +86,13 @@ namespace Examples
var features = new List<Feature>();
var jsonFeatures = JsonConvert.DeserializeObject<List<JsonFeature>>(File.ReadAllText(filename));
foreach(var jsonFeature in jsonFeatures)
{
features.Add(Feature.CreateBuilder().SetName(jsonFeature.name).SetLocation(
Point.CreateBuilder()
.SetLongitude(jsonFeature.location.longitude)
.SetLatitude(jsonFeature.location.latitude).Build()).Build());
features.Add(new Feature
{
Name = jsonFeature.name,
Location = new Point { Longitude = jsonFeature.location.longitude, Latitude = jsonFeature.location.latitude}
});
}
return features;
}

@ -30,27 +30,24 @@ namespace Examples
{
Log("*** GetFeature: lat={0} lon={1}", lat, lon);
Point request = Point.CreateBuilder().SetLatitude(lat).SetLongitude(lon).Build();
Point request = new Point { Latitude = lat, Longitude = lon };
Feature feature = client.GetFeature(request);
if (RouteGuideUtil.Exists(feature))
if (feature.Exists())
{
Log("Found feature called \"{0}\" at {1}, {2}",
feature.Name,
RouteGuideUtil.GetLatitude(feature.Location),
RouteGuideUtil.GetLongitude(feature.Location));
feature.Name, feature.Location.GetLatitude(), feature.Location.GetLongitude());
}
else
{
Log("Found no feature at {0}, {1}",
RouteGuideUtil.GetLatitude(feature.Location),
RouteGuideUtil.GetLongitude(feature.Location));
feature.Location.GetLatitude(), feature.Location.GetLongitude());
}
}
catch (RpcException e)
{
Log("RPC failed " + e);
throw e;
throw;
}
}
@ -65,18 +62,20 @@ namespace Examples
Log("*** ListFeatures: lowLat={0} lowLon={1} hiLat={2} hiLon={3}", lowLat, lowLon, hiLat,
hiLon);
Rectangle request =
Rectangle.CreateBuilder()
.SetLo(Point.CreateBuilder().SetLatitude(lowLat).SetLongitude(lowLon).Build())
.SetHi(Point.CreateBuilder().SetLatitude(hiLat).SetLongitude(hiLon).Build()).Build();
Rectangle request = new Rectangle
{
Lo = new Point { Latitude = lowLat, Longitude = lowLon },
Hi = new Point { Latitude = hiLat, Longitude = hiLon }
};
using (var call = client.ListFeatures(request))
{
var responseStream = call.ResponseStream;
StringBuilder responseLog = new StringBuilder("Result: ");
while (await call.ResponseStream.MoveNext())
while (await responseStream.MoveNext())
{
Feature feature = call.ResponseStream.Current;
Feature feature = responseStream.Current;
responseLog.Append(feature.ToString());
}
Log(responseLog.ToString());
@ -85,7 +84,7 @@ namespace Examples
catch (RpcException e)
{
Log("RPC failed " + e);
throw e;
throw;
}
}
@ -107,8 +106,7 @@ namespace Examples
{
int index = rand.Next(features.Count);
Point point = features[index].Location;
Log("Visiting point {0}, {1}", RouteGuideUtil.GetLatitude(point),
RouteGuideUtil.GetLongitude(point));
Log("Visiting point {0}, {1}", point.GetLatitude(), point.GetLongitude());
await call.RequestStream.WriteAsync(point);
@ -117,7 +115,7 @@ namespace Examples
}
await call.RequestStream.CompleteAsync();
RouteSummary summary = await call.Result;
RouteSummary summary = await call.ResponseAsync;
Log("Finished trip with {0} points. Passed {1} features. "
+ "Travelled {2} meters. It took {3} seconds.", summary.PointCount,
summary.FeatureCount, summary.Distance, summary.ElapsedTime);
@ -128,7 +126,7 @@ namespace Examples
catch (RpcException e)
{
Log("RPC failed", e);
throw e;
throw;
}
}
@ -141,8 +139,13 @@ namespace Examples
try
{
Log("*** RouteChat");
var requests =
new List<RouteNote> { NewNote("First message", 0, 0), NewNote("Second message", 0, 1), NewNote("Third message", 1, 0), NewNote("Fourth message", 1, 1) };
var requests = new List<RouteNote>
{
NewNote("First message", 0, 0),
NewNote("Second message", 0, 1),
NewNote("Third message", 1, 0),
NewNote("Fourth message", 0, 0)
};
using (var call = client.RouteChat())
{
@ -172,7 +175,7 @@ namespace Examples
catch (RpcException e)
{
Log("RPC failed", e);
throw e;
throw;
}
}
@ -188,36 +191,37 @@ namespace Examples
private RouteNote NewNote(string message, int lat, int lon)
{
return RouteNote.CreateBuilder().SetMessage(message).SetLocation(
Point.CreateBuilder().SetLatitude(lat).SetLongitude(lat).Build()).Build();
return new RouteNote
{
Message = message,
Location = new Point { Latitude = lat, Longitude = lon }
};
}
}
static void Main(string[] args)
{
GrpcEnvironment.Initialize();
var channel = new Channel("127.0.0.1:50052", Credentials.Insecure);
var client = new RouteGuideClient(RouteGuide.NewClient(channel));
using (Channel channel = new Channel("127.0.0.1:50052"))
{
var client = new RouteGuideClient(RouteGuide.NewStub(channel));
// Looking for a valid feature
client.GetFeature(409146138, -746188906);
// Looking for a valid feature
client.GetFeature(409146138, -746188906);
// Feature missing.
client.GetFeature(0, 0);
// Feature missing.
client.GetFeature(0, 0);
// Looking for features between 40, -75 and 42, -73.
client.ListFeatures(400000000, -750000000, 420000000, -730000000).Wait();
// Looking for features between 40, -75 and 42, -73.
client.ListFeatures(400000000, -750000000, 420000000, -730000000).Wait();
// Record a few randomly selected points from the features file.
client.RecordRoute(RouteGuideUtil.ParseFeatures(RouteGuideUtil.DefaultFeaturesFile), 10).Wait();
// Record a few randomly selected points from the features file.
client.RecordRoute(RouteGuideUtil.ParseFeatures(RouteGuideUtil.DefaultFeaturesFile), 10).Wait();
// Send and receive some notes.
client.RouteChat().Wait();
}
// Send and receive some notes.
client.RouteChat().Wait();
GrpcEnvironment.Shutdown();
channel.ShutdownAsync().Wait();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}

@ -11,20 +11,22 @@ namespace Examples
{
static void Main(string[] args)
{
const int Port = 50052;
var features = RouteGuideUtil.ParseFeatures(RouteGuideUtil.DefaultFeaturesFile);
GrpcEnvironment.Initialize();
Server server = new Server();
server.AddServiceDefinition(RouteGuide.BindService(new RouteGuideImpl(features)));
int port = server.AddListeningPort("localhost", 50052);
Server server = new Server
{
Services = { RouteGuide.BindService(new RouteGuideImpl(features)) },
Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) }
};
server.Start();
Console.WriteLine("RouteGuide server listening on port " + port);
Console.WriteLine("RouteGuide server listening on port " + Port);
Console.WriteLine("Press any key to stop the server...");
Console.ReadKey();
server.ShutdownAsync().Wait();
GrpcEnvironment.Shutdown();
}
}
}

@ -6,6 +6,8 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Grpc.Core.Utils;
namespace Examples
{
/// <summary>
@ -14,8 +16,8 @@ namespace Examples
public class RouteGuideImpl : RouteGuide.IRouteGuide
{
readonly List<Feature> features;
private readonly ConcurrentDictionary<Point, List<RouteNote>> routeNotes =
new ConcurrentDictionary<Point, List<RouteNote>>();
readonly object myLock = new object();
readonly Dictionary<Point, List<RouteNote>> routeNotes = new Dictionary<Point, List<RouteNote>>();
public RouteGuideImpl(List<Feature> features)
{
@ -26,7 +28,7 @@ namespace Examples
/// Gets the feature at the requested point. If no feature at that location
/// exists, an unnammed feature is returned at the provided location.
/// </summary>
public Task<Feature> GetFeature(Grpc.Core.ServerCallContext context, Point request)
public Task<Feature> GetFeature(Point request, Grpc.Core.ServerCallContext context)
{
return Task.FromResult(CheckFeature(request));
}
@ -34,34 +36,17 @@ namespace Examples
/// <summary>
/// Gets all features contained within the given bounding rectangle.
/// </summary>
public async Task ListFeatures(Grpc.Core.ServerCallContext context, Rectangle request, Grpc.Core.IServerStreamWriter<Feature> responseStream)
public async Task ListFeatures(Rectangle request, Grpc.Core.IServerStreamWriter<Feature> responseStream, Grpc.Core.ServerCallContext context)
{
int left = Math.Min(request.Lo.Longitude, request.Hi.Longitude);
int right = Math.Max(request.Lo.Longitude, request.Hi.Longitude);
int top = Math.Max(request.Lo.Latitude, request.Hi.Latitude);
int bottom = Math.Min(request.Lo.Latitude, request.Hi.Latitude);
foreach (var feature in features)
{
if (!RouteGuideUtil.Exists(feature))
{
continue;
}
int lat = feature.Location.Latitude;
int lon = feature.Location.Longitude;
if (lon >= left && lon <= right && lat >= bottom && lat <= top)
{
await responseStream.WriteAsync(feature);
}
}
var responses = features.FindAll( (feature) => feature.Exists() && request.Contains(feature.Location) );
await responseStream.WriteAllAsync(responses);
}
/// <summary>
/// Gets a stream of points, and responds with statistics about the "trip": number of points,
/// number of known features visited, total distance traveled, and total time spent.
/// </summary>
public async Task<RouteSummary> RecordRoute(Grpc.Core.ServerCallContext context, Grpc.Core.IAsyncStreamReader<Point> requestStream)
public async Task<RouteSummary> RecordRoute(Grpc.Core.IAsyncStreamReader<Point> requestStream, Grpc.Core.ServerCallContext context)
{
int pointCount = 0;
int featureCount = 0;
@ -74,61 +59,61 @@ namespace Examples
{
var point = requestStream.Current;
pointCount++;
if (RouteGuideUtil.Exists(CheckFeature(point)))
if (CheckFeature(point).Exists())
{
featureCount++;
}
if (previous != null)
{
distance += (int) CalcDistance(previous, point);
distance += (int) previous.GetDistance(point);
}
previous = point;
}
stopwatch.Stop();
return RouteSummary.CreateBuilder().SetPointCount(pointCount)
.SetFeatureCount(featureCount).SetDistance(distance)
.SetElapsedTime((int) (stopwatch.ElapsedMilliseconds / 1000)).Build();
return new RouteSummary
{
PointCount = pointCount,
FeatureCount = featureCount,
Distance = distance,
ElapsedTime = (int)(stopwatch.ElapsedMilliseconds / 1000)
};
}
/// <summary>
/// Receives a stream of message/location pairs, and responds with a stream of all previous
/// messages at each of those locations.
/// </summary>
public async Task RouteChat(Grpc.Core.ServerCallContext context, Grpc.Core.IAsyncStreamReader<RouteNote> requestStream, Grpc.Core.IServerStreamWriter<RouteNote> responseStream)
public async Task RouteChat(Grpc.Core.IAsyncStreamReader<RouteNote> requestStream, Grpc.Core.IServerStreamWriter<RouteNote> responseStream, Grpc.Core.ServerCallContext context)
{
while (await requestStream.MoveNext())
{
var note = requestStream.Current;
List<RouteNote> notes = GetOrCreateNotes(note.Location);
List<RouteNote> prevNotes;
lock (notes)
{
prevNotes = new List<RouteNote>(notes);
}
List<RouteNote> prevNotes = AddNoteForLocation(note.Location, note);
foreach (var prevNote in prevNotes)
{
await responseStream.WriteAsync(prevNote);
}
lock (notes)
{
notes.Add(note);
}
}
}
/// <summary>
/// Get the notes list for the given location. If missing, create it.
/// Adds a note for location and returns a list of pre-existing notes for that location (not containing the newly added note).
/// </summary>
private List<RouteNote> GetOrCreateNotes(Point location)
private List<RouteNote> AddNoteForLocation(Point location, RouteNote note)
{
List<RouteNote> notes = new List<RouteNote>();
routeNotes.TryAdd(location, notes);
return routeNotes[location];
lock (myLock)
{
List<RouteNote> notes;
if (!routeNotes.TryGetValue(location, out notes)) {
notes = new List<RouteNote>();
routeNotes.Add(location, notes);
}
var preexistingNotes = new List<RouteNote>(notes);
notes.Add(note);
return preexistingNotes;
}
}
/// <summary>
@ -138,47 +123,13 @@ namespace Examples
/// <returns>The feature object at the point Note that an empty name indicates no feature.</returns>
private Feature CheckFeature(Point location)
{
foreach (var feature in features)
var result = features.FirstOrDefault((feature) => feature.Location.Equals(location));
if (result == null)
{
if (feature.Location.Latitude == location.Latitude
&& feature.Location.Longitude == location.Longitude)
{
return feature;
}
// No feature was found, return an unnamed feature.
return new Feature { Name = "", Location = location };
}
// No feature was found, return an unnamed feature.
return Feature.CreateBuilder().SetName("").SetLocation(location).Build();
}
/// <summary>
/// Calculate the distance between two points using the "haversine" formula.
/// This code was taken from http://www.movable-type.co.uk/scripts/latlong.html.
/// </summary>
/// <param name="start">the starting point</param>
/// <param name="end">the end point</param>
/// <returns>the distance between the points in meters</returns>
private static double CalcDistance(Point start, Point end)
{
double lat1 = RouteGuideUtil.GetLatitude(start);
double lat2 = RouteGuideUtil.GetLatitude(end);
double lon1 = RouteGuideUtil.GetLongitude(start);
double lon2 = RouteGuideUtil.GetLongitude(end);
int r = 6371000; // metres
double φ1 = ToRadians(lat1);
double φ2 = ToRadians(lat2);
double Δφ = ToRadians(lat2 - lat1);
double Δλ = ToRadians(lon2 - lon1);
double a = Math.Sin(Δφ / 2) * Math.Sin(Δφ / 2) + Math.Cos(φ1) * Math.Cos(φ2) * Math.Sin(Δλ / 2) * Math.Sin(Δλ / 2);
double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
return r * c;
}
private static double ToRadians(double val)
{
return (Math.PI / 180) * val;
return result;
}
}
}

Loading…
Cancel
Save