From 3249a8b24344d9fa016f19212dfdf3f43eeb9a7e Mon Sep 17 00:00:00 2001 From: mfedderly Date: Wed, 23 Jun 2021 10:20:19 -0400 Subject: [PATCH] Enable typescript strict mode (#2126) * bezier-spline * clusters-dbscan and clusters-kmeans, but they both need new DefinitelyTyped updates * kind of speculative fixes for polygonize * Enforce strict mode * turf-clusters-kmeans by way of skmeans types * types Co-authored-by: Matt Fedderly --- packages/turf-bezier-spline/index.ts | 2 +- packages/turf-boolean-equal/package.json | 1 + packages/turf-boolean-overlap/index.ts | 2 +- packages/turf-boolean-overlap/package.json | 1 + packages/turf-clusters-dbscan/index.ts | 6 +-- packages/turf-clusters-dbscan/package.json | 1 + packages/turf-clusters-kmeans/index.ts | 6 +-- packages/turf-clusters-kmeans/package.json | 1 + packages/turf-polygonize/index.ts | 4 +- packages/turf-polygonize/lib/Edge.ts | 14 +++--- packages/turf-polygonize/lib/EdgeRing.ts | 58 ++++++++++++++-------- packages/turf-polygonize/lib/Graph.ts | 25 +++++----- packages/turf-polygonize/lib/Node.ts | 10 ++-- packages/turf-polygonize/lib/util.ts | 39 +++++++-------- tsconfig.shared.json | 2 +- yarn.lock | 17 +++++++ 16 files changed, 112 insertions(+), 77 deletions(-) diff --git a/packages/turf-bezier-spline/index.ts b/packages/turf-bezier-spline/index.ts index c818d9bd53..45b8c78b42 100644 --- a/packages/turf-bezier-spline/index.ts +++ b/packages/turf-bezier-spline/index.ts @@ -44,7 +44,7 @@ function bezier

( const resolution = options.resolution || 10000; const sharpness = options.sharpness || 0.85; - const coords = []; + const coords: [number, number][] = []; const points = getGeom(line).coordinates.map((pt) => { return { x: pt[0], y: pt[1] }; }); diff --git a/packages/turf-boolean-equal/package.json b/packages/turf-boolean-equal/package.json index 74be8413d5..98da3e95c4 100644 --- a/packages/turf-boolean-equal/package.json +++ b/packages/turf-boolean-equal/package.json @@ -52,6 +52,7 @@ "test:tape": "ts-node -r esm test.js" }, "devDependencies": { + "@types/geojson-equality": "^0.2.0", "@types/tape": "*", "benchmark": "*", "boolean-shapely": "*", diff --git a/packages/turf-boolean-overlap/index.ts b/packages/turf-boolean-overlap/index.ts index 3a7adb8ee5..648727bf27 100644 --- a/packages/turf-boolean-overlap/index.ts +++ b/packages/turf-boolean-overlap/index.ts @@ -48,7 +48,7 @@ export default function booleanOverlap( // features must be not equal const equality = new GeojsonEquality({ precision: 6 }); - if (equality.compare(feature1, feature2)) return false; + if (equality.compare(feature1 as any, feature2 as any)) return false; let overlap = 0; diff --git a/packages/turf-boolean-overlap/package.json b/packages/turf-boolean-overlap/package.json index 87511d0410..ee53876ad6 100755 --- a/packages/turf-boolean-overlap/package.json +++ b/packages/turf-boolean-overlap/package.json @@ -51,6 +51,7 @@ "test:tape": "ts-node -r esm test.js" }, "devDependencies": { + "@types/geojson-equality": "^0.2.0", "@types/tape": "*", "benchmark": "*", "boolean-shapely": "*", diff --git a/packages/turf-clusters-dbscan/index.ts b/packages/turf-clusters-dbscan/index.ts index c0b0371eac..8a83078aaa 100644 --- a/packages/turf-clusters-dbscan/index.ts +++ b/packages/turf-clusters-dbscan/index.ts @@ -11,10 +11,10 @@ import { import clustering from "density-clustering"; export type Dbscan = "core" | "edge" | "noise"; -export interface DbscanProps extends Properties { +export type DbscanProps = Properties & { dbscan?: Dbscan; cluster?: number; -} +}; /** * Takes a set of {@link Point|points} and partition them into clusters according to {@link DBSCAN's|https://en.wikipedia.org/wiki/DBSCAN} data clustering algorithm. @@ -91,7 +91,7 @@ function clustersDbscan( else noisePoint.properties.dbscan = "noise"; }); - return points; + return points as FeatureCollection; } export default clustersDbscan; diff --git a/packages/turf-clusters-dbscan/package.json b/packages/turf-clusters-dbscan/package.json index aaa634f63f..75b1822bec 100644 --- a/packages/turf-clusters-dbscan/package.json +++ b/packages/turf-clusters-dbscan/package.json @@ -56,6 +56,7 @@ "devDependencies": { "@turf/centroid": "^6.4.0", "@turf/clusters": "^6.4.0", + "@types/density-clustering": "^1.3.0", "@types/tape": "*", "benchmark": "*", "chromatism": "*", diff --git a/packages/turf-clusters-kmeans/index.ts b/packages/turf-clusters-kmeans/index.ts index 0384e7abe4..753be25888 100644 --- a/packages/turf-clusters-kmeans/index.ts +++ b/packages/turf-clusters-kmeans/index.ts @@ -55,11 +55,7 @@ function clustersKmeans( var initialCentroids = data.slice(0, options.numberOfClusters); // create skmeans clusters - var skmeansResult = skmeans( - data, - options.numberOfClusters, - initialCentroids as any // typings are slightly wrong here - ); + var skmeansResult = skmeans(data, options.numberOfClusters, initialCentroids); // store centroids {clusterId: [number, number]} var centroids: Record = {}; diff --git a/packages/turf-clusters-kmeans/package.json b/packages/turf-clusters-kmeans/package.json index 38b9b4242f..f70f731154 100644 --- a/packages/turf-clusters-kmeans/package.json +++ b/packages/turf-clusters-kmeans/package.json @@ -56,6 +56,7 @@ "@turf/centroid": "^6.4.0", "@turf/clusters": "^6.4.0", "@turf/random": "^6.4.0", + "@types/skmeans": "^0.11.2", "@types/tape": "*", "benchmark": "*", "chromatism": "*", diff --git a/packages/turf-polygonize/index.ts b/packages/turf-polygonize/index.ts index 8cf380bee9..ac95af7ddc 100644 --- a/packages/turf-polygonize/index.ts +++ b/packages/turf-polygonize/index.ts @@ -39,8 +39,8 @@ export default function polygonize( graph.deleteCutEdges(); // 3. Get all holes and shells - const holes = [], - shells = []; + const holes: EdgeRing[] = [], + shells: EdgeRing[] = []; graph .getEdgeRings() diff --git a/packages/turf-polygonize/lib/Edge.ts b/packages/turf-polygonize/lib/Edge.ts index 8541e14251..989d86df07 100644 --- a/packages/turf-polygonize/lib/Edge.ts +++ b/packages/turf-polygonize/lib/Edge.ts @@ -7,12 +7,12 @@ import EdgeRing from "./EdgeRing"; * This class is inspired by GEOS's geos::operation::polygonize::PolygonizeDirectedEdge */ export default class Edge { - public label: number; - public symetric: Edge; + public label?: number; + public symetric?: Edge; public from: Node; public to: Node; - public next: Edge; - public ring: EdgeRing; + public next?: Edge; + public ring?: EdgeRing; /** * Creates or get the symetric Edge. @@ -32,7 +32,7 @@ export default class Edge { * @param {Node} from - start node of the Edge * @param {Node} to - end node of the edge */ - constructor(from, to) { + constructor(from: Node, to: Node) { this.from = from; //< start this.to = to; //< End @@ -61,7 +61,7 @@ export default class Edge { * @param {Edge} edge - Another Edge * @returns {boolean} - True if Edges are equal, False otherwise */ - isEqual(edge) { + isEqual(edge: Edge) { return this.from.id === edge.from.id && this.to.id === edge.to.id; } @@ -88,7 +88,7 @@ export default class Edge { * 0 if the Edges are colinear, * 1 otherwise */ - compareTo(edge) { + compareTo(edge: Edge) { return orientationIndex( edge.from.coordinates, edge.to.coordinates, diff --git a/packages/turf-polygonize/lib/EdgeRing.ts b/packages/turf-polygonize/lib/EdgeRing.ts index eea5425385..1007552dda 100644 --- a/packages/turf-polygonize/lib/EdgeRing.ts +++ b/packages/turf-polygonize/lib/EdgeRing.ts @@ -4,7 +4,15 @@ import { envelopeContains, coordinatesEqual, } from "./util"; -import { multiPoint, polygon, point, Polygon, Feature } from "@turf/helpers"; +import { + multiPoint, + polygon, + point, + Polygon, + Feature, + Point, + Position, +} from "@turf/helpers"; import envelope from "@turf/envelope"; import booleanPointInPolygon from "@turf/boolean-point-in-polygon"; import Edge from "./Edge"; @@ -18,13 +26,13 @@ import Edge from "./Edge"; */ export default class EdgeRing { private edges: Edge[]; - private polygon: Feature< + private polygon?: Feature< Polygon, { [name: string]: any; } >; - private envelope: Feature< + private envelope?: Feature< Polygon, { [name: string]: any; @@ -43,9 +51,7 @@ export default class EdgeRing { * @memberof EdgeRing * @param {Edge} edge - Edge to be inserted */ - push(edge) { - // Emulate Array getter ([]) behaviour - this[this.edges.length] = edge; + push(edge: Edge) { this.edges.push(edge); this.polygon = this.envelope = undefined; } @@ -57,7 +63,7 @@ export default class EdgeRing { * @param {number} i - Index * @returns {Edge} - Edge in the i position */ - get(i) { + get(i: number) { return this.edges[i]; } @@ -77,7 +83,7 @@ export default class EdgeRing { * @memberof EdgeRing * @param {Function} f - The same function to be passed to Array.prototype.forEach */ - forEach(f) { + forEach(f: (edge: Edge, index: number, array: Edge[]) => void) { this.edges.forEach(f); } @@ -88,7 +94,7 @@ export default class EdgeRing { * @param {Function} f - The same function to be passed to Array.prototype.map * @returns {Array} - The mapped values in the function */ - map(f) { + map(f: (edge: Edge, index: number, array: Edge[]) => T): T[] { return this.edges.map(f); } @@ -99,7 +105,7 @@ export default class 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) { + some(f: (edge: Edge, index: number, array: Edge[]) => boolean) { return this.edges.some(f); } @@ -182,7 +188,10 @@ export default class EdgeRing { */ getEnvelope() { if (this.envelope) return this.envelope; - return (this.envelope = envelope(this.toPolygon())); + return (this.envelope = envelope(this.toPolygon()) as Feature< + Polygon, + { [name: string]: any } + >); } /** @@ -193,10 +202,13 @@ export default class EdgeRing { * * @returns {EdgeRing} - EdgeRing which contains the testEdgeRing */ - static findEdgeRingContaining(testEdgeRing, shellList) { + static findEdgeRingContaining( + testEdgeRing: EdgeRing, + shellList: EdgeRing[] + ): EdgeRing | undefined { const testEnvelope = testEdgeRing.getEnvelope(); - let minEnvelope, minShell; + let minEnvelope: Feature, minShell: EdgeRing | undefined; shellList.forEach((shell) => { const tryEnvelope = shell.getEnvelope(); @@ -206,12 +218,18 @@ export default class EdgeRing { if (envelopeIsEqual(tryEnvelope, testEnvelope)) return; if (envelopeContains(tryEnvelope, testEnvelope)) { - const testPoint = testEdgeRing - .map((edge) => edge.from.coordinates) - .find( - (pt) => - !shell.some((edge) => coordinatesEqual(pt, edge.from.coordinates)) - ); + const testEdgeRingCoordinates = testEdgeRing.map( + (edge) => edge.from.coordinates + ); + + let testPoint: Position | undefined; + for (const pt of testEdgeRingCoordinates) { + if ( + !shell.some((edge) => coordinatesEqual(pt, edge.from.coordinates)) + ) { + testPoint = pt; + } + } if (testPoint && shell.inside(point(testPoint))) { if (!minShell || envelopeContains(minEnvelope, tryEnvelope)) @@ -229,7 +247,7 @@ export default class EdgeRing { * @param {Feature} pt - Point to check if it is inside the edgeRing * @returns {boolean} - True if it is inside, False otherwise */ - inside(pt) { + inside(pt: Feature) { return booleanPointInPolygon(pt, this.toPolygon()); } } diff --git a/packages/turf-polygonize/lib/Graph.ts b/packages/turf-polygonize/lib/Graph.ts index ca0e524fe8..6f2e6e1b09 100644 --- a/packages/turf-polygonize/lib/Graph.ts +++ b/packages/turf-polygonize/lib/Graph.ts @@ -8,6 +8,7 @@ import { LineString, MultiLineString, Feature, + GeoJSONObject, } from "@turf/helpers"; /** @@ -16,7 +17,7 @@ import { * @param {GeoJSON} geoJson - input geoJson. * @throws {Error} if geoJson is invalid. */ -function validateGeoJson(geoJson) { +function validateGeoJson(geoJson: GeoJSONObject) { if (!geoJson) throw new Error("No geojson passed"); if ( @@ -153,8 +154,8 @@ export default class Graph { // Cut-edges (bridges) are edges where both edges have the same label this.edges.forEach((edge) => { - if (edge.label === edge.symetric.label) { - this.removeEdge(edge.symetric); + if (edge.label === edge.symetric!.label) { + this.removeEdge(edge.symetric!); this.removeEdge(edge); } }); @@ -177,7 +178,7 @@ export default class Graph { node.getOuterEdges().forEach((edge, i) => { node.getOuterEdge( (i === 0 ? node.getOuterEdges().length : i) - 1 - ).symetric.next = edge; + ).symetric!.next = edge; }); } } @@ -205,7 +206,7 @@ export default class Graph { if (de.label === label) outDE = de; - if (sym.label === label) inDE = sym; + if (sym!.label === label) inDE = sym; if (!outDE || !inDE) // This edge is not in edgering @@ -234,17 +235,17 @@ export default class Graph { * @returns {Edge[]} edges that start rings */ _findLabeledEdgeRings() { - const edgeRingStarts = []; + const edgeRingStarts: Edge[] = []; let label = 0; this.edges.forEach((edge) => { - if (edge.label >= 0) return; + if (edge.label! >= 0) return; edgeRingStarts.push(edge); let e = edge; do { e.label = label; - e = e.next; + e = e.next!; } while (!edge.isEqual(e)); label++; @@ -269,11 +270,11 @@ export default class Graph { this._findLabeledEdgeRings().forEach((edge) => { // convertMaximalToMinimalEdgeRings this._findIntersectionNodes(edge).forEach((node) => { - this._computeNextCCWEdges(node, edge.label); + this._computeNextCCWEdges(node, edge.label!); }); }); - const edgeRingList = []; + const edgeRingList: EdgeRing[] = []; // find all edgerings this.edges.forEach((edge) => { @@ -302,7 +303,7 @@ export default class Graph { if (degree > 1) intersectionNodes.push(edge.from); - edge = edge.next; + edge = edge.next!; } while (!startEdge.isEqual(edge)); return intersectionNodes; @@ -321,7 +322,7 @@ export default class Graph { do { edgeRing.push(edge); edge.ring = edgeRing; - edge = edge.next; + edge = edge.next!; } while (!startEdge.isEqual(edge)); return edgeRing; diff --git a/packages/turf-polygonize/lib/Node.ts b/packages/turf-polygonize/lib/Node.ts index 96f3a04c21..d377e4858a 100644 --- a/packages/turf-polygonize/lib/Node.ts +++ b/packages/turf-polygonize/lib/Node.ts @@ -25,11 +25,11 @@ export default class Node { this.outerEdgesSorted = false; //< {Boolean} flag that stores if the outer Edges had been sorted } - removeInnerEdge(edge) { + removeInnerEdge(edge: Edge) { this.innerEdges = this.innerEdges.filter((e) => e.from.id !== edge.from.id); } - removeOuterEdge(edge) { + removeOuterEdge(edge: Edge) { this.outerEdges = this.outerEdges.filter((e) => e.to.id !== edge.to.id); } @@ -39,7 +39,7 @@ export default class Node { * @memberof Node * @param {Edge} edge - Edge to add as an outerEdge. */ - addOuterEdge(edge) { + addOuterEdge(edge: Edge) { this.outerEdges.push(edge); this.outerEdgesSorted = false; } @@ -115,12 +115,12 @@ export default class Node { return this.outerEdges; } - getOuterEdge(i) { + getOuterEdge(i: number) { this.sortOuterEdges(); return this.outerEdges[i]; } - addInnerEdge(edge) { + addInnerEdge(edge: Edge) { this.innerEdges.push(edge); } } diff --git a/packages/turf-polygonize/lib/util.ts b/packages/turf-polygonize/lib/util.ts index 1bb3945941..c8502544af 100644 --- a/packages/turf-polygonize/lib/util.ts +++ b/packages/turf-polygonize/lib/util.ts @@ -1,5 +1,5 @@ import booleanPointInPolygon from "@turf/boolean-point-in-polygon"; -import { point } from "@turf/helpers"; +import { Feature, point, Polygon } from "@turf/helpers"; // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign#Polyfill function mathSign(x: number) { @@ -22,7 +22,7 @@ function mathSign(x: number) { * -1 if q is cw (right) from p1->p2, * 0 if q is colinear with p1->p2 */ -function orientationIndex(p1, p2, q) { +export function orientationIndex(p1: number[], p2: number[], q: number[]) { const dx1 = p2[0] - p1[0], dy1 = p2[1] - p1[1], dx2 = q[0] - p2[0], @@ -40,17 +40,20 @@ function orientationIndex(p1, p2, q) { * @param {Feature} env2 - Envelope * @returns {boolean} - True if the envelopes are equal */ -function envelopeIsEqual(env1, env2) { - const envX1 = env1.geometry.coordinates.map((c) => c[0]), - envY1 = env1.geometry.coordinates.map((c) => c[1]), - envX2 = env2.geometry.coordinates.map((c) => c[0]), - envY2 = env2.geometry.coordinates.map((c) => c[1]); +export function envelopeIsEqual( + env1: Feature, + env2: Feature +) { + const envX1 = env1.geometry.coordinates[0].map((c) => c[0]), + envY1 = env1.geometry.coordinates[0].map((c) => c[1]), + envX2 = env2.geometry.coordinates[0].map((c) => c[0]), + envY2 = env2.geometry.coordinates[0].map((c) => c[1]); return ( - Math.max(null, envX1) === Math.max(null, envX2) && - Math.max(null, envY1) === Math.max(null, envY2) && - Math.min(null, envX1) === Math.min(null, envX2) && - Math.min(null, envY1) === Math.min(null, envY2) + 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) ); } @@ -65,7 +68,10 @@ function envelopeIsEqual(env1, env2) { * @param {Feature} env - Envelope * @returns {boolean} - True if env is contained in self */ -function envelopeContains(self, env) { +export function envelopeContains( + self: Feature, + env: Feature +) { return env.geometry.coordinates[0].every((c) => booleanPointInPolygon(point(c), self) ); @@ -78,13 +84,6 @@ function envelopeContains(self, env) { * @param {number[]} coord2 - Second coordinate * @returns {boolean} - True if coordinates are equal */ -function coordinatesEqual(coord1, coord2) { +export function coordinatesEqual(coord1: number[], coord2: number[]) { return coord1[0] === coord2[0] && coord1[1] === coord2[1]; } - -export { - orientationIndex, - envelopeIsEqual, - envelopeContains, - coordinatesEqual, -}; diff --git a/tsconfig.shared.json b/tsconfig.shared.json index 2ab59d2a94..e351d2abb2 100644 --- a/tsconfig.shared.json +++ b/tsconfig.shared.json @@ -4,7 +4,7 @@ "module": "commonjs", "declaration": true, "esModuleInterop": true, - "strict": false, + "strict": true, "moduleResolution": "node" } } diff --git a/yarn.lock b/yarn.lock index ffc508f86b..ac0bc13642 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1200,6 +1200,11 @@ resolved "https://registry.yarnpkg.com/@types/deep-equal/-/deep-equal-1.0.1.tgz#71cfabb247c22bcc16d536111f50c0ed12476b03" integrity sha512-mMUu4nWHLBlHtxXY17Fg6+ucS/MnndyOWyOe7MmwkoMYxvfQU2ajtRaEvqSUv+aVkMqH/C0NCI8UoVfRNQ10yg== +"@types/density-clustering@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/density-clustering/-/density-clustering-1.3.0.tgz#43788f753498cc1912619bbaa942b8f68a06f244" + integrity sha512-3dBJlxpR8vHFSA4C0JDruxl2UqdSVoP3shJdqWctaXhS+pli6NeQB2zweoRyO/QIYxgwYaAuqGTb/Henq6mvcA== + "@types/estree@*", "@types/estree@^0.0.48": version "0.0.48" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.48.tgz#18dc8091b285df90db2f25aa7d906cfc394b7f74" @@ -1215,6 +1220,13 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== +"@types/geojson-equality@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@types/geojson-equality/-/geojson-equality-0.2.0.tgz#2c7d411f534bee6bf2e7689c14c4c6f3dced2a8c" + integrity sha512-JH6J3S3MW79WKbEuSrwosuu3oUnC6mujFL7UosxlcIWIHwC77Zg1+edkC4oZTRHXAp8SfqTisIEPBBOWLAwlzw== + dependencies: + "@types/geojson" "*" + "@types/geojson@*": version "7946.0.1" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.1.tgz#1fc41280e42f08f0d568401a556bc97c34f5262e" @@ -1300,6 +1312,11 @@ dependencies: "@types/node" "*" +"@types/skmeans@^0.11.2": + version "0.11.2" + resolved "https://registry.yarnpkg.com/@types/skmeans/-/skmeans-0.11.2.tgz#33b74ff650a166ea86ae4644d8ce16c50a93e603" + integrity sha512-VFOatc1ITAAaYjslFTow+2qJckJROAa5eUvivcTZ4wnSLELFCVt3ezwC0ENl21A0SfqclhKeK4unthZ3uTBCCg== + "@types/tape@*": version "4.2.32" resolved "https://registry.yarnpkg.com/@types/tape/-/tape-4.2.32.tgz#1188330d22c4e822648c344faa070277737982d9"