Google的'编码折线算法'的C#实现

时间:2010-10-04 01:14:53

标签: c# .net algorithm google-maps gis

有没有人在C#中实现Google Encoded Polyline Algorithm的简洁而强大的实施?

我基本上想要实现这个签名:

public string Encode(IEnumerable<Point> points);

3 个答案:

答案 0 :(得分:45)

以下是我确定的实施方案:

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();
}

希望能帮助其他人。

答案 1 :(得分:15)

也许已经晚了但是我刚刚解决了同样的问题,但是为了编码位置列表和解码折线,我使用http://json2csharp.com/在C#中生成相应的类,以便用JsonConvert反序列化响应,如:

  var googleDirectionsResponse = JsonConvert.DeserializeObject<RootObject>(responseString);

这给了我这个定位的定义(我迟早会把它清理干净):

public class Location
{
    public double lat { get; set; }
    public double lng { get; set; }
}

我创建了一个转换器类来完成两个方向的技巧(IT不是原创的,它只是这个类的重构: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();
    }
}

我希望它有所帮助。

答案 2 :(得分:6)

Javascript实施,万一有人感兴趣:

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 _;
})();