Does anyone have a concise and robust implementation of Google's Encoded Polyline Algorithm in C#?
I essentially want the implementation of this signature:
pu开发者_运维百科blic string Encode(IEnumerable<Point> points);
Here's the implementation I settled on:
public static string Encode(IEnumerable<GeoLocation> points)
{
var str = new StringBuilder();
var encodeDiff = (Action<int>)(diff => {
int shifted = diff << 1;
if (diff < 0)
shifted = ~shifted;
int rem = shifted;
while (rem >= 0x20)
{
str.Append((char)((0x20 | (rem & 0x1f)) + 63));
rem >>= 5;
}
str.Append((char)(rem + 63));
});
int lastLat = 0;
int lastLng = 0;
foreach (var point in points)
{
int lat = (int)Math.Round(point.Latitude * 1E5);
int lng = (int)Math.Round(point.Longitude * 1E5);
encodeDiff(lat - lastLat);
encodeDiff(lng - lastLng);
lastLat = lat;
lastLng = lng;
}
return str.ToString();
}
Hope that helps someone else out.
Maybe is late but I've just solved the same problem but to Encode list of locations and Decode polylines, I used http://json2csharp.com/ to generate corresponding classes in C# in order to deserialize the response with JsonConvert like:
var googleDirectionsResponse = JsonConvert.DeserializeObject<RootObject>(responseString);
This gave me this definition of Location (I'll clean it sooner or later):
public class Location
{
public double lat { get; set; }
public double lng { get; set; }
}
And I created a converter class to do the trick in both directions (IT's not original, it is just a refactor from this class: https://gist.github.com/shinyzhu/4617989 ):
/// <summary>
/// Google Polyline Converter (Encoder and Decoder)
/// </summary>
public static class GooglePolylineConverter
{
/// <summary>
/// Decodes the specified polyline string.
/// </summary>
/// <param name="polylineString">The polyline string.</param>
/// <returns>A list with Locations</returns>
public static IEnumerable<Location> Decode(string polylineString)
{
if (string.IsNullOrEmpty(polylineString))
throw new ArgumentNullException(nameof(polylineString));
var polylineChars = polylineString.ToCharArray();
var index = 0;
var currentLat = 0;
var currentLng = 0;
while (index < polylineChars.Length)
{
// Next lat
var sum = 0;
var shifter = 0;
int nextFiveBits;
do
{
nextFiveBits = polylineChars[index++] - 63;
sum |= (nextFiveBits & 31) << shifter;
shifter += 5;
} while (nextFiveBits >= 32 && index < polylineChars.Length);
if (index >= polylineChars.Length)
break;
currentLat += (sum & 1) == 1 ? ~(sum >> 1) : (sum >> 1);
// Next lng
sum = 0;
shifter = 0;
do
{
nextFiveBits = polylineChars[index++] - 63;
sum |= (nextFiveBits & 31) << shifter;
shifter += 5;
} while (nextFiveBits >= 32 && index < polylineChars.Length);
if (index >= polylineChars.Length && nextFiveBits >= 32)
break;
currentLng += (sum & 1) == 1 ? ~(sum >> 1) : (sum >> 1);
yield return new Location
{
lat = Convert.ToDouble(currentLat) / 1E5,
lng = Convert.ToDouble(currentLng) / 1E5
};
}
}
/// <summary>
/// Encodes the specified locations list.
/// </summary>
/// <param name="locations">The locations.</param>
/// <returns>The polyline string.</returns>
public static string Encode(IEnumerable<Location> locations)
{
var str = new StringBuilder();
var encodeDiff = (Action<int>)(diff =>
{
var shifted = diff << 1;
if (diff < 0)
shifted = ~shifted;
var rem = shifted;
while (rem >= 0x20)
{
str.Append((char)((0x20 | (rem & 0x1f)) + 63));
rem >>= 5;
}
str.Append((char)(rem + 63));
});
var lastLat = 0;
var lastLng = 0;
foreach (var point in locations)
{
var lat = (int)Math.Round(point.lat * 1E5);
var lng = (int)Math.Round(point.lng * 1E5);
encodeDiff(lat - lastLat);
encodeDiff(lng - lastLng);
lastLat = lat;
lastLng = lng;
}
return str.ToString();
}
}
I hope it helps.
Javascript implementation, in case anyone is interested:
var polyline_encoder = (function() {
var _ = {};
var invert_bits = function(str) {
var ret = "";
for(var i=0; i<str.length; i++) {
if(str.charAt(i) == "1")
ret += "0";
else
ret += "1";
}
return ret;
};
var get_binary = function(num) {
var binary = parseInt(num).toString(2);
var bit_difference = 32 - binary.length;
for(var i=0; i<bit_difference; i++)
binary = "0" + binary;
if(num < 0) {
binary = invert_bits(binary);
binary = parseInt(binary, 2);
binary++;
return parseInt(binary).toString(2);
}
return binary;
};
_.encode_polyline = function(points) {
var ret = "";
var last_point, val_1, val_2;
for(var i=0; i<points.length; i++) {
if(!last_point) {
val_1 = points[i][0];
val_2 = points[i][1];
} else {
val_1 = points[i][0] - last_point[0];
val_2 = points[i][1] - last_point[1];
}
last_point = points[i];
ret += _.encode_polyline_value(val_1) + _.encode_polyline_value(val_2);
}
return ret;
};
_.encode_polyline_value = function(value) {
var ret = "";
value = Math.round(value * 100000);
var shifted = value << 1;
if(shifted < 0)
shifted = ~shifted;
var rem = shifted;
while(rem >= 32) {
ret += get_ascii_value(((0x20 | (rem & 0x1f)) + 63));
rem >>= 5;
}
ret += get_ascii_value(rem + 63);
return ret;
};
var get_ascii_value = function(num) {
var ascii_table =
// 0 thru 9
"??????????" +
// 10 thru 19
"??????????" +
// 20 thru 29
"??????????" +
// 30 thru 39
"?? !\"#$%&'" +
// 40 thru 49
"()*+,-./01" +
// 50 thru 59
"23456789:;" +
// 60 thru 69
"<=>?@ABCDE" +
// 70 thru 79
"FGHIJKLMNO" +
// 80 thru 89
"PQRSTUVWXY" +
// 90 thru 99
"Z[\\]^_`abc" +
// 100 thru 109
"defghijklm" +
// 110 thru 119
"nopqrstuvw" +
// 120 thru 127
"xyz{|}~?";
var value = ascii_table.substr(num, 1);
if(value == "?")
value = "";
return value;
};
return _;
})();
精彩评论