private ClusteredLocation <T> GetClusterForThisSector(List <ClusteredLocationRect <T> > clusters, LocationRect sector)
        {
            foreach (var cluster in clusters)
            {
                if (cluster.LocationRect.Intersects(sector))
                {
                    return(cluster);
                }
            }

            var newCluster = new ClusteredLocationRect <T>(sector);

            clusters.Add(newCluster);

            return(newCluster);
        }
        private ClusteredLocation <T> GetClusterForThisPoint(List <ClusteredLocationRect <T> > clusters, Position location, double thresholdLat, double thresholdLon)
        {
            foreach (var cluster in clusters)
            {
                if (cluster.LocationRect.Contains(location))
                {
                    return(cluster);
                }
            }

            var clusterRect = new LocationRect();
            var nw          = new Position(this.EnsureLatitude(location.Latitude + thresholdLat), this.EnsureLongitude(location.Longitude - thresholdLon));
            var se          = new Position(this.EnsureLatitude(location.Latitude - thresholdLat), this.EnsureLongitude(location.Longitude + thresholdLon));

            clusterRect.Northwest = nw;
            clusterRect.Southeast = se;

            var newCluster = new ClusteredLocationRect <T>(clusterRect);

            clusters.Add(newCluster);

            return(newCluster);
        }
        private static void UpdateItemsSource(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var map = d as Map;
            var newPushPinCollection = e.NewValue as IEnumerable <IClusteredGeoObject>;

            if (newPushPinCollection != null && newPushPinCollection.Any() && map != null)
            {
                // Read dependency properties
                var pushpinTemplate        = GetPushpinTemplate(d);
                var clusterPushpinTemplate = GetClusterTemplate(d);
                ////var clusteringDistance = GetClusteringDistance(d);

                // Perform clustering
                var visibleAreaTopLeft     = map.ConvertViewportPointToGeoCoordinate(new Point(0, 0));
                var visibleAreaBottomRight = map.ConvertViewportPointToGeoCoordinate(new Point(map.ActualWidth, map.ActualHeight));
                var boundingRectangle      = LocationRect.CreateLocationRect(visibleAreaTopLeft.ToPosition(), visibleAreaBottomRight.ToPosition());

                //var clusterer = new SectorClusterer<IClusteredGeoObject>();
                var clusterer = new RectangularClusterer <IClusteredGeoObject>();

                //var stopwatch = new Stopwatch();
                //stopwatch.Start();
                IEnumerable <IClusteredLocation <IClusteredGeoObject> > clusteredLocations = clusterer.Cluster(newPushPinCollection, boundingRectangle, map.ZoomLevel);
                if (map.ZoomLevel >= 18)
                {
                    clusteredLocations = clusteredLocations.SelectMany(x => x.ClusteredItems).Select(
                        clusteredGeoObject =>
                    {
                        var rect = new ClusteredLocationRect <IClusteredGeoObject>(boundingRectangle);
                        rect.Add(clusteredGeoObject);
                        return(rect);
                    }).ToList();
                }

                //stopwatch.Stop();
                //Console.WriteLine("UPDATED CLUSTER IN {0}ms", stopwatch.ElapsedMilliseconds);

                // Update map on UI
                map.Dispatcher.BeginInvoke(
                    () =>
                {
                    // Remove layer which hosts pushpins
                    var clusteredPushpinLayer = map.Layers.FirstOrDefault(x => x.All(y => y.Content is IClusteredLocation <IClusteredGeoObject>));
                    map.Layers.Remove(clusteredPushpinLayer);

                    // Create new layer
                    var layer = new MapLayer();

                    // The descending-by-latitude ordering makes sure the pins are not overlapping eachother
                    Position lastGeoCoordinate = Position.Unknown;
                    foreach (var pushPinModel in clusteredLocations.OrderByDescending(p => p.Location.Latitude))
                    {
                        var mapOverlay = new MapOverlay
                        {
                            GeoCoordinate = new GeoCoordinate(pushPinModel.Location.Latitude, pushPinModel.Location.Longitude),
                            Content       = pushPinModel
                        };

                        if (pushPinModel.IsClustered)
                        {
                            mapOverlay.ContentTemplate = clusterPushpinTemplate;
                            mapOverlay.PositionOrigin  = new System.Windows.Point(0.5, 0.5);
                        }
                        else
                        {
                            mapOverlay.ContentTemplate = pushpinTemplate;

                            // In case two/more pins have the same GPS position, we add a random offset to longitude/latitude.
                            if (Math.Abs(pushPinModel.Location.Latitude - lastGeoCoordinate.Latitude) < 0.00005 &&
                                Math.Abs(pushPinModel.Location.Longitude - lastGeoCoordinate.Longitude) < 0.00005)
                            {
                                mapOverlay.GeoCoordinate = lastGeoCoordinate.WithRandomOffset().ToGeoCoordinate();
                            }
                            lastGeoCoordinate = pushPinModel.Location;
                        }

                        layer.Add(mapOverlay);
                    }

                    // Finally, add all items (pushpins, clusters and current location) to the map.
                    if (layer.Any())
                    {
                        map.Layers.Insert(ItemsSourceLayerIndex, layer);
                    }
                });
            }
        }