123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- "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<Point>} 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<Polygon>} 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;
- }
|