public ClusterGenerator(MapControl map, ControlTemplate itemTemplate) { _map = map; ItemTemplate = itemTemplate; GenerateMapItems(); _contractService = SimpleIoc.Default.GetInstance <IContractService>(); _contractService.ContractRefreshed += OnContractRefreshed; _contractService.StationRefreshed += OnStationRefreshed; // maps event var mapObserver = Observable.FromEventPattern(map, "CenterChanged"); mapObserver .Do((e) => { cts.Cancel(); cts = new CancellationTokenSource(); }) .Throttle(throttleTime) .Select(async x => { var stations = SimpleIoc.Default.GetInstance <IContractService>().GetStations(); // some services can provide wrong values in lat or lon... just take care of it foreach (var station in stations.Where(s => s.Location == null)) { station.Location = new Geopoint(new BasicGeoposition { Latitude = station.Latitude, Longitude = station.Longitude }); } await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { //mapArea = _map.GetViewArea(); mapLocations = _map.GetViewLocations(); if (mapLocations != null) { leftCornerLocation = mapLocations.First().Position; } zoomLevel = _map.ZoomLevel; // TESTING only //velibControls[0].SetValue(MapControl.LocationProperty, new Geopoint(new BasicGeoposition() // { // Latitude = mapArea.NorthwestCorner.Latitude, // Longitude = mapArea.NorthwestCorner.Longitude, // })); //velibControls[0].ShowVelibStation(); //velibControls[1].SetValue(MapControl.LocationProperty, new Geopoint(new BasicGeoposition() //{ // Latitude = mapArea.SoutheastCorner.Latitude, // Longitude = mapArea.SoutheastCorner.Longitude, //})); //velibControls[1].ShowVelibStation(); }); //return null; // that could happend is the zoom is really low and the map is turned if (mapLocations == null) { return(null); } var collection = new AddRemoveCollection(); collection.ToRemove = Items.Where(t => !MapExtensions.Contains(mapLocations, t.Latitude, t.Longitude)).ToList(); collection.ToAdd = stations.Where(t => !Items.Contains(t) && MapExtensions.Contains(mapLocations, t.Latitude, t.Longitude)).Take(MAX_CONTROLS).ToList(); if (Items.Count > MAX_CONTROLS + collection.ToRemove.Count) { collection.ToAdd.Clear(); } // precalculate the items offset (that deffer well calculation) foreach (var velib in collection.ToAdd) { velib.GetOffsetLocation2(leftCornerLocation, zoomLevel); } return(collection); }) .Switch() .Subscribe(x => { if (x == null) { return; } RefreshView(x, cts.Token); }); }
private async void RefreshView(AddRemoveCollection addRemoveCollection, CancellationToken token) { if (token.IsCancellationRequested) { return; } // remove out of view items foreach (var velib in Items.Where(t => addRemoveCollection.ToRemove.Contains(t)).ToList()) { if (velib.Control != null) { ((StationControl)velib.Control).RemoveVelib(velib); } Items.Remove(velib); if (token.IsCancellationRequested) { return; } } if (zoomLevel > 15) { MAXDISTANCE = 1; } else { MAXDISTANCE = 100; } // refresh clusters by removing them from current cluster if required // and send them back to the ToAddPool to be retreated // (when zoom in) if (previousZoom < zoomLevel) { foreach (var control in StationControls.Where(t => t.Stations.Count > 1)) { foreach (var velib in control.Stations.ToList().Where(t => t.GetOffsetLocation2(leftCornerLocation, zoomLevel) .GetDistanceTo(control.GetOffsetLocation2(leftCornerLocation, zoomLevel)) > MAXDISTANCE).ToList()) { addRemoveCollection.ToAdd.Add(velib); Items.Remove(velib); control.RemoveVelib(velib); await Task.Delay(1); if (token.IsCancellationRequested) { return; } } } } // (when dezoom) // refresh clusters clustering clusters if (previousZoom > zoomLevel) { foreach (var alreadyAddedVelib in Items.ToList()) { if (alreadyAddedVelib.Control == null) { continue; } Point locationOffset = alreadyAddedVelib.GetOffsetLocation2(leftCornerLocation, zoomLevel); foreach (var alreadyAddedVelib2 in Items.Where(t => t.GetHashCode() != alreadyAddedVelib.GetHashCode()).ToList()) { if (alreadyAddedVelib2.Control == null) { continue; } var loc = alreadyAddedVelib2.GetOffsetLocation2(leftCornerLocation, zoomLevel); double distance = loc.GetDistanceTo(locationOffset); if (distance < MAXDISTANCE && alreadyAddedVelib.Control != alreadyAddedVelib2.Control) { // add the velib the ToAdd collection to be handled again addRemoveCollection.ToAdd.Add(alreadyAddedVelib2); Items.Remove(alreadyAddedVelib2); ((StationControl)alreadyAddedVelib2.Control).RemoveVelib(alreadyAddedVelib2); } if (token.IsCancellationRequested) { return; } } } } previousZoom = zoomLevel; foreach (var station in addRemoveCollection.ToAdd) { AddToCollection(station); if (token.IsCancellationRequested) { return; } } var list = StationControls.Where(c => c.NeedRefresh && c.Stations.Count == 0); foreach (var control in StationControls.Where(c => c.NeedRefresh && c.Stations.Count == 0)) { await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { control.Hide(); } ); } // finalise the ui cycle foreach (var control in StationControls.Where(c => c.NeedRefresh && c.Stations.Count != 0)) { await Task.Delay(TimeSpan.FromMilliseconds(25)); if (token.IsCancellationRequested) { break; } var stations = control.Stations; if (stations.Count == 1) { var station = stations.FirstOrDefault(); if (station != null) { if (station.Contract.StationRefreshGranularity) { if (!station.IsInRefreshPool) { _contractService.AddStationToRefreshingPool(station); } } } } var location = control.GetLocation(); await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => control.FinaliseUiCycle(dispatcher, location, token)); } foreach (var station in Items) { // reinit calculated location to refresh it next UI cycle station.OffsetLocation = null; var control = station.Control as StationControl; if (control != null) { control.Location = null; control.OffsetLocation.X = 0; } } }