#' Read tcx-file content #' #' This function reads Garmin tcx-files to R. #' #' @param file \code{character} scalar, name of the file to be read, #' with extension. #' #' The function was written and tested for the tcx xml-format version 1.0 #' as obtained from a Garmin Forerunner 610 watch ( #' http://www8.garmin.com/xmlschemas/ActivityExtensionv2.xsd). \cr #' The function extracts the position of all point tags containing #' "" and adds lines as given by \code{point.order}. #' This parameter is by default defined as \code{c(1, 3, 4, 6, 7, 10)}, #' describing the number of lines skipped until the data fields are reached: #' \code{Time}, \code{LatitudeDegrees}, \code{LongitudeDegrees}, #' \code{AltitudeMeters}, \code{DistanceMeters}, \code{Speed}. \cr #' Similarly, \code{lap.order} describes the number of lines pointing at the #' lap-based data fields \code{Lap StartTime}, \code{TotalTimeSeconds}, #' \code{DistanceMeters}, \code{MaximumSpeed}, \code{Calories}, #' \code{Intensity}. It is by default set to \code{c(0, 1, 2, 3, 4, 5).} \cr #' To change the order, for example if another xml-version is present, the #' values of \code{point.order} and \code{lap.order} can be modified. #' #' @param speed_unit \code{character} scalar, unit of speed values. #' Either \code{m/s} or \code{km/h}. Set to \code{m/s} by default. #' #' @param point_order \code{numeric} vector, optional order of the point #' data fields in the data set. Only useful when tcx-format differs from #' standard format (cf. details). #' #' @param lap_order \code{numeric} vector, optional order of the lap data #' fields in the data set. Only useful when tcx-format differs from standard #' format (cf. details). #' #' @return \code{data.frame} with segment coordinates (x1, y1, x2, y2). #' @author Michael Dietze #' @examples #' #' ## uncomment to use, change "dummyfile.tcx" to actual file name. #' # x <- read.tcx(file = "dummyfile.tcx", speed.unit = "km/h") #' #' # plot(x = x$Point$Lon, #' # y = x$Point$Lat, #' # cex = (x$Point$Speed/10)^4, #' # col = x$Point$Lap.ID) #' #' @export read_tcx read_tcx <- function( file, speed_unit = "m/s", point_order, lap_order ) { ## data checks -------------------------------------------------------------- ## check/set point order if(missing(point_order) == TRUE) { point.order <- c(1, 3, 4, 6, 7, 10) } ## check/set lap order if(missing(lap_order) == TRUE) { lap.order <- c(0, 1, 2, 3, 4, 5) } ## read raw file data_raw <- as.character(x = readLines(con = file)) ## point-based data extraction ---------------------------------------------- ## get data lines data_lines <- seq(1, length(data_raw))[data_raw == " "] ## extract and convert time data Time_raw <- data_raw[data_lines + point_order[1]] Time_raw <- unlist(strsplit(Time_raw, ">", fixed = TRUE)) Time_raw <- Time_raw[seq(2, length(Time_raw), 2)] Time_raw <- unlist(strsplit(Time_raw, "Z<", fixed = TRUE)) Time_raw <- Time_raw[seq(1, length(Time_raw), 2)] Time_raw <- sub(pattern = "T", replacement = " ", x = Time_raw) Time <- as.POSIXct(strptime(Time_raw, "%Y-%m-%d %H:%M:%S")) ## extract Latitude Lat_raw <- data_raw[data_lines + point_order[2]] Lat_raw <- unlist(strsplit(Lat_raw, ">", fixed = TRUE)) Lat_raw <- Lat_raw[seq(2, length(Lat_raw), 2)] Lat_raw <- unlist(strsplit(Lat_raw, "<", fixed = TRUE)) Lat <- as.numeric(Lat_raw[seq(1, length(Lat_raw), 2)]) ## extract Longitude Lon_raw <- data_raw[data_lines + point_order[3]] Lon_raw <- unlist(strsplit(Lon_raw, ">", fixed = TRUE)) Lon_raw <- Lon_raw[seq(2, length(Lon_raw), 2)] Lon_raw <- unlist(strsplit(Lon_raw, "<", fixed = TRUE)) Lon <- as.numeric(Lon_raw[seq(1, length(Lon_raw), 2)]) ## extract Altitude Alt_raw <- data_raw[data_lines + point_order[4]] Alt_raw <- unlist(strsplit(Alt_raw, ">", fixed = TRUE)) Alt_raw <- Alt_raw[seq(2, length(Alt_raw), 2)] Alt_raw <- unlist(strsplit(Alt_raw, "<", fixed = TRUE)) Alt <- as.numeric(Alt_raw[seq(1, length(Alt_raw), 2)]) ## extract cumulative Distance Dist_raw <- data_raw[data_lines + point_order[5]] Dist_raw <- unlist(strsplit(Dist_raw, ">", fixed = TRUE)) Dist_raw <- Dist_raw[seq(2, length(Dist_raw), 2)] Dist_raw <- unlist(strsplit(Dist_raw, "<", fixed = TRUE)) Dist_cum <- as.numeric(Dist_raw[seq(1, length(Dist_raw), 2)]) ## calculate incremental distance Dist_inc <- c(0, Dist_cum[2:length(Dist_cum)] - Dist_cum[1:(length(Dist_cum) - 1)]) ## extract Speed Speed_raw <- data_raw[data_lines + point_order[6]] Speed_raw <- unlist(strsplit(Speed_raw, ">", fixed = TRUE)) Speed_raw <- Speed_raw[seq(2, length(Speed_raw), 2)] Speed_raw <- unlist(strsplit(Speed_raw, "<", fixed = TRUE)) Speed <- as.numeric(Speed_raw[seq(1, length(Speed_raw), 2)]) ## optinally change speed unit if(speed_unit == "m/s") { Speed <- Speed } else if (speed_unit == "km/h") { Speed <- Speed * 3.6 } else stop("Unknown speed unit!") ## Lap-based data extraction------------------------------------------------- ## get lap lines Lap_lines <- grep(pattern = "", fixed = TRUE)) Span_lap_raw <- Span_lap_raw[seq(2, length(Span_lap_raw), 2)] Span_lap_raw <- unlist(strsplit(Span_lap_raw, "<", fixed = TRUE)) Span_lap <- as.numeric(Span_lap_raw[seq(1, length(Span_lap_raw), 2)]) ## extract DistanceMeters Dist_lap_raw <- data_raw[Lap_lines + lap_order[3]] Dist_lap_raw <- unlist(strsplit(Dist_lap_raw, ">", fixed = TRUE)) Dist_lap_raw <- Dist_lap_raw[seq(2, length(Dist_lap_raw), 2)] Dist_lap_raw <- unlist(strsplit(Dist_lap_raw, "<", fixed = TRUE)) Dist_lap <- as.numeric(Dist_lap_raw[seq(1, length(Dist_lap_raw), 2)]) ## extract MaximumSpeed Speed_lap_raw <- data_raw[Lap_lines + lap_order[4]] Speed_lap_raw <- unlist(strsplit(Speed_lap_raw, ">", fixed = TRUE)) Speed_lap_raw <- Speed_lap_raw[seq(2, length(Speed_lap_raw), 2)] Speed_lap_raw <- unlist(strsplit(Speed_lap_raw, "<", fixed = TRUE)) Speed_lap <- as.numeric(Speed_lap_raw[seq(1, length(Speed_lap_raw), 2)]) ## extract Calories Calories_lap_raw <- data_raw[Lap_lines + lap_order[5]] Calories_lap_raw <- unlist(strsplit(Calories_lap_raw, ">", fixed = TRUE)) Calories_lap_raw <- Calories_lap_raw[seq(2, length(Calories_lap_raw), 2)] Calories_lap_raw <- unlist(strsplit(Calories_lap_raw, "<", fixed = TRUE)) Calories_lap <- as.numeric(Calories_lap_raw[ seq(1, length(Calories_lap_raw), 2)]) ## extract Intensity Intensity_lap_raw <- data_raw[Lap_lines + lap_order[6]] Intensity_lap_raw <- unlist(strsplit(Intensity_lap_raw, ">", fixed = TRUE)) Intensity_lap_raw <- Intensity_lap_raw[seq(2, length(Intensity_lap_raw), 2)] Intensity_lap_raw <- unlist(strsplit(Intensity_lap_raw, "<", fixed = TRUE)) Intensity_lap <- as.character(Intensity_lap_raw[ seq(1, length(Intensity_lap_raw), 2)]) ## add lap number to point data Lap_ID <- rep(length(Time_lap), length(Time)) for(i in 1:(length(Lap_lines) - 1)) { Lap_ID[Time >= Time_lap[i] & Time < Time_lap[i + 1]] <- i } ## create output------------------------------------------------------------- Point <- data.frame(Time = Time, Lat = Lat, Lon = Lon, Alt = Alt, Dist_cum = Dist_cum, Dist_inc = Dist_inc, Speed = Speed, Lap_ID = Lap_ID) Lap <- data.frame(Time = Time_lap, Span = Span_lap, Dist = Dist_lap, Speed = Speed_lap, Calories = Calories_lap, Intensity = Intensity_lap) Meta <- data_raw[c(1:6, 12495:12534)] ##value<< A list object. list(Point = Point, ##<< Point-based data Lap = Lap, ##<< Lap-based data Meta = Meta) ##<< Raw metainformation, extracted from input file }