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); } }); } }