Agriculture de précision
en langage R
L'essentiel de cette page
Ce guide "Agriculture de précision en langage R" présente les étapes essentielles pour l'utilisation de R dans le domaine de l'agriculture de précision. Il aborde les points suivants :
0. Préparation des données : importation de données captées par drone et de données satellitaires via Pix4Dfields.
Chargement des données : utilisation de la bibliothèque {terra} pour charger les données dans des objets raster.
Calcul des indices : calcul des indices VARI, TGI et NDWI à partir des données raster.
Regroupement des données : utilisation de l'ACP et du clustering hiérarchique pour identifier les types de zones dans la parcelle.
Suivi de la saisonnalité : création de graphiques pour visualiser l'évolution d'un indice en fonction des mois de l'année.
Suivi de l'évolution du "climat" : analyse de l'évolution de l'indice sur plusieurs années et prédiction de sa valeur future à l'aide d'une régression linéaire.
0- Préparer les données - ici avec Pix4Dfiels
Pas de traitement de données sans données.
Ici l'on présentera des données captées par Drone ainsi que des données satellitaires importées grâce au logiciel pix4Dfields.
1- Charger les données de l'orthomozaïque et les données satellitaires dans R
Nous allons ouvrir les données satellitaires dans un objet raster r1 et les données orthomozaïques du drone dans un objet raster r0_ortho.
library(terra)
# Données Satellite - basse résolution
r0 <- terra::rast(file.choose()) # 2023-05-15, Délimitation 2, NDVI.data.tif
r1 <- rectify(r0) # Faire une rotation du raster si message d'alterne rectify à l'ouverture
# Attention, Pix4Dfields fournit bcp d'images, assurez-vous d'avoir chargé l'image à plusieurs bandes en faisant names(r1)
# Données Drone - très grande résolution
r0_ortho <- terra::rast(file.choose()) # Orthomosaïque.data.tif
# AFFICHAGE EN COULEURS NATURELLES
names(r0)
terra::plotRGB(r0_ortho,1,2,3) # 1, 2, 3 ordre des couches rgb
terra::plotRGB(r1*1810,3,2,1) # ne marche pas car les couches ne sont pas codés de 0 à 255, il faut donc multiplier par 255/max(value)
Optionnellement, je peux aussi forcer le raster à raisonner en longitude-latitude pour pouvoir ajouter des points de repères où le placer sur une carte.
# Définir la nouvelle projection
new_crs <- "+proj=longlat +datum=WGS84"
r1_long_lat<- project(r1, new_crs) # terra
Pour se simplifier la vie, on ne va pas le faire ici.
Remarque importante : la fonction res() permet d'avoir la dimension en mètres des pixels. Cela ne sert à rien d'avoir une résolution au-delà de 5 mètres si l'on souhaite faire un traitement personnalisé avec un gros tracteur.
res(r1) # 0.01679497 0.01679497 Mes pixels ont une précision de 1,6 cm. Ca va loin...
5/res(r0_ortho) # Cela donne le facteur de réduction que je peux appliquer à mon raster (entre 200 et 300 ici)
On se retrouve maintenant avec un raster satellitaire de basse résolution et une orthomozaïque drone trop lourd à manipuler. On va donc compresser le dernier et augmenter la résolution du premier.
dim(r1) # 64 79 11 - mesures à 11 lambdas, image de 64 sur 79 pixels.
dim(r0_ortho) # 19911 25257 4 # Seulement 3 mesures (rgb + masque de la zone binaire) : on peut réduire facilement d'un facteur 200
# Aggreger le raster drone
facteur <- 200 # A ajuster si on ne veut pas trop perdre
r1_ortho <- aggregate(r0_ortho, facteur, fun = median) # Ca va être long, car il faut faire la médiane sur chaque carré de 200 pixels sur 200
dim(r1_ortho)
# Resample le raster satellitaire à la résolution du raster drone
r2 <- resample(r1, r1_ortho, method = "bilinear")
# Affichage
layout(matrix(1:2,2,1)) ; terra::plotRGB(r1_ortho,1,2,3) ; terra::plotRGB(r2*1818,3,2,1) # 1818 car les données satellitaires ont une échelle basse (255/max)
Convertissons maintenant toutes les donnée des deux rasters en un tableau data.frame. Il sera possible sur ce tableau de faire des ACP et autres calculs, mais c'est peut-être plus pertinent de faire directement un calculs des indices.
# Mise au format tableau et fusion
df1 <- as.data.frame(r2,xy=TRUE)
df2 <- as.data.frame(r1_ortho,xy=TRUE)
# Fusionner les dataframes en utilisant les colonnes x et y comme clés de fusion
df <- merge(df1, df2, by = c("x", "y"))
df <- na.omit(df) # suppression des lignes sans données
colnames(df)
df[-ncol(df)]-> df # étape optionnelle : suppression de la dernière colonne (inutile : masque de la zone)
colnames(df)[c(14,15,16)] <- c("Red_ortho","Green_ortho","Blue_ortho")
2- Calculer les indices nécessaires à établir un diagnostic agronomique
Il existe de nombreux indices en télédétections, certains sont plus courants, tels de VARI (Couverture végétal), le TGI (Charge chlorophyllienne) et NDWI (stress hydrique). Leurs formules peuvent hélas varier pour certains d'une publication à l'autre :
VARI (Visible Atmospherically Resistant Index), toujours la même formule auto-normalisante : VARI = (g- r) / (g+ r– b). Cet indice varie entre -1 et +1. Les valeurs négatives indiquent une faible vigueur de la végétation.
TGI (Triangular Greenness Index) - il n'est pas autonormalisant car il calcule l'aire d'un triangle (cf. figure ci-devant) qui dépend de l'échelle. On trouve souvent celle-ci : -0.5*(190*(r-v) - 120*(r-b)) qui va donner des TGI très différentes d'une image à l'autre, mais pix4Dfields utile celle-ci pour normaliser son TGI d'une image à l'autre (g -0.39 * r -0.61 * b)/max(c(r,v,b)). Cet indice varie entre -1 et +1. Les valeurs négatives une faible teneur en chlorophylle de la végétation.
NDWI (Normalized Difference Water Index) utilise une formule auto-normalisante qui va permettre de comparer des images entre-elles. NDWI = (g - nir)/(g + nir). Cet indice varie entre -1 et +1. Les valeurs négatives indiquent un stress hydrique.
Voici quelques formules qui vont nous permettre de calculer rapidement ces indices à partir de rasters.
Calcul du TGI : l'aire de ce triangle (source)
Si vos images Pix4Dfields n'ont pas des noms correspondant à ceux indiqués, voici une légende des 11 bandes présentes dans l'ordre :
"Blue" "Green" "Red" "Red edge" "Red edge" "Red edge" "NIR" "NarrowNIR" "SWIR" "SWIR" "Alpha"
# VARI
vari<- function(r,g,b) {
if (is(r)[1] == "SpatRaster") {
vari <- clamp(((g - r) / (g+ r - b)),-1,1)# Version pix4Dfields normalisée entre -1 et 1
} else {
vari <- (g - r) / (g+ r - b)
}
return(vari)
}
# TGI
tgi <- function(r,g,b) {
rr <- as.data.frame(r) ; gg <- as.data.frame(g) ; bb <- as.data.frame(b)
# Normaliser le pixel pour normaliser le TGI
tgi <- (g-0.39*r-0.61*b)/max(apply(cbind(rr,gg,bb),2,max))
#bbb <- -0.5*(190*(r-v) - 120*(r-b))#/max(c(r,v,b))
#print(cor.test(as.data.frame(tgi)[,1],as.data.frame(bbb)[,1]))
#plot(as.data.frame(tgi)[,1],as.data.frame(bbb)[,1])
return(tgi)
}
# NDWI
ndwi <- function(g, nir) {
ndwi<- (g - nir)/(g + nir)
return(ndwi)
}
# On notera que certains suggèrent de faire un NDWI/NDMI avec cette formule (NIR - SWIR) / (NIR + SWIR)
On peut ainsi calculer ces indices directement à partir des raster, mais il est plus simple de les appliquer sur data.frame (encadré suivant).
r2_vari <- vari(r2[["Red"]],r2[["Green"]],r2[["Blue"]])
r1_ortho_vari <- vari(r1_ortho[[1]],r1_ortho[[2]],r1_ortho[[3]])
# Affichage
layout(matrix(1:2,2,1)) ; plot(r1_ortho_vari,main="Drone") ; plot(r2_vari,main="Satellite")
Fait intrigant qui mériterait qu'on s'y penche, le VARI de l'image drone (en haut) ne donne pas le même VARI pour l'image satellite (en bas). On comprend que ces images sont bien différentes, mais laquelle garder ?
Le plus simple est toujours de travailler avec les données en tableurs.
# Reprenons la data.frame df ci-dessus.
colnames(df)
col_vari <- vari(df$Red,df$Green,df$Blue)
col_vari2 <- vari(df$Red_ortho,df$Green_ortho,df$Blue_ortho)
col_tgi <- tgi(df$Red,df$Green,df$Blue)
col_tgi2 <- tgi(df$Red_ortho,df$Green_ortho,df$Blue_ortho)
col_ndwi <- ndwi(df$Green,df$NIR)
col_ndwi2 <- ndwi(df$Green_ortho,df$NIR*255/max(df$NIR))
df_indices <- data.frame(col_vari,col_vari2,col_tgi,col_tgi2,col_ndwi,col_ndwi2)
Remarque importante : il existe d'autres indices peut être nettement plus pertinents, pour en citer certains :
Normalized Difference Vegetation Index (NDVI) : Utilisé pour estimer la biomasse, l'indice foliaire (LAI) et l'activité photosynthétique dans les prairies mixtes et les chaparrals en Californie du Sud, avec une taille de pixel optimale de 6 m ou moins pour des études fonctionnelles (Rahman et al., 2003). On peut aussi inclure le NDVI2 certainement plus pertinent.
Renormalized Difference Vegetation Index (RDVI) : Comparaison avec d'autres indices pour l'estimation de l'indice foliaire (LAI) dans des écosystèmes de prairies mixtes, montrant une légère amélioration par rapport aux autres indices (He et al., 2006).
Adjusted Transformed Soil-Adjusted Vegetation Index (ATSAVI) : Utilisé pour améliorer l'estimation du LAI dans les prairies, particulièrement lorsqu'il est combiné avec l'indice d'absorption de cellulose (CAI) pour un nouvel indice (L-ATSAVI), augmentant la capacité d'estimation de l'AGB d'environ 10% (He et al., 2006).
Water Band Index (WBI) : employé comme indicateur de contenu en eau des plantes dans les prairies et les chaparrals, avec des analyses de mise à l'échelle spatiale pour optimiser les procédures d'échantillonnage (Rahman et al., 2003).
Photochemical Reflectance Index (PRI) : Utilisé pour les études de la biomasse, du contenu en eau des plantes et de l'activité photosynthétique (Rahman et al., 2003).
Modified Chlorophyll Absorption Ratio Index 2 (MCARI2) : Performant pour estimer le LAI dans les prairies mixtes (He et al., 2006).
Pure Vegetation Index (PVI) : Un index extrait du VI normal pour minimiser les effets du sol, améliorant ainsi les estimations de l'AGB des prairies (Li et al., 2016).
Cellulose Absorption Index (CAI) : Utilisé en combinaison avec ATSAVI pour améliorer les estimations de LAI en tenant compte de la matière morte au sol (He et al., 2006).
Soil Adjusted Total Vegetation Index (SATVI) : Développé pour estimer la couverture végétale totale, la hauteur et la biomasse dans les prairies arides (Marsett et al., 2006).
3- Combiner les indices pour réaliser un regroupement : identifier les types de zones de la parcelle
3.1- ACP - réduire les indices à quelques composantes - optionnel mais utile si beaucoup d'indices
apply(df_indices,2,function(x){any(is.na(x))}) # Vérifier qu'une donnée manquante ne s'est pas glissée dans le tableau
myacp <- prcomp(na.omit(df_indices),center=T,scale=T)
library(KefiR) # Pour installer KefiR
pareto(myacp$sdev)
biplt(myacp,labels="",cex=0.2)
library(rgl)
plot3d(myacp$x[,1:3])
pareto() de {KefiR}
Ce Pareto montre que l'on peut réduire les 6 indices à 3-4 composantes
biplt() de {KefiR}
Ce biplot() réalisé avec {KefiR} permet de voir la dispersion des points en nuages et les variables VARI et NDWI qui jouent le plus.
plot3d() de {rgl}
Ce graphique 3D permet de voir les groupes de pixels sur la parcelle : il faut envisager de les identifier (regroupement) et les caractériser.
On peut aussi transformer les résultats de l'ACP pour reproduire une image.
# install.packages("tidyterra")
library(tidyterra)
tablo <- cbind(df[-which(apply(df_indices, 1, function(x) {any(is.na(x))})),1:2],myacp$x[,1:3])
tablo[,3:5] <- apply(tablo[,3:5] ,2,function(x) {(x-min(x))/(max(x)-min(x))*255})
myraster <- as_spatraster(tablo)
terra::plotRGB(myraster*50,1,2,3)
terra::plotRGB(myraster*50,1,1,1) # Que la composante 1
3.2- Faire du regroupement - pour identifier les zones à diagnostiquer
Faire du regroupement, c'est identifier les zones sans avoir les réponses à l'avance. Il existe de nombreuses techniques :
A titre d'exemple, utilisont le CAH pour déterminer le nombre de zones dans la parcelle.
myclust <- hclust(dist(df_indices), method="ward.D2")
#plot(myclust)
inertie <- sort(myclust$height, decreasing = TRUE)
plot(inertie[1:20], type = "s", xlab = "Nombre de classes", ylab = "Inertie",lwd=2);grid()
k <- 3 # Nombre de catégories à paramétrer manuellement selon le graphique
abline(v=k,col="red",lty=3)
points(k,inertie[k],pch=16,cex=2,col="red")
Les 2 grands bons à 3 ou 4 catégories suggèrent que l'on peut avec pertinence travailler sur 3 ou 4 types de zones (l'indication en rouge correspond ici à 3 catégories).
Remarque : il ne faut pas hésiter à centrer-réduire les données avant avec scale()
Les comparaisons des images montre que 4 zones est assez pertinent.
cutree(myclust, k=3) # 3 catégories
raster_catego <- rast(data.frame("x"=df$x,"y"=df$y,"z"=cutree(myclust, k=3)))
x11()
plot(raster_catego)
On voit ici, les cinq zones identifiées sur la parcelle, mais avec pas mal d'erreur sur les bords.
En mettant en place un mélange de gaussiennes, on va être plus fin pour identifier les zones qui correspondent à la réalité du terrain.
Le CAH aura permis de fixer que G=4.
library(mclust)
df_indices <- cbind("x"=df$x,"y"=df$y,df_indices)
df_indices <- na.omit(df_indices)
Mclust(df_indices[,-c(1,2)], G = 4) -> my_mclust
summary(my_mclust)
names(my_mclust)
raster_catego <- rast(data.frame("x"=df_indices$x,"y"=df_indices$y,"z"=my_mclust$classification))
plot(raster_catego)
Une classification (et non un regroupement) va permettre d'essayer de reprédire ces zones uniquement en fonction de leur position x ou y. On va ainsi faire un lissage.
On peut par exemple faire un SVM ou un dbscan. Le SVM est performant et gagnera en finesse avec le paramètre cross.
# Ajuste un modèle SVM en utilisant
model <- svm(as.factor(my_mclust$classification)~ ., df_indices[,1:2], cross = 10)
# Utilise le modèle pour prédire les valeurs de la variable dépendante pour toutes les observations, sauf la variable dépendante elle-même
mypred <- predict(model, subset(df_indices[,1:2]))
raster_catego <- rast(data.frame("x"=df_indices$x,"y"=df_indices$y,"z"=as.numeric(mypred)))
# Définit les couleurs
my_colors <- c("red", "blue", "green", "yellow")
# Plot le raster avec les couleurs définies
plot(raster_catego, col=my_colors)
On peut recoloriser les zones pour clarifier leur identification.
La technique SVM permet de dégager les zones trop petits, de faire ainsi un lissage.
df_indices_categories <- cbind(df_indices,mypred)
#install.packages("rpart.plot")
#install.packages("rpart")
library(rpart)
# Construire un modèle des catégories 1, 2, 3
# La 4 est sortie car correspondant aux zones de bordures
model<-rpart(mypred~.,data=df_indices_categories[mypred!=4,-c(1:2)] ,control=rpart.control(minsplit = 9,cp=0))
plot(model)
# Simplification du modèle
model_pruned <- prune(model, cp = 0.01)
plot(model_pruned)
library(rpart.plot)
rpart.plot(model_pruned)
On voit ainsi que la catégorie 3 présente un indice NDWI (celui déterminé à partir des données du drone) inférieur à -0.66. Cela implique que cette zone présente un fort stress hydrique qui justifiera qu'on lui donne plus d'eau (ou qu'on change d'autres modalités de culture). Au passage, on notera que les zones considérées en catégorie 3 par cet arbre ne le seront effectivement qu'à 89% (11% seront en réalité de la catégorie 1). On peut toujours affiner un tel arbre en faisant des forêts aléatoires qui permettront de ne conserver que les indices les plus influents avant de recommencer le cycle regroupement(catégorisation) des étapes ci-avant.
Pour séparer la catégorie 1 de la catégorie 2, seul l'indice VARI (celui déterminé par drone) a été retenu. Il permet de décrire la zone 1 comme présentant une faible vigueur de végétation. Sans surprise, la catégorie 2 représente de la végétation plus verte et plus vigoureuse.
Il faudrait donc, en théorie, faire maintenant appel à un.e expert.e pour proposer des traitements différenciés.
Les données compilées en raster peuvent être exportées en tiff, recompilées avec pix4Dfield pour permettre d'automatiser le travail d'un tracteur.
On peut envisager d'importer un raster zonal (importation > indice) dans pix4Dfields pour retraiter les données.
writeRaster(raster_catego, "raster_catego_new1.tif")
Solution peut-être plus simple, l'exporté en shape pour l'ouvrir avec QGIS :
sortie1 <- raster::raster(raster_catego)
sortie2 <- raster::rasterToPolygons(sortie1, dissolve=TRUE)
plot(sortie2,col=1:4)
raster::shapefile(sortie2, "votre_shapefile.shp")
raster::shapefile(sortie2, "votre_shapefile.shp",overwrite=TRUE)
On peut aussi directement traiter les données statistiquement et graphiquement sous R
Bilan statistique
colnames(df_indices_categories)
bilan <- aggregate(df_indices_categories[,c(4)],by=list(df_indices_categories$mypred),summary)
bilan2 <- t(bilan[,-1]) ; colnames(bilan2) <- bilan[,1] ; print(round(bilan2[,-4],2)) # Indie VARI (colonne 4)
1 2 3
Min. -0.04 0.02 0.00
1st Qu. 0.06 0.13 0.06
Median 0.07 0.15 0.08
Mean 0.07 0.15 0.08
3rd Qu. 0.08 0.17 0.09
Max. 0.13 0.23 0.14
Etudier une variable en fonction des zones
boxplot(df_indices_categories$col_vari~df_indices_categories$mypred,xlab="zones",ylab="VARI")
Etudier deux variables en fonction des zones (voire 3 en plot3D())
plot(df_indices_categories$col_vari,df_indices_categories$col_ndwi2,
col=df_indices_categories$mypred,cex=0.1,pch=16,xlab="VARI satellite",ylab="NDWI drone") ; grid()
Analyser l'ensemble des variables pour les zones (ici 3 variables)
layout(matrix(1:3,1,3)) ; library(vioplot)
vioplot(df_indices_categories$col_vari~df_indices_categories$mypred,xlab="zones",ylab="VARI",col="red")
vioplot(df_indices_categories$col_tgi~df_indices_categories$mypred,xlab="zones",ylab="TGI",col="yellow")
vioplot(df_indices_categories$col_ndwi~df_indices_categories$mypred,xlab="zones",ylab="NDWI",col="cyan")
4- Suivre la saisonnalité
Pour construire un graphique qui l'évolution d'un indice en fonction des mois de l'année, il faut procéder en plusieurs étapes :
Charger toutes les images TIFF d'un dossier (type ndvi exportés depuis Pix4Dfields)
Trier les images TIFF pour ne garder que celles de l'année écoulée.
Calculer l'indice de chaque TIFF, puis en déduire la médiane
Tracer le graphique
library(terra)
setwd("G:/Drone/Export/La Forge/NDVI/La Forge") # Mettre l'adresse de son dossier
tif_files <- list.files(pattern = "Délimitation 2.data.tif$", full.names = TRUE) # Ouvre tous les tif terminant par Délimitation 2.data.tif
colnames(tif_files)
Cette image illustre le suivi de l'indice NDWI sur une année à titre d'exemple. Un autre indice (ou plusieurs) peut s'avérer pertinent.
On peut aussi (recommandé), étudier plusieurs indices sur le même graphique en traçant différentes courbes en couleur (à légender).
Charger tous les TIFF
# Initialiser des vecteurs pour stocker les mois et les années
dates <- c()
months <- numeric(length(tif_files))
years <- numeric(length(tif_files))
days <- numeric(length(tif_files))
# Pour chaque fichier .tif
for (i in seq_along(tif_files)) {
# Extraire la date du nom du fichier
file_name <- basename(tif_files[i])
date_str <- substr(file_name, 1, 10)
# Convertir la date en objet Date
date <- as.Date(date_str)
# Extraire le mois et l'année de la date
dates <- c(dates,as.character(format(date, "%Y-%m-%d")))
days[i] <- as.numeric(format(date, "%d"))
months[i] <- as.numeric(format(date, "%m"))
years[i] <- as.numeric(format(date, "%Y"))
}
Ne conserver que les TIFF de l'année écoulée
# Nettoyer la liste de TIFF du dossier pour ne garder que ceux de l'année en cours
dates <- strptime(dates,format="%Y-%m-%d")
indices <- which(as.Date(dates) >= as.Date("2022-05-31")) # Ici on ne garde que les TIFF ayant dépassé mai 2022
tif_files <- tif_files[indices]
Calculer les indices pour chaque TIFF puis faire la médiane pour chaque TIFF
# Initier les listes des valeurs extraites de chaque TIFF
dates <- c()
months <- numeric(length(tif_files))
years <- numeric(length(tif_files))
days <- numeric(length(tif_files))
indice <- numeric(length(tif_files))
# Pour chaque tif de l'année en cours
for (i in seq_along(tif_files)) {
# Ouvrir le fichier .tif en tant qu'objet raster
r0 <- terra::rast(tif_files[i])
r1 <- rectify(r0)
# Calcule de l'indice du TIFF (ici NDWI)
indice.temp <- ndwi(r1[["Green"]],r1[["NIR"]])
# Extraire la date du nom du fichier
file_name <- basename(tif_files[i])
date_str <- substr(file_name, 1, 10)
# Convertir la date en objet Date
date <- as.Date(date_str)
# Extraire le mois et l'année de la date
dates <- c(dates,as.character(format(date, "%Y-%m-%d")))
days[i] <- as.numeric(format(date, "%d"))
months[i] <- as.numeric(format(date, "%m"))
years[i] <- as.numeric(format(date, "%Y"))
# Calcule de l'indice médian de l'image
indice[i] <- median(as.data.frame(indice.temp[[1]])[,1],na.rm=T)
}
Tracer le graphique
dates <- strptime(dates,format="%Y-%m-%d")
plot(dates,indice,type="o",axes=F,lwd=2) ; grid()
# Abscisses exprimant dans ses graduations uniquement le nom des mois
axis.POSIXct(1, at=seq(from=strptime("01/06/2022","%d/%m/%Y" ), to=strptime("31/05/2023","%d/%m/%Y" ), by="month"), format="%b", las=2) # force indiquant la date de chaque jour
# Ordonnées
axis(2,seq(from = -1, to=1,by=0.1))
5- Suivre l'évolution du "climat" (il faudrait en théorie 30 ans)
Suivre une éventuelle évolution du climat. Pour l'instant, les bases de données ne couvrent qu'une dizaine d'année (2023), mais cela va progresser.
Il faut charger tous les TIFF du mois de l'analyse sur un maximum d'année.
L'étude du champ ici montre une hausse de l'indice VARI depuis 2017 (en cinq ans), et une légère baisse de l'indice NDWI. Le graphique ici n'affiche que l'évolution du VARI. Rien n'exclue d'étudier l'évolution de plusieurs indices.
library(terra)
setwd("G:/Mon Drive/IUT/statistiques/SAE_AGRO/Drone/Export/La Forge/NDVI/La Forge")
tif_files <- list.files(pattern = "Délimitation 2.data.tif$", full.names = TRUE)
# Initialiser des vecteurs pour stocker les mois et les années
months <- numeric(length(tif_files))
years <- numeric(length(tif_files))
days <- numeric(length(tif_files))
indice <- numeric(length(tif_files))
dates <- c()
# Pour chaque fichier .tif
for (i in seq_along(tif_files)) {
# Ouvrir le fichier .tif en tant qu'objet raster
r0 <- terra::rast(tif_files[i])
r1 <- rectify(r0)
indice.temp <- vari(r1[["Red"]],r1[["Green"]],r1[["Blue"]])
#indice.temp <- ndwi(r1[["Green"]],r1[["NIR"]])
# Extraire la date du nom du fichier
file_name <- basename(tif_files[i])
date_str <- substr(file_name, 1, 10)
# Convertir la date en objet Date
date <- as.Date(date_str)
# Extraire le mois et l'année de la date
dates <- c(dates,as.character(format(date, "%Y-%m-%d")))
days[i] <- as.numeric(format(date, "%d"))
months[i] <- as.numeric(format(date, "%m"))
years[i] <- as.numeric(format(date, "%Y"))
indice[i] <- median(as.data.frame(indice.temp)[,1],na.rm=T)
}
Tracer le graphique et analyser la régression
dates <- strptime(dates,format="%Y-%m-%d")
synth <- data.frame("dates"=as.numeric(dates),"indice"=indice)
plot(dates,indice,axes=F,data=synth)
reg <- lm(indice~as.numeric(dates))
summary(reg)
abline(reg,col="red",lwd=3)
axis.POSIXct(1, at=seq(from=strptime(dates[1],"%Y-%m-%d" ), to=strptime(dates[length(dates)],"%Y-%m-%d" ), by="year"), format="%Y", las=2) # force indiquant la date de chaque jour
# Ordonnées
axis(2,seq(0,1,by=0.1))
Call:
lm(formula = indice ~ as.numeric(dates))
Residuals:
Min 1Q Median 3Q Max
-0.26641 -0.11099 -0.04687 0.11155 0.45676
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -2.562e+00 7.002e-01 -3.660 0.000599 ***
as.numeric(dates) 1.565e-09 4.312e-10 3.629 0.000659 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.1601 on 51 degrees of freedom
Multiple R-squared: 0.2052, Adjusted R-squared: 0.1896
F-statistic: 13.17 on 1 and 51 DF, p-value: 0.0006588
Permettons nous une prédiction. Comment sera l'indice VARI en 2030 ?
Actuellement, lors de cet étude, il est de 0.21 (mai 2023), mais il devrait passer à 0.42 en mai 2030.
Si j'applique le même raisonnement en changeant d'indice, je trouve une évolution moins significative du NDWI qui devrait le faire passer de -0.62 à -0.67. Cela permet de diagnostiquer que, sans surprise, le stress hydrique semble à la hausse, mais que, pour l'instant, la végétation profite de la hausse des températures* (*Attention diagnostic sur seulement cinq ans).
median(df_indices_categories$col_vari) # Mai 2023, mois de l'étude
Prédire sa valeur pour une date.
date_to_predict <- "2030-05-30"
predict(reg,data.frame("dates"=as.numeric(strptime(date_to_predict,format="%Y-%m-%d"))))