"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var meta_1 = require("@turf/meta"); var helpers_1 = require("@turf/helpers"); /** * Get Cluster * * @name getCluster * @param {FeatureCollection} geojson GeoJSON Features * @param {*} filter Filter used on GeoJSON properties to get Cluster * @returns {FeatureCollection} Single Cluster filtered by GeoJSON Properties * @example * var geojson = turf.featureCollection([ * turf.point([0, 0], {'marker-symbol': 'circle'}), * turf.point([2, 4], {'marker-symbol': 'star'}), * turf.point([3, 6], {'marker-symbol': 'star'}), * turf.point([5, 1], {'marker-symbol': 'square'}), * turf.point([4, 2], {'marker-symbol': 'circle'}) * ]); * * // Create a cluster using K-Means (adds `cluster` to GeoJSON properties) * var clustered = turf.clustersKmeans(geojson); * * // Retrieve first cluster (0) * var cluster = turf.getCluster(clustered, {cluster: 0}); * //= cluster * * // Retrieve cluster based on custom properties * turf.getCluster(clustered, {'marker-symbol': 'circle'}).length; * //= 2 * turf.getCluster(clustered, {'marker-symbol': 'square'}).length; * //= 1 */ function getCluster(geojson, filter) { // Validation if (!geojson) throw new Error("geojson is required"); if (geojson.type !== "FeatureCollection") throw new Error("geojson must be a FeatureCollection"); if (filter === undefined || filter === null) throw new Error("filter is required"); // Filter Features var features = []; meta_1.featureEach(geojson, function (feature) { if (applyFilter(feature.properties, filter)) features.push(feature); }); return helpers_1.featureCollection(features); } exports.getCluster = getCluster; /** * Callback for clusterEach * * @callback clusterEachCallback * @param {FeatureCollection} [cluster] The current cluster being processed. * @param {*} [clusterValue] Value used to create cluster being processed. * @param {number} [currentIndex] The index of the current element being processed in the array.Starts at index 0 * @returns {void} */ /** * clusterEach * * @name clusterEach * @param {FeatureCollection} geojson GeoJSON Features * @param {string|number} property GeoJSON property key/value used to create clusters * @param {Function} callback a method that takes (cluster, clusterValue, currentIndex) * @returns {void} * @example * var geojson = turf.featureCollection([ * turf.point([0, 0]), * turf.point([2, 4]), * turf.point([3, 6]), * turf.point([5, 1]), * turf.point([4, 2]) * ]); * * // Create a cluster using K-Means (adds `cluster` to GeoJSON properties) * var clustered = turf.clustersKmeans(geojson); * * // Iterate over each cluster * turf.clusterEach(clustered, 'cluster', function (cluster, clusterValue, currentIndex) { * //= cluster * //= clusterValue * //= currentIndex * }) * * // Calculate the total number of clusters * var total = 0 * turf.clusterEach(clustered, 'cluster', function () { * total++; * }); * * // Create an Array of all the values retrieved from the 'cluster' property * var values = [] * turf.clusterEach(clustered, 'cluster', function (cluster, clusterValue) { * values.push(clusterValue); * }); */ function clusterEach(geojson, property, callback) { // Validation if (!geojson) throw new Error("geojson is required"); if (geojson.type !== "FeatureCollection") throw new Error("geojson must be a FeatureCollection"); if (property === undefined || property === null) throw new Error("property is required"); // Create clusters based on property values var bins = createBins(geojson, property); var values = Object.keys(bins); for (var index = 0; index < values.length; index++) { var value = values[index]; var bin = bins[value]; var features = []; for (var i = 0; i < bin.length; i++) { features.push(geojson.features[bin[i]]); } callback(helpers_1.featureCollection(features), value, index); } } exports.clusterEach = clusterEach; /** * Callback for clusterReduce * * The first time the callback function is called, the values provided as arguments depend * on whether the reduce method has an initialValue argument. * * If an initialValue is provided to the reduce method: * - The previousValue argument is initialValue. * - The currentValue argument is the value of the first element present in the array. * * If an initialValue is not provided: * - The previousValue argument is the value of the first element present in the array. * - The currentValue argument is the value of the second element present in the array. * * @callback clusterReduceCallback * @param {*} [previousValue] The accumulated value previously returned in the last invocation * of the callback, or initialValue, if supplied. * @param {FeatureCollection} [cluster] The current cluster being processed. * @param {*} [clusterValue] Value used to create cluster being processed. * @param {number} [currentIndex] The index of the current element being processed in the * array. Starts at index 0, if an initialValue is provided, and at index 1 otherwise. */ /** * Reduce clusters in GeoJSON Features, similar to Array.reduce() * * @name clusterReduce * @param {FeatureCollection} geojson GeoJSON Features * @param {string|number} property GeoJSON property key/value used to create clusters * @param {Function} callback a method that takes (previousValue, cluster, clusterValue, currentIndex) * @param {*} [initialValue] Value to use as the first argument to the first call of the callback. * @returns {*} The value that results from the reduction. * @example * var geojson = turf.featureCollection([ * turf.point([0, 0]), * turf.point([2, 4]), * turf.point([3, 6]), * turf.point([5, 1]), * turf.point([4, 2]) * ]); * * // Create a cluster using K-Means (adds `cluster` to GeoJSON properties) * var clustered = turf.clustersKmeans(geojson); * * // Iterate over each cluster and perform a calculation * var initialValue = 0 * turf.clusterReduce(clustered, 'cluster', function (previousValue, cluster, clusterValue, currentIndex) { * //=previousValue * //=cluster * //=clusterValue * //=currentIndex * return previousValue++; * }, initialValue); * * // Calculate the total number of clusters * var total = turf.clusterReduce(clustered, 'cluster', function (previousValue) { * return previousValue++; * }, 0); * * // Create an Array of all the values retrieved from the 'cluster' property * var values = turf.clusterReduce(clustered, 'cluster', function (previousValue, cluster, clusterValue) { * return previousValue.concat(clusterValue); * }, []); */ function clusterReduce(geojson, property, callback, initialValue) { var previousValue = initialValue; clusterEach(geojson, property, function (cluster, clusterValue, currentIndex) { if (currentIndex === 0 && initialValue === undefined) previousValue = cluster; else previousValue = callback(previousValue, cluster, clusterValue, currentIndex); }); return previousValue; } exports.clusterReduce = clusterReduce; /** * 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 * var 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 = {}; meta_1.featureEach(geojson, function (feature, i) { var properties = feature.properties || {}; if (Object.prototype.hasOwnProperty.call(properties, String(property))) { var value = properties[property]; if (Object.prototype.hasOwnProperty.call(bins, value)) bins[value].push(i); else bins[value] = [i]; } }); return bins; } exports.createBins = createBins; /** * 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 Object.prototype.hasOwnProperty.call(properties, 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); } } exports.applyFilter = applyFilter; /** * 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; } exports.propertiesContainsFilter = propertiesContainsFilter; /** * Filter Properties * * @private * @param {*} properties Properties * @param {Array} 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 (Object.prototype.hasOwnProperty.call(properties, key)) newProperties[key] = properties[key]; } return newProperties; } exports.filterProperties = filterProperties;