diff --git a/lib/OpenLayers/Format/EncodedPolyline.js b/lib/OpenLayers/Format/EncodedPolyline.js index b6e81f48e3..7457bcf7f0 100644 --- a/lib/OpenLayers/Format/EncodedPolyline.js +++ b/lib/OpenLayers/Format/EncodedPolyline.js @@ -62,15 +62,16 @@ OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, { else if (this.geometryType != "point" && this.geometryType != "polygon") return null; - var points = this.decode(encoded, 2); - var pointGeometries = new Array(); - for (var i in points) { - var point = points[i]; - pointGeometries.push( - new OpenLayers.Geometry.Point(point[1] * 1e-5, point[0] * 1e-5) - ); + var flatPoints = this.decodeDeltas(encoded, 2); + var flatPointsLength = flatPoints.length; + + var pointGeometries = []; + for (var i = 0; i + 1 < flatPointsLength;) { + var y = flatPoints[i++], x = flatPoints[i++]; + pointGeometries.push(new OpenLayers.Geometry.Point(x, y)); } + if (this.geometryType == "point") return new OpenLayers.Feature.Vector( pointGeometries[0] @@ -102,29 +103,18 @@ OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, { * coordinates. */ decode: function(encoded, dims) { - var points = new Array(); - var point = new Array(dims); + var flatPoints = this.decodeDeltas(encoded, dims, 1); + var flatPointsLength = flatPoints.length; - // Reset the point array - for (var i = 0; i < point.length; ++i) - point[i] = 0; + var points = []; + for (var i = 0; i + (dims - 1) < flatPointsLength;) { + var point = []; - for (var i = 0; i < encoded.length;) { for (var dim = 0; dim < dims; ++dim) { - var result = 0; - var shift = 0; - - var b; - do { - b = encoded.charCodeAt(i++) - 63; - result |= (b & 0x1f) << shift; - shift += 5; - } while (b >= 0x20); - - point[dim] += ((result & 1) ? ~(result >> 1) : (result >> 1)); + point.push(flatPoints[i++]) } - points.push(point.slice(0)); + points.push(point); } return points; @@ -163,16 +153,16 @@ OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, { else return null; - var points = new Array(); - for (var i in pointGeometries) { + var flatPoints = []; + + var pointGeometriesLength = pointGeometries.length; + for (var i = 0; i < pointGeometriesLength; ++i) { var pointGeometry = pointGeometries[i]; - var point = [Math.round(pointGeometry.y * 1e5), - Math.round(pointGeometry.x * 1e5)]; - points.push(point); + flatPoints.push(pointGeometry.y); + flatPoints.push(pointGeometry.x); } - var result = this.encode(points, 2); - return result; + return this.encodeDeltas(flatPoints, 2); }, /** @@ -188,66 +178,377 @@ OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, { * {String} An encoded string */ encode: function (points, dims) { - var encoded_points = ""; + var flatPoints = []; - var lastPoint = new Array(dims); - for (var i = 0; i < lastPoint.length; ++i) - lastPoint[i] = 0; - - for (var i = 0; i < points.length; i++) { + var pointsLength = points.length; + for (var i = 0; i < pointsLength; ++i) { var point = points[i]; - for (var dim = 0; dim < lastPoint.length; ++dim) { - var delta = point[dim] - lastPoint[dim]; - encoded_points += this.encodeSignedNumber(delta); + for (var dim = 0; dim < dims; ++dim) { + flatPoints.push(point[dim]); } - - lastPoint = point; } - return encoded_points; + + return this.encodeDeltas(flatPoints, dims, 1); }, /** - * Method: encodeSignedNumber + * APIMethod: encodeDeltas + * Encode a list of n-dimensional points and return an encoded string + * + * Attention: This function will modify the passed array! + * + * Parameters: + * numbers - {Array.} A list of n-dimensional points. + * dimension - {number} The dimension of the points in the list. + * opt_factor - {number=} The factor by which the numbers will be + * multiplied. The remaining decimal places will get rounded away. + * + * Returns: + * {string} The encoded string. + */ + encodeDeltas: function(numbers, dimension, opt_factor) { + var factor = opt_factor || 1e5; + var d; + + var lastNumbers = new Array(dimension); + for (d = 0; d < dimension; ++d) { + lastNumbers[d] = 0; + } + + var numbersLength = numbers.length; + for (var i = 0; i < numbersLength;) { + for (d = 0; d < dimension; ++d, ++i) { + var num = numbers[i]; + var delta = num - lastNumbers[d]; + lastNumbers[d] = num; + + numbers[i] = delta; + } + } + + return this.encodeFloats(numbers, factor); + }, + + + /** + * APIMethod: decodeDeltas + * Decode a list of n-dimensional points from an encoded string + * + * Parameters: + * encoded - {string} An encoded string. + * dimension - {number} The dimension of the points in the encoded string. + * opt_factor - {number=} The factor by which the resulting numbers will + * be divided. + * + * Returns: + * {Array.} A list of n-dimensional points. + */ + decodeDeltas: function(encoded, dimension, opt_factor) { + var factor = opt_factor || 1e5; + var d; + + var lastNumbers = new Array(dimension); + for (d = 0; d < dimension; ++d) { + lastNumbers[d] = 0; + } + + var numbers = this.decodeFloats(encoded, factor); + + var numbersLength = numbers.length; + for (var i = 0; i < numbersLength;) { + for (d = 0; d < dimension; ++d, ++i) { + lastNumbers[d] += numbers[i]; + + numbers[i] = lastNumbers[d]; + } + } + + return numbers; + }, + + + /** + * APIMethod: encodeFloats + * Encode a list of floating point numbers and return an encoded string + * + * Attention: This function will modify the passed array! + * + * Parameters: + * numbers - {Array.} A list of floating point numbers. + * opt_factor - {number=} The factor by which the numbers will be + * multiplied. The remaining decimal places will get rounded away. + * + * Returns: + * {string} The encoded string. + */ + encodeFloats: function(numbers, opt_factor) { + var factor = opt_factor || 1e5; + + var numbersLength = numbers.length; + for (var i = 0; i < numbersLength; ++i) { + numbers[i] = Math.round(numbers[i] * factor); + } + + return this.encodeSignedIntegers(numbers); + }, + + + /** + * APIMethod: decodeFloats + * Decode a list of floating point numbers from an encoded string + * + * Parameters: + * encoded - {string} An encoded string. + * opt_factor - {number=} The factor by which the result will be divided. + * + * Returns: + * {Array.} A list of floating point numbers. + */ + decodeFloats: function(encoded, opt_factor) { + var factor = opt_factor || 1e5; + + var numbers = this.decodeSignedIntegers(encoded); + + var numbersLength = numbers.length; + for (var i = 0; i < numbersLength; ++i) { + numbers[i] /= factor; + } + + return numbers; + }, + + + /** + * APIMethod: encodeSignedIntegers + * Encode a list of signed integers and return an encoded string + * + * Attention: This function will modify the passed array! + * + * Parameters: + * numbers - {Array.} A list of signed integers. + * + * Returns: + * {string} The encoded string. + */ + encodeSignedIntegers: function(numbers) { + var numbersLength = numbers.length; + for (var i = 0; i < numbersLength; ++i) { + var num = numbers[i]; + + var signedNum = num << 1; + if (num < 0) { + signedNum = ~(signedNum); + } + + numbers[i] = signedNum; + } + + return this.encodeUnsignedIntegers(numbers); + }, + + + /** + * APIMethod: decodeSignedIntegers + * Decode a list of signed integers from an encoded string + * + * Parameters: + * encoded - {string} An encoded string. + * + * Returns: + * {Array.} A list of signed integers. + */ + decodeSignedIntegers: function(encoded) { + var numbers = this.decodeUnsignedIntegers(encoded); + + var numbersLength = numbers.length; + for (var i = 0; i < numbersLength; ++i) { + var num = numbers[i]; + numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1); + } + + return numbers; + }, + + + /** + * APIMethod: encodeUnsignedIntegers + * Encode a list of unsigned integers and return an encoded string + * + * Parameters: + * numbers - {Array.} A list of unsigned integers. + * + * Returns: + * {string} The encoded string. + */ + encodeUnsignedIntegers: function(numbers) { + var encoded = ''; + + var numbersLength = numbers.length; + for (var i = 0; i < numbersLength; ++i) { + encoded += this.encodeUnsignedInteger(numbers[i]); + } + + return encoded; + }, + + + /** + * APIMethod: decodeUnsignedIntegers + * Decode a list of unsigned integers from an encoded string + * + * Parameters: + * encoded - {string} An encoded string. + * + * Returns: + * {Array.} A list of unsigned integers. + */ + decodeUnsignedIntegers: function(encoded) { + var numbers = []; + + var current = 0; + var shift = 0; + + var encodedLength = encoded.length; + for (var i = 0; i < encodedLength; ++i) { + var b = encoded.charCodeAt(i) - 63; + + current |= (b & 0x1f) << shift; + + if (b < 0x20) { + numbers.push(current); + current = 0; + shift = 0; + } else { + shift += 5; + } + } + + return numbers; + }, + + + /** + * Method: encodeFloat + * Encode one single floating point number and return an encoded string + * + * Parameters: + * num - {number} Floating point number that should be encoded. + * opt_factor - {number=} The factor by which num will be multiplied. + * The remaining decimal places will get rounded away. + * + * Returns: + * {string} The encoded string. + */ + encodeFloat: function(num, opt_factor) { + num = Math.round(num * (opt_factor || 1e5)); + return this.encodeSignedInteger(num); + }, + + + /** + * Method: decodeFloat + * Decode one single floating point number from an encoded string + * + * Parameters: + * encoded - {string} An encoded string. + * opt_factor - {number=} The factor by which the result will be divided. + * + * Returns: + * {number} The decoded floating point number. + */ + decodeFloat: function(encoded, opt_factor) { + var result = this.decodeSignedInteger(encoded); + return result / (opt_factor || 1e5); + }, + + + /** + * Method: encodeSignedInteger * Encode one single signed integer and return an encoded string * * Parameters: - * num - {int} A signed integer that should be encoded + * num - {number} Signed integer that should be encoded. * * Returns: - * {String} An encoded string + * {string} The encoded string. */ - encodeSignedNumber: function (num) { - var sgn_num = num << 1; - if (num < 0) - sgn_num = ~(sgn_num); + encodeSignedInteger: function(num) { + var signedNum = num << 1; + if (num < 0) { + signedNum = ~(signedNum); + } - return this.encodeNumber(sgn_num); + return this.encodeUnsignedInteger(signedNum); }, + /** - * Method: encodeNumber - * Encode one single unsigned integer and return an encoded string - * - * encodeSignedNumber should be used instead of using this method directly! + * Method: decodeSignedInteger + * Decode one single signed integer from an encoded string * * Parameters: - * num - {int} An unsigned integer that should be encoded + * encoded - {string} An encoded string. * * Returns: - * {String} An encoded string + * {number} The decoded signed integer. */ - encodeNumber: function (num) { - var encodeString = ""; - var value; - while (num >= 0x20) { - value = (0x20 | (num & 0x1f)) + 63; - encodeString += (String.fromCharCode(value)); - num >>= 5; - } - value = num + 63; - encodeString += (String.fromCharCode(value)); - return encodeString; + decodeSignedInteger: function(encoded) { + var result = this.decodeUnsignedInteger(encoded); + return ((result & 1) ? ~(result >> 1) : (result >> 1)); + }, + + + /** + * Method: encodeUnsignedInteger + * Encode one single unsigned integer and return an encoded string + * + * Parameters: + * num - {number} Unsigned integer that should be encoded. + * + * Returns: + * {string} The encoded string. + */ + encodeUnsignedInteger: function(num) { + var value, encoded = ''; + while (num >= 0x20) { + value = (0x20 | (num & 0x1f)) + 63; + encoded += (String.fromCharCode(value)); + num >>= 5; + } + value = num + 63; + encoded += (String.fromCharCode(value)); + return encoded; + }, + + + /** + * Method: decodeUnsignedInteger + * Decode one single unsigned integer from an encoded string + * + * Parameters: + * encoded - {string} An encoded string. + * + * Returns: + * {number} The decoded unsigned integer. + */ + decodeUnsignedInteger: function(encoded) { + var result = 0; + var shift = 0; + + var encodedLength = encoded.length; + for (var i = 0; i < encodedLength; ++i) { + var b = encoded.charCodeAt(i) - 63; + + result |= (b & 0x1f) << shift; + + if (b < 0x20) + break; + + shift += 5; + } + + return result; }, CLASS_NAME: "OpenLayers.Format.EncodedPolyline" diff --git a/tests/Format/EncodedPolyline.html b/tests/Format/EncodedPolyline.html index 1a93a41afa..6bc7208de0 100644 --- a/tests/Format/EncodedPolyline.html +++ b/tests/Format/EncodedPolyline.html @@ -3,6 +3,27 @@