The 30-Day Map Challenge is on again, and I’m hoping to be able to scrounge some time to get an entry for each day. Day 2 is lines (Day 1 was posted on Twitter only) and — while I’m hoping to focus on saving U.S. democracy for the majority of the entries, today’s is a short one that shows all the walks/hikes/boats we took during our Iceland vacation.
I use HealthFit to export data (automagically) from all my Apple Watch, Garmin, and Peloton activities, and have it auto-sync to iCloud, which means I have instant access to all the Garmin FIT files it creates in ~/Library/Mobile Documents/iCloud~com~altifondo~HealthFit/
.
We can use {FITFileR} to read in those files, turn the points into lines, and plot them all on a {leaflet} map, so I can get an interactive view into the paths.
If I had more time, I’d add activity names and other clickable statistics in popups, which is pretty straightforward in {leaflet}.
Comments in code hopefully explain the workflow:
library(sf)
library(FITfileR) # remotes::install_github("grimbough/FITfileR")
library(leaflet)
library(tidyverse)
# get a listing of all the files for when we were in Iceland
list.files(
path = "~/Library/Mobile Documents/iCloud~com~altifondo~HealthFit/Documents",
pattern = "(2021-07-3[01]|2021-08-0[1-9])",
full.names = TRUE
) %>%
map(
~readFitFile(.x) %>% # read the file in
records() %>% # turn into something we can then bind into a data frame
bind_rows() %>% # bind ^^ into a data frame!
mutate(file = basename(.x)) # add the file info in case we want to eventually make a popup of information
) -> iceland_fit_files
# look at the data
iceland_fit_files[[1]]
## # A tibble: 2,210 × 12
## timestamp position_lat position_long gps_accuracy altitude distance speed temperature heart_rate cadence
## <dttm> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2021-07-30 06:28:26 64.2 -21.9 2 21 318. 0.221 13 116 NA
## 2 2021-07-30 06:28:27 64.2 -21.9 2 21 318. 0.521 13 116 NA
## 3 2021-07-30 06:28:28 64.2 -21.9 2 21 319. 0.792 13 116 NA
## 4 2021-07-30 06:28:47 64.2 -21.9 2 19.4 338. 0.217 13 118 NA
## 5 2021-07-30 06:28:48 64.2 -21.9 2 19.2 338. 0.147 13 118 NA
## 6 2021-07-30 06:28:49 64.2 -21.9 2 19 338. 0.109 13 117 NA
## 7 2021-07-30 06:28:50 64.2 -21.9 2 19 338. 0.076 13 117 NA
## 8 2021-07-30 06:28:51 64.2 -21.9 2 18.8 338. 0.036 13 116 NA
## 9 2021-07-30 06:28:52 64.2 -21.9 2 18.6 338. 0.153 13 115 NA
## 10 2021-07-30 06:28:53 64.2 -21.9 2 18.4 339. 0.393 13 115 NA
## # … with 2,200 more rows, and 2 more variables: fractional_cadence <dbl>, file <chr>
iceland_fit_files[map_lgl(iceland_fit_files, has_name, "position_long")] %>% # only want activities with geo data
map(
~.x %>%
filter(!is.na(position_long), !is.na(position_lat)) %>% # {sf} hates NAs
st_as_sf(
coords = c("position_long", "position_lat"), # turn the data frame into an {sf} object
crs = 4326
)
) %>%
bind_rows() %>% # this makes one big data frame
group_by(file) %>% # which we can turn into individual geometries
summarise( # with the epic summarise() function
m = max(distance)
) %>%
st_cast("LINESTRING") -> paths # and then turn the points into linestrings
# let's take a look
paths
## Simple feature collection with 33 features and 2 fields
## Geometry type: LINESTRING
## Dimension: XY
## Bounding box: xmin: -23.92788 ymin: 63.40121 xmax: -16.18086 ymax: 65.08101
## Geodetic CRS: WGS 84
## # A tibble: 33 × 3
## file m geometry
## <chr> <dbl> <LINESTRING [°]>
## 1 2021-07-30-062401-Walking-Chetzmoka.fit 2807. (-21.94386 64.15495, -21.94382 64.15497, -21.94384 64.15496, -21.94388 64.…
## 2 2021-07-30-101146-Walking-Chetzmoka.fit 1191. (-21.93504 64.14761, -21.93502 64.1476, -21.93503 64.14761, -21.93501 64.1…
## 3 2021-07-30-105706-Walking-Chetzmoka.fit 1554. (-21.91706 64.12906, -21.91704 64.12905, -21.91702 64.12905, -21.917 64.12…
## 4 2021-07-30-143247-Walking-Chetzmoka.fit 1620. (-21.94977 64.15683, -21.94978 64.15685, -21.94979 64.15686, -21.94995 64.…
## 5 2021-07-31-122122-Walking-Chetzmoka.fit 702. (-22.26431 64.7628, -22.26463 64.76297, -22.26527 64.76332, -22.26496 64.7…
## 6 2021-07-31-131832-Walking-Chetzmoka.fit 2084. (-22.84353 64.90553, -22.84351 64.90553, -22.84349 64.90553, -22.84347 64.…
## 7 2021-07-31-182725-Walking-Chetzmoka.fit 578. (-22.72256 65.0809, -22.72255 65.0809, -22.72254 65.08089, -22.72252 65.08…
## 8 2021-08-01-105420-Walking-Chetzmoka.fit 1210. (-23.63732 64.79827, -23.6373 64.79829, -23.6373 64.79829, -23.6373 64.798…
## 9 2021-08-01-142847-Walking-Chetzmoka.fit 2385. (-23.80382 64.73048, -23.8038 64.73047, -23.8038 64.73047, -23.8038 64.730…
## 10 2021-08-01-165642-Walking-Chetzmoka.fit 423. (-23.92745 64.85198, -23.92749 64.85197, -23.92747 64.85197, -23.92746 64.…
## # … with 23 more rows
# make room locally
dir.create("~/projects/2021-iceland", showWarnings = FALSE)
# save the widget out
paths %>%
filter(!grepl("2021-08-10", file)) %>%
leaflet() %>%
addProviderTiles("CartoDB.Positron") %>%
addPolygons(color = "green") %>%
htmlwidgets::saveWidget(
file = "~/projects/2021-iceland/index.html",
selfcontained = FALSE
)
# rsync it out to my website
#
This is the (static) overview:
And, this is a zoom into our boating tour of a glacier lagoon:
Hit up the widget to see where we did our Iceland activities this summer!