public static GeographyPoint AsGeographyPoint(this LapDataPointViewModel point)
 {
     if (!point.Latitude.HasValue || !point.Longitude.HasValue)
     {
         return(null);
     }
     return(GeographyPoint.Create(point.Latitude.Value, point.Longitude.Value, null, point.Heading));
 }
Пример #2
0
        public static Dictionary <int, TrackSectorViewModel> ToTrackSectors(this IList <LapDataPointViewModel> lapDataPoints)
        {
            if (lapDataPoints.Count() < 3)
            {
                return(new Dictionary <int, TrackSectorViewModel>());
            }
            int lastDataPointInFirstSectorIndex               = (lapDataPoints.Count() / 3) - 1;
            int lastDataPointInSecondSectorIndex              = lastDataPointInFirstSectorIndex * 2;
            LapDataPointViewModel lastDataPointInFirstSector  = null;
            LapDataPointViewModel lastDataPointInSecondSector = null;
            LapDataPointViewModel lastDataPointInFinalSector  = null;

            for (int i = 0 + 1; i < lapDataPoints.Count(); i++)
            {
                var dataPoint = lapDataPoints[i];
                if (i <= lastDataPointInFirstSectorIndex)
                {
                    if (dataPoint.Latitude.HasValue && dataPoint.Longitude.HasValue && dataPoint.Heading.HasValue)
                    {
                        lastDataPointInFirstSector = dataPoint;
                    }
                }
                else if (i <= lastDataPointInSecondSectorIndex)
                {
                    if (dataPoint.Latitude.HasValue && dataPoint.Longitude.HasValue && dataPoint.Heading.HasValue)
                    {
                        lastDataPointInSecondSector = dataPoint;
                    }
                    dataPoint.SectorNumber = 2;
                }
                else
                {
                    if (dataPoint.Latitude.HasValue && dataPoint.Longitude.HasValue && dataPoint.Heading.HasValue)
                    {
                        lastDataPointInFinalSector = dataPoint;
                    }
                    dataPoint.SectorNumber = 3;
                }
            }

            if (lastDataPointInFirstSector == null || lastDataPointInSecondSector == null || lastDataPointInFinalSector == null)
            {
                return(new Dictionary <int, TrackSectorViewModel>());
            }

            var firstSector  = lastDataPointInFirstSector.ToTrackSector(1);
            var secondSector = lastDataPointInSecondSector.ToTrackSector(2);
            var finalSector  = lastDataPointInFinalSector.ToTrackSector(3, true);

            return(new Dictionary <int, TrackSectorViewModel> {
                { 1, firstSector }, { 2, secondSector }, { 3, finalSector }
            });
        }
Пример #3
0
        private bool PositionCompletesSector(LapDataPointViewModel currentPosition, LapDataPointViewModel previousPosition, TrackSectorViewModel sector)
        {
            if (previousPosition == null)
            {
                return(false);
            }
            if (currentPosition.Heading.HasValue && !HeadingIsWithinRange(sector.Heading, currentPosition.Heading.Value, 170d))
            {
                return(false);
            }

            return(sector.IsCompletedByLapDataPoints(previousPosition, currentPosition));
        }
Пример #4
0
        public static BestLap AsModel(this BestLapViewModel lapViewModel, VehicleViewModel vehicle, bool isMetricUnits, bool isPublic, string userName, long trackId, IEnumerable <TrackSectorViewModel> sectors)
        {
            DateTimeOffset utcLapTimestamp  = lapViewModel.Timestamp.ToUniversalTime();
            var            hashAlgorithm    = new HMACSHA256(Convert.FromBase64String(Constants.APP_LAPVERIFICATION_HASHKEY));
            string         verificationText = string.Format("{0}{1}{2}{3}{4}{5}", Constants.APP_LAPVERIFICATION_PREFIX, trackId, vehicle.Key.ToString().ToUpperInvariant(), lapViewModel.LapTime.Ticks, utcLapTimestamp.ToString("yyyy-MM-ddTHH:mm:ss.fffK"), userName);
            string         verificationCode = Convert.ToBase64String(hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(verificationText)));

            var model = new BestLap
            {
                LapTimeTicks       = lapViewModel.LapTime.Ticks,
                Timestamp          = utcLapTimestamp,
                VehicleId          = vehicle.Id,
                VehicleClass       = (int)vehicle.Class,
                Vehicle            = vehicle.AsModel(),
                VerificationCode   = verificationCode,
                TrackId            = trackId,
                IsPublic           = isPublic,
                UserName           = userName,
                IsUnofficial       = lapViewModel.IsUnofficial,
                GpsDeviceName      = lapViewModel.GpsDeviceName,
                WeatherCondition   = lapViewModel.WeatherCondition,
                AmbientTemperature = lapViewModel.AmbientTemperature
            };

            var dataPoints           = new List <LapDataPoint>();
            var geographicDataPoints = lapViewModel.DataPoints.Where(dp => dp != null && dp.Latitude.HasValue && dp.Longitude.HasValue);

            foreach (var sector in sectors)
            {
                LapDataPointViewModel lastDataPoint     = null;
                LapDataPointViewModel previousDataPoint = null;
                foreach (var dataPoint in geographicDataPoints.SkipWhile(dp => dp.SectorNumber != sector.SectorNumber))
                {
                    if (dataPoint.SectorNumber != sector.SectorNumber)
                    {
                        break;
                    }
                    lastDataPoint = dataPoint;
                    if (previousDataPoint != null && (dataPoint.ElapsedTime - previousDataPoint.ElapsedTime < TimeSpan.FromMilliseconds(900d)))
                    {
                        continue;
                    }
                    previousDataPoint = dataPoint;
                    dataPoints.Add(dataPoint.AsBestLapDataPoint(isMetricUnits));
                }
                // Last data point used for split times
                dataPoints.Add(lastDataPoint.AsBestLapDataPoint(isMetricUnits));
            }
            model.DataPoints = dataPoints;
            return(model);
        }
Пример #5
0
 private string ComputeRelativeGforce(LapDataPointViewModel dataPoint, DeviceOrientation deviceOrientation)
 {
     if (deviceOrientation == DeviceOrientation.Landscape || deviceOrientation == DeviceOrientation.LandscapeLeft || deviceOrientation == DeviceOrientation.LandscapeRight)
     {
         return(dataPoint.AccelerationY.HasValue
                 ? Math.Round(dataPoint.AccelerationY.Value, 2).ToString(CultureInfo.CurrentCulture)
                 : "?");
     }
     else
     {
         return(dataPoint.AccelerationY.HasValue
                 ? Math.Round(dataPoint.AccelerationX.Value, 2).ToString(CultureInfo.CurrentCulture)
                 : "?");
     }
 }
Пример #6
0
        public static TrackSectorViewModel ToTrackSector(this LapDataPointViewModel lapDataPoint, int sectorNumber, bool isFinishLine = false)
        {
            var sectorPoint = GeographyPoint.Create(lapDataPoint.Latitude.Value, lapDataPoint.Longitude.Value, lapDataPoint.Altitude, lapDataPoint.Heading);
            var sectorLine  = sectorPoint.ConvertToLine(0.03);

            return(new TrackSectorViewModel
            {
                SectorNumber = sectorNumber,
                StartLatitude = sectorLine[0].Latitude,
                StartLongitude = sectorLine[0].Longitude,
                EndLatitude = sectorLine[1].Latitude,
                EndLongitude = sectorLine[1].Longitude,
                Heading = lapDataPoint.Heading.Value,
                IsFinishLine = isFinishLine
            });
        }
Пример #7
0
        private void EndSector(LapDataPointViewModel sectorEndDataPoint, int currentSectorNumber)
        {
            if (SectorComplete == null)
            {
                return;
            }

            var lap = new LapViewModel
            {
                StartElapsedTime = lapStartElapsedTime,
                EndElapsedTime   = sectorEndDataPoint.ElapsedTime,
                MaximumSpeed     = sectorEndDataPoint.Speed ?? 0,
                Timestamp        = sectorEndDataPoint.Timestamp,
                SectorNumber     = currentSectorNumber,
                IsComplete       = false
            };

            SectorComplete(this, new LapEventArgs(lap));
        }
Пример #8
0
 public static LapDataPoint AsLapDataPoint(this LapDataPointViewModel lapDataPointViewModel, bool isMetricUnits)
 {
     return(new LapDataPoint
     {
         AccelerationX = lapDataPointViewModel.AccelerationX,
         AccelerationY = lapDataPointViewModel.AccelerationY,
         AccelerationZ = lapDataPointViewModel.AccelerationZ,
         Altitude = lapDataPointViewModel.Altitude,
         ElapsedTimeTicks = lapDataPointViewModel.ElapsedTime.Ticks,
         Heading = lapDataPointViewModel.Heading,
         IsEndOfLap = lapDataPointViewModel.IsEndOfLap,
         Latitude = lapDataPointViewModel.Latitude,
         Longitude = lapDataPointViewModel.Longitude,
         SectorNumber = lapDataPointViewModel.SectorNumber,
         Speed = isMetricUnits
                     ? lapDataPointViewModel.Speed / Constants.KMH_TO_METRES_PER_SECOND
                     : lapDataPointViewModel.Speed / Constants.KMH_TO_METRES_PER_SECOND * Constants.MILES_TO_KILOMETRES,
         Timestamp = lapDataPointViewModel.Timestamp.ToUniversalTime()
     });
 }
Пример #9
0
        private void accelerometer_ReadingChanged(Accelerometer sender, AccelerometerReadingChangedEventArgs args)
        {
            if (!isTiming)
            {
                return;
            }

            var elapsedTime            = stopwatch.Elapsed;
            var accelerometerDataPoint = new LapDataPointViewModel
            {
                ElapsedTime   = elapsedTime,
                AccelerationX = args.Reading.AccelerationX,
                AccelerationY = args.Reading.AccelerationY,
                AccelerationZ = args.Reading.AccelerationZ,
                SectorNumber  = currentSector != null ? currentSector.SectorNumber : 1,
                Timestamp     = args.Reading.Timestamp.ToLocalTime()
            };

            lapCoordinates.Add(accelerometerDataPoint);
        }
Пример #10
0
        private void EndLap(LapDataPointViewModel lapEndDataPoint, int lapEndDataPointIndex, int currentSectorNumber)
        {
            if (!lapCoordinates.Any(lc => lc.Latitude.HasValue && lc.Longitude.HasValue && lc.Heading.HasValue))
            {
                return;
            }

            var lapStartTime = lapStartElapsedTime;
            var lapTime      = lapStartElapsedTime = lapEndDataPoint.ElapsedTime;

            lapEndDataPoint.IsEndOfLap = true;
            int currentLapEndCoordinate = lapEndDataPointIndex;
            var allCoordinates          = lapCoordinates.ToList();
            var currentLapCoordinates   = allCoordinates.Skip(previousLapEndCoordinateIndex + 1).Take(currentLapEndCoordinate - previousLapEndCoordinateIndex).ToList();

            if (!currentLapCoordinates.Any())
            {
                return;
            }
            var lap = new LapViewModel
            {
                StartElapsedTime = lapStartTime,
                EndElapsedTime   = lapTime,
                MaximumSpeed     = currentLapCoordinates.Max(c => c.Speed) ?? 0,
                Timestamp        = currentLapCoordinates.First().Timestamp,
                SectorNumber     = currentSectorNumber,
                LapNumber        = lapNumber,
                IsComplete       = true
            };

            previousLapEndCoordinateIndex = currentLapEndCoordinate;

            if (generateSectors)
            {
                if (currentLapCoordinates.Count() < 3)
                {
                    return;
                }
                lap.DataPoints = new ObservableCollection <LapDataPointViewModel>(currentLapCoordinates);

                var sectors = currentLapCoordinates.ToTrackSectors();
                if (sectors.Count < 3)
                {
                    return;
                }

                currentSector     = sectors[1];
                sectorCoordinates = sectors.Select(s => s.Value);
                lastSectorNumber  = 3;
            }
            else
            {
                // Some accelerometer readings in this lap but received before currentSector changed will have the last SectorNumber
                // so change them to be the first sector
                foreach (var coordinate in currentLapCoordinates)
                {
                    if (coordinate.SectorNumber == 1)
                    {
                        break;
                    }

                    coordinate.SectorNumber = 1;
                }
                lap.DataPoints = new ObservableCollection <LapDataPointViewModel>(currentLapCoordinates);
            }

            lapNumber++;
            if (LapComplete != null)
            {
                LapComplete(this, new LapEventArgs(lap));
            }
        }
Пример #11
0
        private void geolocator_PositionChanged(IGeolocator sender, PositionChangedEventArgs args)
        {
            if (!isTiming && isFirstReading)
            {
                // Speed threshold for starting timing - 4m/s
#if DEBUG
                if (!args.Position.Speed.HasValue || double.IsNaN(args.Position.Speed.Value) || args.Position.Speed.Value < 0.5d)
                {
                    return;
                }
#else
                if (!args.Position.Speed.HasValue || double.IsNaN(args.Position.Speed.Value) || args.Position.Speed.Value < 4d)
                {
                    return;
                }
#endif
                isFirstReading = false;
                stopwatch.Start();
                isTiming = true;
                if (TimingStarted != null)
                {
                    TimingStarted(this, null);
                }
            }

            var    elapsedTime      = stopwatch.Elapsed;
            double?speedInUserUnits = args.Position.Speed.HasValue && !double.IsNaN(args.Position.Speed.Value)
                                        ? isMetricUnits
                                            ? args.Position.Speed * Constants.KMH_TO_METRES_PER_SECOND
                                            : args.Position.Speed * Constants.KMH_TO_METRES_PER_SECOND / Constants.MILES_TO_KILOMETRES
                                        : null;

            var newGeographicLapDataPoint = new LapDataPointViewModel
            {
                ElapsedTime  = elapsedTime,
                Altitude     = args.Position.Altitude.HasValue && !double.IsNaN(args.Position.Altitude.Value) ?  args.Position.Altitude : null,
                Heading      = args.Position.Heading.HasValue && !double.IsNaN(args.Position.Heading.Value) ?  args.Position.Heading : null,
                Latitude     = args.Position.Latitude,
                Longitude    = args.Position.Longitude,
                Speed        = speedInUserUnits,
                SectorNumber = currentSector != null ? currentSector.SectorNumber : 1,
                Timestamp    = args.Position.Timestamp.ToLocalTime()
            };
            lapCoordinates.Add(newGeographicLapDataPoint);
            // Assuming this adding to the end of the list for performance
            int possibleLapEndGeographicLapDataPointIndex = lastGeographicLapDataPointIndex;
            lastGeographicLapDataPointIndex = lapCoordinates.Count - 1;

            bool skipCurrentCoordinate = false;
            if (generateSectors && currentSector == null)
            {
                skipCurrentCoordinate = true;
                if (newGeographicLapDataPoint.Heading.HasValue)
                {
                    double?lastHeading = headingNormalisation.Any() ? headingNormalisation.Last() : (double?)null;
                    headingNormalisation.Add(newGeographicLapDataPoint.Heading.Value);
                    if (lastHeading.HasValue && HeadingIsWithinRange(lastHeading.Value, newGeographicLapDataPoint.Heading.Value, 30d))
                    {
                        currentSector = newGeographicLapDataPoint.ToTrackSector(1, true);
                    }
                }
            }

            if (skipCurrentCoordinate)
            {
                return;
            }

            int  currentSectorNumber         = currentSector.SectorNumber;
            bool sectorCompleted             = PositionCompletesSector(newGeographicLapDataPoint, lastGeographicLapDataPoint, currentSector);
            var  possibleSectorEndCoordinate = lastGeographicLapDataPoint;
            lastGeographicLapDataPoint = newGeographicLapDataPoint;
            bool lapComplete = false;
            if (sectorCompleted)
            {
                lapComplete = currentSector.IsFinishLine;
                int nextSectorNumber = currentSectorNumber + 1;
                if (nextSectorNumber > lastSectorNumber)
                {
                    nextSectorNumber = firstSectorNumber;
                }

                // Fix the sector number for the current point, i.e. Move it over into the next sector and possibleSectorEndCoordinate is the end of the last sector
                newGeographicLapDataPoint.SectorNumber = nextSectorNumber;

                if (sectorCoordinates.Any())
                {
                    currentSector = sectorCoordinates.Single(s => s.SectorNumber == nextSectorNumber);
                }
                else
                {
                    lapComplete = true;
                }

                if (!lapComplete)
                {
                    EndSector(possibleSectorEndCoordinate, currentSectorNumber);
                }
            }
            if (lapComplete)
            {
                EndLap(possibleSectorEndCoordinate, possibleLapEndGeographicLapDataPointIndex, currentSectorNumber);
            }
        }
Пример #12
0
        public static bool IsCompletedByLapDataPoints(this TrackSectorViewModel sector, LapDataPointViewModel startPoint, LapDataPointViewModel endPoint)
        {
            var sectorLineStartPoint = GeoUtmConverter.ToUTM(sector.StartLatitude, sector.StartLongitude);
            var sectorLineEndPoint   = GeoUtmConverter.ToUTM(sector.EndLatitude, sector.EndLongitude);
            var dataLineStartPoint   = GeoUtmConverter.ToUTM(startPoint.Latitude.Value, startPoint.Longitude.Value);
            var dataLineEndPoint     = GeoUtmConverter.ToUTM(endPoint.Latitude.Value, endPoint.Longitude.Value);

            return(IsIntersect(sectorLineStartPoint, sectorLineEndPoint, dataLineStartPoint, dataLineEndPoint));
        }
Пример #13
0
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            string navigationParameter;

            if (!NavigationContext.QueryString.TryGetValue(AppConstants.NAVIGATIONPARAMETER_TYPE, out navigationParameter))
            {
                navigationParameter = null;
            }

            if (App.ViewModel.Settings.IsTrial)
            {
                adRotatorControl.Visibility = System.Windows.Visibility.Visible;
                adDefaultControl.Visibility = System.Windows.Visibility.Visible;
            }
            else
            {
                adRotatorControl.Visibility = System.Windows.Visibility.Collapsed;
                adDefaultControl.Visibility = System.Windows.Visibility.Collapsed;
            }

            var    lapViewModel          = App.ViewModel.Track.History.SelectedSession.SelectedLap;
            string lapMinimumLengthBlurb = string.Format("* {0} {1} {2}",
                                                         AppResources.Text_Blurb_OfficialLength,
                                                         Math.Round(App.ViewModel.Track.Length * Constants.APP_MINIMUMLAPLENGTH_FACTOR, 3),
                                                         App.ViewModel.Settings.IsMetricUnits
                                                         ? AppResources.Text_Unit_Kilometres
                                                         : AppResources.Text_Unit_Miles);

            var lapDetailViewModel = new LapDetailViewModel
            {
                Lap                     = lapViewModel,
                SpeedUnitText           = App.ViewModel.SpeedUnitText,
                LengthUnitText          = App.ViewModel.LengthUnitText,
                ProjectedLength         = lapViewModel.ProjectedLength(),
                SessionWeather          = App.ViewModel.Track.History.SelectedSession.Weather,
                GpsDeviceName           = App.ViewModel.Track.History.SelectedSession.GpsDeviceName,
                LapMinimumLengthBlurb   = lapMinimumLengthBlurb,
                IsOfficial              = null,
                LapBelongsToCurrentUser = true,
                IsLeaderboardTime       = string.Equals(navigationParameter, AppConstants.NAVIGATIONPARAMETER_VALUE_LEADERBOARD)
            };

            var bestLapViewModel = lapViewModel as BestLapViewModel;

            if (bestLapViewModel != null)
            {
                lapDetailViewModel.UserDisplayName         = bestLapViewModel.UserDisplayName;
                lapDetailViewModel.GpsDeviceName           = bestLapViewModel.GpsDeviceName;
                lapDetailViewModel.IsOfficial              = !bestLapViewModel.IsUnofficial;
                lapDetailViewModel.LapBelongsToCurrentUser = App.ViewModel.IsAuthenticated && bestLapViewModel.UserName == App.MobileService.CurrentUser.UserId;
            }

            DataContext = lapDetailViewModel;
            var sectorSplitsAhead = new Dictionary <int, bool>();

            foreach (var sector in App.ViewModel.Track.Sectors)
            {
                var sectorSplitDataPoint = lapDetailViewModel.Lap.DataPoints.Where(dp => dp.Latitude.HasValue && dp.Longitude.HasValue).SkipWhile(dp => dp.SectorNumber != sector.SectorNumber).TakeWhile(dp => dp.SectorNumber == sector.SectorNumber).LastOrDefault();
                if (!sector.IsFinishLine && sectorSplitDataPoint == null)
                {
                    sectorSplitsAhead.Add(sector.SectorNumber, false);
                    continue;
                }

                var sectorSplit = sector.IsFinishLine
                                    ? lapDetailViewModel.Lap.LapTime
                                    : sectorSplitDataPoint.ElapsedTime - lapDetailViewModel.Lap.StartElapsedTime;

                TimeSpan bestSplit;
                if (sector.IsFinishLine)
                {
                    bestSplit = App.ViewModel.Track.BestLap != null && App.ViewModel.Track.BestLap.DataPoints != null
                                ? App.ViewModel.Track.BestLap.LapTime
                                : sectorSplit;
                }
                else
                {
                    var bestSplitDataPoint = App.ViewModel.Track.BestLap != null && App.ViewModel.Track.BestLap.DataPoints != null
                                                ? App.ViewModel.Track.BestLap.DataPoints.Where(dp => dp.Latitude.HasValue && dp.Longitude.HasValue).SkipWhile(dp => dp.SectorNumber != sector.SectorNumber).TakeWhile(dp => dp.SectorNumber == sector.SectorNumber).LastOrDefault()
                                                : null;

                    bestSplit = bestSplitDataPoint != null
                                ? bestSplitDataPoint.ElapsedTime - App.ViewModel.Track.BestLap.StartElapsedTime
                                : sectorSplit;
                }

                var splitStatus = sectorSplit - bestSplit;
                lapDetailViewModel.SectorSplits.Add(Tuple.Create(sector.SectorNumber,
                                                                 sectorSplit,
                                                                 splitStatus));
                sectorSplitsAhead.Add(sector.SectorNumber, splitStatus <= TimeSpan.Zero);
            }

            mapCircuit.Center = new GeoCoordinate(App.ViewModel.Track.Latitude, App.ViewModel.Track.Longitude);
            mapCircuit.SetZoomLevelForTrack(App.ViewModel.Track.LengthInMetres());

            TrackSectorViewModel finishLineSector = null;

            foreach (var sector in App.ViewModel.Track.Sectors)
            {
                if (sector.IsFinishLine)
                {
                    finishLineSector = sector;
                }

                var sectorLine = new MapPolyline();
                sectorLine.StrokeColor = sector.IsFinishLine ? Colors.Orange : Colors.Black;
                sectorLine.Path.Add(new GeoCoordinate(sector.StartLatitude, sector.StartLongitude));
                sectorLine.Path.Add(new GeoCoordinate(sector.EndLatitude, sector.EndLongitude));
                mapCircuit.MapElements.Add(sectorLine);
            }

            LapDataPointViewModel previousGeographicDataPoint = App.ViewModel.Track.History.SelectedSession.SelectedLap.DataPoints.FirstOrDefault(dp => dp.Latitude != null && dp.Longitude != null);

            if (previousGeographicDataPoint == null)
            {
                return;
            }

            var finishLineMidPoint = finishLineSector == null
                                        ? null
                                        : GeoUtmConverter.Midpoint(GeographyPoint.Create(finishLineSector.StartLatitude, finishLineSector.StartLongitude),
                                                                   GeographyPoint.Create(finishLineSector.EndLatitude, finishLineSector.EndLongitude),
                                                                   finishLineSector.Heading);

            if (finishLineMidPoint != null && App.ViewModel.Track.History.SelectedSession.SelectedLap.IsComplete)
            {
                AddCoordinatesAsPathLineToMap(previousGeographicDataPoint.SectorNumber, sectorSplitsAhead, finishLineMidPoint, previousGeographicDataPoint.AsGeographyPoint());
            }

            LapDataPointViewModel previousAccelerometerDataPoint = null;
            LapDataPointViewModel maxSpeedDataPoint = null;
            double?minSpeed           = null;
            var    decelerationSpeeds = new List <Tuple <double?, TimeSpan> >();
            var    mapLineAheadColor  = new SolidColorBrush(System.Windows.Media.Colors.Green);
            var    mapLineBehindColor = new SolidColorBrush(System.Windows.Media.Colors.Red);

            foreach (var dataPoint in App.ViewModel.Track.History.SelectedSession.SelectedLap.DataPoints)
            {
                if (!dataPoint.Latitude.HasValue || !dataPoint.Longitude.HasValue)
                {
                    if (dataPoint.AccelerationX.HasValue || dataPoint.AccelerationY.HasValue || dataPoint.AccelerationZ.HasValue)
                    {
                        previousAccelerometerDataPoint = dataPoint;
                    }
                    continue;
                }

                if (maxSpeedDataPoint == null || dataPoint.Speed > maxSpeedDataPoint.Speed)
                {
                    maxSpeedDataPoint = dataPoint;
                }

                if (!minSpeed.HasValue || minSpeed < dataPoint.Speed)
                {
                    if (decelerationSpeeds.Count > 1 && decelerationSpeeds.Sum(ds => ds.Item2.TotalSeconds) > 0.5)
                    {
                        // Create a pushpin for maximum speed before braking
                        if (maxSpeedDataPoint != null)
                        {
                            string pushpinContent = string.Format("{0}{1}",
                                                                  Math.Round(maxSpeedDataPoint.Speed.Value).ToString(CultureInfo.CurrentCulture),
                                                                  App.ViewModel.Settings.IsMetricUnits
                                                                        ? AppResources.Text_Unit_MetricSpeed
                                                                        : AppResources.Text_Unit_ImperialSpeed);
                            var pushpinData = Tuple.Create(new GeoCoordinate(maxSpeedDataPoint.Latitude.Value, maxSpeedDataPoint.Longitude.Value), pushpinContent);
                            apexDataLayer.Add(pushpinData);

                            maxSpeedDataPoint = null;
                        }

                        double decelerationTime = decelerationSpeeds.Sum(ds => ds.Item2.TotalSeconds);
                        double?totalSpeedLost   = decelerationSpeeds.First().Item1 - decelerationSpeeds.Last().Item1;
                        if (totalSpeedLost.HasValue && (totalSpeedLost.Value / decelerationTime) > 3)
                        {
                            // Create a pushpin for minimum speed through corner and the nearest g-force reading (if available)
                            string pushpinContent;
                            if (previousAccelerometerDataPoint != null)
                            {
                                pushpinContent = string.Format("{0}{1} | {2}{3}",
                                                               Math.Round(minSpeed.Value).ToString(CultureInfo.CurrentCulture),
                                                               App.ViewModel.Settings.IsMetricUnits
                                                                    ? AppResources.Text_Unit_MetricSpeed
                                                                    : AppResources.Text_Unit_ImperialSpeed,
                                                               ComputeRelativeGforce(previousAccelerometerDataPoint, App.ViewModel.Track.History.SelectedSession.DeviceOrientation),
                                                               AppResources.Text_Unit_GForce);
                            }
                            else
                            {
                                pushpinContent = string.Format("{0}{1}",
                                                               Math.Round(minSpeed.Value).ToString(CultureInfo.CurrentCulture),
                                                               App.ViewModel.Settings.IsMetricUnits
                                                                    ? AppResources.Text_Unit_MetricSpeed
                                                                    : AppResources.Text_Unit_ImperialSpeed);
                            }
                            var pushpinData = Tuple.Create(new GeoCoordinate(previousGeographicDataPoint.Latitude.Value, previousGeographicDataPoint.Longitude.Value), pushpinContent);
                            apexDataLayer.Add(pushpinData);
                        }
                    }
                    decelerationSpeeds.Clear();
                    minSpeed = dataPoint.Speed;
                }
                else
                {
                    minSpeed = dataPoint.Speed;
                    decelerationSpeeds.Add(Tuple.Create(minSpeed, dataPoint.ElapsedTime - previousGeographicDataPoint.ElapsedTime));
                }
                AddCoordinatesAsPathLineToMap(dataPoint.SectorNumber, sectorSplitsAhead, previousGeographicDataPoint.AsGeographyPoint(), dataPoint.AsGeographyPoint());

                previousGeographicDataPoint = dataPoint;
            }

            if (finishLineMidPoint != null && App.ViewModel.Track.History.SelectedSession.SelectedLap.IsComplete)
            {
                AddCoordinatesAsPathLineToMap(previousGeographicDataPoint.SectorNumber, sectorSplitsAhead, previousGeographicDataPoint.AsGeographyPoint(), finishLineMidPoint);
            }

            base.OnNavigatedTo(e);
        }