/lib/OpenLayers/Format/EncodedPolyline.js

https://github.com/pgiraud/openlayers · JavaScript · 557 lines · 234 code · 86 blank · 237 comment · 62 complexity · e92a20c1034b464b7d752e1174f4af1a MD5 · raw file

  1. /* Copyright (c) 2006-2015 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the 2-clause BSD license.
  3. * See license.txt in the OpenLayers distribution or repository for the
  4. * full text of the license. */
  5. /**
  6. * @requires OpenLayers/Format.js
  7. * @requires OpenLayers/Feature/Vector.js
  8. */
  9. /**
  10. * Class: OpenLayers.Format.EncodedPolyline
  11. * Class for reading and writing encoded polylines. Create a new instance
  12. * with the <OpenLayers.Format.EncodedPolyline> constructor.
  13. *
  14. * Inherits from:
  15. * - <OpenLayers.Format>
  16. */
  17. OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, {
  18. /**
  19. * APIProperty: geometryType
  20. * {String} Geometry type to output. One of: linestring (default),
  21. * linearring, point, multipoint or polygon. If the geometryType is
  22. * point, only the first point of the string is returned.
  23. */
  24. geometryType: "linestring",
  25. /**
  26. * Constructor: OpenLayers.Format.EncodedPolyline
  27. * Create a new parser for encoded polylines
  28. *
  29. * Parameters:
  30. * options - {Object} An optional object whose properties will be set on
  31. * this instance
  32. *
  33. * Returns:
  34. * {<OpenLayers.Format.EncodedPolyline>} A new encoded polylines parser.
  35. */
  36. initialize: function(options) {
  37. OpenLayers.Format.prototype.initialize.apply(this, [options]);
  38. },
  39. /**
  40. * APIMethod: read
  41. * Deserialize an encoded polyline string and return a vector feature.
  42. *
  43. * Parameters:
  44. * encoded - {String} An encoded polyline string
  45. *
  46. * Returns:
  47. * {<OpenLayers.Feature.Vector>} A vector feature with a linestring.
  48. */
  49. read: function(encoded) {
  50. var geomType;
  51. if (this.geometryType == "linestring")
  52. geomType = OpenLayers.Geometry.LineString;
  53. else if (this.geometryType == "linearring")
  54. geomType = OpenLayers.Geometry.LinearRing;
  55. else if (this.geometryType == "multipoint")
  56. geomType = OpenLayers.Geometry.MultiPoint;
  57. else if (this.geometryType != "point" && this.geometryType != "polygon")
  58. return null;
  59. var flatPoints = this.decodeDeltas(encoded, 2);
  60. var flatPointsLength = flatPoints.length;
  61. var pointGeometries = [];
  62. for (var i = 0; i + 1 < flatPointsLength;) {
  63. var y = flatPoints[i++], x = flatPoints[i++];
  64. pointGeometries.push(new OpenLayers.Geometry.Point(x, y));
  65. }
  66. if (this.geometryType == "point")
  67. return new OpenLayers.Feature.Vector(
  68. pointGeometries[0]
  69. );
  70. if (this.geometryType == "polygon")
  71. return new OpenLayers.Feature.Vector(
  72. new OpenLayers.Geometry.Polygon([
  73. new OpenLayers.Geometry.LinearRing(pointGeometries)
  74. ])
  75. );
  76. return new OpenLayers.Feature.Vector(
  77. new geomType(pointGeometries)
  78. );
  79. },
  80. /**
  81. * APIMethod: decode
  82. * Deserialize an encoded string and return an array of n-dimensional
  83. * points.
  84. *
  85. * Parameters:
  86. * encoded - {String} An encoded string
  87. * dims - {int} The dimension of the points that are returned
  88. *
  89. * Returns:
  90. * {Array(Array(int))} An array containing n-dimensional arrays of
  91. * coordinates.
  92. */
  93. decode: function(encoded, dims, opt_factor) {
  94. var factor = opt_factor || 1e5;
  95. var flatPoints = this.decodeDeltas(encoded, dims, factor);
  96. var flatPointsLength = flatPoints.length;
  97. var points = [];
  98. for (var i = 0; i + (dims - 1) < flatPointsLength;) {
  99. var point = [];
  100. for (var dim = 0; dim < dims; ++dim) {
  101. point.push(flatPoints[i++])
  102. }
  103. points.push(point);
  104. }
  105. return points;
  106. },
  107. /**
  108. * APIMethod: write
  109. * Serialize a feature or array of features into a WKT string.
  110. *
  111. * Parameters:
  112. * features - {<OpenLayers.Feature.Vector>|Array} A feature or array of
  113. * features
  114. *
  115. * Returns:
  116. * {String} The WKT string representation of the input geometries
  117. */
  118. write: function(features) {
  119. var feature;
  120. if (features.constructor == Array)
  121. feature = features[0];
  122. else
  123. feature = features;
  124. var geometry = feature.geometry;
  125. var type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
  126. var pointGeometries;
  127. if (type == "point")
  128. pointGeometries = new Array(geometry);
  129. else if (type == "linestring" ||
  130. type == "linearring" ||
  131. type == "multipoint")
  132. pointGeometries = geometry.components;
  133. else if (type == "polygon")
  134. pointGeometries = geometry.components[0].components;
  135. else
  136. return null;
  137. var flatPoints = [];
  138. var pointGeometriesLength = pointGeometries.length;
  139. for (var i = 0; i < pointGeometriesLength; ++i) {
  140. var pointGeometry = pointGeometries[i];
  141. flatPoints.push(pointGeometry.y);
  142. flatPoints.push(pointGeometry.x);
  143. }
  144. return this.encodeDeltas(flatPoints, 2);
  145. },
  146. /**
  147. * APIMethod: encode
  148. * Serialize an array of n-dimensional points and return an encoded string
  149. *
  150. * Parameters:
  151. * points - {Array(Array(int))} An array containing n-dimensional
  152. * arrays of coordinates
  153. * dims - {int} The dimension of the points that should be read
  154. *
  155. * Returns:
  156. * {String} An encoded string
  157. */
  158. encode: function (points, dims, opt_factor) {
  159. var factor = opt_factor || 1e5;
  160. var flatPoints = [];
  161. var pointsLength = points.length;
  162. for (var i = 0; i < pointsLength; ++i) {
  163. var point = points[i];
  164. for (var dim = 0; dim < dims; ++dim) {
  165. flatPoints.push(point[dim]);
  166. }
  167. }
  168. return this.encodeDeltas(flatPoints, dims, factor);
  169. },
  170. /**
  171. * APIMethod: encodeDeltas
  172. * Encode a list of n-dimensional points and return an encoded string
  173. *
  174. * Attention: This function will modify the passed array!
  175. *
  176. * Parameters:
  177. * numbers - {Array.<number>} A list of n-dimensional points.
  178. * dimension - {number} The dimension of the points in the list.
  179. * opt_factor - {number=} The factor by which the numbers will be
  180. * multiplied. The remaining decimal places will get rounded away.
  181. *
  182. * Returns:
  183. * {string} The encoded string.
  184. */
  185. encodeDeltas: function(numbers, dimension, opt_factor) {
  186. var factor = opt_factor || 1e5;
  187. var d;
  188. var lastNumbers = new Array(dimension);
  189. for (d = 0; d < dimension; ++d) {
  190. lastNumbers[d] = 0;
  191. }
  192. var numbersLength = numbers.length;
  193. for (var i = 0; i < numbersLength;) {
  194. for (d = 0; d < dimension; ++d, ++i) {
  195. var num = numbers[i];
  196. var delta = num - lastNumbers[d];
  197. lastNumbers[d] = num;
  198. numbers[i] = delta;
  199. }
  200. }
  201. return this.encodeFloats(numbers, factor);
  202. },
  203. /**
  204. * APIMethod: decodeDeltas
  205. * Decode a list of n-dimensional points from an encoded string
  206. *
  207. * Parameters:
  208. * encoded - {string} An encoded string.
  209. * dimension - {number} The dimension of the points in the encoded string.
  210. * opt_factor - {number=} The factor by which the resulting numbers will
  211. * be divided.
  212. *
  213. * Returns:
  214. * {Array.<number>} A list of n-dimensional points.
  215. */
  216. decodeDeltas: function(encoded, dimension, opt_factor) {
  217. var factor = opt_factor || 1e5;
  218. var d;
  219. var lastNumbers = new Array(dimension);
  220. for (d = 0; d < dimension; ++d) {
  221. lastNumbers[d] = 0;
  222. }
  223. var numbers = this.decodeFloats(encoded, factor);
  224. var numbersLength = numbers.length;
  225. for (var i = 0; i < numbersLength;) {
  226. for (d = 0; d < dimension; ++d, ++i) {
  227. lastNumbers[d] += numbers[i];
  228. numbers[i] = lastNumbers[d];
  229. }
  230. }
  231. return numbers;
  232. },
  233. /**
  234. * APIMethod: encodeFloats
  235. * Encode a list of floating point numbers and return an encoded string
  236. *
  237. * Attention: This function will modify the passed array!
  238. *
  239. * Parameters:
  240. * numbers - {Array.<number>} A list of floating point numbers.
  241. * opt_factor - {number=} The factor by which the numbers will be
  242. * multiplied. The remaining decimal places will get rounded away.
  243. *
  244. * Returns:
  245. * {string} The encoded string.
  246. */
  247. encodeFloats: function(numbers, opt_factor) {
  248. var factor = opt_factor || 1e5;
  249. var numbersLength = numbers.length;
  250. for (var i = 0; i < numbersLength; ++i) {
  251. numbers[i] = Math.round(numbers[i] * factor);
  252. }
  253. return this.encodeSignedIntegers(numbers);
  254. },
  255. /**
  256. * APIMethod: decodeFloats
  257. * Decode a list of floating point numbers from an encoded string
  258. *
  259. * Parameters:
  260. * encoded - {string} An encoded string.
  261. * opt_factor - {number=} The factor by which the result will be divided.
  262. *
  263. * Returns:
  264. * {Array.<number>} A list of floating point numbers.
  265. */
  266. decodeFloats: function(encoded, opt_factor) {
  267. var factor = opt_factor || 1e5;
  268. var numbers = this.decodeSignedIntegers(encoded);
  269. var numbersLength = numbers.length;
  270. for (var i = 0; i < numbersLength; ++i) {
  271. numbers[i] /= factor;
  272. }
  273. return numbers;
  274. },
  275. /**
  276. * APIMethod: encodeSignedIntegers
  277. * Encode a list of signed integers and return an encoded string
  278. *
  279. * Attention: This function will modify the passed array!
  280. *
  281. * Parameters:
  282. * numbers - {Array.<number>} A list of signed integers.
  283. *
  284. * Returns:
  285. * {string} The encoded string.
  286. */
  287. encodeSignedIntegers: function(numbers) {
  288. var numbersLength = numbers.length;
  289. for (var i = 0; i < numbersLength; ++i) {
  290. var num = numbers[i];
  291. var signedNum = num << 1;
  292. if (num < 0) {
  293. signedNum = ~(signedNum);
  294. }
  295. numbers[i] = signedNum;
  296. }
  297. return this.encodeUnsignedIntegers(numbers);
  298. },
  299. /**
  300. * APIMethod: decodeSignedIntegers
  301. * Decode a list of signed integers from an encoded string
  302. *
  303. * Parameters:
  304. * encoded - {string} An encoded string.
  305. *
  306. * Returns:
  307. * {Array.<number>} A list of signed integers.
  308. */
  309. decodeSignedIntegers: function(encoded) {
  310. var numbers = this.decodeUnsignedIntegers(encoded);
  311. var numbersLength = numbers.length;
  312. for (var i = 0; i < numbersLength; ++i) {
  313. var num = numbers[i];
  314. numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1);
  315. }
  316. return numbers;
  317. },
  318. /**
  319. * APIMethod: encodeUnsignedIntegers
  320. * Encode a list of unsigned integers and return an encoded string
  321. *
  322. * Parameters:
  323. * numbers - {Array.<number>} A list of unsigned integers.
  324. *
  325. * Returns:
  326. * {string} The encoded string.
  327. */
  328. encodeUnsignedIntegers: function(numbers) {
  329. var encoded = '';
  330. var numbersLength = numbers.length;
  331. for (var i = 0; i < numbersLength; ++i) {
  332. encoded += this.encodeUnsignedInteger(numbers[i]);
  333. }
  334. return encoded;
  335. },
  336. /**
  337. * APIMethod: decodeUnsignedIntegers
  338. * Decode a list of unsigned integers from an encoded string
  339. *
  340. * Parameters:
  341. * encoded - {string} An encoded string.
  342. *
  343. * Returns:
  344. * {Array.<number>} A list of unsigned integers.
  345. */
  346. decodeUnsignedIntegers: function(encoded) {
  347. var numbers = [];
  348. var current = 0;
  349. var shift = 0;
  350. var encodedLength = encoded.length;
  351. for (var i = 0; i < encodedLength; ++i) {
  352. var b = encoded.charCodeAt(i) - 63;
  353. current |= (b & 0x1f) << shift;
  354. if (b < 0x20) {
  355. numbers.push(current);
  356. current = 0;
  357. shift = 0;
  358. } else {
  359. shift += 5;
  360. }
  361. }
  362. return numbers;
  363. },
  364. /**
  365. * Method: encodeFloat
  366. * Encode one single floating point number and return an encoded string
  367. *
  368. * Parameters:
  369. * num - {number} Floating point number that should be encoded.
  370. * opt_factor - {number=} The factor by which num will be multiplied.
  371. * The remaining decimal places will get rounded away.
  372. *
  373. * Returns:
  374. * {string} The encoded string.
  375. */
  376. encodeFloat: function(num, opt_factor) {
  377. num = Math.round(num * (opt_factor || 1e5));
  378. return this.encodeSignedInteger(num);
  379. },
  380. /**
  381. * Method: decodeFloat
  382. * Decode one single floating point number from an encoded string
  383. *
  384. * Parameters:
  385. * encoded - {string} An encoded string.
  386. * opt_factor - {number=} The factor by which the result will be divided.
  387. *
  388. * Returns:
  389. * {number} The decoded floating point number.
  390. */
  391. decodeFloat: function(encoded, opt_factor) {
  392. var result = this.decodeSignedInteger(encoded);
  393. return result / (opt_factor || 1e5);
  394. },
  395. /**
  396. * Method: encodeSignedInteger
  397. * Encode one single signed integer and return an encoded string
  398. *
  399. * Parameters:
  400. * num - {number} Signed integer that should be encoded.
  401. *
  402. * Returns:
  403. * {string} The encoded string.
  404. */
  405. encodeSignedInteger: function(num) {
  406. var signedNum = num << 1;
  407. if (num < 0) {
  408. signedNum = ~(signedNum);
  409. }
  410. return this.encodeUnsignedInteger(signedNum);
  411. },
  412. /**
  413. * Method: decodeSignedInteger
  414. * Decode one single signed integer from an encoded string
  415. *
  416. * Parameters:
  417. * encoded - {string} An encoded string.
  418. *
  419. * Returns:
  420. * {number} The decoded signed integer.
  421. */
  422. decodeSignedInteger: function(encoded) {
  423. var result = this.decodeUnsignedInteger(encoded);
  424. return ((result & 1) ? ~(result >> 1) : (result >> 1));
  425. },
  426. /**
  427. * Method: encodeUnsignedInteger
  428. * Encode one single unsigned integer and return an encoded string
  429. *
  430. * Parameters:
  431. * num - {number} Unsigned integer that should be encoded.
  432. *
  433. * Returns:
  434. * {string} The encoded string.
  435. */
  436. encodeUnsignedInteger: function(num) {
  437. var value, encoded = '';
  438. while (num >= 0x20) {
  439. value = (0x20 | (num & 0x1f)) + 63;
  440. encoded += (String.fromCharCode(value));
  441. num >>= 5;
  442. }
  443. value = num + 63;
  444. encoded += (String.fromCharCode(value));
  445. return encoded;
  446. },
  447. /**
  448. * Method: decodeUnsignedInteger
  449. * Decode one single unsigned integer from an encoded string
  450. *
  451. * Parameters:
  452. * encoded - {string} An encoded string.
  453. *
  454. * Returns:
  455. * {number} The decoded unsigned integer.
  456. */
  457. decodeUnsignedInteger: function(encoded) {
  458. var result = 0;
  459. var shift = 0;
  460. var encodedLength = encoded.length;
  461. for (var i = 0; i < encodedLength; ++i) {
  462. var b = encoded.charCodeAt(i) - 63;
  463. result |= (b & 0x1f) << shift;
  464. if (b < 0x20)
  465. break;
  466. shift += 5;
  467. }
  468. return result;
  469. },
  470. CLASS_NAME: "OpenLayers.Format.EncodedPolyline"
  471. });