############################################################ ##### Extraction des segments STRAVA via l'API ## ##### Auteurs : Matthieu Viry, Renaud Le Goix, UAR RIATE ## ##### CNRS Université Paris Cité ## ##### Licence : CCBY 4.0 Ce document est mis à disposition## ##### selon les termes de la Licence Creative Commons ## ##### Attribution - Pas d'Utilisation Commerciale - ## ##### Partage dans les Mêmes Conditions 4.0 International.## ############################################################ #Ce code couvre la partie principale de l'agglomération avec des carreaux de 5 km de côté, par itération, et des requêtes par carrezaux de 1 km de côté. library(rStrava) library(sf) library(googlePolylines) library(mapview) ### Ouvrir les fichiers carto de base ## Load map files #BoundingBox<- st_read("~/SIG/Projet_Airbnb_DVF_Bien_Luc_Reno/data/Shapes/geom_Mask.gpkg", layer = "mask", quiet = TRUE) ParisCom<- st_read("~/SIG/cours_m2_traitementsstatscarto/public/data/Paris_PC.gpkg", layer = "COM_PC", quiet = TRUE) plot(ParisCom$geom) st_crs(ParisCom) ParisCom<-st_transform(ParisCom, 4326) #Remettre à jour le CRS ## Create bounding box BoundingBox <- st_bbox(ParisCom) BoundingBox <- st_as_sfc(BoundingBox) BoundingBox <- st_sf(geometry = BoundingBox) st_crs(BoundingBox) plot(BoundingBox$geom) BoundingBox <- st_transform(BoundingBox, 4326) #Remettre à jour le CRS mapview(BoundingBox) + mapview(ParisCom) # Contrôle # Création du token d'authentification get_token <- function(app_name, app_client_id, app_secret) { return(httr::config(token = strava_oauth(app_name, app_client_id, app_secret, app_scope = "read_all"))) } # Converti une liste de segments en une feature collection de sf convert_segments_sf <- function (segments) { df <- as.data.frame(do.call(rbind, segments)) # On enlève les doublons sur la base de la colonne id df <- df[!duplicated(df$id), ] # On met le bon type pour les colonnes (sinon type = list) df$id <- as.numeric(df$id) df$resource_state <- as.numeric(df$resource_state) df$name <- as.character(df$name) df$climb_category <- as.numeric(df$climb_category) df$climb_category_desc <- as.character(df$climb_category_desc) df$avg_grade <- as.numeric(df$avg_grade) # df$start_latlng <- as.numeric(df$start_latlng) # df$end_latlng <- as.numeric(df$end_latlng) df$elev_difference <- as.numeric(df$elev_difference) df$distance <- as.numeric(df$distance) df$starred <- as.factor(df$starred) df$elevation_profile <- as.character(df$elevation_profile) df$local_legend_enabled <- as.character(df$local_legend_enabled) # Décode la géom des segments coords <- lapply(df$points, decode) # On inverse l'ordre des colonnes lat & long coords <- lapply(coords, function(df1) df1[[1]][,order(ncol(df1[[1]]):1)]) # On enlève les colonnes désormais inutiles df <- subset(df, select = -c(points, start_latlng, end_latlng)) # Création de l'objet sf geom_colum <- st_sfc(lapply(coords, function(x) sf::st_linestring( x = as.matrix(x)))) st_crs(geom_colum) <- 4326 df$geom <- geom_colum return(st_as_sf(df)) } km_to_deg <- function(km, latitude) { earth_radius <- 6371.0 deg_lat_length <- 2 * pi * earth_radius / 360 deg_lon_length <- deg_lat_length * cos(latitude * pi / 180) return(km / deg_lat_length) } make_grid <- function (xmin, ymin, xmax, ymax, cell_size){ # On converti la taille souhaité en degrés, sur la base de la latitude moyenne # de la grille cell_size <- km_to_deg(cell_size, (ymin + ymax) / 2) # la grille g <- c(xmin = xmin, ymin = ymin, xmax = xmax, ymax = ymax) |> st_bbox(crs = "epsg:4326") |> st_as_sfc() |> st_make_grid(cellsize = cell_size) g <- st_sf(id = 1:length(g), geom = g) return(g) } get_segments <- function(grid_sf, cell_size, stoken) { segments <- list() print(paste(length(row.names(grid_sf)), 'cellules à parcourir...')) # ajouter un buffer de 5% autour du poly grid_sf <- st_buffer(grid_sf, dist = sqrt(st_area(grid_sf[1,])) * .05) for (i in 1:nrow(grid_sf)) { bb <- st_bbox(grid_sf[i, ]) # [southwest corner latitude, southwest corner longitude, northeast corner latitude, northeast corner longitude] bounds <- paste(bb["ymin"], bb["xmin"], bb["ymax"], bb["xmax"], sep = ', ') print(paste("Récupération des segments dans la zone", bounds, '...')) res <- get_explore(stoken, bounds, activity_type = "running") segments <- c(segments, res$segments) } return(segments) } # Identification STRAVA # Insérer ses identifiants de clé pour l'API, voir ici https://www.strava.com/settings/api et renseigner les 3 variables ci-dessous app_name <- 'NomAppli' # Voir Appli API app_client_id <- '000000' # ID Généré par strava, app_secret <- 'xxxxxxxxxxxxxx' # Clé de l'API Strava # Pense à rafraîchir le token d'ID stoken <- get_token(app_name, app_client_id, app_secret) ### Préparation d'un grid sur Paris PC #### Grid global 12 km de côté #### # Prepare a grid within PARIS - REAL WORLD PROBLEM -> RUN this one only once to prepare the large grid, then save it for later. # grid_global <- make_grid(xmin = 2.14, ymin = 48.67, xmax = 2.61, ymax = 49.01, # cell_size = 5) #168 carreaux # # # # Selection within # grid_globalParis <- grid_global[ParisCom, , op = st_intersects] # # st_write(grid_globalParis, "strava_extract_Paris/gridglobalParis.geojson") #st_write(grid_global2, "strava_extract/gridglobal2.geojson") #st_write(grid_global_large_desert, "strava_extract/gridglobal_large_desert2.geojson") grid_global2 <- st_read("strava_extract_Paris/gridglobalParis.geojson") #Ici on charge le grid normal ou large #mapview(BoundingBox) + mapview(grid_global2) mapview(BoundingBox) + mapview(grid_global2) + mapview(sdf_result) #On ajoute les sdf results de l'itération précédente pour ne rien oublier # Doing 30 queries at once, if setting a timer every 6.5 sec will allow to stay below the 200 queries every 15 min. # we will however need to subselect the areas to query manually, each day, during 8 days. 2000 queries max a day. # L'enveloppe des petits grids est arrondie vers l'extérieur -> compte 30 cells par grille de 5 km -> 66 gd carreau par jour max. # Here we set the ID of cells we will do today selected_ids<- c(56,57,58,59,60,61,45,46,34,35,23,24) # A faire prochaine itération. # A Paris # 30/9 15:53 : 50,51,52,39,40,47,48,49,36,37,38,41, 25,26,27,28,29,30, 53,54,42,43,44,31,32,33, 14, 15, 16, 17, 18, 19, 20, 21, 22, 7, 9, 10, 86, 87, # 69, 70, 71, 72, 73, 74, 75, 76, 77, 63,64,65,66 select_cells <- subset(grid_global2, id %in% selected_ids) mapview(BoundingBox) + mapview(select_cells) # Vérifier la sélection # #### BOUCLE DE REQUETE SUR L'API STRAVA A PARTIR DU GRAND CARREAU for(i in selected_ids) { # Start for-loop print(i) start_time <- Sys.time() # Save starting time Sys.sleep(300) # Wait 6 min + (15 min =900s). 5 min = 300s. 1O0 queries / 15 min max -> send 90 queries every 5 min. # Boucle de requête. # 200 requêtes / 15 min # 2000 requêtes par jour bbox_large_cell <- subset(select_cells , id %in% i) bbox <- st_bbox(bbox_large_cell) print(paste("itération i=" , i)) print(bbox) cell_size <- 1 # en km. NB = 7 km pour grand grid xmin = as.numeric(bbox['xmin']) ymin = as.numeric(bbox['ymin']) xmax = as.numeric(bbox['xmax']) ymax = as.numeric(bbox['ymax']) # make grid à partir grand cellule selectionné grid <- make_grid(xmin = xmin, ymin = ymin, xmax = xmax, ymax = ymax, cell_size = cell_size) g1 <- mapview(bbox_large_cell) + mapview(grid) g1 # Récupération des segments via l'API Strava sur l'emprise de la grille segments <- get_segments(grid_sf = grid, cell_size = cell_size, stoken = stoken) # Conversion de l'ensemble des résultats obtenus sdf <- convert_segments_sf(segments) # g2 <- mapview(list(sdf),col.regions=list("red","blue"),col=list("red","blue")) + mapview(grid) # # g2 filenameresults <- paste0("strava_extract_Paris/result_", i, ".geojson") #filenameresults <- paste0("strava_extract/resultlarge_", i, ".geojson") st_write(sdf, filenameresults) filenamegrid <- paste0("strava_extract_Paris/grid_", i, ".geojson") # filenamegrid <- paste0("strava_extract/gridlarge_", i, ".geojson") st_write(grid, filenamegrid) # Sys.sleep(1000) # Wait 15 min + (15 min =900s) end_time <- Sys.time() # Save finishing time time_needed <- end_time - start_time # Calculate time difference print(paste("Step", i, "was finished after", time_needed, "min")) # Print time difference } ### RBIND CLEANUP AND MAP EVERYTHING # Assembler ensemble des fichiers grid normal # liste les fichiers list_files <- list.files("strava_extract_Paris/","result*", full.names = T) list_files n_files <- length(list_files) my_files <- vector("list", n_files) for (i in 1:n_files){ my_files[[i]] <- st_read(list_files[i]) } sdf_result <- do.call(rbind, my_files) # Assembler ensemble des fichiers grid large nord # liste les fichiers list_files <- list.files("strava_extract_Paris/","result*", full.names = T) list_files n_files <- length(list_files) my_files <- vector("list", n_files) for (i in 1:n_files){ my_files[[i]] <- st_read(list_files[i]) } sdf_large_result <- do.call(rbind, my_files) sdf_result <- rbind(sdf_result, sdf_large_result) sdf_result <- unique(sdf_result) # Remove duplicate lines mapview(ParisCom)+mapview(sdf_result) st_write(sdf_result, "strava_extract_Paris/sdf_result.geojson")