Google Maps polyline encoding in C#

One of the new features in the Advanced Sitecore Google Maps (WCAG edition) is the ability to draw polylines. Polylines in Google Maps can be drawn using 2 methods: By creating a new GPolyline with the array of coordinates to draw, or by using the GPolyline.fromEncoded function which takes an encoded string of coordinates and renders this as a line.

UPDATE 2018-03-07: Improved latitude/longitude rounding as suggested by Alex.

The encoded polyline function has the advantage of being faster and more compressed compared with feeding the GPolyline with an array of coordinates. However, some of the precision is lost, as the encoding algorithm only uses 5 digit coordinates. 5 digits is still a pretty good precision, so I decided to go with the encoded string.

I was looking for a method of encoded my coordinates in C# when i stumbled upon a solution on the SoulSolutions webpage, where the developer demonstrates a Javascript and C# method to encode and decode polyline coordinates.

This is the C# method re-written. SoulSolutions should be credited for the code. However, his code examples are incomplete, so I rewrote them. First I need a Coordinate class, which contains the latitude and longitude of a coordinate set (I have removed comments and other stuff to compress the code):

public class Coordinate
{
  public double Latitude { get; set; }
  public double Longitude { get; set; }
}

The following function EncodeCoordinates encodes a list of coordinates into an encoded string:

/// 
<summary>
/// Encodes the list of coordinates to a Google Maps encoded coordinate string.
/// </summary>

/// <param name="coordinates">The coordinates.</param>
/// <returns>Encoded coordinate string</returns>
public static string EncodeCoordinates(List<Coordinate> coordinates)
{
  int plat = 0;
  int plng = 0;
  StringBuilder encodedCoordinates = new StringBuilder();
  foreach (Coordinate coordinate in coordinates)
  {
    // Round to 5 decimal places and drop the decimal
    int late5 = (int)Math.Round((double)latitude * 1e5,MidpointRounding.AwayFromZero);
    int lnge5 = (int)Math.Round((double)longitude * 1e5,MidpointRounding.AwayFromZero);    
    // Encode the differences between the coordinates
    encodedCoordinates.Append(EncodeSignedNumber(late5 - plat));
    encodedCoordinates.Append(EncodeSignedNumber(lnge5 - plng));
    // Store the current coordinates
    plat = late5;
    plng = lnge5;
  }
  return encodedCoordinates.ToString();
}
/// 
<summary>
/// Encode a signed number in the encode format.
/// </summary>

/// <param name="num">The signed number</param>
/// <returns>The encoded string</returns>
private static string EncodeSignedNumber(int num)
{
  int sgn_num = num << 1; //shift the binary value
  if (num < 0) //if negative invert
  {
    sgn_num = ~(sgn_num);
  }
  return (EncodeNumber(sgn_num));
}

/// 
<summary>
/// Encode an unsigned number in the encode format.
/// </summary>

/// <param name="num">The unsigned number</param>
/// <returns>The encoded string</returns>
private static string EncodeNumber(int num)
{
  StringBuilder encodeString = new StringBuilder();
  while (num >= 0x20)
  {
    encodeString.Append((char)((0x20 | (num & 0x1f)) + 63));
    num >>= 5;
  }
  encodeString.Append((char)(num + 63));
  // All backslashes needs to be replaced with double backslashes
  // before being used in a Javascript string.
  return encodeString.ToString().Replace(@"\", @"\\");
}

Using the encoded string, I can draw a line surrounding Denmark like this:

var line = new GPolyline.fromEncoded({
               color: "#0000FF",
               weight: 5,
               opacity: 0.5,
               points: "kpgiI}{wt@{xeDfxv@ylqC|flAyfyDidPiioBoabD_fkAyjfDooZ{nyCtw_BihCnzlBzbyAl}XehjBfp`B_se@vdgAhdPya_BoabDipHoabDngiAsen@jz}@htcAzqt@itcAnha@|~eBdzh@qqnBf~w@zrlCjkx@fppAy{u@zflA{zRpeuC`zWh`]bmx@}byAlwn@ny{DncNn}nDsxd@uqG",
               levels: "BBBBBBBBBBBBBBBBBBBBBBBBBBBB",
               zoomFactor: 32,
               numLevels: 4 });

The line looks like this using 28 coordinates:

Line drawing using GPolyline.fromEncoded

Line drawing using GPolyline.fromEncoded

About briancaos

Developer at Pentia A/S since 2003. Have developed Web Applications using Sitecore Since Sitecore 4.1.
This entry was posted in c#, General .NET, Sitecore 6 and tagged , , , . Bookmark the permalink.

15 Responses to Google Maps polyline encoding in C#

  1. Simon says:

    Hi Brian,

    Thanks for the code on encoding coordinates. I am using your code above to retrieve a static map from google and at first it seemed to work very well. When I looked at some routes though, there are some points that don’t line up with where they should be once they are encoded. Have you seen this issue with accuracy? Some points are out by half a mile on the map, larger than what I would expect. I am using the encoding as some of my routes are quite long with hundreds of points.

    Thanks!

    Simon.

    Like

  2. briancaos says:

    The encoding algorithm precision is “only” 5 digits. However 5 digits shoul still provide accuracy in the feet range and should not bring your lines out of course by half a mile. I use the same algorithm in a professional product, an none of my customers have experienced such a bad precision.
    Without haven seen your code i would expect it to be your data that is off.

    Like

  3. Simon says:

    Yeah, the 5 digit precision should be more than fine. For the issue with the data, when displayed without encoding it appears correctly, then with encoding it is no longer correct. This only occurs where there is a large number of points to encode. I will look into it further to see what is happening.

    Simon.

    Like

  4. Robert Simon says:

    Hi Simon,

    Thanks for your code, it is quiet helpful!
    Have you managed to improve the encoding (+precision) with more than ex. 10000 point records?

    Robert

    Like

  5. briancaos says:

    Hi Robert,
    No I havent had any problems with the precision of the Google Map coordinates, so no changes here,

    Like

  6. Pingback: Calculate distance between two coordinates on earth « Brian Pedersen’s Sitecore and .NET Blog

  7. Pingback: Calculate distance between two coordinates on earth | CMS News Today

  8. Thomas says:

    I can report the same precision problems as by predecessors.

    Like

  9. Chris says:

    Hi, and thanks for sharing this!

    I implemented this algorithm in Scala and got one encoding character in a three point polyline wrong. To get it right i rounded the dropped decimal double instead of flooring it with an int cast, e.g. (int)Math.round(coordinate.Latitude * 1e5).

    /Chris

    Like

  10. Kim Jørgensen says:

    Hello Brian,

    Thanks for this great algorithm! I am trying to implement it into my system, and everything is working fine, but I have no idea how you use the last part to actually show the line in Google Maps?

    Thanks,
    Kim!

    Like

  11. mtb2440 says:

    Thank you for the algorithm. I was really stuck. What would it take to get 6 digits of precision? I changed it to 1e6, but then nothing displayed. Is there other code that would need to be changed. If it’s simple to do I’d love to see if it makes a difference.
    Thanks again!
    Matt

    Like

  12. Peter says:

    Thank you for the algorithm!

    I got sometimes differences between results of this code and googles polyline utility if the coordinate consisted of more than 5 digits after point. Thats why i rounded the coordinates with Math.Round() before using Brians code. Brians algorithm seems not to round (up or down depending on the value) but cuts unused digits after point.
    Try Erlangen (49.5891559, 10.9142728). 49.5891559 should be rounded to 49.58916.

    Like

  13. Luke Charles says:

    Also tried using this algorithm and its brilliant. But it doesn’t show up with out using “int late5 = (int) Math.Round(coordinate.Latitude * 1e5);”. And when it does display the route isn’t accurate. It’s probably the rounding but without that it doesn’t work at all. Its not the coordinates because they display accurately when used with other applications. Is there a max length for this code? When displaying a route with 1600+ coordinates its inaccurate but displaying a polygon of 5 coordinates works perfectly.

    Like

  14. Luke Charles says:

    I FIGURED IT OUT! This code replaces single backslashes(\) with double backslashes(\\) for use in Javascript and Im trying to run this in C#. So I replaced “return encodeString.ToString().Replace(@”\”, @”\\”);” with “return encodeString.ToString();” and it worked perfectly for me.

    Hope this helps anyone struggling with the accuracy issue in C#.

    Like

  15. Alex says:

    Bugfix:

    Change this lines:
    int late5 = (int)(coordinate.Latitude * 1e5);
    int lnge5 = (int)(coordinate.Longitude * 1e5);
    by:
    int late5 = (int)Math.Round((double)latitude * 1e5,MidpointRounding.AwayFromZero);
    int lnge5 = (int)Math.Round((double)longitude * 1e5,MidpointRounding.AwayFromZero);

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.