turf-line-dissolve.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import clone from "@turf/clone";
  2. import { isObject, lineString, multiLineString } from "@turf/helpers";
  3. import { getType } from "@turf/invariant";
  4. import { lineReduce } from "@turf/meta";
  5. /**
  6. * Merges all connected (non-forking, non-junctioning) line strings into single lineStrings.
  7. * [LineString] -> LineString|MultiLineString
  8. *
  9. * @param {FeatureCollection<LineString|MultiLineString>} geojson Lines to dissolve
  10. * @param {Object} [options={}] Optional parameters
  11. * @param {boolean} [options.mutate=false] Prevent input mutation
  12. * @returns {Feature<LineString|MultiLineString>} Dissolved lines
  13. */
  14. function lineDissolve(geojson, options) {
  15. if (options === void 0) { options = {}; }
  16. // Optional parameters
  17. options = options || {};
  18. if (!isObject(options)) {
  19. throw new Error("options is invalid");
  20. }
  21. var mutate = options.mutate;
  22. // Validation
  23. if (getType(geojson) !== "FeatureCollection") {
  24. throw new Error("geojson must be a FeatureCollection");
  25. }
  26. if (!geojson.features.length) {
  27. throw new Error("geojson is empty");
  28. }
  29. // Clone geojson to avoid side effects
  30. if (mutate === false || mutate === undefined) {
  31. geojson = clone(geojson);
  32. }
  33. var result = [];
  34. var lastLine = lineReduce(geojson, function (previousLine, currentLine) {
  35. // Attempt to merge this LineString with the other LineStrings, updating
  36. // the reference as it is merged with others and grows.
  37. var merged = mergeLineStrings(previousLine, currentLine);
  38. // Accumulate the merged LineString
  39. if (merged) {
  40. return merged;
  41. // Put the unmerged LineString back into the list
  42. }
  43. else {
  44. result.push(previousLine);
  45. return currentLine;
  46. }
  47. });
  48. // Append the last line
  49. if (lastLine) {
  50. result.push(lastLine);
  51. }
  52. // Return null if no lines were dissolved
  53. if (!result.length) {
  54. return null;
  55. // Return LineString if only 1 line was dissolved
  56. }
  57. else if (result.length === 1) {
  58. return result[0];
  59. // Return MultiLineString if multiple lines were dissolved with gaps
  60. }
  61. else {
  62. return multiLineString(result.map(function (line) {
  63. return line.coordinates;
  64. }));
  65. }
  66. }
  67. // [Number, Number] -> String
  68. function coordId(coord) {
  69. return coord[0].toString() + "," + coord[1].toString();
  70. }
  71. /**
  72. * LineString, LineString -> LineString
  73. *
  74. * @private
  75. * @param {Feature<LineString>} a line1
  76. * @param {Feature<LineString>} b line2
  77. * @returns {Feature<LineString>|null} Merged LineString
  78. */
  79. function mergeLineStrings(a, b) {
  80. var coords1 = a.geometry.coordinates;
  81. var coords2 = b.geometry.coordinates;
  82. var s1 = coordId(coords1[0]);
  83. var e1 = coordId(coords1[coords1.length - 1]);
  84. var s2 = coordId(coords2[0]);
  85. var e2 = coordId(coords2[coords2.length - 1]);
  86. // TODO: handle case where more than one of these is true!
  87. var coords;
  88. if (s1 === e2) {
  89. coords = coords2.concat(coords1.slice(1));
  90. }
  91. else if (s2 === e1) {
  92. coords = coords1.concat(coords2.slice(1));
  93. }
  94. else if (s1 === s2) {
  95. coords = coords1.slice(1).reverse().concat(coords2);
  96. }
  97. else if (e1 === e2) {
  98. coords = coords1.concat(coords2.reverse().slice(1));
  99. }
  100. else {
  101. return null;
  102. }
  103. return lineString(coords);
  104. }
  105. export default lineDissolve;