Skip to content

Commit 11ec0a8

Browse files
Merge pull request #2830 from smallsaucepan/simplify-infinite
Fixed simplify to not get stuck in an infinite loop on certain geometries
2 parents 3a4cd44 + ac72619 commit 11ec0a8

File tree

4 files changed

+93
-28
lines changed

4 files changed

+93
-28
lines changed

packages/turf-nearest-neighbor-analysis/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Type: [object][1]
3030

3131
## nearestNeighborAnalysis
3232

33-
Nearest Neighbor Analysis calculates an index based the average distances
33+
Nearest Neighbor Analysis calculates an index based on the average distances
3434
between points in the dataset, thereby providing inference as to whether the
3535
data is clustered, dispersed, or randomly distributed within the study area.
3636

packages/turf-simplify/README.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,22 @@
44

55
## simplify
66

7-
Takes a [GeoJSON][1] object and returns a simplified version. Internally uses the 2d version of
8-
[simplify-js][2] to perform simplification using the Ramer-Douglas-Peucker algorithm.
7+
Simplifies the geometries in a GeoJSON object. Uses the 2d version of
8+
[simplify-js][1].
99

1010
### Parameters
1111

12-
* `geojson` **[GeoJSON][1]** object to be simplified
12+
* `geojson` **[GeoJSON][2]** GeoJSON object to be simplified
1313
* `options` **[Object][3]** Optional parameters (optional, default `{}`)
1414

15-
* `options.tolerance` **[number][4]** simplification tolerance (optional, default `1`)
16-
* `options.highQuality` **[boolean][5]** whether or not to spend more time to create a higher-quality simplification with a different algorithm (optional, default `false`)
17-
* `options.mutate` **[boolean][5]** allows GeoJSON input to be mutated (significant performance increase if true) (optional, default `false`)
15+
* `options.tolerance` **[number][4]** Simplification tolerance (optional, default `1`)
16+
* `options.highQuality` **[boolean][5]** Produce a higher-quality simplification using a slower algorithm (optional, default `false`)
17+
* `options.mutate` **[boolean][5]** Allow GeoJSON input to be mutated (significant performance improvement if true) (optional, default `false`)
1818

1919
### Examples
2020

2121
```javascript
22-
var geojson = turf.polygon([[
22+
const geojson = turf.polygon([[
2323
[-70.603637, -33.399918],
2424
[-70.614624, -33.395332],
2525
[-70.639343, -33.392466],
@@ -41,18 +41,18 @@ var geojson = turf.polygon([[
4141
[-70.594711, -33.406224],
4242
[-70.603637, -33.399918]
4343
]]);
44-
var options = {tolerance: 0.01, highQuality: false};
45-
var simplified = turf.simplify(geojson, options);
44+
const result0_01 = turf.simplify(geojson, {tolerance: 0.01 });
45+
const result0_005 = turf.simplify(geojson, {tolerance: 0.005 });
4646

4747
//addToMap
48-
var addToMap = [geojson, simplified]
48+
const addToMap = [geojson, result0_01, result0_005]
4949
```
5050

51-
Returns **[GeoJSON][1]** a simplified GeoJSON
51+
Returns **[GeoJSON][2]** Simplified GeoJSON
5252

53-
[1]: https://tools.ietf.org/html/rfc7946#section-3
53+
[1]: https://mourner.github.io/simplify-js/
5454

55-
[2]: http://mourner.github.io/simplify-js/
55+
[2]: https://tools.ietf.org/html/rfc7946#section-3
5656

5757
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
5858

packages/turf-simplify/index.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,18 @@ import { AllGeoJSON, isObject } from "@turf/helpers";
66
import { simplify as simplifyJS } from "./lib/simplify.js";
77

88
/**
9-
* Takes a {@link GeoJSON} object and returns a simplified version. Internally uses the 2d version of
10-
* [simplify-js](http://mourner.github.io/simplify-js/) to perform simplification using the Ramer-Douglas-Peucker algorithm.
11-
*
9+
* Simplifies the geometries in a GeoJSON object. Uses the 2d version of
10+
* [simplify-js](https://mourner.github.io/simplify-js/).
1211
*
1312
* @function
14-
* @param {GeoJSON} geojson object to be simplified
13+
* @param {GeoJSON} geojson GeoJSON object to be simplified
1514
* @param {Object} [options={}] Optional parameters
16-
* @param {number} [options.tolerance=1] simplification tolerance
17-
* @param {boolean} [options.highQuality=false] whether or not to spend more time to create a higher-quality simplification with a different algorithm
18-
* @param {boolean} [options.mutate=false] allows GeoJSON input to be mutated (significant performance increase if true)
19-
* @returns {GeoJSON} a simplified GeoJSON
15+
* @param {number} [options.tolerance=1] Simplification tolerance
16+
* @param {boolean} [options.highQuality=false] Produce a higher-quality simplification using a slower algorithm
17+
* @param {boolean} [options.mutate=false] Allow GeoJSON input to be mutated (significant performance improvement if true)
18+
* @returns {GeoJSON} Simplified GeoJSON
2019
* @example
21-
* var geojson = turf.polygon([[
20+
* const geojson = turf.polygon([[
2221
* [-70.603637, -33.399918],
2322
* [-70.614624, -33.395332],
2423
* [-70.639343, -33.392466],
@@ -40,11 +39,11 @@ import { simplify as simplifyJS } from "./lib/simplify.js";
4039
* [-70.594711, -33.406224],
4140
* [-70.603637, -33.399918]
4241
* ]]);
43-
* var options = {tolerance: 0.01, highQuality: false};
44-
* var simplified = turf.simplify(geojson, options);
42+
* const result0_01 = turf.simplify(geojson, {tolerance: 0.01 });
43+
* const result0_005 = turf.simplify(geojson, {tolerance: 0.005 });
4544
*
4645
* //addToMap
47-
* var addToMap = [geojson, simplified]
46+
* const addToMap = [geojson, result0_01, result0_005]
4847
*/
4948
function simplify<T extends AllGeoJSON>(
5049
geojson: T,
@@ -147,11 +146,20 @@ function simplifyPolygon(
147146
}
148147
let ringTolerance = tolerance;
149148
let simpleRing = simplifyJS(ring, ringTolerance, highQuality);
150-
// remove 1 percent of tolerance until enough points to make a triangle
151-
while (!checkValidity(simpleRing)) {
149+
150+
// If simplified ring isn't valid (has been over simplified) reduce the
151+
// tolerance by 1% and try again.
152+
while (!checkValidity(simpleRing) && ringTolerance >= Number.EPSILON) {
152153
ringTolerance -= ringTolerance * 0.01;
153154
simpleRing = simplifyJS(ring, ringTolerance, highQuality);
154155
}
156+
157+
// If ring wasn't able to be simplified in a valid way, return it unchanged.
158+
if (!checkValidity(simpleRing)) {
159+
return ring;
160+
}
161+
162+
// Close the ring if it wasn't already.
155163
if (
156164
simpleRing[simpleRing.length - 1][0] !== simpleRing[0][0] ||
157165
simpleRing[simpleRing.length - 1][1] !== simpleRing[0][1]

packages/turf-simplify/test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,60 @@ test("simplify -- issue #918", (t) => {
214214
);
215215
t.end();
216216
});
217+
218+
test("simplify - issue 1788 - infinite loop", (t) => {
219+
// For particularly small polygons simplify was getting stuck in an
220+
// infinite loop.
221+
222+
// https://github.com/Turfjs/turf/issues/1788#issue-521052548
223+
const poly1 = polygon([
224+
[
225+
[11.662180661499999, 50.1081498005],
226+
[11.662192661499999, 50.108041800500004],
227+
[11.6621866615, 50.1080958005],
228+
[11.662180661499999, 50.1081498005],
229+
],
230+
]);
231+
simplify(poly1, {
232+
tolerance: 0.000001,
233+
highQuality: true,
234+
mutate: false,
235+
});
236+
t.pass("issue 1788 test 1 didn't hang");
237+
238+
// https://github.com/Turfjs/turf/issues/1788#issuecomment-951109683
239+
const poly2 = polygon([
240+
[
241+
[4.0881641, 6.8121605],
242+
[4.0881639, 6.8121607],
243+
[4.0881638, 6.8121608],
244+
[4.0881641, 6.8121605],
245+
],
246+
]);
247+
simplify(poly2);
248+
t.pass("issue 1788 test 2 didn't hang");
249+
250+
// https://github.com/Turfjs/turf/issues/1788#issuecomment-1362073785
251+
const poly3 = multiPolygon([
252+
[
253+
[
254+
[-10.06762725, -17.428977611],
255+
[-10.067626613, -17.428977323],
256+
[-10.067625943, -17.428976957],
257+
[-10.06762725, -17.428977611],
258+
],
259+
],
260+
[
261+
[
262+
[-10.067625943, -17.428976957],
263+
[-10.067599027, -17.428963499],
264+
[-10.067625941, -17.428976956],
265+
[-10.067625943, -17.428976957],
266+
],
267+
],
268+
]);
269+
simplify(poly3, { tolerance: 0.00001, highQuality: true });
270+
t.pass("issue 1788 test 3 didn't hang");
271+
272+
t.end();
273+
});

0 commit comments

Comments
 (0)