From 1b84dfc61c1ab3be76d28c6aa547aa4993057bb0 Mon Sep 17 00:00:00 2001 From: samuelarbibe Date: Thu, 27 Feb 2025 11:41:11 +0200 Subject: [PATCH 1/7] fix boolean-contains and boolean-within isLineInPoly --- packages/turf-boolean-contains/index.ts | 50 +++++++++++------ packages/turf-boolean-contains/package.json | 1 + .../LineIsNotFullyContainedByPolygon.geojson | 35 ++++++++++++ packages/turf-boolean-within/index.ts | 54 ++++++++++++------- packages/turf-boolean-within/package.json | 1 + .../LineIsNotFullyWithinPolygon.geojson | 35 ++++++++++++ pnpm-lock.yaml | 6 +++ 7 files changed, 147 insertions(+), 35 deletions(-) create mode 100644 packages/turf-boolean-contains/test/false/LineString/Polygon/LineIsNotFullyContainedByPolygon.geojson create mode 100644 packages/turf-boolean-within/test/false/LineString/Polygon/LineIsNotFullyWithinPolygon.geojson diff --git a/packages/turf-boolean-contains/index.ts b/packages/turf-boolean-contains/index.ts index 98e390331c..9e3ae225a6 100644 --- a/packages/turf-boolean-contains/index.ts +++ b/packages/turf-boolean-contains/index.ts @@ -12,6 +12,8 @@ import { bbox as calcBbox } from "@turf/bbox"; import { booleanPointInPolygon } from "@turf/boolean-point-in-polygon"; import { booleanPointOnLine as isPointOnLine } from "@turf/boolean-point-on-line"; import { getGeom } from "@turf/invariant"; +import { feature } from "@turf/helpers"; +import { lineSplit } from "@turf/line-split"; /** * Boolean-contains returns True if the second geometry is completely contained by the first geometry. @@ -178,29 +180,47 @@ function isLineOnLine(lineString1: LineString, lineString2: LineString) { } function isLineInPoly(polygon: Polygon, linestring: LineString) { - let output = false; - let i = 0; - const polyBbox = calcBbox(polygon); const lineBbox = calcBbox(linestring); + if (!doBBoxOverlap(polyBbox, lineBbox)) { return false; } - for (i; i < linestring.coordinates.length - 1; i++) { - const midPoint = getMidpoint( - linestring.coordinates[i], - linestring.coordinates[i + 1] + + let isContainedByPolygonBoundary = false; + + for (let i = 0; i < linestring.coordinates.length - 1; i++) { + const coord1 = linestring.coordinates[i]; + + if (!booleanPointInPolygon(coord1, polygon)) { + return false; + } + + if (isContainedByPolygonBoundary) continue; + + const coord2 = linestring.coordinates[i + 1]; + const midpoint = getMidpoint(coord1, coord2); + + if (booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true })) { + isContainedByPolygonBoundary = true; + } + } + + if (!isContainedByPolygonBoundary) return false; + + const lineSegments = lineSplit(feature(linestring), feature(polygon)); + + for (const lineSegment of lineSegments.features) { + const midpoint = getMidpoint( + lineSegment.geometry.coordinates[0], + lineSegment.geometry.coordinates[1] ); - if ( - booleanPointInPolygon({ type: "Point", coordinates: midPoint }, polygon, { - ignoreBoundary: true, - }) - ) { - output = true; - break; + if (!booleanPointInPolygon(midpoint, polygon)) { + return false; } } - return output; + + return true; } /** diff --git a/packages/turf-boolean-contains/package.json b/packages/turf-boolean-contains/package.json index d034e1219c..5e714fb785 100644 --- a/packages/turf-boolean-contains/package.json +++ b/packages/turf-boolean-contains/package.json @@ -74,6 +74,7 @@ "@turf/boolean-point-on-line": "workspace:^", "@turf/helpers": "workspace:^", "@turf/invariant": "workspace:^", + "@turf/line-split": "workspace:^", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } diff --git a/packages/turf-boolean-contains/test/false/LineString/Polygon/LineIsNotFullyContainedByPolygon.geojson b/packages/turf-boolean-contains/test/false/LineString/Polygon/LineIsNotFullyContainedByPolygon.geojson new file mode 100644 index 0000000000..077cd41ff3 --- /dev/null +++ b/packages/turf-boolean-contains/test/false/LineString/Polygon/LineIsNotFullyContainedByPolygon.geojson @@ -0,0 +1,35 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [9.6459207, -1.2281524], + [11.0822393, -1.2461022], + [11.405411, -0.1869151], + [11.1181473, 1.9133448], + [10.0588623, 1.4646914], + [10.6692977, -0.0253296], + [10.6692977, -0.0253296], + [9.6459207, -1.2281524] + ] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [10.2166256, -0.7379163], + [11.0362904, 1.794324] + ] + } + } + ] +} diff --git a/packages/turf-boolean-within/index.ts b/packages/turf-boolean-within/index.ts index db0bf0b0a4..d0f12142b8 100644 --- a/packages/turf-boolean-within/index.ts +++ b/packages/turf-boolean-within/index.ts @@ -12,6 +12,8 @@ import { bbox as calcBbox } from "@turf/bbox"; import { booleanPointOnLine } from "@turf/boolean-point-on-line"; import { booleanPointInPolygon } from "@turf/boolean-point-in-polygon"; import { getGeom } from "@turf/invariant"; +import { feature } from "@turf/helpers"; +import { lineSplit } from "@turf/line-split"; /** * Boolean-within returns true if the first geometry is completely within the second geometry. @@ -166,35 +168,47 @@ function isLineOnLine(lineString1: LineString, lineString2: LineString) { } function isLineInPoly(linestring: LineString, polygon: Polygon) { - var polyBbox = calcBbox(polygon); - var lineBbox = calcBbox(linestring); + const polyBbox = calcBbox(polygon); + const lineBbox = calcBbox(linestring); + if (!doBBoxOverlap(polyBbox, lineBbox)) { return false; } - var foundInsidePoint = false; - for (var i = 0; i < linestring.coordinates.length; i++) { - if (!booleanPointInPolygon(linestring.coordinates[i], polygon)) { + let isContainedByPolygonBoundary = false; + + for (let i = 0; i < linestring.coordinates.length - 1; i++) { + const coord1 = linestring.coordinates[i]; + + if (!booleanPointInPolygon(coord1, polygon)) { return false; } - if (!foundInsidePoint) { - foundInsidePoint = booleanPointInPolygon( - linestring.coordinates[i], - polygon, - { ignoreBoundary: true } - ); + + if (isContainedByPolygonBoundary) continue; + + const coord2 = linestring.coordinates[i + 1]; + const midpoint = getMidpoint(coord1, coord2); + + if (booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true })) { + isContainedByPolygonBoundary = true; } - if (!foundInsidePoint && i < linestring.coordinates.length - 1) { - var midpoint = getMidpoint( - linestring.coordinates[i], - linestring.coordinates[i + 1] - ); - foundInsidePoint = booleanPointInPolygon(midpoint, polygon, { - ignoreBoundary: true, - }); + } + + if (!isContainedByPolygonBoundary) return false; + + const lineSegments = lineSplit(feature(linestring), feature(polygon)); + + for (const lineSegment of lineSegments.features) { + const midpoint = getMidpoint( + lineSegment.geometry.coordinates[0], + lineSegment.geometry.coordinates[1] + ); + if (!booleanPointInPolygon(midpoint, polygon)) { + return false; } } - return foundInsidePoint; + + return true; } /** diff --git a/packages/turf-boolean-within/package.json b/packages/turf-boolean-within/package.json index 9934c07cb9..4953096026 100644 --- a/packages/turf-boolean-within/package.json +++ b/packages/turf-boolean-within/package.json @@ -76,6 +76,7 @@ "@turf/boolean-point-on-line": "workspace:^", "@turf/helpers": "workspace:^", "@turf/invariant": "workspace:^", + "@turf/line-split": "workspace:^", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } diff --git a/packages/turf-boolean-within/test/false/LineString/Polygon/LineIsNotFullyWithinPolygon.geojson b/packages/turf-boolean-within/test/false/LineString/Polygon/LineIsNotFullyWithinPolygon.geojson new file mode 100644 index 0000000000..d1390d66a4 --- /dev/null +++ b/packages/turf-boolean-within/test/false/LineString/Polygon/LineIsNotFullyWithinPolygon.geojson @@ -0,0 +1,35 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [10.2166256, -0.7379163], + [11.0362904, 1.794324] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [9.6459207, -1.2281524], + [11.0822393, -1.2461022], + [11.405411, -0.1869151], + [11.1181473, 1.9133448], + [10.0588623, 1.4646914], + [10.6692977, -0.0253296], + [10.6692977, -0.0253296], + [9.6459207, -1.2281524] + ] + ] + } + } + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b9674968f3..f675a98603 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -997,6 +997,9 @@ importers: '@turf/invariant': specifier: workspace:^ version: link:../turf-invariant + '@turf/line-split': + specifier: workspace:^ + version: link:../turf-line-split '@types/geojson': specifier: ^7946.0.10 version: 7946.0.14 @@ -1614,6 +1617,9 @@ importers: '@turf/invariant': specifier: workspace:^ version: link:../turf-invariant + '@turf/line-split': + specifier: workspace:^ + version: link:../turf-line-split '@types/geojson': specifier: ^7946.0.10 version: 7946.0.14 From ca461e54642dd553ffa3682fe027e94f86382e05 Mon Sep 17 00:00:00 2001 From: samuelarbibe Date: Thu, 27 Feb 2025 11:42:49 +0200 Subject: [PATCH 2/7] update esri-contains and esri-within diagrams --- packages/turf-boolean-contains/README.md | 2 +- .../diagrams/esri-contains.gif | Bin 34103 -> 0 bytes .../diagrams/esri-contains.png | Bin 0 -> 6473 bytes packages/turf-boolean-within/README.md | 5 +++++ .../diagrams/esri-within.png | Bin 0 -> 6338 bytes .../turf-boolean-within/digrams/esri-within.gif | Bin 32889 -> 0 bytes 6 files changed, 6 insertions(+), 1 deletion(-) delete mode 100644 packages/turf-boolean-contains/diagrams/esri-contains.gif create mode 100644 packages/turf-boolean-contains/diagrams/esri-contains.png create mode 100644 packages/turf-boolean-within/diagrams/esri-within.png delete mode 100644 packages/turf-boolean-within/digrams/esri-within.gif diff --git a/packages/turf-boolean-contains/README.md b/packages/turf-boolean-contains/README.md index cffaf92905..807c150d06 100644 --- a/packages/turf-boolean-contains/README.md +++ b/packages/turf-boolean-contains/README.md @@ -55,4 +55,4 @@ $ npm install @turf/turf ### Diagrams -![esri-contains](diagrams/esri-contains.gif) \ No newline at end of file +![esri-contains](diagrams/esri-contains.png) \ No newline at end of file diff --git a/packages/turf-boolean-contains/diagrams/esri-contains.gif b/packages/turf-boolean-contains/diagrams/esri-contains.gif deleted file mode 100644 index 6b219104cf3dc29ba48b6f73cd4119b592d92100..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34103 zcmYJ4S5y<<+qS2q5g>#fdZr0Tng$B5F`TM9PF3dgx6wR6#(46lrQG z(nJLtAeLa^2Z9c<;&;KpHH=?zkT~wS68<+ z449protc?=^5hAbOip$MN=r*0J$fW~Gx;VH`dX|1up#to0o2`)`>`uvg$>_{1{#ZF zmgfc*i)4S5fOkuFeXo?&lLwyGxPKT(({TJKYy;R zt^N4%JK6`TS!Gi}A6BCy&U;e)OrMI`Yt*x!8sp;d#m71FB zyEiX?`64(M9`xeHi}f;K<62)|SN)0l-xHw!fA@_!0kG>xLSj;KN@^M_JtH$KJ13W& zmtRm=R9sS8R$g(T^5P{T#w$(DEmyB`Td%jZcXZz9>h9_7yLsz2uYX{0 z=+5xS=-Bw(iF@}SOin#~^!Uls>6zJQbI)JAe8r!C{pRhvh5r_p-Y>6w`1nci`ODYU zZ{L5c{rvU&&)7Wj&LpM9@Ayq!N%>MF_P zE)IxoU$n8qm^O5Uh;+q&NE;S;lyz;p@-3mjvhfn*Q-MqRADwXuZnZN4zBYKJU)I#Y zFX+Z1L3z8N<;aKsuNW34Otgrp4hl3 zpMGjmT%#i^6g7#+O6oT7P)DtQnWvf%TV$9y%k$Jk$blcJ_Qb28?a7lPJ#oqWq^7~wbp7jkfuX$2fsZ7fNc64QvFkE#t;zxU z>iKIn;ZDT57z-e#xjhA8fw$f(vEQzD4QGX8K&ewzw!#Jt+&f2vXZSDsQ?i)D_}(X+ z&ArR1lPO1(6lvxC6S&<}9`s+&@oN|Kj|V=8e@o{*(3Z3h#786o4f0h+i8^g@3zIJg zCGz{99ntmXT)84^AMS~w(1CVUeD%!V+wq{`tbjE~YC*UqCqqIs=} z&>Owfb5Uk}zXZs!b619dzk%kKCa1ztAsO;cRO6D$eSv2`1AlVo!^hQv&+cgu^`(0m1#V;x(`E zJ1;|zTJ;X-a_>UQd^xkI?JLGud`U)>0KC4tNh-ci^Kt6KptBvx9>l|Lc zD+z8_Q+=}E`e-;;Tx_gREo~`HluS5<`T1JoF9+W9j8G6z#RS)=wjagSPN;eL`Baey{?bb*Xwv9-XH#6@5*70cXK#Z@Vx{Z~2)3!cZT@^fOr+YIe8*X0ipLg;K%FeM za3(&U4_0IyGkF5Y_qrP;sPY0&iO`c+F=vP&>NeuLAA=a)Ly>xSo?_5h^qy2+N=z(o z_ld7}NY**~BpYBTMq?`C;~4l_zu{_DS+jyPxgkQ{2D3=pw=e#&$|e72(l2H=KhNd^ zTI>4<_pbx{h&VkgQTsoOyA5e_&sQ^+ejgJ3WdYXT`b?DH&l7>j2PDdP1ksri$gQyM ztxy{Ww?w;$j!PnoKM z+Zjnym74gFjKs4t^t7PuZpEQ|K;<>Dc{+>?6Iph-oCPlrekxdxZjMQfF>}zr%S~PZ zZ~_6%Dt);W_*pF~FunP{#R;q7$203~WR*Xa-r0Od*JP{&Bf>*Kn-5e_N_fYqL`N~T zE)a^MJbY;m+{oN3aV!p`%)frmIeX=D&1kZ>dK4Lw4?g)W`nR=-BOl#iAiOR~eVQ2m z%Lj^W_TH4e+%JtI7girUrFhcJ>&S$Qm9*98h`AgxDAvG9#ApKi$W_C8SZJ$O`Uf%#4VzW$UFdRj(|iWqR`BTFUgy3 zj_CAt8f<_?gRh;)mtDT?Gk=Td6;{)sbkCSW!eRtQr)WHHSZbL)oT7xK18Ep9PP&pK z6T9NQQlR8#CCFw1^0YK1glb9FMleRSG#E$5{JQh)Vu~Xmq_2Q6!*z?&86QLq$4A9& zV4-~PNrJw_-YYjV>$0ER*>=1fco*d>BsdPcF!8=Np^6yqI7G6dMaD;H)+37gMIT-PV` zkpWCJRY)I6e0Z3SmFG~auZ4A%EBsivI08OX;Q@FW0*VDAAOyhONtL(fQ~_)t5mw}Z z;OH(ukwHv)l@;{$S$d}PLk`B2KCE`Zu=QBcM50n59sAU+>X%#HRN2<=V$8-1vSpewFps2SLRmFeC_jqyiV<6h)y@-;DV zKl!Aiqvj}pB?3S4R3aPn#X%X*uCs?6ffH5Cqf_EWpH<4h^x3VVgHzJa`HJS;cyuya zh7X#Ouurw1q3MGcSg!K#u*GKnF%;0TPQ5NVqcJ!+yVd^l6szemjONYuGJUy6<7Duom zlaXc=|B*y-uH>;VUFf4<%^LG9Q^Xk)k3m1LfM%>IbS{6VoT$7ys2gJcVpl-#^!eGH z5_zjoIA7^72bLwGwojt3yg5N#D&%N|I-D!?pA~tR5s^+ zvWmB4jSwP$CM)R>LCoaYu_e9xp8@qESeYwZ+9=G|IJGZ1W`z~*fQ#U!!>j3)H>cP~ zrj^_Bl3wl>yDxq!#RirGF2%|jo_~0v&ptW3OxTZd=w73`I<>+BlNW!P^+X7c;$NEe z&k_Z&TOHHTc;Z&0O?@CFaV6Vjr`F$}@}iRL7iH^0>^q={rheN(z9rTuZKYAd`v<{#nbf8?!1kWfpG#gf zYKIw4Aquop>SJ~O7OLtzO>H`W1UIP+tgC6#PI}Qm602niW%UZJJ~SSvYYg{>yDldk zi2}jO{k~e_p2j>_|BA9UBfWRJJS@ZU@Y8^Q2d=99G+7KSybq@KO@Wf!dQnRQe_!am z4IECF-y0*Ws}7B4$cizp<(#$+D?ojV-qj@u-Z>rLiRgsVk1GA-hFXN#EKtfa9JAFA ztKz-9Xu!U7xN2u% zw0*6QulEGPIQE(1O<3t0!CUYhZxekncb&KCVVCMl;N_CJaM-a3wENyrt)_BDNB;Y~ zmVX`FG7&PCHdJ<}e+kW%Pg#d>G~V$}Az8qFm+`^sXwBtYZqts;vyiNfmo2oR8j@yx`KxasNVcY6!F zZVPD6RtJq4@aitCn4nJ#0DEKs8_PG+bfg3-n19+HN{-bz})EL%FS`rDDC9WDQ^;GQc1L6K7AjrdLdb z?%!z-KRX!q+@d||%GG0<+p{0G{&a(S1A1F&w@C>0=XJ0wvhO^mZ^d8siGOZ!fQ%LY z_VWR?7z9+Iu0wi^K|KmA8?1Tz+|2-KV8C$`G0XGgR*uvkvE6b*ufl;3bxI*^@J}PC zWFh7zFc8&iLHVPqY;P}JtNUa>O1>MgeA2rs%ldOTCfQ5UtP0rw`R3~e)6r+wo*jn5 zqpL6HRVT4)o&?K?P}cDERhN6_hadT(fnRf!A@xV7B7klk=NN401);}0<1ti;RQ zdH2G>L#87B_@MXCK~0XAwWh;)kIkPQu!~muXwH!@iKjkIO4Sribh)*xch&vKde9_t zn_>(5+}Sb{)cRuo!X5;SEqfWBFl^*H|xP%>WdLPT+s}mq~iciiF)u zp})dp71&%e{Jj*xZk^|w(0oS`rCkym=X_kN-}BM-{6|)z(DoBhz5Zhg#_AL%MvMz; zbAix%(A~Q7OBtqLk*U#VK#g~qZF+!896M+X8@GHziafc0uNT)20tKys^MKE{^&Mdz zcl`gJ!_cr~9^^%tZ9N19p3k5FT~dcPD{`Uh(?EL=R*oLr{Uhpo*yw#=TmE54oed5B ze5OUrzar~zvpkH(Tk2{)cvZi#?eEsC#W7ZRmidrD=XLjqWBw0kFCD>F8(?dWU?X_l zE_XxUeMvCn!QsFOQSu2K56-G@ZFr$*VQ@Zz5?oygE}jTp+XNVPmz+D1?;i+@ahkF9 zM}{l=H@=Fv6SDfijn41)wjX$*!Fcf?3J~R-fBZC-Z(w>Q598zrZK1@=|=K zJpVz6`4%w-PD|#%>S^9EXA})NGAyPp}I;cR)lBZ+EkXdb`;<+k7gARxD z7*@!V0w;32Ol61CyDi{iwC-YZPTT=q(?d3xu8+)h#^sW{%TIq~_bk50f@%XJVGtd- ze;7MX0I@{uiS%)NQJ5Mz62p!fKZ@*N?~E||v=v3F%*y!DN|f1f_m&YD=m;$BQ+LE80(`H0IQp?oA} z4U#6MSpRtm9Q(}pENxtcbzn-itxbjdq|gkd4zrXxPJY(9R*@Br$z2;8E%>7QsbW6< zi_z_}q4^J{JHDD<`f7RmtM#j|+tVUtQs0(~8zi+wkw;WPx_&aGolGmfPRAOa6gV4qE(g1*a_A$BG}|^eABKDR8R* ztHFi)U2s>|!CXE-W&mU=tj!1fc|iR*)VgCLl^XL%1*|!RBhXdS*kJQDQ9T-1w-wZ& zmFgoKL;RJbwwD^)yoa0+ARKSO z#l)|RCwNIu>Lgg)j`zCOq@bk7wRoHm9|MhN+%bu<|@*d?Epqn{Y)vT3iA-NLmw!R zYgN1kxUSY zSVS(`_8Q9*OroRd{fSs*wM7fvmn*6EqJ_Y$il#j!6_jHmSeNj%cK94upF5!=~t7h<=UbYR**#WK*KzMS2sc2JVdxOp1p+$r5|PG&Zpvo_<4znS_tDg zUZ#`=;Rt`k0U7oFJQiBN!R=l$mxDN{p(tg_IBj>@Bta*Z@g{5hz$7T6U|F|N9=2KY zU_R<0?S+b89BL0r>V&zC5rIlc6iry6*vbo^BT6f)lh%1MN35*v>3Fhx8YW*ai_Ju< zTT>HVOccpG3F;rdr@J6{C-SSEgck!Wn{420%wA- z28Mz>M`PQTf#L%se?V7Q?j+8}$#Bh;DKz8G1!8V$CW7a;WYzA;C$HJq61@Ht*mNs} zIS%3^o*E9S7*2i}k@Mov_83#kToLDAPHR}qfN9bQs^`xW=8o0LWh^c*OCXH%FP-dH zNjvCw0#qW`?=rYm%BR9;P20oecIUcAZOzwglU#-sixXYYbmkq5CW?Z=N>uQYTn37o zhHZtN?UNwMDt4Bk_*)J^RFXn-S1Xxa_pQUlOq2}7&b{;)_S(XDUx1FD$#SU(zMO5Q zcy6PPm&?eyV`mVZ!}&!25y5ioj?woYu_s3~fGYCUHo$;L)}2Jt=qy#m6PH{5`|kv( zIBAn+;Y{rp&eHsH;EL~0@sTDAePqztSy{Jt9QaEZ{3@ow9x1g9TMtCJ8)Dm0eC)lnuIYWZ< z-@;9A`mLVV+dX-X(Olo1_c`Erj#P!}IY`Ak(sc}Gh4<#2jE|-g)Tr>76vy?NT&C>34ms3BT9!U(zxw`smPltFY4;^$Zz(r)V>B-f0oD7~W zf|CSxo*`rW8B(>2e-}OVX$M|!9}{!;MqlL9i=-wO>M+{|?E{?>@44sb4IM@u-bqXS zqx9kOeO8W2s-;}#b9$sSIb|EYeV11jdULwhXqv~3q~@H?_V1htw~uM1qCk$}r^Od) zEOGE^x(1fSEkifACW$Oz4KcyWJ(eXbLK$7(gkslyKSgRA7b`D3BLc4vu}vhC+*(Iv#?2k=6d|m=ePY6uEt+r7Zqv6#F>OS^ut%0VXufK`r5*(5y29n_d~I>< zEc0N2yJIu*LV`WNeX;=bylw)|KkFx%v6Ju|{tgLdh1w8zJ?SNa$mIS-9HjI-mJvMq z3Y&_K**|VfPe-daH~_)qG{y-WdKiZsxM~WTPjd9oqu}rA)%ypiJY6kh0<+lfc51>~ zL?rs)flUVyA$#PxS_3u8$L68J3r7Icz{3iNV5ZduVPGvbFVR3nAy_m=nnzHi%3HYe zMBGOlF>1|e67GEPK_>4Sg~4qQigkZ{)FDUz7E9WkdM2wesigDPEUp0sN(TS9g8d|A zOrt|G+6YC$C+)}+2$ET&D(aIFF)xl~(+U?oNy7>=sGvBP`WnRpg=29Nm6s6eeF{^8 z#sA_|-FI7`&PD8WM=TFpcxHG+$ewy9lbXx9>z$!zN|sb*nssr z;yhZ?NA|pJc1w2oyX3NW?~JGKTIFf;XBD@9B&Vgc?I%WFGz-8kJc#@CpUZl^BKe<8 z1J!r=!O(%>9S8rENQKg{A~XcO76-D-?rtDlukAuOH(xSHqA)k2RN@zeQ5*bs=UHbP zK*F3U!4DysCnjCSREH<3aN;x^<2Px`D^lY1NJ&QKNhV!VC~ksjX_94ElJ!i|_O&D` zDcROM+1@L8XLPb-X|hvSvhz&xzQrV8Vu~Ln#gCXmSxfdRO*zz+;>|LvaKHpGFu`n$ ze>5hLia8LSa-=IYY-T<6WN@;pBPNiIi6o1Yv7iJ8i(+E}X_%0;RD{GzG-s)I9SmS& z6mAftd~~F{(t>E1U{Xr5d3vE&dhw`KDhm@sHq3|?RYD*uBB0uU31*}wM5i~DW(aAg z_yMT_Kzd$7>XlKg@L)_R7haf5uPn8&M!*=aj8^l^0rM>709Ftivr)R4bJ9?>jCk$z zq3E~@(==<2VNY~+4;Ls)1o3><1MSQyQqJo&?6GJJib3R2AQ3WDv&QN-&mrffqj>2v zUb%p(`idh^JChb%n##xJ0y?aY=*)Evt7#t4W2fB<&X^>z-_B$#n1kKHxl)b7O|I)8 z$jg3OhY8{5h%T|oa;!3KK+-19f_7ehHt!WK*Udalik>~=WhvdANfp35_z*x$3>IXm z&K8{T%0baVnn}S78?vH8A8_fHwX@1bvA5{CJ3C-B1R2{DI@4gGN$jCn>}E1Vn#^+g znYZ1d*vXu2MPu79rPn0`c%iI4A?Z||yzkmOG&x`t9f{(DllV5wL!DmWH? za(V{Z2ruK>WwS34axNaAWb$H$p9b*e7hf%Tmv-(kRbv)o&|_BY!nUp^VCp@ z55Hb2SDl0^NqeNI7g%z|U9%T8dhFezSs+j_?FbWSP&9d?Fw@hg0TsAfx?rf~l z>~XAklHpIQ54242SiIOAyj~xWQ9n|a^kuDLy3|UU%ppz!q5wE&5#<41=wT>}1J%P8 z)jJkTef;t_(t9bO5+uSJf+DMfDJ`Vb5GsI2z zJ(S|3Ti+LgaSA=c?a5ObW0TfWuJ+Wn_T=V`CQ+Aw?V;?QYS_q0U}_9DCjzrV;Bz_g z*n^i$Xdni;6x(%?FlprdW%78Y}&~!c+&Ctsz zE{>+a5^PL>Rfqdr2a0A#)9FMpJikqM96OvVOotT(U5$GZ!z~KE@#`EY1_-BfEYDwK zJ8sxqSCyNtj`cK8z3H_N3Ej%PWVOT<6K?HG$EeXZ|FZxi$6FZumTrZsdCO8MO@Imy zTO?V9_S#5z0hD>vmECz-bXbkpJH1?xa}L;aJ{y+~hH`tGWYU89VxH$=6;AKZyY}!{ z*>_yVrQKNb-_TE7x5rxBJKc5-K`q&;E`nCq6{H#OB(B4`j_>0wnX+#=<(cR;i&FaZ zXnoe11L?e;jZ|(c@j3Sq<>{ful3(E6WlpWd^~8tm zb-$o)z21*yH%XM>iKUxSlbzY2rSx;-q5Ct7elexBK?%njVb3FjScAJ3TC30kgmD1XkiK5q;0Yw|6v4Vs+tEA8vLaMhaEIDTbh zmIY9dq5Tu3TNDb3H^tW^#8CT7S0ERyCT?E7_ z@-%;?JZl4m39<92+{spo1Sm8=xzHe!rW2uIAV^2`rD5=0U9-J z>uSOz7(6E!lq8)1Q}%K&@;8)1Qq{(LkC9SQ2&l({jrPFd0_eTST+z(K=gxD>)ajDS z7yqm=J@@8r?!}rjZucL_%9#aDzevyMZIGMm^3xlIV_$|eKTex?tYSUBOam>m=Vs2c zM35&t6CX&1)pC0<87*H_M$?A_G+4ie%aeH^|SNM@!_`PthX_Da}DYu_%&Me+0T0u`pPgfzPbC< zA8(M}s+Nuak#)=tZjqY+KE&xL_HGK`V2$bX(=-6QI`6ks+6BDrVZD0a{3^d<{9pDX z+wE`+lB6}>qh~#1VBJj7AH4e)@jG$E)(SSG=8GG=QKY`vwigpjm+3qXzM5-!)|%!Q zjdG)0bYr0WmM7F5F(?7pSjE!jynF3jY+!IX_7=e^=fCM)qsGXaWMv>VXYs-FcWHBF zhwcHS-y_VG_Z|58P`;cQdwQ9UJ^OEg*f4kD1Y3vJHYoqvD)Z$Yc3qqR=I(i-XTEXx zfj1?lFl@OC%J~cfXyDfJtk*PhRQQM$?6s}Hi~RQLiIrN>JQDJjnmysC_)O>Hbw36s zW`qCu*R#lid-Kh&xx9}g#wQ^{rS)Xj(d<-zHf9(7V;mbrqxbB#PV%K=@D#O5ZERul zhxx@)O=NQIhAX|t^u?Cdh(MF~XFU$~hBCXu5y0hss$Bdp%Ao$@ zK4{0WJbfyNAwD|!cj6-9jdbsdgmW!3Yl;zykpRACcwtX$;}87(_Nbb*vk}bM*EepE zZg36jbLxu`4~Ac@9{;jxD0oz7^zFCuTnh_hEO-=gEz{Dap)C4I!~;kk06(>?KXe*; zrHD-K`#PIFE}$tVzjFXKO{J48e$nr(;o1m02j1RS{_<|_iv$m|oBq*W01SQnUMm(K zL!@EyK?`*%>a}Jky|Bct$_--H~BxswkX>_2t|JSe~K+f+)M@K|Dw>>FCfI~ zy|aOV?&0T+#`Da6P+3o-wrbPIJW>dHrcAqvX#=Co*N?`sIm041V?^z?OP<*|ARO_O zOJen_-XrCen5FabPmaJSGka}Ezf{+4$6_2&RJV!JO4!^!3>NyTAZMx)HrYOGOC8w1 zw=GoqY_3Q-$pn?5boZnUB8|V_*B5aut32+h<)S08`XpP%|F4&JG?^#t-D{g)h&()$ zgHzU~QizC{karL1MCC+h)lcKJTLX2IM?nUdl>NP&FK00M_Wy=ow*(rwisxBGE2^suyY#cnsqg&RYvgG zq6_kLdGKJkwx6y3f~9nNSeP5mLaLvrDP+&CKe+AsWd|pYVRxpg7Qh~wSpCjcE|VJu zpziMEi(0{s6qtaa5>plBv;%pf8nqoy9kUC~RZXZZlN{3u9fubQl!Z+gxj-Vf|BQM5 z9a|&3MsRK<3RjYYe=hxG7vzldXJR5rh}}8Vg&(sfs6^-$yf3ttx=W?sexF=WZB?p# z$OYG(B_n@RK^6VlHXxbAF?{Wj^dk5kj;J{ECS9zN*^l2ksu~|_(%>Pd68)1rcgWrhgm%fIg@|po7X2|CH z(=OQ(EJ32EIiEf#tj5r&>ssTqrb^lY902tGC7T~ZLf&azI31%)$#~j0Z&Os4qve4u zbt}4O^dd_^!|IIO?5^e&`aGi8p>|~_{pdd<8%qls;?9~3eBvR$S*j>mSY@SsG~8H% zYzw?c*jdUM`#Q&jaX}2Kb?C%cab(CMvmdL*5`de3I9PpD+##i&BFPTXYmt*^CNeu_ z?i&kcO4)vdCZTJ_wjC=GleXg9y*YMN?u_g6Oyi3$a++lPnh7$V@s76D-S{XBM#ujb z0wh#11NR%(&nMqmk29|-cr%q)bMg2kFJmViuf3hi3@H+C*M*%bd;R25?N>4K%F@aO zhY$3M@USuT&7~%4)|~q&s~^ta1>;3b4D_MyCP7Aeg$509D6aN6A$Ju>T$eX6Ft~l; zukDRx;_TV{!4DMSeM!4+o{(3J_-N3z=V$D&tQ&2g!#>ygtEYEw%EmAw94aQ-Z!L7+ zIih(cL3@fc%A@+KQcmx4MD8>Y;d86@8Wns*^!X;dTPegGs+s6|Ok}3&Y*8|qVPY)( z!N>@Dj6uUkU@- zTSesl1gxu?6kvp6O9)s#Rbe2>d-qOujwqEJO%{;@LxT+UeCeto7l!CZ>6^SF=)pbb zd~xf3W{}3mgw};BsHhrZzRJ?)yN4DKGLw~h=joL$_kGV z!Lk#vXZMcS?30fq-A~$=mTDgOS`}5QC>_9n>ypFuT*Qe3&h#R&lL2SaSM5;zkqYMD zozS-$Z^=-(+o}4&7!leol=+#v+rAhc#W*UHeX3eBOvnUl9ut_s2;Y)RFL>IZicK!U zY^PpR`YRX_@nI%P%v6C;14lWvZcEErB1PMTgEFtXerz=VwC#+XO@feOJ2TPTV13HZ zoZ1Fy8o62y8)|LJ4{6o95a6OFL<6E`@1`7*v`~*ggl-7oWeTZ83?CE;- z`29kR((_d^X-KC@REVf>L<=RBdQ~;_b&}m*U@GgPiG_r^`W_!uNBm@!%sW?V_ettL z8tZ{$e3AnujFPm%b#IUn0z{ssKYkhZ8pbsNh7N0)(g5=`%+MVQ_RxWwrKA+h?*^-m zbjTcn(HPVc?@$5uiu{-;6sPBDd3c6K;Wq`})a#pI-@BRW3liuL*FiqTDJQw{ILN1C zV|k7}_FzglnQyj>CwrSF(|icr3+$T%Eldn^)!r%*F4E_GP*r#>KP~sR>lO(I-|P(( zCA~dlI4v}(s&G$gW?^j-r7o^2JW9>pecRPooXl^Jcl?0&(DN40R~3yExg+Dw7ei3r z>bzd}3lqiVLhS2qxRRl7IriHQ283Rlm&-l-Rra~(Auad{$}4;1vlKy~N}!rE6`nI7 zbtFIhl)l!zHP2Go^SofEVe+QLg9w~#Fn&?qZnp;!(dtqa+D#r1;!V~@qe}I_9-E4f znaCff_JRA&uBm@HiNe7~DB01AXDlbDZ_7infBHs>Yu1m8I{)H`?QZCg%>Heqbw|qS z)1m9Ag%zwkE~1|@bzRw>4x=Wk2vJ-+rOk;uI`{`N8=ZHd0qbLziF~(kog?8dFE~K4 zWK1|GiI~g>ZP>e@o;Tyr1MU(OEGZ+xP`lvZl`pczEpdpGd2uAs`<>B^BPxeQ^ z_hQnrlIBugeJ(3 zO~Vgv5Q}xdlgAZSzrTC$MFq{Z%m-YzNpcK%!PbnIz(f6`DE7u`5r72i%mtHWeeY&h zh`(@I`gkh({-CKfO`IO0L1yCgubm}kD2bvtR6Myar`@GXMYIt#Wqs9CPt$Ll*vj!7 zJA3TzF8hp0msod+gxYN-QPDmlDu0>GHx%_@I{*Tp$pZMq&bVFnKwrLu2;H)wZP%|> zY5*~bU=qGug3;guRl8(#lBGHe6Ml_s-&?$!bpSpALuavG$Z%Xf!hNE9b5EZALT z;)F*m6j?$QXQC7k5qd<*raRiwKR`)Fm_}Pfl%_%PJgDuCJ^3!B@=FYg2ekdCC@fHJ zBVxN-6}Fd#ELVYALnvJj5mh4U_Ss|oTa~95Hq&K=xc)jKGsw3H|6l$okR+M709b3A zooA`}j##33PFZDOvS!jzT=>%>L6{A-A^yKK2@h$ax?YJb?P)D6Sdy5T zFbK6bfKoKajhNJ&hc)Jk<7NuBHwNGts?ih~9f4csXN~36_?MF`!q3x*{zT){LMSjN zVD%V1xhShEo{I&~l*)SXAeN!9imj8x(_f)FZ&>KvY2S zY=*FQ;pQPol9F2;CEm%_?st$NQ9PyDn>_m~wl6WuETEGXiblH&%+l8jK<;J@UJTbb z>84JY>FMJ*B0JSE+GrSS=^Ei(byVz}?dzu+owj~MZR9B|{#{Y_`-XBKTR?Sp8kq*& z^teQtC-p;RFWD(e^o=P^TXxsVdrgfkW74ZJVdru_PJ%CYi_eE>pDiotchl+>R5vz; z6rT6g{h2${ye42<8QSg{!W!lJ2(B1w*&ct9kV^fi3_f^v9tMmMMSIv`DrezQAv^9(*H z2=X+IY+k&G+VFOGl{Mit^5oDT+CaxolWq(BM~!%n4dnWZCJaC_lYk+A|5_r3T`ix0 zx48ZcP^UHP7+wmo#C?l`x3DigkgyUKYP>vjMuP}jleNF@JQa3D6Zc;8uH4>n$&~+K z{STJb6;!DIvHMDASj(bmr@U;+Az&3(ZB12aI=kh!T+2LJRFB@Gv7s)|fLJnke!M#0 zP0d?8VG9jh58c&OUeh;@wM!H3`C0S&SG30nXuJ`z0}(bqAUj$8y-u6nKPmdQtJFN4 z^Bkyc1mw4H2=e5}bf{(CDf^H`{R8GYT8jplG}dQL)xQi?;b$;Kmbo7`I$Y?WI06{H zi8ts`aX2ovt0KwIwIQt>{3f&y({wpUzH`&E3wx6ni3dhxf?;49s*SFha6s?N?a5GQK-XImLP)ptGE=gN@mpZ%m7~|WSq)boQ1wUrGx@5Qu zM9wz;osw2iXIThqQ=)~Y8lL^=KwoGzbp*tz@cT@(I~QEn%*X1=q`EA zogZ1(!arp35Ok`55BuvIrW zha3k)b3tu39DJeOf(>oZp@eXtVz-Piv3EA~(({ag{k=o%EVXSia9o8fYQB_KcEc?l ztZ!Vm_M)Zj{dOxmRHb3L%XiQ1FS`=%+rZix*dEJ@j_xi;{o$n|jAuzChyWOdqZngE zj*Qi$gX(N}>A3eH>u&AxreAZze|x*z6~`v~lJ|P|Nox~J{uGK|9(z2xgSK+Rf(xP% zpkY3K4%>-GX@&4jUnoYa|Ebc7z0@&RlWT3JcP2)S3+bPSH^kaY{YhhXlZ*HFly-%Ci$~B4+o@=~yzgUg&1fu@LYydHnHm zbMAoUX*#5u1sRvMq(Imc<29$62TslO<0&^(sL+}sd`K2p{PmzDVVbsPlSV+}LLgwphO`jq(9I`@PBbLj*F7AY644S>1YiaO5ZmYh;sY`WWP+eg zvqoPjg0`DL2?PkrnlRU`D7CtNJ4WllpVY5&+Hy`nqUpIGf1p@oGe`#hI%98Ujhv(;8Q6B)DXie-r)}$}`ta~faTen{ad*vi3g-cX zto=^}EXjCwd>g34m~~m(Dy`pp9N*!R-79N(+aJ&ho*c{@d9=Oq!C_f!kMm=wa)dwu zZ{4GKT4myomWXVAK*)Ki8*efl9;r8XeJkCh98fzKFstwV*!K7 z)4Ufl3+6rN?%n{L=CfD=tVTKi4nncrn*# zAI5!M@q9t)9_{7xZ6YroX8&i#do`o<_Wx1!9!^blZ`khMJIUUGBoKO+fOHUoB7~xZ z-Yk)3RM60ihyejkvpaZ4Loki;`V2X-kbGWx9Q#OoSz#nDP6yCPvhzF!wd9h zv&Rm+bNUQiD-kqn-_*f3eRw}+xaP5$@eKqABaiH%Qc%}w^Y z_Ybd^nzgghUBg4q3}zqMaPETB*PxV!h1*iXzCcKU3|Y`7KeI3%4QCR@Cwt#bHvZQ# zx$gDZL$m(pX5U^&1o?@U04n3amq!^Q>l9|5;Q}^7_%W+s^+0`gR0iyh!e- zf}j7;nNpB3&0fU+3eWtt%kbOXP2ZKIX7(TmL;^rQ%8K@~ckasX(qE}Jeq&dEJm2;@ zf2)r%c}a)+td)HK}fT> z${30)4rQ7}=R<{6^$@xF|5u98rEQqeXJD?-cRfS8nQ$XRE~H&MW9{WL$SvzW4Y^FU z;S6lEYF~1ujz$Kl1CxPRnG9N6Kf7Tl+3~JZ?e^r4NlCTm83vXwhgF-Qzw};<$w6bZ zNz*QB_J}ju+xG8F!DW=!QB0rWSYOaO^$v+X;a_vkZBl2nx-vJJ?YfJ)Z)%wPf@`8s zBd_rO-Te5)w(WGqzs`}wL1~;a(I9~ff)L_;mukUf-8{y2Av5cjkBj*?3H9jzi2Sb! zxh3XS&L&gYA-9^Ot%iq{e?~x+E%^7EqzYhQ{fF6yWB)QkQ+YCh-jBE!_HJt6gJk%;>#GRfXG++2vsj1`(G{6)2JB9H1I9=F9hAXE54JAks>kEzRPZsIN!^B zl*7B}#_jhscVFJfOdsAe44)*QodJfW9f>50~(znDfV zSO5@i?bugD%CB-yi$`&_`OSWlX3^?)BgJZ>qG3cRj}#-H-_Mf3Sxs`gn~HwT);M$burWGQnWEv2qcfj_I`_GQd$Ga`*=I8&1r9)KF!-5JoKTr` z_Vg4JCOCZZoYTZF$#iL&vvl&%LVja0Vp~1oMw9tl?S$Xx@RGVijn6SpImci`pCI21 z%1Uu%7fD(?4}P_3YIslPh8#@Su`qX)8K9kOH{o($=tk!MIomEz>LMFORd;Af*s>VW zQqe(OaYlESF0hc0$m?U2c=~Qhp7`Ffg)5c88>;y$1P%y-WaNxJ#djS9a%O<*t*Myg zgnoPMowe=cNvR~KY*~}7VFPKkAl{;>vw-T`M;2<*SVu#Y$3O%32f$i~Z1qV&Ui1eE z2}v>(`HCtNSEptKyAbV#cckN%hB7NRWXa@rGS%cCI5wkYH@S=eI?H&5h;=lDzZRn^ z=LB1_3^D%=9_j=cB{Xw1e=yJXy*;(j)0QKdbQX;kEXWj40NhY*qQ6d49LF z%~$mdK_5jK`MWJIVv4FAMtZ&3@}X}^H1dbAYrJy_uR;++5&=6Y%0r4tn4b%LtewCk zmb=Z0K6^~Xn~@drcfoqQ!_iC7(N-oEK=bTe(Uwfe5?(*s=2)4d~dHs^LQxqD2tc(Mzzp$MLyY|pS^WIy zF%st_CWe{)ui{IFj8+Kc1zMM7BdZ8{1i-;UBhNo@SKlKl=Cv{$2 zZUkJ$2M7215a=0r-1Yn6M!8=(UI`$Z2El& zaWBu-7;D_{Qmr$co>xQ|I|u9tW~ceTnL!%~k2z{#azAiT$IaK5?bw~$y#7a_&G1Gg zy`=z}UUgv(!@*aBNOcxOaV60%(l7I!LdjJq3nKTHEa!>84GJv%$Ckp&JGD)a!N{e{ zew?%WjRZi6=1LIA6xS*k&D1FH-=X5wP+#0PTFe^oA#x~aYo65P%W1w`%n3846Z2G3 znpJadqFg)I6Ktuc-CPQeO%*TRfqieCSL4w)`|MbY)d`1kua~vMbkLTAf|Yl8BzX*m z?RZVyC#S)6_Q8}>TdGw4V=tUhl3Cs7%YCOr!oELZXaLDX8Y6Ho`wAtgHph2b8hfQM zD{eS1=TbH1_wHeA2nF=NUX~d5V@R^_1vsbDysel16qyb0+B$x>_F#$(K}v8vjNhx5 zruD#>%(29ixJrMX#qI89fH+@Lp+i)0Q`R@|rax(C1Z`K!qRpi^AxMFZsMKUWa(RHN zn<0z~9Syi3K_@|0wCoz~ES8K3a96*44j$nJ%(|0cW6rqt2p`!k!HQ$p0n))#L+?qJ z^r(Roetp6zgLtviKw@v$eI{3Vtmt{&5`gy?s=0QNVDa5`YLx|Y)mU-b`5;(f3ige8 zp<@e(;LLxB)N1Km)VT1! z<+A~K->_Kz&IvBSi`-moIc6*ML)QyflntD+^UOKqU=O@SvwjqfpsgQ%vtm;f z6MiQGLy5Z%TAFv78(COM=Z|7_iDI>x{ZStGfCZh?hO9#%IuAJaRW+T%R(=Zv?ObJ> zB=69rfht(Wx>@U~GU7BE2Lsv4bWn;Yv0)6K%2QgK9B$`N(Ech#r}-C=KtGUR!3Pig zLdq79ZdsMJ20JrpYn_zAajMb76R2q#BKw`7M)OaLOD+<~DqhC!UfQ=|2HeqPuBiiS z2tcc)?8`Jjj;f?71PX_c)&Oj8!PiE3C6$Y5Dq^2PBDrSYM z34ux$qDWHhd~Ga8LbQ7&u#EUY?!LEZlp+g}BN}J&#UMI}BuLnb3gXb@Vj?2P1LeLV zDlUK=SNqRT=~9YzQFZY|Z}H@8@!c^wgvOl09z0K8jXmY!VITtV3~ig=a-a1}6#3rw zD5bCHrHjd>Z+o$5TFl2KuzE>Sg`u58qr_b{;w`DF^Gk189(y-??7yXBfC?uo8CEliyXQm{O)(Q>NNirao7;X1R={Qm$=XuG7H@7L@4L zlpFPxtEH5OP~k8d98NwSMukJkuzo?gN#Ak1x#Np(%R?FPHbvyPJ|M|O&Q^)xVlkV1 zoRU($S>=Sc^$7)Rg_vAM3q8pdGU7Z=jf#LIP?A&OHCJI(bKJM4GP%lTxkwt`bc{{B^2I4XgSWh--LI ziU;7C0&h&I%&n;o7L_~Jz|n%#vu$t`x58_@Vynt=OT+4%UDf%^wbJj(*B6{9?mKZT zrLJ@i{G9_y@leXNnyM%`YpzOTuEybAD4qlP`D`?ys@~lN-7u_lGps%BQQKNj+fh?v zHCKC1qV8Eo@r4xl(p+`#u7*VwLix=)FbNQMh0TUS=T zYpCrw4!x_aTnJN1U2XWiEAPR#bp{;h|GtM26##@f5%g{@Qp?>?Q+uZGq?Biqj7@VD z>6ALHahcQDU)F5c+4TFB2cB3-Qmy}D*!()OK1>+8d#*Vky21Wei^t9Fe=XL3+JV5# z(=#3b99{`D!1t==4h$Pit zm{={2^=PbW?V7DU8$S+dQh`Vs?5|qhm3m?`rc_7=)VWAB@yx>e;tMq}fe-0!IInvg zg>cV>lh4`*orq+dNv}O)!aH+qLX0%;6FagHwIV`a`EkBlJfjPB5xCNE_Ky?l#KPGy z?)e;pQwd~fi)S~f?yNK)L^I(p?&X|ltlWOW?wg<_7hZmR?l!hVOmdmMsK!wtWh!9) zMDDMRq9SL*RwF~G4n*IkFj**b0AYJ8?kP6Vd1 z;84=Vp2bQXhJ}jfr5o8DuA4w7OXsZT`9w?jJ7gax; zGaK)b5nLYTZ#|4VXY=)<9r4l)E@VUQTSvrf5mon3oN>j0?nS6{v4Kw204Gw^MW8n8 zu$U3=*S{RG^D?36ax!h}!+51xBS4#YC40|_5yJ}c9L0c$;ivU2)}Tl{)EW|!L-W zzzM{v*990*%=WV2z7vsIleM?*}|q`m~vx%7WSm7EJd5QmOr| zMg5&>mm%`yb9<1x#)v9W>{mm#7p}dj33(d53ctLn)m7VqrXuwhFfIZFm`7Z=kdC^; zs23Y!jMn!eF9`HL`e(Q-1Y+Rt_*B2y7u4$m`VX@cHl%`{n~-1+}+U6Gk}3f zxy61|b$OE-Q7f9$owEmyBBENe8pTlX(}~`0(ZwGHO(toe*a%2cht?6@!83hsMSM?O zKfWHG+FgBexqQ80L!^ ziBMAds1_5n;bF+#qpA#q76CxKWUm#744~4*QAPeP$7-R8}?PGVdCdNSZslluDs~7d~K{3=!v3`AUoUZfhDMa`%itd zo6={kdY_x0^0C_J)THh)BDUD$G{*xOk+fZj0!K76;Hdf(bo9xT5t) z@Ae#Tp?v2YQ1!IsY8Dv9ydaLM!ueqSO7#{drezbL0wfL|7T0Ia0(az~BV+#_rxuie zMNelf`L~%|9wL~3V_WsG3wU`}tFS;<=kJY{lMC8g(c1;+^#F!Uh15wP;p1vWoMiQx zKJF&ZCzmy9X9}M5U3=0}H*K>Cb>rW3hwZ%6o!bz@;2~LDsPzcV=~3QwJ+G*49TBay zX=*cf;voQNCN#)YhBhzA2>PeyNJqVeiY2mWx2sbhrN z7e?Cz((^A&O>28M^>4WIL_r;;6GXt!q%^tk=fE;U9O~V=svR`+ld}t2H1xCmVDQHg zlioWwv(73I0b|zvU9oT~^K5U_y=&IM_kanM_y>-UXp$@M|9RfOXSGxM;|t8*{^A2D zkO_QS!Hqc9EO|X^CY^$K;9NtU3+-ce#%Jwy7%w5TO@P`>0^R%kOCHx=rj8v|ueP*) zXR@~zew->xf?OQ%bOr)b6F!|Rdr~aj(}%I4PQ6(Jp-ie0fx909u6*7PN7I0p86C)P zNR|n&w}qp4(3|w|;-mLAq(Utk=mSh3?kv*uzE$XP22B|J??2Ft2n781YT@dGNp7_a z=Y3BHiv0|6U_zT&pT*Nj4<6cHhz3NTzc#>I`c9_2m*Z@%4r83T_YG%FL*j&AN!zan zo`i|KuftquGwsK@hEWr>#HhfpF()rzfseB`>VM2vf1M%0VS=m23Wm-GAViX~JMUW% zO{6Y(_H6G5s!9c#roklr=mWs_lXLXu%%)2IcU#)f3{}6-6r}R z(&rzw###Gd=0gs2|JsMmnx|eB!(qu8O|FP! zE3Pp_lv(g6BN!d^o1zE~yq`#50)t9oduK^o1C1BgINVsu8vBiA9Jby5SFz-N%U#K| zGq4Wnv!@NzylyI%28`maxdK>yha)-vx5d`O+ujt|lo}l#Dn@Pp-xgajXKO~!Xl_v? zP_)sZ4gXJzZRUy4aJ1Cw>6WW4>pZjyj%axlXu0$c7?`W0!$T@B-oG4y#%K(PYiu9O z!RE@5f|DUp8y*c<*e1&p5iDqBW^A_CH!n63GL{X0aeoyEE3rP!fRD*PmNfBk@~Esf z51*3f#>J|gm$ERDr<4IZWY?;`7g!4bDBDtho+vSLAxz8AiA>WB%xoiZWA8OSx3$>5sV3URnG@16ck$`8vzLgv8;mS?GuW%B$mb z`7I+-IFpu}nC-gSeo9+D%3@?rE68R^&M~wAiB#I64t4TQ#9+vWTjMqGA9A0bwazO4 z9fC_zXAMcNZ!{mLg4*i=qfQ?5x~VEOe-IZJ>FsyYM`YQR7!8XgcDh@Xb zO(&VbGrnAXvvmP7=d>hr_^&P31+1II__T=;m$3FdGhtD~gEzhRToPXC`C4AS4JML! z#YdO#?tE^lF;>RB70hL`ak;8gE!sBwWumHV7l5?Q+VTAO`}bzaNj+wi-OP(8QydkU(2?Aa{wm1>ys}(@|?7dAZ>%ojdb3nI6NY^&N(LY$l5$l|w@&%GfX1G1I0#Z7T zkG~&>P|j*~64sjPG48~mu~XkFcdqG4=fi6RFz(~)V*g7ntI{gUchQ^WI$`m3n<5;N zbQZ0@sT&NmzZk@4<#c!!v+>4GPO9`4K;okddPDv$d@SvvQr{w4XXyF*A8iz&bG{Oe z3qVVNCba%b&RugaT&oZML(GE79zp4^f#W5p{5-qvNIB4i)N#-{%;IU84`R94-9O7h z%f6ie)Ltw0GD09?yZm)BSSbBhNOWNEiA9mlukCV3##sKdSJZeWm&SJ z!b5iXgRnUl*yIiXgB137K89@KeLKg%Vn~G^;qhqD)F0$HkWEQ>l+RA?g>vf4NeyPD7j`834En%yUEbLUMC33-wkW7DQd4+WHj4l=V@Qx+1 z`4$T%QSCKpRMm;aX6GqHO+&zc{+E4Rn!Z4EcAqM*ZI+O0;NX{r-|*v?3x`egV7PjB zsB3#`95(jAz%20ex&%s=NrlkvLz$OIN1cgUxTWtN{THwk^5=Md$|N=`70E63f14)R zuWU)BaaE{i;{Q$tF9}HQKLdo-o4LXDe1#J~YK&n8&p`X3Dp)<>wzdY1Ej{osZ+~YV zEJH`oG&z(ky0E@McR$XJ^3#*b;yf#j2ORTFR+M?*&O=x-yW_xVBRX#Ip-EQOu5XUWUL0Gx^$MuW`P4llsku=xs>~qrO;rvS zw7ipbl#nbe3l9L1ADrA7tqUc8<8|$?zfiRFP3Sri{n_9FzjF=1$C%Qy)DPaxP1SE?E@pF@)l_l_;7d zj`)~+*)&H3YeWucVv9%WMh7XG7(doeiyGSI~T;<#yHfN@d2(HL85j&L|0$^&Iqw2;t~pBrUL zj<0I;QtC9w2MonTtsR-GaV3pEQ`onzBcWHG?tb)?+Qw8vOR4M$0oSlJ2na$hn%fH9JH%YMj+;MV?!d$e9# zn8wZ{=!|ISRT41VfUYc5c^<)t7&mhWM5+kZoVYJee*UjD>AD2UePE6To^w|II%xko zBEvQwyOnI&ghZCmo??) z*kX6-hr+AfFu*Jl%QP|%LP~n}fDbB=J)o>{r4BO*-N zBGSXnI(iI3NR;wXC{#D+@KT5@3rVXcyrUg?glv-(9RBlwVkZD@AI`rnudrcECBWja z-}j_F-bv5nLacvnT`6-d?qSW1fzL<%_FlG+60L$R*zL|#H53D_E0(88h$^)#mM^;= zFbG@-sgd0!Ze}Y)}-v^F(6T#0%^>^91hxn33gBb(jurT z&h~`XL;+AJ0UqENA2CElT2vTY7F}KnOn)ci#53|A&(4~T`7#S)c{<-ud*{r^jXUi> zS%V}$L_6s*q}T}s!dw<+HE&wn0wn}mJIGtc6}L&8$Pj=%hY$DXg>{b|j~!HofN3CLCQ-jW^)NTXO&+z2qfonFETEbbk#mE9ys)7l z1%QdG2vbb&h^roW&;}Bu4H#3HrtCzZ>e7z3w*8{vsi^T&JqxA(plFjvTQhr?WG{Jf_%x0g{>5SY)fy{V8j>~( zCgugG_o&%W_GdODQa<_5wOn>K=Ff3`%d2+_A`}g1z+(Q9?$);c9J#$qt`7NLKXuY% zsgOAlh+_d0Hm3rVP~WJKCLdHLf&T`Lv=UJ;uf2fZdRxa})dScsMB6iK7I$JXk3-+L zhyBscRHBDJ9HUHlNX|!A-At80H3FqW#qKnH-Z1Ns(Eh+uuEe6n_=OVoj#dBpIXCtd zXsxo@>)EZ(FCBg{Q;;x1*}y_2h)PxTlJ388xoBj10dpqo;btpk!0%_Jgqq^BaVues z!E3Y(@D2RVN76~sVYN!K+#;FRXO|M>v-8h-@@2OZQM^_p6#$$_9j{gK_Iy}#XT2|- z2r_+VY3`Gp6TW*Ss$sJJk^r z6sp4jep?=XXzh2P<_K8hLN5T>XfV()Nr@QM!bjT(({FLmCwd79Z`>1tlqPNk$fqgK zy*)AuWEgu!n4AVu&Ckww0x_7Q-eYB#Xc4l^ZkTv!P=G#2TIIefOy0~nWoC}%Ri67d zv&~XeQ#haCB?=8b3D|rS5Jg( zi>09aJy_qHcepOOGc#QNI0iM|K`4>szPu(SQ)H=?QZKDYa!5eC8Qs>xrf?< z74X(7?iqT$=bKu=OgQig=;AgdY?pjEln@4qSlu341o;pJ2dOFG_{`_+@l5g~U<2mir z)R?*pQ#A7DT%Ej$4m*?hXI`K~A!aS9521A$tTb-w>-_90d2T{u9k7RwIee_n+`Awk zliv44VtkVch4#3wIP1E)2x3x^(13H9DI+oxMw-*W=@+U7acW)wW|E(LC3EfQ z#()pwYM~GnfiKf|X`Mhk)e(mZR6-B{bQRq$1b|7BZ=>Fwh%$(2iPB--vFJsOy3z+8 z8+=tlJ&wk973f)%IYx64H&WP;UY*yQ+D@yR1EO7<-R>!8ohW9D|GY|Ck?Q#?XI6@o zhi!UIVbe4C$`R77k^D0cAF<>9-fL(F=Bk*x0X+B;<7F+C}Lf~;N*rx-olp@Adaiqhr9p!KIxkhBD{^$ZQr!p=f?41u+$f+HMp&P zE|qH&DQ3PY!l*kg+h(>#_$g<>0`%%3u=DMsU6M@YP}Dc>DDjs6alyrcNzj;qf|x)e z7d@%GiuNHn5?ugsW!w`UP~v-pKmw?`ptscGV-9MK@VUQ-fiY{~niHnv&RP#9barND z)3fadnmRLfONRd{uH3RPlXXHIev)~r5D|$Z0sB;?wTTl$Ay27$pSR&JxCpwRn=h`^ z-^fh6AIqMe6R6akEsCPipY*&@`0t{_IkX=Q^E9x!A;cjLkXCITRTgNr?X_Id(KXGU zg#pl!KZWA#TG;{RQr@eb24j|^`{`X2A-K*x`@nw!=%u36>6;Sg;@1#9979JG^*oM| zo!c7=nTA8N{^xU^*`@M9vW4_Fah!9lNJ9O7qGf(mbp96NvEXu_PS;x#C$sMySnUWW zflX3<213AG@G7hP{jckMrBdMB;DV^;l^IKE+Owaq@5|4C2=nw8L}V?hj{#_7o* zJt%N|%TA}yhk3}7f7)FA>b!axHvrl3fsiAc-<;lZdE4+(nT#?Xq|ASwsc#`j6inJaHPGaf6rzT7gX6!@40uw`N3zgWQx#PZ_#rTc=$JHea#cG%63ZYB<{?>dqPjrvW;_Rw(Fq=I zoN7Bgddy+IUKwabudz;2w&NZWM{4SpOI^f4P(}5yNPDZQ)5sQ8NX2po`X~g|E6Cn8 zm>9p*G2M1?WabY8lCaCwy11~gp|-dRuRv0v@Hi(+(Y)~+5ngg61{TAzEi1`&;;xbZGSm9h96Zf3^~PF z>-A!s+9;I+!vQxdJYF~jLebx@AL_ptaTBliZSd2~V9rUDl{3-6XSVD68^O*3twXM0 zff`K5k6vgTeu;{UC9V&3J@|t@z50NWEF+|z#4AQ(HUEnm%-1*`CvBOTRGzuI{gXHm zk)-pgHXXCW>1oq zCzK;*zd=&coA^{%rkQiPv>{*voA4posk-vq55LoFx^Ay{e22fu4x9)ze40=lrHG11 zq;rEd8q5UcsjEhk-1eWpRoO%^n;d2rkxRAK9()zx=!l7!>Y*N%e0X2%Go^FzM$9Yk zqc;RsG_FaLQ}6d$4H=}0{O~%(iN{Z!zw7Ags6I(VNr}7K0CeGJauzKzseA~VIG?*y zSd_+9Z?5lb9L>}Ycx98`) zr(Uljzj{}w8hD+CWDVnmz+a*MJ7dg+loW$yTj_Dv#C+u`l4UiJd}IVTnjZkwXt#Nh z_>i^W{yXZ(A9KCckJE&E&AO-PCKN~6Z_Qq^vs@8gv{72>7CKJ( z^k=yYme7px`h+kj7!*EpUTl=xlXkigAVO4WiXNQIDv-pyI zD-YFBLSx5LpK6H?C&^L|!cp_dAYRHi=}M1H4Cqr&(wNP3RrOB3L?Be%4!A>k)2CNx ziiwp_Riq19z$K3Z3B`F1ziY@De|>gB`7K2I-GcaL^JL~&i{sDxY>9w(JKN?JOJuW9 z>61RGGZ!0N*M%uHyIgK&5R8}phKzc>*nWK6hN`d6&kFpNP?6MES-j}Rk{!F)^z>`q zxq9p4jTsx$;Y~)4D!iF!#$ZQA%getre3bm%;~2TI_hg=%VaA>E_TW2R;V%zQt?f*} zcn;Ti;TU>_??YnatHJf{HFs1$3C~?q_Ot)*tHQWW3lnlGxReJ9s7B{E-VL)XR?%rv-^>;&*N*@*{J$P8ILdpV+IpIz14C|&c<~9 zCs?z_r0MG8JEov|P%f&sO2(MNLI;?`6mfpLsmQ$|AAc$g%&Y6Sk>%>l zmqsEnQkSMBWP4T_Rj?supr(Dp$GNz>``Jn9*szo5K{UK}Xg^N`I zRdM1{!3X-49q_8$5Ay0<@N)Pt@|Tn4s?30l1x~;ufC9|43E(dDyd5*vK2i;5&2qpq zIPhjska#T;6&Pb?E0eS4YSfZ>+iJb<4Dw;SrTGgZuULd(i`_TiogLU~`X(B+`C1PT zn1bu9Z~`<6GKjfpbfBkrRWeww=j;1X+q05swtMg@w4dew0Jb~OC!;quh%{9>s?G|f zF=K8BbumwN@)4}c1oRadEU9Jyr^s-q+QmVAG6A4q^2VxvT(H@KMK<*TsE#q_$7XsnpkU0*rMUbQEZ+?IQRdyfF9jeORN{ zclUrf>G%}>{69W$>SDi)0iBO1vM_e(eUKUNu=qZuOxOHLof z|L1rs{_3;DYIpcQu7Vx*6FZtdOqgF&&}(%Q%)s(dfB9NQYgr!<8q}u8%HWc9S+F(_ zun>X1t`=|Fz!ED+nkXvZ9nQKoDmj~!c&rCovNN3{I#kR_ciI`NBTAMRBD>o( zq^Pi{QA2}?+Bbs8Qe%sFz(QNZo=B)4V5m^jzvls&A_yZ;{A;!E`|qSmdf7oNg&`u~e8lbm9VPJ%JyRZFJBQ>d7H%_FDxw0X^m2wH z(iikHCakQ=ekbEe#dz`F4xaevB3tt^%{WJ+UEzQwB*02T@_QI4LPC}R86JRUfHHjS zG=q77p1E5&Z@DM)Ltdr|O})^^Knf_$5JBwf^e}2Z@huXb99lS*FZVOQ9IcF?E?X{qLWa8l4J-31Y85Y#)io)=Mm)mgiyw#k0$|b2Iaw(BOKr8ru6f*D# z6N~){qNo`6OeT`9K1BK=R)_i0<56+|_NAr(m zGR0m}vMQ8$D!ucZ(>*8$VooQu<3G@gdK&wFMvPzQaiTAJ> zFRD;>L3L81{l5Oj1QFt|r>@7`XDI+<-(r=yT4JMJg9s1>;JaCn$w2K1{adzli?01dcW{z|$@s5Npk#ur0t z>U2pGY*3q5-Bw+g8wN>HHbVy;)24`jv9{cu1C|GD=}q6+(Rlw!n1{{hyB z^NZaG2ZqWYa-^7%NRs3zC0 z%=zr6{_}tP8_B6~>s@p-8gUe|7qaB{aWG=zxTfMqk( znT8)2?Mf@%q$g>sG8YnmD2WTN#gzfibGGX|xnR0-A>Y%#wn^!XqPmgqfhqtF3yEtP$<4vPo26)k+jYdOXO#%2AKg zWEOK!gA;Rs1#T=L{0)apNJ9p!ietd`{5)>khyU%o(flYc!{0Ae33XeeT(%6G04b$B z(6KAg?l0EUOE~$)|-fdlyFA>BT97;dn*r}S}fDDp<& zqoim>;K0M=a7yvyyyVN`@=PjdK}*{84w1x5Fuw<;a*AYWdWyom$@>8{GFU_g4GaGL%dM_x_tC-Ub!oEwW3PA6QwaFp`Y{nU|qD!t}TNh)Gv6A}YG7holfBizBn8VSn zt2j$RvNf+3%~VKz2+sGcmChXrzH>foJm+8x)XsevlU`CR-Xp*Ro_t8!*)f;gb4Za7 z7^0E>aF9R^i(^67#925W6E4)**8%PfdI*i(FZ-Kpy$i@V8_xM8sHul2Jd`0LS|LLL zW@Wbb4i*s~N4r*Ffcu6#&*rCigqUr>eSSeh!uQIPlcJ3WT4Q2=hnbO!&n?^^90Wso z66)NDm9S`Ovc*T;$BjP_8Nj70>S+citdxAubJPOIZAiY2a{C4Th)m98K8qxdT9b=8 z`p|`*oYy1a57nQq7y-Wi;RP$m(zV;Q6Ig_ISe9HY=Hd;V5uMafZ}e(-Fd6iziU~j# zQt7e5yb?z_(5q}gW3>h+ z%Rq&aKu8o8|L7&@+I~eE81yTQDZINc3Dp}3D)N8`UVafX%ov!<{67IM1JV4TjCN(3 zG2jR2<_8V%0%x`YbwFi`j)-2~2=bxq6`*SV{0DaT0H&s5#Mc3C29tjfbd4A8Tn7W9 z9qV}huwT;1e?R~@kfLc22Lw;>2yg>9klBrJ27)lV+OP;|8F_So037gTno0sV;HWWh zbYf6&0S~=6@bJ?0@GF1-1y3a_5b`Ro2RLwMWfRyfZ>2oY$O;hh8`%F4B7g&w3T-$* zU}_NXB+vi{Fau+_!m&Vlc|i37(dVP{bX z=ile2Z2?F92nApP3jc1H7y(X3SOg{ppJupp000PICJXU$+M@=pFo2O9ZIyQ(W6L{DqWPyw5ijlP@_tnO0}xht5~yY z-O9DA*RNp1iXBU~tl6_@)2dy|wyoQ@aO29IOSi7wyLj{J-OIPH-@kwZ3m#0ku;Igq z6DwZKxUu8MkRwZ;Ou4e<%a}83-pskP=g*)+iylq7wCU5RQ>$Lhy0z=quw%=fO}n=3 g+qiS<-p#wW@87_K3m;Crxbfr2lPh1&99$p(JE0$U!vFvP diff --git a/packages/turf-boolean-contains/diagrams/esri-contains.png b/packages/turf-boolean-contains/diagrams/esri-contains.png new file mode 100644 index 0000000000000000000000000000000000000000..519aaa097e3c784cd7434a6199b55bace9d863a1 GIT binary patch literal 6473 zcmZ8j2T;?&v!+WYk!olmAiWDBO%Q*fDM&90A{~@&=uL#c50EZJq?kzW(hUL%8j4Z^ z2na-_mjDU`lp;L+zj<%wy_?y&`F3x1@7vwG*+dg#Jr+h7BLxKoi-A61MnOS|{VVeH z)PFTS;b_jko3Y7F3*=2te~+q}LE_Kl2M#u6Uw-K8{i@;L{_*4N?9AQWJ+k@xA_wiW1=f<@C2&3U!PXmkVlNebmo@={X8-3L*75ZThoRGs?M%WwnfsNaCGu3O&o@;(4;RVy#Em3sgB^H%#a_ge#Uv%WvwMvT<}{90Ek-UgzB&DCe`IZhuA{K zN0&2$cS>)*Fbipf*nbNg`fcut5OPRg3~)>oPiy+j!PotW8ymXwE>2Q=2_HOULjh{6 zCh2b##Cy{aI(3wEp9FB}c+gnp4Urjrl%%wmM3M}9D-PFi!}Uo(JlI~hY0u+Xzy!v9 zcAwx-h3{?*Hqs!7cOB4Trsmou?Yp0NGgv?l@XbXU@ceWjnh8RQ(C3Mda~xOL|3T#W zi~hS>{|fgHJe5A+@xJWU*2MyO-MN#uct6BDa@H1<)_eW~%vYfPq@)tLI6JK4qB`>l zvYtv^`-`}LppXcR>w8{Q42?aGt2O!Tl$5?bI9ShU20+32cczAR)$1A3nzN zubck+e%NSwsK`=Sv3x`u?8<6N~i2CFM24m7|j zd}B2Eg!2VajtZLv`OJ3k%llXIe^Mrof^$<4xRJq<>_iFB#~K90*zK$Zp)v(G-6`SY z;!O_l1Dv}0{L=e)=1~nO#{y4P6Ue0z}u56^uYy=*oQQ;la{D4ihVqW{R3s zbg$EcCxAsfc$p!(+>{o411M?SnY@3t;-&mADa`zEs@D{tG!V>)<+ZccrlEw>zohzq zP4J%-js-|#xW_ROl5i2Gy(ER`dU=X6pQ8cigzw`ahnHx8 zC`A6{{PZl(f2#bp2QN>oA*F%mz!Tj#VXd_9f|pUVL#dtG6Q9?&;suCWlH5VJCy`jf}2bHW|&fb(y*0^4R{V=#92o&RVBk21?c7Ul?AM{ z;ylcy-%R^SfAbHbyskHe5G4t;9SL6JD7o^~`n~OOw^TnZmQxp*i%si$b)}Ak0B2f> zx)Y_IMIVq^CGV6^uu~fCkM|dCij*Q*0HIg*=K(vRH;>5ls5u6RrpBAvczE(l6WeEDv<~@3BI4HbiRcdDMD^ z?X6Y5YAv!PZ`(kKd=k&J(K$Llp#bFLKLtezKXqjb61DTbAe576a9Qguq8#?rS386*Eorcgz~A zZri-vznAjKM{nsd12(ecxvKqLp(@`g?YMfxxL$KLMcYH%u%3mpX0BOn`d2e4;1Cjj z-N$E5pimPlIlK?$pS`mC$Yrb04|ZXYp8T=wi# zL?!jTN~w6#P=EL3RmP7RQ(Q!eqNb%PhgJf*#xIV(TA48M<7u)?~>BX3LKA3ie`=d2*Mu6-4Pm4?~x8AHUawk|; zS}#&4=m#m_SU&uW1=Zfj2++Gqi-J0J6VK*x`;o`*YjmKwVahA%RMqlTGZTM`Fr?8G zL|Dl=h|nu|R?*!FGaG{2;6(K#nw(f+s;0gYAhc&ETk`wDn61k^?lSa}#c%e5NG(K| zIq=eC?xx04`oXVqt?m+~z!fa-k6XW`a04{-c~wkac2n{|;OOVJ)pjb)V()J4=G?AX z&9&LFVGh&mWm(8Y(>N&7bTM$7>bi^bTBaoPEXV$|!ud%-T=$r`DdA#<*qXe45yq zL(eBbXg#Wp#)}af_-H%Q;h-Dp;1Pi(1g> zt~jFtO=kGzQTRJy;o~5k6xAQE3F%A8$#_!F}{oD8mKj`@n-$58vSVB=Bm$ z*=8#_f2pLv$LsU^lMDxdqbB%sO)I%)R|80vu5nO84J^9VuC2&)?m=qD%qw~-6hY1OY=H}vSc_9sq7e_qSS9)nokHu6Nc(>FQF zm+NJmQgT-40xE(1j?PnMWM3QEFJz%oGo>cNzOVsrb4`Ru(&x-5y#e1~6<6yjO;CAv ztZ%+JbBN2&5Hr(#`f*Qqjhyj0+Wy@rMh01K>R_=@YpIyhOeLGsRTtz3xm6mXkWD3O zlhJreIuE8=t)mI-TCpZ#C*N$2aH2c2Zj-DU@77u^g`#$b=sRyuX(agxFw{}h1?6J; z$EK8-UOn7;D>mxgp>whTDoNqbDW>?Sk5@<-3=fPCPbq(;6`H|p`3nAl)aQ3-A1m=| zPmFptOT4*(km_-dZrTepf@X+#jFprS>kHoK(m`y&GDi95*r*yFAKoMk=9%3{&~Q^< zm(Qs(AXh{V46kYFb!{WxpO@>vo=S@J@EV|lkt}x8yXcPP_3HUHTy8ppQoVKxPjLH@ z8&D_-1Xr0(q;XOsl7ya0&}hVjbOoP@G*O)>y;FYb!0@uGf@*z<-J_c{Kr1PnX-ep16{^CU z@Uv#fa#E@Ix$c$ftE4$)*)_2U>VHNoGVUYqL}d(QE%by-CqX!=mJeEn!2CZO^|P&P zBDqYt6=M&BYR;K5gPg6YjF?sS3d77FLW}~*N(N1&^DTA55a_(-erFzs&J`txuVW;e zY2PPtdG_5V3O+9)dp!ZQOMbUiM3G+hDY#-}v$=xLBahSe{_L3X4g~@BcujPj>spW` zz7}F6J1QY&mT#B4M)~j5&yqOkx|%yF{Ne?5*Es769+eQYaN)5Vz}H_dXX|-XLd|B- zwO563+SYxAN&YXzmdO*d(IAkF~KKTKiR6(&CYNB!Cu6C5#6M)~8`20}MCf@IQgoKZ`Mf;WU;Mcf@LPLhc_ z;~+?S&h4Z;ulylabffg(6;~$j_?t^P^x|($*a7JQu0+oG=tK|uFpOeMj*4ut`LDUk zVp&yji~@3Ely&NNw_(KL5R&kmhiLIOPH%u(rH+L|baWRVQXO@27LX@SHw=Y|2N3nL|8F2$UB>E3*J@hz=*nnlRd zWZ3Y_0Lom;-R)yDi;ag)R}lV}jXjMqma@}*G&#?;(+-mLQ@(iDEr`HkP#e0&kd!YR zDz}>zitoo*R9uC1tLrE&*JIKStFK(`=R|OukvY?;|4deO+_2}td1i~=oLPq2jFh{W zMe-FlTt+Q{=$^UJ6!53umt*4Cp+;k7xsj^Ql6F!m|IX-xZ0d;XtV>jv$n6snB1&PI z@EdG~5YtYig#z`S zvJ7N@%TyZ=^n4zJx^fuPjxXJ0Qta#>$5c$4m1-+Rk6{>{zq*9U8;1=kN^(!vLYz7G z9u!*Cq4j_A_2vGubo{E5w43|-^J7#;;79yC&u|fGrs2yAQg;G1@;xpVmNsMvQIN&#!_0Xy=2KuHgtMKY`Xb^sUccN_mS>|Y<&Mb5#;ITBEJRu#4o zjYd~ITh__s%VLV*!p6uRy@^yWLPp4mJ|PE-~s8;5UrA?82}ewFfL3K{IjfBK z1f=Lhs>UDVspUr> z2FIv6m$uCzmC5h}CYx>NJ7j`;DX4f}CiS>{S;BwFeWqXm|IwQpNeB5d$5s}*?z~7< zxCfP4Ro@BfqbxUltK(ZRel_k>GG>RS5#lN$`E1tKIwkO0j=c29OD3BnW+rT%-P8xu z`=^N8ZqwuGggcp?wWo#pd@mvui(5pF^DpdxU;nuAxz6IY>zR(eR7gzGvdj*4YG9)n zNwu75yTOAyCdLmIJk5$ZeFxJE&w{%<&tX!f6oSG@k1u2I4PN@_$-I^Xav>pHU=KK1 zNn26sBso%H@s^1&DT`H8dtf2Z{%I`TO1I?UF*l5KK$8JSqI1;(Fy~YK)|#2^*>|$? zsDmt=-bGG&S2pa5akeN_UAJZ4NTyP-&63uOf-BzSDZ`@pTJZrwX*2oOugxVDSmU;3 z0bVe!vRbYBo(yhW^@gy0w3*|1g@Nzq5WUQBQNyh&JS}j|&`>@;glx-R>s=uO46>Im2a1x#La&$*MFk>C9;dRxK&}tw%5LR|6L=Ix`oMvbLT3yQ?U@4 zIN>f(cp3Zb>;gkClli?E`VrfRT_ZgG?S+^7rS+Frm`fVi$A891-#1S4m#>$vXG#6;|8(1DATfwiE%aU|i{9 zT-DC0cPDK`|oi$VFi?N?WeS(MbW0byB$&CR|nP!DCy>at05>={+DCo{x zn7OBaXPO5ePqODkdN13+;UU^Xqp4!-a}JCfP-QY$~)(@10Dun!C} zih&}w4E-JNMKBN~scPml=ejIajf8gJ{f>VpC_H0-YY5_ba0hZOUfOJfg0Pg2n#JI2 z?_^N^0^YE~H5f9eO~bVA+h3%l$pwE_eu}a8!8`y|;h(dbLWI zn`9L{?oSc#EOxZtU)GVNb8J;7c^hGtIDk}O{SPy#N&;s-!?YUn6tv(y6Et7r0DOQ- z7KDmycMb_R^};Nsf-gHrZbIZt6`_KiIKijdMK$qO=kSI8i^!6dclZ#H1u8$J@Y`CC z^HwKKyKI(80E2xW2^ahQ$3EGDo5;ULdt2*8-s+n7=Q=)1DyjYbqDZ$CQLWgC75s2b zb%#$w^*2XVbjT((H*BnX_TkdwTJe@9mAV>IF6>}MP$%`n)%+6=xPYXhcTT|*eTS!Ne)jw})_vd#bIrk` z36(G8%ap{ok%qcDXrkDD4eP}hftcSm87npRQ%mm`S=nr?r@)z8)i;bSu{Ax=Y1`LK z?nOjTU;A#iHIW6^0;^h6y3u;U`Xk~?c@_3n%hm3=PrGa1Y3Ay|ZI0Su5gP#t=gbsF zuxkYu&!T1FGLTf1M-2{l?&#rtvgAh6Z1>vOioWvot&KZDg| z9M3i7b^Sh}PMq#j|4rmQ&nw>!ciS&}-WD1*>S(t+MQ*N0?lU>SovCHS-88ucI@5JV z2g*CY{lEPOKEjy$fN7-Zew|Q6S;MnLlR*L#A9l>);L$?Oywy(g7~?1m>v&;SVb^^X%MKI`8x|N z1ybcS*mrn~r}H1(b->TQ@82~xY|M^u=G4BnU&||h!ln280a_YnLH%Xwe?Uhd78KVk z#nBQrU7)#Govb&r3#9k5|6YBO&~0u0rrC&ny3@L97*El42hS_@GrSLuoo)w%V|dSP z8cCm)D*P;!J!+_yLNb7hVb2#_8`;bGqspY*67UH3qgHnsQeF1WxB8JHS67~n-^sj6 zhnffZ=BIndLwO*DH?!s5p9`T~5za^e2qj&SKM|VPnNPiM5^l$)bu6QJXvE8;cNI3t zfHlwBVIH|(=#4!uL2y5mp%MNEXXp>B{6~P~x=NRcnmHglD(Qh=8`km&*L&vNj0K!< zKFyvlB*xX|S<PohJq`{GHS7>F$YSpJAA+bv{7xNZhBw#*&d->yYRB61+Cj1E> zs%BvJnP-53OclRGt)*2T!BPv@NQYKY3GgQtrZIu?uj&GFSYa07>CSwtCG-B@i649U zj+pM_@G?@zdadPcMv18p=sw6gCvbqfekoQn1H{6X4QOF;MDILe9u`6aV37%5VnHN& z6YKeNKm-FvEZjEmy_4&%A3gfQSrB3Wf&YbZhZ*l0Oq>t@=n%>ao7@p(SINxF-hFsq z`{I60S4ydXd_u@VU0rr4BlgGe(jN0Edfj{K&yhNq-UbV23Yk<7xAjk*GMoFomGZI* z{XYI1X)TJP8J1bmLhV`9u8u1_8X6jK-$~IK-p0BU9zz?eS_^4Of}gCeWl}VjTPQ|^ zqiOsgj5w4)0SNqdI2aU0X8phR(c$fad(zVQL))uTJV;jAnRM) zp48DbqKE+yroS~VT_QGm>BicX{+Lu4A+~eY3;Vpa@wFfgAAN~&a9X;m7!Ve zH+hBeeO&atVl}?0;nwuRZ4O^GBw53}u1A^&+g<$F;o4&yi&=U}t&U2z{;k_S39(xh|c#Fg{Ji;EmQ-mG|vV8O$rY$NZN->lBMP=XNE6 zCD|(q&S*FIZ(coi$OnzlY3udlou2F*{9DJ4v8ZWN5L zg|=4W>3x(Gv6lTByL^|)5Bq$L-EQNN95|vdqr>=BXvJjd*PsO_H{AMVfUS7a}lCq8W zK1&eJvn^p%nN2f9gNr~YOJMc7d_l4@UvuYvGG^lxJ0GJOmMUy3qL2xf0R6P{>0ML3+Z3%{2|6gPH|xntAS#YhiTrb{`d)L;vrm z&WNV28p5@A6Q0R@bx_0ECENeOIXxDAv-P3{1&u62+V4N5k*J;*<9|j z!8;x#T$U5DQbK1k3y&QK7P&OWQA9#AR?MhRFfZTtCdD^4uhkg|anB6x7~kL9&}3(r zQCM&n0=j=@#x|-q&txrETaJr_2JG-ihZjADyN2{tEiIQwLF@O@qF2ug>QyuurB)K>d6@ejkw1V9oqFZ=I72XX|!>k`nGm{Kivcq#m-_0&QV-ZYu(8^*s&IqK?glPvXgZpd0p?8=GM4< zjCR%RJ*)M}s-R_swTTC;%b5rSeQ@*RudE*aA07WZB0b4TTajHgg1*rdC&-0Oh02Ca z^_+{wMGYpoev-aKn_l%VS*&yRTOdlXm{ZYlgl{lAB?7~QXjCQ2b=rhqOO>$7B3)&_((S|uORfhCl$Y_ z$pZP+eWml#?f5^|ydNySWiywUZ6bq>e#^fZN{*IHzdpMu^2NunF3<*u6K7Y~AfV6z z0)XNA);phhjBXwC`c@HvU(ZOl)sG$^U5EfdzFs*8JhEpcST%#NNqCOUbATLzT>BIR zVBv_a<_IyL#@BCoo7%ZLxXLK&_lYPR?f={$q{H%F{lsK7*d%NE-WM_P!^Znd$*v$$ z3HTtu#%O0PkAQkwJ{Ec&m^D|WeJQL52yY?l!~b0eIwj3w$D*+?;$4T_i^j`}DV3Z1 zOJQF}uE*n~SHyD-WE#fxF4p{@->lESyV*xCq4F518As6@Of($?e{xY+4(gQW2Iy-M z)>DFTPggeA4K$&2by-8CWq0`tedM7Mh3zxb9V~WRTN%_v_%LAT8{-{%dZui`rqbK>tG^YX2c?sxM^8hzDWZP!F%9jF_x9%`tGLP+~a zYR(Ht-}TgaiZfL=w)Enx;w3xlu73)R%zpl>?t*zA3RTBpd|?i90k~?V^vz!Sr>#2@te(7s6UU**f)3x+Kqc~D=`ufj*$b=Z0nbv9h?@d; z!o+~iQ9m)|2GuKok&MIYuc*b%Quxe-Z>!Xj!COh|7cT9k0V2E!(Qwz&as~I4X0RN| zm@REA!E-bXv&Tg&FwbBz7ExG?@+_wC5Ek#@#o{bWQswsH1o-uHkJrZh95MANDSrZ? zVI{_V!0m1!5{cy3qH{QaiCMLYvBiE)JlLfBsp6YPSJYhd5!ITxk<-JnuP=No* zC)1jKIzI-NRvG;S87B<|lF8c?KFF%ryJTA3$kKb-oeu5p4C3U6Abs8$v^LOPysV5q zOL%sRSrJah?KIcL7e!nuh~XtMCSZ}|RM6Uf2H)xm9thCj}w zPSG~j89e`yV)B(2MMAD9qshmH+$62B+fB82Gb3nbA)^AiMCa-6y#-a(rIlVK8`W|D zJ`|%~X9lxHhCHv5{kvzGpPhVYmVJnc{pk=nmv8#NCryH^3h;dLtC6B6KaJ!>{5V%Q zj7@a|lDQM^rr_}nS2Kc<#M&E(jFbj9`S)IYjp|cRY;a%v2i)cUA%(W~%d~v^gK(}Z zbEJ39OJf?#l(V;nux(t@9d{XGLGzf-1qfETrMQcDNRUveA`DL?!S8pz`nsBedE7aW zByQN#wBk|UW+7M#YgYv>+{LNZ7xxoCUfxzklDYDaACoVOz0v*MP(Rc2(FmMH&c-`i zm6;S#=;~v;?$-75=$b+ zpN*YfcT~&b+0rl-%?TlAJU3$$$#RU##ygP8`o*Wc5ORC$WAQma{>UgFco1)?v@3WFdl8 z16~$4MwDKK^|4n_YF0C4?V3|L-e)p*h+fyd{#bP+; z^jXO0)(JrhOn!UD$2zsez>{72PR!&ZO4+a3o&Tn7=;VmHX6bo}B0_YtlgvTAGSt#M z@SvpY+lZ}aoi;ced$W@NT2mxT%NKr;%vtg8Q31MY@0dWOja0rtPXVLSaI0CVcGHVl z!vuX_xBAM_O)hIJ=KO83$+qV_Klwlet=s0DPx}WRp4T<6dP;wAvhu>M9Ia@UBszIW z5GEfYbXtrr^QILE%DWq|c(WBsi0>pDi((=U=ZdjxNh4ol^nIHwu$^~;i{3rHYc+nA(PBg>;^Uq8Da z2mGzN=!CWQIwe^PqXI^8@~(&8+YP_s<;E^vT?y$Nk%ajOo|2 zJo=Hl<>ZbDg2{81$q~=eHJwihu~5Z&d)$0lip<{Z!r>^9V-^0oLe(~;M$&D<;JTqc zM6uotM8W$MZ0IeT%MET(-o1oFqc3&l7R@K)rMMO#(A^{9My~{C72EHm*ZNmjbiEkP zj+mW#OBn7%f1Mw?tG0-1;;Ru`=OK)Z1PqTDo4gS7fr@|?z- z2j}J1yjn?o#Y*HFyDGOcZzXrazra?%jD`j>rmHznWZf%xPSfL`zQp;l?A3&tVlLv= zxfW!{{j!^6z0n^#R{Oh-M!3LhxcrQItxDS+&(7g=E;)8QTQnsMdUKfxwPOtQg}I?d zN|gWF`l5D5`_rVErACzQ&K%8;iVz4fwGIvq0^@fs{|QB_j-Gz6Vl<_0<5Xh93cMb8fzr zG^)88uMV?FWoR#hTUq zkww*TLn=aaZC*Tm%A$p9@;0+Z7u8(u;Q&_voy8pRJ)=G;raw|XF0dsG>ZOqRXaGzyeNl+og1arCS;dgQ$|4kfDWcx_HS18H=@`%U0;ES0q zZ*e8_rKMOH@XBc8lT5=Vy7xc?SzzJM)i8KL{ld+)MQ1(ZR+jYlwNEfi8f#Eid^`A# z9+1e@TgWR%Z;}NcX5^sCQJ!G#{VO(j_y`v&)x!@lkLZ2W{c0!b*s#I$+fddIk4I^= z2-{uG7P`g1pCL)rt)S@= zxE^`z&T^%*N1LsQ{p(0(vnlp7n}7Z5!ESS-+VBRxeFl%cb%fuA1w6u)L3S1Hg+Nq{ zTI@4HiTo+*2ex0;(H-MfCDxFAt4;=OKlBREKTpj`XCL z55aOc&jBs?ks7*NWEUO;D;%|$=&U~5JT=rB4PIuTAv^$zRCH|z5m=|mMhJPV@7&qP z%IStGYt9kbbxKJwLd;ZV(GJmb%=A1DU~qA=af%Sb;D+DwJ>yO9?B&dJx9)>Rf}38%wKJFt=}BqO)c ztM}2rek3rsT5CQj&01QMS0mu*ynm-KNOWsPVvH{bnF0V3Zz=p=-I9P$rbL#i%tlpb zGlwKyg&ndHc;OZ+LtsCnVL1{jz6I}Wf=3#ZibG68R+;cEaTpzZzh|AaU9zz=hyfhR zN|Jhmoox2EmpqT!xe5?(XNc1&UhM|~bJ|75e`H?SgI#=YJag!2xhesh6vTGR z%)5Q9%qnnOs=(5a!tk*&$?ZQot7=$CNmi$4Eo^HUMVY^PS6J|NLZ7B03KPRr<$Ggj m*{aS1|22FG+lVO1t<_VNQrn1)D-a9N8R_eq0F~MgqW&Lb-}c1- literal 0 HcmV?d00001 diff --git a/packages/turf-boolean-within/digrams/esri-within.gif b/packages/turf-boolean-within/digrams/esri-within.gif deleted file mode 100644 index 12687ebc0947eee026e92f75ee5954094bdbe789..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32889 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≤M0Orx@A-KSsDbY(G9e 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�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 From 103dd1c28f0db960c43744253a2ab6370778bfab Mon Sep 17 00:00:00 2001 From: samuelarbibe Date: Thu, 27 Feb 2025 11:44:56 +0200 Subject: [PATCH 3/7] add contributor --- packages/turf-boolean-contains/package.json | 3 ++- packages/turf-boolean-within/package.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/turf-boolean-contains/package.json b/packages/turf-boolean-contains/package.json index 5e714fb785..34d2502fc8 100644 --- a/packages/turf-boolean-contains/package.json +++ b/packages/turf-boolean-contains/package.json @@ -5,7 +5,8 @@ "author": "Turf Authors", "contributors": [ "Rowan Winsemius <@rowanwins>", - "Denis Carriere <@DenisCarriere>" + "Denis Carriere <@DenisCarriere>", + "Samuel Arbibe <@samuelarbibe>" ], "license": "MIT", "bugs": { diff --git a/packages/turf-boolean-within/package.json b/packages/turf-boolean-within/package.json index 4953096026..85a4fee6a1 100644 --- a/packages/turf-boolean-within/package.json +++ b/packages/turf-boolean-within/package.json @@ -4,7 +4,8 @@ "description": "Determines whether the first geometry is completely within the second geometry.", "author": "Turf Authors", "contributors": [ - "Rowan Winsemius <@rowanwins>" + "Rowan Winsemius <@rowanwins>", + "Samuel Arbibe <@samuelarbibe>" ], "license": "MIT", "bugs": { From 6a737d439769de02679a4a2eafeb1af9bdbdeea7 Mon Sep 17 00:00:00 2001 From: samuelarbibe Date: Thu, 27 Feb 2025 19:26:28 +0200 Subject: [PATCH 4/7] check if line is contained in boundary after splitting on intersections --- packages/turf-boolean-contains/index.ts | 30 ++++++++++--------------- packages/turf-boolean-within/index.ts | 30 ++++++++++--------------- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/packages/turf-boolean-contains/index.ts b/packages/turf-boolean-contains/index.ts index 9e3ae225a6..093d390fbb 100644 --- a/packages/turf-boolean-contains/index.ts +++ b/packages/turf-boolean-contains/index.ts @@ -187,27 +187,13 @@ function isLineInPoly(polygon: Polygon, linestring: LineString) { return false; } - let isContainedByPolygonBoundary = false; - - for (let i = 0; i < linestring.coordinates.length - 1; i++) { - const coord1 = linestring.coordinates[i]; - - if (!booleanPointInPolygon(coord1, polygon)) { + for (const coord of linestring.coordinates) { + if (!booleanPointInPolygon(coord, polygon)) { return false; } - - if (isContainedByPolygonBoundary) continue; - - const coord2 = linestring.coordinates[i + 1]; - const midpoint = getMidpoint(coord1, coord2); - - if (booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true })) { - isContainedByPolygonBoundary = true; - } } - if (!isContainedByPolygonBoundary) return false; - + let isContainedByPolygonBoundary = false; const lineSegments = lineSplit(feature(linestring), feature(polygon)); for (const lineSegment of lineSegments.features) { @@ -215,12 +201,20 @@ function isLineInPoly(polygon: Polygon, linestring: LineString) { lineSegment.geometry.coordinates[0], lineSegment.geometry.coordinates[1] ); + if (!booleanPointInPolygon(midpoint, polygon)) { return false; } + + if ( + !isContainedByPolygonBoundary && + booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true }) + ) { + isContainedByPolygonBoundary = true; + } } - return true; + return isContainedByPolygonBoundary; } /** diff --git a/packages/turf-boolean-within/index.ts b/packages/turf-boolean-within/index.ts index d0f12142b8..2f8bcbdf51 100644 --- a/packages/turf-boolean-within/index.ts +++ b/packages/turf-boolean-within/index.ts @@ -175,27 +175,13 @@ function isLineInPoly(linestring: LineString, polygon: Polygon) { return false; } - let isContainedByPolygonBoundary = false; - - for (let i = 0; i < linestring.coordinates.length - 1; i++) { - const coord1 = linestring.coordinates[i]; - - if (!booleanPointInPolygon(coord1, polygon)) { + for (const coord of linestring.coordinates) { + if (!booleanPointInPolygon(coord, polygon)) { return false; } - - if (isContainedByPolygonBoundary) continue; - - const coord2 = linestring.coordinates[i + 1]; - const midpoint = getMidpoint(coord1, coord2); - - if (booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true })) { - isContainedByPolygonBoundary = true; - } } - if (!isContainedByPolygonBoundary) return false; - + let isWithinPolygonBoundary = false; const lineSegments = lineSplit(feature(linestring), feature(polygon)); for (const lineSegment of lineSegments.features) { @@ -203,12 +189,20 @@ function isLineInPoly(linestring: LineString, polygon: Polygon) { lineSegment.geometry.coordinates[0], lineSegment.geometry.coordinates[1] ); + if (!booleanPointInPolygon(midpoint, polygon)) { return false; } + + if ( + !isWithinPolygonBoundary && + booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true }) + ) { + isWithinPolygonBoundary = true; + } } - return true; + return isWithinPolygonBoundary; } /** From e474cf7879ec9efe280505b4ee5433bbee9963c0 Mon Sep 17 00:00:00 2001 From: Samuel Arbibe Date: Fri, 28 Nov 2025 15:13:57 +0200 Subject: [PATCH 5/7] fix line simply containes in polygon, and tidy up test examples --- packages/turf-boolean-contains/index.ts | 30 ++++++++------ .../LineIsNotContainedByPolygon.geojson | 0 ...ineIsNotContainedByPolygonBoundary.geojson | 0 .../LineIsNotFullyContainedByPolygon.geojson | 0 .../LineIsContainedByPolygon.geojson | 0 ...nedByPolygonWithNoInternalVertices.geojson | 0 .../LineIsFullyContainedByPolygon.geojson | 32 +++++++++++++++ .../LineString/issue-#1201-true.geojson | 40 ------------------- packages/turf-boolean-within/index.ts | 30 ++++++++------ .../LineIsFullyContainedByPolygon.geojson | 32 +++++++++++++++ pnpm-lock.yaml | 4 +- 11 files changed, 102 insertions(+), 66 deletions(-) rename packages/turf-boolean-contains/test/false/{LineString/Polygon => Polygon/LineString}/LineIsNotContainedByPolygon.geojson (100%) rename packages/turf-boolean-contains/test/false/{LineString/Polygon => Polygon/LineString}/LineIsNotContainedByPolygonBoundary.geojson (100%) rename packages/turf-boolean-contains/test/false/{LineString/Polygon => Polygon/LineString}/LineIsNotFullyContainedByPolygon.geojson (100%) rename packages/turf-boolean-contains/test/true/{LineString/Polygon => Polygon/LineString}/LineIsContainedByPolygon.geojson (100%) rename packages/turf-boolean-contains/test/true/{LineString/Polygon => Polygon/LineString}/LineIsContainedByPolygonWithNoInternalVertices.geojson (100%) create mode 100644 packages/turf-boolean-contains/test/true/Polygon/LineString/LineIsFullyContainedByPolygon.geojson delete mode 100644 packages/turf-boolean-contains/test/true/Polygon/LineString/issue-#1201-true.geojson create mode 100644 packages/turf-boolean-within/test/true/LineString/Polygon/LineIsFullyContainedByPolygon.geojson diff --git a/packages/turf-boolean-contains/index.ts b/packages/turf-boolean-contains/index.ts index 093d390fbb..47c42e6ea9 100644 --- a/packages/turf-boolean-contains/index.ts +++ b/packages/turf-boolean-contains/index.ts @@ -12,7 +12,7 @@ import { bbox as calcBbox } from "@turf/bbox"; import { booleanPointInPolygon } from "@turf/boolean-point-in-polygon"; import { booleanPointOnLine as isPointOnLine } from "@turf/boolean-point-on-line"; import { getGeom } from "@turf/invariant"; -import { feature } from "@turf/helpers"; +import { feature, featureCollection, lineString } from "@turf/helpers"; import { lineSplit } from "@turf/line-split"; /** @@ -179,6 +179,16 @@ function isLineOnLine(lineString1: LineString, lineString2: LineString) { return haveFoundInteriorPoint; } +function splitLineIntoSegments(linestring: LineString) { + const coords = linestring.coordinates; + + const segments = coords + .slice(1) + .map((coord, i) => lineString([coords[i], coord])); + + return featureCollection(segments); +} + function isLineInPoly(polygon: Polygon, linestring: LineString) { const polyBbox = calcBbox(polygon); const lineBbox = calcBbox(linestring); @@ -193,8 +203,11 @@ function isLineInPoly(polygon: Polygon, linestring: LineString) { } } - let isContainedByPolygonBoundary = false; - const lineSegments = lineSplit(feature(linestring), feature(polygon)); + let lineSegments = lineSplit(feature(linestring), feature(polygon)); + + if (!lineSegments.features.length) { + lineSegments = splitLineIntoSegments(linestring); + } for (const lineSegment of lineSegments.features) { const midpoint = getMidpoint( @@ -202,19 +215,12 @@ function isLineInPoly(polygon: Polygon, linestring: LineString) { lineSegment.geometry.coordinates[1] ); - if (!booleanPointInPolygon(midpoint, polygon)) { + if (!booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true })) { return false; } - - if ( - !isContainedByPolygonBoundary && - booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true }) - ) { - isContainedByPolygonBoundary = true; - } } - return isContainedByPolygonBoundary; + return true; } /** diff --git a/packages/turf-boolean-contains/test/false/LineString/Polygon/LineIsNotContainedByPolygon.geojson b/packages/turf-boolean-contains/test/false/Polygon/LineString/LineIsNotContainedByPolygon.geojson similarity index 100% rename from packages/turf-boolean-contains/test/false/LineString/Polygon/LineIsNotContainedByPolygon.geojson rename to packages/turf-boolean-contains/test/false/Polygon/LineString/LineIsNotContainedByPolygon.geojson diff --git a/packages/turf-boolean-contains/test/false/LineString/Polygon/LineIsNotContainedByPolygonBoundary.geojson b/packages/turf-boolean-contains/test/false/Polygon/LineString/LineIsNotContainedByPolygonBoundary.geojson similarity index 100% rename from packages/turf-boolean-contains/test/false/LineString/Polygon/LineIsNotContainedByPolygonBoundary.geojson rename to packages/turf-boolean-contains/test/false/Polygon/LineString/LineIsNotContainedByPolygonBoundary.geojson diff --git a/packages/turf-boolean-contains/test/false/LineString/Polygon/LineIsNotFullyContainedByPolygon.geojson b/packages/turf-boolean-contains/test/false/Polygon/LineString/LineIsNotFullyContainedByPolygon.geojson similarity index 100% rename from packages/turf-boolean-contains/test/false/LineString/Polygon/LineIsNotFullyContainedByPolygon.geojson rename to packages/turf-boolean-contains/test/false/Polygon/LineString/LineIsNotFullyContainedByPolygon.geojson diff --git a/packages/turf-boolean-contains/test/true/LineString/Polygon/LineIsContainedByPolygon.geojson b/packages/turf-boolean-contains/test/true/Polygon/LineString/LineIsContainedByPolygon.geojson similarity index 100% rename from packages/turf-boolean-contains/test/true/LineString/Polygon/LineIsContainedByPolygon.geojson rename to packages/turf-boolean-contains/test/true/Polygon/LineString/LineIsContainedByPolygon.geojson diff --git a/packages/turf-boolean-contains/test/true/LineString/Polygon/LineIsContainedByPolygonWithNoInternalVertices.geojson b/packages/turf-boolean-contains/test/true/Polygon/LineString/LineIsContainedByPolygonWithNoInternalVertices.geojson similarity index 100% rename from packages/turf-boolean-contains/test/true/LineString/Polygon/LineIsContainedByPolygonWithNoInternalVertices.geojson rename to packages/turf-boolean-contains/test/true/Polygon/LineString/LineIsContainedByPolygonWithNoInternalVertices.geojson diff --git a/packages/turf-boolean-contains/test/true/Polygon/LineString/LineIsFullyContainedByPolygon.geojson b/packages/turf-boolean-contains/test/true/Polygon/LineString/LineIsFullyContainedByPolygon.geojson new file mode 100644 index 0000000000..478080637d --- /dev/null +++ b/packages/turf-boolean-contains/test/true/Polygon/LineString/LineIsFullyContainedByPolygon.geojson @@ -0,0 +1,32 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [2, 3], + [2, 3.5] + ] + } + } + ] +} diff --git a/packages/turf-boolean-contains/test/true/Polygon/LineString/issue-#1201-true.geojson b/packages/turf-boolean-contains/test/true/Polygon/LineString/issue-#1201-true.geojson deleted file mode 100644 index 6c5dad23f5..0000000000 --- a/packages/turf-boolean-contains/test/true/Polygon/LineString/issue-#1201-true.geojson +++ /dev/null @@ -1,40 +0,0 @@ -{ - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "properties": { - "stroke": "#0000ff", - "stroke-width": 6, - "stroke-opacity": 1, - "fill": "#0000ff", - "fill-opacity": 0.3 - }, - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [98.8769531, 35.3173663], - [83.144531, 14.349547], - [111.621093, 14.8598504], - [98.8769531, 35.3173663] - ] - ] - } - }, - { - "type": "Feature", - "properties": { - "stroke": "#F00", - "stroke-width": 6 - }, - "geometry": { - "type": "LineString", - "coordinates": [ - [111.621093, 14.8598504], - [98.8769531, 30.3173663] - ] - } - } - ] -} diff --git a/packages/turf-boolean-within/index.ts b/packages/turf-boolean-within/index.ts index 2f8bcbdf51..107a62ea4b 100644 --- a/packages/turf-boolean-within/index.ts +++ b/packages/turf-boolean-within/index.ts @@ -12,7 +12,7 @@ import { bbox as calcBbox } from "@turf/bbox"; import { booleanPointOnLine } from "@turf/boolean-point-on-line"; import { booleanPointInPolygon } from "@turf/boolean-point-in-polygon"; import { getGeom } from "@turf/invariant"; -import { feature } from "@turf/helpers"; +import { feature, featureCollection, lineString } from "@turf/helpers"; import { lineSplit } from "@turf/line-split"; /** @@ -167,6 +167,16 @@ function isLineOnLine(lineString1: LineString, lineString2: LineString) { return true; } +function splitLineIntoSegments(linestring: LineString) { + const coords = linestring.coordinates; + + const segments = coords + .slice(1) + .map((coord, i) => lineString([coords[i], coord])); + + return featureCollection(segments); +} + function isLineInPoly(linestring: LineString, polygon: Polygon) { const polyBbox = calcBbox(polygon); const lineBbox = calcBbox(linestring); @@ -181,8 +191,11 @@ function isLineInPoly(linestring: LineString, polygon: Polygon) { } } - let isWithinPolygonBoundary = false; - const lineSegments = lineSplit(feature(linestring), feature(polygon)); + let lineSegments = lineSplit(feature(linestring), feature(polygon)); + + if (!lineSegments.features.length) { + lineSegments = splitLineIntoSegments(linestring); + } for (const lineSegment of lineSegments.features) { const midpoint = getMidpoint( @@ -190,19 +203,12 @@ function isLineInPoly(linestring: LineString, polygon: Polygon) { lineSegment.geometry.coordinates[1] ); - if (!booleanPointInPolygon(midpoint, polygon)) { + if (!booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true })) { return false; } - - if ( - !isWithinPolygonBoundary && - booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true }) - ) { - isWithinPolygonBoundary = true; - } } - return isWithinPolygonBoundary; + return true; } /** diff --git a/packages/turf-boolean-within/test/true/LineString/Polygon/LineIsFullyContainedByPolygon.geojson b/packages/turf-boolean-within/test/true/LineString/Polygon/LineIsFullyContainedByPolygon.geojson new file mode 100644 index 0000000000..f2dcc17b72 --- /dev/null +++ b/packages/turf-boolean-within/test/true/LineString/Polygon/LineIsFullyContainedByPolygon.geojson @@ -0,0 +1,32 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [2, 3], + [2, 3.5] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + } + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8cc9c8e20c..358a037d80 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -965,7 +965,7 @@ importers: specifier: workspace:* version: link:../turf-invariant '@turf/line-split': - specifier: workspace:^ + specifier: workspace:* version: link:../turf-line-split '@types/geojson': specifier: ^7946.0.10 @@ -1552,7 +1552,7 @@ importers: specifier: workspace:* version: link:../turf-invariant '@turf/line-split': - specifier: workspace:^ + specifier: workspace:* version: link:../turf-line-split '@types/geojson': specifier: ^7946.0.10 From 0ebfa939dbb7dafbafa65a68b372992c27151314 Mon Sep 17 00:00:00 2001 From: Samuel Arbibe Date: Fri, 28 Nov 2025 16:11:38 +0200 Subject: [PATCH 6/7] split linestring segments on polygon before checking midpoints --- packages/turf-boolean-contains/index.ts | 40 +++++++++++++------ ...inedByPolygonWithSegmentOnBoundary.geojson | 33 +++++++++++++++ packages/turf-boolean-within/index.ts | 40 +++++++++++++------ ...inedByPolygonWithSegmentOnBoundary.geojson | 33 +++++++++++++++ 4 files changed, 122 insertions(+), 24 deletions(-) create mode 100644 packages/turf-boolean-contains/test/true/Polygon/LineString/LineIsContainedByPolygonWithSegmentOnBoundary.geojson create mode 100644 packages/turf-boolean-within/test/true/LineString/Polygon/LineIsContainedByPolygonWithSegmentOnBoundary.geojson diff --git a/packages/turf-boolean-contains/index.ts b/packages/turf-boolean-contains/index.ts index 47c42e6ea9..b16fed95b9 100644 --- a/packages/turf-boolean-contains/index.ts +++ b/packages/turf-boolean-contains/index.ts @@ -179,14 +179,26 @@ function isLineOnLine(lineString1: LineString, lineString2: LineString) { return haveFoundInteriorPoint; } -function splitLineIntoSegments(linestring: LineString) { +function splitLineIntoSegmentsOnPolygon( + linestring: LineString, + polygon: Polygon +) { const coords = linestring.coordinates; - const segments = coords - .slice(1) - .map((coord, i) => lineString([coords[i], coord])); + const outputSegments: Feature[] = []; + + for (let i = 0; i < coords.length - 1; i++) { + const seg = lineString([coords[i], coords[i + 1]]); + const split = lineSplit(seg, feature(polygon)); + + if (split.features.length === 0) { + outputSegments.push(seg); + } else { + outputSegments.push(...split.features); + } + } - return featureCollection(segments); + return featureCollection(outputSegments); } function isLineInPoly(polygon: Polygon, linestring: LineString) { @@ -203,11 +215,8 @@ function isLineInPoly(polygon: Polygon, linestring: LineString) { } } - let lineSegments = lineSplit(feature(linestring), feature(polygon)); - - if (!lineSegments.features.length) { - lineSegments = splitLineIntoSegments(linestring); - } + let isContainedByPolygonBoundary = false; + const lineSegments = splitLineIntoSegmentsOnPolygon(linestring, polygon); for (const lineSegment of lineSegments.features) { const midpoint = getMidpoint( @@ -215,12 +224,19 @@ function isLineInPoly(polygon: Polygon, linestring: LineString) { lineSegment.geometry.coordinates[1] ); - if (!booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true })) { + if (!booleanPointInPolygon(midpoint, polygon)) { return false; } + + if ( + !isContainedByPolygonBoundary && + booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true }) + ) { + isContainedByPolygonBoundary = true; + } } - return true; + return isContainedByPolygonBoundary; } /** diff --git a/packages/turf-boolean-contains/test/true/Polygon/LineString/LineIsContainedByPolygonWithSegmentOnBoundary.geojson b/packages/turf-boolean-contains/test/true/Polygon/LineString/LineIsContainedByPolygonWithSegmentOnBoundary.geojson new file mode 100644 index 0000000000..892b32dd48 --- /dev/null +++ b/packages/turf-boolean-contains/test/true/Polygon/LineString/LineIsContainedByPolygonWithSegmentOnBoundary.geojson @@ -0,0 +1,33 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 1], + [1, 2], + [2, 3] + ] + } + } + ] +} diff --git a/packages/turf-boolean-within/index.ts b/packages/turf-boolean-within/index.ts index 107a62ea4b..a3fc7a7a9f 100644 --- a/packages/turf-boolean-within/index.ts +++ b/packages/turf-boolean-within/index.ts @@ -167,14 +167,26 @@ function isLineOnLine(lineString1: LineString, lineString2: LineString) { return true; } -function splitLineIntoSegments(linestring: LineString) { +function splitLineIntoSegmentsOnPolygon( + linestring: LineString, + polygon: Polygon +) { const coords = linestring.coordinates; - const segments = coords - .slice(1) - .map((coord, i) => lineString([coords[i], coord])); + const outputSegments: Feature[] = []; + + for (let i = 0; i < coords.length - 1; i++) { + const seg = lineString([coords[i], coords[i + 1]]); + const split = lineSplit(seg, feature(polygon)); + + if (split.features.length === 0) { + outputSegments.push(seg); + } else { + outputSegments.push(...split.features); + } + } - return featureCollection(segments); + return featureCollection(outputSegments); } function isLineInPoly(linestring: LineString, polygon: Polygon) { @@ -191,11 +203,8 @@ function isLineInPoly(linestring: LineString, polygon: Polygon) { } } - let lineSegments = lineSplit(feature(linestring), feature(polygon)); - - if (!lineSegments.features.length) { - lineSegments = splitLineIntoSegments(linestring); - } + let isContainedByPolygonBoundary = false; + const lineSegments = splitLineIntoSegmentsOnPolygon(linestring, polygon); for (const lineSegment of lineSegments.features) { const midpoint = getMidpoint( @@ -203,12 +212,19 @@ function isLineInPoly(linestring: LineString, polygon: Polygon) { lineSegment.geometry.coordinates[1] ); - if (!booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true })) { + if (!booleanPointInPolygon(midpoint, polygon)) { return false; } + + if ( + !isContainedByPolygonBoundary && + booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true }) + ) { + isContainedByPolygonBoundary = true; + } } - return true; + return isContainedByPolygonBoundary; } /** diff --git a/packages/turf-boolean-within/test/true/LineString/Polygon/LineIsContainedByPolygonWithSegmentOnBoundary.geojson b/packages/turf-boolean-within/test/true/LineString/Polygon/LineIsContainedByPolygonWithSegmentOnBoundary.geojson new file mode 100644 index 0000000000..7e72774099 --- /dev/null +++ b/packages/turf-boolean-within/test/true/LineString/Polygon/LineIsContainedByPolygonWithSegmentOnBoundary.geojson @@ -0,0 +1,33 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [1, 1], + [1, 2], + [2, 3] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [1, 1], + [1, 10], + [10, 10], + [10, 1], + [1, 1] + ] + ] + } + } + ] +} From 8a14d8239eac9f9982545b6b26443f4d03b7f3cf Mon Sep 17 00:00:00 2001 From: Samuel Arbibe Date: Thu, 4 Dec 2025 14:44:07 +0200 Subject: [PATCH 7/7] add explanatory comments and update docs --- packages/turf-boolean-contains/README.md | 7 +++---- packages/turf-boolean-contains/index.ts | 10 ++++++---- packages/turf-boolean-within/README.md | 7 +++---- packages/turf-boolean-within/index.ts | 10 ++++++---- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/turf-boolean-contains/README.md b/packages/turf-boolean-contains/README.md index 807c150d06..82317ef264 100644 --- a/packages/turf-boolean-contains/README.md +++ b/packages/turf-boolean-contains/README.md @@ -4,10 +4,9 @@ ## booleanContains -Boolean-contains returns True if the second geometry is completely contained by the first geometry. -The interiors of both geometries must intersect and, the interior and boundary of the secondary (geometry b) -must not intersect the exterior of the primary (geometry a). -Boolean-contains returns the exact opposite result of the `@turf/boolean-within`. +Tests whether geometry a contains geometry b. +The interiors of both geometries must intersect, and the interior and boundary of geometry b must not intersect the exterior of geometry a. +booleanContains(a, b) is equivalent to booleanWithin(b, a) ### Parameters diff --git a/packages/turf-boolean-contains/index.ts b/packages/turf-boolean-contains/index.ts index b16fed95b9..5e845d044d 100644 --- a/packages/turf-boolean-contains/index.ts +++ b/packages/turf-boolean-contains/index.ts @@ -16,10 +16,9 @@ import { feature, featureCollection, lineString } from "@turf/helpers"; import { lineSplit } from "@turf/line-split"; /** - * Boolean-contains returns True if the second geometry is completely contained by the first geometry. - * The interiors of both geometries must intersect and, the interior and boundary of the secondary (geometry b) - * must not intersect the exterior of the primary (geometry a). - * Boolean-contains returns the exact opposite result of the `@turf/boolean-within`. + * Tests whether geometry a contains geometry b. + * The interiors of both geometries must intersect, and the interior and boundary of geometry b must not intersect the exterior of geometry a. + * booleanContains(a, b) is equivalent to booleanWithin(b, a) * * @function * @param {Geometry|Feature} feature1 GeoJSON Feature or Geometry @@ -216,6 +215,7 @@ function isLineInPoly(polygon: Polygon, linestring: LineString) { } let isContainedByPolygonBoundary = false; + // split intersecting segments and verify their inclusion const lineSegments = splitLineIntoSegmentsOnPolygon(linestring, polygon); for (const lineSegment of lineSegments.features) { @@ -224,10 +224,12 @@ function isLineInPoly(polygon: Polygon, linestring: LineString) { lineSegment.geometry.coordinates[1] ); + // make sure all segments do not intersect with polygon exterior if (!booleanPointInPolygon(midpoint, polygon)) { return false; } + // make sure at least 1 segment intersects with the polygon's interior if ( !isContainedByPolygonBoundary && booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true }) diff --git a/packages/turf-boolean-within/README.md b/packages/turf-boolean-within/README.md index 5a5710a261..0d8f7c4a13 100644 --- a/packages/turf-boolean-within/README.md +++ b/packages/turf-boolean-within/README.md @@ -4,10 +4,9 @@ ## booleanWithin -Boolean-within returns true if the first geometry is completely within the second geometry. -The interiors of both geometries must intersect and, the interior and boundary of the primary (geometry a) -must not intersect the exterior of the secondary (geometry b). -Boolean-within returns the exact opposite result of the `@turf/boolean-contains`. +Tests whether geometry a is contained by geometry b. +The interiors of both geometries must intersect, and the interior and boundary of geometry a must not intersect the exterior of geometry b. +booleanWithin(a, b) is equivalent to booleanContains(b, a) ### Parameters diff --git a/packages/turf-boolean-within/index.ts b/packages/turf-boolean-within/index.ts index a3fc7a7a9f..900a1c93a7 100644 --- a/packages/turf-boolean-within/index.ts +++ b/packages/turf-boolean-within/index.ts @@ -16,10 +16,9 @@ import { feature, featureCollection, lineString } from "@turf/helpers"; import { lineSplit } from "@turf/line-split"; /** - * Boolean-within returns true if the first geometry is completely within the second geometry. - * The interiors of both geometries must intersect and, the interior and boundary of the primary (geometry a) - * must not intersect the exterior of the secondary (geometry b). - * Boolean-within returns the exact opposite result of the `@turf/boolean-contains`. + * Tests whether geometry a is contained by geometry b. + * The interiors of both geometries must intersect, and the interior and boundary of geometry a must not intersect the exterior of geometry b. + * booleanWithin(a, b) is equivalent to booleanContains(b, a) * * @function * @param {Geometry|Feature} feature1 GeoJSON Feature or Geometry @@ -204,6 +203,7 @@ function isLineInPoly(linestring: LineString, polygon: Polygon) { } let isContainedByPolygonBoundary = false; + // split intersecting segments and verify their inclusion const lineSegments = splitLineIntoSegmentsOnPolygon(linestring, polygon); for (const lineSegment of lineSegments.features) { @@ -212,10 +212,12 @@ function isLineInPoly(linestring: LineString, polygon: Polygon) { lineSegment.geometry.coordinates[1] ); + // make sure all segments do not intersect with polygon exterior if (!booleanPointInPolygon(midpoint, polygon)) { return false; } + // make sure at least 1 segment intersects with the polygon's interior if ( !isContainedByPolygonBoundary && booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true })