index.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import calcBbox from "@turf/bbox";
  2. import booleanPointOnLine from "@turf/boolean-point-on-line";
  3. import booleanPointInPolygon from "@turf/boolean-point-in-polygon";
  4. import { getGeom } from "@turf/invariant";
  5. /**
  6. * Boolean-within returns true if the first geometry is completely within the second geometry.
  7. * The interiors of both geometries must intersect and, the interior and boundary of the primary (geometry a)
  8. * must not intersect the exterior of the secondary (geometry b).
  9. * Boolean-within returns the exact opposite result of the `@turf/boolean-contains`.
  10. *
  11. * @name booleanWithin
  12. * @param {Geometry|Feature<any>} feature1 GeoJSON Feature or Geometry
  13. * @param {Geometry|Feature<any>} feature2 GeoJSON Feature or Geometry
  14. * @returns {boolean} true/false
  15. * @example
  16. * var line = turf.lineString([[1, 1], [1, 2], [1, 3], [1, 4]]);
  17. * var point = turf.point([1, 2]);
  18. *
  19. * turf.booleanWithin(point, line);
  20. * //=true
  21. */
  22. function booleanWithin(feature1, feature2) {
  23. var geom1 = getGeom(feature1);
  24. var geom2 = getGeom(feature2);
  25. var type1 = geom1.type;
  26. var type2 = geom2.type;
  27. switch (type1) {
  28. case "Point":
  29. switch (type2) {
  30. case "MultiPoint":
  31. return isPointInMultiPoint(geom1, geom2);
  32. case "LineString":
  33. return booleanPointOnLine(geom1, geom2, { ignoreEndVertices: true });
  34. case "Polygon":
  35. case "MultiPolygon":
  36. return booleanPointInPolygon(geom1, geom2, { ignoreBoundary: true });
  37. default:
  38. throw new Error("feature2 " + type2 + " geometry not supported");
  39. }
  40. case "MultiPoint":
  41. switch (type2) {
  42. case "MultiPoint":
  43. return isMultiPointInMultiPoint(geom1, geom2);
  44. case "LineString":
  45. return isMultiPointOnLine(geom1, geom2);
  46. case "Polygon":
  47. case "MultiPolygon":
  48. return isMultiPointInPoly(geom1, geom2);
  49. default:
  50. throw new Error("feature2 " + type2 + " geometry not supported");
  51. }
  52. case "LineString":
  53. switch (type2) {
  54. case "LineString":
  55. return isLineOnLine(geom1, geom2);
  56. case "Polygon":
  57. case "MultiPolygon":
  58. return isLineInPoly(geom1, geom2);
  59. default:
  60. throw new Error("feature2 " + type2 + " geometry not supported");
  61. }
  62. case "Polygon":
  63. switch (type2) {
  64. case "Polygon":
  65. case "MultiPolygon":
  66. return isPolyInPoly(geom1, geom2);
  67. default:
  68. throw new Error("feature2 " + type2 + " geometry not supported");
  69. }
  70. default:
  71. throw new Error("feature1 " + type1 + " geometry not supported");
  72. }
  73. }
  74. function isPointInMultiPoint(point, multiPoint) {
  75. var i;
  76. var output = false;
  77. for (i = 0; i < multiPoint.coordinates.length; i++) {
  78. if (compareCoords(multiPoint.coordinates[i], point.coordinates)) {
  79. output = true;
  80. break;
  81. }
  82. }
  83. return output;
  84. }
  85. function isMultiPointInMultiPoint(multiPoint1, multiPoint2) {
  86. for (var i = 0; i < multiPoint1.coordinates.length; i++) {
  87. var anyMatch = false;
  88. for (var i2 = 0; i2 < multiPoint2.coordinates.length; i2++) {
  89. if (compareCoords(multiPoint1.coordinates[i], multiPoint2.coordinates[i2])) {
  90. anyMatch = true;
  91. }
  92. }
  93. if (!anyMatch) {
  94. return false;
  95. }
  96. }
  97. return true;
  98. }
  99. function isMultiPointOnLine(multiPoint, lineString) {
  100. var foundInsidePoint = false;
  101. for (var i = 0; i < multiPoint.coordinates.length; i++) {
  102. if (!booleanPointOnLine(multiPoint.coordinates[i], lineString)) {
  103. return false;
  104. }
  105. if (!foundInsidePoint) {
  106. foundInsidePoint = booleanPointOnLine(multiPoint.coordinates[i], lineString, { ignoreEndVertices: true });
  107. }
  108. }
  109. return foundInsidePoint;
  110. }
  111. function isMultiPointInPoly(multiPoint, polygon) {
  112. var output = true;
  113. var oneInside = false;
  114. var isInside = false;
  115. for (var i = 0; i < multiPoint.coordinates.length; i++) {
  116. isInside = booleanPointInPolygon(multiPoint.coordinates[1], polygon);
  117. if (!isInside) {
  118. output = false;
  119. break;
  120. }
  121. if (!oneInside) {
  122. isInside = booleanPointInPolygon(multiPoint.coordinates[1], polygon, {
  123. ignoreBoundary: true,
  124. });
  125. }
  126. }
  127. return output && isInside;
  128. }
  129. function isLineOnLine(lineString1, lineString2) {
  130. for (var i = 0; i < lineString1.coordinates.length; i++) {
  131. if (!booleanPointOnLine(lineString1.coordinates[i], lineString2)) {
  132. return false;
  133. }
  134. }
  135. return true;
  136. }
  137. function isLineInPoly(linestring, polygon) {
  138. var polyBbox = calcBbox(polygon);
  139. var lineBbox = calcBbox(linestring);
  140. if (!doBBoxOverlap(polyBbox, lineBbox)) {
  141. return false;
  142. }
  143. var foundInsidePoint = false;
  144. for (var i = 0; i < linestring.coordinates.length - 1; i++) {
  145. if (!booleanPointInPolygon(linestring.coordinates[i], polygon)) {
  146. return false;
  147. }
  148. if (!foundInsidePoint) {
  149. foundInsidePoint = booleanPointInPolygon(linestring.coordinates[i], polygon, { ignoreBoundary: true });
  150. }
  151. if (!foundInsidePoint) {
  152. var midpoint = getMidpoint(linestring.coordinates[i], linestring.coordinates[i + 1]);
  153. foundInsidePoint = booleanPointInPolygon(midpoint, polygon, {
  154. ignoreBoundary: true,
  155. });
  156. }
  157. }
  158. return foundInsidePoint;
  159. }
  160. /**
  161. * Is Polygon2 in Polygon1
  162. * Only takes into account outer rings
  163. *
  164. * @private
  165. * @param {Polygon} geometry1
  166. * @param {Polygon|MultiPolygon} geometry2
  167. * @returns {boolean} true/false
  168. */
  169. function isPolyInPoly(geometry1, geometry2) {
  170. var poly1Bbox = calcBbox(geometry1);
  171. var poly2Bbox = calcBbox(geometry2);
  172. if (!doBBoxOverlap(poly2Bbox, poly1Bbox)) {
  173. return false;
  174. }
  175. for (var i = 0; i < geometry1.coordinates[0].length; i++) {
  176. if (!booleanPointInPolygon(geometry1.coordinates[0][i], geometry2)) {
  177. return false;
  178. }
  179. }
  180. return true;
  181. }
  182. function doBBoxOverlap(bbox1, bbox2) {
  183. if (bbox1[0] > bbox2[0])
  184. return false;
  185. if (bbox1[2] < bbox2[2])
  186. return false;
  187. if (bbox1[1] > bbox2[1])
  188. return false;
  189. if (bbox1[3] < bbox2[3])
  190. return false;
  191. return true;
  192. }
  193. /**
  194. * compareCoords
  195. *
  196. * @private
  197. * @param {Position} pair1 point [x,y]
  198. * @param {Position} pair2 point [x,y]
  199. * @returns {boolean} true/false if coord pairs match
  200. */
  201. function compareCoords(pair1, pair2) {
  202. return pair1[0] === pair2[0] && pair1[1] === pair2[1];
  203. }
  204. /**
  205. * getMidpoint
  206. *
  207. * @private
  208. * @param {Position} pair1 point [x,y]
  209. * @param {Position} pair2 point [x,y]
  210. * @returns {Position} midpoint of pair1 and pair2
  211. */
  212. function getMidpoint(pair1, pair2) {
  213. return [(pair1[0] + pair2[0]) / 2, (pair1[1] + pair2[1]) / 2];
  214. }
  215. export default booleanWithin;