@turf/polygonize 7.0.0-alpha.2 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -9
- package/dist/cjs/index.cjs +674 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/index.d.cts +23 -0
- package/dist/{js → esm}/index.d.ts +5 -2
- package/dist/esm/index.js +674 -0
- package/dist/esm/index.js.map +1 -0
- package/package.json +36 -30
- package/dist/es/index.js +0 -46
- package/dist/es/lib/Edge.js +0 -75
- package/dist/es/lib/EdgeRing.js +0 -188
- package/dist/es/lib/Graph.js +0 -289
- package/dist/es/lib/Node.js +0 -90
- package/dist/es/lib/util.js +0 -64
- package/dist/es/package.json +0 -1
- package/dist/js/index.js +0 -50
- package/dist/js/lib/Edge.d.ts +0 -55
- package/dist/js/lib/Edge.js +0 -78
- package/dist/js/lib/EdgeRing.d.ts +0 -122
- package/dist/js/lib/EdgeRing.js +0 -192
- package/dist/js/lib/Graph.d.ts +0 -124
- package/dist/js/lib/Graph.js +0 -293
- package/dist/js/lib/Node.d.ts +0 -40
- package/dist/js/lib/Node.js +0 -93
- package/dist/js/lib/util.d.ts +0 -46
- package/dist/js/lib/util.js +0 -71
package/README.md
CHANGED
|
@@ -44,26 +44,21 @@ Returns **[FeatureCollection][3]<[Polygon][9]>** Polygons created
|
|
|
44
44
|
|
|
45
45
|
[9]: https://tools.ietf.org/html/rfc7946#section-3.1.6
|
|
46
46
|
|
|
47
|
-
<!-- This file is automatically generated. Please don't edit it directly
|
|
48
|
-
if you find an error, edit the source file (likely index.js), and re-run
|
|
49
|
-
./scripts/generate-readmes in the turf project. -->
|
|
47
|
+
<!-- This file is automatically generated. Please don't edit it directly. If you find an error, edit the source file of the module in question (likely index.js or index.ts), and re-run "yarn docs" from the root of the turf project. -->
|
|
50
48
|
|
|
51
49
|
---
|
|
52
50
|
|
|
53
|
-
This module is part of the [Turfjs project](
|
|
54
|
-
module collection dedicated to geographic algorithms. It is maintained in the
|
|
55
|
-
[Turfjs/turf](https://github.com/Turfjs/turf) repository, where you can create
|
|
56
|
-
PRs and issues.
|
|
51
|
+
This module is part of the [Turfjs project](https://turfjs.org/), an open source module collection dedicated to geographic algorithms. It is maintained in the [Turfjs/turf](https://github.com/Turfjs/turf) repository, where you can create PRs and issues.
|
|
57
52
|
|
|
58
53
|
### Installation
|
|
59
54
|
|
|
60
|
-
Install this module individually:
|
|
55
|
+
Install this single module individually:
|
|
61
56
|
|
|
62
57
|
```sh
|
|
63
58
|
$ npm install @turf/polygonize
|
|
64
59
|
```
|
|
65
60
|
|
|
66
|
-
Or install the
|
|
61
|
+
Or install the all-encompassing @turf/turf module that includes all modules as functions:
|
|
67
62
|
|
|
68
63
|
```sh
|
|
69
64
|
$ npm install @turf/turf
|
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// index.ts
|
|
5
|
+
var _helpers = require('@turf/helpers');
|
|
6
|
+
|
|
7
|
+
// lib/util.ts
|
|
8
|
+
var _booleanpointinpolygon = require('@turf/boolean-point-in-polygon');
|
|
9
|
+
|
|
10
|
+
function mathSign(x) {
|
|
11
|
+
return (x > 0) - (x < 0) || +x;
|
|
12
|
+
}
|
|
13
|
+
__name(mathSign, "mathSign");
|
|
14
|
+
function orientationIndex(p1, p2, q) {
|
|
15
|
+
const dx1 = p2[0] - p1[0], dy1 = p2[1] - p1[1], dx2 = q[0] - p2[0], dy2 = q[1] - p2[1];
|
|
16
|
+
return mathSign(dx1 * dy2 - dx2 * dy1);
|
|
17
|
+
}
|
|
18
|
+
__name(orientationIndex, "orientationIndex");
|
|
19
|
+
function envelopeIsEqual(env1, env2) {
|
|
20
|
+
const envX1 = env1.geometry.coordinates[0].map((c) => c[0]), envY1 = env1.geometry.coordinates[0].map((c) => c[1]), envX2 = env2.geometry.coordinates[0].map((c) => c[0]), envY2 = env2.geometry.coordinates[0].map((c) => c[1]);
|
|
21
|
+
return Math.max.apply(null, envX1) === Math.max.apply(null, envX2) && Math.max.apply(null, envY1) === Math.max.apply(null, envY2) && Math.min.apply(null, envX1) === Math.min.apply(null, envX2) && Math.min.apply(null, envY1) === Math.min.apply(null, envY2);
|
|
22
|
+
}
|
|
23
|
+
__name(envelopeIsEqual, "envelopeIsEqual");
|
|
24
|
+
function envelopeContains(self, env) {
|
|
25
|
+
return env.geometry.coordinates[0].every(
|
|
26
|
+
(c) => _booleanpointinpolygon.booleanPointInPolygon.call(void 0, _helpers.point.call(void 0, c), self)
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
__name(envelopeContains, "envelopeContains");
|
|
30
|
+
function coordinatesEqual(coord1, coord2) {
|
|
31
|
+
return coord1[0] === coord2[0] && coord1[1] === coord2[1];
|
|
32
|
+
}
|
|
33
|
+
__name(coordinatesEqual, "coordinatesEqual");
|
|
34
|
+
|
|
35
|
+
// lib/Node.ts
|
|
36
|
+
var _Node = class _Node {
|
|
37
|
+
static buildId(coordinates) {
|
|
38
|
+
return coordinates.join(",");
|
|
39
|
+
}
|
|
40
|
+
constructor(coordinates) {
|
|
41
|
+
this.id = _Node.buildId(coordinates);
|
|
42
|
+
this.coordinates = coordinates;
|
|
43
|
+
this.innerEdges = [];
|
|
44
|
+
this.outerEdges = [];
|
|
45
|
+
this.outerEdgesSorted = false;
|
|
46
|
+
}
|
|
47
|
+
removeInnerEdge(edge) {
|
|
48
|
+
this.innerEdges = this.innerEdges.filter((e) => e.from.id !== edge.from.id);
|
|
49
|
+
}
|
|
50
|
+
removeOuterEdge(edge) {
|
|
51
|
+
this.outerEdges = this.outerEdges.filter((e) => e.to.id !== edge.to.id);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Outer edges are stored CCW order.
|
|
55
|
+
*
|
|
56
|
+
* @memberof Node
|
|
57
|
+
* @param {Edge} edge - Edge to add as an outerEdge.
|
|
58
|
+
*/
|
|
59
|
+
addOuterEdge(edge) {
|
|
60
|
+
this.outerEdges.push(edge);
|
|
61
|
+
this.outerEdgesSorted = false;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Sorts outer edges in CCW way.
|
|
65
|
+
*
|
|
66
|
+
* @memberof Node
|
|
67
|
+
* @private
|
|
68
|
+
*/
|
|
69
|
+
sortOuterEdges() {
|
|
70
|
+
if (!this.outerEdgesSorted) {
|
|
71
|
+
this.outerEdges.sort((a, b) => {
|
|
72
|
+
const aNode = a.to, bNode = b.to;
|
|
73
|
+
if (aNode.coordinates[0] - this.coordinates[0] >= 0 && bNode.coordinates[0] - this.coordinates[0] < 0)
|
|
74
|
+
return 1;
|
|
75
|
+
if (aNode.coordinates[0] - this.coordinates[0] < 0 && bNode.coordinates[0] - this.coordinates[0] >= 0)
|
|
76
|
+
return -1;
|
|
77
|
+
if (aNode.coordinates[0] - this.coordinates[0] === 0 && bNode.coordinates[0] - this.coordinates[0] === 0) {
|
|
78
|
+
if (aNode.coordinates[1] - this.coordinates[1] >= 0 || bNode.coordinates[1] - this.coordinates[1] >= 0)
|
|
79
|
+
return aNode.coordinates[1] - bNode.coordinates[1];
|
|
80
|
+
return bNode.coordinates[1] - aNode.coordinates[1];
|
|
81
|
+
}
|
|
82
|
+
const det = orientationIndex(
|
|
83
|
+
this.coordinates,
|
|
84
|
+
aNode.coordinates,
|
|
85
|
+
bNode.coordinates
|
|
86
|
+
);
|
|
87
|
+
if (det < 0)
|
|
88
|
+
return 1;
|
|
89
|
+
if (det > 0)
|
|
90
|
+
return -1;
|
|
91
|
+
const d1 = Math.pow(aNode.coordinates[0] - this.coordinates[0], 2) + Math.pow(aNode.coordinates[1] - this.coordinates[1], 2), d2 = Math.pow(bNode.coordinates[0] - this.coordinates[0], 2) + Math.pow(bNode.coordinates[1] - this.coordinates[1], 2);
|
|
92
|
+
return d1 - d2;
|
|
93
|
+
});
|
|
94
|
+
this.outerEdgesSorted = true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Retrieves outer edges.
|
|
99
|
+
*
|
|
100
|
+
* They are sorted if they aren't in the CCW order.
|
|
101
|
+
*
|
|
102
|
+
* @memberof Node
|
|
103
|
+
* @returns {Edge[]} - List of outer edges sorted in a CCW order.
|
|
104
|
+
*/
|
|
105
|
+
getOuterEdges() {
|
|
106
|
+
this.sortOuterEdges();
|
|
107
|
+
return this.outerEdges;
|
|
108
|
+
}
|
|
109
|
+
getOuterEdge(i) {
|
|
110
|
+
this.sortOuterEdges();
|
|
111
|
+
return this.outerEdges[i];
|
|
112
|
+
}
|
|
113
|
+
addInnerEdge(edge) {
|
|
114
|
+
this.innerEdges.push(edge);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
__name(_Node, "Node");
|
|
118
|
+
var Node = _Node;
|
|
119
|
+
|
|
120
|
+
// lib/Edge.ts
|
|
121
|
+
|
|
122
|
+
var _Edge = class _Edge {
|
|
123
|
+
/**
|
|
124
|
+
* Creates or get the symetric Edge.
|
|
125
|
+
*
|
|
126
|
+
* @returns {Edge} - Symetric Edge.
|
|
127
|
+
*/
|
|
128
|
+
getSymetric() {
|
|
129
|
+
if (!this.symetric) {
|
|
130
|
+
this.symetric = new _Edge(this.to, this.from);
|
|
131
|
+
this.symetric.symetric = this;
|
|
132
|
+
}
|
|
133
|
+
return this.symetric;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* @param {Node} from - start node of the Edge
|
|
137
|
+
* @param {Node} to - end node of the edge
|
|
138
|
+
*/
|
|
139
|
+
constructor(from, to) {
|
|
140
|
+
this.from = from;
|
|
141
|
+
this.to = to;
|
|
142
|
+
this.next = void 0;
|
|
143
|
+
this.label = void 0;
|
|
144
|
+
this.symetric = void 0;
|
|
145
|
+
this.ring = void 0;
|
|
146
|
+
this.from.addOuterEdge(this);
|
|
147
|
+
this.to.addInnerEdge(this);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Removes edge from from and to nodes.
|
|
151
|
+
*/
|
|
152
|
+
deleteEdge() {
|
|
153
|
+
this.from.removeOuterEdge(this);
|
|
154
|
+
this.to.removeInnerEdge(this);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Compares Edge equallity.
|
|
158
|
+
*
|
|
159
|
+
* An edge is equal to another, if the from and to nodes are the same.
|
|
160
|
+
*
|
|
161
|
+
* @param {Edge} edge - Another Edge
|
|
162
|
+
* @returns {boolean} - True if Edges are equal, False otherwise
|
|
163
|
+
*/
|
|
164
|
+
isEqual(edge) {
|
|
165
|
+
return this.from.id === edge.from.id && this.to.id === edge.to.id;
|
|
166
|
+
}
|
|
167
|
+
toString() {
|
|
168
|
+
return `Edge { ${this.from.id} -> ${this.to.id} }`;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Returns a LineString representation of the Edge
|
|
172
|
+
*
|
|
173
|
+
* @returns {Feature<LineString>} - LineString representation of the Edge
|
|
174
|
+
*/
|
|
175
|
+
toLineString() {
|
|
176
|
+
return _helpers.lineString.call(void 0, [this.from.coordinates, this.to.coordinates]);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Comparator of two edges.
|
|
180
|
+
*
|
|
181
|
+
* Implementation of geos::planargraph::DirectedEdge::compareTo.
|
|
182
|
+
*
|
|
183
|
+
* @param {Edge} edge - Another edge to compare with this one
|
|
184
|
+
* @returns {number} -1 if this Edge has a greater angle with the positive x-axis than b,
|
|
185
|
+
* 0 if the Edges are colinear,
|
|
186
|
+
* 1 otherwise
|
|
187
|
+
*/
|
|
188
|
+
compareTo(edge) {
|
|
189
|
+
return orientationIndex(
|
|
190
|
+
edge.from.coordinates,
|
|
191
|
+
edge.to.coordinates,
|
|
192
|
+
this.to.coordinates
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
__name(_Edge, "Edge");
|
|
197
|
+
var Edge = _Edge;
|
|
198
|
+
|
|
199
|
+
// lib/EdgeRing.ts
|
|
200
|
+
|
|
201
|
+
var _envelope = require('@turf/envelope');
|
|
202
|
+
|
|
203
|
+
var _EdgeRing = class _EdgeRing {
|
|
204
|
+
constructor() {
|
|
205
|
+
this.edges = [];
|
|
206
|
+
this.polygon = void 0;
|
|
207
|
+
this.envelope = void 0;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Add an edge to the ring, inserting it in the last position.
|
|
211
|
+
*
|
|
212
|
+
* @memberof EdgeRing
|
|
213
|
+
* @param {Edge} edge - Edge to be inserted
|
|
214
|
+
*/
|
|
215
|
+
push(edge) {
|
|
216
|
+
this.edges.push(edge);
|
|
217
|
+
this.polygon = this.envelope = void 0;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Get Edge.
|
|
221
|
+
*
|
|
222
|
+
* @memberof EdgeRing
|
|
223
|
+
* @param {number} i - Index
|
|
224
|
+
* @returns {Edge} - Edge in the i position
|
|
225
|
+
*/
|
|
226
|
+
get(i) {
|
|
227
|
+
return this.edges[i];
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Getter of length property.
|
|
231
|
+
*
|
|
232
|
+
* @memberof EdgeRing
|
|
233
|
+
* @returns {number} - Length of the edge ring.
|
|
234
|
+
*/
|
|
235
|
+
get length() {
|
|
236
|
+
return this.edges.length;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Similar to Array.prototype.forEach for the list of Edges in the EdgeRing.
|
|
240
|
+
*
|
|
241
|
+
* @memberof EdgeRing
|
|
242
|
+
* @param {Function} f - The same function to be passed to Array.prototype.forEach
|
|
243
|
+
*/
|
|
244
|
+
forEach(f) {
|
|
245
|
+
this.edges.forEach(f);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Similar to Array.prototype.map for the list of Edges in the EdgeRing.
|
|
249
|
+
*
|
|
250
|
+
* @memberof EdgeRing
|
|
251
|
+
* @param {Function} f - The same function to be passed to Array.prototype.map
|
|
252
|
+
* @returns {Array} - The mapped values in the function
|
|
253
|
+
*/
|
|
254
|
+
map(f) {
|
|
255
|
+
return this.edges.map(f);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Similar to Array.prototype.some for the list of Edges in the EdgeRing.
|
|
259
|
+
*
|
|
260
|
+
* @memberof EdgeRing
|
|
261
|
+
* @param {Function} f - The same function to be passed to Array.prototype.some
|
|
262
|
+
* @returns {boolean} - True if an Edge check the condition
|
|
263
|
+
*/
|
|
264
|
+
some(f) {
|
|
265
|
+
return this.edges.some(f);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Check if the ring is valid in geomtry terms.
|
|
269
|
+
*
|
|
270
|
+
* A ring must have either 0 or 4 or more points. The first and the last must be
|
|
271
|
+
* equal (in 2D)
|
|
272
|
+
* geos::geom::LinearRing::validateConstruction
|
|
273
|
+
*
|
|
274
|
+
* @memberof EdgeRing
|
|
275
|
+
* @returns {boolean} - Validity of the EdgeRing
|
|
276
|
+
*/
|
|
277
|
+
isValid() {
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Tests whether this ring is a hole.
|
|
282
|
+
*
|
|
283
|
+
* A ring is a hole if it is oriented counter-clockwise.
|
|
284
|
+
* Similar implementation of geos::algorithm::CGAlgorithms::isCCW
|
|
285
|
+
*
|
|
286
|
+
* @memberof EdgeRing
|
|
287
|
+
* @returns {boolean} - true: if it is a hole
|
|
288
|
+
*/
|
|
289
|
+
isHole() {
|
|
290
|
+
const hiIndex = this.edges.reduce((high, edge, i) => {
|
|
291
|
+
if (edge.from.coordinates[1] > this.edges[high].from.coordinates[1])
|
|
292
|
+
high = i;
|
|
293
|
+
return high;
|
|
294
|
+
}, 0), iPrev = (hiIndex === 0 ? this.length : hiIndex) - 1, iNext = (hiIndex + 1) % this.length, disc = orientationIndex(
|
|
295
|
+
this.edges[iPrev].from.coordinates,
|
|
296
|
+
this.edges[hiIndex].from.coordinates,
|
|
297
|
+
this.edges[iNext].from.coordinates
|
|
298
|
+
);
|
|
299
|
+
if (disc === 0)
|
|
300
|
+
return this.edges[iPrev].from.coordinates[0] > this.edges[iNext].from.coordinates[0];
|
|
301
|
+
return disc > 0;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Creates a MultiPoint representing the EdgeRing (discarts edges directions).
|
|
305
|
+
*
|
|
306
|
+
* @memberof EdgeRing
|
|
307
|
+
* @returns {Feature<MultiPoint>} - Multipoint representation of the EdgeRing
|
|
308
|
+
*/
|
|
309
|
+
toMultiPoint() {
|
|
310
|
+
return _helpers.multiPoint.call(void 0, this.edges.map((edge) => edge.from.coordinates));
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Creates a Polygon representing the EdgeRing.
|
|
314
|
+
*
|
|
315
|
+
* @memberof EdgeRing
|
|
316
|
+
* @returns {Feature<Polygon>} - Polygon representation of the Edge Ring
|
|
317
|
+
*/
|
|
318
|
+
toPolygon() {
|
|
319
|
+
if (this.polygon)
|
|
320
|
+
return this.polygon;
|
|
321
|
+
const coordinates = this.edges.map((edge) => edge.from.coordinates);
|
|
322
|
+
coordinates.push(this.edges[0].from.coordinates);
|
|
323
|
+
return this.polygon = _helpers.polygon.call(void 0, [coordinates]);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Calculates the envelope of the EdgeRing.
|
|
327
|
+
*
|
|
328
|
+
* @memberof EdgeRing
|
|
329
|
+
* @returns {Feature<Polygon>} - envelope
|
|
330
|
+
*/
|
|
331
|
+
getEnvelope() {
|
|
332
|
+
if (this.envelope)
|
|
333
|
+
return this.envelope;
|
|
334
|
+
return this.envelope = _envelope.envelope.call(void 0, this.toPolygon());
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* `geos::operation::polygonize::EdgeRing::findEdgeRingContaining`
|
|
338
|
+
*
|
|
339
|
+
* @param {EdgeRing} testEdgeRing - EdgeRing to look in the list
|
|
340
|
+
* @param {EdgeRing[]} shellList - List of EdgeRing in which to search
|
|
341
|
+
*
|
|
342
|
+
* @returns {EdgeRing} - EdgeRing which contains the testEdgeRing
|
|
343
|
+
*/
|
|
344
|
+
static findEdgeRingContaining(testEdgeRing, shellList) {
|
|
345
|
+
const testEnvelope = testEdgeRing.getEnvelope();
|
|
346
|
+
let minEnvelope, minShell;
|
|
347
|
+
shellList.forEach((shell) => {
|
|
348
|
+
const tryEnvelope = shell.getEnvelope();
|
|
349
|
+
if (minShell)
|
|
350
|
+
minEnvelope = minShell.getEnvelope();
|
|
351
|
+
if (envelopeIsEqual(tryEnvelope, testEnvelope))
|
|
352
|
+
return;
|
|
353
|
+
if (envelopeContains(tryEnvelope, testEnvelope)) {
|
|
354
|
+
const testEdgeRingCoordinates = testEdgeRing.map(
|
|
355
|
+
(edge) => edge.from.coordinates
|
|
356
|
+
);
|
|
357
|
+
let testPoint;
|
|
358
|
+
for (const pt of testEdgeRingCoordinates) {
|
|
359
|
+
if (!shell.some((edge) => coordinatesEqual(pt, edge.from.coordinates))) {
|
|
360
|
+
testPoint = pt;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (testPoint && shell.inside(_helpers.point.call(void 0, testPoint))) {
|
|
364
|
+
if (!minShell || envelopeContains(minEnvelope, tryEnvelope))
|
|
365
|
+
minShell = shell;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
return minShell;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Checks if the point is inside the edgeRing
|
|
373
|
+
*
|
|
374
|
+
* @param {Feature<Point>} pt - Point to check if it is inside the edgeRing
|
|
375
|
+
* @returns {boolean} - True if it is inside, False otherwise
|
|
376
|
+
*/
|
|
377
|
+
inside(pt) {
|
|
378
|
+
return _booleanpointinpolygon.booleanPointInPolygon.call(void 0, pt, this.toPolygon());
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
__name(_EdgeRing, "EdgeRing");
|
|
382
|
+
var EdgeRing = _EdgeRing;
|
|
383
|
+
|
|
384
|
+
// lib/Graph.ts
|
|
385
|
+
var _meta = require('@turf/meta');
|
|
386
|
+
var _invariant = require('@turf/invariant');
|
|
387
|
+
function validateGeoJson(geoJson) {
|
|
388
|
+
if (!geoJson)
|
|
389
|
+
throw new Error("No geojson passed");
|
|
390
|
+
if (geoJson.type !== "FeatureCollection" && geoJson.type !== "GeometryCollection" && geoJson.type !== "MultiLineString" && geoJson.type !== "LineString" && geoJson.type !== "Feature")
|
|
391
|
+
throw new Error(
|
|
392
|
+
`Invalid input type '${geoJson.type}'. Geojson must be FeatureCollection, GeometryCollection, LineString, MultiLineString or Feature`
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
__name(validateGeoJson, "validateGeoJson");
|
|
396
|
+
var _Graph = class _Graph {
|
|
397
|
+
/**
|
|
398
|
+
* Creates a graph from a GeoJSON.
|
|
399
|
+
*
|
|
400
|
+
* @param {FeatureCollection<LineString>} geoJson - it must comply with the restrictions detailed in the index
|
|
401
|
+
* @returns {Graph} - The newly created graph
|
|
402
|
+
* @throws {Error} if geoJson is invalid.
|
|
403
|
+
*/
|
|
404
|
+
static fromGeoJson(geoJson) {
|
|
405
|
+
validateGeoJson(geoJson);
|
|
406
|
+
const graph = new _Graph();
|
|
407
|
+
_meta.flattenEach.call(void 0, geoJson, (feature) => {
|
|
408
|
+
_invariant.featureOf.call(void 0, feature, "LineString", "Graph::fromGeoJson");
|
|
409
|
+
_meta.coordReduce.call(void 0, feature, (prev, cur) => {
|
|
410
|
+
if (prev) {
|
|
411
|
+
const start = graph.getNode(prev), end = graph.getNode(cur);
|
|
412
|
+
graph.addEdge(start, end);
|
|
413
|
+
}
|
|
414
|
+
return cur;
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
return graph;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Creates or get a Node.
|
|
421
|
+
*
|
|
422
|
+
* @param {number[]} coordinates - Coordinates of the node
|
|
423
|
+
* @returns {Node} - The created or stored node
|
|
424
|
+
*/
|
|
425
|
+
getNode(coordinates) {
|
|
426
|
+
const id = Node.buildId(coordinates);
|
|
427
|
+
let node = this.nodes[id];
|
|
428
|
+
if (!node)
|
|
429
|
+
node = this.nodes[id] = new Node(coordinates);
|
|
430
|
+
return node;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Adds an Edge and its symetricall.
|
|
434
|
+
*
|
|
435
|
+
* Edges are added symetrically, i.e.: we also add its symetric
|
|
436
|
+
*
|
|
437
|
+
* @param {Node} from - Node which starts the Edge
|
|
438
|
+
* @param {Node} to - Node which ends the Edge
|
|
439
|
+
*/
|
|
440
|
+
addEdge(from, to) {
|
|
441
|
+
const edge = new Edge(from, to), symetricEdge = edge.getSymetric();
|
|
442
|
+
this.edges.push(edge);
|
|
443
|
+
this.edges.push(symetricEdge);
|
|
444
|
+
}
|
|
445
|
+
constructor() {
|
|
446
|
+
this.edges = [];
|
|
447
|
+
this.nodes = {};
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Removes Dangle Nodes (nodes with grade 1).
|
|
451
|
+
*/
|
|
452
|
+
deleteDangles() {
|
|
453
|
+
Object.keys(this.nodes).map((id) => this.nodes[id]).forEach((node) => this._removeIfDangle(node));
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Check if node is dangle, if so, remove it.
|
|
457
|
+
*
|
|
458
|
+
* It calls itself recursively, removing a dangling node might cause another dangling node
|
|
459
|
+
*
|
|
460
|
+
* @param {Node} node - Node to check if it's a dangle
|
|
461
|
+
*/
|
|
462
|
+
_removeIfDangle(node) {
|
|
463
|
+
if (node.innerEdges.length <= 1) {
|
|
464
|
+
const outerNodes = node.getOuterEdges().map((e) => e.to);
|
|
465
|
+
this.removeNode(node);
|
|
466
|
+
outerNodes.forEach((n) => this._removeIfDangle(n));
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Delete cut-edges (bridge edges).
|
|
471
|
+
*
|
|
472
|
+
* The graph will be traversed, all the edges will be labeled according the ring
|
|
473
|
+
* in which they are. (The label is a number incremented by 1). Edges with the same
|
|
474
|
+
* label are cut-edges.
|
|
475
|
+
*/
|
|
476
|
+
deleteCutEdges() {
|
|
477
|
+
this._computeNextCWEdges();
|
|
478
|
+
this._findLabeledEdgeRings();
|
|
479
|
+
this.edges.forEach((edge) => {
|
|
480
|
+
if (edge.label === edge.symetric.label) {
|
|
481
|
+
this.removeEdge(edge.symetric);
|
|
482
|
+
this.removeEdge(edge);
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Set the `next` property of each Edge.
|
|
488
|
+
*
|
|
489
|
+
* The graph will be transversed in a CW form, so, we set the next of the symetrical edge as the previous one.
|
|
490
|
+
* OuterEdges are sorted CCW.
|
|
491
|
+
*
|
|
492
|
+
* @param {Node} [node] - If no node is passed, the function calls itself for every node in the Graph
|
|
493
|
+
*/
|
|
494
|
+
_computeNextCWEdges(node) {
|
|
495
|
+
if (typeof node === "undefined") {
|
|
496
|
+
Object.keys(this.nodes).forEach(
|
|
497
|
+
(id) => this._computeNextCWEdges(this.nodes[id])
|
|
498
|
+
);
|
|
499
|
+
} else {
|
|
500
|
+
node.getOuterEdges().forEach((edge, i) => {
|
|
501
|
+
node.getOuterEdge(
|
|
502
|
+
(i === 0 ? node.getOuterEdges().length : i) - 1
|
|
503
|
+
).symetric.next = edge;
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Computes the next edge pointers going CCW around the given node, for the given edgering label.
|
|
509
|
+
*
|
|
510
|
+
* This algorithm has the effect of converting maximal edgerings into minimal edgerings
|
|
511
|
+
*
|
|
512
|
+
* XXX: method literally transcribed from `geos::operation::polygonize::PolygonizeGraph::computeNextCCWEdges`,
|
|
513
|
+
* could be written in a more javascript way.
|
|
514
|
+
*
|
|
515
|
+
* @param {Node} node - Node
|
|
516
|
+
* @param {number} label - Ring's label
|
|
517
|
+
*/
|
|
518
|
+
_computeNextCCWEdges(node, label) {
|
|
519
|
+
const edges = node.getOuterEdges();
|
|
520
|
+
let firstOutDE, prevInDE;
|
|
521
|
+
for (let i = edges.length - 1; i >= 0; --i) {
|
|
522
|
+
let de = edges[i], sym = de.symetric, outDE, inDE;
|
|
523
|
+
if (de.label === label)
|
|
524
|
+
outDE = de;
|
|
525
|
+
if (sym.label === label)
|
|
526
|
+
inDE = sym;
|
|
527
|
+
if (!outDE || !inDE)
|
|
528
|
+
continue;
|
|
529
|
+
if (inDE)
|
|
530
|
+
prevInDE = inDE;
|
|
531
|
+
if (outDE) {
|
|
532
|
+
if (prevInDE) {
|
|
533
|
+
prevInDE.next = outDE;
|
|
534
|
+
prevInDE = void 0;
|
|
535
|
+
}
|
|
536
|
+
if (!firstOutDE)
|
|
537
|
+
firstOutDE = outDE;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (prevInDE)
|
|
541
|
+
prevInDE.next = firstOutDE;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Finds rings and labels edges according to which rings are.
|
|
545
|
+
*
|
|
546
|
+
* The label is a number which is increased for each ring.
|
|
547
|
+
*
|
|
548
|
+
* @returns {Edge[]} edges that start rings
|
|
549
|
+
*/
|
|
550
|
+
_findLabeledEdgeRings() {
|
|
551
|
+
const edgeRingStarts = [];
|
|
552
|
+
let label = 0;
|
|
553
|
+
this.edges.forEach((edge) => {
|
|
554
|
+
if (edge.label >= 0)
|
|
555
|
+
return;
|
|
556
|
+
edgeRingStarts.push(edge);
|
|
557
|
+
let e = edge;
|
|
558
|
+
do {
|
|
559
|
+
e.label = label;
|
|
560
|
+
e = e.next;
|
|
561
|
+
} while (!edge.isEqual(e));
|
|
562
|
+
label++;
|
|
563
|
+
});
|
|
564
|
+
return edgeRingStarts;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Computes the EdgeRings formed by the edges in this graph.
|
|
568
|
+
*
|
|
569
|
+
* @returns {EdgeRing[]} - A list of all the EdgeRings in the graph.
|
|
570
|
+
*/
|
|
571
|
+
getEdgeRings() {
|
|
572
|
+
this._computeNextCWEdges();
|
|
573
|
+
this.edges.forEach((edge) => {
|
|
574
|
+
edge.label = void 0;
|
|
575
|
+
});
|
|
576
|
+
this._findLabeledEdgeRings().forEach((edge) => {
|
|
577
|
+
this._findIntersectionNodes(edge).forEach((node) => {
|
|
578
|
+
this._computeNextCCWEdges(node, edge.label);
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
const edgeRingList = [];
|
|
582
|
+
this.edges.forEach((edge) => {
|
|
583
|
+
if (edge.ring)
|
|
584
|
+
return;
|
|
585
|
+
edgeRingList.push(this._findEdgeRing(edge));
|
|
586
|
+
});
|
|
587
|
+
return edgeRingList;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Find all nodes in a Maxima EdgeRing which are self-intersection nodes.
|
|
591
|
+
*
|
|
592
|
+
* @param {Node} startEdge - Start Edge of the Ring
|
|
593
|
+
* @returns {Node[]} - intersection nodes
|
|
594
|
+
*/
|
|
595
|
+
_findIntersectionNodes(startEdge) {
|
|
596
|
+
const intersectionNodes = [];
|
|
597
|
+
let edge = startEdge;
|
|
598
|
+
do {
|
|
599
|
+
let degree = 0;
|
|
600
|
+
edge.from.getOuterEdges().forEach((e) => {
|
|
601
|
+
if (e.label === startEdge.label)
|
|
602
|
+
++degree;
|
|
603
|
+
});
|
|
604
|
+
if (degree > 1)
|
|
605
|
+
intersectionNodes.push(edge.from);
|
|
606
|
+
edge = edge.next;
|
|
607
|
+
} while (!startEdge.isEqual(edge));
|
|
608
|
+
return intersectionNodes;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Get the edge-ring which starts from the provided Edge.
|
|
612
|
+
*
|
|
613
|
+
* @param {Edge} startEdge - starting edge of the edge ring
|
|
614
|
+
* @returns {EdgeRing} - EdgeRing which start Edge is the provided one.
|
|
615
|
+
*/
|
|
616
|
+
_findEdgeRing(startEdge) {
|
|
617
|
+
let edge = startEdge;
|
|
618
|
+
const edgeRing = new EdgeRing();
|
|
619
|
+
do {
|
|
620
|
+
edgeRing.push(edge);
|
|
621
|
+
edge.ring = edgeRing;
|
|
622
|
+
edge = edge.next;
|
|
623
|
+
} while (!startEdge.isEqual(edge));
|
|
624
|
+
return edgeRing;
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Removes a node from the Graph.
|
|
628
|
+
*
|
|
629
|
+
* It also removes edges asociated to that node
|
|
630
|
+
* @param {Node} node - Node to be removed
|
|
631
|
+
*/
|
|
632
|
+
removeNode(node) {
|
|
633
|
+
node.getOuterEdges().forEach((edge) => this.removeEdge(edge));
|
|
634
|
+
node.innerEdges.forEach((edge) => this.removeEdge(edge));
|
|
635
|
+
delete this.nodes[node.id];
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Remove edge from the graph and deletes the edge.
|
|
639
|
+
*
|
|
640
|
+
* @param {Edge} edge - Edge to be removed
|
|
641
|
+
*/
|
|
642
|
+
removeEdge(edge) {
|
|
643
|
+
this.edges = this.edges.filter((e) => !e.isEqual(edge));
|
|
644
|
+
edge.deleteEdge();
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
__name(_Graph, "Graph");
|
|
648
|
+
var Graph = _Graph;
|
|
649
|
+
|
|
650
|
+
// index.ts
|
|
651
|
+
function polygonize(geoJson) {
|
|
652
|
+
const graph = Graph.fromGeoJson(geoJson);
|
|
653
|
+
graph.deleteDangles();
|
|
654
|
+
graph.deleteCutEdges();
|
|
655
|
+
const holes = [], shells = [];
|
|
656
|
+
graph.getEdgeRings().filter((edgeRing) => edgeRing.isValid()).forEach((edgeRing) => {
|
|
657
|
+
if (edgeRing.isHole())
|
|
658
|
+
holes.push(edgeRing);
|
|
659
|
+
else
|
|
660
|
+
shells.push(edgeRing);
|
|
661
|
+
});
|
|
662
|
+
holes.forEach((hole) => {
|
|
663
|
+
if (EdgeRing.findEdgeRingContaining(hole, shells))
|
|
664
|
+
shells.push(hole);
|
|
665
|
+
});
|
|
666
|
+
return _helpers.featureCollection.call(void 0, shells.map((shell) => shell.toPolygon()));
|
|
667
|
+
}
|
|
668
|
+
__name(polygonize, "polygonize");
|
|
669
|
+
var turf_polygonize_default = polygonize;
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
exports.default = turf_polygonize_default; exports.polygonize = polygonize;
|
|
674
|
+
//# sourceMappingURL=index.cjs.map
|