index.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // index.ts
  2. import { clone } from "@turf/clone";
  3. import { distance } from "@turf/distance";
  4. import { degreesToRadians, lengthToDegrees } from "@turf/helpers";
  5. // lib/rbush-export.ts
  6. import lib from "rbush";
  7. var rbush = lib;
  8. // index.ts
  9. function clustersDbscan(points, maxDistance, options = {}) {
  10. if (options.mutate !== true) points = clone(points);
  11. const minPoints = options.minPoints || 3;
  12. const latDistanceInDegrees = lengthToDegrees(maxDistance, options.units);
  13. var tree = new rbush(points.features.length);
  14. var visited = points.features.map((_) => false);
  15. var assigned = points.features.map((_) => false);
  16. var isnoise = points.features.map((_) => false);
  17. var clusterIds = points.features.map((_) => -1);
  18. tree.load(
  19. points.features.map((point, index) => {
  20. var [x, y] = point.geometry.coordinates;
  21. return {
  22. minX: x,
  23. minY: y,
  24. maxX: x,
  25. maxY: y,
  26. index
  27. };
  28. })
  29. );
  30. const regionQuery = (index) => {
  31. const point = points.features[index];
  32. const [x, y] = point.geometry.coordinates;
  33. const minY = Math.max(y - latDistanceInDegrees, -90);
  34. const maxY = Math.min(y + latDistanceInDegrees, 90);
  35. const lonDistanceInDegrees = function() {
  36. if (minY < 0 && maxY > 0) {
  37. return latDistanceInDegrees;
  38. }
  39. if (Math.abs(minY) < Math.abs(maxY)) {
  40. return latDistanceInDegrees / Math.cos(degreesToRadians(maxY));
  41. } else {
  42. return latDistanceInDegrees / Math.cos(degreesToRadians(minY));
  43. }
  44. }();
  45. const minX = Math.max(x - lonDistanceInDegrees, -360);
  46. const maxX = Math.min(x + lonDistanceInDegrees, 360);
  47. const bbox = { minX, minY, maxX, maxY };
  48. return tree.search(bbox).filter(
  49. (neighbor) => {
  50. const neighborIndex = neighbor.index;
  51. const neighborPoint = points.features[neighborIndex];
  52. const distanceInKm = distance(point, neighborPoint, {
  53. units: "kilometers"
  54. });
  55. return distanceInKm <= maxDistance;
  56. }
  57. );
  58. };
  59. const expandCluster = (clusteredId, neighbors) => {
  60. for (var i = 0; i < neighbors.length; i++) {
  61. var neighbor = neighbors[i];
  62. const neighborIndex = neighbor.index;
  63. if (!visited[neighborIndex]) {
  64. visited[neighborIndex] = true;
  65. const nextNeighbors = regionQuery(neighborIndex);
  66. if (nextNeighbors.length >= minPoints) {
  67. neighbors.push(...nextNeighbors);
  68. }
  69. }
  70. if (!assigned[neighborIndex]) {
  71. assigned[neighborIndex] = true;
  72. clusterIds[neighborIndex] = clusteredId;
  73. }
  74. }
  75. };
  76. var nextClusteredId = 0;
  77. points.features.forEach((_, index) => {
  78. if (visited[index]) return;
  79. const neighbors = regionQuery(index);
  80. if (neighbors.length >= minPoints) {
  81. const clusteredId = nextClusteredId;
  82. nextClusteredId++;
  83. visited[index] = true;
  84. expandCluster(clusteredId, neighbors);
  85. } else {
  86. isnoise[index] = true;
  87. }
  88. });
  89. points.features.forEach((_, index) => {
  90. var clusterPoint = points.features[index];
  91. if (!clusterPoint.properties) {
  92. clusterPoint.properties = {};
  93. }
  94. if (clusterIds[index] >= 0) {
  95. clusterPoint.properties.dbscan = isnoise[index] ? "edge" : "core";
  96. clusterPoint.properties.cluster = clusterIds[index];
  97. } else {
  98. clusterPoint.properties.dbscan = "noise";
  99. }
  100. });
  101. return points;
  102. }
  103. var turf_clusters_dbscan_default = clustersDbscan;
  104. export {
  105. clustersDbscan,
  106. turf_clusters_dbscan_default as default
  107. };
  108. //# sourceMappingURL=index.js.map