Working with gpx files

There are many file formats for spatial data. One of the more common formats used for many hand held devices (GPS, smart watches, etc.), .gpx are an interesting file type. They can hold points, as well as lines (or “tracks”). These usually have additional information, and include timestamps, elevation, and other metadata.

Let’s load packages first:

library(sf)
library(dplyr)
library(lubridate)
library(mapview)
library(here)
mapviewOptions(fgb = FALSE)

Read in a Track from gpx File

Let’s use an example file that lives here on github on github. Download the file, and save it into a data folder.

Now we can read it in! Notice there’s lots of additional information in there. However, this is a still a data.frame and an sf object, which means its easy to work with.

# a locally downloaded gpx file
file1 <- "NFA.GPX"

# read just the tracks:
trax <- st_read(here::here("data", file1), layer = "tracks")
## Reading layer `tracks' from data source `/Users/ryanpeek/Documents/github/WEBSITES/mapping-in-R-workshop/data/NFA.GPX' using driver `GPX'
## Simple feature collection with 4 features and 14 fields
## geometry type:  MULTILINESTRING
## dimension:      XY
## bbox:           xmin: -120.9267 ymin: 39.10202 xmax: -120.9186 ymax: 39.10835
## geographic CRS: WGS 84
# check the names of the tracks:
## here these represent dates for individual tracks
trax$name
## [1] "2017-05-05-NFA" "2018-05-02-NFA" "2020-05-06-NFA" "2020-06-03-NFA"
# here we can see all the data inside the data frame
str(trax)
## Classes 'sf' and 'data.frame':   4 obs. of  15 variables:
##  $ name                       : chr  "2017-05-05-NFA" "2018-05-02-NFA" "2020-05-06-NFA" "2020-06-03-NFA"
##  $ cmt                        : chr  NA NA NA NA
##  $ desc                       : chr  NA NA NA NA
##  $ src                        : chr  NA NA NA NA
##  $ link1_href                 : chr  NA NA NA NA
##  $ link1_text                 : chr  NA NA NA NA
##  $ link1_type                 : chr  NA NA NA NA
##  $ link2_href                 : chr  NA NA NA NA
##  $ link2_text                 : chr  NA NA NA NA
##  $ link2_type                 : chr  NA NA NA NA
##  $ number                     : int  NA NA NA NA
##  $ type                       : chr  NA NA NA NA
##  $ gpxx_TrackExtension        : chr  "        <gpxx:DisplayColor>Cyan</gpxx:DisplayColor>      " "        <gpxx:DisplayColor>Cyan</gpxx:DisplayColor>      " "        <gpxx:DisplayColor>Yellow</gpxx:DisplayColor>      " "        <gpxx:DisplayColor>Magenta</gpxx:DisplayColor>      "
##  $ gpxtrkx_TrackStatsExtension: chr  "        <gpxtrkx:Distance>4470</gpxtrkx:Distance>        <gpxtrkx:TimerTime>13209</gpxtrkx:TimerTime>        <g"| __truncated__ "        <gpxtrkx:Distance>3171</gpxtrkx:Distance>        <gpxtrkx:TimerTime>10017</gpxtrkx:TimerTime>        <g"| __truncated__ NA "        <gpxtrkx:Distance>1659</gpxtrkx:Distance>        <gpxtrkx:TimerTime>6197</gpxtrkx:TimerTime>        <gp"| __truncated__
##  $ geometry                   :sfc_MULTILINESTRING of length 4; first list element: List of 1
##   ..$ : num [1:529, 1:2] -121 -121 -121 -121 -121 ...
##   ..- attr(*, "class")= chr [1:3] "XY" "MULTILINESTRING" "sfg"
##  - attr(*, "sf_column")= chr "geometry"
##  - attr(*, "agr")= Factor w/ 3 levels "constant","aggregate",..: NA NA NA NA NA NA NA NA NA NA ...
##   ..- attr(*, "names")= chr [1:14] "name" "cmt" "desc" "src" ...

Read points from a gpx from a URL

Now let’s do the same thing, but let’s read in points instead, and use a weblink instead of a local file.

# read straight from the interwebs
file1url <- "https://raw.githubusercontent.com/ryanpeek/mapping-in-R-workshop/main/data/NFA.GPX"

# read just the tracks:
pts <- st_read(file1url, layer = "waypoints")
## Reading layer `waypoints' from data source `https://raw.githubusercontent.com/ryanpeek/mapping-in-R-workshop/main/data/NFA.GPX' using driver `GPX'
## Simple feature collection with 167 features and 26 fields
## geometry type:  POINT
## dimension:      XY
## bbox:           xmin: -120.9266 ymin: 39.1043 xmax: -120.9182 ymax: 39.10817
## geographic CRS: WGS 84
# check the names of attributes
glimpse(pts)
## Rows: 167
## Columns: 27
## $ ele                       <dbl> 326.9717, 343.0736, 351.0044, 363.0208, 356.0513, 349.0818, 344.9962, 346.9188, 342.1123, 347.8801, 358.9352, 356.0513, 0.0…
## $ time                      <dttm> 2013-05-21 13:32:10, 2013-05-21 13:32:10, 2013-05-21 13:32:10, 2013-05-21 13:32:10, 2013-05-21 13:32:10, 2013-05-21 13:32:…
## $ magvar                    <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ geoidheight               <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ name                      <chr> "NFA09-E01", "NFA09-E02", "NFA09-E03", "NFA09-E04", "NFA09-E05", "NFA09-E07", "NFA09-E09", "NFA09-E10", "NFA09-E11", "NFA09…
## $ cmt                       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, "<html xmlns:fo=\"http://www.w3.org/1999/XSL/Format\" xmlns:msxsl=\"urn:sch…
## $ desc                      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, "<html xmlns:fo=\"http://www.w3.org/1999/XSL/Format\" xmlns:msxsl=\"urn:sch…
## $ src                       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ link1_href                <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ link1_text                <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ link1_type                <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ link2_href                <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ link2_text                <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ link2_type                <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ sym                       <chr> "City (Small)", "City (Small)", "City (Small)", "City (Small)", "City (Small)", "City (Small)", "City (Small)", "City (Smal…
## $ type                      <chr> "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "user", "us…
## $ fix                       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ sat                       <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ hdop                      <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ vdop                      <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ pdop                      <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ ageofdgpsdata             <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ dgpsid                    <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ gpxx_WaypointExtension    <chr> "        <gpxx:DisplayMode>SymbolAndName</gpxx:DisplayMode>      ", "        <gpxx:DisplayMode>SymbolAndName</gpxx:DisplayM…
## $ wptx1_WaypointExtension   <chr> "        <wptx1:DisplayMode>SymbolAndName</wptx1:DisplayMode>      ", "        <wptx1:DisplayMode>SymbolAndName</wptx1:Disp…
## $ ctx_CreationTimeExtension <chr> "        <ctx:CreationTime>2013-05-21T20:32:10Z</ctx:CreationTime>      ", "        <ctx:CreationTime>2013-05-21T20:32:10Z<…
## $ geometry                  <POINT [°]> POINT (-120.9217 39.10688), POINT (-120.9212 39.1069), POINT (-120.9204 39.10674), POINT (-120.9203 39.10672), POINT …

Great!

Pull a Single Track and Subset Waypoints

If we want to pull a single track or a single set of points, we can leverage {dplyr} and filter or subset our data in whatever way we want.

Let’s grab a single track, and only pull waypoints from a selected set of locations and specific year (need the {lubridate} package).

# pull a single track
trx1 <- trax %>% dplyr::filter(name=="2017-05-05-NFA")

# get points and exclude a site called "NFARRAV"

pts <- st_read(here("data/",file1), layer="waypoints") %>% 
    filter(!grepl("NFARRAV", x = name)) %>% 
    mutate(YYYY = year(time)) %>% 
  filter(YYYY == 2018)
## Reading layer `waypoints' from data source `/Users/ryanpeek/Documents/github/WEBSITES/mapping-in-R-workshop/data/NFA.GPX' using driver `GPX'
## Simple feature collection with 167 features and 26 fields
## geometry type:  POINT
## dimension:      XY
## bbox:           xmin: -120.9266 ymin: 39.1043 xmax: -120.9182 ymax: 39.10817
## geographic CRS: WGS 84

Mapview!

Now that we have our filtered and selected pieces, we can make a quick map with the excellent {mapview} package. We’ll color points by elevation, just because. This information isn’t particularly relevant, more to show how you can make fun mapview maps.

mapview(pts, zcol="ele") + mapview(trx1, color="orange")

Put It All Together