2  * Utility functions to decode/encode numbers and array's of numbers
 
   3  * to/from strings (Google maps polyline encoding)
 
   5  * Extends the L.Polyline and L.Polygon object with methods to convert
 
   6  * to and create from these strings.
 
   8  * Jan Pieter Waagmeester <jieter@jieter.nl>
 
  11  * http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/
 
  12  * (which is down as of december 2014)
 
  18         var defaultOptions = function (options) {
 
  19                 if (typeof options === 'number') {
 
  21                         options = { precision: options };
 
  23                         options = options || {};
 
  26                 options.precision = options.precision || 5;
 
  27                 options.factor = options.factor || Math.pow(10, options.precision);
 
  28                 options.dimension = options.dimension || 2;
 
  33                 encode: function (points, options) {
 
  34                         options = defaultOptions(options);
 
  37                         for (var i = 0, len = points.length; i < len; ++i) {
 
  38                                 var point = points[i];
 
  40                                 if (options.dimension === 2) {
 
  41                                         flatPoints.push(point.lat || point[0]);
 
  42                                         flatPoints.push(point.lng || point[1]);
 
  44                                         for (var dim = 0; dim < options.dimension; ++dim) {
 
  45                                                 flatPoints.push(point[dim]);
 
  50                         return this.encodeDeltas(flatPoints, options);
 
  53                 decode: function (encoded, options) {
 
  54                         options = defaultOptions(options);
 
  56                         var flatPoints = this.decodeDeltas(encoded, options);
 
  59                         for (var i = 0, len = flatPoints.length; i + (options.dimension - 1) < len;) {
 
  62                                 for (var dim = 0; dim < options.dimension; ++dim) {
 
  63                                         point.push(flatPoints[i++]);
 
  72                 encodeDeltas: function(numbers, options) {
 
  73                         options = defaultOptions(options);
 
  77                         for (var i = 0, len = numbers.length; i < len;) {
 
  78                                 for (var d = 0; d < options.dimension; ++d, ++i) {
 
  80                                         var delta = num - (lastNumbers[d] || 0);
 
  87                         return this.encodeFloats(numbers, options);
 
  90                 decodeDeltas: function(encoded, options) {
 
  91                         options = defaultOptions(options);
 
  95                         var numbers = this.decodeFloats(encoded, options);
 
  96                         for (var i = 0, len = numbers.length; i < len;) {
 
  97                                 for (var d = 0; d < options.dimension; ++d, ++i) {
 
  98                                         numbers[i] = lastNumbers[d] = numbers[i] + (lastNumbers[d] || 0);
 
 105                 encodeFloats: function(numbers, options) {
 
 106                         options = defaultOptions(options);
 
 108                         for (var i = 0, len = numbers.length; i < len; ++i) {
 
 109                                 numbers[i] = Math.round(numbers[i] * options.factor);
 
 112                         return this.encodeSignedIntegers(numbers);
 
 115                 decodeFloats: function(encoded, options) {
 
 116                         options = defaultOptions(options);
 
 118                         var numbers = this.decodeSignedIntegers(encoded);
 
 119                         for (var i = 0, len = numbers.length; i < len; ++i) {
 
 120                                 numbers[i] /= options.factor;
 
 126                 /* jshint bitwise:false */
 
 128                 encodeSignedIntegers: function(numbers) {
 
 129                         for (var i = 0, len = numbers.length; i < len; ++i) {
 
 130                                 var num = numbers[i];
 
 131                                 numbers[i] = (num < 0) ? ~(num << 1) : (num << 1);
 
 134                         return this.encodeUnsignedIntegers(numbers);
 
 137                 decodeSignedIntegers: function(encoded) {
 
 138                         var numbers = this.decodeUnsignedIntegers(encoded);
 
 140                         for (var i = 0, len = numbers.length; i < len; ++i) {
 
 141                                 var num = numbers[i];
 
 142                                 numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1);
 
 148                 encodeUnsignedIntegers: function(numbers) {
 
 150                         for (var i = 0, len = numbers.length; i < len; ++i) {
 
 151                                 encoded += this.encodeUnsignedInteger(numbers[i]);
 
 156                 decodeUnsignedIntegers: function(encoded) {
 
 162                         for (var i = 0, len = encoded.length; i < len; ++i) {
 
 163                                 var b = encoded.charCodeAt(i) - 63;
 
 165                                 current |= (b & 0x1f) << shift;
 
 168                                         numbers.push(current);
 
 179                 encodeSignedInteger: function (num) {
 
 180                         num = (num < 0) ? ~(num << 1) : (num << 1);
 
 181                         return this.encodeUnsignedInteger(num);
 
 184                 // This function is very similar to Google's, but I added
 
 185                 // some stuff to deal with the double slash issue.
 
 186                 encodeUnsignedInteger: function (num) {
 
 187                         var value, encoded = '';
 
 188                         while (num >= 0x20) {
 
 189                                 value = (0x20 | (num & 0x1f)) + 63;
 
 190                                 encoded += (String.fromCharCode(value));
 
 194                         encoded += (String.fromCharCode(value));
 
 199                 /* jshint bitwise:true */
 
 202         // Export Node module
 
 203         if (typeof module === 'object' && typeof module.exports === 'object') {
 
 204                 module.exports = PolylineUtil;
 
 207         // Inject functionality into Leaflet
 
 208         if (typeof L === 'object') {
 
 209                 if (!(L.Polyline.prototype.fromEncoded)) {
 
 210                         L.Polyline.fromEncoded = function (encoded, options) {
 
 211                                 return new L.Polyline(PolylineUtil.decode(encoded), options);
 
 214                 if (!(L.Polygon.prototype.fromEncoded)) {
 
 215                         L.Polygon.fromEncoded = function (encoded, options) {
 
 216                                 return new L.Polygon(PolylineUtil.decode(encoded), options);
 
 221                         encodePath: function () {
 
 222                                 return PolylineUtil.encode(this.getLatLngs());
 
 226                 if (!L.Polyline.prototype.encodePath) {
 
 227                         L.Polyline.include(encodeMixin);
 
 229                 if (!L.Polygon.prototype.encodePath) {
 
 230                         L.Polygon.include(encodeMixin);
 
 233                 L.PolylineUtil = PolylineUtil;