library(httr)
library(jsonlite)
library(tidyverse)
library(rvest)
library(oce)
<- function(lat, lon, API_key){
weather <- paste0("api.openweathermap.org/data/2.5/weather?lat=", lat, "&lon=", lon, "&appid=", API_key, "&units=metric")
url
<- GET(url)
rep
<- fromJSON(content(rep, "text"))
table
# The weather API don't provide moon phase so I compute it with Oce package
<- round(moonAngle(t = Sys.Date(),
moon_phase longitude = as.numeric(lon),
latitude = as.numeric(lat))$illuminatedFraction,
3)
<- data.frame(Air_temp = table$main$temp,
current.weather.info Weather = table$weather$main,
Atm_pres = table$main$pressure,
Wind_str = table$wind$speed,
Wind_dir = table$wind$deg,
Cloud_cover = table$clouds$all,
PrecipInt = ifelse(is.null(table$rains$`1h`), 0, table$rains$`1h`),
Moon = moon_phase)
return(current.weather.info)
}
Dans cet article précédent, j’ai présenté l’application Shiny que j’ai développée pour enregistrer les données de mes sessions de pêche. Dans cet article, je vais présenter brièvement les modifications et les mises à jour que j’ai apportées pour améliorer l’application. Voici les principaux changements :
API Météo : l’API que j’utilisais (Dark Sky) a arrêté de fournir des données gratuites. Je suis donc passé à Openweathermap et j’ai dû mettre à jour les fonctions pour récupérer les mêmes informations qu’avant.
Débit de la rivière : la première version de l’application ne collectait pas de données sur le débit de la rivière dans laquelle je pêche. Cependant, je suis convaincu que le débit de la rivière avant la session de pêche peut avoir un impact sur la présence de bar. J’ai donc créé une fonction de web scraping qui collecte les débits de la Nive et de l’Adour (les principales rivières où je pêche).
Collecte des données sur les leurres : dans la première version, les données sur les leurres utilisés n’étaient collectées que lorsqu’un poisson était capturé. Cependant, je n’avais pas de données sur le temps que je passais à utiliser un leurre avant de capturer un poisson. La nouvelle version de l’application collecte maintenant des données sur le leurre et la fréquence à laquelle je change de leurre.
API Météo
De petites modifications ont été apportées pour adapter la fonction météo précédente à la nouvelle API météo. Comme cette nouvelle API ne fournit pas de données sur la phase de la lune, j’ai décidé de calculer la phase de la lune avec le package oce :
Débit de la rivière
J’ai écrit des fonctions pour scrapper des informations sur les débits des rivières dans lesquelles je pêche le plus, sur un site web :
# Get and prepare the flow data
<- function(link){
get_Qdata <- fromJSON(content(GET(link), "text"))
table <- table$Serie$ObssHydro
table <- as.data.frame(table)
table $DtObsHydro <- sub("T", " ", table$DtObsHydro)
table$DtObsHydro <- substr(table$DtObsHydro, start = 1, stop = 19)
table<- data.frame(Date = seq.POSIXt(as.POSIXct(range(table$DtObsHydro)[1],'%m/%d/%y %H:%M:%S'),
ts as.POSIXct(range(table$DtObsHydro)[2],'%m/%d/%y %H:%M:%S'), by="hour"))
$DtObsHydro <- as.POSIXct(table$DtObsHydro, format = "%Y-%m-%d %H:%M:%S")
table
<- full_join(table, ts, by = c("DtObsHydro" = "Date")) %>% arrange(DtObsHydro)
table return(table)
}
# Main function to collect river flow
<- function(){
river_flow # Url of website to scrap:
<- "https://www.vigicrues.gouv.fr/services/station.json/index.php"
url_index
<- GET(url_index)
rep
<- fromJSON(content(rep, "text"))$Stations%>%
table_index na.omit()
# I need to add the flow of several rivers to get the flow of the rivers I am interested in:
<- table_index %>%
stations filter(LbStationHydro %in% c("Pontonx-sur-l'Adour", "St-Pandelon", "Artiguelouve", "Escos",
"Aïcirits [St-Palais]", "Cambo-les-Bains"))
<- "http://www.vigicrues.gouv.fr/services/observations.json?CdStationHydro="
base_url <- "&FormatDate=iso"
height_url <- "&GrdSerie=Q"
Q_url
<- stations %>%
stations mutate(WL_link = paste0(base_url, CdStationHydro, height_url),
Q_link = paste0(WL_link, Q_url))
<- lapply(stations$Q_link,
data_Q function(x){get_Qdata(x)})
<- suppressWarnings(Reduce(function(...) merge(..., all = TRUE, by = "DtObsHydro"),
data_Q
data_Q))
names(data_Q) <- c("Date", stations$LbStationHydro)
<- data_Q %>%
data_Q mutate(hour_of_day = format(Date, "%Y-%m-%d %H"))
<- aggregate(.~hour_of_day, data = data_Q, mean, na.rm = TRUE, na.action = na.pass)
data_Q
<- imputeTS::na_interpolation(data_Q, option = "linear")
data_Q
<- data_Q %>%
final_data mutate(Adour = `Pontonx-sur-l'Adour` + `Aïcirits [St-Palais]` + Artiguelouve + Escos + `St-Pandelon`,
Date = as.POSIXct(hour_of_day, tryFormats = "%Y-%m-%d %H")) %>%
select(Date, `Cambo-les-Bains`, Adour) %>%
rename(Nive = `Cambo-les-Bains`)
<- data.frame("Nive_c" = final_data[nrow(final_data), 2],
Cur_flow "Adour_c" = final_data[nrow(final_data), 3])
<- cbind(Cur_flow, final_data) %>%
final_data nest(Ts_flow = c(Date, Nive, Adour)) %>%
mutate(Ts_flow = paste(Ts_flow))
return(final_data)
}
Application Shiny
Un graphique simplifié de la nouvelle application est montré ci-dessous :
Côté UI
Le côté UI n’a pas beaucoup changé, j’ai seulement supprimé l’onglet qui affichait les données de pêche sur une carte car je n’utilisais pas beaucoup cette fonctionnalité :
# Load libraries
library(shiny)
library(shinyWidgets)
library(googlesheets)
library(miniUI)
library(leaflet)
library(rdrop2)
Sys.setenv(TZ="Europe/Paris")
#Import the functions for weather API and webscrapping
suppressMessages(source("api_functions.R"))
# Load the dropbox token :
<<- readRDS("token.rds")
token
# Minipage for small screens
<- miniPage(tags$script('$(document).ready(function () {
ui navigator.geolocation.getCurrentPosition(onSuccess, onError);
function onError (err) {
Shiny.onInputChange("geolocation", false);
}
function onSuccess (position) {
setTimeout(function () {
var coords = position.coords;
console.log(coords.latitude + ", " + coords.longitude);
Shiny.onInputChange("geolocation", true);
Shiny.onInputChange("lat", coords.latitude);
Shiny.onInputChange("long", coords.longitude);
}, 1100)
}
});'),
gadgetTitleBar("Catch them all", left = NULL, right = NULL),
miniTabstripPanel(
miniTabPanel("Session", icon = icon("sliders"),
miniContentPanel(uiOutput("UI_sess", align = "center"),
uiOutput("UI", align = "center"))
)
)
)
Côté serveur
Plusieurs changements ont été apportés du côté serveur pour collecter des données sur les leurres que j’utilise. Désormais, chaque fois que je change de leurre, je remplis un petit formulaire pour collecter les caractéristiques du leurre et cela ajoute une ligne dans un troisième fichier csv :
<- function(input, output, session){
server
observeEvent(input$go ,{
# Read the csv file containing information about fishing session. If a session is running,
# display the UI that allows the user to input data about the fish caught. If a session is not started,
# display a button to start the session and small survey on lure characteristics.
<<- drop_read_csv("/app_peche/session1.csv", header = T, stringsAsFactors = F, dtoken = token)
dat
# Reactive UI
$UI <- renderUI({
output
if(!is.na(rev(dat$End)[1])){
# We now indicate what type of lure we use at the beginning of the session:
tagList(
selectInput("lure1",
label = "Type de leurre",
choices = list("Shad" = "shad",
"Slug" = "slug",
"Jerkbait" = "jerkbait",
"Casting jig" = "jig",
"Topwater" = "topwater"),
selected = "shad",
selectize = FALSE),
selectInput("color_lure1",
label = "Couleur du leurre",
choices = list("Naturel" = "naturel",
"Sombre" = "sombre",
"Clair" = "clair",
"Flashy" = "flashy" ),
selected = "naturel",
selectize = FALSE),
selectInput("length_lure1",
label = "Taille du leurre",
choices = list("Petit" = "petit",
"Moyen" = "moyen",
"Grand" = "grand"),
selected = "petit",
selectize = FALSE),
actionButton("go","Commencer session !"))
else{
}
tagList(actionButton("go","End session"))
}
})
$UI_sess <- renderUI({
output
if(!is.na(rev(dat$End)[1])){
tagList(textInput("comments", label = "Commentaire avant le début?", value = "NA"))
else{
}$catch
input$lure
inputtagList(
selectInput("lure_type",
label = "Type de leurre",
choices = list("Shad" = "shad",
"Slug" = "slug",
"Jerkbait" = "jerkbait",
"Casting jig" = "jig",
"Topwater" = "topwater"),
selected = "shad",
selectize = FALSE),
selectInput("color_lure",
label = "Couleur du leurre",
choices = list("Naturel" = "naturel",
"Sombre" = "sombre",
"Clair" = "clair",
"Flashy" = "flashy" ),
selected = "naturel",
selectize = FALSE),
selectInput("length_lure",
label = "Taille du leurre",
choices = list("Petit" = "petit",
"Moyen" = "moyen",
"Grand" = "grand"),
selected = "petit",
selectize = FALSE),
actionButton("lure",
label = "Changer de leurre!"),
br(),
br(),
h4("Ajouter une capture"),
selectInput("species",
label = "Espèces",
choices = list("Bar" = "bar",
"Bar moucheté" = "bar_m",
"Alose" = "alose",
"Maquereau" = "maquereau",
"Chinchard" = "chinchard"),
selected = "bar"),
sliderInput("length",
label = "Taille du poisson",
value = 25,
min = 0,
max = 80,
step = 1),
actionButton("catch","Rajoutez cette capture aux stats!"),
br(),
br(),
textInput("comments1", label = h4("Commentaire avant la fin ?"), value = "NA")
)
}
})ignoreNULL = F)
},
#If the button is pushed, create the line to be added in the csv file.
observeEvent(input$go,{
# Two outcomes depending if the session starts or ends. This gives the possibility
# to the user to add a comment before starting the session or after ending the session
if(!is.na(rev(dat$End)[1])){
#Tide + geoloc + Weather
<- tide()
c_tide <- c(input$lat,input$long)
geoloc <- weather(lat = geoloc[1], lon = geoloc[2])
current.weather.info <- river_flow()
river.flow
<- c(rev(dat$Session)[1] + 1)
n_ses <- as.character(as.POSIXct(Sys.time()))
time_beg <- input$comments
comment <<- cbind(data.frame(n_ses,
dat.f
time_beg,NA,
2],
geoloc[1]),
geoloc[
current.weather.info,
c_tide,
river.flow,
comment)names(dat.f) <- names(dat)
print(dat.f)
<- rbind(dat, dat.f)
final_dat
<- drop_read_csv("/app_peche/lure.csv",
lure header = T,
stringsAsFactors = F,
dtoken = token)
<- data.frame(n_ses = n_ses,
new_lure time = as.character(as.POSIXct(Sys.time())),
type_lure = input$lure1,
color_lure = input$color_lure1,
length_lure = input$length_lure1)
<- rbind(lure,
new_df
new_lure)
write_csv(as.data.frame(new_df), "lure.csv")
drop_upload("lure.csv", path = "App_peche", mode = "overwrite", dtoken = token)
else{
}
$End[nrow(dat)] <- as.character(as.POSIXct(Sys.time()))
dat$Comments[nrow(dat)] <- paste(dat$Comments[nrow(dat)], "/", input$comments1)
dat<- dat
final_dat
}
# Write csv in temporary files of shiny server
write_csv(as.data.frame(final_dat), "session1.csv")
# Upload it to dropbox account
drop_upload("session1.csv", path = "App_peche", mode = "overwrite", dtoken = token)
})
# Add a line to the catch csv file whenever a fish is caught
observeEvent(input$catch,{
<- drop_read_csv("/app_peche/catch1.csv", header = T, stringsAsFactors = F, dtoken = token)
caugth
<- data.frame(n_ses = dat$Session[nrow(dat)],
catch time = as.character(as.POSIXct(Sys.time())),
species = input$species,
length = input$length)
<- rbind(caugth,catch)
b
write_csv(as.data.frame(b), "catch1.csv")
drop_upload("catch1.csv", path = "App_peche", mode = "overwrite", dtoken = token)
})
observeEvent(input$lure,{
<- drop_read_csv("/app_peche/lure.csv",
lure header = T,
stringsAsFactors = F,
dtoken = token)
<- data.frame(n_ses = dat$Session[nrow(dat)],
new_lure time = as.character(as.POSIXct(Sys.time())),
type_lure = input$lure_type,
color_lure = input$color_lure,
length_lure = input$length_lure)
<- rbind(lure,
new_df
new_lure)
write_csv(as.data.frame(new_df), "lure.csv")
drop_upload("lure.csv", path = "App_peche", mode = "overwrite", dtoken = token)
}) }
Conclusion
J’ai testé cette nouvelle application lors de deux sessions de pêche et elle fonctionne à merveille. J’ai hâte de vous présenter mes résultats à la fin de cette saison de pêche !