"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); // http://en.wikipedia.org/wiki/Delaunay_triangulation // https://github.com/ironwallaby/delaunay var helpers_1 = require("@turf/helpers"); /** * Takes a set of {@link Point|points} and creates a * [Triangulated Irregular Network](http://en.wikipedia.org/wiki/Triangulated_irregular_network), * or a TIN for short, returned as a collection of Polygons. These are often used * for developing elevation contour maps or stepped heat visualizations. * * If an optional z-value property is provided then it is added as properties called `a`, `b`, * and `c` representing its value at each of the points that represent the corners of the * triangle. * * @name tin * @param {FeatureCollection} points input points * @param {String} [z] name of the property from which to pull z values * This is optional: if not given, then there will be no extra data added to the derived triangles. * @returns {FeatureCollection} TIN output * @example * // generate some random point data * var points = turf.randomPoint(30, {bbox: [50, 30, 70, 50]}); * * // add a random property to each point between 0 and 9 * for (var i = 0; i < points.features.length; i++) { * points.features[i].properties.z = ~~(Math.random() * 9); * } * var tin = turf.tin(points, 'z'); * * //addToMap * var addToMap = [tin, points] * for (var i = 0; i < tin.features.length; i++) { * var properties = tin.features[i].properties; * properties.fill = '#' + properties.a + properties.b + properties.c; * } */ function tin(points, z) { // break down points var isPointZ = false; return helpers_1.featureCollection(triangulate(points.features.map(function (p) { var point = { x: p.geometry.coordinates[0], y: p.geometry.coordinates[1], }; if (z) { point.z = p.properties[z]; } else if (p.geometry.coordinates.length === 3) { isPointZ = true; point.z = p.geometry.coordinates[2]; } return point; })).map(function (triangle) { var a = [triangle.a.x, triangle.a.y]; var b = [triangle.b.x, triangle.b.y]; var c = [triangle.c.x, triangle.c.y]; var properties = {}; // Add z coordinates to triangle points if user passed // them in that way otherwise add it as a property. if (isPointZ) { a.push(triangle.a.z); b.push(triangle.b.z); c.push(triangle.c.z); } else { properties = { a: triangle.a.z, b: triangle.b.z, c: triangle.c.z, }; } return helpers_1.polygon([[a, b, c, a]], properties); })); } exports.default = tin; var Triangle = /** @class */ (function () { function Triangle(a, b, c) { this.a = a; this.b = b; this.c = c; var A = b.x - a.x; var B = b.y - a.y; var C = c.x - a.x; var D = c.y - a.y; var E = A * (a.x + b.x) + B * (a.y + b.y); var F = C * (a.x + c.x) + D * (a.y + c.y); var G = 2 * (A * (c.y - b.y) - B * (c.x - b.x)); var dx; var dy; // If the points of the triangle are collinear, then just find the // extremes and use the midpoint as the center of the circumcircle. this.x = (D * E - B * F) / G; this.y = (A * F - C * E) / G; dx = this.x - a.x; dy = this.y - a.y; this.r = dx * dx + dy * dy; } return Triangle; }()); function byX(a, b) { return b.x - a.x; } function dedup(edges) { var j = edges.length; var a; var b; var i; var m; var n; outer: while (j) { b = edges[--j]; a = edges[--j]; i = j; while (i) { n = edges[--i]; m = edges[--i]; if ((a === m && b === n) || (a === n && b === m)) { edges.splice(j, 2); edges.splice(i, 2); j -= 2; continue outer; } } } } function triangulate(vertices) { // Bail if there aren't enough vertices to form any triangles. if (vertices.length < 3) { return []; } // Ensure the vertex array is in order of descending X coordinate // (which is needed to ensure a subquadratic runtime), and then find // the bounding box around the points. vertices.sort(byX); var i = vertices.length - 1; var xmin = vertices[i].x; var xmax = vertices[0].x; var ymin = vertices[i].y; var ymax = ymin; var epsilon = 1e-12; var a; var b; var c; var A; var B; var G; while (i--) { if (vertices[i].y < ymin) { ymin = vertices[i].y; } if (vertices[i].y > ymax) { ymax = vertices[i].y; } } // Find a supertriangle, which is a triangle that surrounds all the // vertices. This is used like something of a sentinel value to remove // cases in the main algorithm, and is removed before we return any // results. // Once found, put it in the "open" list. (The "open" list is for // triangles who may still need to be considered; the "closed" list is // for triangles which do not.) var dx = xmax - xmin; var dy = ymax - ymin; var dmax = dx > dy ? dx : dy; var xmid = (xmax + xmin) * 0.5; var ymid = (ymax + ymin) * 0.5; var open = [ new Triangle({ __sentinel: true, x: xmid - 20 * dmax, y: ymid - dmax, }, { __sentinel: true, x: xmid, y: ymid + 20 * dmax, }, { __sentinel: true, x: xmid + 20 * dmax, y: ymid - dmax, }), ]; var closed = []; var edges = []; var j; // Incrementally add each vertex to the mesh. i = vertices.length; while (i--) { // For each open triangle, check to see if the current point is // inside it's circumcircle. If it is, remove the triangle and add // it's edges to an edge list. edges.length = 0; j = open.length; while (j--) { // If this point is to the right of this triangle's circumcircle, // then this triangle should never get checked again. Remove it // from the open list, add it to the closed list, and skip. dx = vertices[i].x - open[j].x; if (dx > 0 && dx * dx > open[j].r) { closed.push(open[j]); open.splice(j, 1); continue; } // If not, skip this triangle. dy = vertices[i].y - open[j].y; if (dx * dx + dy * dy > open[j].r) { continue; } // Remove the triangle and add it's edges to the edge list. edges.push(open[j].a, open[j].b, open[j].b, open[j].c, open[j].c, open[j].a); open.splice(j, 1); } // Remove any doubled edges. dedup(edges); // Add a new triangle for each edge. j = edges.length; while (j) { b = edges[--j]; a = edges[--j]; c = vertices[i]; // Avoid adding colinear triangles (which have error-prone // circumcircles) A = b.x - a.x; B = b.y - a.y; G = 2 * (A * (c.y - b.y) - B * (c.x - b.x)); if (Math.abs(G) > epsilon) { open.push(new Triangle(a, b, c)); } } } // Copy any remaining open triangles to the closed list, and then // remove any triangles that share a vertex with the supertriangle. Array.prototype.push.apply(closed, open); i = closed.length; while (i--) { if (closed[i].a.__sentinel || closed[i].b.__sentinel || closed[i].c.__sentinel) { closed.splice(i, 1); } } return closed; }