diff --git a/packages/turf-clusters/index.js b/packages/turf-clusters/index.js index f3f37b6ea4..ab3407ab1f 100644 --- a/packages/turf-clusters/index.js +++ b/packages/turf-clusters/index.js @@ -1,5 +1,8 @@ var featureEach = require('@turf/meta').featureEach; var featureCollection = require('@turf/helpers').featureCollection; +var utils = require('./utils'); +var applyFilter = utils.applyFilter; +var createBins = utils.createBins; /** * Get Cluster @@ -186,114 +189,8 @@ function clusterReduce(geojson, property, callback, initialValue) { return previousValue; } -/** - * Create Bins - * - * @private - * @param {FeatureCollection} geojson GeoJSON Features - * @param {string|number} property Property values are used to create bins - * @returns {Object} bins with Feature IDs - * @example - * const geojson = turf.featureCollection([ - * turf.point([0, 0], {cluster: 0, foo: 'null'}), - * turf.point([2, 4], {cluster: 1, foo: 'bar'}), - * turf.point([5, 1], {0: 'foo'}), - * turf.point([3, 6], {cluster: 1}), - * ]); - * createBins(geojson, 'cluster'); - * //= { '0': [ 0 ], '1': [ 1, 3 ] } - */ -function createBins(geojson, property) { - var bins = {}; - - featureEach(geojson, function (feature, i) { - var properties = feature.properties || {}; - if (properties.hasOwnProperty(property)) { - var value = properties[property]; - if (bins.hasOwnProperty(value)) bins[value].push(i); - else bins[value] = [i]; - } - }); - return bins; -} - -/** - * Apply Filter - * - * @private - * @param {*} properties Properties - * @param {*} filter Filter - * @returns {Boolean} applied Filter to properties - */ -function applyFilter(properties, filter) { - if (properties === undefined) return false; - var filterType = typeof filter; - - // String & Number - if (filterType === 'number' || filterType === 'string') return properties.hasOwnProperty(filter); - // Array - else if (Array.isArray(filter)) { - for (var i = 0; i < filter.length; i++) { - if (!applyFilter(properties, filter[i])) return false; - } - return true; - // Object - } else { - return propertiesContainsFilter(properties, filter); - } -} - -/** - * Properties contains filter (does not apply deepEqual operations) - * - * @private - * @param {*} properties Properties - * @param {Object} filter Filter - * @returns {Boolean} does filter equal Properties - * @example - * propertiesContainsFilter({foo: 'bar', cluster: 0}, {cluster: 0}) - * //= true - * propertiesContainsFilter({foo: 'bar', cluster: 0}, {cluster: 1}) - * //= false - */ -function propertiesContainsFilter(properties, filter) { - var keys = Object.keys(filter); - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - if (properties[key] !== filter[key]) return false; - } - return true; -} - -/** - * Filter Properties - * - * @private - * @param {*} properties Properties - * @param {string[]} keys Used to filter Properties - * @returns {*} filtered Properties - * @example - * filterProperties({foo: 'bar', cluster: 0}, ['cluster']) - * //= {cluster: 0} - */ -function filterProperties(properties, keys) { - if (!keys) return {}; - if (!keys.length) return {}; - - var newProperties = {}; - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - if (properties.hasOwnProperty(key)) newProperties[key] = properties[key]; - } - return newProperties; -} - module.exports = { getCluster: getCluster, clusterEach: clusterEach, - clusterReduce: clusterReduce, - createBins: createBins, // Not exposed in @turf/turf - Internal purposes only - applyFilter: applyFilter, // Not exposed in @turf/turf - Internal purposes only - propertiesContainsFilter: propertiesContainsFilter, // Not exposed in @turf/turf - Internal purposes only - filterProperties: filterProperties // Not exposed in @turf/turf - Internal purposes only + clusterReduce: clusterReduce }; diff --git a/packages/turf-clusters/package.json b/packages/turf-clusters/package.json index 1636c667a1..cc960eaed7 100644 --- a/packages/turf-clusters/package.json +++ b/packages/turf-clusters/package.json @@ -6,7 +6,8 @@ "types": "index.d.ts", "files": [ "index.js", - "index.d.ts" + "index.d.ts", + "utils.js" ], "scripts": { "test": "node test.js", diff --git a/packages/turf-clusters/test.js b/packages/turf-clusters/test.js index 598976d262..928feadee5 100644 --- a/packages/turf-clusters/test.js +++ b/packages/turf-clusters/test.js @@ -1,7 +1,14 @@ const test = require('tape'); const {featureCollection, point} = require('@turf/helpers'); -const {propertiesContainsFilter, filterProperties, applyFilter, createBins} = require('./'); // Testing Purposes -const {getCluster, clusterEach, clusterReduce} = require('./'); +const { + propertiesContainsFilter, + filterProperties, + applyFilter, + createBins} = require('./utils'); +const { + getCluster, + clusterEach, + clusterReduce} = require('./'); const properties = {foo: 'bar', cluster: 0}; const geojson = featureCollection([ @@ -51,8 +58,7 @@ test('clusters -- clusterReduce', t => { t.end(); }); -// Internal purposes only -test('clusters -- applyFilter', t => { +test('clusters.utils -- applyFilter', t => { t.true(applyFilter(properties, 'cluster')); t.true(applyFilter(properties, ['cluster'])); t.false(applyFilter(properties, {cluster: 1})); @@ -61,24 +67,21 @@ test('clusters -- applyFilter', t => { t.end(); }); -// Internal purposes only -test('clusters -- filterProperties', t => { +test('clusters.utils -- filterProperties', t => { t.deepEqual(filterProperties(properties, ['cluster']), {cluster: 0}); t.deepEqual(filterProperties(properties, []), {}); t.deepEqual(filterProperties(properties, undefined), {}); t.end(); }); -// Internal purposes only -test('clusters -- propertiesContainsFilter', t => { +test('clusters.utils -- propertiesContainsFilter', t => { t.deepEqual(propertiesContainsFilter(properties, {cluster: 0}), true); t.deepEqual(propertiesContainsFilter(properties, {cluster: 1}), false); t.deepEqual(propertiesContainsFilter(properties, {bar: 'foo'}), false); t.end(); }); -// Internal purposes only -test('clusters -- propertiesContainsFilter', t => { +test('clusters.utils -- propertiesContainsFilter', t => { t.deepEqual(createBins(geojson, 'cluster'), {'0': [0], '1': [1, 2]}); t.end(); }); diff --git a/packages/turf-clusters/utils.js b/packages/turf-clusters/utils.js new file mode 100644 index 0000000000..3b9b1bb387 --- /dev/null +++ b/packages/turf-clusters/utils.js @@ -0,0 +1,110 @@ +var featureEach = require('@turf/meta').featureEach; + +/** + * Create Bins + * + * @private + * @param {FeatureCollection} geojson GeoJSON Features + * @param {string|number} property Property values are used to create bins + * @returns {Object} bins with Feature IDs + * @example + * const geojson = turf.featureCollection([ + * turf.point([0, 0], {cluster: 0, foo: 'null'}), + * turf.point([2, 4], {cluster: 1, foo: 'bar'}), + * turf.point([5, 1], {0: 'foo'}), + * turf.point([3, 6], {cluster: 1}), + * ]); + * createBins(geojson, 'cluster'); + * //= { '0': [ 0 ], '1': [ 1, 3 ] } + */ +function createBins(geojson, property) { + var bins = {}; + + featureEach(geojson, function (feature, i) { + var properties = feature.properties || {}; + if (properties.hasOwnProperty(property)) { + var value = properties[property]; + if (bins.hasOwnProperty(value)) bins[value].push(i); + else bins[value] = [i]; + } + }); + return bins; +} + +/** +* Apply Filter +* +* @private +* @param {*} properties Properties +* @param {*} filter Filter +* @returns {Boolean} applied Filter to properties +*/ +function applyFilter(properties, filter) { + if (properties === undefined) return false; + var filterType = typeof filter; + + // String & Number + if (filterType === 'number' || filterType === 'string') return properties.hasOwnProperty(filter); + // Array + else if (Array.isArray(filter)) { + for (var i = 0; i < filter.length; i++) { + if (!applyFilter(properties, filter[i])) return false; + } + return true; + // Object + } else { + return propertiesContainsFilter(properties, filter); + } +} + +/** +* Properties contains filter (does not apply deepEqual operations) +* +* @private +* @param {*} properties Properties +* @param {Object} filter Filter +* @returns {Boolean} does filter equal Properties +* @example +* propertiesContainsFilter({foo: 'bar', cluster: 0}, {cluster: 0}) +* //= true +* propertiesContainsFilter({foo: 'bar', cluster: 0}, {cluster: 1}) +* //= false +*/ +function propertiesContainsFilter(properties, filter) { + var keys = Object.keys(filter); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (properties[key] !== filter[key]) return false; + } + return true; +} + +/** +* Filter Properties +* +* @private +* @param {*} properties Properties +* @param {string[]} keys Used to filter Properties +* @returns {*} filtered Properties +* @example +* filterProperties({foo: 'bar', cluster: 0}, ['cluster']) +* //= {cluster: 0} +*/ +function filterProperties(properties, keys) { + if (!keys) return {}; + if (!keys.length) return {}; + + var newProperties = {}; + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (properties.hasOwnProperty(key)) newProperties[key] = properties[key]; + } + return newProperties; +} + +module.exports = { + createBins: createBins, + applyFilter: applyFilter, + propertiesContainsFilter: propertiesContainsFilter, + filterProperties: filterProperties +}; diff --git a/packages/turf/index.d.ts b/packages/turf/index.d.ts index de7e49dc4f..62868999e7 100644 --- a/packages/turf/index.d.ts +++ b/packages/turf/index.d.ts @@ -49,6 +49,17 @@ import { lineEach, // v4.7.0 lineReduce // v4.7.0 } from '@turf/meta'; +import * as clusters from '@turf/clusters'; // v4.6.0 +import { + clusterEach, + clusterReduce, + getCluster +} from '@turf/clusters'; // v4.6.0 +import * as projection from '@turf/projection'; // v4.7.0 +import { + toMercator, + toWgs84 +} from '@turf/projection'; // v4.7.0 import * as isolines from '@turf/isolines'; import * as convex from '@turf/convex'; import * as within from '@turf/within'; @@ -134,6 +145,11 @@ import * as cleanCoords from '@turf/clean-coords'; import * as pointToLineDistance from '@turf/point-to-line-distance'; // v4.7.0 import * as booleanParallel from '@turf/boolean-parallel'; // v4.8.0 import * as nearestPointToLine from '@turf/nearest-point-to-line'; // v4.8.0 +import * as clustersDbscan from '@turf/clusters-dbscan'; // v4.6.0 +import * as clustersKmeans from '@turf/clusters-kmeans'; // v4.6.0 +import * as interpolate from '@turf/interpolate'; // v4.6.0 +import * as booleanPointOnLine from '@turf/boolean-point-on-line' // v4.6.0 +import * as booleanOverlap from '@turf/boolean-overlap' // v4.6.0 export { isolines, convex, @@ -256,7 +272,7 @@ export { clone, segmentEach, segmentReduce, - cleanCoords, + cleanCoords, // v4.6.0 isNumber, pointToLineDistance, // v4.7.0 helpers, @@ -266,5 +282,17 @@ export { lineReduce, // v4.7.0 getType, // v4.8.0 booleanParallel, // v4.8.0 - nearestPointToLine // v4.8.0 -}; \ No newline at end of file + nearestPointToLine, // v4.8.0 + clusters, // v4.6.0 + clustersDbscan, // v4.6.0 + clustersKmeans, // v4.6.0 + clusterEach, // v4.6.0 + clusterReduce, // v4.6.0 + getCluster, // v4.6.0 + interpolate, // v4.6.0 + booleanPointOnLine, // v4.6.0 + booleanOverlap, // v4.6.0 + projection, // v4.7.0 + toMercator, // v4.7.0 + toWgs84 // v4.7.0 +}; diff --git a/packages/turf/index.js b/packages/turf/index.js index 00b081d105..0d0b0cda9d 100644 --- a/packages/turf/index.js +++ b/packages/turf/index.js @@ -11,6 +11,7 @@ var helpers = require('@turf/helpers'); var invariant = require('@turf/invariant'); var meta = require('@turf/meta'); var clusters = require('@turf/clusters'); +var projection = require('@turf/projection'); // v4.7.0 var turf = { isolines: require('@turf/isolines'), @@ -103,9 +104,14 @@ var turf = { pointToLineDistance: require('@turf/point-to-line-distance'), // v4.7.0 booleanParallel: require('@turf/boolean-parallel'), // v4.8.0 nearestPointToLine: require('@turf/nearest-point-to-line'), // v4.8.0 + projection: projection, // v4.7.0 + toMercator: projection.toMercator, // v4.7.0 + toWgs84: projection.toWgs84, // v4.7.0 + clusters: clusters, // v4.6.0 getCluster: clusters.getCluster, // v4.6.0 clusterEach: clusters.clusterEach, // v4.6.0 clusterReduce: clusters.clusterReduce, // v4.6.0 + helpers: helpers, point: helpers.point, polygon: helpers.polygon, lineString: helpers.lineString, @@ -126,6 +132,7 @@ var turf = { isNumber: helpers.isNumber, // 4.7.0 round: helpers.round, convertArea: helpers.convertArea, + invariant: invariant, getCoord: invariant.getCoord, getCoords: invariant.getCoords, geojsonType: invariant.geojsonType, @@ -135,6 +142,7 @@ var turf = { getType: invariant.getType, // v4.8.0 getGeom: invariant.getGeom, getGeomType: invariant.getGeomType, + meta: meta, coordEach: meta.coordEach, coordReduce: meta.coordReduce, propEach: meta.propEach, diff --git a/packages/turf/module.js b/packages/turf/module.js index b940e51be1..62868999e7 100644 --- a/packages/turf/module.js +++ b/packages/turf/module.js @@ -49,6 +49,17 @@ import { lineEach, // v4.7.0 lineReduce // v4.7.0 } from '@turf/meta'; +import * as clusters from '@turf/clusters'; // v4.6.0 +import { + clusterEach, + clusterReduce, + getCluster +} from '@turf/clusters'; // v4.6.0 +import * as projection from '@turf/projection'; // v4.7.0 +import { + toMercator, + toWgs84 +} from '@turf/projection'; // v4.7.0 import * as isolines from '@turf/isolines'; import * as convex from '@turf/convex'; import * as within from '@turf/within'; @@ -134,6 +145,11 @@ import * as cleanCoords from '@turf/clean-coords'; import * as pointToLineDistance from '@turf/point-to-line-distance'; // v4.7.0 import * as booleanParallel from '@turf/boolean-parallel'; // v4.8.0 import * as nearestPointToLine from '@turf/nearest-point-to-line'; // v4.8.0 +import * as clustersDbscan from '@turf/clusters-dbscan'; // v4.6.0 +import * as clustersKmeans from '@turf/clusters-kmeans'; // v4.6.0 +import * as interpolate from '@turf/interpolate'; // v4.6.0 +import * as booleanPointOnLine from '@turf/boolean-point-on-line' // v4.6.0 +import * as booleanOverlap from '@turf/boolean-overlap' // v4.6.0 export { isolines, convex, @@ -256,7 +272,7 @@ export { clone, segmentEach, segmentReduce, - cleanCoords, + cleanCoords, // v4.6.0 isNumber, pointToLineDistance, // v4.7.0 helpers, @@ -266,5 +282,17 @@ export { lineReduce, // v4.7.0 getType, // v4.8.0 booleanParallel, // v4.8.0 - nearestPointToLine // v4.8.0 + nearestPointToLine, // v4.8.0 + clusters, // v4.6.0 + clustersDbscan, // v4.6.0 + clustersKmeans, // v4.6.0 + clusterEach, // v4.6.0 + clusterReduce, // v4.6.0 + getCluster, // v4.6.0 + interpolate, // v4.6.0 + booleanPointOnLine, // v4.6.0 + booleanOverlap, // v4.6.0 + projection, // v4.7.0 + toMercator, // v4.7.0 + toWgs84 // v4.7.0 }; diff --git a/packages/turf/package.json b/packages/turf/package.json index a4725a41a6..c1df4c268a 100644 --- a/packages/turf/package.json +++ b/packages/turf/package.json @@ -128,6 +128,7 @@ "@turf/polygon-tangents": "^4.7.3", "@turf/polygon-to-linestring": "^4.7.3", "@turf/polygonize": "^4.7.3", + "@turf/projection": "^4.7.3", "@turf/random": "^4.7.3", "@turf/rewind": "^4.7.3", "@turf/rhumb-bearing": "^4.7.3", @@ -152,6 +153,7 @@ }, "devDependencies": { "browserify": "~9.0.3", + "camelcase": "^4.1.0", "disc": "^1.3.2", "documentation": "^4.0.0-rc.1", "glob": "^7.1.2", diff --git a/packages/turf/test.js b/packages/turf/test.js index fef45b3e09..d57985d660 100644 --- a/packages/turf/test.js +++ b/packages/turf/test.js @@ -3,6 +3,7 @@ const path = require('path'); const glob = require('glob'); const test = require('tape'); const documentation = require('documentation'); +const camelcase = require('camelcase'); const turf = require('./'); // Helpers @@ -23,7 +24,7 @@ for (const name of fs.readdirSync(directory)) { // Exclude main Turf module modules = modules.filter(({name}) => name !== 'turf'); -test('turf - required files', t => { +test('turf -- required files', t => { for (const {name, dir} of modules) { for (const filename of ['test.js', 'bench.js', 'index.js', 'index.d.ts', 'LICENSE', 'README.md', 'yarn.lock']) { if (!fs.existsSync(path.join(dir, filename))) t.fail(`${name} ${filename} is required`); @@ -34,7 +35,7 @@ test('turf - required files', t => { t.end(); }); -test('turf - invalid dependencies', t => { +test('turf -- invalid dependencies', t => { for (const {name, dependencies, devDependencies} of modules) { for (const invalidDependency of ['load-json-file', 'write-json-file', 'tape', 'benchmark', 'glob', 'lerna', 'documentation', 'uglify-js']) { if (dependencies[invalidDependency]) t.fail(`${name} ${invalidDependency} should be defined as devDependencies`); @@ -46,14 +47,14 @@ test('turf - invalid dependencies', t => { t.end(); }); -test('turf - strict version dependencies', t => { +test('turf -- strict version dependencies', t => { for (const {name, dependencies} of modules) { if (dependencies['jsts'] && dependencies['jsts'] !== '1.3.0') t.fail(`${name} jsts must use v1.3.0`); } t.end(); }); -test('turf - duplicate dependencies', t => { +test('turf -- duplicate dependencies', t => { for (const {name, dependencies, devDependencies} of modules) { for (const dependency of Object.keys(dependencies)) { if (devDependencies[dependency]) t.fail(`${name} ${dependency} is duplicated in devDependencies`); @@ -62,7 +63,7 @@ test('turf - duplicate dependencies', t => { t.end(); }); -test('turf - check if files exists', t => { +test('turf -- check if files exists', t => { for (const {name, dir, pckg} of modules) { const {files} = pckg; if (!files || !files.length) t.fail(`${name} (files) must be included in package.json`); @@ -73,7 +74,7 @@ test('turf - check if files exists', t => { t.end(); }); -test('turf - MIT license', t => { +test('turf -- MIT license', t => { const text = fs.readFileSync(path.join(__dirname, 'LICENSE'), 'utf8'); for (const {name, dir, pckg} of modules) { const {license} = pckg; @@ -83,7 +84,7 @@ test('turf - MIT license', t => { t.end(); }); -test('turf - contributors', t => { +test('turf -- contributors', t => { for (const {name, pckg} of modules) { for (const contributor of pckg.contributors || []) { if (!contributor.match(/<@.+>/)) t.fail(`${name} ${contributor} (contributors) should use "Full Name <@GitHub>"`); @@ -92,7 +93,7 @@ test('turf - contributors', t => { t.end(); }); -test('turf - scoped package name', t => { +test('turf -- scoped package name', t => { for (const {name, pckg} of modules) { const expected = name.replace('turf-', '@turf/'); if (pckg.name !== expected) t.fail(`${name} (name) must use ${expected} in package.json`); @@ -100,7 +101,7 @@ test('turf - scoped package name', t => { t.end(); }); -test('turf - pre-defined attributes in package.json', t => { +test('turf -- pre-defined attributes in package.json', t => { for (const {name, pckg} of modules) { if (pckg.author !== 'Turf Authors') t.fail(name + ' (author) should be "Turf Authors"'); if (pckg.main !== 'index.js') t.fail(`${name} (main) must be "index.js" in package.json`); @@ -111,7 +112,7 @@ test('turf - pre-defined attributes in package.json', t => { t.end(); }); -test('turf - parsing dependencies from index.js', t => { +test('turf -- parsing dependencies from index.js', t => { for (const {name, dir, dependencies} of modules) { const index = fs.readFileSync(path.join(dir, 'index.js'), 'utf8'); @@ -181,6 +182,37 @@ test('turf-${turfName}', t => { `; } +// Test for missing modules +test('turf -- missing modules', t => { + const files = { + typescript: fs.readFileSync(path.join(__dirname, 'index.d.ts')), + modules: fs.readFileSync(path.join(__dirname, 'module.js')) + }; + + modules.forEach(({name}) => { + name = camelcase(name.replace('turf-', '')); + // name exception with linestring => lineString + name = name.replace('linestring', 'lineString').replace('Linestring', 'LineString'); + + if (!files.typescript.includes(name)) t.fail(name + ' is missing from index.d.ts'); + if (!files.modules.includes(name)) t.fail(name + ' is missing from module.js'); + + switch (typeof turf[name]) { + case 'function': break; + case 'object': + Object.keys(turf[name]).forEach(method => { + if (typeof turf[method] !== 'function') t.fail(name + '.' + method + ' is missing from index.js'); + if (!files.typescript.includes(method)) t.fail(name + '.' + method + ' is missing from index.d.ts'); + if (!files.modules.includes(method)) t.fail(name + '.' + method + ' is missing from module.js'); + }); + break; + case 'undefined': + t.fail(name + ' is missing from index.js'); + } + }); + t.end(); +}); + // Iterate over each module and retrieve @example to build tests from them glob(turfModulesPath, (err, files) => { if (err) throw err; diff --git a/packages/turf/yarn.lock b/packages/turf/yarn.lock index 8442aa30f7..11312b93a5 100644 --- a/packages/turf/yarn.lock +++ b/packages/turf/yarn.lock @@ -703,6 +703,13 @@ dependencies: polygonize "1.0.1" +"@turf/projection@^4.7.3": + version "4.7.3" + resolved "https://registry.yarnpkg.com/@turf/projection/-/projection-4.7.3.tgz#c3e45745d89371ee720e43add56f08850e90694a" + dependencies: + "@turf/clone" "^4.7.3" + "@turf/meta" "^4.7.3" + "@turf/random@^4.7.3": version "4.7.3" resolved "https://registry.yarnpkg.com/@turf/random/-/random-4.7.3.tgz#0a8a3b84a1f15b8916d4dce43566beb2e2bda522"