Skip navigation

Tag Archives: 30DayMapChallenge

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!