5 min read

Google Photos mapping using leaflet

In this post I will demonstrate how to display your Google Photos on a map. As you probably know, most of today´s mobile devices or cameras allow to record GPS position during taking the photos.

If you are using Google Photos you can leverage Google Takeout for this task. Google Takeout is a service for anyone with Google account allowing the users to download back any previously updated data.

It means you can download back all your uploaded photos including JSON files for each file. These JSON files are exactly what we are interested in as they contains all information the Google tracks about your photos.

library(jsonlite)
library(lubridate)
library(tidyverse)
library(leaflet)
library(mapview)

After you download your photos you can simply retrieve the list of all JSON files:

path_vector <- list.files("D:/GoogleDrive/Takeout", pattern = ".json$", recursive = TRUE, full.names = TRUE)
path_vector %>% head
## [1] "D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory party 12,13/DSCF0019.jpg.json"
## [2] "D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory party 12,13/DSCF0029.jpg.json"
## [3] "D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory party 12,13/DSCF0030.jpg.json"
## [4] "D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory party 12,13/DSCF0033.jpg.json"
## [5] "D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory party 12,13/DSCF0037.jpg.json"
## [6] "D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory party 12,13/DSCF6092.jpg.json"

Each JSON contains several variables about the photo. I´m interested only in GPS location, path to Google server and the date the picture was taken.

lst <- path_vector %>% map(fromJSON)
lst[1]
## [[1]]
## [[1]]$title
## [1] "DSCF0019.jpg"
## 
## [[1]]$description
## [1] ""
## 
## [[1]]$url
## [1] "https://lh3.googleusercontent.com/-6TjimfukJvM/XCJ3wEGP6OI/AAAAAAACR_Q/-hRC-QHc2Lgw16X21jBovjDDJ0RDSuRdACLABGAYYCw/s0-d/DSCF0019.jpg"
## 
## [[1]]$imageViews
## [1] "0"
## 
## [[1]]$creationTime
## [[1]]$creationTime$timestamp
## [1] "1545762751"
## 
## [[1]]$creationTime$formatted
## [1] "25. 12. 2018 18:32:31 UTC"
## 
## 
## [[1]]$modificationTime
## [[1]]$modificationTime$timestamp
## [1] "1545851448"
## 
## [[1]]$modificationTime$formatted
## [1] "26. 12. 2018 19:10:48 UTC"
## 
## 
## [[1]]$geoData
## [[1]]$geoData$latitude
## [1] 0
## 
## [[1]]$geoData$longitude
## [1] 0
## 
## [[1]]$geoData$altitude
## [1] 0
## 
## [[1]]$geoData$latitudeSpan
## [1] 0
## 
## [[1]]$geoData$longitudeSpan
## [1] 0
## 
## 
## [[1]]$geoDataExif
## [[1]]$geoDataExif$latitude
## [1] 0
## 
## [[1]]$geoDataExif$longitude
## [1] 0
## 
## [[1]]$geoDataExif$altitude
## [1] 0
## 
## [[1]]$geoDataExif$latitudeSpan
## [1] 0
## 
## [[1]]$geoDataExif$longitudeSpan
## [1] 0
## 
## 
## [[1]]$photoTakenTime
## [[1]]$photoTakenTime$timestamp
## [1] "1086109200"
## 
## [[1]]$photoTakenTime$formatted
## [1] "1. 6. 2004 17:00:00 UTC"

Tibble is always good starting point..

df_raw <- tibble(orig_path = path_vector, data = lst)
df_raw
## # A tibble: 6,944 x 2
##    orig_path                                                     data     
##    <chr>                                                         <list>   
##  1 D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory p~ <list [9~
##  2 D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory p~ <list [9~
##  3 D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory p~ <list [9~
##  4 D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory p~ <list [9~
##  5 D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory p~ <list [9~
##  6 D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory p~ <list [9~
##  7 D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory p~ <list [9~
##  8 D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory p~ <list [9~
##  9 D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory p~ <list [9~
## 10 D:/GoogleDrive/Takeout/Fotky Google/2004_06 Budvar factory p~ <list [9~
## # ... with 6,934 more rows

A little bit of extractions and manipulation..

df <- df_raw %>% 
  # exclude "metadata.json" files
  filter(!str_detect(orig_path, "metadata")) %>% 
  # extract link to google server
  mutate(google_path = map_chr(data, "url")) %>% 
  # exclude video files
  filter(!str_detect(google_path, "video-downloads")) %>%
  # this will modify the link in order to display in popup window later instead of download
  mutate(google_path = str_replace(google_path, "/s0-d", "")) %>% 
  # extract GPS location
  mutate(lat = map(data, "geoData") %>% map_dbl(., "latitude")) %>%
  mutate(lon = map(data, "geoData") %>% map_dbl(., "longitude")) %>%
  # extract photo taken time
  mutate(photo_taken_time = map(data, "photoTakenTime") %>% map_chr(., "formatted")) %>%
  mutate(photo_taken_time = dmy_hms(photo_taken_time)) %>% 
  select(-data)
df
## # A tibble: 6,725 x 5
##    orig_path           google_path           lat   lon photo_taken_time   
##    <chr>               <chr>               <dbl> <dbl> <dttm>             
##  1 D:/GoogleDrive/Tak~ https://lh3.google~     0     0 2004-06-01 17:00:00
##  2 D:/GoogleDrive/Tak~ https://lh3.google~     0     0 2004-06-01 17:00:00
##  3 D:/GoogleDrive/Tak~ https://lh3.google~     0     0 2004-06-01 17:00:00
##  4 D:/GoogleDrive/Tak~ https://lh3.google~     0     0 2004-06-01 17:00:00
##  5 D:/GoogleDrive/Tak~ https://lh3.google~     0     0 2004-06-01 17:00:00
##  6 D:/GoogleDrive/Tak~ https://lh3.google~     0     0 2004-06-01 17:00:00
##  7 D:/GoogleDrive/Tak~ https://lh3.google~     0     0 2004-06-01 17:00:00
##  8 D:/GoogleDrive/Tak~ https://lh3.google~     0     0 2004-06-01 17:00:00
##  9 D:/GoogleDrive/Tak~ https://lh3.google~     0     0 2004-06-01 17:00:00
## 10 D:/GoogleDrive/Tak~ https://lh3.google~     0     0 2004-06-01 17:00:00
## # ... with 6,715 more rows
df %>% 
  count(lat != 0 & lon != 0) %>% 
  mutate(prop = round(n/sum(n)*100,2))
## # A tibble: 2 x 3
##   `lat != 0 & lon != 0`     n  prop
##   <lgl>                 <int> <dbl>
## 1 FALSE                  6382  94.9
## 2 TRUE                    343   5.1

..obviously most of my pictures is without GPS data. The good news is it can be easily edited directly on Google Photos app for any photo.

df %>% 
  filter(lat!=0) %>% 
  count(year(photo_taken_time))
## # A tibble: 7 x 2
##   `year(photo_taken_time)`     n
##                      <dbl> <int>
## 1                     2012    37
## 2                     2013    16
## 3                     2014    14
## 4                     2015    10
## 5                     2016     6
## 6                     2017    13
## 7                     2018   247

..it is related to the fact that my last phone is my first which allows to record GPS during taking photos.

Finally, using the leaflet maps we can display all photos as points on a map and use it´s functionality as selecting the point and display the photo.

imgurl <- df %>% 
  filter(lat!=0) %>% 
  pull(google_path) #%>% 
m <- df %>% 
  filter(lat!=0) %>% 
  leaflet() %>%
  # add default OpenStreetMap map tiles
  addTiles() %>%
  addCircles(popup = popupImage(img = imgurl, src = c("remote"))) %>% 
  clearBounds()
m 

This is just image due to obvious personal reasons. Feel free to replicate for your own data;) Hope you enjoyed.