diff --git a/packages/turf-tesselate/README.md b/packages/turf-tesselate/README.md index dd2b056c3e..c3fd14d0e7 100644 --- a/packages/turf-tesselate/README.md +++ b/packages/turf-tesselate/README.md @@ -4,36 +4,34 @@ ## tesselate -Tesselates a [Feature\][1] into a [FeatureCollection\][2] of triangles -using [earcut][3]. +Tesselates a polygon or multipolygon into a collection of triangle polygons +using [earcut][1]. ### Parameters -* `poly` **[Feature][4]<[Polygon][5]>** the polygon to tesselate +* `poly` **[Feature][2]<([Polygon][3] | [MultiPolygon][4])>** the polygon to tesselate ### Examples ```javascript -var poly = turf.polygon([[[11, 0], [22, 4], [31, 0], [31, 11], [21, 15], [11, 11], [11, 0]]]); -var triangles = turf.tesselate(poly); +const poly = turf.polygon([[[11, 0], [22, 4], [31, 0], [31, 11], [21, 15], [11, 11], [11, 0]]]); +const triangles = turf.tesselate(poly); //addToMap -var addToMap = [poly, triangles] +const addToMap = [poly, triangles] ``` -Returns **[FeatureCollection][6]<[Polygon][5]>** a geometrycollection feature +Returns **[FeatureCollection][5]<[Polygon][3]>** collection of polygon tesselations -[1]: Feature +[1]: https://github.com/mapbox/earcut -[2]: FeatureCollection +[2]: https://tools.ietf.org/html/rfc7946#section-3.2 -[3]: https://github.com/mapbox/earcut +[3]: https://tools.ietf.org/html/rfc7946#section-3.1.6 -[4]: https://tools.ietf.org/html/rfc7946#section-3.2 +[4]: https://tools.ietf.org/html/rfc7946#section-3.1.7 -[5]: https://tools.ietf.org/html/rfc7946#section-3.1.6 - -[6]: https://tools.ietf.org/html/rfc7946#section-3.3 +[5]: https://tools.ietf.org/html/rfc7946#section-3.3 diff --git a/packages/turf-tesselate/bench.ts b/packages/turf-tesselate/bench.ts index 41731d8a65..a238f5de66 100644 --- a/packages/turf-tesselate/bench.ts +++ b/packages/turf-tesselate/bench.ts @@ -1,4 +1,4 @@ -import Benchmark from "benchmark"; +import Benchmark, { Event } from "benchmark"; import { polygon } from "@turf/helpers"; import { tesselate } from "./index.js"; @@ -20,5 +20,5 @@ var poly = polygon([ */ new Benchmark.Suite("turf-tesselate") .add("polygon", () => tesselate(poly)) - .on("cycle", (e) => console.log(String(e.target))) + .on("cycle", (e: Event) => console.log(String(e.target))) .run(); diff --git a/packages/turf-tesselate/earcut.d.ts b/packages/turf-tesselate/earcut.d.ts new file mode 100644 index 0000000000..fab99ed18e --- /dev/null +++ b/packages/turf-tesselate/earcut.d.ts @@ -0,0 +1,9 @@ +declare module "earcut" { + declare function earcut( + vertices: number[], + holes: number[], + dimensions: number + ); + + export default earcut; +} diff --git a/packages/turf-tesselate/index.d.ts b/packages/turf-tesselate/index.d.ts deleted file mode 100644 index 67544306e8..0000000000 --- a/packages/turf-tesselate/index.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Feature, FeatureCollection, Polygon } from "geojson"; - -/** - * http://turfjs.org/docs/#tesselate - */ -declare function tesselate( - polygon: Feature -): FeatureCollection; - -export { tesselate }; -export default tesselate; diff --git a/packages/turf-tesselate/index.js b/packages/turf-tesselate/index.js deleted file mode 100644 index 0aef2a7acf..0000000000 --- a/packages/turf-tesselate/index.js +++ /dev/null @@ -1,80 +0,0 @@ -import earcut from "earcut"; -import { polygon } from "@turf/helpers"; - -/** - * Tesselates a {@link Feature} into a {@link FeatureCollection} of triangles - * using [earcut](https://github.com/mapbox/earcut). - * - * @name tesselate - * @param {Feature} poly the polygon to tesselate - * @returns {FeatureCollection} a geometrycollection feature - * @example - * var poly = turf.polygon([[[11, 0], [22, 4], [31, 0], [31, 11], [21, 15], [11, 11], [11, 0]]]); - * var triangles = turf.tesselate(poly); - * - * //addToMap - * var addToMap = [poly, triangles] - */ -function tesselate(poly) { - if ( - !poly.geometry || - (poly.geometry.type !== "Polygon" && poly.geometry.type !== "MultiPolygon") - ) { - throw new Error("input must be a Polygon or MultiPolygon"); - } - - var fc = { type: "FeatureCollection", features: [] }; - - if (poly.geometry.type === "Polygon") { - fc.features = processPolygon(poly.geometry.coordinates); - } else { - poly.geometry.coordinates.forEach(function (coordinates) { - fc.features = fc.features.concat(processPolygon(coordinates)); - }); - } - - return fc; -} - -function processPolygon(coordinates) { - var data = flattenCoords(coordinates); - var dim = 2; - var result = earcut(data.vertices, data.holes, dim); - - var features = []; - var vertices = []; - - result.forEach(function (vert, i) { - var index = result[i]; - vertices.push([data.vertices[index * dim], data.vertices[index * dim + 1]]); - }); - - for (var i = 0; i < vertices.length; i += 3) { - var coords = vertices.slice(i, i + 3); - coords.push(vertices[i]); - features.push(polygon([coords])); - } - - return features; -} - -function flattenCoords(data) { - var dim = data[0][0].length, - result = { vertices: [], holes: [], dimensions: dim }, - holeIndex = 0; - - for (var i = 0; i < data.length; i++) { - for (var j = 0; j < data[i].length; j++) { - for (var d = 0; d < dim; d++) result.vertices.push(data[i][j][d]); - } - if (i > 0) { - holeIndex += data[i - 1].length; - result.holes.push(holeIndex); - } - } - - return result; -} - -export { tesselate }; -export default tesselate; diff --git a/packages/turf-tesselate/index.ts b/packages/turf-tesselate/index.ts new file mode 100644 index 0000000000..54ed91eaf9 --- /dev/null +++ b/packages/turf-tesselate/index.ts @@ -0,0 +1,96 @@ +import { + Feature, + FeatureCollection, + MultiPolygon, + Polygon, + Position, +} from "geojson"; +import earcut from "earcut"; +import { polygon } from "@turf/helpers"; + +/** + * Tesselates a polygon or multipolygon into a collection of triangle polygons + * using [earcut](https://github.com/mapbox/earcut). + * + * @name tesselate + * @param {Feature} poly the polygon to tesselate + * @returns {FeatureCollection} collection of polygon tesselations + * @example + * const poly = turf.polygon([[[11, 0], [22, 4], [31, 0], [31, 11], [21, 15], [11, 11], [11, 0]]]); + * const triangles = turf.tesselate(poly); + * + * //addToMap + * const addToMap = [poly, triangles] + */ +function tesselate( + poly: Feature +): FeatureCollection { + if ( + !poly.geometry || + (poly.geometry.type !== "Polygon" && poly.geometry.type !== "MultiPolygon") + ) { + throw new Error("input must be a Polygon or MultiPolygon"); + } + + const fc: FeatureCollection = { + type: "FeatureCollection", + features: [], + }; + + if (poly.geometry.type === "Polygon") { + fc.features = processPolygon(poly.geometry.coordinates); + } else { + poly.geometry.coordinates.forEach(function (coordinates) { + fc.features = fc.features.concat(processPolygon(coordinates)); + }); + } + + return fc; +} + +function processPolygon(coordinates: Position[][]) { + const data = flattenCoords(coordinates); + const dim = 2; + const result = earcut(data.vertices, data.holes, dim); + + const features: Feature[] = []; + const vertices: Position[] = []; + + result.forEach(function (vert: any, i: number) { + const index = result[i]; + vertices.push([data.vertices[index * dim], data.vertices[index * dim + 1]]); + }); + + for (var i = 0; i < vertices.length; i += 3) { + const coords = vertices.slice(i, i + 3); + coords.push(vertices[i]); + features.push(polygon([coords])); + } + + return features; +} + +function flattenCoords(data: Position[][]) { + const dim: number = data[0][0].length, + result: { vertices: number[]; holes: number[]; dimensions: number } = { + vertices: [], + holes: [], + dimensions: dim, + }; + let holeIndex = 0; + + for (let i = 0; i < data.length; i++) { + for (let j = 0; j < data[i].length; j++) { + for (let d = 0; d < dim; d++) result.vertices.push(data[i][j][d]); + } + if (i > 0) { + holeIndex += data[i - 1].length; + result.holes.push(holeIndex); + } + } + + return result; +} + +export { tesselate }; +export default tesselate; diff --git a/packages/turf-tesselate/package.json b/packages/turf-tesselate/package.json index 9941a8c5ad..0c3b76f03f 100644 --- a/packages/turf-tesselate/package.json +++ b/packages/turf-tesselate/package.json @@ -60,14 +60,18 @@ }, "devDependencies": { "@types/benchmark": "^2.1.5", + "@types/tape": "^4.2.32", "benchmark": "^2.1.4", "npm-run-all": "^4.1.5", "tape": "^5.7.2", "tsup": "^8.0.1", - "tsx": "^4.6.2" + "tsx": "^4.6.2", + "typescript": "^5.2.2" }, "dependencies": { "@turf/helpers": "workspace:^", - "earcut": "^2.2.4" + "@types/geojson": "7946.0.8", + "earcut": "^2.2.4", + "tslib": "^2.6.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2e12d33568..09a7502ec6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5689,13 +5689,22 @@ importers: '@turf/helpers': specifier: workspace:^ version: link:../turf-helpers + '@types/geojson': + specifier: 7946.0.8 + version: 7946.0.8 earcut: specifier: ^2.2.4 version: 2.2.4 + tslib: + specifier: ^2.6.2 + version: 2.6.2 devDependencies: '@types/benchmark': specifier: ^2.1.5 version: 2.1.5 + '@types/tape': + specifier: ^4.2.32 + version: 4.13.4 benchmark: specifier: ^2.1.4 version: 2.1.4 @@ -5711,6 +5720,9 @@ importers: tsx: specifier: ^4.6.2 version: 4.6.2 + typescript: + specifier: ^5.2.2 + version: 5.3.3 packages/turf-tin: dependencies: