index.js 19 KB

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