Skip to content

Instantly share code, notes, and snippets.

@aronsommer
Forked from jieter/Polyline.encoded.js
Created August 27, 2025 18:23
Show Gist options
  • Select an option

  • Save aronsommer/3a7de6c33db3a3853239ef4ab2bc47d0 to your computer and use it in GitHub Desktop.

Select an option

Save aronsommer/3a7de6c33db3a3853239ef4ab2bc47d0 to your computer and use it in GitHub Desktop.

Revisions

  1. @jieter jieter created this gist Nov 28, 2013.
    154 changes: 154 additions & 0 deletions Polyline.encoded.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,154 @@
    /*
    * L.PolylineUtil contains utilify functions for polylines, two methods
    * are added to the L.Polyline object to support creation of polylines
    * from an encoded string and converting existing polylines to an
    * encoded string.
    *
    * - L.Polyline.fromEncoded(encoded [, options]) returns a L.Polyline
    * - L.Polyline.encodePath() returns a string
    *
    * Actual code from:
    * http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/\
    */

    (function () {
    'use strict';

    /* jshint bitwise:false */

    // This function is very similar to Google's, but I added
    // some stuff to deal with the double slash issue.
    var encodeNumber = function (num) {
    var encodeString = '';
    var nextValue, finalValue;
    while (num >= 0x20) {
    nextValue = (0x20 | (num & 0x1f)) + 63;
    encodeString += (String.fromCharCode(nextValue));
    num >>= 5;
    }
    finalValue = num + 63;
    encodeString += (String.fromCharCode(finalValue));
    return encodeString;
    };

    // This one is Google's verbatim.
    var encodeSignedNumber = function (num) {
    var sgn_num = num << 1;
    if (num < 0) {
    sgn_num = ~(sgn_num);
    }

    return encodeNumber(sgn_num);
    };

    var getLat = function (latlng) {
    if (latlng.lat) {
    return latlng.lat;
    } else {
    return latlng[0];
    }
    };
    var getLng = function (latlng) {
    if (latlng.lng) {
    return latlng.lng;
    } else {
    return latlng[1];
    }
    };

    var PolylineUtil = {
    encode: function (latlngs, precision) {
    var i, dlat, dlng;
    var plat = 0;
    var plng = 0;
    var encoded_points = '';

    precision = Math.pow(10, precision || 5);

    for (i = 0; i < latlngs.length; i++) {
    var lat = getLat(latlngs[i]);
    var lng = getLng(latlngs[i]);
    var latFloored = Math.floor(lat * precision);
    var lngFloored = Math.floor(lng * precision);
    dlat = latFloored - plat;
    dlng = lngFloored - plng;
    plat = latFloored;
    plng = lngFloored;
    encoded_points += encodeSignedNumber(dlat) + encodeSignedNumber(dlng);
    }
    return encoded_points;
    },

    decode: function (encoded, precision) {
    var len = encoded.length;
    var index = 0;
    var latlngs = [];
    var lat = 0;
    var lng = 0;

    precision = Math.pow(10, -(precision || 5));

    while (index < len) {
    var b;
    var shift = 0;
    var result = 0;
    do {
    b = encoded.charCodeAt(index++) - 63;
    result |= (b & 0x1f) << shift;
    shift += 5;
    } while (b >= 0x20);
    var dlat = ((result & 1) ? ~(result >> 1) : (result >> 1));
    lat += dlat;

    shift = 0;
    result = 0;
    do {
    b = encoded.charCodeAt(index++) - 63;
    result |= (b & 0x1f) << shift;
    shift += 5;
    } while (b >= 0x20);
    var dlng = ((result & 1) ? ~(result >> 1) : (result >> 1));
    lng += dlng;

    latlngs.push([lat * precision, lng * precision]);
    }

    return latlngs;
    }
    };
    /* jshint bitwise:true */

    // Export Node module
    if (typeof module === 'object' && typeof module.exports === 'object') {
    module.exports = PolylineUtil;
    }

    // Inject functionality into Leaflet
    if (typeof L === 'object') {
    if (!(L.Polyline.prototype.fromEncoded)) {
    L.Polyline.fromEncoded = function (encoded, options) {
    return new L.Polyline(PolylineUtil.decode(encoded), options);
    };
    }
    if (!(L.Polygon.prototype.fromEncoded)) {
    L.Polygon.fromEncoded = function (encoded, options) {
    return new L.Polygon(PolylineUtil.decode(encoded), options);
    };
    }

    var encodeMixin = {
    encodePath: function () {
    return PolylineUtil.encode(this.getLatLngs());
    }
    };

    if (!L.Polyline.prototype.encodePath) {
    L.Polyline.include(encodeMixin);
    }
    if (!L.Polygon.prototype.encodePath) {
    L.Polygon.include(encodeMixin);
    }

    L.PolylineUtil = PolylineUtil;
    }
    })();
    40 changes: 40 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,40 @@
    <!DOCTYPE html>
    <html>
    <head>
    <title>Leaflet L.Polyline.fromEncoded()</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
    <script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
    </head>
    <body>
    <div id="map" style="width: 600px; height: 400px"></div>

    <script src="Polyline.encoded.js"></script>
    <script>
    var map = new L.Map('map');
    L.tileLayer(
    'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
    {
    attribution: '&copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>',
    maxZoom: 18
    }
    ).addTo(map);

    var encoded = 'c{|xHbo|OSnFrSjW~M{EnFwVz@wy@vQsq@cBoUzJ{EzObBrIcLfE~C~H{Y~C~CfOgY{EjM{EgTcLzTsSwQcGf@IoKgJSkMsb@RkR' +
    'vVkRDon@SooBvBsXbGgE{@{EfE{EwLsXgEo{AcVgw@kRc|A_q@seAcLg@bBvQkCnKcGjCwQsSkM?cL{YjHrS{J~HgOwBgOjf@{Jj' +
    'u@cLjRcGz^oUjW{Tni@f@jRsIj\wBjf@sNbo@wG~iArDnlArIj\bGfr@bQf^~WrSnPvVfOjHnURzc@cQbQzc@nUrtAbBfYkC~M~H' +
    'nZ~Mz^f^_N~MrDbBwQkWsXR{E';

    var polyline = L.Polyline.fromEncoded(encoded).addTo(map);

    var polygon = L.Polygon.fromEncoded(encoded, {
    weight: 1,
    color: '#f30'
    }).addTo(map);

    map.fitBounds(polyline.getBounds());
    </script>
    </body>
    </html>