index.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import lineIntersect from "@turf/line-intersect";
  2. import { polygonToLine } from "@turf/polygon-to-line";
  3. import booleanPointInPolygon from "@turf/boolean-point-in-polygon";
  4. import { getGeom } from "@turf/invariant";
  5. import { point, } from "@turf/helpers";
  6. /**
  7. * Boolean-Crosses returns True if the intersection results in a geometry whose dimension is one less than
  8. * the maximum dimension of the two source geometries and the intersection set is interior to
  9. * both source geometries.
  10. *
  11. * Boolean-Crosses returns t (TRUE) for only multipoint/polygon, multipoint/linestring, linestring/linestring, linestring/polygon, and linestring/multipolygon comparisons.
  12. *
  13. * @name booleanCrosses
  14. * @param {Geometry|Feature<any>} feature1 GeoJSON Feature or Geometry
  15. * @param {Geometry|Feature<any>} feature2 GeoJSON Feature or Geometry
  16. * @returns {boolean} true/false
  17. * @example
  18. * var line1 = turf.lineString([[-2, 2], [4, 2]]);
  19. * var line2 = turf.lineString([[1, 1], [1, 2], [1, 3], [1, 4]]);
  20. *
  21. * var cross = turf.booleanCrosses(line1, line2);
  22. * //=true
  23. */
  24. function booleanCrosses(feature1, feature2) {
  25. var geom1 = getGeom(feature1);
  26. var geom2 = getGeom(feature2);
  27. var type1 = geom1.type;
  28. var type2 = geom2.type;
  29. switch (type1) {
  30. case "MultiPoint":
  31. switch (type2) {
  32. case "LineString":
  33. return doMultiPointAndLineStringCross(geom1, geom2);
  34. case "Polygon":
  35. return doesMultiPointCrossPoly(geom1, geom2);
  36. default:
  37. throw new Error("feature2 " + type2 + " geometry not supported");
  38. }
  39. case "LineString":
  40. switch (type2) {
  41. case "MultiPoint": // An inverse operation
  42. return doMultiPointAndLineStringCross(geom2, geom1);
  43. case "LineString":
  44. return doLineStringsCross(geom1, geom2);
  45. case "Polygon":
  46. return doLineStringAndPolygonCross(geom1, geom2);
  47. default:
  48. throw new Error("feature2 " + type2 + " geometry not supported");
  49. }
  50. case "Polygon":
  51. switch (type2) {
  52. case "MultiPoint": // An inverse operation
  53. return doesMultiPointCrossPoly(geom2, geom1);
  54. case "LineString": // An inverse operation
  55. return doLineStringAndPolygonCross(geom2, geom1);
  56. default:
  57. throw new Error("feature2 " + type2 + " geometry not supported");
  58. }
  59. default:
  60. throw new Error("feature1 " + type1 + " geometry not supported");
  61. }
  62. }
  63. function doMultiPointAndLineStringCross(multiPoint, lineString) {
  64. var foundIntPoint = false;
  65. var foundExtPoint = false;
  66. var pointLength = multiPoint.coordinates.length;
  67. var i = 0;
  68. while (i < pointLength && !foundIntPoint && !foundExtPoint) {
  69. for (var i2 = 0; i2 < lineString.coordinates.length - 1; i2++) {
  70. var incEndVertices = true;
  71. if (i2 === 0 || i2 === lineString.coordinates.length - 2) {
  72. incEndVertices = false;
  73. }
  74. if (isPointOnLineSegment(lineString.coordinates[i2], lineString.coordinates[i2 + 1], multiPoint.coordinates[i], incEndVertices)) {
  75. foundIntPoint = true;
  76. }
  77. else {
  78. foundExtPoint = true;
  79. }
  80. }
  81. i++;
  82. }
  83. return foundIntPoint && foundExtPoint;
  84. }
  85. function doLineStringsCross(lineString1, lineString2) {
  86. var doLinesIntersect = lineIntersect(lineString1, lineString2);
  87. if (doLinesIntersect.features.length > 0) {
  88. for (var i = 0; i < lineString1.coordinates.length - 1; i++) {
  89. for (var i2 = 0; i2 < lineString2.coordinates.length - 1; i2++) {
  90. var incEndVertices = true;
  91. if (i2 === 0 || i2 === lineString2.coordinates.length - 2) {
  92. incEndVertices = false;
  93. }
  94. if (isPointOnLineSegment(lineString1.coordinates[i], lineString1.coordinates[i + 1], lineString2.coordinates[i2], incEndVertices)) {
  95. return true;
  96. }
  97. }
  98. }
  99. }
  100. return false;
  101. }
  102. function doLineStringAndPolygonCross(lineString, polygon) {
  103. var line = polygonToLine(polygon);
  104. var doLinesIntersect = lineIntersect(lineString, line);
  105. if (doLinesIntersect.features.length > 0) {
  106. return true;
  107. }
  108. return false;
  109. }
  110. function doesMultiPointCrossPoly(multiPoint, polygon) {
  111. var foundIntPoint = false;
  112. var foundExtPoint = false;
  113. var pointLength = multiPoint.coordinates.length;
  114. for (var i = 0; i < pointLength && (!foundIntPoint || !foundExtPoint); i++) {
  115. if (booleanPointInPolygon(point(multiPoint.coordinates[i]), polygon)) {
  116. foundIntPoint = true;
  117. }
  118. else {
  119. foundExtPoint = true;
  120. }
  121. }
  122. return foundExtPoint && foundIntPoint;
  123. }
  124. /**
  125. * Is a point on a line segment
  126. * Only takes into account outer rings
  127. * See http://stackoverflow.com/a/4833823/1979085
  128. *
  129. * @private
  130. * @param {number[]} lineSegmentStart coord pair of start of line
  131. * @param {number[]} lineSegmentEnd coord pair of end of line
  132. * @param {number[]} pt coord pair of point to check
  133. * @param {boolean} incEnd whether the point is allowed to fall on the line ends
  134. * @returns {boolean} true/false
  135. */
  136. function isPointOnLineSegment(lineSegmentStart, lineSegmentEnd, pt, incEnd) {
  137. var dxc = pt[0] - lineSegmentStart[0];
  138. var dyc = pt[1] - lineSegmentStart[1];
  139. var dxl = lineSegmentEnd[0] - lineSegmentStart[0];
  140. var dyl = lineSegmentEnd[1] - lineSegmentStart[1];
  141. var cross = dxc * dyl - dyc * dxl;
  142. if (cross !== 0) {
  143. return false;
  144. }
  145. if (incEnd) {
  146. if (Math.abs(dxl) >= Math.abs(dyl)) {
  147. return dxl > 0
  148. ? lineSegmentStart[0] <= pt[0] && pt[0] <= lineSegmentEnd[0]
  149. : lineSegmentEnd[0] <= pt[0] && pt[0] <= lineSegmentStart[0];
  150. }
  151. return dyl > 0
  152. ? lineSegmentStart[1] <= pt[1] && pt[1] <= lineSegmentEnd[1]
  153. : lineSegmentEnd[1] <= pt[1] && pt[1] <= lineSegmentStart[1];
  154. }
  155. else {
  156. if (Math.abs(dxl) >= Math.abs(dyl)) {
  157. return dxl > 0
  158. ? lineSegmentStart[0] < pt[0] && pt[0] < lineSegmentEnd[0]
  159. : lineSegmentEnd[0] < pt[0] && pt[0] < lineSegmentStart[0];
  160. }
  161. return dyl > 0
  162. ? lineSegmentStart[1] < pt[1] && pt[1] < lineSegmentEnd[1]
  163. : lineSegmentEnd[1] < pt[1] && pt[1] < lineSegmentStart[1];
  164. }
  165. }
  166. export default booleanCrosses;