From 5857473cc7fb7b86b8ee66aebd6579bb3097d59e Mon Sep 17 00:00:00 2001 From: Tom Wanzek Date: Wed, 3 May 2017 14:09:12 -0400 Subject: [PATCH] [d3-contour] Add Definitions (#16280) * [d3-contour] Initialize skeleton * [d3-contour] * Initial definitions * Add DOM to compilation context libraries * [d3-contour] * Add JSDoc comments * Unify `thresholds(...)`-setter signatures which accept generator functions. * [d3-contour] * Add tests * Add additional lint-rule to ignore unified-signatures for now * [d3-contour] * Linted. * [d3-contour] Lint arrow function --- types/d3-contour/d3-contour-tests.ts | 189 ++++++++++++++++++++ types/d3-contour/index.d.ts | 246 +++++++++++++++++++++++++++ types/d3-contour/tsconfig.json | 23 +++ types/d3-contour/tslint.json | 6 + 4 files changed, 464 insertions(+) create mode 100644 types/d3-contour/d3-contour-tests.ts create mode 100644 types/d3-contour/index.d.ts create mode 100644 types/d3-contour/tsconfig.json create mode 100644 types/d3-contour/tslint.json diff --git a/types/d3-contour/d3-contour-tests.ts b/types/d3-contour/d3-contour-tests.ts new file mode 100644 index 0000000000..a70f1f44e7 --- /dev/null +++ b/types/d3-contour/d3-contour-tests.ts @@ -0,0 +1,189 @@ +/** + * Typescript definition tests for d3/d3-contour module + * + * Note: These tests are intended to test the definitions only + * in the sense of typing and call signature consistency. They + * are not intended as functional tests. + */ + +import * as d3Contour from 'd3-contour'; +import { + range, + thresholdSturges, + ThresholdArrayGenerator, + ThresholdCountGenerator +} from 'd3-array'; +import { geoPath } from 'd3-geo'; +import { randomNormal } from 'd3-random'; + +// ----------------------------------------------------------------------------- +// Preparatory Steps +// ----------------------------------------------------------------------------- + +// Some test setup is based on Contour Plot II Golstein-Price function at https://bl.ocks.org/mbostock/f48ff9c1af4d637c9a518727f5fdfef5 + +const n = 256; +const m = 256; +const values: number[] = new Array(n * m); +for (let j = 0.5, k = 0; j < m; ++j) { + for (let i = 0.5; i < n; ++i, ++k) { + values[k] = goldsteinPrice(i / n * 4 - 2, 1 - j / m * 3); + } +} + +function goldsteinPrice(x: number, y: number) { + return (1 + Math.pow(x + y + 1, 2) * (19 - 14 * x + 3 * x * x - 14 * y + 6 * x * x + 3 * y * y)) + * (30 + Math.pow(2 * x - 3 * y, 2) * (18 - 32 * x + 12 * x * x + 48 * y - 36 * x * y + 27 * y * y)); +} + +let size: [number, number]; +let boolFlag: boolean; +const thresholdArrayGen: ThresholdArrayGenerator = (values: number[], min: number, max: number) => { + let thresholds: number[]; + thresholds = [values[1], values[2], values[4]]; + return thresholds; +}; + +let thresholdGenerator: ThresholdArrayGenerator | ThresholdCountGenerator; +let pathStringMaybe: string | undefined; +let num: number; + +const pathSolo = geoPath(); + +// ----------------------------------------------------------------------------- +// Test Contour Generator +// ----------------------------------------------------------------------------- + +// Get contour generator ------------------------------------------------------- + +let contGen: d3Contour.Contours = d3Contour.contours(); + +// Configure contour generator ================================================= + +// size(...) ------------------------------------------------------------------- + +// set with chainability +contGen = contGen.size([n, m]); + +size = contGen.size(); + +// smooth(...) ----------------------------------------------------------------- + +// set with chainability +contGen = contGen.smooth(true); + +boolFlag = contGen.smooth(); + +// thresholds(...) ------------------------------------------------------------- + +// set with count +contGen = contGen.thresholds(10); + +// set with array +const thresholds1 = range(1, 21) + .map(p => Math.pow(2, p)); +contGen = contGen.thresholds(thresholds1); + +// set with threshold array generator + +contGen = contGen.thresholds(thresholdArrayGen); // mock + +// set with threshold count generator + +contGen = contGen.thresholds(thresholdSturges); + +// get +thresholdGenerator = contGen.thresholds(); + +// Use contour generator ======================================================= + +pathStringMaybe = pathSolo(contGen(values)[0]); + +// ----------------------------------------------------------------------------- +// Test Contour Generator for Density Estimates +// ----------------------------------------------------------------------------- + +interface CustomDatum { + x: number; + y: number; +} + +// Get contour generator ------------------------------------------------------- + +let contDensDefault: d3Contour.ContourDensity<[number, number]> = d3Contour.contourDensity(); +let contDensCustom: d3Contour.ContourDensity = d3Contour.contourDensity(); + +// Configure contour generator ================================================= + +// x(...) ---------------------------------------------------------------------- + +// set with chainability +contDensCustom = contDensCustom.x((datum) => { + const d: CustomDatum = datum; // check passed in argument type + return d.x; +}); + +// get +const xAcc: (d: CustomDatum) => number = contDensCustom.x(); + +// y(...) ---------------------------------------------------------------------- + +// set with chainability +contDensCustom = contDensCustom.y((datum) => { + const d: CustomDatum = datum; // check passed in argument type + return d.y; +}); + +// get +const yAcc: (d: CustomDatum) => number = contDensCustom.y(); + +// size(...) ------------------------------------------------------------------- + +// set with chainability +contDensCustom = contDensCustom.size([900, 600]); + +size = contDensCustom.size(); + +// cellSize(...) ----------------------------------------------------------------- + +// set with chainability +contDensCustom = contDensCustom.cellSize(3); + +num = contDensCustom.cellSize(); + +// thresholds(...) ------------------------------------------------------------- + +// set with count +contDensCustom = contDensCustom.thresholds(10); + +// set with array +contDensCustom = contDensCustom.thresholds([0.1, 0.25, 0.5, 0.75, 1]); + +// set with threshold array generator +contDensCustom = contDensCustom.thresholds(thresholdArrayGen); // mock + +// set with threshold count generator +contDensCustom = contDensCustom.thresholds(thresholdSturges); + +// get +thresholdGenerator = contDensCustom.thresholds(); + +// bandwidth(...) -------------------------------------------------------------- +// set with chainability +contDensCustom = contDensCustom.bandwidth(40); +// get +num = contDensCustom.bandwidth(); + +// Use contour generator ======================================================= + +const indNorm: CustomDatum[] = []; +const rX = randomNormal(); +const rY = randomNormal(1, 2); +for (let i = 0; i < 1000; i++) { + indNorm.push({ + x: rX(), + y: rY() + }); +} + +pathStringMaybe = pathSolo(contDensCustom(indNorm)[0]); diff --git a/types/d3-contour/index.d.ts b/types/d3-contour/index.d.ts new file mode 100644 index 0000000000..2222e459a1 --- /dev/null +++ b/types/d3-contour/index.d.ts @@ -0,0 +1,246 @@ +// Type definitions for d3-contour 1.1 +// Project: https://d3js.org/d3-contour/ +// Definitions by: Tom Wanzek +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +// Last module patch version validated against: 1.1.0 + +import { MultiPolygon } from 'geojson'; +import { ThresholdArrayGenerator, ThresholdCountGenerator } from 'd3-array'; + +/** + * An extended GeoJSON MultiPolygon representing a contour. + */ +export interface ContourMultiPolygon extends MultiPolygon { + /** + * Threshold value of the contour. + */ + value: number; +} + +/** + * A contour generator which computes contour polygons by applying marching squares to a rectangular array of numeric values. + * + * For each threshold value, the contour generator constructs a GeoJSON MultiPolygon geometry object representing the area + * where the input values are greater than or equal to the threshold value. + * The geometry is in planar coordinates, where ⟨i + 0.5, j + 0.5⟩ corresponds to element i + jn in the input values array. + * + */ +export interface Contours { + /** + * Computes the contours for the given array of values, returning an array of GeoJSON MultiPolygon geometry objects. + * Each geometry object represents the area where the input values are greater than or equal to the corresponding threshold value; + * the threshold value for each geometry object is exposed as geometry.value. + * + * The returned geometry objects are typically passed to d3.geoPath to display, + * using null or d3.geoIdentity as the associated projection + * + * @param values Array of input values. The input values must be an array of length n×m where [n, m] is the contour generator’s size; + * furthermore, each values[i + jn] must represent the value at the position ⟨i, j⟩. + */ + (values: number[]): ContourMultiPolygon[]; + + /** + * Return the expected size of the input values grid, which defaults to [1,1]. + */ + size(): [number, number]; + /** + * Sets the expected size of the input values grid to the contour generator and returns the contour generator. + * + * @param size Size of the input values grid specified as an array [n, m] + * where n is the number of columns in the grid and m is the number of rows; n and m must be positive integers. + */ + size(size: [number, number]): this; + + /** + * Returns the current smoothing flag, which defaults to true. + */ + smooth(): boolean; + /** + * Sets whether or not the generated contour polygons are smoothed using linear interpolation and returns the contour generator. + * + * @param smooth Flag to enable linear interpolation. The default is "true". + */ + smooth(smooth: boolean): this; + + /** + * Returns the current threshold generator, which by default implements Sturges’ formula. + */ + thresholds(): ThresholdCountGenerator | ThresholdArrayGenerator; + /** + * Sets the threshold generator to use the specified count and returns this contour generator. + * The input values’ extent will be uniformly divided into approximately count bins. + * + * @param count Expected number of threshold bins. + */ + thresholds(count: number): this; + /** + * Sets the threshold generator to the specified array and returns this contour generator. + * + * Thresholds are defined as an array of values [x0, x1, …]. + * The first generated contour corresponds to the area where the input values are greater than or equal to x0; + * the second contour corresponds to the area where the input values are greater than or equal to x1, and so on. + * Thus, there is exactly one generated MultiPolygon geometry object for each specified threshold value; + * the threshold value is exposed as geometry.value. + * + * @param thresholds Array of thresholds to use. + */ + thresholds(thresholds: number[]): this; + /** + * Sets the threshold generator to the specified function and returns this contour generator. + * + * Thresholds are defined as an array of values [x0, x1, …]. + * The first generated contour corresponds to the area where the input values are greater than or equal to x0; + * the second contour corresponds to the area where the input values are greater than or equal to x1, and so on. + * Thus, there is exactly one generated MultiPolygon geometry object for each specified threshold value; + * the threshold value is exposed as geometry.value. + * + * @param thresholds A threshold generator function. The threshold generator function is passed the array of input values + * as its argument and returns either an array of calculated thresholds, or the count of thresholds to use. + */ + thresholds(thresholds: ThresholdCountGenerator | ThresholdArrayGenerator): this; +} + +/** + * Construct a new contour generator with the default settings. + */ +export function contours(): Contours; + +/** + * A contour generator for density estimates. + */ +export interface ContourDensity { + /** + * Estimates the density contours for the given array of data, returning an array of GeoJSON MultiPolygon geometry objects. + * Each geometry object represents the area where the estimated number of points per square pixel is greater than or equal to + * the corresponding threshold value; the threshold value for each geometry object is exposed as geometry.value. + * The returned geometry objects are typically passed to d3.geoPath to display, using null or d3.geoIdentity as the associated projection. + * See also d3.contours. + * + * The x- and y-coordinate for each data point are computed using density.x and density.y. + * The generated contours are only accurate within the estimator’s defined size. + * + * @param data Array of input data. + */ + (data: Datum[]): ContourMultiPolygon[]; + + /** + * Returns the current x-coordinate accessor. + * The default x-coordinate accessor is a functions which accepts as input a two-element array of numbers + * and returns the element at index 0. + */ + x(): (d: Datum) => number; + /** + * Sets the x-coordinate accessor and returns the density contour estimator. + * + * @param x An x-coordinate accessor function, which accepts as input an element of the input data array and returns the + * x-coordinate. + */ + x(x: (d: Datum) => number): this; + + /** + * Returns the current y-coordinate accessor. + * The default y-coordinate accessor is a functions which accepts as input a two-element array of numbers + * and returns the element at index 1. + */ + y(): (d: Datum) => number; + /** + * Sets the y-coordinate accessor and returns the density contour estimator. + * + * @param y An y-coordinate accessor function, which accepts as input an element of the input data array and returns the + * y-coordinate. + */ + y(y: (d: Datum) => number): this; + + /** + * Returns the current size, which defaults to [960, 500]. + */ + size(): [number, number]; + /** + * Sets the size of the density estimator to the specified bounds and returns the density contour estimator. + * + * @param size The size is specified as an array [width, height], where width is the maximum x-value and height is the maximum y-value. + */ + size(size: [number, number]): this; + + /** + * Returns the current cell size, which defaults to 4. + */ + cellSize(): number; + /** + * Sets the size of individual cells in the underlying bin grid to the specified positive integer and returns the density contour estimator. + * + * The cell size is rounded down to the nearest power of two. Smaller cells produce more detailed contour polygons, but are more expensive to compute. + * + * @param cellSize Cell size, a positive integer. + */ + cellSize(cellSize: number): this; + + /** + * Returns the current threshold generator, which by default generates about twenty nicely-rounded density thresholds. + */ + thresholds(): ThresholdCountGenerator | ThresholdArrayGenerator; + /** + * Sets the threshold generator to use the specified count and returns this density contour estimator. + * Approximately count uniformly-spaced nicely-rounded thresholds will be generated. + * + * @param count Expected number of thresholds. + */ + thresholds(count: number): this; + /** + * Sets the threshold generator to the specified array and returns this density contour estimator. + * + * Thresholds are defined as an array of values [x0, x1, …]. The first generated density contour corresponds to the area + * where the estimated density is greater than or equal to x0; the second contour corresponds to the area + * where the estimated density is greater than or equal to x1, and so on. + * Thus, there is exactly one generated MultiPolygon geometry object for each specified threshold value; + * the threshold value is exposed as geometry.value. The first value x0 should typically be greater than zero. + * + * @param thresholds Array of thresholds to use. + */ + thresholds(thresholds: number[]): this; + /** + * Sets the threshold generator to the specified function and returns this density contour estimator. + * + * Thresholds are defined as an array of values [x0, x1, …]. The first generated density contour corresponds to the area + * where the estimated density is greater than or equal to x0; the second contour corresponds to the area + * where the estimated density is greater than or equal to x1, and so on. + * Thus, there is exactly one generated MultiPolygon geometry object for each specified threshold value; + * the threshold value is exposed as geometry.value. The first value x0 should typically be greater than zero. + * + * @param thresholds A threshold generator function. The threshold generator function is passed the array of input values + * as its argument and returns either an array of calculated thresholds, or the count of thresholds to use. + */ + thresholds(thresholds: ThresholdCountGenerator | ThresholdArrayGenerator): this; + + /** + * Returns the current bandwidth, which defaults to 20.4939…. + */ + bandwidth(): number; + /** + * Sets the bandwidth (the standard deviation) of the Gaussian kernel and returns the density contour estimator. + * + * @param bandwidth Bandwidth (the standard deviation) of the Gaussian kernel. + * The specified bandwidth is currently rounded to the nearest supported value by this implementation, and must be nonnegative. + */ + bandwidth(bandwidth: number): this; +} + +/** + * Construct a new contour generator for density estimates with the default settings. + * + * The default settings assume that, the elements of the data array used + * with the density contour generator are two-element arrays. The first element + * corresponds to the x-dimension, the second to the y-dimension. + */ +export function contourDensity(): ContourDensity<[number, number]>; +/** + * Construct a new contour generator for density estimates. + * + * The generic refers to the data type of an element in the data array + * used with the density contour generator. + * + * Important: ensure that the x- and y-accessor functions are configured to + * match the data type used for the generic Datum. + */ +export function contourDensity(): ContourDensity; diff --git a/types/d3-contour/tsconfig.json b/types/d3-contour/tsconfig.json new file mode 100644 index 0000000000..4f9d0a8284 --- /dev/null +++ b/types/d3-contour/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6", + "dom" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "d3-contour-tests.ts" + ] +} diff --git a/types/d3-contour/tslint.json b/types/d3-contour/tslint.json new file mode 100644 index 0000000000..08016de61a --- /dev/null +++ b/types/d3-contour/tslint.json @@ -0,0 +1,6 @@ +{ + "extends": "dtslint/dt.json", + "rules": { + "unified-signatures": false + } +}