| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 | "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;
 |