index.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import rbush from "geojson-rbush";
  2. import lineSegment from "@turf/line-segment";
  3. import nearestPointOnLine from "@turf/nearest-point-on-line";
  4. import booleanPointOnLine from "@turf/boolean-point-on-line";
  5. import { getCoords } from "@turf/invariant";
  6. import { featureEach, segmentEach } from "@turf/meta";
  7. import { featureCollection, isObject, } from "@turf/helpers";
  8. import equal from "deep-equal";
  9. /**
  10. * Takes any LineString or Polygon and returns the overlapping lines between both features.
  11. *
  12. * @name lineOverlap
  13. * @param {Geometry|Feature<LineString|MultiLineString|Polygon|MultiPolygon>} line1 any LineString or Polygon
  14. * @param {Geometry|Feature<LineString|MultiLineString|Polygon|MultiPolygon>} line2 any LineString or Polygon
  15. * @param {Object} [options={}] Optional parameters
  16. * @param {number} [options.tolerance=0] Tolerance distance to match overlapping line segments (in kilometers)
  17. * @returns {FeatureCollection<LineString>} lines(s) that are overlapping between both features
  18. * @example
  19. * var line1 = turf.lineString([[115, -35], [125, -30], [135, -30], [145, -35]]);
  20. * var line2 = turf.lineString([[115, -25], [125, -30], [135, -30], [145, -25]]);
  21. *
  22. * var overlapping = turf.lineOverlap(line1, line2);
  23. *
  24. * //addToMap
  25. * var addToMap = [line1, line2, overlapping]
  26. */
  27. function lineOverlap(line1, line2, options) {
  28. if (options === void 0) { options = {}; }
  29. // Optional parameters
  30. options = options || {};
  31. if (!isObject(options))
  32. throw new Error("options is invalid");
  33. var tolerance = options.tolerance || 0;
  34. // Containers
  35. var features = [];
  36. // Create Spatial Index
  37. var tree = rbush();
  38. // To-Do -- HACK way to support typescript
  39. var line = lineSegment(line1);
  40. tree.load(line);
  41. var overlapSegment;
  42. // Line Intersection
  43. // Iterate over line segments
  44. segmentEach(line2, function (segment) {
  45. var doesOverlaps = false;
  46. if (!segment) {
  47. return;
  48. }
  49. // Iterate over each segments which falls within the same bounds
  50. featureEach(tree.search(segment), function (match) {
  51. if (doesOverlaps === false) {
  52. var coordsSegment = getCoords(segment).sort();
  53. var coordsMatch = getCoords(match).sort();
  54. // Segment overlaps feature
  55. if (equal(coordsSegment, coordsMatch)) {
  56. doesOverlaps = true;
  57. // Overlaps already exists - only append last coordinate of segment
  58. if (overlapSegment)
  59. overlapSegment = concatSegment(overlapSegment, segment);
  60. else
  61. overlapSegment = segment;
  62. // Match segments which don't share nodes (Issue #901)
  63. }
  64. else if (tolerance === 0
  65. ? booleanPointOnLine(coordsSegment[0], match) &&
  66. booleanPointOnLine(coordsSegment[1], match)
  67. : nearestPointOnLine(match, coordsSegment[0]).properties.dist <=
  68. tolerance &&
  69. nearestPointOnLine(match, coordsSegment[1]).properties.dist <=
  70. tolerance) {
  71. doesOverlaps = true;
  72. if (overlapSegment)
  73. overlapSegment = concatSegment(overlapSegment, segment);
  74. else
  75. overlapSegment = segment;
  76. }
  77. else if (tolerance === 0
  78. ? booleanPointOnLine(coordsMatch[0], segment) &&
  79. booleanPointOnLine(coordsMatch[1], segment)
  80. : nearestPointOnLine(segment, coordsMatch[0]).properties.dist <=
  81. tolerance &&
  82. nearestPointOnLine(segment, coordsMatch[1]).properties.dist <=
  83. tolerance) {
  84. // Do not define (doesOverlap = true) since more matches can occur within the same segment
  85. // doesOverlaps = true;
  86. if (overlapSegment)
  87. overlapSegment = concatSegment(overlapSegment, match);
  88. else
  89. overlapSegment = match;
  90. }
  91. }
  92. });
  93. // Segment doesn't overlap - add overlaps to results & reset
  94. if (doesOverlaps === false && overlapSegment) {
  95. features.push(overlapSegment);
  96. overlapSegment = undefined;
  97. }
  98. });
  99. // Add last segment if exists
  100. if (overlapSegment)
  101. features.push(overlapSegment);
  102. return featureCollection(features);
  103. }
  104. /**
  105. * Concat Segment
  106. *
  107. * @private
  108. * @param {Feature<LineString>} line LineString
  109. * @param {Feature<LineString>} segment 2-vertex LineString
  110. * @returns {Feature<LineString>} concat linestring
  111. */
  112. function concatSegment(line, segment) {
  113. var coords = getCoords(segment);
  114. var lineCoords = getCoords(line);
  115. var start = lineCoords[0];
  116. var end = lineCoords[lineCoords.length - 1];
  117. var geom = line.geometry.coordinates;
  118. if (equal(coords[0], start))
  119. geom.unshift(coords[1]);
  120. else if (equal(coords[0], end))
  121. geom.push(coords[1]);
  122. else if (equal(coords[1], start))
  123. geom.unshift(coords[0]);
  124. else if (equal(coords[1], end))
  125. geom.push(coords[0]);
  126. return line;
  127. }
  128. export default lineOverlap;