"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var clone_1 = __importDefault(require("@turf/clone")); var distance_1 = __importDefault(require("@turf/distance")); var meta_1 = require("@turf/meta"); var helpers_1 = require("@turf/helpers"); var density_clustering_1 = __importDefault(require("density-clustering")); /** * 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. * * @name clustersDbscan * @param {FeatureCollection} points to be clustered * @param {number} maxDistance Maximum Distance between any point of the cluster to generate the clusters (kilometers only) * @param {Object} [options={}] Optional parameters * @param {string} [options.units="kilometers"] in which `maxDistance` is expressed, can be degrees, radians, miles, or kilometers * @param {boolean} [options.mutate=false] Allows GeoJSON input to be mutated * @param {number} [options.minPoints=3] Minimum number of points to generate a single cluster, * points which do not meet this requirement will be classified as an 'edge' or 'noise'. * @returns {FeatureCollection} Clustered Points with an additional two properties associated to each Feature: * - {number} cluster - the associated clusterId * - {string} dbscan - type of point it has been classified as ('core'|'edge'|'noise') * @example * // create random points with random z-values in their properties * var points = turf.randomPoint(100, {bbox: [0, 30, 20, 50]}); * var maxDistance = 100; * var clustered = turf.clustersDbscan(points, maxDistance); * * //addToMap * var addToMap = [clustered]; */ function clustersDbscan(points, maxDistance, options) { // Input validation being handled by Typescript // collectionOf(points, 'Point', 'points must consist of a FeatureCollection of only Points'); // if (maxDistance === null || maxDistance === undefined) throw new Error('maxDistance is required'); // if (!(Math.sign(maxDistance) > 0)) throw new Error('maxDistance is invalid'); // if (!(minPoints === undefined || minPoints === null || Math.sign(minPoints) > 0)) throw new Error('options.minPoints is invalid'); if (options === void 0) { options = {}; } // Clone points to prevent any mutations if (options.mutate !== true) points = clone_1.default(points); // Defaults options.minPoints = options.minPoints || 3; // create clustered ids var dbscan = new density_clustering_1.default.DBSCAN(); var clusteredIds = dbscan.run(meta_1.coordAll(points), helpers_1.convertLength(maxDistance, options.units), options.minPoints, distance_1.default); // Tag points to Clusters ID var clusterId = -1; clusteredIds.forEach(function (clusterIds) { clusterId++; // assign cluster ids to input points clusterIds.forEach(function (idx) { var clusterPoint = points.features[idx]; if (!clusterPoint.properties) clusterPoint.properties = {}; clusterPoint.properties.cluster = clusterId; clusterPoint.properties.dbscan = "core"; }); }); // handle noise points, if any // edges points are tagged by DBSCAN as both 'noise' and 'cluster' as they can "reach" less than 'minPoints' number of points dbscan.noise.forEach(function (noiseId) { var noisePoint = points.features[noiseId]; if (!noisePoint.properties) noisePoint.properties = {}; if (noisePoint.properties.cluster) noisePoint.properties.dbscan = "edge"; else noisePoint.properties.dbscan = "noise"; }); return points; } exports.default = clustersDbscan;