public void OnAnimationUpdate(ValueAnimator animation) { float progress = transport.Progress + (transport.NextProgress - transport.Progress) * animation.AnimatedFraction; int index = transport.Step.Trajectory.TakeWhile(s => s.Index <= progress).Count(); bool last = index >= transport.Step.Trajectory.Length; TrajectoryStep from = transport.Step.Trajectory[index - 1]; TrajectoryStep to = last ? transport.TimeStep.Step.Trajectory.First() : transport.Step.Trajectory[index]; progress = (progress - from.Index) / ((last ? 1 : to.Index) - from.Index); LatLng position = new LatLng(from.Position.Latitude + (to.Position.Latitude - from.Position.Latitude) * progress, from.Position.Longitude + (to.Position.Longitude - from.Position.Longitude) * progress); positionUpdater(position); }
private void UpdatePositions() { TimeStep[] currentTimeSteps; DateTime now = DateTime.Now; // Take time steps snapshot lock (timeSteps) currentTimeSteps = timeSteps.ToArray(); // Sort time steps by step Dictionary <Step, TimeStep> steps = currentTimeSteps.GroupBy(s => s.Step) .ToDictionary(g => g.Key, g => g.OrderBy(s => s.Date).First()); // For each route, find transport positions List <Transport> transports = new List <Transport>(); foreach (Line line in TramUrWayApplication.Lines) { foreach (Route route in line.Routes) { for (int i = 0; i < route.Steps.Length - 1; i++) { Step step = route.Steps[i]; Step nextStep = route.Steps[i + 1]; TimeStep timeStep, nextTimeStep; if (!steps.TryGetValue(nextStep, out nextTimeStep)) { continue; } steps.TryGetValue(step, out timeStep); if (timeStep != null && timeStep.Date < nextTimeStep.Date) { continue; } TimeSpan diff = nextTimeStep.Date - now; TimeSpan duration = step.Duration ?? TimeSpan.Zero; if (duration == TimeSpan.Zero) { duration = TimeSpan.FromMinutes(2); } float progress = (float)(1 - Math.Min(diff.TotalMinutes, duration.TotalMinutes) / duration.TotalMinutes); if (progress < 0) { progress = 0; } if (progress > 1) { progress = 1; } float nextProgress = (float)(1 - Math.Min(diff.Subtract(TimeSpan.FromSeconds(1)).TotalMinutes, duration.TotalMinutes) / duration.TotalMinutes); if (nextProgress < 0) { nextProgress = 0; } if (nextProgress > 1) { nextProgress = 1; } Transport transport = new Transport() { Route = route, Step = step, TimeStep = nextTimeStep, Progress = step.Speed.Evaluate(progress), NextProgress = step.Speed.Evaluate(nextProgress) }; transports.Add(transport); } } } // Sort markers Dictionary <Step, Marker> stepMarkers = transportMarkers.ToDictionary(p => p.Key.Step, p => p.Value); List <Marker> unusedMarkers = stepMarkers.Where(p => !transports.Any(t => t.Step == p.Key)).Select(p => p.Value).ToList(); List <Transport> missingTransports = transports.Where(t => !stepMarkers.Keys.Contains(t.Step)).ToList(); Dictionary <Step, Marker> reusableMarkers = stepMarkers.Where(p => transports.Any(t => t.Step == p.Key) && !missingTransports.Any(t => t.Step == p.Key)).ToDictionary(); // Adjust markers diff while (unusedMarkers.Count > missingTransports.Count) { Marker marker = unusedMarkers.Last(); unusedMarkers.RemoveAt(unusedMarkers.Count - 1); Activity.RunOnUiThread(marker.Remove); } while (missingTransports.Count > unusedMarkers.Count) { ManualResetEvent resetEvent = new ManualResetEvent(false); Activity.RunOnUiThread(() => { MarkerOptions markerOptions = new MarkerOptions() .Anchor(0.5f, 0.5f) .SetPosition(new LatLng(0, 0)); Marker marker = googleMap.AddMarker(markerOptions); unusedMarkers.Add(marker); resetEvent.Set(); }); resetEvent.WaitOne(); } // Use existing markers to match current transports transportMarkers = reusableMarkers.Select(p => new KeyValuePair <Transport, Marker>(transports.First(t => t.Step == p.Key), p.Value)) .Concat(missingTransports.Zip(unusedMarkers, (t, m) => new KeyValuePair <Transport, Marker>(t, m))) .ToDictionary(); foreach (var pair in transportMarkers) { Transport transport = pair.Key; Marker marker = pair.Value; // Compute quick position Position quickFrom = transport.Step.Stop.Position; Position quickTo = transport.TimeStep.Step.Stop.Position; LatLng quickPosition = new LatLng(quickFrom.Latitude + (quickTo.Latitude - quickFrom.Latitude) * transport.Progress, quickFrom.Longitude + (quickTo.Longitude - quickFrom.Longitude) * transport.Progress); // Update marker Activity.RunOnUiThread(() => marker.SetIcon(lineIcons[transport.Route.Line])); bool visible; if (markersVisibility.TryGetValue(marker, out visible) && !visible) { continue; } // Use animation only if zoomed enough if (cameraPosition.Zoom >= AnimationZoomLimit) { ValueAnimator valueAnimator = new ValueAnimator(); valueAnimator.AddUpdateListener(new MarkerAnimator(Activity, marker, transport, p => SetMarkerPosition(transport, marker, p))); valueAnimator.SetFloatValues(0, 1); // Ignored. valueAnimator.SetInterpolator(new LinearInterpolator()); valueAnimator.SetDuration(1000); Activity.RunOnUiThread(valueAnimator.Start); } else { float progress = transport.Progress; int index = transport.Step.Trajectory.TakeWhile(s => s.Index <= progress).Count(); bool last = index >= transport.Step.Trajectory.Length; TrajectoryStep from = transport.Step.Trajectory[index - 1]; TrajectoryStep to = last ? transport.TimeStep.Step.Trajectory.First() : transport.Step.Trajectory[index]; progress = (progress - from.Index) / ((last ? 1 : to.Index) - from.Index); LatLng position = new LatLng(from.Position.Latitude + (to.Position.Latitude - from.Position.Latitude) * transport.Progress, from.Position.Longitude + (to.Position.Longitude - from.Position.Longitude) * transport.Progress); SetMarkerPosition(transport, marker, position); } // Follow selected transport Activity.RunOnUiThread(() => { if (marker.Id == selectedMarkerId) { googleMap.AnimateCamera(CameraUpdateFactory.NewLatLng(quickPosition)); } }); } }