public IEnumerable <ActivityEntity> ImportActivitiesStream(Stream stream, IDictionary <string, ACTIVITY_SPORT> categoryMapping) { _Logger.Info("Importing data stream"); _CurrentContext = PARSE_CONTEXT.ROOT; XmlReaderSettings settings = new XmlReaderSettings { Async = false }; using (XmlReader reader = XmlReader.Create(stream, settings)) { // SAX parsing for performance while (reader.Read()) { switch (reader.NodeType) { case XmlNodeType.Element: switch (reader.Name) { case "gpx": _CurrentContext = PARSE_CONTEXT.GPX; _CurrentCreator = reader.GetAttribute("creator"); _CurrentActivityBuilder = new ActivityEntity.Builder { OriginSystem = _CurrentCreator }; while (reader.MoveToNextAttribute()) { if (reader.Name.StartsWith("xmlns:") && reader.Value.ToLower() == "http://www.garmin.com/xmlschemas/trackpointextension/v1") { _XmlNamespaces["gpxtpx"] = reader.Name.Replace("xmlns:", "") + ":"; } } break; case "metadata": _CurrentContext = PARSE_CONTEXT.METADATA; break; case "time": if (_CurrentContext == PARSE_CONTEXT.METADATA) { _CurrentContext = PARSE_CONTEXT.ACTIVITY_TIME; } else if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT_TIME; } break; case "trk": _CurrentContext = PARSE_CONTEXT.TRACK; _CurrentTrackBuilder = new TrackEntity.Builder(); break; case "name": if (_CurrentContext == PARSE_CONTEXT.TRACK) { _CurrentContext = PARSE_CONTEXT.TRACK_NAME; } break; case "trkseg": _CurrentContext = PARSE_CONTEXT.TRACK_SEGMENT; _CurrentTrackSegmentBuilder = new TrackSegmentEntity.Builder(); break; case "trkpt": _CurrentContext = PARSE_CONTEXT.TRACK_POINT; float latitude; var style = System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowLeadingSign; var culture = System.Globalization.CultureInfo.CreateSpecificCulture("en-US"); if (!float.TryParse(reader.GetAttribute("lat"), style, culture, out latitude)) { break; } float longitude; if (!float.TryParse(reader.GetAttribute("lon"), style, culture, out longitude)) { break; } _CurrentTrackPointBuilder = new TrackPointVO.Builder() { Latitude = latitude, Longitude = longitude }; break; case "type": if (_CurrentContext == PARSE_CONTEXT.TRACK) { _CurrentContext = PARSE_CONTEXT.TRACK_TYPE; } break; case "ele": if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT_ELEVATION; } break; case "extensions": if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT_EXTENSIONS; } break; case "power": if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT_EXTENSIONS) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT_POWER; } break; default: if (reader.Name == _XmlNamespaces["gpxtpx"] + "TrackPointExtension") { if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT_EXTENSIONS) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT_GPXTPX_EXTENSION; } } else if (reader.Name == _XmlNamespaces["gpxtpx"] + "atemp") { if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT_GPXTPX_EXTENSION) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT_TEMPERATURE; } } else if (reader.Name == _XmlNamespaces["gpxtpx"] + "hr") { if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT_GPXTPX_EXTENSION) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT_HR; } } else if (reader.Name == _XmlNamespaces["gpxtpx"] + "cad") { if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT_GPXTPX_EXTENSION) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT_CADENCE; } } break; } break; case XmlNodeType.Text: switch (_CurrentContext) { case PARSE_CONTEXT.ACTIVITY_TIME: DateTimeOffset activityStartTime; string activityStartTimeAsString = reader.Value; if (DateTimeOffset.TryParse(activityStartTimeAsString, out activityStartTime)) { _Logger.Debug(string.Format("Activity start time {0}", activityStartTime)); } else { _Logger.Error(string.Format("Error parsing date {0}", activityStartTimeAsString)); } if (_CurrentActivityBuilder != null) { _CurrentActivityBuilder.Time = activityStartTime; if (!string.IsNullOrEmpty(activityStartTimeAsString)) { _CurrentActivityBuilder.OriginId = $"{_CurrentCreator ?? ""}{activityStartTimeAsString}"; } } break; case PARSE_CONTEXT.TRACK_POINT_TIME: DateTimeOffset segmentTime; string segmentTimeAsString = reader.Value; if (DateTimeOffset.TryParse(segmentTimeAsString, out segmentTime)) { _Logger.Debug(string.Format("Segment start time {0}", segmentTime)); } else { _Logger.Error(string.Format("Error parsing date {0}", segmentTimeAsString)); } if (_CurrentTrackPointBuilder != null) { _CurrentTrackPointBuilder.Time = segmentTime; } break; case PARSE_CONTEXT.TRACK_NAME: if (_CurrentActivityBuilder != null) { _CurrentActivityBuilder.Name = reader.Value; } if (_CurrentTrackBuilder != null) { _CurrentTrackBuilder.Name = reader.Value; } break; case PARSE_CONTEXT.TRACK_TYPE: if (_CurrentActivityBuilder != null) { ACTIVITY_SPORT sport; if (!categoryMapping.TryGetValue(reader.Value, out sport)) { sport = ACTIVITY_SPORT.OTHER; } if (_CurrentActivityBuilder != null) { _CurrentActivityBuilder.Sport = sport; } _Logger.Debug(string.Format("Activity sport {0}", sport)); } break; case PARSE_CONTEXT.TRACK_POINT_ELEVATION: if (_CurrentTrackPointBuilder != null) { float elevation; var style = System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowLeadingSign; var culture = System.Globalization.CultureInfo.CreateSpecificCulture("en-US"); if (!float.TryParse(reader.Value, style, culture, out elevation)) { var countPoints = _CurrentTrackSegmentBuilder.TrackPoints.Count; elevation = countPoints > 0 ? _CurrentTrackSegmentBuilder.TrackPoints[countPoints - 1].Elevation : 0; } _CurrentTrackPointBuilder.Elevation = elevation; } break; case PARSE_CONTEXT.TRACK_POINT_POWER: if (_CurrentTrackPointBuilder != null) { int power; if (!int.TryParse(reader.Value, out power)) { var countPoints = _CurrentTrackSegmentBuilder.TrackPoints.Count; power = countPoints > 0 ? _CurrentTrackSegmentBuilder.TrackPoints[countPoints - 1].Power : 0; } _CurrentTrackPointBuilder.Power = power; } break; case PARSE_CONTEXT.TRACK_POINT_TEMPERATURE: if (_CurrentTrackPointBuilder != null) { float temperature; var style = System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowLeadingSign; var culture = System.Globalization.CultureInfo.CreateSpecificCulture("en-US"); if (!float.TryParse(reader.Value, style, culture, out temperature)) { var countPoints = _CurrentTrackSegmentBuilder.TrackPoints.Count; temperature = countPoints > 0 ? _CurrentTrackSegmentBuilder.TrackPoints[countPoints - 1].Temperature : 0; } _CurrentTrackPointBuilder.Temperature = temperature; } break; case PARSE_CONTEXT.TRACK_POINT_HR: if (_CurrentTrackPointBuilder != null) { int hr; if (!int.TryParse(reader.Value, out hr)) { var countPoints = _CurrentTrackSegmentBuilder.TrackPoints.Count; hr = countPoints > 0 ? _CurrentTrackSegmentBuilder.TrackPoints[countPoints - 1].HeartRate : 0; } _CurrentTrackPointBuilder.HeartRate = hr; } break; case PARSE_CONTEXT.TRACK_POINT_CADENCE: if (_CurrentTrackPointBuilder != null) { int cadence; if (!int.TryParse(reader.Value, out cadence)) { var countPoints = _CurrentTrackSegmentBuilder.TrackPoints.Count; cadence = countPoints > 0 ? _CurrentTrackSegmentBuilder.TrackPoints[countPoints - 1].Cadence : 0; } _CurrentTrackPointBuilder.Cadence = cadence; } break; } break; case XmlNodeType.EndElement: switch (reader.Name) { case "gpx": var allPoints = _CurrentActivityBuilder.Tracks.SelectMany(track => track.TrackSegments.SelectMany(segment => segment.TrackPoints)); _CurrentActivityBuilder.HeartRate = (int)Math.Round(allPoints.Where(point => point.HeartRate > 0).DefaultIfEmpty().Average(point => point?.HeartRate) ?? 0); _CurrentActivityBuilder.Cadence = (int)Math.Round(allPoints.Where(point => point.Cadence > 0).DefaultIfEmpty().Average(point => point?.Cadence) ?? 0); _CurrentActivityBuilder.Power = (int)Math.Round(allPoints.Where(point => point.Power > 0).DefaultIfEmpty().Average(point => point?.Power) ?? 0); _CurrentActivityBuilder.Temperature = (int)Math.Round(allPoints.Where(point => point.Temperature > 0).DefaultIfEmpty().Average(point => point?.Temperature) ?? 0); _CurrentActivityBuilder.TracksPointsCount = allPoints.Count(); var activity = _CurrentActivityBuilder.Build(); _CurrentActivityBuilder = null; _CurrentContext = PARSE_CONTEXT.ROOT; yield return(activity); break; case "time": if (_CurrentContext == PARSE_CONTEXT.ACTIVITY_TIME) { _CurrentContext = PARSE_CONTEXT.METADATA; } if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT_TIME) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT; } break; case "metadata": if (_CurrentContext == PARSE_CONTEXT.METADATA) { _CurrentContext = PARSE_CONTEXT.GPX; } break; case "trk": _CurrentContext = PARSE_CONTEXT.GPX; if (_CurrentActivityBuilder != null && _CurrentTrackBuilder != null) { _CurrentActivityBuilder.Tracks.Add(_CurrentTrackBuilder.Build()); } _CurrentTrackBuilder = null; break; case "name": if (_CurrentContext == PARSE_CONTEXT.TRACK_NAME) { _CurrentContext = PARSE_CONTEXT.TRACK; } break; case "trkseg": _CurrentContext = PARSE_CONTEXT.TRACK; if (_CurrentTrackBuilder != null && _CurrentTrackSegmentBuilder != null) { _CurrentTrackBuilder.TrackSegments.Add(_CurrentTrackSegmentBuilder.Build()); } _CurrentTrackSegmentBuilder = null; break; case "trkpt": _CurrentContext = PARSE_CONTEXT.TRACK_SEGMENT; if (_CurrentTrackSegmentBuilder != null && _CurrentTrackPointBuilder != null) { _CurrentTrackSegmentBuilder.TrackPoints.Add(_CurrentTrackPointBuilder.Build()); } _CurrentTrackPointBuilder = null; break; case "type": if (_CurrentContext == PARSE_CONTEXT.TRACK_TYPE) { _CurrentContext = PARSE_CONTEXT.TRACK; } break; case "ele": if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT_ELEVATION) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT; } break; case "extensions": if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT_EXTENSIONS) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT; } break; case "power": if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT_POWER) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT_EXTENSIONS; } break; default: if (reader.Name == _XmlNamespaces["gpxtpx"] + "TrackPointExtension") { if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT_GPXTPX_EXTENSION) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT_EXTENSIONS; } } else if (reader.Name == _XmlNamespaces["gpxtpx"] + "atemp") { if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT_TEMPERATURE) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT_GPXTPX_EXTENSION; } } else if (reader.Name == _XmlNamespaces["gpxtpx"] + "hr") { if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT_HR) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT_GPXTPX_EXTENSION; } } else if (reader.Name == _XmlNamespaces["gpxtpx"] + "cad") { if (_CurrentContext == PARSE_CONTEXT.TRACK_POINT_CADENCE) { _CurrentContext = PARSE_CONTEXT.TRACK_POINT_GPXTPX_EXTENSION; } } break; } break; } } } }
public IEnumerable <ActivityEntity> ImportActivitiesStream(Stream stream, IDictionary <string, ACTIVITY_SPORT> categoryMapping) { _Logger.Info("Importing data stream"); XmlReaderSettings settings = new XmlReaderSettings { Async = false }; using (XmlReader reader = XmlReader.Create(stream, settings)) { // SAX parsing for performance while (reader.Read()) { switch (reader.NodeType) { case XmlNodeType.Element: switch (reader.Name) { case "FitnessWorkbook": _CurrentContext = PARSE_CONTEXT.FITNESS_WORKBOOK; break; case "AthleteLog": _CurrentContext = PARSE_CONTEXT.ATHLETE_LOG; break; case "Athlete": _CurrentContext = PARSE_CONTEXT.ATHLETE; //_CurrentAthlete = new AthleteEntity(new List<ActivityVO>(), reader.GetAttribute("Name"), reader.GetAttribute("Id")); break; case "Activity": _CurrentContext = PARSE_CONTEXT.ACTIVITY; DateTimeOffset activityStartTime; string activityStartTimeAsString = reader.GetAttribute("StartTime"); if (DateTimeOffset.TryParse(activityStartTimeAsString, out activityStartTime)) { _Logger.Debug(string.Format("Activity start time {0}", activityStartTime)); } else { _Logger.Error(string.Format("Error parsing date {0}", activityStartTimeAsString)); } _CurrentActivityBuilder = new ActivityEntity.Builder { OriginId = reader.GetAttribute("Id"), OriginSystem = "FITLOG", Time = activityStartTime }; break; case "Metadata": _CurrentContext = PARSE_CONTEXT.ACTIVITY_METADATA; break; case "Calories": _CurrentContext = PARSE_CONTEXT.ACTIVITY_CALORIES; int calories; if (int.TryParse(reader.GetAttribute("TotalCal"), out calories)) { if (_CurrentActivityBuilder != null) { _CurrentActivityBuilder.Calories = calories; } _Logger.Debug(string.Format("Activity calories {0}", calories)); } break; case "Name": _CurrentContext = PARSE_CONTEXT.ACTIVITY_NAME; break; case "Category": _CurrentContext = PARSE_CONTEXT.ACTIVITY_CATEGORY; string category = reader.GetAttribute("Id"); ACTIVITY_SPORT sport; if (!categoryMapping.TryGetValue(category, out sport)) { sport = ACTIVITY_SPORT.OTHER; } if (_CurrentActivityBuilder != null) { _CurrentActivityBuilder.Sport = sport; } _Logger.Debug(string.Format("Activity sport {0}", sport)); break; case "Location": _CurrentContext = PARSE_CONTEXT.ACTIVITY_LOCATION; string location = reader.GetAttribute("Name"); if (_CurrentActivityBuilder != null) { _CurrentActivityBuilder.Location = location; } _Logger.Debug(string.Format("Activity location {0}", location)); break; case "EquipmentUsed": _CurrentContext = PARSE_CONTEXT.ACTIVITY_EQUIPMENT_USED; break; case "EquipmentItem": _CurrentContext = PARSE_CONTEXT.ACTIVITY_EQUIPMENT_ITEM; break; case "Track": _CurrentContext = PARSE_CONTEXT.ACTIVITY_TRACK; _CurrentTrackSegmentBuilder = new TrackSegmentEntity.Builder(); string startTimeAsString = reader.GetAttribute("StartTime"); DateTimeOffset startTime; if (DateTimeOffset.TryParse(startTimeAsString, out startTime)) { _CurrentTrackStartTime = startTime; _Logger.Debug(string.Format("Track start time {0}", startTime)); } else { _Logger.Error(string.Format("Track parsing date {0}", startTimeAsString)); } break; case "pt": if (_CurrentTrackSegmentBuilder == null) { break; } _CurrentContext = PARSE_CONTEXT.ACTIVITY_TRACK_PT; int tm; var tmAsString = reader.GetAttribute("tm"); if (int.TryParse(tmAsString, out tm)) { _LastTrackTime = _CurrentTrackStartTime.AddSeconds(tm); } else { _Logger.Error(string.Format("Error parsing tm (number of seconds since start {0}", tmAsString)); break; } float latitude; var style = System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowLeadingSign; var culture = System.Globalization.CultureInfo.CreateSpecificCulture("en-US"); if (!float.TryParse(reader.GetAttribute("lat"), style, culture, out latitude)) { break; } float longitude; if (!float.TryParse(reader.GetAttribute("lon"), style, culture, out longitude)) { break; } float elevation; if (!float.TryParse(reader.GetAttribute("ele"), style, culture, out elevation)) { var countPoints = _CurrentTrackSegmentBuilder.TrackPoints.Count; elevation = countPoints > 0 ? _CurrentTrackSegmentBuilder.TrackPoints[countPoints - 1].Elevation : 0; } int hr; if (!int.TryParse(reader.GetAttribute("hr"), out hr)) { var countPoints = _CurrentTrackSegmentBuilder.TrackPoints.Count; hr = countPoints > 0 ? _CurrentTrackSegmentBuilder.TrackPoints[countPoints - 1].HeartRate : 0; } int cadence; if (!int.TryParse(reader.GetAttribute("cadence"), out cadence)) { var countPoints = _CurrentTrackSegmentBuilder.TrackPoints.Count; cadence = countPoints > 0 ? _CurrentTrackSegmentBuilder.TrackPoints[countPoints - 1].Cadence : 0; } TrackPointVO trackPoint = new TrackPointVO.Builder { Time = _LastTrackTime, Latitude = latitude, Longitude = longitude, Elevation = elevation, HeartRate = hr, Cadence = cadence }; _CurrentTrackSegmentBuilder.TrackPoints.Add(trackPoint); _Logger.Trace(string.Format("Trackpoint time {0}, latitude {1}, longitude {2}, elevation {3}, heart-rate {4}, cadence {5}", _LastTrackTime, latitude, longitude, elevation, hr, cadence)); break; } break; case XmlNodeType.Text: switch (_CurrentContext) { case PARSE_CONTEXT.ACTIVITY_NAME: var name = reader.Value; if (_CurrentActivityBuilder != null) { _CurrentActivityBuilder.Name = name; } _Logger.Debug(string.Format("Activity name {0}", name)); break; } break; case XmlNodeType.EndElement: switch (reader.Name) { case "FitnessWorkbook": _CurrentContext = PARSE_CONTEXT.ROOT; break; case "AthleteLog": _CurrentContext = PARSE_CONTEXT.FITNESS_WORKBOOK; break; case "Athlete": _CurrentContext = PARSE_CONTEXT.FITNESS_WORKBOOK; break; case "Activity": _CurrentContext = PARSE_CONTEXT.FITNESS_WORKBOOK; if (_LastTrackTime != DateTimeOffset.MinValue && _CurrentActivityBuilder.Time != null) { _CurrentActivityBuilder.TimeSpan = _LastTrackTime - _CurrentActivityBuilder.Time; } var allPoints = _CurrentActivityBuilder.Tracks.SelectMany(track => track.TrackSegments.SelectMany(s => s.TrackPoints)); _CurrentActivityBuilder.HeartRate = (int)Math.Round(allPoints.Where(point => point.HeartRate > 0).DefaultIfEmpty().Average(point => point?.HeartRate) ?? 0); _CurrentActivityBuilder.Cadence = (int)Math.Round(allPoints.Where(point => point.Cadence > 0).DefaultIfEmpty().Average(point => point?.Cadence) ?? 0); _CurrentActivityBuilder.Power = (int)Math.Round(allPoints.Where(point => point.Power > 0).DefaultIfEmpty().Average(point => point?.Power) ?? 0); _CurrentActivityBuilder.Temperature = (int)Math.Round(allPoints.Where(point => point.Temperature > 0).DefaultIfEmpty().Average(point => point?.Temperature) ?? 0); _CurrentActivityBuilder.TracksPointsCount = allPoints.Count(); var activity = _CurrentActivityBuilder.Build(); _CurrentActivityBuilder = null; _LastTrackTime = DateTimeOffset.MinValue; yield return(activity); break; case "Metadata": _CurrentContext = PARSE_CONTEXT.ACTIVITY; break; case "Calories": _CurrentContext = PARSE_CONTEXT.ACTIVITY; break; case "Name": _CurrentContext = PARSE_CONTEXT.ACTIVITY; break; case "Category": _CurrentContext = PARSE_CONTEXT.ACTIVITY; break; case "Location": _CurrentContext = PARSE_CONTEXT.ACTIVITY; break; case "EquipmentUsed": _CurrentContext = PARSE_CONTEXT.ACTIVITY; break; case "EquipmentItem": _CurrentContext = PARSE_CONTEXT.ACTIVITY_EQUIPMENT_USED; break; case "Track": _CurrentContext = PARSE_CONTEXT.ACTIVITY; var segment = _CurrentTrackSegmentBuilder.Build(); var trackBuilder = new TrackEntity.Builder(); trackBuilder.TrackSegments.Add(segment); if (_CurrentActivityBuilder != null) { _CurrentActivityBuilder.Tracks.Add(trackBuilder.Build()); } _CurrentTrackSegmentBuilder = null; break; case "pt": _CurrentContext = PARSE_CONTEXT.ACTIVITY_TRACK; break; } //_LoggerService.Debug(string.Format("End Element {0}", reader.Name)); break; default: //_LoggerService.Debug(string.Format("Other node {0} with value {1}", // reader.NodeType, reader.Value)); break; } } } _Logger.Info("Imported data stream"); }