Skip to content

Commit 4a0ff38

Browse files
rowanwinsmfedderly
andauthored
Rework Mask module (#2130)
* rework dissolve * make es5 friendly * remove spread operator * remove arrow function * another prettier fix Co-authored-by: mfedderly <mdfedderly@mdfedderly.com>
1 parent 779ac05 commit 4a0ff38

File tree

5 files changed

+58
-160
lines changed

5 files changed

+58
-160
lines changed

packages/turf-mask/README.md

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,44 @@ Takes any type of [polygon][1] and an optional mask and returns a [polygon][1] e
88

99
**Parameters**
1010

11-
- `polygon` **([FeatureCollection][2] \| [Feature][3]&lt;([Polygon][4] \| [MultiPolygon][5])>)** GeoJSON Polygon used as interior rings or holes.
12-
- `mask` **[Feature][3]&lt;[Polygon][4]>?** GeoJSON Polygon used as the exterior ring (if undefined, the world extent is used)
11+
- `polygon` **([FeatureCollection][2] \| [Feature][3]&lt;([Polygon][4] \| [MultiPolygon][5])>)** GeoJSON Polygon used as interior rings or holes.
12+
- `mask` **[Feature][3]&lt;[Polygon][4]>?** GeoJSON Polygon used as the exterior ring (if undefined, the world extent is used)
1313

1414
**Examples**
1515

1616
```javascript
17-
var polygon = turf.polygon([[[112, -21], [116, -36], [146, -39], [153, -24], [133, -10], [112, -21]]]);
18-
var mask = turf.polygon([[[90, -55], [170, -55], [170, 10], [90, 10], [90, -55]]]);
17+
var polygon = turf.polygon([
18+
[
19+
[112, -21],
20+
[116, -36],
21+
[146, -39],
22+
[153, -24],
23+
[133, -10],
24+
[112, -21],
25+
],
26+
]);
27+
var mask = turf.polygon([
28+
[
29+
[90, -55],
30+
[170, -55],
31+
[170, 10],
32+
[90, 10],
33+
[90, -55],
34+
],
35+
]);
1936

2037
var masked = turf.mask(polygon, mask);
2138

2239
//addToMap
23-
var addToMap = [masked]
40+
var addToMap = [masked];
2441
```
2542

2643
Returns **[Feature][3]&lt;[Polygon][4]>** Masked Polygon (exterior ring with holes).
2744

2845
[1]: https://tools.ietf.org/html/rfc7946#section-3.1.6
29-
3046
[2]: https://tools.ietf.org/html/rfc7946#section-3.3
31-
3247
[3]: https://tools.ietf.org/html/rfc7946#section-3.2
33-
3448
[4]: https://tools.ietf.org/html/rfc7946#section-3.1.6
35-
3649
[5]: https://tools.ietf.org/html/rfc7946#section-3.1.7
3750

3851
<!-- This file is automatically generated. Please don't edit it directly:

packages/turf-mask/bench.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ for (const { name, geojson } of fixtures) {
2323
suite.add(name, () => turfMask(polygon, masking));
2424
}
2525

26+
// basic x 4,627 ops/sec ±25.23% (21 runs sampled)
27+
// mask-outside x 3,892 ops/sec ±34.80% (15 runs sampled)
28+
// multi-polygon x 5,837 ops/sec ±3.03% (91 runs sampled)
29+
// overlapping x 22,326 ops/sec ±1.34% (90 runs sampled)
2630
suite
2731
.on("cycle", (event) => {
2832
console.log(String(event.target));

packages/turf-mask/index.js

Lines changed: 29 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
import rbush from "rbush";
2-
import union from "@turf/union";
3-
import { polygon, featureCollection } from "@turf/helpers";
4-
import turfBBox from "@turf/bbox";
5-
import { flattenEach } from "@turf/meta";
1+
import { polygon as createPolygon, multiPolygon } from "@turf/helpers";
2+
import polygonClipping from "polygon-clipping";
63

74
/**
85
* Takes any type of {@link Polygon|polygon} and an optional mask and returns a {@link Polygon|polygon} exterior ring with holes.
@@ -24,63 +21,38 @@ function mask(polygon, mask) {
2421
// Define mask
2522
var maskPolygon = createMask(mask);
2623

27-
// Define polygon
28-
var separated = separatePolygons(polygon);
29-
var polygonOuters = separated[0];
30-
var polygonInners = separated[1];
24+
var polygonOuters = null;
25+
if (polygon.type === "FeatureCollection") polygonOuters = unionFc(polygon);
26+
else
27+
polygonOuters = createGeomFromPolygonClippingOutput(
28+
polygonClipping.union(polygon.geometry.coordinates)
29+
);
3130

32-
// Union Outers & Inners
33-
polygonOuters = unionPolygons(polygonOuters);
34-
polygonInners = unionPolygons(polygonInners);
31+
polygonOuters.geometry.coordinates.forEach(function (contour) {
32+
maskPolygon.geometry.coordinates.push(contour[0]);
33+
});
3534

36-
// Create masked area
37-
var masked = buildMask(maskPolygon, polygonOuters, polygonInners);
38-
return masked;
35+
return maskPolygon;
3936
}
4037

41-
/**
42-
* Build Mask
43-
*
44-
* @private
45-
* @param {Feature<Polygon>} maskPolygon Mask Outer
46-
* @param {FeatureCollection<Polygon>} polygonOuters Polygon Outers
47-
* @param {FeatureCollection<Polygon>} polygonInners Polygon Inners
48-
* @returns {Feature<Polygon>} Feature Polygon
49-
*/
50-
function buildMask(maskPolygon, polygonOuters, polygonInners) {
51-
var coordinates = [];
52-
coordinates.push(maskPolygon.geometry.coordinates[0]);
53-
54-
flattenEach(polygonOuters, function (feature) {
55-
coordinates.push(feature.geometry.coordinates[0]);
56-
});
57-
58-
flattenEach(polygonInners, function (feature) {
59-
coordinates.push(feature.geometry.coordinates[0]);
60-
});
61-
return polygon(coordinates);
38+
function unionFc(fc) {
39+
var unioned =
40+
fc.features.length === 2
41+
? polygonClipping.union(
42+
fc.features[0].geometry.coordinates,
43+
fc.features[1].geometry.coordinates
44+
)
45+
: polygonClipping.union.apply(
46+
polygonClipping,
47+
fc.features.map(function (f) {
48+
return f.geometry.coordinates;
49+
})
50+
);
51+
return createGeomFromPolygonClippingOutput(unioned);
6252
}
6353

64-
/**
65-
* Separate Polygons to inners & outers
66-
*
67-
* @private
68-
* @param {FeatureCollection|Feature<Polygon|MultiPolygon>} poly GeoJSON Feature
69-
* @returns {Array<FeatureCollection<Polygon>, FeatureCollection<Polygon>>} Outer & Inner lines
70-
*/
71-
function separatePolygons(poly) {
72-
var outers = [];
73-
var inners = [];
74-
flattenEach(poly, function (feature) {
75-
var coordinates = feature.geometry.coordinates;
76-
var featureOuter = coordinates[0];
77-
var featureInner = coordinates.slice(1);
78-
outers.push(polygon([featureOuter]));
79-
featureInner.forEach(function (inner) {
80-
inners.push(polygon([inner]));
81-
});
82-
});
83-
return [featureCollection(outers), featureCollection(inners)];
54+
function createGeomFromPolygonClippingOutput(unioned) {
55+
return multiPolygon(unioned);
8456
}
8557

8658
/**
@@ -101,95 +73,7 @@ function createMask(mask) {
10173
],
10274
];
10375
var coordinates = (mask && mask.geometry.coordinates) || world;
104-
return polygon(coordinates);
105-
}
106-
107-
/**
108-
* Union Polygons
109-
*
110-
* @private
111-
* @param {FeatureCollection<Polygon>} polygons collection of polygons
112-
* @returns {FeatureCollection<Polygon>} polygons only apply union if they collide
113-
*/
114-
function unionPolygons(polygons) {
115-
if (polygons.features.length <= 1) return polygons;
116-
117-
var tree = createIndex(polygons);
118-
var results = [];
119-
var removed = {};
120-
121-
flattenEach(polygons, function (currentFeature, currentIndex) {
122-
// Exclude any removed features
123-
if (removed[currentIndex]) return true;
124-
125-
// Don't search for itself
126-
tree.remove({ index: currentIndex }, filterByIndex);
127-
removed[currentIndex] = true;
128-
129-
// Keep applying the union operation until no more overlapping features
130-
while (true) {
131-
var bbox = turfBBox(currentFeature);
132-
var search = tree.search({
133-
minX: bbox[0],
134-
minY: bbox[1],
135-
maxX: bbox[2],
136-
maxY: bbox[3],
137-
});
138-
if (search.length > 0) {
139-
var polys = search.map(function (item) {
140-
removed[item.index] = true;
141-
tree.remove({ index: item.index }, filterByIndex);
142-
return item.geojson;
143-
});
144-
145-
for (var i = 0, l = polys.length; i < l; i++) {
146-
currentFeature = union(currentFeature, polys[i]);
147-
}
148-
}
149-
// Done
150-
if (search.length === 0) break;
151-
}
152-
results.push(currentFeature);
153-
});
154-
155-
return featureCollection(results);
156-
}
157-
158-
/**
159-
* Filter by Index - RBush helper function
160-
*
161-
* @private
162-
* @param {Object} a remove item
163-
* @param {Object} b search item
164-
* @returns {boolean} true if matches
165-
*/
166-
function filterByIndex(a, b) {
167-
return a.index === b.index;
168-
}
169-
170-
/**
171-
* Create RBush Tree Index
172-
*
173-
* @private
174-
* @param {FeatureCollection<any>} features GeoJSON FeatureCollection
175-
* @returns {RBush} RBush Tree
176-
*/
177-
function createIndex(features) {
178-
var tree = rbush();
179-
var load = [];
180-
flattenEach(features, function (feature, index) {
181-
var bbox = turfBBox(feature);
182-
load.push({
183-
minX: bbox[0],
184-
minY: bbox[1],
185-
maxX: bbox[2],
186-
maxY: bbox[3],
187-
geojson: feature,
188-
index: index,
189-
});
190-
});
191-
tree.load(load);
192-
return tree;
76+
return createPolygon(coordinates);
19377
}
19478

19579
export default mask;

packages/turf-mask/package.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,7 @@
5454
"write-json-file": "*"
5555
},
5656
"dependencies": {
57-
"@turf/bbox": "^6.4.0",
5857
"@turf/helpers": "^6.4.0",
59-
"@turf/meta": "^6.4.0",
60-
"@turf/union": "^6.4.0",
61-
"rbush": "^2.0.1"
58+
"polygon-clipping": "^0.15.3"
6259
}
6360
}

packages/turf-mask/test/out/mask-outside.geojson

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
[119.53125, -39.77476948529546]
1313
],
1414
[
15-
[113.115234375, -22.105998799750566],
1615
[112.587890625, -24.766784522874428],
1716
[113.73046875, -29.382175075145277],
1817
[114.78515624999999, -32.916485347314385],
@@ -25,7 +24,8 @@
2524
[127.44140625, -17.560246503294888],
2625
[122.431640625, -17.308687886770024],
2726
[116.806640625, -17.978733095556155],
28-
[113.115234375, -22.105998799750566]
27+
[113.115234375, -22.105998799750566],
28+
[112.587890625, -24.766784522874428]
2929
]
3030
]
3131
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy