public IList <MapPoint> RunCluster()
        {
            // Skip points outside the grid, not visible to user then skip those
            //IList<MapPoint> filtered = ClusterInfo.DoFilterData(this._jsonReceive.Zoomlevel) // TODO: by kit
            //    ? FilterUtil.FilterDataByViewport(this.points, Grid)
            //    : this.points;

            IList <MapPoint> filtered = this.points;

            // Put points in buckets
            foreach (var p in filtered)
            {
                var idxy = GetPointMappedIds(p, Grid, DeltaX, DeltaY);
                var idx  = idxy[0];
                var idy  = idxy[1];

                // Bucket id
                var id = GetId(idx, idy);

                // Bucket exists, add point
                if (BucketsLookup.ContainsKey(id))
                {
                    BucketsLookup[id].Points.Add(p);
                }
                // New bucket, create and add point
                else
                {
                    var bucket = new Bucket(idx, idy, id);
                    bucket.Points.Add(p);
                    BucketsLookup.Add(id, bucket);
                }
            }

            // Calculate centroid for all buckets
            SetCentroidForAllBuckets(BucketsLookup.Values);

            // Merge if gridpoint is to close
            if (GmcConfiguration.Get.DoMergeGridIfCentroidsAreCloseToEachOther)
            {
                MergeClustersGrid();
            }

            if (GmcConfiguration.Get.DoUpdateAllCentroidsToNearestContainingPoint)
            {
                UpdateAllCentroidsToNearestContainingPoint();
            }

            // Check again
            // Merge if gridpoint is to close
            if (GmcConfiguration.Get.DoMergeGridIfCentroidsAreCloseToEachOther &&
                GmcConfiguration.Get.DoUpdateAllCentroidsToNearestContainingPoint)
            {
                MergeClustersGrid();
                // And again set centroid to closest point in bucket
                UpdateAllCentroidsToNearestContainingPoint();
            }

            return(GetClusterResult(Grid));
        }
        public IPoints RunClusterAlgo(ClusterInfo clusterInfo)
        {
            // Skip points outside the grid
            IPoints filtered = clusterInfo.IsFilterData ? FilterDataset(Dataset, Grid) : Dataset;

            // Put points in buckets
            foreach (var p in filtered.Data)
            {
                var idxy = GetPointMappedIds(p, Grid, DeltaX, DeltaY);
                var idx  = idxy[0];
                var idy  = idxy[1];

                // Bucket id
                var id = GetId(idx, idy);

                // Bucket exists, add point
                if (BucketsLookup.ContainsKey(id))
                {
                    BucketsLookup[id].Points.Add(p);
                }
                // New bucket, create and add point
                else
                {
                    var bucket = new Bucket(idx, idy, id);
                    bucket.Points.Add(p);
                    BucketsLookup.Add(id, bucket);
                }
            }

            // Calculate centroid for all buckets
            SetCentroidForAllBuckets(BucketsLookup.Values);

            // Merge if gridpoint is to close
            if (AlgoConfig.DoMergeGridIfCentroidsAreCloseToEachOther)
            {
                MergeClustersGrid();
            }

            if (AlgoConfig.DoUpdateAllCentroidsToNearestContainingPoint)
            {
                UpdateAllCentroidsToNearestContainingPoint();
            }

            // Check again
            // Merge if gridpoint is to close
            if (AlgoConfig.DoMergeGridIfCentroidsAreCloseToEachOther &&
                AlgoConfig.DoUpdateAllCentroidsToNearestContainingPoint)
            {
                MergeClustersGrid();
                // And again set centroid to closest point in bucket
                UpdateAllCentroidsToNearestContainingPoint();
            }

            return(GetClusterResult(Grid));
        }
        void MergeClustersGridHelper(string currentKey, IEnumerable <string> neighborKeys)
        {
            double minDistX = DeltaX / GmcSettings.Get.MergeWithin;
            double minDistY = DeltaY / GmcSettings.Get.MergeWithin;
            // If clusters in grid are too close to each other, merge them
            double withinDist = Math.Max(minDistX, minDistY);

            foreach (var neighborKey in neighborKeys)
            {
                if (!BucketsLookup.ContainsKey(neighborKey))
                {
                    continue;
                }

                var neighbor = BucketsLookup[neighborKey];
                if (neighbor.IsUsed == false)
                {
                    continue;
                }

                var current = BucketsLookup[currentKey];
                var dist    = MathTool.Distance(current.Centroid, neighbor.Centroid);
                if (dist > withinDist)
                {
                    continue;
                }

                current.Points.AddRange(neighbor.Points);                 //O(n)

                // recalc centroid
                var centroidPoint = GetCentroidFromClusterLatLon(current.Points);
                current.Centroid = centroidPoint;
                neighbor.IsUsed  = false;                // merged, then not used anymore
                neighbor.Points.Clear();                 // clear mem
            }
        }