I have multiple .gpx files that I would like to merge into a single file with multiple tracks using R. As an example, two files can be downloaded here:
https://github.com/twesleyb/StackOverflow/blob/master/Afternoon_Ride.gpx
https://github.com/twesleyb/StackOverflow/blob/master/Evening_Run.gpx
Note: I tried to download these with download.file(), but the formatting of the .gpx file is messed up, so don't do this. Download them by hand. Alternatively, you can copy some of the data I pasted below as a minimal example.
gpx_files <- c("Evening_Run.gpx","Afternoon_Ride.gpx")
I can load the files with the plotKML package.
library(plotKML)
# Create empty list for storing .gpx files.
list_gpx <- list()
# Loop to read files, store in a list with name:
for (i in seq_along(gpx_files)){
list_gpx[[i]] <- readGPX(gpx_files[1])
names(list_gpx)[[i]] <- gpx_files[i]
}
The gpx data is stored in a data frame, tracks. I can extract each from the list, and then merge them into one data frame.
# Loop through list_gpx, get track df, clean up columns, and save in list.
# Empty list for tracks.
track_list <- list()
# Loop
for (i in 1:length(list_gpx)){
track_list[[i]] <- do.call(cbind,list_gpx[[i]]$tracks[[1]])[,c(1:4)]
if (grepl("Run",colnames(track_list[[i]]))==TRUE){
track_list[[i]]$activity <- rep("Run",nrow(track_list[[i]]))
}else{
track_list[[i]]$activity <- rep("Bike",nrow(track_list[[i]]))
}
names(track_list[[i]]) <- c("lon","lat","ele","time","activity")
}
# Merge dataframes in track_list.
data <- do.call(rbind,track_list)
I have a custom function (adapted from here) for writing this data to a new file. The result is a single .gpx file with the track info from both files.
# A function for writting GPX files.
writeGPX <- function(lat,lon,ele,time,file="file.gpx"){
o <- c('<gpx version="1.1" creator="R">','<trk>','<trkseg>')
o <- c(o, paste('<trkpt lat="',lat,'" lon="',lon,'"><time>',
paste("<ele>",ele,"</ele>",sep=""),
paste(gsub(' ','T', as.character(time)), 'Z', sep=''),'</time></trkpt>', sep=''))
o <- c(o, '</trkseg>', '</trk>', '</gpx>')
cat(o, file=file, sep='\n')
}
# Write gpx data to a new file.
lat <- data$lat
lon <- data$lon
ele <- data$ele
time <- data$time
writeGPX(lat,lon,ele,time,file=paste(Sys.Date(),"merged.gpx",sep="_"))
The problem is, that this results in a .gpx file with a single track. Because the two original files start and end in different places this results in a big jump between the end of one track and the beginning of the other when you load this into google earth, and I'd like to avoid this. How can I modify my writeGPX function, or use some other existing function, to write a single .gpx file with multiple tracks?
Addendum:
A simple .gpx track might look like this:
<trk>
<trkseg>
<trkpt lat="40.779" lon="-74.428" />
<trkpt lat="40.777" lon="-74.418" />
</trkseg>
</trk>
</gpx>
So, a naive solution to my problem might be something like:
<gpx version="1.1" creator="R">
<trk>
<trkseg>
<trkpt lat="40.779" lon="-74.428" />
<trkpt lat="40.777" lon="-74.418" />
</trkseg>
<trkseg>
<trkpt lat="50.779" lon="-64.428" />
<trkpt lat="50.777" lon="-64.418" />
</trkseg>
</trk>
</gpx>
But, this doesn't work (if you save this as .gpx and try loading it into google earth nothing happens--its not detected in google earth).
Thanks!
Data
## The last 10 lines of evening_run and first ten lines of afternoon_ride:
data <- structure(list(lon = c(-79.045899, -79.045919, -79.045937, -79.045951,
-79.045967, -79.046174, -79.04619, -79.046203, -79.046302, -79.046311,
-79.046704, -79.046694, -79.046687, -79.046702, -79.046727, -79.046735,
-79.046739, -79.046752, -79.046879, -79.046885), lat = c(35.898049,
35.89805, 35.898054, 35.898059, 35.898066, 35.8981, 35.898108,
35.898115, 35.898169, 35.898177, 35.898017, 35.898038, 35.898021,
35.89801, 35.898004, 35.897989, 35.897964, 35.897954, 35.897897,
35.897905), ele = c("99.6", "99.6", "99.8", "99.8", "99.8", "101.2",
"101.2", "101.2", "101.6", "102.0", "105.8", "134.2", "134.2",
"134.2", "107.2", "107.0", "107.2", "107.4", "107.6", "107.6"
), time = c("2019-02-06T01:34:35Z", "2019-02-06T01:34:36Z", "2019-02-06T01:34:37Z",
"2019-02-06T01:34:38Z", "2019-02-06T01:34:39Z", "2019-02-06T01:34:52Z",
"2019-02-06T01:34:53Z", "2019-02-06T01:34:54Z", "2019-02-06T01:35:02Z",
"2019-02-06T01:35:07Z", "2019-02-06T00:15:59Z", "2019-02-06T00:16:00Z",
"2019-02-06T00:16:01Z", "2019-02-06T00:16:03Z", "2019-02-06T00:16:04Z",
"2019-02-06T00:16:05Z", "2019-02-06T00:16:09Z", "2019-02-06T00:16:10Z",
"2019-02-06T00:16:15Z", "2019-02-06T00:16:17Z"), activity = c("Run",
"Run", "Run", "Run", "Run", "Run", "Run", "Run", "Run", "Run",
"Run", "Run", "Run", "Run", "Run", "Run", "Run", "Run", "Run",
"Run")), row.names = c(1020L, 1021L, 1022L, 1023L, 1024L, 1025L,
1026L, 1027L, 1028L, 1029L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L,
10L), class = "data.frame")
As @Dave2e pointed in the first comment, the naïve solution to your problem reads
Each track has to be within
<trk> ... </trk>. You can use the elementnameto give a name to each trackI'll show three ways to merge two gpx files into one gpx file. For the sake of simplicity, I'll assume you have read both files already using your code and that you have a data frame with the two tracks information (latitude, longitude, elevation and time). I'll use the data you provide:
The three methods are:
writeGPXCustom function
Instead of using a function like
writeGPX, I prefer to create four functions that give you more control to create more than one track. It can also be generalized to add way-points and routes.With these functions, you build the file with the two tracks. I'll included the possibility to add a name to each track.
We save gpx_file_content to a file with the extension "gpx"
You can use GPSVisualizer to read the gpx file created. At the end, I'll explain how to read it and map it in R.
Using xml2 package
GPX files are xml files with a defined defined schema GPX 1.1. Both XML and xml2 packages simplifies our lives to work with xml files. I'll picked xml2 because is maintained by the omnipresent Hadley Wickham, xml2
In this case, we created a couple of functions to build the gpx root node and a trk node.
Now, you build the complete xml tree with the two tracks nodes
Using sf package
This package opens the door to geocomputation in R. There are many good resources to learn about it, with package main page sf and the book Geocomputation with R among my favourites.
First create an
sfobject with the track geometry (latitude and longitude) and the elevation and time attributes to each point. Coordinates are given a geometric sense with the coordinate reference system used in GPX files: WGS84, which has the EPSG code 4326.We need to create a new variable that identifies each track. According to the specs of GPX driver gdal, each track is identified with a different track_seg_id and each point in the track by track_seg_point_id. A name for each track can be defined also in the first point of each track.
With this additional information in the tracks_points sf object, we export it to a gpx file using the GPX driver
I did not manage to add a name to each track nor to add the time element to each data point. So this third method is not as complete as the previous two. Nevertheless, I've included it because gpx files can be read directly into sf objects and those can be represented in a map, all within R!
The gpx file can be read into an sf object with
st_read. Two layers can be read:Both sf objects will be used in this example
Map with tracks
Using the leaflet (CRAN) package, we can plot the two tracks included in the gpx file we have created before.
which generates the following dynamic map (I took an snapshot of it)