all these changes

This commit is contained in:
Jake Kasper
2026-04-09 13:19:47 -05:00
parent e83a51a051
commit 65315f36d1
39102 changed files with 7932979 additions and 567 deletions

20
frontend/node_modules/@turf/polygonize/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2017 TurfJS
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

69
frontend/node_modules/@turf/polygonize/README.md generated vendored Normal file
View File

@@ -0,0 +1,69 @@
# @turf/polygonize
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## polygonize
Polygonizes [(Multi)LineString(s)][1] into [Polygons][2].
Implementation of GEOSPolygonize function (`geos::operation::polygonize::Polygonizer`).
Polygonizes a set of lines that represents edges in a planar graph. Edges must be correctly
noded, i.e., they must only meet at their endpoints.
The implementation correctly handles:
- Dangles: edges which have one or both ends which are not incident on another edge endpoint.
- Cut Edges (bridges): edges that are connected at both ends but which do not form part of a polygon.
**Parameters**
- `geoJson` **([FeatureCollection][3] \| [Geometry][4] \| [Feature][5]&lt;([LineString][6] \| [MultiLineString][7])>)** Lines in order to polygonize
- Throws **[Error][8]** if geoJson is invalid.
Returns **[FeatureCollection][3]&lt;[Polygon][9]>** Polygons created
[1]: https://tools.ietf.org/html/rfc7946#section-3.1.4
[2]: https://tools.ietf.org/html/rfc7946#section-3.1.6
[3]: https://tools.ietf.org/html/rfc7946#section-3.3
[4]: https://tools.ietf.org/html/rfc7946#section-3.1
[5]: https://tools.ietf.org/html/rfc7946#section-3.2
[6]: https://tools.ietf.org/html/rfc7946#section-3.1.4
[7]: https://tools.ietf.org/html/rfc7946#section-3.1.5
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[9]: https://tools.ietf.org/html/rfc7946#section-3.1.6
<!-- This file is automatically generated. Please don't edit it directly:
if you find an error, edit the source file (likely index.js), and re-run
./scripts/generate-readmes in the turf project. -->
---
This module is part of the [Turfjs project](http://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.
### Installation
Install this module individually:
```sh
$ npm install @turf/polygonize
```
Or install the Turf module that includes it as a function:
```sh
$ npm install @turf/turf
```

46
frontend/node_modules/@turf/polygonize/dist/es/index.js generated vendored Executable file
View File

@@ -0,0 +1,46 @@
import { featureCollection } from "@turf/helpers";
import Graph from "./lib/Graph.js";
import EdgeRing from "./lib/EdgeRing.js";
/**
* Polygonizes {@link LineString|(Multi)LineString(s)} into {@link Polygons}.
*
* Implementation of GEOSPolygonize function (`geos::operation::polygonize::Polygonizer`).
*
* Polygonizes a set of lines that represents edges in a planar graph. Edges must be correctly
* noded, i.e., they must only meet at their endpoints.
*
* The implementation correctly handles:
*
* - Dangles: edges which have one or both ends which are not incident on another edge endpoint.
* - Cut Edges (bridges): edges that are connected at both ends but which do not form part of a polygon.
*
* @name polygonize
* @param {FeatureCollection|Geometry|Feature<LineString|MultiLineString>} geoJson Lines in order to polygonize
* @returns {FeatureCollection<Polygon>} Polygons created
* @throws {Error} if geoJson is invalid.
*/
export default function polygonize(geoJson) {
var graph = Graph.fromGeoJson(geoJson);
// 1. Remove dangle node
graph.deleteDangles();
// 2. Remove cut-edges (bridge edges)
graph.deleteCutEdges();
// 3. Get all holes and shells
var holes = [], shells = [];
graph
.getEdgeRings()
.filter(function (edgeRing) { return edgeRing.isValid(); })
.forEach(function (edgeRing) {
if (edgeRing.isHole())
holes.push(edgeRing);
else
shells.push(edgeRing);
});
// 4. Assign Holes to Shells
holes.forEach(function (hole) {
if (EdgeRing.findEdgeRingContaining(hole, shells))
shells.push(hole);
});
// 5. EdgeRings to Polygons
return featureCollection(shells.map(function (shell) { return shell.toPolygon(); }));
}

View File

@@ -0,0 +1,77 @@
import { lineString } from "@turf/helpers";
import { orientationIndex } from "./util.js";
/**
* This class is inspired by GEOS's geos::operation::polygonize::PolygonizeDirectedEdge
*/
var Edge = /** @class */ (function () {
/**
* @param {Node} from - start node of the Edge
* @param {Node} to - end node of the edge
*/
function Edge(from, to) {
this.from = from; //< start
this.to = to; //< End
this.next = undefined; //< The edge to be computed after
this.label = undefined; //< Used in order to detect Cut Edges (Bridges)
this.symetric = undefined; //< The symetric edge of this
this.ring = undefined; //< EdgeRing in which the Edge is
this.from.addOuterEdge(this);
this.to.addInnerEdge(this);
}
/**
* Creates or get the symetric Edge.
*
* @returns {Edge} - Symetric Edge.
*/
Edge.prototype.getSymetric = function () {
if (!this.symetric) {
this.symetric = new Edge(this.to, this.from);
this.symetric.symetric = this;
}
return this.symetric;
};
/**
* Removes edge from from and to nodes.
*/
Edge.prototype.deleteEdge = function () {
this.from.removeOuterEdge(this);
this.to.removeInnerEdge(this);
};
/**
* Compares Edge equallity.
*
* An edge is equal to another, if the from and to nodes are the same.
*
* @param {Edge} edge - Another Edge
* @returns {boolean} - True if Edges are equal, False otherwise
*/
Edge.prototype.isEqual = function (edge) {
return this.from.id === edge.from.id && this.to.id === edge.to.id;
};
Edge.prototype.toString = function () {
return "Edge { " + this.from.id + " -> " + this.to.id + " }";
};
/**
* Returns a LineString representation of the Edge
*
* @returns {Feature<LineString>} - LineString representation of the Edge
*/
Edge.prototype.toLineString = function () {
return lineString([this.from.coordinates, this.to.coordinates]);
};
/**
* Comparator of two edges.
*
* Implementation of geos::planargraph::DirectedEdge::compareTo.
*
* @param {Edge} edge - Another edge to compare with this one
* @returns {number} -1 if this Edge has a greater angle with the positive x-axis than b,
* 0 if the Edges are colinear,
* 1 otherwise
*/
Edge.prototype.compareTo = function (edge) {
return orientationIndex(edge.from.coordinates, edge.to.coordinates, this.to.coordinates);
};
return Edge;
}());
export default Edge;

View File

@@ -0,0 +1,199 @@
import { orientationIndex, envelopeIsEqual, envelopeContains, coordinatesEqual, } from "./util.js";
import { multiPoint, polygon, point, } from "@turf/helpers";
import envelope from "@turf/envelope";
import booleanPointInPolygon from "@turf/boolean-point-in-polygon";
/**
* Ring of edges which form a polygon.
*
* The ring may be either an outer shell or a hole.
*
* This class is inspired in GEOS's geos::operation::polygonize::EdgeRing
*/
var EdgeRing = /** @class */ (function () {
function EdgeRing() {
this.edges = [];
this.polygon = undefined; //< Caches Polygon representation
this.envelope = undefined; //< Caches Envelope representation
}
/**
* Add an edge to the ring, inserting it in the last position.
*
* @memberof EdgeRing
* @param {Edge} edge - Edge to be inserted
*/
EdgeRing.prototype.push = function (edge) {
this.edges.push(edge);
this.polygon = this.envelope = undefined;
};
/**
* Get Edge.
*
* @memberof EdgeRing
* @param {number} i - Index
* @returns {Edge} - Edge in the i position
*/
EdgeRing.prototype.get = function (i) {
return this.edges[i];
};
Object.defineProperty(EdgeRing.prototype, "length", {
/**
* Getter of length property.
*
* @memberof EdgeRing
* @returns {number} - Length of the edge ring.
*/
get: function () {
return this.edges.length;
},
enumerable: true,
configurable: true
});
/**
* Similar to Array.prototype.forEach for the list of Edges in the EdgeRing.
*
* @memberof EdgeRing
* @param {Function} f - The same function to be passed to Array.prototype.forEach
*/
EdgeRing.prototype.forEach = function (f) {
this.edges.forEach(f);
};
/**
* Similar to Array.prototype.map for the list of Edges in the EdgeRing.
*
* @memberof EdgeRing
* @param {Function} f - The same function to be passed to Array.prototype.map
* @returns {Array} - The mapped values in the function
*/
EdgeRing.prototype.map = function (f) {
return this.edges.map(f);
};
/**
* Similar to Array.prototype.some for the list of Edges in the EdgeRing.
*
* @memberof EdgeRing
* @param {Function} f - The same function to be passed to Array.prototype.some
* @returns {boolean} - True if an Edge check the condition
*/
EdgeRing.prototype.some = function (f) {
return this.edges.some(f);
};
/**
* Check if the ring is valid in geomtry terms.
*
* A ring must have either 0 or 4 or more points. The first and the last must be
* equal (in 2D)
* geos::geom::LinearRing::validateConstruction
*
* @memberof EdgeRing
* @returns {boolean} - Validity of the EdgeRing
*/
EdgeRing.prototype.isValid = function () {
// TODO: stub
return true;
};
/**
* Tests whether this ring is a hole.
*
* A ring is a hole if it is oriented counter-clockwise.
* Similar implementation of geos::algorithm::CGAlgorithms::isCCW
*
* @memberof EdgeRing
* @returns {boolean} - true: if it is a hole
*/
EdgeRing.prototype.isHole = function () {
var _this = this;
// XXX: Assuming Ring is valid
// Find highest point
var hiIndex = this.edges.reduce(function (high, edge, i) {
if (edge.from.coordinates[1] > _this.edges[high].from.coordinates[1])
high = i;
return high;
}, 0), iPrev = (hiIndex === 0 ? this.length : hiIndex) - 1, iNext = (hiIndex + 1) % this.length, disc = orientationIndex(this.edges[iPrev].from.coordinates, this.edges[hiIndex].from.coordinates, this.edges[iNext].from.coordinates);
if (disc === 0)
return (this.edges[iPrev].from.coordinates[0] >
this.edges[iNext].from.coordinates[0]);
return disc > 0;
};
/**
* Creates a MultiPoint representing the EdgeRing (discarts edges directions).
*
* @memberof EdgeRing
* @returns {Feature<MultiPoint>} - Multipoint representation of the EdgeRing
*/
EdgeRing.prototype.toMultiPoint = function () {
return multiPoint(this.edges.map(function (edge) { return edge.from.coordinates; }));
};
/**
* Creates a Polygon representing the EdgeRing.
*
* @memberof EdgeRing
* @returns {Feature<Polygon>} - Polygon representation of the Edge Ring
*/
EdgeRing.prototype.toPolygon = function () {
if (this.polygon)
return this.polygon;
var coordinates = this.edges.map(function (edge) { return edge.from.coordinates; });
coordinates.push(this.edges[0].from.coordinates);
return (this.polygon = polygon([coordinates]));
};
/**
* Calculates the envelope of the EdgeRing.
*
* @memberof EdgeRing
* @returns {Feature<Polygon>} - envelope
*/
EdgeRing.prototype.getEnvelope = function () {
if (this.envelope)
return this.envelope;
return (this.envelope = envelope(this.toPolygon()));
};
/**
* `geos::operation::polygonize::EdgeRing::findEdgeRingContaining`
*
* @param {EdgeRing} testEdgeRing - EdgeRing to look in the list
* @param {EdgeRing[]} shellList - List of EdgeRing in which to search
*
* @returns {EdgeRing} - EdgeRing which contains the testEdgeRing
*/
EdgeRing.findEdgeRingContaining = function (testEdgeRing, shellList) {
var testEnvelope = testEdgeRing.getEnvelope();
var minEnvelope, minShell;
shellList.forEach(function (shell) {
var tryEnvelope = shell.getEnvelope();
if (minShell)
minEnvelope = minShell.getEnvelope();
// the hole envelope cannot equal the shell envelope
if (envelopeIsEqual(tryEnvelope, testEnvelope))
return;
if (envelopeContains(tryEnvelope, testEnvelope)) {
var testEdgeRingCoordinates = testEdgeRing.map(function (edge) { return edge.from.coordinates; });
var testPoint = void 0;
var _loop_1 = function (pt) {
if (!shell.some(function (edge) { return coordinatesEqual(pt, edge.from.coordinates); })) {
testPoint = pt;
}
};
for (var _i = 0, testEdgeRingCoordinates_1 = testEdgeRingCoordinates; _i < testEdgeRingCoordinates_1.length; _i++) {
var pt = testEdgeRingCoordinates_1[_i];
_loop_1(pt);
}
if (testPoint && shell.inside(point(testPoint))) {
if (!minShell || envelopeContains(minEnvelope, tryEnvelope))
minShell = shell;
}
}
});
return minShell;
};
/**
* Checks if the point is inside the edgeRing
*
* @param {Feature<Point>} pt - Point to check if it is inside the edgeRing
* @returns {boolean} - True if it is inside, False otherwise
*/
EdgeRing.prototype.inside = function (pt) {
return booleanPointInPolygon(pt, this.toPolygon());
};
return EdgeRing;
}());
export default EdgeRing;

View File

@@ -0,0 +1,302 @@
import Node from "./Node.js";
import Edge from "./Edge.js";
import EdgeRing from "./EdgeRing.js";
import { flattenEach, coordReduce } from "@turf/meta";
import { featureOf } from "@turf/invariant";
/**
* Validates the geoJson.
*
* @param {GeoJSON} geoJson - input geoJson.
* @throws {Error} if geoJson is invalid.
*/
function validateGeoJson(geoJson) {
if (!geoJson)
throw new Error("No geojson passed");
if (geoJson.type !== "FeatureCollection" &&
geoJson.type !== "GeometryCollection" &&
geoJson.type !== "MultiLineString" &&
geoJson.type !== "LineString" &&
geoJson.type !== "Feature")
throw new Error("Invalid input type '" + geoJson.type + "'. Geojson must be FeatureCollection, GeometryCollection, LineString, MultiLineString or Feature");
}
/**
* Represents a planar graph of edges and nodes that can be used to compute a polygonization.
*
* Although, this class is inspired by GEOS's `geos::operation::polygonize::PolygonizeGraph`,
* it isn't a rewrite. As regards algorithm, this class implements the same logic, but it
* isn't a javascript transcription of the C++ source.
*
* This graph is directed (both directions are created)
*/
var Graph = /** @class */ (function () {
function Graph() {
this.edges = []; //< {Edge[]} dirEdges
// The key is the `id` of the Node (ie: coordinates.join(','))
this.nodes = {};
}
/**
* Creates a graph from a GeoJSON.
*
* @param {FeatureCollection<LineString>} geoJson - it must comply with the restrictions detailed in the index
* @returns {Graph} - The newly created graph
* @throws {Error} if geoJson is invalid.
*/
Graph.fromGeoJson = function (geoJson) {
validateGeoJson(geoJson);
var graph = new Graph();
flattenEach(geoJson, function (feature) {
featureOf(feature, "LineString", "Graph::fromGeoJson");
// When a LineString if formed by many segments, split them
coordReduce(feature, function (prev, cur) {
if (prev) {
var start = graph.getNode(prev), end = graph.getNode(cur);
graph.addEdge(start, end);
}
return cur;
});
});
return graph;
};
/**
* Creates or get a Node.
*
* @param {number[]} coordinates - Coordinates of the node
* @returns {Node} - The created or stored node
*/
Graph.prototype.getNode = function (coordinates) {
var id = Node.buildId(coordinates);
var node = this.nodes[id];
if (!node)
node = this.nodes[id] = new Node(coordinates);
return node;
};
/**
* Adds an Edge and its symetricall.
*
* Edges are added symetrically, i.e.: we also add its symetric
*
* @param {Node} from - Node which starts the Edge
* @param {Node} to - Node which ends the Edge
*/
Graph.prototype.addEdge = function (from, to) {
var edge = new Edge(from, to), symetricEdge = edge.getSymetric();
this.edges.push(edge);
this.edges.push(symetricEdge);
};
/**
* Removes Dangle Nodes (nodes with grade 1).
*/
Graph.prototype.deleteDangles = function () {
var _this = this;
Object.keys(this.nodes)
.map(function (id) { return _this.nodes[id]; })
.forEach(function (node) { return _this._removeIfDangle(node); });
};
/**
* Check if node is dangle, if so, remove it.
*
* It calls itself recursively, removing a dangling node might cause another dangling node
*
* @param {Node} node - Node to check if it's a dangle
*/
Graph.prototype._removeIfDangle = function (node) {
var _this = this;
// As edges are directed and symetrical, we count only innerEdges
if (node.innerEdges.length <= 1) {
var outerNodes = node.getOuterEdges().map(function (e) { return e.to; });
this.removeNode(node);
outerNodes.forEach(function (n) { return _this._removeIfDangle(n); });
}
};
/**
* Delete cut-edges (bridge edges).
*
* The graph will be traversed, all the edges will be labeled according the ring
* in which they are. (The label is a number incremented by 1). Edges with the same
* label are cut-edges.
*/
Graph.prototype.deleteCutEdges = function () {
var _this = this;
this._computeNextCWEdges();
this._findLabeledEdgeRings();
// Cut-edges (bridges) are edges where both edges have the same label
this.edges.forEach(function (edge) {
if (edge.label === edge.symetric.label) {
_this.removeEdge(edge.symetric);
_this.removeEdge(edge);
}
});
};
/**
* Set the `next` property of each Edge.
*
* The graph will be transversed in a CW form, so, we set the next of the symetrical edge as the previous one.
* OuterEdges are sorted CCW.
*
* @param {Node} [node] - If no node is passed, the function calls itself for every node in the Graph
*/
Graph.prototype._computeNextCWEdges = function (node) {
var _this = this;
if (typeof node === "undefined") {
Object.keys(this.nodes).forEach(function (id) {
return _this._computeNextCWEdges(_this.nodes[id]);
});
}
else {
node.getOuterEdges().forEach(function (edge, i) {
node.getOuterEdge((i === 0 ? node.getOuterEdges().length : i) - 1).symetric.next = edge;
});
}
};
/**
* Computes the next edge pointers going CCW around the given node, for the given edgering label.
*
* This algorithm has the effect of converting maximal edgerings into minimal edgerings
*
* XXX: method literally transcribed from `geos::operation::polygonize::PolygonizeGraph::computeNextCCWEdges`,
* could be written in a more javascript way.
*
* @param {Node} node - Node
* @param {number} label - Ring's label
*/
Graph.prototype._computeNextCCWEdges = function (node, label) {
var edges = node.getOuterEdges();
var firstOutDE, prevInDE;
for (var i = edges.length - 1; i >= 0; --i) {
var de = edges[i], sym = de.symetric, outDE = void 0, inDE = void 0;
if (de.label === label)
outDE = de;
if (sym.label === label)
inDE = sym;
if (!outDE || !inDE)
// This edge is not in edgering
continue;
if (inDE)
prevInDE = inDE;
if (outDE) {
if (prevInDE) {
prevInDE.next = outDE;
prevInDE = undefined;
}
if (!firstOutDE)
firstOutDE = outDE;
}
}
if (prevInDE)
prevInDE.next = firstOutDE;
};
/**
* Finds rings and labels edges according to which rings are.
*
* The label is a number which is increased for each ring.
*
* @returns {Edge[]} edges that start rings
*/
Graph.prototype._findLabeledEdgeRings = function () {
var edgeRingStarts = [];
var label = 0;
this.edges.forEach(function (edge) {
if (edge.label >= 0)
return;
edgeRingStarts.push(edge);
var e = edge;
do {
e.label = label;
e = e.next;
} while (!edge.isEqual(e));
label++;
});
return edgeRingStarts;
};
/**
* Computes the EdgeRings formed by the edges in this graph.
*
* @returns {EdgeRing[]} - A list of all the EdgeRings in the graph.
*/
Graph.prototype.getEdgeRings = function () {
var _this = this;
this._computeNextCWEdges();
// Clear labels
this.edges.forEach(function (edge) {
edge.label = undefined;
});
this._findLabeledEdgeRings().forEach(function (edge) {
// convertMaximalToMinimalEdgeRings
_this._findIntersectionNodes(edge).forEach(function (node) {
_this._computeNextCCWEdges(node, edge.label);
});
});
var edgeRingList = [];
// find all edgerings
this.edges.forEach(function (edge) {
if (edge.ring)
return;
edgeRingList.push(_this._findEdgeRing(edge));
});
return edgeRingList;
};
/**
* Find all nodes in a Maxima EdgeRing which are self-intersection nodes.
*
* @param {Node} startEdge - Start Edge of the Ring
* @returns {Node[]} - intersection nodes
*/
Graph.prototype._findIntersectionNodes = function (startEdge) {
var intersectionNodes = [];
var edge = startEdge;
var _loop_1 = function () {
// getDegree
var degree = 0;
edge.from.getOuterEdges().forEach(function (e) {
if (e.label === startEdge.label)
++degree;
});
if (degree > 1)
intersectionNodes.push(edge.from);
edge = edge.next;
};
do {
_loop_1();
} while (!startEdge.isEqual(edge));
return intersectionNodes;
};
/**
* Get the edge-ring which starts from the provided Edge.
*
* @param {Edge} startEdge - starting edge of the edge ring
* @returns {EdgeRing} - EdgeRing which start Edge is the provided one.
*/
Graph.prototype._findEdgeRing = function (startEdge) {
var edge = startEdge;
var edgeRing = new EdgeRing();
do {
edgeRing.push(edge);
edge.ring = edgeRing;
edge = edge.next;
} while (!startEdge.isEqual(edge));
return edgeRing;
};
/**
* Removes a node from the Graph.
*
* It also removes edges asociated to that node
* @param {Node} node - Node to be removed
*/
Graph.prototype.removeNode = function (node) {
var _this = this;
node.getOuterEdges().forEach(function (edge) { return _this.removeEdge(edge); });
node.innerEdges.forEach(function (edge) { return _this.removeEdge(edge); });
delete this.nodes[node.id];
};
/**
* Remove edge from the graph and deletes the edge.
*
* @param {Edge} edge - Edge to be removed
*/
Graph.prototype.removeEdge = function (edge) {
this.edges = this.edges.filter(function (e) { return !e.isEqual(edge); });
edge.deleteEdge();
};
return Graph;
}());
export default Graph;

View File

@@ -0,0 +1,93 @@
import { orientationIndex } from "./util.js";
/**
* Node
*/
var Node = /** @class */ (function () {
function Node(coordinates) {
this.id = Node.buildId(coordinates);
this.coordinates = coordinates; //< {Number[]}
this.innerEdges = []; //< {Edge[]}
// We wil store to (out) edges in an CCW order as geos::planargraph::DirectedEdgeStar does
this.outerEdges = []; //< {Edge[]}
this.outerEdgesSorted = false; //< {Boolean} flag that stores if the outer Edges had been sorted
}
Node.buildId = function (coordinates) {
return coordinates.join(",");
};
Node.prototype.removeInnerEdge = function (edge) {
this.innerEdges = this.innerEdges.filter(function (e) { return e.from.id !== edge.from.id; });
};
Node.prototype.removeOuterEdge = function (edge) {
this.outerEdges = this.outerEdges.filter(function (e) { return e.to.id !== edge.to.id; });
};
/**
* Outer edges are stored CCW order.
*
* @memberof Node
* @param {Edge} edge - Edge to add as an outerEdge.
*/
Node.prototype.addOuterEdge = function (edge) {
this.outerEdges.push(edge);
this.outerEdgesSorted = false;
};
/**
* Sorts outer edges in CCW way.
*
* @memberof Node
* @private
*/
Node.prototype.sortOuterEdges = function () {
var _this = this;
if (!this.outerEdgesSorted) {
//this.outerEdges.sort((a, b) => a.compareTo(b));
// Using this comparator in order to be deterministic
this.outerEdges.sort(function (a, b) {
var aNode = a.to, bNode = b.to;
if (aNode.coordinates[0] - _this.coordinates[0] >= 0 &&
bNode.coordinates[0] - _this.coordinates[0] < 0)
return 1;
if (aNode.coordinates[0] - _this.coordinates[0] < 0 &&
bNode.coordinates[0] - _this.coordinates[0] >= 0)
return -1;
if (aNode.coordinates[0] - _this.coordinates[0] === 0 &&
bNode.coordinates[0] - _this.coordinates[0] === 0) {
if (aNode.coordinates[1] - _this.coordinates[1] >= 0 ||
bNode.coordinates[1] - _this.coordinates[1] >= 0)
return aNode.coordinates[1] - bNode.coordinates[1];
return bNode.coordinates[1] - aNode.coordinates[1];
}
var det = orientationIndex(_this.coordinates, aNode.coordinates, bNode.coordinates);
if (det < 0)
return 1;
if (det > 0)
return -1;
var 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);
return d1 - d2;
});
this.outerEdgesSorted = true;
}
};
/**
* Retrieves outer edges.
*
* They are sorted if they aren't in the CCW order.
*
* @memberof Node
* @returns {Edge[]} - List of outer edges sorted in a CCW order.
*/
Node.prototype.getOuterEdges = function () {
this.sortOuterEdges();
return this.outerEdges;
};
Node.prototype.getOuterEdge = function (i) {
this.sortOuterEdges();
return this.outerEdges[i];
};
Node.prototype.addInnerEdge = function (edge) {
this.innerEdges.push(edge);
};
return Node;
}());
export default Node;

View File

@@ -0,0 +1,66 @@
import booleanPointInPolygon from "@turf/boolean-point-in-polygon";
import { point } from "@turf/helpers";
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign#Polyfill
function mathSign(x) {
return ((x > 0) - (x < 0) || +x);
}
/**
* Returns the direction of the point q relative to the vector p1 -> p2.
*
* Implementation of geos::algorithm::CGAlgorithm::orientationIndex()
* (same as geos::algorithm::CGAlgorithm::computeOrientation())
*
* @param {number[]} p1 - the origin point of the vector
* @param {number[]} p2 - the final point of the vector
* @param {number[]} q - the point to compute the direction to
*
* @returns {number} - 1 if q is ccw (left) from p1->p2,
* -1 if q is cw (right) from p1->p2,
* 0 if q is colinear with p1->p2
*/
export function orientationIndex(p1, p2, q) {
var dx1 = p2[0] - p1[0], dy1 = p2[1] - p1[1], dx2 = q[0] - p2[0], dy2 = q[1] - p2[1];
return mathSign(dx1 * dy2 - dx2 * dy1);
}
/**
* Checks if two envelopes are equal.
*
* The function assumes that the arguments are envelopes, i.e.: Rectangular polygon
*
* @param {Feature<Polygon>} env1 - Envelope
* @param {Feature<Polygon>} env2 - Envelope
* @returns {boolean} - True if the envelopes are equal
*/
export function envelopeIsEqual(env1, env2) {
var envX1 = env1.geometry.coordinates[0].map(function (c) { return c[0]; }), envY1 = env1.geometry.coordinates[0].map(function (c) { return c[1]; }), envX2 = env2.geometry.coordinates[0].map(function (c) { return c[0]; }), envY2 = env2.geometry.coordinates[0].map(function (c) { return c[1]; });
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));
}
/**
* Check if a envelope is contained in other one.
*
* The function assumes that the arguments are envelopes, i.e.: Convex polygon
* XXX: Envelopes are rectangular, checking if a point is inside a rectangule is something easy,
* this could be further improved.
*
* @param {Feature<Polygon>} self - Envelope
* @param {Feature<Polygon>} env - Envelope
* @returns {boolean} - True if env is contained in self
*/
export function envelopeContains(self, env) {
return env.geometry.coordinates[0].every(function (c) {
return booleanPointInPolygon(point(c), self);
});
}
/**
* Checks if two coordinates are equal.
*
* @param {number[]} coord1 - First coordinate
* @param {number[]} coord2 - Second coordinate
* @returns {boolean} - True if coordinates are equal
*/
export function coordinatesEqual(coord1, coord2) {
return coord1[0] === coord2[0] && coord1[1] === coord2[1];
}

View File

@@ -0,0 +1 @@
{"type":"module"}

View File

@@ -0,0 +1,20 @@
import { Feature, FeatureCollection, LineString, MultiLineString, Polygon } from "@turf/helpers";
/**
* Polygonizes {@link LineString|(Multi)LineString(s)} into {@link Polygons}.
*
* Implementation of GEOSPolygonize function (`geos::operation::polygonize::Polygonizer`).
*
* Polygonizes a set of lines that represents edges in a planar graph. Edges must be correctly
* noded, i.e., they must only meet at their endpoints.
*
* The implementation correctly handles:
*
* - Dangles: edges which have one or both ends which are not incident on another edge endpoint.
* - Cut Edges (bridges): edges that are connected at both ends but which do not form part of a polygon.
*
* @name polygonize
* @param {FeatureCollection|Geometry|Feature<LineString|MultiLineString>} geoJson Lines in order to polygonize
* @returns {FeatureCollection<Polygon>} Polygons created
* @throws {Error} if geoJson is invalid.
*/
export default function polygonize<T extends LineString | MultiLineString>(geoJson: Feature<T> | FeatureCollection<T> | T): FeatureCollection<Polygon>;

52
frontend/node_modules/@turf/polygonize/dist/js/index.js generated vendored Executable file
View File

@@ -0,0 +1,52 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var helpers_1 = require("@turf/helpers");
var Graph_1 = __importDefault(require("./lib/Graph"));
var EdgeRing_1 = __importDefault(require("./lib/EdgeRing"));
/**
* Polygonizes {@link LineString|(Multi)LineString(s)} into {@link Polygons}.
*
* Implementation of GEOSPolygonize function (`geos::operation::polygonize::Polygonizer`).
*
* Polygonizes a set of lines that represents edges in a planar graph. Edges must be correctly
* noded, i.e., they must only meet at their endpoints.
*
* The implementation correctly handles:
*
* - Dangles: edges which have one or both ends which are not incident on another edge endpoint.
* - Cut Edges (bridges): edges that are connected at both ends but which do not form part of a polygon.
*
* @name polygonize
* @param {FeatureCollection|Geometry|Feature<LineString|MultiLineString>} geoJson Lines in order to polygonize
* @returns {FeatureCollection<Polygon>} Polygons created
* @throws {Error} if geoJson is invalid.
*/
function polygonize(geoJson) {
var graph = Graph_1.default.fromGeoJson(geoJson);
// 1. Remove dangle node
graph.deleteDangles();
// 2. Remove cut-edges (bridge edges)
graph.deleteCutEdges();
// 3. Get all holes and shells
var holes = [], shells = [];
graph
.getEdgeRings()
.filter(function (edgeRing) { return edgeRing.isValid(); })
.forEach(function (edgeRing) {
if (edgeRing.isHole())
holes.push(edgeRing);
else
shells.push(edgeRing);
});
// 4. Assign Holes to Shells
holes.forEach(function (hole) {
if (EdgeRing_1.default.findEdgeRingContaining(hole, shells))
shells.push(hole);
});
// 5. EdgeRings to Polygons
return helpers_1.featureCollection(shells.map(function (shell) { return shell.toPolygon(); }));
}
exports.default = polygonize;

View File

@@ -0,0 +1,55 @@
import Node from "./Node";
import EdgeRing from "./EdgeRing";
/**
* This class is inspired by GEOS's geos::operation::polygonize::PolygonizeDirectedEdge
*/
export default class Edge {
label?: number;
symetric?: Edge;
from: Node;
to: Node;
next?: Edge;
ring?: EdgeRing;
/**
* Creates or get the symetric Edge.
*
* @returns {Edge} - Symetric Edge.
*/
getSymetric(): Edge;
/**
* @param {Node} from - start node of the Edge
* @param {Node} to - end node of the edge
*/
constructor(from: Node, to: Node);
/**
* Removes edge from from and to nodes.
*/
deleteEdge(): void;
/**
* Compares Edge equallity.
*
* An edge is equal to another, if the from and to nodes are the same.
*
* @param {Edge} edge - Another Edge
* @returns {boolean} - True if Edges are equal, False otherwise
*/
isEqual(edge: Edge): boolean;
toString(): string;
/**
* Returns a LineString representation of the Edge
*
* @returns {Feature<LineString>} - LineString representation of the Edge
*/
toLineString(): import("@turf/helpers").Feature<import("@turf/helpers").LineString, import("@turf/helpers").Properties>;
/**
* Comparator of two edges.
*
* Implementation of geos::planargraph::DirectedEdge::compareTo.
*
* @param {Edge} edge - Another edge to compare with this one
* @returns {number} -1 if this Edge has a greater angle with the positive x-axis than b,
* 0 if the Edges are colinear,
* 1 otherwise
*/
compareTo(edge: Edge): number;
}

View File

@@ -0,0 +1,79 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var helpers_1 = require("@turf/helpers");
var util_1 = require("./util");
/**
* This class is inspired by GEOS's geos::operation::polygonize::PolygonizeDirectedEdge
*/
var Edge = /** @class */ (function () {
/**
* @param {Node} from - start node of the Edge
* @param {Node} to - end node of the edge
*/
function Edge(from, to) {
this.from = from; //< start
this.to = to; //< End
this.next = undefined; //< The edge to be computed after
this.label = undefined; //< Used in order to detect Cut Edges (Bridges)
this.symetric = undefined; //< The symetric edge of this
this.ring = undefined; //< EdgeRing in which the Edge is
this.from.addOuterEdge(this);
this.to.addInnerEdge(this);
}
/**
* Creates or get the symetric Edge.
*
* @returns {Edge} - Symetric Edge.
*/
Edge.prototype.getSymetric = function () {
if (!this.symetric) {
this.symetric = new Edge(this.to, this.from);
this.symetric.symetric = this;
}
return this.symetric;
};
/**
* Removes edge from from and to nodes.
*/
Edge.prototype.deleteEdge = function () {
this.from.removeOuterEdge(this);
this.to.removeInnerEdge(this);
};
/**
* Compares Edge equallity.
*
* An edge is equal to another, if the from and to nodes are the same.
*
* @param {Edge} edge - Another Edge
* @returns {boolean} - True if Edges are equal, False otherwise
*/
Edge.prototype.isEqual = function (edge) {
return this.from.id === edge.from.id && this.to.id === edge.to.id;
};
Edge.prototype.toString = function () {
return "Edge { " + this.from.id + " -> " + this.to.id + " }";
};
/**
* Returns a LineString representation of the Edge
*
* @returns {Feature<LineString>} - LineString representation of the Edge
*/
Edge.prototype.toLineString = function () {
return helpers_1.lineString([this.from.coordinates, this.to.coordinates]);
};
/**
* Comparator of two edges.
*
* Implementation of geos::planargraph::DirectedEdge::compareTo.
*
* @param {Edge} edge - Another edge to compare with this one
* @returns {number} -1 if this Edge has a greater angle with the positive x-axis than b,
* 0 if the Edges are colinear,
* 1 otherwise
*/
Edge.prototype.compareTo = function (edge) {
return util_1.orientationIndex(edge.from.coordinates, edge.to.coordinates, this.to.coordinates);
};
return Edge;
}());
exports.default = Edge;

View File

@@ -0,0 +1,122 @@
import { Polygon, Feature, Point } from "@turf/helpers";
import Edge from "./Edge";
/**
* Ring of edges which form a polygon.
*
* The ring may be either an outer shell or a hole.
*
* This class is inspired in GEOS's geos::operation::polygonize::EdgeRing
*/
export default class EdgeRing {
private edges;
private polygon?;
private envelope?;
constructor();
/**
* Add an edge to the ring, inserting it in the last position.
*
* @memberof EdgeRing
* @param {Edge} edge - Edge to be inserted
*/
push(edge: Edge): void;
/**
* Get Edge.
*
* @memberof EdgeRing
* @param {number} i - Index
* @returns {Edge} - Edge in the i position
*/
get(i: number): Edge;
/**
* Getter of length property.
*
* @memberof EdgeRing
* @returns {number} - Length of the edge ring.
*/
get length(): number;
/**
* Similar to Array.prototype.forEach for the list of Edges in the EdgeRing.
*
* @memberof EdgeRing
* @param {Function} f - The same function to be passed to Array.prototype.forEach
*/
forEach(f: (edge: Edge, index: number, array: Edge[]) => void): void;
/**
* Similar to Array.prototype.map for the list of Edges in the EdgeRing.
*
* @memberof EdgeRing
* @param {Function} f - The same function to be passed to Array.prototype.map
* @returns {Array} - The mapped values in the function
*/
map<T>(f: (edge: Edge, index: number, array: Edge[]) => T): T[];
/**
* Similar to Array.prototype.some for the list of Edges in the EdgeRing.
*
* @memberof EdgeRing
* @param {Function} f - The same function to be passed to Array.prototype.some
* @returns {boolean} - True if an Edge check the condition
*/
some(f: (edge: Edge, index: number, array: Edge[]) => boolean): boolean;
/**
* Check if the ring is valid in geomtry terms.
*
* A ring must have either 0 or 4 or more points. The first and the last must be
* equal (in 2D)
* geos::geom::LinearRing::validateConstruction
*
* @memberof EdgeRing
* @returns {boolean} - Validity of the EdgeRing
*/
isValid(): boolean;
/**
* Tests whether this ring is a hole.
*
* A ring is a hole if it is oriented counter-clockwise.
* Similar implementation of geos::algorithm::CGAlgorithms::isCCW
*
* @memberof EdgeRing
* @returns {boolean} - true: if it is a hole
*/
isHole(): boolean;
/**
* Creates a MultiPoint representing the EdgeRing (discarts edges directions).
*
* @memberof EdgeRing
* @returns {Feature<MultiPoint>} - Multipoint representation of the EdgeRing
*/
toMultiPoint(): Feature<import("@turf/helpers").MultiPoint, import("@turf/helpers").Properties>;
/**
* Creates a Polygon representing the EdgeRing.
*
* @memberof EdgeRing
* @returns {Feature<Polygon>} - Polygon representation of the Edge Ring
*/
toPolygon(): Feature<Polygon, {
[name: string]: any;
}>;
/**
* Calculates the envelope of the EdgeRing.
*
* @memberof EdgeRing
* @returns {Feature<Polygon>} - envelope
*/
getEnvelope(): Feature<Polygon, {
[name: string]: any;
}>;
/**
* `geos::operation::polygonize::EdgeRing::findEdgeRingContaining`
*
* @param {EdgeRing} testEdgeRing - EdgeRing to look in the list
* @param {EdgeRing[]} shellList - List of EdgeRing in which to search
*
* @returns {EdgeRing} - EdgeRing which contains the testEdgeRing
*/
static findEdgeRingContaining(testEdgeRing: EdgeRing, shellList: EdgeRing[]): EdgeRing | undefined;
/**
* Checks if the point is inside the edgeRing
*
* @param {Feature<Point>} pt - Point to check if it is inside the edgeRing
* @returns {boolean} - True if it is inside, False otherwise
*/
inside(pt: Feature<Point>): boolean;
}

View File

@@ -0,0 +1,204 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var util_1 = require("./util");
var helpers_1 = require("@turf/helpers");
var envelope_1 = __importDefault(require("@turf/envelope"));
var boolean_point_in_polygon_1 = __importDefault(require("@turf/boolean-point-in-polygon"));
/**
* Ring of edges which form a polygon.
*
* The ring may be either an outer shell or a hole.
*
* This class is inspired in GEOS's geos::operation::polygonize::EdgeRing
*/
var EdgeRing = /** @class */ (function () {
function EdgeRing() {
this.edges = [];
this.polygon = undefined; //< Caches Polygon representation
this.envelope = undefined; //< Caches Envelope representation
}
/**
* Add an edge to the ring, inserting it in the last position.
*
* @memberof EdgeRing
* @param {Edge} edge - Edge to be inserted
*/
EdgeRing.prototype.push = function (edge) {
this.edges.push(edge);
this.polygon = this.envelope = undefined;
};
/**
* Get Edge.
*
* @memberof EdgeRing
* @param {number} i - Index
* @returns {Edge} - Edge in the i position
*/
EdgeRing.prototype.get = function (i) {
return this.edges[i];
};
Object.defineProperty(EdgeRing.prototype, "length", {
/**
* Getter of length property.
*
* @memberof EdgeRing
* @returns {number} - Length of the edge ring.
*/
get: function () {
return this.edges.length;
},
enumerable: true,
configurable: true
});
/**
* Similar to Array.prototype.forEach for the list of Edges in the EdgeRing.
*
* @memberof EdgeRing
* @param {Function} f - The same function to be passed to Array.prototype.forEach
*/
EdgeRing.prototype.forEach = function (f) {
this.edges.forEach(f);
};
/**
* Similar to Array.prototype.map for the list of Edges in the EdgeRing.
*
* @memberof EdgeRing
* @param {Function} f - The same function to be passed to Array.prototype.map
* @returns {Array} - The mapped values in the function
*/
EdgeRing.prototype.map = function (f) {
return this.edges.map(f);
};
/**
* Similar to Array.prototype.some for the list of Edges in the EdgeRing.
*
* @memberof EdgeRing
* @param {Function} f - The same function to be passed to Array.prototype.some
* @returns {boolean} - True if an Edge check the condition
*/
EdgeRing.prototype.some = function (f) {
return this.edges.some(f);
};
/**
* Check if the ring is valid in geomtry terms.
*
* A ring must have either 0 or 4 or more points. The first and the last must be
* equal (in 2D)
* geos::geom::LinearRing::validateConstruction
*
* @memberof EdgeRing
* @returns {boolean} - Validity of the EdgeRing
*/
EdgeRing.prototype.isValid = function () {
// TODO: stub
return true;
};
/**
* Tests whether this ring is a hole.
*
* A ring is a hole if it is oriented counter-clockwise.
* Similar implementation of geos::algorithm::CGAlgorithms::isCCW
*
* @memberof EdgeRing
* @returns {boolean} - true: if it is a hole
*/
EdgeRing.prototype.isHole = function () {
var _this = this;
// XXX: Assuming Ring is valid
// Find highest point
var hiIndex = this.edges.reduce(function (high, edge, i) {
if (edge.from.coordinates[1] > _this.edges[high].from.coordinates[1])
high = i;
return high;
}, 0), iPrev = (hiIndex === 0 ? this.length : hiIndex) - 1, iNext = (hiIndex + 1) % this.length, disc = util_1.orientationIndex(this.edges[iPrev].from.coordinates, this.edges[hiIndex].from.coordinates, this.edges[iNext].from.coordinates);
if (disc === 0)
return (this.edges[iPrev].from.coordinates[0] >
this.edges[iNext].from.coordinates[0]);
return disc > 0;
};
/**
* Creates a MultiPoint representing the EdgeRing (discarts edges directions).
*
* @memberof EdgeRing
* @returns {Feature<MultiPoint>} - Multipoint representation of the EdgeRing
*/
EdgeRing.prototype.toMultiPoint = function () {
return helpers_1.multiPoint(this.edges.map(function (edge) { return edge.from.coordinates; }));
};
/**
* Creates a Polygon representing the EdgeRing.
*
* @memberof EdgeRing
* @returns {Feature<Polygon>} - Polygon representation of the Edge Ring
*/
EdgeRing.prototype.toPolygon = function () {
if (this.polygon)
return this.polygon;
var coordinates = this.edges.map(function (edge) { return edge.from.coordinates; });
coordinates.push(this.edges[0].from.coordinates);
return (this.polygon = helpers_1.polygon([coordinates]));
};
/**
* Calculates the envelope of the EdgeRing.
*
* @memberof EdgeRing
* @returns {Feature<Polygon>} - envelope
*/
EdgeRing.prototype.getEnvelope = function () {
if (this.envelope)
return this.envelope;
return (this.envelope = envelope_1.default(this.toPolygon()));
};
/**
* `geos::operation::polygonize::EdgeRing::findEdgeRingContaining`
*
* @param {EdgeRing} testEdgeRing - EdgeRing to look in the list
* @param {EdgeRing[]} shellList - List of EdgeRing in which to search
*
* @returns {EdgeRing} - EdgeRing which contains the testEdgeRing
*/
EdgeRing.findEdgeRingContaining = function (testEdgeRing, shellList) {
var testEnvelope = testEdgeRing.getEnvelope();
var minEnvelope, minShell;
shellList.forEach(function (shell) {
var tryEnvelope = shell.getEnvelope();
if (minShell)
minEnvelope = minShell.getEnvelope();
// the hole envelope cannot equal the shell envelope
if (util_1.envelopeIsEqual(tryEnvelope, testEnvelope))
return;
if (util_1.envelopeContains(tryEnvelope, testEnvelope)) {
var testEdgeRingCoordinates = testEdgeRing.map(function (edge) { return edge.from.coordinates; });
var testPoint = void 0;
var _loop_1 = function (pt) {
if (!shell.some(function (edge) { return util_1.coordinatesEqual(pt, edge.from.coordinates); })) {
testPoint = pt;
}
};
for (var _i = 0, testEdgeRingCoordinates_1 = testEdgeRingCoordinates; _i < testEdgeRingCoordinates_1.length; _i++) {
var pt = testEdgeRingCoordinates_1[_i];
_loop_1(pt);
}
if (testPoint && shell.inside(helpers_1.point(testPoint))) {
if (!minShell || util_1.envelopeContains(minEnvelope, tryEnvelope))
minShell = shell;
}
}
});
return minShell;
};
/**
* Checks if the point is inside the edgeRing
*
* @param {Feature<Point>} pt - Point to check if it is inside the edgeRing
* @returns {boolean} - True if it is inside, False otherwise
*/
EdgeRing.prototype.inside = function (pt) {
return boolean_point_in_polygon_1.default(pt, this.toPolygon());
};
return EdgeRing;
}());
exports.default = EdgeRing;

View File

@@ -0,0 +1,124 @@
import Node from "./Node";
import Edge from "./Edge";
import EdgeRing from "./EdgeRing";
import { FeatureCollection, LineString, MultiLineString, Feature } from "@turf/helpers";
/**
* Represents a planar graph of edges and nodes that can be used to compute a polygonization.
*
* Although, this class is inspired by GEOS's `geos::operation::polygonize::PolygonizeGraph`,
* it isn't a rewrite. As regards algorithm, this class implements the same logic, but it
* isn't a javascript transcription of the C++ source.
*
* This graph is directed (both directions are created)
*/
export default class Graph {
private nodes;
private edges;
/**
* Creates a graph from a GeoJSON.
*
* @param {FeatureCollection<LineString>} geoJson - it must comply with the restrictions detailed in the index
* @returns {Graph} - The newly created graph
* @throws {Error} if geoJson is invalid.
*/
static fromGeoJson(geoJson: FeatureCollection<LineString | MultiLineString> | LineString | MultiLineString | Feature<LineString | MultiLineString>): Graph;
/**
* Creates or get a Node.
*
* @param {number[]} coordinates - Coordinates of the node
* @returns {Node} - The created or stored node
*/
getNode(coordinates: number[]): Node;
/**
* Adds an Edge and its symetricall.
*
* Edges are added symetrically, i.e.: we also add its symetric
*
* @param {Node} from - Node which starts the Edge
* @param {Node} to - Node which ends the Edge
*/
addEdge(from: Node, to: Node): void;
constructor();
/**
* Removes Dangle Nodes (nodes with grade 1).
*/
deleteDangles(): void;
/**
* Check if node is dangle, if so, remove it.
*
* It calls itself recursively, removing a dangling node might cause another dangling node
*
* @param {Node} node - Node to check if it's a dangle
*/
_removeIfDangle(node: Node): void;
/**
* Delete cut-edges (bridge edges).
*
* The graph will be traversed, all the edges will be labeled according the ring
* in which they are. (The label is a number incremented by 1). Edges with the same
* label are cut-edges.
*/
deleteCutEdges(): void;
/**
* Set the `next` property of each Edge.
*
* The graph will be transversed in a CW form, so, we set the next of the symetrical edge as the previous one.
* OuterEdges are sorted CCW.
*
* @param {Node} [node] - If no node is passed, the function calls itself for every node in the Graph
*/
_computeNextCWEdges(node?: Node): void;
/**
* Computes the next edge pointers going CCW around the given node, for the given edgering label.
*
* This algorithm has the effect of converting maximal edgerings into minimal edgerings
*
* XXX: method literally transcribed from `geos::operation::polygonize::PolygonizeGraph::computeNextCCWEdges`,
* could be written in a more javascript way.
*
* @param {Node} node - Node
* @param {number} label - Ring's label
*/
_computeNextCCWEdges(node: Node, label: number): void;
/**
* Finds rings and labels edges according to which rings are.
*
* The label is a number which is increased for each ring.
*
* @returns {Edge[]} edges that start rings
*/
_findLabeledEdgeRings(): Edge[];
/**
* Computes the EdgeRings formed by the edges in this graph.
*
* @returns {EdgeRing[]} - A list of all the EdgeRings in the graph.
*/
getEdgeRings(): EdgeRing[];
/**
* Find all nodes in a Maxima EdgeRing which are self-intersection nodes.
*
* @param {Node} startEdge - Start Edge of the Ring
* @returns {Node[]} - intersection nodes
*/
_findIntersectionNodes(startEdge: Edge): Node[];
/**
* Get the edge-ring which starts from the provided Edge.
*
* @param {Edge} startEdge - starting edge of the edge ring
* @returns {EdgeRing} - EdgeRing which start Edge is the provided one.
*/
_findEdgeRing(startEdge: Edge): EdgeRing;
/**
* Removes a node from the Graph.
*
* It also removes edges asociated to that node
* @param {Node} node - Node to be removed
*/
removeNode(node: Node): void;
/**
* Remove edge from the graph and deletes the edge.
*
* @param {Edge} edge - Edge to be removed
*/
removeEdge(edge: Edge): void;
}

View File

@@ -0,0 +1,307 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var Node_1 = __importDefault(require("./Node"));
var Edge_1 = __importDefault(require("./Edge"));
var EdgeRing_1 = __importDefault(require("./EdgeRing"));
var meta_1 = require("@turf/meta");
var invariant_1 = require("@turf/invariant");
/**
* Validates the geoJson.
*
* @param {GeoJSON} geoJson - input geoJson.
* @throws {Error} if geoJson is invalid.
*/
function validateGeoJson(geoJson) {
if (!geoJson)
throw new Error("No geojson passed");
if (geoJson.type !== "FeatureCollection" &&
geoJson.type !== "GeometryCollection" &&
geoJson.type !== "MultiLineString" &&
geoJson.type !== "LineString" &&
geoJson.type !== "Feature")
throw new Error("Invalid input type '" + geoJson.type + "'. Geojson must be FeatureCollection, GeometryCollection, LineString, MultiLineString or Feature");
}
/**
* Represents a planar graph of edges and nodes that can be used to compute a polygonization.
*
* Although, this class is inspired by GEOS's `geos::operation::polygonize::PolygonizeGraph`,
* it isn't a rewrite. As regards algorithm, this class implements the same logic, but it
* isn't a javascript transcription of the C++ source.
*
* This graph is directed (both directions are created)
*/
var Graph = /** @class */ (function () {
function Graph() {
this.edges = []; //< {Edge[]} dirEdges
// The key is the `id` of the Node (ie: coordinates.join(','))
this.nodes = {};
}
/**
* Creates a graph from a GeoJSON.
*
* @param {FeatureCollection<LineString>} geoJson - it must comply with the restrictions detailed in the index
* @returns {Graph} - The newly created graph
* @throws {Error} if geoJson is invalid.
*/
Graph.fromGeoJson = function (geoJson) {
validateGeoJson(geoJson);
var graph = new Graph();
meta_1.flattenEach(geoJson, function (feature) {
invariant_1.featureOf(feature, "LineString", "Graph::fromGeoJson");
// When a LineString if formed by many segments, split them
meta_1.coordReduce(feature, function (prev, cur) {
if (prev) {
var start = graph.getNode(prev), end = graph.getNode(cur);
graph.addEdge(start, end);
}
return cur;
});
});
return graph;
};
/**
* Creates or get a Node.
*
* @param {number[]} coordinates - Coordinates of the node
* @returns {Node} - The created or stored node
*/
Graph.prototype.getNode = function (coordinates) {
var id = Node_1.default.buildId(coordinates);
var node = this.nodes[id];
if (!node)
node = this.nodes[id] = new Node_1.default(coordinates);
return node;
};
/**
* Adds an Edge and its symetricall.
*
* Edges are added symetrically, i.e.: we also add its symetric
*
* @param {Node} from - Node which starts the Edge
* @param {Node} to - Node which ends the Edge
*/
Graph.prototype.addEdge = function (from, to) {
var edge = new Edge_1.default(from, to), symetricEdge = edge.getSymetric();
this.edges.push(edge);
this.edges.push(symetricEdge);
};
/**
* Removes Dangle Nodes (nodes with grade 1).
*/
Graph.prototype.deleteDangles = function () {
var _this = this;
Object.keys(this.nodes)
.map(function (id) { return _this.nodes[id]; })
.forEach(function (node) { return _this._removeIfDangle(node); });
};
/**
* Check if node is dangle, if so, remove it.
*
* It calls itself recursively, removing a dangling node might cause another dangling node
*
* @param {Node} node - Node to check if it's a dangle
*/
Graph.prototype._removeIfDangle = function (node) {
var _this = this;
// As edges are directed and symetrical, we count only innerEdges
if (node.innerEdges.length <= 1) {
var outerNodes = node.getOuterEdges().map(function (e) { return e.to; });
this.removeNode(node);
outerNodes.forEach(function (n) { return _this._removeIfDangle(n); });
}
};
/**
* Delete cut-edges (bridge edges).
*
* The graph will be traversed, all the edges will be labeled according the ring
* in which they are. (The label is a number incremented by 1). Edges with the same
* label are cut-edges.
*/
Graph.prototype.deleteCutEdges = function () {
var _this = this;
this._computeNextCWEdges();
this._findLabeledEdgeRings();
// Cut-edges (bridges) are edges where both edges have the same label
this.edges.forEach(function (edge) {
if (edge.label === edge.symetric.label) {
_this.removeEdge(edge.symetric);
_this.removeEdge(edge);
}
});
};
/**
* Set the `next` property of each Edge.
*
* The graph will be transversed in a CW form, so, we set the next of the symetrical edge as the previous one.
* OuterEdges are sorted CCW.
*
* @param {Node} [node] - If no node is passed, the function calls itself for every node in the Graph
*/
Graph.prototype._computeNextCWEdges = function (node) {
var _this = this;
if (typeof node === "undefined") {
Object.keys(this.nodes).forEach(function (id) {
return _this._computeNextCWEdges(_this.nodes[id]);
});
}
else {
node.getOuterEdges().forEach(function (edge, i) {
node.getOuterEdge((i === 0 ? node.getOuterEdges().length : i) - 1).symetric.next = edge;
});
}
};
/**
* Computes the next edge pointers going CCW around the given node, for the given edgering label.
*
* This algorithm has the effect of converting maximal edgerings into minimal edgerings
*
* XXX: method literally transcribed from `geos::operation::polygonize::PolygonizeGraph::computeNextCCWEdges`,
* could be written in a more javascript way.
*
* @param {Node} node - Node
* @param {number} label - Ring's label
*/
Graph.prototype._computeNextCCWEdges = function (node, label) {
var edges = node.getOuterEdges();
var firstOutDE, prevInDE;
for (var i = edges.length - 1; i >= 0; --i) {
var de = edges[i], sym = de.symetric, outDE = void 0, inDE = void 0;
if (de.label === label)
outDE = de;
if (sym.label === label)
inDE = sym;
if (!outDE || !inDE)
// This edge is not in edgering
continue;
if (inDE)
prevInDE = inDE;
if (outDE) {
if (prevInDE) {
prevInDE.next = outDE;
prevInDE = undefined;
}
if (!firstOutDE)
firstOutDE = outDE;
}
}
if (prevInDE)
prevInDE.next = firstOutDE;
};
/**
* Finds rings and labels edges according to which rings are.
*
* The label is a number which is increased for each ring.
*
* @returns {Edge[]} edges that start rings
*/
Graph.prototype._findLabeledEdgeRings = function () {
var edgeRingStarts = [];
var label = 0;
this.edges.forEach(function (edge) {
if (edge.label >= 0)
return;
edgeRingStarts.push(edge);
var e = edge;
do {
e.label = label;
e = e.next;
} while (!edge.isEqual(e));
label++;
});
return edgeRingStarts;
};
/**
* Computes the EdgeRings formed by the edges in this graph.
*
* @returns {EdgeRing[]} - A list of all the EdgeRings in the graph.
*/
Graph.prototype.getEdgeRings = function () {
var _this = this;
this._computeNextCWEdges();
// Clear labels
this.edges.forEach(function (edge) {
edge.label = undefined;
});
this._findLabeledEdgeRings().forEach(function (edge) {
// convertMaximalToMinimalEdgeRings
_this._findIntersectionNodes(edge).forEach(function (node) {
_this._computeNextCCWEdges(node, edge.label);
});
});
var edgeRingList = [];
// find all edgerings
this.edges.forEach(function (edge) {
if (edge.ring)
return;
edgeRingList.push(_this._findEdgeRing(edge));
});
return edgeRingList;
};
/**
* Find all nodes in a Maxima EdgeRing which are self-intersection nodes.
*
* @param {Node} startEdge - Start Edge of the Ring
* @returns {Node[]} - intersection nodes
*/
Graph.prototype._findIntersectionNodes = function (startEdge) {
var intersectionNodes = [];
var edge = startEdge;
var _loop_1 = function () {
// getDegree
var degree = 0;
edge.from.getOuterEdges().forEach(function (e) {
if (e.label === startEdge.label)
++degree;
});
if (degree > 1)
intersectionNodes.push(edge.from);
edge = edge.next;
};
do {
_loop_1();
} while (!startEdge.isEqual(edge));
return intersectionNodes;
};
/**
* Get the edge-ring which starts from the provided Edge.
*
* @param {Edge} startEdge - starting edge of the edge ring
* @returns {EdgeRing} - EdgeRing which start Edge is the provided one.
*/
Graph.prototype._findEdgeRing = function (startEdge) {
var edge = startEdge;
var edgeRing = new EdgeRing_1.default();
do {
edgeRing.push(edge);
edge.ring = edgeRing;
edge = edge.next;
} while (!startEdge.isEqual(edge));
return edgeRing;
};
/**
* Removes a node from the Graph.
*
* It also removes edges asociated to that node
* @param {Node} node - Node to be removed
*/
Graph.prototype.removeNode = function (node) {
var _this = this;
node.getOuterEdges().forEach(function (edge) { return _this.removeEdge(edge); });
node.innerEdges.forEach(function (edge) { return _this.removeEdge(edge); });
delete this.nodes[node.id];
};
/**
* Remove edge from the graph and deletes the edge.
*
* @param {Edge} edge - Edge to be removed
*/
Graph.prototype.removeEdge = function (edge) {
this.edges = this.edges.filter(function (e) { return !e.isEqual(edge); });
edge.deleteEdge();
};
return Graph;
}());
exports.default = Graph;

View File

@@ -0,0 +1,40 @@
import Edge from "./Edge";
/**
* Node
*/
export default class Node {
static buildId(coordinates: number[]): string;
id: string;
coordinates: number[];
innerEdges: Edge[];
private outerEdges;
private outerEdgesSorted;
constructor(coordinates: number[]);
removeInnerEdge(edge: Edge): void;
removeOuterEdge(edge: Edge): void;
/**
* Outer edges are stored CCW order.
*
* @memberof Node
* @param {Edge} edge - Edge to add as an outerEdge.
*/
addOuterEdge(edge: Edge): void;
/**
* Sorts outer edges in CCW way.
*
* @memberof Node
* @private
*/
sortOuterEdges(): void;
/**
* Retrieves outer edges.
*
* They are sorted if they aren't in the CCW order.
*
* @memberof Node
* @returns {Edge[]} - List of outer edges sorted in a CCW order.
*/
getOuterEdges(): Edge[];
getOuterEdge(i: number): Edge;
addInnerEdge(edge: Edge): void;
}

View File

@@ -0,0 +1,95 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var util_1 = require("./util");
/**
* Node
*/
var Node = /** @class */ (function () {
function Node(coordinates) {
this.id = Node.buildId(coordinates);
this.coordinates = coordinates; //< {Number[]}
this.innerEdges = []; //< {Edge[]}
// We wil store to (out) edges in an CCW order as geos::planargraph::DirectedEdgeStar does
this.outerEdges = []; //< {Edge[]}
this.outerEdgesSorted = false; //< {Boolean} flag that stores if the outer Edges had been sorted
}
Node.buildId = function (coordinates) {
return coordinates.join(",");
};
Node.prototype.removeInnerEdge = function (edge) {
this.innerEdges = this.innerEdges.filter(function (e) { return e.from.id !== edge.from.id; });
};
Node.prototype.removeOuterEdge = function (edge) {
this.outerEdges = this.outerEdges.filter(function (e) { return e.to.id !== edge.to.id; });
};
/**
* Outer edges are stored CCW order.
*
* @memberof Node
* @param {Edge} edge - Edge to add as an outerEdge.
*/
Node.prototype.addOuterEdge = function (edge) {
this.outerEdges.push(edge);
this.outerEdgesSorted = false;
};
/**
* Sorts outer edges in CCW way.
*
* @memberof Node
* @private
*/
Node.prototype.sortOuterEdges = function () {
var _this = this;
if (!this.outerEdgesSorted) {
//this.outerEdges.sort((a, b) => a.compareTo(b));
// Using this comparator in order to be deterministic
this.outerEdges.sort(function (a, b) {
var aNode = a.to, bNode = b.to;
if (aNode.coordinates[0] - _this.coordinates[0] >= 0 &&
bNode.coordinates[0] - _this.coordinates[0] < 0)
return 1;
if (aNode.coordinates[0] - _this.coordinates[0] < 0 &&
bNode.coordinates[0] - _this.coordinates[0] >= 0)
return -1;
if (aNode.coordinates[0] - _this.coordinates[0] === 0 &&
bNode.coordinates[0] - _this.coordinates[0] === 0) {
if (aNode.coordinates[1] - _this.coordinates[1] >= 0 ||
bNode.coordinates[1] - _this.coordinates[1] >= 0)
return aNode.coordinates[1] - bNode.coordinates[1];
return bNode.coordinates[1] - aNode.coordinates[1];
}
var det = util_1.orientationIndex(_this.coordinates, aNode.coordinates, bNode.coordinates);
if (det < 0)
return 1;
if (det > 0)
return -1;
var 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);
return d1 - d2;
});
this.outerEdgesSorted = true;
}
};
/**
* Retrieves outer edges.
*
* They are sorted if they aren't in the CCW order.
*
* @memberof Node
* @returns {Edge[]} - List of outer edges sorted in a CCW order.
*/
Node.prototype.getOuterEdges = function () {
this.sortOuterEdges();
return this.outerEdges;
};
Node.prototype.getOuterEdge = function (i) {
this.sortOuterEdges();
return this.outerEdges[i];
};
Node.prototype.addInnerEdge = function (edge) {
this.innerEdges.push(edge);
};
return Node;
}());
exports.default = Node;

View File

@@ -0,0 +1,46 @@
import { Feature, Polygon } from "@turf/helpers";
/**
* Returns the direction of the point q relative to the vector p1 -> p2.
*
* Implementation of geos::algorithm::CGAlgorithm::orientationIndex()
* (same as geos::algorithm::CGAlgorithm::computeOrientation())
*
* @param {number[]} p1 - the origin point of the vector
* @param {number[]} p2 - the final point of the vector
* @param {number[]} q - the point to compute the direction to
*
* @returns {number} - 1 if q is ccw (left) from p1->p2,
* -1 if q is cw (right) from p1->p2,
* 0 if q is colinear with p1->p2
*/
export declare function orientationIndex(p1: number[], p2: number[], q: number[]): number;
/**
* Checks if two envelopes are equal.
*
* The function assumes that the arguments are envelopes, i.e.: Rectangular polygon
*
* @param {Feature<Polygon>} env1 - Envelope
* @param {Feature<Polygon>} env2 - Envelope
* @returns {boolean} - True if the envelopes are equal
*/
export declare function envelopeIsEqual(env1: Feature<Polygon>, env2: Feature<Polygon>): boolean;
/**
* Check if a envelope is contained in other one.
*
* The function assumes that the arguments are envelopes, i.e.: Convex polygon
* XXX: Envelopes are rectangular, checking if a point is inside a rectangule is something easy,
* this could be further improved.
*
* @param {Feature<Polygon>} self - Envelope
* @param {Feature<Polygon>} env - Envelope
* @returns {boolean} - True if env is contained in self
*/
export declare function envelopeContains(self: Feature<Polygon>, env: Feature<Polygon>): boolean;
/**
* Checks if two coordinates are equal.
*
* @param {number[]} coord1 - First coordinate
* @param {number[]} coord2 - Second coordinate
* @returns {boolean} - True if coordinates are equal
*/
export declare function coordinatesEqual(coord1: number[], coord2: number[]): boolean;

View File

@@ -0,0 +1,75 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var boolean_point_in_polygon_1 = __importDefault(require("@turf/boolean-point-in-polygon"));
var helpers_1 = require("@turf/helpers");
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign#Polyfill
function mathSign(x) {
return ((x > 0) - (x < 0) || +x);
}
/**
* Returns the direction of the point q relative to the vector p1 -> p2.
*
* Implementation of geos::algorithm::CGAlgorithm::orientationIndex()
* (same as geos::algorithm::CGAlgorithm::computeOrientation())
*
* @param {number[]} p1 - the origin point of the vector
* @param {number[]} p2 - the final point of the vector
* @param {number[]} q - the point to compute the direction to
*
* @returns {number} - 1 if q is ccw (left) from p1->p2,
* -1 if q is cw (right) from p1->p2,
* 0 if q is colinear with p1->p2
*/
function orientationIndex(p1, p2, q) {
var dx1 = p2[0] - p1[0], dy1 = p2[1] - p1[1], dx2 = q[0] - p2[0], dy2 = q[1] - p2[1];
return mathSign(dx1 * dy2 - dx2 * dy1);
}
exports.orientationIndex = orientationIndex;
/**
* Checks if two envelopes are equal.
*
* The function assumes that the arguments are envelopes, i.e.: Rectangular polygon
*
* @param {Feature<Polygon>} env1 - Envelope
* @param {Feature<Polygon>} env2 - Envelope
* @returns {boolean} - True if the envelopes are equal
*/
function envelopeIsEqual(env1, env2) {
var envX1 = env1.geometry.coordinates[0].map(function (c) { return c[0]; }), envY1 = env1.geometry.coordinates[0].map(function (c) { return c[1]; }), envX2 = env2.geometry.coordinates[0].map(function (c) { return c[0]; }), envY2 = env2.geometry.coordinates[0].map(function (c) { return c[1]; });
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));
}
exports.envelopeIsEqual = envelopeIsEqual;
/**
* Check if a envelope is contained in other one.
*
* The function assumes that the arguments are envelopes, i.e.: Convex polygon
* XXX: Envelopes are rectangular, checking if a point is inside a rectangule is something easy,
* this could be further improved.
*
* @param {Feature<Polygon>} self - Envelope
* @param {Feature<Polygon>} env - Envelope
* @returns {boolean} - True if env is contained in self
*/
function envelopeContains(self, env) {
return env.geometry.coordinates[0].every(function (c) {
return boolean_point_in_polygon_1.default(helpers_1.point(c), self);
});
}
exports.envelopeContains = envelopeContains;
/**
* Checks if two coordinates are equal.
*
* @param {number[]} coord1 - First coordinate
* @param {number[]} coord2 - Second coordinate
* @returns {boolean} - True if coordinates are equal
*/
function coordinatesEqual(coord1, coord2) {
return coord1[0] === coord2[0] && coord1[1] === coord2[1];
}
exports.coordinatesEqual = coordinatesEqual;

71
frontend/node_modules/@turf/polygonize/package.json generated vendored Normal file
View File

@@ -0,0 +1,71 @@
{
"name": "@turf/polygonize",
"version": "6.5.0",
"description": "turf polygonize module",
"author": "Turf Authors",
"contributors": [
"Nicolas Cisco <@nickcis>",
"Denis Carriere <@DenisCarriere>"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/Turfjs/turf/issues"
},
"homepage": "https://github.com/Turfjs/turf",
"repository": {
"type": "git",
"url": "git://github.com/Turfjs/turf.git"
},
"funding": "https://opencollective.com/turf",
"publishConfig": {
"access": "public"
},
"keywords": [
"turf",
"geojson",
"gis",
"polygonize"
],
"main": "dist/js/index.js",
"module": "dist/es/index.js",
"exports": {
"./package.json": "./package.json",
".": {
"import": "./dist/es/index.js",
"require": "./dist/js/index.js"
}
},
"types": "dist/js/index.d.ts",
"sideEffects": false,
"files": [
"dist"
],
"scripts": {
"bench": "ts-node bench.js",
"build": "npm-run-all build:*",
"build:es": "tsc --outDir dist/es --module esnext --declaration false && echo '{\"type\":\"module\"}' > dist/es/package.json",
"build:js": "tsc",
"docs": "node ../../scripts/generate-readmes",
"test": "npm-run-all test:*",
"test:tape": "ts-node -r esm test.js",
"test:types": "tsc --esModuleInterop --noEmit types.ts"
},
"devDependencies": {
"benchmark": "*",
"load-json-file": "*",
"npm-run-all": "*",
"rollup": "*",
"tape": "*",
"ts-node": "*",
"typescript": "*",
"write-json-file": "*"
},
"dependencies": {
"@turf/boolean-point-in-polygon": "^6.5.0",
"@turf/envelope": "^6.5.0",
"@turf/helpers": "^6.5.0",
"@turf/invariant": "^6.5.0",
"@turf/meta": "^6.5.0"
},
"gitHead": "5375941072b90d489389db22b43bfe809d5e451e"
}