index.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. var helpers_1 = require("@turf/helpers");
  4. var invariant_1 = require("@turf/invariant");
  5. // To-Do => Improve Typescript GeoJSON handling
  6. /**
  7. * Removes redundant coordinates from any GeoJSON Geometry.
  8. *
  9. * @name cleanCoords
  10. * @param {Geometry|Feature} geojson Feature or Geometry
  11. * @param {Object} [options={}] Optional parameters
  12. * @param {boolean} [options.mutate=false] allows GeoJSON input to be mutated
  13. * @returns {Geometry|Feature} the cleaned input Feature/Geometry
  14. * @example
  15. * var line = turf.lineString([[0, 0], [0, 2], [0, 5], [0, 8], [0, 8], [0, 10]]);
  16. * var multiPoint = turf.multiPoint([[0, 0], [0, 0], [2, 2]]);
  17. *
  18. * turf.cleanCoords(line).geometry.coordinates;
  19. * //= [[0, 0], [0, 10]]
  20. *
  21. * turf.cleanCoords(multiPoint).geometry.coordinates;
  22. * //= [[0, 0], [2, 2]]
  23. */
  24. function cleanCoords(geojson, options) {
  25. if (options === void 0) { options = {}; }
  26. // Backwards compatible with v4.0
  27. var mutate = typeof options === "object" ? options.mutate : options;
  28. if (!geojson)
  29. throw new Error("geojson is required");
  30. var type = invariant_1.getType(geojson);
  31. // Store new "clean" points in this Array
  32. var newCoords = [];
  33. switch (type) {
  34. case "LineString":
  35. newCoords = cleanLine(geojson);
  36. break;
  37. case "MultiLineString":
  38. case "Polygon":
  39. invariant_1.getCoords(geojson).forEach(function (line) {
  40. newCoords.push(cleanLine(line));
  41. });
  42. break;
  43. case "MultiPolygon":
  44. invariant_1.getCoords(geojson).forEach(function (polygons) {
  45. var polyPoints = [];
  46. polygons.forEach(function (ring) {
  47. polyPoints.push(cleanLine(ring));
  48. });
  49. newCoords.push(polyPoints);
  50. });
  51. break;
  52. case "Point":
  53. return geojson;
  54. case "MultiPoint":
  55. var existing = {};
  56. invariant_1.getCoords(geojson).forEach(function (coord) {
  57. var key = coord.join("-");
  58. if (!Object.prototype.hasOwnProperty.call(existing, key)) {
  59. newCoords.push(coord);
  60. existing[key] = true;
  61. }
  62. });
  63. break;
  64. default:
  65. throw new Error(type + " geometry not supported");
  66. }
  67. // Support input mutation
  68. if (geojson.coordinates) {
  69. if (mutate === true) {
  70. geojson.coordinates = newCoords;
  71. return geojson;
  72. }
  73. return { type: type, coordinates: newCoords };
  74. }
  75. else {
  76. if (mutate === true) {
  77. geojson.geometry.coordinates = newCoords;
  78. return geojson;
  79. }
  80. return helpers_1.feature({ type: type, coordinates: newCoords }, geojson.properties, {
  81. bbox: geojson.bbox,
  82. id: geojson.id,
  83. });
  84. }
  85. }
  86. /**
  87. * Clean Coords
  88. *
  89. * @private
  90. * @param {Array<number>|LineString} line Line
  91. * @returns {Array<number>} Cleaned coordinates
  92. */
  93. function cleanLine(line) {
  94. var points = invariant_1.getCoords(line);
  95. // handle "clean" segment
  96. if (points.length === 2 && !equals(points[0], points[1]))
  97. return points;
  98. var newPoints = [];
  99. var secondToLast = points.length - 1;
  100. var newPointsLength = newPoints.length;
  101. newPoints.push(points[0]);
  102. for (var i = 1; i < secondToLast; i++) {
  103. var prevAddedPoint = newPoints[newPoints.length - 1];
  104. if (points[i][0] === prevAddedPoint[0] &&
  105. points[i][1] === prevAddedPoint[1])
  106. continue;
  107. else {
  108. newPoints.push(points[i]);
  109. newPointsLength = newPoints.length;
  110. if (newPointsLength > 2) {
  111. if (isPointOnLineSegment(newPoints[newPointsLength - 3], newPoints[newPointsLength - 1], newPoints[newPointsLength - 2]))
  112. newPoints.splice(newPoints.length - 2, 1);
  113. }
  114. }
  115. }
  116. newPoints.push(points[points.length - 1]);
  117. newPointsLength = newPoints.length;
  118. if (equals(points[0], points[points.length - 1]) && newPointsLength < 4)
  119. throw new Error("invalid polygon");
  120. if (isPointOnLineSegment(newPoints[newPointsLength - 3], newPoints[newPointsLength - 1], newPoints[newPointsLength - 2]))
  121. newPoints.splice(newPoints.length - 2, 1);
  122. return newPoints;
  123. }
  124. /**
  125. * Compares two points and returns if they are equals
  126. *
  127. * @private
  128. * @param {Position} pt1 point
  129. * @param {Position} pt2 point
  130. * @returns {boolean} true if they are equals
  131. */
  132. function equals(pt1, pt2) {
  133. return pt1[0] === pt2[0] && pt1[1] === pt2[1];
  134. }
  135. /**
  136. * Returns if `point` is on the segment between `start` and `end`.
  137. * Borrowed from `@turf/boolean-point-on-line` to speed up the evaluation (instead of using the module as dependency)
  138. *
  139. * @private
  140. * @param {Position} start coord pair of start of line
  141. * @param {Position} end coord pair of end of line
  142. * @param {Position} point coord pair of point to check
  143. * @returns {boolean} true/false
  144. */
  145. function isPointOnLineSegment(start, end, point) {
  146. var x = point[0], y = point[1];
  147. var startX = start[0], startY = start[1];
  148. var endX = end[0], endY = end[1];
  149. var dxc = x - startX;
  150. var dyc = y - startY;
  151. var dxl = endX - startX;
  152. var dyl = endY - startY;
  153. var cross = dxc * dyl - dyc * dxl;
  154. if (cross !== 0)
  155. return false;
  156. else if (Math.abs(dxl) >= Math.abs(dyl))
  157. return dxl > 0 ? startX <= x && x <= endX : endX <= x && x <= startX;
  158. else
  159. return dyl > 0 ? startY <= y && y <= endY : endY <= y && y <= startY;
  160. }
  161. exports.default = cleanCoords;