Skip to content

Commit 57536ea

Browse files
Merge branch 'master' into mf/upgrade-lerna
2 parents a94f4e9 + 5c4b040 commit 57536ea

33 files changed

+629
-99
lines changed

packages/turf-boolean-contains/README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44

55
## booleanContains
66

7-
Boolean-contains returns True if the second geometry is completely contained by the first geometry.
8-
The interiors of both geometries must intersect and, the interior and boundary of the secondary (geometry b)
9-
must not intersect the exterior of the primary (geometry a).
10-
Boolean-contains returns the exact opposite result of the `@turf/boolean-within`.
7+
Tests whether geometry a contains geometry b.
8+
The interiors of both geometries must intersect, and the interior and boundary of geometry b must not intersect the exterior of geometry a.
9+
booleanContains(a, b) is equivalent to booleanWithin(b, a)
1110

1211
### Parameters
1312

@@ -55,4 +54,4 @@ $ npm install @turf/turf
5554

5655
### Diagrams
5756

58-
![esri-contains](diagrams/esri-contains.gif)
57+
![esri-contains](diagrams/esri-contains.png)
-33.3 KB
Binary file not shown.
6.32 KB
Loading

packages/turf-boolean-contains/index.ts

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import { bbox as calcBbox } from "@turf/bbox";
1212
import { booleanPointInPolygon } from "@turf/boolean-point-in-polygon";
1313
import { booleanPointOnLine as isPointOnLine } from "@turf/boolean-point-on-line";
1414
import { getGeom } from "@turf/invariant";
15+
import { feature, featureCollection, lineString } from "@turf/helpers";
16+
import { lineSplit } from "@turf/line-split";
1517

1618
/**
17-
* Boolean-contains returns True if the second geometry is completely contained by the first geometry.
18-
* The interiors of both geometries must intersect and, the interior and boundary of the secondary (geometry b)
19-
* must not intersect the exterior of the primary (geometry a).
20-
* Boolean-contains returns the exact opposite result of the `@turf/boolean-within`.
19+
* Tests whether geometry a contains geometry b.
20+
* The interiors of both geometries must intersect, and the interior and boundary of geometry b must not intersect the exterior of geometry a.
21+
* booleanContains(a, b) is equivalent to booleanWithin(b, a)
2122
*
2223
* @function
2324
* @param {Geometry|Feature<any>} feature1 GeoJSON Feature or Geometry
@@ -79,6 +80,8 @@ function booleanContains(
7980
return isPolyInPoly(geom1, geom2);
8081
case "MultiPoint":
8182
return isMultiPointInPoly(geom1, geom2);
83+
case "MultiPolygon":
84+
return isMultiPolyInPoly(geom1, geom2);
8285
default:
8386
throw new Error("feature2 " + type2 + " geometry not supported");
8487
}
@@ -100,6 +103,12 @@ function isPolygonInMultiPolygon(multiPolygon: MultiPolygon, polygon: Polygon) {
100103
);
101104
}
102105

106+
function isMultiPolyInPoly(polygon: Polygon, multiPolygon: MultiPolygon) {
107+
return multiPolygon.coordinates.every((coords) =>
108+
isPolyInPoly(polygon, { type: "Polygon", coordinates: coords })
109+
);
110+
}
111+
103112
function isPointInMultiPoint(multiPoint: MultiPoint, pt: Point) {
104113
let i;
105114
let output = false;
@@ -177,30 +186,67 @@ function isLineOnLine(lineString1: LineString, lineString2: LineString) {
177186
return haveFoundInteriorPoint;
178187
}
179188

180-
function isLineInPoly(polygon: Polygon, linestring: LineString) {
181-
let output = false;
182-
let i = 0;
189+
function splitLineIntoSegmentsOnPolygon(
190+
linestring: LineString,
191+
polygon: Polygon
192+
) {
193+
const coords = linestring.coordinates;
194+
195+
const outputSegments: Feature<LineString>[] = [];
196+
197+
for (let i = 0; i < coords.length - 1; i++) {
198+
const seg = lineString([coords[i], coords[i + 1]]);
199+
const split = lineSplit(seg, feature(polygon));
183200

201+
if (split.features.length === 0) {
202+
outputSegments.push(seg);
203+
} else {
204+
outputSegments.push(...split.features);
205+
}
206+
}
207+
208+
return featureCollection(outputSegments);
209+
}
210+
211+
function isLineInPoly(polygon: Polygon, linestring: LineString) {
184212
const polyBbox = calcBbox(polygon);
185213
const lineBbox = calcBbox(linestring);
214+
186215
if (!doBBoxOverlap(polyBbox, lineBbox)) {
187216
return false;
188217
}
189-
for (i; i < linestring.coordinates.length - 1; i++) {
190-
const midPoint = getMidpoint(
191-
linestring.coordinates[i],
192-
linestring.coordinates[i + 1]
218+
219+
for (const coord of linestring.coordinates) {
220+
if (!booleanPointInPolygon(coord, polygon)) {
221+
return false;
222+
}
223+
}
224+
225+
let isContainedByPolygonBoundary = false;
226+
// split intersecting segments and verify their inclusion
227+
const lineSegments = splitLineIntoSegmentsOnPolygon(linestring, polygon);
228+
229+
for (const lineSegment of lineSegments.features) {
230+
const midpoint = getMidpoint(
231+
lineSegment.geometry.coordinates[0],
232+
lineSegment.geometry.coordinates[1]
193233
);
234+
235+
// make sure all segments do not intersect with polygon exterior
236+
if (!booleanPointInPolygon(midpoint, polygon)) {
237+
return false;
238+
}
239+
240+
// make sure at least 1 segment intersects with the polygon's interior
194241
if (
195-
booleanPointInPolygon({ type: "Point", coordinates: midPoint }, polygon, {
196-
ignoreBoundary: true,
197-
})
242+
!isContainedByPolygonBoundary &&
243+
booleanPointInPolygon(midpoint, polygon, { ignoreBoundary: true })
198244
) {
199-
output = true;
200-
break;
245+
isContainedByPolygonBoundary = true;
201246
}
202247
}
203-
return output;
248+
249+
return isContainedByPolygonBoundary;
204250
}
205251

206252
/**
@@ -283,6 +329,7 @@ export {
283329
isLineOnLine,
284330
isLineInPoly,
285331
isPolyInPoly,
332+
isMultiPolyInPoly,
286333
doBBoxOverlap,
287334
compareCoords,
288335
getMidpoint,

packages/turf-boolean-contains/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"author": "Turf Authors",
66
"contributors": [
77
"Rowan Winsemius <@rowanwins>",
8-
"Denis Carriere <@DenisCarriere>"
8+
"Denis Carriere <@DenisCarriere>",
9+
"Samuel Arbibe <@samuelarbibe>"
910
],
1011
"license": "MIT",
1112
"bugs": {
@@ -73,6 +74,7 @@
7374
"@turf/boolean-point-on-line": "workspace:*",
7475
"@turf/helpers": "workspace:*",
7576
"@turf/invariant": "workspace:*",
77+
"@turf/line-split": "workspace:*",
7678
"@types/geojson": "^7946.0.10",
7779
"tslib": "^2.8.1"
7880
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"type": "FeatureCollection",
3+
"features": [
4+
{
5+
"type": "Feature",
6+
"properties": {},
7+
"geometry": {
8+
"type": "Polygon",
9+
"coordinates": [
10+
[
11+
[5.697231218450497, 39.505906288651715],
12+
[5.697231218450497, 40.50986523812119],
13+
[4.30801420435526, 40.50986523812119],
14+
[4.30801420435526, 39.505906288651715],
15+
[5.697231218450497, 39.505906288651715]
16+
]
17+
]
18+
}
19+
},
20+
{
21+
"type": "Feature",
22+
"properties": null,
23+
"geometry": {
24+
"type": "MultiPolygon",
25+
"coordinates": [
26+
[
27+
[
28+
[3.6968994140625, 39.65645604812829],
29+
[4.622497558593749, 39.65645604812829],
30+
[4.622497558593749, 40.168380093142446],
31+
[3.6968994140625, 40.168380093142446],
32+
[3.6968994140625, 39.65645604812829]
33+
]
34+
],
35+
[
36+
[
37+
[4.73236083984375, 39.69450749856091],
38+
[5.416259765624999, 39.69450749856091],
39+
[5.416259765624999, 40.386304853509046],
40+
[4.73236083984375, 40.386304853509046],
41+
[4.73236083984375, 39.69450749856091]
42+
]
43+
]
44+
]
45+
}
46+
}
47+
]
48+
}

packages/turf-boolean-contains/test/false/LineString/Polygon/LineIsNotContainedByPolygon.geojson renamed to packages/turf-boolean-contains/test/false/Polygon/LineString/LineIsNotContainedByPolygon.geojson

File renamed without changes.

packages/turf-boolean-contains/test/false/LineString/Polygon/LineIsNotContainedByPolygonBoundary.geojson renamed to packages/turf-boolean-contains/test/false/Polygon/LineString/LineIsNotContainedByPolygonBoundary.geojson

File renamed without changes.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"type": "FeatureCollection",
3+
"features": [
4+
{
5+
"type": "Feature",
6+
"properties": {},
7+
"geometry": {
8+
"type": "Polygon",
9+
"coordinates": [
10+
[
11+
[9.6459207, -1.2281524],
12+
[11.0822393, -1.2461022],
13+
[11.405411, -0.1869151],
14+
[11.1181473, 1.9133448],
15+
[10.0588623, 1.4646914],
16+
[10.6692977, -0.0253296],
17+
[10.6692977, -0.0253296],
18+
[9.6459207, -1.2281524]
19+
]
20+
]
21+
}
22+
},
23+
{
24+
"type": "Feature",
25+
"properties": {},
26+
"geometry": {
27+
"type": "LineString",
28+
"coordinates": [
29+
[10.2166256, -0.7379163],
30+
[11.0362904, 1.794324]
31+
]
32+
}
33+
}
34+
]
35+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"type": "FeatureCollection",
3+
"features": [
4+
{
5+
"type": "Feature",
6+
"properties": {},
7+
"geometry": {
8+
"type": "Polygon",
9+
"coordinates": [
10+
[
11+
[5.860668514226688, 40.60423664824944],
12+
[3.248940527728166, 40.60423664824944],
13+
[3.248940527728166, 39.35441976583036],
14+
[5.860668514226688, 39.35441976583036],
15+
[5.860668514226688, 40.60423664824944]
16+
]
17+
]
18+
}
19+
},
20+
{
21+
"type": "Feature",
22+
"properties": null,
23+
"geometry": {
24+
"type": "MultiPolygon",
25+
"coordinates": [
26+
[
27+
[
28+
[3.6968994140625, 39.65645604812829],
29+
[4.622497558593749, 39.65645604812829],
30+
[4.622497558593749, 40.168380093142446],
31+
[3.6968994140625, 40.168380093142446],
32+
[3.6968994140625, 39.65645604812829]
33+
]
34+
],
35+
[
36+
[
37+
[4.73236083984375, 39.69450749856091],
38+
[5.416259765624999, 39.69450749856091],
39+
[5.416259765624999, 40.386304853509046],
40+
[4.73236083984375, 40.386304853509046],
41+
[4.73236083984375, 39.69450749856091]
42+
]
43+
]
44+
]
45+
}
46+
}
47+
]
48+
}

0 commit comments

Comments
 (0)