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') {
25 options = options || {};
28 options.precision = options.precision || 5;
29 options.factor = options.factor || Math.pow(10, options.precision);
30 options.dimension = options.dimension || 2;
35 encode: function (points, options) {
36 options = defaultOptions(options);
39 for (var i = 0, len = points.length; i < len; ++i) {
40 var point = points[i];
42 if (options.dimension === 2) {
43 flatPoints.push(point.lat || point[0]);
44 flatPoints.push(point.lng || point[1]);
46 for (var dim = 0; dim < options.dimension; ++dim) {
47 flatPoints.push(point[dim]);
52 return this.encodeDeltas(flatPoints, options);
55 decode: function (encoded, options) {
56 options = defaultOptions(options);
58 var flatPoints = this.decodeDeltas(encoded, options);
61 for (var i = 0, len = flatPoints.length; i + (options.dimension - 1) < len;) {
64 for (var dim = 0; dim < options.dimension; ++dim) {
65 point.push(flatPoints[i++]);
74 encodeDeltas: function(numbers, options) {
75 options = defaultOptions(options);
79 for (var i = 0, len = numbers.length; i < len;) {
80 for (var d = 0; d < options.dimension; ++d, ++i) {
82 var delta = num - (lastNumbers[d] || 0);
89 return this.encodeFloats(numbers, options);
92 decodeDeltas: function(encoded, options) {
93 options = defaultOptions(options);
97 var numbers = this.decodeFloats(encoded, options);
98 for (var i = 0, len = numbers.length; i < len;) {
99 for (var d = 0; d < options.dimension; ++d, ++i) {
100 numbers[i] = Math.round((lastNumbers[d] = numbers[i] + (lastNumbers[d] || 0)) * options.factor) / options.factor;
107 encodeFloats: function(numbers, options) {
108 options = defaultOptions(options);
110 for (var i = 0, len = numbers.length; i < len; ++i) {
111 numbers[i] = Math.round(numbers[i] * options.factor);
114 return this.encodeSignedIntegers(numbers);
117 decodeFloats: function(encoded, options) {
118 options = defaultOptions(options);
120 var numbers = this.decodeSignedIntegers(encoded);
121 for (var i = 0, len = numbers.length; i < len; ++i) {
122 numbers[i] /= options.factor;
128 /* jshint bitwise:false */
130 encodeSignedIntegers: function(numbers) {
131 for (var i = 0, len = numbers.length; i < len; ++i) {
132 var num = numbers[i];
133 numbers[i] = (num < 0) ? ~(num << 1) : (num << 1);
136 return this.encodeUnsignedIntegers(numbers);
139 decodeSignedIntegers: function(encoded) {
140 var numbers = this.decodeUnsignedIntegers(encoded);
142 for (var i = 0, len = numbers.length; i < len; ++i) {
143 var num = numbers[i];
144 numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1);
150 encodeUnsignedIntegers: function(numbers) {
152 for (var i = 0, len = numbers.length; i < len; ++i) {
153 encoded += this.encodeUnsignedInteger(numbers[i]);
158 decodeUnsignedIntegers: function(encoded) {
164 for (var i = 0, len = encoded.length; i < len; ++i) {
165 var b = encoded.charCodeAt(i) - 63;
167 current |= (b & 0x1f) << shift;
170 numbers.push(current);
181 encodeSignedInteger: function (num) {
182 num = (num < 0) ? ~(num << 1) : (num << 1);
183 return this.encodeUnsignedInteger(num);
186 // This function is very similar to Google's, but I added
187 // some stuff to deal with the double slash issue.
188 encodeUnsignedInteger: function (num) {
189 var value, encoded = '';
190 while (num >= 0x20) {
191 value = (0x20 | (num & 0x1f)) + 63;
192 encoded += (String.fromCharCode(value));
196 encoded += (String.fromCharCode(value));
201 /* jshint bitwise:true */
204 // Export Node module
205 if (typeof module === 'object' && typeof module.exports === 'object') {
206 module.exports = PolylineUtil;
209 // Inject functionality into Leaflet
210 if (typeof L === 'object') {
211 if (!(L.Polyline.prototype.fromEncoded)) {
212 L.Polyline.fromEncoded = function (encoded, options) {
213 return new L.Polyline(PolylineUtil.decode(encoded), options);
216 if (!(L.Polygon.prototype.fromEncoded)) {
217 L.Polygon.fromEncoded = function (encoded, options) {
218 return new L.Polygon(PolylineUtil.decode(encoded), options);
223 encodePath: function () {
224 return PolylineUtil.encode(this.getLatLngs());
228 if (!L.Polyline.prototype.encodePath) {
229 L.Polyline.include(encodeMixin);
231 if (!L.Polygon.prototype.encodePath) {
232 L.Polygon.include(encodeMixin);
235 L.PolylineUtil = PolylineUtil;