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)); }
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 } }); }
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)); }
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); }
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) : "?"); } }
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 }); }
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)); }
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() }); }
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); }
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)); } }
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); } }
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)); }
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); }