diff --git a/.gitignore b/.gitignore index a566227..b3976b5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ docs inst/doc /doc/ /Meta/ +.quarto diff --git a/DESCRIPTION b/DESCRIPTION index 1392941..9d7c697 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -32,7 +32,7 @@ Imports: ncdf4 Encoding: UTF-8 LazyData: true -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.1 URL: https://github.com/RobelTakele/AquaBEHERgui, https://robeltakele.github.io/AquaBEHERgui/ BugReports: https://github.com/RobelTakele/AquaBEHERgui/issues diff --git a/R/app_server.R b/R/app_server.R index ab170b1..ccf441e 100644 --- a/R/app_server.R +++ b/R/app_server.R @@ -1716,6 +1716,765 @@ data.pet <- pet.ncData[[startdate.SWBindex:enddate.SWBindex]] leaflet::leafletProxy("spSeasCALmap", session) %>% leaflet.extras2::easyprintMap(sizeModes = input$spWSCscene, filename = input$spWSCmapFileN) }) + +################################################################################################### +################################################################################################### + + # ***** Forecast + + rainTerc_dataInput <- reactive({ + req(input$rainTerc_xlsxInput) + openxlsx::read.xlsx(input$rainTerc_xlsxInput$datapath, sheet = 1) + }) + + sesRain_dataInput <- reactive({ + req(input$sesRain_xlsxInput) + openxlsx::read.xlsx(input$sesRain_xlsxInput$datapath, sheet = 1) + }) + + fcstVarDF_dataInput <- reactive({ + req(input$fcstVarDF_xlsxInput) + openxlsx::read.xlsx(input$fcstVarDF_xlsxInput$datapath, sheet = 1) + }) + + + seasFCST_table <- eventReactive(input$FCST_runButton, { + + rainTerc_dat <- rainTerc_dataInput() + sesRain_dat <- sesRain_dataInput() + fcstVarDF_dat <- fcstVarDF_dataInput() + + seasFCSTdata.lst <- AquaBEHER::fcstWSC(sesRain = sesRain_dat, + var.dF = fcstVarDF_dat, + rainTerc = rainTerc_dat, + variable = as.character(input$FCSTvar)) + + seasFCSTdata <- data.frame(seasFCSTdata.lst) + seasFCSTdata + + }) + + output$seasFCSTtable <- DT::renderDataTable(DT::datatable({ + + seasFCST_table() + + })) + + + output$downloadFCST <- downloadHandler( + filename = function() { + paste0("FCST_", Sys.Date(), ".xlsx") + }, + content = function(file) { + openxlsx::write.xlsx(seasFCST_table(), file) + } + ) + + # ***************************************************************************** + + plot_FCST <- eventReactive(input$seasFCST_plotButton, { + + plot.var <- seasFCST_table() + + plot.var2 <- data.frame(Catagory = c("Below Normal", "Normal", "Above Normal"), + FCST = as.double(t(plot.var))) + + plotly::plot_ly(data = plot.var2) %>% + plotly::add_bars(y = ~ FCST, x = ~Catagory) %>% + layout(title = "Seasonal Forecast of WSC", + xaxis = list(title = " "), + yaxis = list(title = "Tercile Probability (%)")) + + + + }) + + output$seasFCSTplot <- plotly::renderPlotly({ + + plot_FCST() + + }) + + ## ***** Convert CPT to NetCDF + + cptDF_dataInput <- reactive({ + req(input$cpt2nc_xlsxInput) + openxlsx::read.xlsx(input$cpt2nc_xlsxInput$datapath, sheet = 1) + }) + + + cpt2nc_roots <- c(wd = '.', home = '~', shinyFiles::getVolumes()()) + + shinyFiles::shinyDirChoose(input, 'dirCDF', + roots = cpt2nc_roots, + # defaultPath='~', + defaultRoot='~', + allowDirCreate = TRUE, + session = session, + filetypes = c(" ", "nc", "xlsx", "tif") + ) + + global <- reactiveValues(cpt2nc_dataPath = getwd()) + + output$dirCDF <- renderPrint({ + global$cpt2nc_dataPath + + }) + + observeEvent(ignoreNULL = TRUE, + eventExpr = { + input$dirCDF + }, + + handlerExpr = { + + global$cpt2nc_dataPath <- file.path(shinyFiles::parseDirPath(roots = cpt2nc_roots, + input$dirCDF), + fsep = .Platform$file.sep) + + output$dirCDF <- renderPrint({ + file.path(shinyFiles::parseDirPath(roots = cpt2nc_roots, + input$dirCDF), + fsep = .Platform$file.sep) + }) + + }) + + + cpt2NetCDF <- observeEvent(input$cpt2nc_runButton, { + + data.xlsx <- cptDF_dataInput() + + lat.length <- input$LATlengthInput + lon.length <- input$LONGlengthInput + + outDir.cpt2nc = global$cpt2nc_dataPath + cpt2nc.outFile = input$cdfFilePrefix + DateStart.cpt2nc = lubridate::as_date(input$cpt2nc_date) + + + outputFile.C1 <- paste0(outDir.cpt2nc, "/", cpt2nc.outFile, "_", + DateStart.cpt2nc, "_C1", ".nc") + + outputFile.C2 <- paste0(outDir.cpt2nc, "/", cpt2nc.outFile, "_", + DateStart.cpt2nc, "_C2", ".nc") + + outputFile.C3 <- paste0(outDir.cpt2nc, "/", cpt2nc.outFile, "_", + DateStart.cpt2nc, "_C3", ".nc") + + + ############################################################################### + ## ***** nc header + + nc.title <- 'Seasonal Forecast' + nc.institution <- 'Crop Genetics Group, Center of Plant Sciences, Sant’Anna School of Advanced Studies, Pisa, Italy' + nc.source <- ' INAM' + nc.input_data <- ' ' + nc.reference <- 'AquaBEHER' + nc.project_id <- 'FOCUSafrica, CaseStudy-3 (CS3)' + nc.experiment_id <- 'WP4' + nc.version <- 'V.1.0' + nc.version_comment <- 'alpha.test' + nc.contact1 <- "Robel Takele/Matteo Dell'Acqua" + nc.contact2 <- 'takelerobel.miteku@santannapisa.it/matteo.dellacqua@santannapisa.it' + nc.contact3 <- '+251913623066/+251986317965' + nc.history <- '' + + ## **************************************************************************** + ## ***** getting dimensions + + lon.vec <- as.double(data.xlsx[3, 2:length(data.xlsx[1,])]) + lat.vec <- as.double(data.xlsx[4:(lat.length + 3),1]) + + time.units <- "month since 2023-10-01 06:00:00" # ??????? + time.calendar <- "standard" + time.values <- 1 + + ## **************************************************************************** + ## ***** getting variables + + missing_value <- -9876543210 + + lat.length <- as.numeric(input$LATlengthInput) + lon.length <- as.numeric(input$LONGlengthInput) + + data.C1.dF <- as.matrix(data.xlsx[4:(lat.length + 3), + 2:length(data.xlsx[1,])]) + + data.C2.dF <- as.matrix(data.xlsx[(lat.length + 6):(2*lat.length + 5), + 2:length(data.xlsx[1,])]) + + data.C3.dF <- as.matrix(data.xlsx[(2*lat.length + 8):(3*lat.length + 7), + 2:length(data.xlsx[1,])]) + + prob.dF.C1 <- array(1, dim = c(length(lon.vec), length(lat.vec), 1)) + prob.dF.C2 <- array(1, dim = c(length(lon.vec), length(lat.vec), 1)) + prob.dF.C3 <- array(1, dim = c(length(lon.vec), length(lat.vec), 1)) + + for (x in seq_along(lon.vec)) { + + for (y in seq_along(lat.vec)) { + + prob.dF.C1[x, y,] <- data.C1.dF[y, x] + prob.dF.C2[x, y,] <- data.C2.dF[y, x] + prob.dF.C3[x, y,] <- data.C3.dF[y, x] + + } + + } + + ## **************************************************************************** + ## ***** define dimentions for NetCDF4 + + lon <- ncdf4::ncdim_def("lon", "degrees_east", as.double(lon.vec), + longname = "Longitude") + lat <- ncdf4::ncdim_def("lat", "degrees_north", as.double(lat.vec), + longname = "Latitude") + + time <- ncdf4::ncdim_def( "time", time.units, time.values, + longname = "Time", unlim = TRUE, calendar = time.calendar) + + ## ***************************************************************************** + ## ***** define variable for NetCDF4 + + varz.C1 = ncdf4::ncvar_def("rainProbability.C1", "%", + list(lon,lat,time), + longname="Rainfall Probability for C1)", + missval = missing_value, + prec = "double", + # shuffle=TRUE, + compression = 9, + verbose = TRUE) + + varz.C2 = ncdf4::ncvar_def("rainProbability.C2", "%", + list(lon,lat,time), + longname="Rainfall Probability for C2)", + missval = missing_value, + prec = "double", + # shuffle=TRUE, + compression = 9, + verbose = TRUE) + + varz.C3 = ncdf4::ncvar_def("rainProbability.C3", "%", + list(lon,lat,time), + longname="Rainfall Probability for C3)", + missval = missing_value, + prec = "double", + # shuffle=TRUE, + compression = 9, + verbose = TRUE) + + ## **************************************************************************** + ## ***** create NetCDF file for C1 + + nccfs = ncdf4::nc_create(outputFile.C1, varz.C1) + + ncdf4::ncatt_put(nccfs, varid = 'time', attname = 'units', attval = time.units) + ncdf4::ncatt_put(nccfs, varz.C1, attname = 'missing_value', attval = missing_value) + + ncdf4::ncatt_put(nccfs, varid = 0, attname = 'title', attval = nc.title) + ncdf4::ncatt_put(nccfs, varid=0, attname='institution', attval=nc.institution) + ncdf4::ncatt_put(nccfs, varid=0, attname='source', attval=nc.source) + ncdf4::ncatt_put(nccfs, varid=0, attname='input_data', attval=nc.input_data) + ncdf4::ncatt_put(nccfs, varid=0, attname='reference', attval=nc.reference) + ncdf4::ncatt_put(nccfs, varid=0, attname='project_id', attval=nc.project_id) + ncdf4::ncatt_put(nccfs, varid=0, attname='experiment_id', attval=nc.experiment_id) + ncdf4::ncatt_put(nccfs, varid=0, attname='version', attval=nc.version) + ncdf4::ncatt_put(nccfs, varid=0, attname='version_comment', attval=nc.version_comment) + ncdf4::ncatt_put(nccfs, varid=0, attname='contact1', attval=nc.contact1) + ncdf4::ncatt_put(nccfs, varid=0, attname='contact2', attval=nc.contact2) + ncdf4::ncatt_put(nccfs, varid=0, attname='contact3', attval=nc.contact3) + ncdf4::ncatt_put(nccfs, varid=0, attname='history', attval=nc.history) + + ncdf4::ncvar_put(nccfs, varz.C1, prob.dF.C1) + + ncdf4::nc_close(nccfs) + + ## **************************************************************************** + ## **************************************************************************** + ## ***** create NetCDF file for C2 + + nccfs = nc_create(outputFile.C2, varz.C2) + + ncatt_put(nccfs, varid = 'time', attname = 'units', attval = time.units) + ncatt_put(nccfs, varz.C2, attname = 'missing_value', attval = missing_value) + + ncatt_put(nccfs, varid = 0, attname = 'title', attval = nc.title) + ncatt_put(nccfs, varid=0, attname='institution', attval=nc.institution) + ncatt_put(nccfs, varid=0, attname='source', attval=nc.source) + ncatt_put(nccfs, varid=0, attname='input_data', attval=nc.input_data) + ncatt_put(nccfs, varid=0, attname='reference', attval=nc.reference) + ncatt_put(nccfs, varid=0, attname='project_id', attval=nc.project_id) + ncatt_put(nccfs, varid=0, attname='experiment_id', attval=nc.experiment_id) + ncatt_put(nccfs, varid=0, attname='version', attval=nc.version) + ncatt_put(nccfs, varid=0, attname='version_comment', attval=nc.version_comment) + ncatt_put(nccfs, varid=0, attname='contact1', attval=nc.contact1) + ncatt_put(nccfs, varid=0, attname='contact2', attval=nc.contact2) + ncatt_put(nccfs, varid=0, attname='contact3', attval=nc.contact3) + ncatt_put(nccfs, varid=0, attname='history', attval=nc.history) + + ncvar_put(nccfs, varz.C2, prob.dF.C2) + + nc_close(nccfs) + + ## **************************************************************************** + ## ***** create NetCDF file for C3 + + nccfs = nc_create(outputFile.C3, varz.C3) + + ncatt_put(nccfs, varid = 'time', attname = 'units', attval = time.units) + ncatt_put(nccfs, varz.C3, attname = 'missing_value', attval = missing_value) + + ncatt_put(nccfs, varid = 0, attname = 'title', attval = nc.title) + ncatt_put(nccfs, varid=0, attname='institution', attval=nc.institution) + ncatt_put(nccfs, varid=0, attname='source', attval=nc.source) + ncatt_put(nccfs, varid=0, attname='input_data', attval=nc.input_data) + ncatt_put(nccfs, varid=0, attname='reference', attval=nc.reference) + ncatt_put(nccfs, varid=0, attname='project_id', attval=nc.project_id) + ncatt_put(nccfs, varid=0, attname='experiment_id', attval=nc.experiment_id) + ncatt_put(nccfs, varid=0, attname='version', attval=nc.version) + ncatt_put(nccfs, varid=0, attname='version_comment', attval=nc.version_comment) + ncatt_put(nccfs, varid=0, attname='contact1', attval=nc.contact1) + ncatt_put(nccfs, varid=0, attname='contact2', attval=nc.contact2) + ncatt_put(nccfs, varid=0, attname='contact3', attval=nc.contact3) + ncatt_put(nccfs, varid=0, attname='history', attval=nc.history) + + ncvar_put(nccfs, varz.C3, prob.dF.C3) + + nc_close(nccfs) + + } ) + + output$rainTERmap = leaflet::renderLeaflet({ + + leaflet::leaflet() %>% + mapboxapi::addMapboxTiles(style_id = "satellite-streets-v12", username = "mapbox", + access_token = "pk.eyJ1Ijoicm9iZWx0YWtlbGUiLCJhIjoiY2xkb2o4NmRtMDEzcjNubHBkenMycnhiaSJ9.UkdfagqGIy7WjMGXtlT1mQ") %>% + + leaflet.multiopacity::addOpacityControls(group = "rainTERlayers", + collapsed = FALSE, + position = "bottomleft", + size = "m", + # title = "PET Opacity Control:", + renderOnLayerAdd = TRUE) %>% + + leaflet::addMiniMap(position = "bottomright", + width = 150, + height = 150) %>% + leaflet::setView(lng = 38, lat = -14, zoom = 4) + }) + + + rainTERmapLeaf <- observeEvent(input$cpt2nc_runButton, { + + outDir.cpt2nc = global$cpt2nc_dataPath + cpt2nc.outFile = input$cdfFilePrefix + DateStart.cpt2nc = lubridate::as_date(input$cpt2nc_date) + + outputFile.C1 <- paste0(outDir.cpt2nc, "/", cpt2nc.outFile, "_", + DateStart.cpt2nc, "_C1", ".nc") + + C1.mapDat <- raster::brick(as.character(outputFile.C1)) + + C1.map_leaflet <- leaflet::projectRasterForLeaflet(C1.mapDat, method = "bilinear") + + C1.colorPal <- leaflet::colorNumeric(c("#9E0142","#D0384D","#EE6445","#FA9C58", + "#FDCD7B","#FEF0A7","#F3FAAD","#D0EC9C", + "#98D5A4","#5CB7A9","#3682BA","#5E4FA2"), + raster::values( C1.map_leaflet), + na.color = "transparent") + + leaflet::leafletProxy("rainTERmap", session) %>% + + leaflet::addRasterImage(C1.map_leaflet, colors = C1.colorPal, opacity = 0.8, + group = "rainTERlayers", layerId = "rainTERlayers") %>% + + leaflet::addLegend(pal = C1.colorPal, values = raster::values(C1.map_leaflet), opacity = 1, + title = "Probability (%)", position = "topright") + + + } ) + + ################################################################################ + ## ***** Seasonal forecast from sp + + + ncData.C1_dataInput <- reactive({ + req(input$C1.cdfInput) + terra::rast(input$C1.cdfInput$datapath) + }) + + ncData.C2_dataInput <- reactive({ + req(input$C2.cdfInput) + terra::rast(input$C2.cdfInput$datapath) + }) + + ncData.C3_dataInput <- reactive({ + req(input$C3.cdfInput) + terra::rast(input$C3.cdfInput$datapath) + }) + + + ncData.seasRAIN_dataInput <- reactive({ + req(input$seasRain.cdfInput) + terra::rast(input$seasRain.cdfInput$datapath) + }) + + + ncData.fcstVAR_dataInput <- reactive({ + req(input$fcstVAR.cdfInput) + terra::rast(input$fcstVAR.cdfInput$datapath) + }) + + + FCST_roots <- c(wd = '.', home = '~', shinyFiles::getVolumes()()) + + shinyFiles::shinyDirChoose(input, 'dirFCSTsp', + roots = FCST_roots, + # defaultPath='~', + defaultRoot='~', + allowDirCreate = TRUE, + session = session, + filetypes = c(" ", "nc", "tif") + ) + + global <- reactiveValues(FCST_dataPath = getwd()) + + output$dirFCSTsp <- renderPrint({ + global$FCST_dataPath + + }) + + observeEvent(ignoreNULL = TRUE, + eventExpr = { + input$dirFCSTsp + }, + + handlerExpr = { + + global$FCST_dataPath <- file.path(shinyFiles::parseDirPath(roots = FCST_roots, + input$dirFCSTsp), + fsep = .Platform$file.sep) + + output$dirFCSTsp <- renderPrint({ + file.path(shinyFiles::parseDirPath(roots = FCST_roots, + input$dirFCSTsp), + fsep = .Platform$file.sep) + }) + + }) + + + ## **************************************************************************** + + FCSTspNetCDF <- eventReactive(input$fcst_runButton, { + + shinyWidgets::progressSweetAlert( + session = session, id = "FCSTprogress", + title = h4(paste0(" Seasonal forecast is in Progress ....."), + style = "color: #FD1C03; font-style: bold; font-family: times;"), + display_pct = TRUE, value = 0, striped = TRUE, width = '55%') + + DateStart = lubridate::as_date(input$obsWSC_DateStart) + DateEnd = lubridate::as_date(input$obsWSC_DateEnd) + + estYears <- lubridate::year(DateStart) : lubridate::year(DateEnd) + + fcstVAR.name <- as.character(spFCSTnames[as.numeric(input$FCSTvar.mapview)]) # input$FCSTvar + + fcstYear = lubridate::year(lubridate::as_date(input$FCST_Date)) + fcstMonth = lubridate::month(lubridate::as_date(input$FCST_Date)) + fcstDay = lubridate::day(lubridate::as_date(input$FCST_Date)) + + C1.ncFile = ncData.C1_dataInput() + C2.ncFile = ncData.C2_dataInput() + C3.ncFile = ncData.C3_dataInput() + + C1.ncFile[C1.ncFile < 0] <- NA + C2.ncFile[C2.ncFile < 0] <- NA + C3.ncFile[C3.ncFile < 0] <- NA + + ncFile.rain = ncData.seasRAIN_dataInput() + rain.nms.date.vec <- lubridate::year(lubridate::as_date(terra::time(ncFile.rain))) + startdate.index <- which(rain.nms.date.vec == lubridate::year(DateStart)) + enddate.index <- which(rain.nms.date.vec == lubridate::year(DateEnd)) + ncFile.rain <- ncFile.rain[[startdate.index:enddate.index]] + + ncData.rain <- terra::crop(ncFile.rain, terra::ext(C1.ncFile)) + ncData.rain.r <- raster::brick(terra::resample(ncData.rain, C1.ncFile, + method = "bilinear")) + ncData.rain.r <- raster::mask(ncData.rain.r, raster::brick(C1.ncFile)) + rain.SpGdF <- as(ncData.rain.r, "SpatialGridDataFrame") + + ncFile.fcstVAR = ncData.fcstVAR_dataInput() + fcstVAR.nms.date.vec <- lubridate::year(lubridate::as_date(terra::time(ncFile.fcstVAR))) + startdate.index <- which(fcstVAR.nms.date.vec == lubridate::year(DateStart)) + enddate.index <- which(fcstVAR.nms.date.vec == lubridate::year(DateEnd)) + ncFile.fcstVAR <- ncFile.fcstVAR[[startdate.index:enddate.index]] + + ncData.fcstVAR <- terra::crop(ncFile.fcstVAR, terra::ext(C1.ncFile)) + ncData.fcstVAR.r <- raster::brick(terra::resample(ncData.fcstVAR, + C1.ncFile, + method = "bilinear")) + ncData.fcstVAR.r <- raster::mask(ncData.fcstVAR.r, raster::brick(C1.ncFile)) + fcstVAR.SpGdF <- as(ncData.fcstVAR.r, "SpatialGridDataFrame") + + C1.SpGdF <- as(raster::brick(C1.ncFile), "SpatialGridDataFrame") + C2.SpGdF <- as(raster::brick(C2.ncFile), "SpatialGridDataFrame") + C3.SpGdF <- as(raster::brick(C3.ncFile), "SpatialGridDataFrame") + + ncData.fcstVAR.r <- terra::rast(ncData.fcstVAR.r) + fcstVAREnse.SpGdF <- C1.SpGdF + fcstVAREnse.SpGdF@data <- data.frame(matrix(data = NA, + nrow = nrow(fcstVAREnse.SpGdF@data), + ncol = 3)) + + colnames(fcstVAREnse.SpGdF@data) <- c("BN", "NN", "AN") + + rainTer.SpGdF <- C1.SpGdF + rainTer.SpGdF@data$C1 <- C1.SpGdF@data[,1] + rainTer.SpGdF@data$C2 <- C2.SpGdF@data[,1] + rainTer.SpGdF@data$C3 <- C3.SpGdF@data[,1] + + ## ***** resampling ensembles onset + + for (grd in seq_along(C1.SpGdF@data[,1])) { + + # for (grd in seq_along(1:2000)) { + + sR.xy <- data.frame("Year" = estYears, + "sRain" = as.double(rain.SpGdF@data[grd, ])) + + fcstVAR.xy <- data.frame(Year = estYears, + fcstVAR.val = as.double(fcstVAR.SpGdF@data[grd, ])) + + if (fcstVAR.name == "onset") { + + colnames(fcstVAR.xy) <- c("Year", "onset.Value") + + } else { + + colnames(fcstVAR.xy) <- c("Year", "cessation.Value") + + } + + rainTercile.xy <- data.frame(T1 = as.double(rainTer.SpGdF@data$C1[grd])/100, + T2 = as.double(rainTer.SpGdF@data$C2[grd])/100, + T3 = as.double(rainTer.SpGdF@data$C3[grd])/100) + + if (length(which(is.na(sR.xy$sRain))) > round(length(sR.xy$sRain)*0.1) | + length(which(is.na(fcstVAR.xy[,2]))) > round(length(fcstVAR.xy[,2])*0.4) | + length(which(!is.na(rainTercile.xy))) < 3 ) { + + fcstVAREnse.SpGdF@data[grd, ] <- fcstVAREnse.SpGdF@data[grd, ] + + } else { + + fcstVAREns.xy <- AquaBEHER::fcstWSC(sesRain = sR.xy, + var.dF = fcstVAR.xy, + rainTerc = rainTercile.xy, + variable = fcstVAR.name) + + fcstVAREnse.SpGdF@data[grd, ] <- fcstVAREns.xy + + } + + Sys.sleep(0.1) + shinyWidgets::updateProgressBar( + session = session, + id = "FCSTprogress", + value = grd, + total = nrow(C1.SpGdF@data)) + + + } + + + fcstVAREnse.rasBRK <- terra::rast(fcstVAREnse.SpGdF) + + + fcstYear = lubridate::year(lubridate::as_date(input$FCST_Date)) + fcstMonth = lubridate::month(lubridate::as_date(input$FCST_Date)) + + + outputFile.FCST.terC <- paste0(global$FCST_dataPath, "/", + input$spFCSToutFilePrefix, "_", + as.character(spFCSTnames[as.numeric(input$FCSTvar.mapview)]), "_", + fcstMonth, fcstYear, "_terProbs.nc") + + + terra::writeCDF(x = fcstVAREnse.rasBRK, + filename = outputFile.FCST.terC, + varname = paste0(as.character(spFCSTnames[as.numeric(input$FCSTvar.mapview)])), + longname = paste0("Tercile Probabilities"), + unit = "%", + zname = 'time', + prec = "double", + compression = 9, + missval = -9876543210, + atts = nc.att, + overwrite = TRUE) + + ## **************************************************************************** + ## *** Debuging + + + # rast <- raster::brick(paste0("/home/robel/Downloads/test.nc")) + # fcstVAREnse.SpGdF <- as(rast, "SpatialGridDataFrame") + + + fcstVAR.probTerc.spGdF <- fcstVAREnse.SpGdF + fcstVAR.probTerc.spGdF@data <- data.frame(matrix(NA, nrow = nrow(fcstVAREnse.SpGdF@data), + ncol = 1)) + + for (grd in seq_along(fcstVAR.probTerc.spGdF@data[,1])) { + + terMax <- NA + probTerc.grd <- NA + + # terCount <- table(as.numeric(onsetEnse.SpGdF@data[grd, ])) + terVec <- as.numeric(fcstVAREnse.SpGdF@data[grd, ]) + # terMax <- as.numeric(which(terCount == max(terCount))[1]) + + if (!is.na(terVec[1])) { + terMax <- as.numeric(which.max(terVec)) + probTerc.grd <- round(terVec[terMax], digits = 2) + } + + if (!is.na(terMax) & terMax == 2) { + probTerc.grd <- 0 + } else if (!is.na(terMax) & terMax == 1) { + probTerc.grd <- (-1)* probTerc.grd + } + + fcstVAR.probTerc.spGdF@data[grd,] <- probTerc.grd + + } + + fcstVAR.probTerc.rast <- terra::rast(fcstVAR.probTerc.spGdF) + + terra::time(fcstVAR.probTerc.rast) <- lubridate::as_date(paste0(fcstYear, "-", + fcstMonth, "-", + fcstDay)) + + + probTerc.noisy.raster <- raster(fcstVAR.probTerc.rast) + + # Define the size of the median filter kernel (adjust as needed) + filter_size <- c(3, 3) # Adjust the size as needed + + # Apply the median filter to smooth the raster data + probTerc.smooth.raster <- focal(probTerc.noisy.raster, w = matrix(1, nrow = filter_size[1], ncol = filter_size[2]), + fun = median, na.rm = TRUE) + + + outputFile.FCST.MstRerC <- paste0(global$FCST_dataPath, "/", + input$spFCSToutFilePrefix, "_", + as.character(spFCSTnames[as.numeric(input$FCSTvar.mapview)]), "_", + fcstMonth, fcstYear, "_mostLikely_terProbs.nc") + + terra::writeCDF(terra::rast(probTerc.smooth.raster), + filename = outputFile.FCST.MstRerC, + varname = paste0(as.character(spFCSTnames[as.numeric(input$FCSTvar.mapview)]), + ".tercileProb"), + longname = paste0("most likely tercile probability of", + as.character(spFCSTnames[as.numeric(input$FCSTvar.mapview)]), + "forecasts"), + unit = '%', + zname = 'time', + prec = "double", + compression = 9, + missval = -9876543210, + atts = nc.att, + overwrite = TRUE) + + + shinyWidgets::closeSweetAlert(session = session) + shinyWidgets::sendSweetAlert( + session = session, + title =" Forecast completed !", + type = "success" + ) + + probTerc.smooth.raster + + } ) + + + output$FCSTmap = leaflet::renderLeaflet({ + + leaflet::leaflet() %>% + mapboxapi::addMapboxTiles(style_id = "satellite-streets-v12", username = "mapbox", + access_token = "pk.eyJ1Ijoicm9iZWx0YWtlbGUiLCJhIjoiY2xkb2o4NmRtMDEzcjNubHBkenMycnhiaSJ9.UkdfagqGIy7WjMGXtlT1mQ") %>% + + leaflet.multiopacity::addOpacityControls(group = "FCSTlayers", + collapsed = FALSE, + position = "bottomleft", + size = "m", + # title = "PET Opacity Control:", + renderOnLayerAdd = TRUE) %>% + + leaflet::addMiniMap(position = "bottomright", + width = 150, + height = 150) %>% + leaflet::setView(lng = 38, lat = -14, zoom = 4) + }) + + + + FCSTmap.eventTrigger <- reactive({ + list(input$FCSTvar.mapview, input$FCST_Date) + }) + + + # observeEvent(input$FCSTvar, { + + # spFCSTNamIndex <- which((spFCSTnames) == as.character(input$FCSTvar.mapview)) + + output$FCSTmapTitle <- renderText({paste0("Seasonal Forecast : ", + as.character(spFCSTnames[as.numeric(input$FCSTvar.mapview)]))}) + + # output$FCSTmapTitle <- renderText({paste0("Seasonal Forecast : ", + # as.character(spFCSTnames[as.numeric(spFCSTNamIndex)]))}) + # }) + + + observeEvent(input$fcst_runButton, { + + FCSTmap.Dat <- FCSTspNetCDF() + FCSTmap.mapDat <- raster::brick(FCSTmap.Dat) + + FCSTmap.leaflet <- leaflet::projectRasterForLeaflet(FCSTmap.mapDat, + method = "bilinear") + + colPal <- c("#3288BD", "#66C2A5", "#ABDDA4", "#E6F598", "#FFFFBF", + "#FEE08B", "#FDAE61", "#F46D43", "#D53E4F") + + FCSTmap.colorPal <- leaflet::colorNumeric(colPal, raster::values(FCSTmap.leaflet), + na.color = "transparent") + + leaflet::leafletProxy("FCSTmap", session) %>% + + leaflet::addRasterImage(FCSTmap.leaflet, colors = FCSTmap.colorPal, + opacity = 0.8) %>% + + leaflet::addLegend(pal = FCSTmap.colorPal, + values = values(FCSTmap.leaflet), + title = "Probability") %>% + + leaflet.extras2::addEasyprint(options = leaflet.extras2::easyprintOptions(exportOnly = FALSE, + hidden = FALSE, + hideControlContainer = FALSE)) + + } ) + + observeEvent(input$printFCSTmap, { + leaflet::leafletProxy("FCSTmap", session) %>% + leaflet.extras2::easyprintMap(sizeModes = input$scene, filename = input$FCSTmapFileN) + }) + + + ############################################################################################################ ############################################################################################################ diff --git a/R/app_ui.R b/R/app_ui.R index 91515bd..508abce 100644 --- a/R/app_ui.R +++ b/R/app_ui.R @@ -70,6 +70,18 @@ spWSCcolPal <- list(rev(c("#440154","#462777","#3D4988","#30678D","#25818E","#1 rev(c("#780085","#7F2FD1","#686DF9","#4C9ED9","#3FC29F","#53DA60","#85EB50","#C1EC58","#E4D05C","#F9965B")), rev(c("#780085","#7F2FD1","#686DF9","#4C9ED9","#3FC29F","#53DA60","#85EB50","#C1EC58","#E4D05C","#F9965B"))) +spFCSTnames <- c("onset", "cessation") + +nc.att <- c("Institution=Sant’Anna School of Advanced Studies (SSSA)", + "Group=Center of Plant Sciences", + "Project=FOCUSafrica, Food security Case study-03 (CS3)", + "WP5=Develop end-user tailored climate services prototypes", + "Contact-1=Robel Takele [takelerobel.miteku@santannapisa.it]", + "Contact-2=Matteo Dell'Acqua [matteo.dellacqua@santannapisa.it]", + "Source=Generated by the AquaBEHER package (version 0.1.0)", + "Source.Reference=URL: https://github.com/RobelTakele/AquaBEHER", + "BugReports=https://github.com/RobelTakele/AquaBEHER/issues") + ################################################################################################################## ################################################################################################################## @@ -102,7 +114,14 @@ app_ui <- function(request) { navbarMenu("Seasonal Calendar", tabPanel("From Location Data", mod_locWSC_ui("locWSC_1")), - tabPanel("From Gridded Data", mod_spWSC_ui("spWSC_1"))) + tabPanel("From Gridded Data", mod_spWSC_ui("spWSC_1"))), + + navbarMenu("Preparing Data", + tabPanel("CPT to NetCDF", mod_cpt2nc_ui("cpt2nc_1"))), + + navbarMenu("Seasonal Forecast", + tabPanel("From Location Data", mod_locFCST_ui("locFCST_1")), + tabPanel("From Gridded Data", mod_spFCST_ui("spFCST_1"))) ) ) diff --git a/R/mod_cpt2nc.R b/R/mod_cpt2nc.R new file mode 100644 index 0000000..0aaf372 --- /dev/null +++ b/R/mod_cpt2nc.R @@ -0,0 +1,141 @@ +#' cpt2nc UI Function +#' +#' @description A shiny Module. +#' +#' @param id,input,output,session Internal parameters for {shiny}. +#' +#' @noRd +#' +#' @importFrom shiny NS tagList +mod_cpt2nc_ui <- function(id){ + ns <- NS(id) + tagList( + + + tabPanel(title = " ", + + titlePanel("Convert CPT file to NetCDF"), + + # shinyjs::useShinyjs(), + + sidebarLayout( + sidebarPanel( + title = " ", + + tags$head( + # this changes the size of the popovers + tags$style(".popover{width:500px;height:200px;}") + ), + + + fileInput(inputId = "cpt2nc_xlsxInput", + label = h4( span("Select CPT Tercile Probability File to Import: ",), + style = "color: #4d3a7d;" + + ), + + accept = ".xlsx", + width = '100%', + buttonLabel = "Browse...", + placeholder = "No file selected", + capture = NULL), + + numericInput(inputId = "LONGlengthInput", + label = "Input the number of longitudes", + value = 152, + min = 1, + max = 10000, + width = '100%'), + + numericInput(inputId = "LATlengthInput", + label = "Input the number of latitudes", + value = 234, + min = 1, + max = 10000, + width = '100%'), + + shinyWidgets::airDatepickerInput( + inputId = "cpt2nc_date", + label = "Forecast Start Date (yyyy-MM-dd):", + separator = " - ", + dateFormat = "yyyy-MM-dd", + autoClose = TRUE, + view = c("days", "months", "years"), + value = Sys.Date() - 7), + + + p(strong("Select NetCDF output directory: ")), + shinyFiles::shinyDirButton(id = "dirCDF", + label = " Please select a folder to save NetCDF data ...", + icon = icon("save", lib = "glyphicon"),, + class = "shinyDirectories btn-default", + title = "Upload ", + type = "button "), + br(), + verbatimTextOutput(outputId = "dirCDF", placeholder = TRUE), + br(), + + shinyWidgets::textInputIcon(inputId = "cdfFilePrefix", + label = "NetCDF data output file name prefix:", + placeholder = "NetCDF", + width = '100%'), + + div(style="position:relative; left:calc(70%);", + + shinyWidgets::actionBttn(inputId = "cpt2nc_runButton", + label = " Convert ", + color = "primary", + size = 'md', + style = "jelly", + # style = "color: #fff; background-color: #27ae60; border-color: #fff;", + icon = icon("play"), + block = FALSE) + ) + + ), + + mainPanel( + tabsetPanel( + tabPanel( + title = "Data Viewer", + + shinyjs::useShinyjs(), + + fluidPage( + + br(), + + leaflet::leafletOutput(outputId = 'rainTERmap', width = "100%", height = 500), + + br(), + + + ) + ) + ) + ) + + ) + ) + + + + + ) +} + +#' cpt2nc Server Functions +#' +#' @noRd +mod_cpt2nc_server <- function(id){ + moduleServer( id, function(input, output, session){ + ns <- session$ns + + }) +} + +## To be copied in the UI +# mod_cpt2nc_ui("cpt2nc_1") + +## To be copied in the server +# mod_cpt2nc_server("cpt2nc_1") diff --git a/R/mod_locFCST.R b/R/mod_locFCST.R new file mode 100644 index 0000000..25c4877 --- /dev/null +++ b/R/mod_locFCST.R @@ -0,0 +1,159 @@ +#' locFCST UI Function +#' +#' @description A shiny Module. +#' +#' @param id,input,output,session Internal parameters for {shiny}. +#' +#' @noRd +#' +#' @importFrom shiny NS tagList +mod_locFCST_ui <- function(id){ + ns <- NS(id) + tagList( + + tabPanel(title = " ", + + titlePanel("Seasonal Forecast"), + + # shinyjs::useShinyjs(), + + sidebarLayout( + sidebarPanel( + title = " ", + + tags$head( + # this changes the size of the popovers + tags$style(".popover{width:500px;height:200px;}") + ), + + fileInput(inputId = "rainTerc_xlsxInput", + label = h4( span("Select Tercile Probability File to Import: ",), + style = "color: #4d3a7d;" + + ), + + accept = ".xlsx", + width = '100%', + buttonLabel = "Browse...", + placeholder = "No file selected", + capture = NULL), + + fileInput(inputId = "sesRain_xlsxInput", + label = h4( span("Select Seasonal Rain File to Import: ",), + style = "color: #4d3a7d;" + + ), + + accept = ".xlsx", + width = '100%', + buttonLabel = "Browse...", + placeholder = "No file selected", + capture = NULL), + + fileInput(inputId = "fcstVarDF_xlsxInput", + label = h4( span("Select WSC Variable File to Import: ",), + style = "color: #4d3a7d;" + + ), + + accept = ".xlsx", + width = '100%', + buttonLabel = "Browse...", + placeholder = "No file selected", + capture = NULL), + + selectInput("FCSTvar", label = h4("Choose WSC Variable:"), + choices = list("Onset" = "onset", + "Cessation" = "cessation"), + selected = 1), + + div(style="position:relative; left:calc(60%);", + + shinyWidgets::actionBttn(inputId = "FCST_runButton", + label = " Run Forecast", + color = "primary", + size = 'md', + style = "jelly", + # style = "color: #fff; background-color: #27ae60; border-color: #fff;", + icon = icon("play"), + block = FALSE) + ) + + ), + + mainPanel( + tabsetPanel( + tabPanel( + title = "Data", + + fluidPage( + + DT::dataTableOutput("seasFCSTtable"), + + br(), + + div(style="position:relative; left:calc(5%);", + shinyWidgets::downloadBttn(outputId = 'downloadFCST', + label = "Download Forecast Data", + color = "success", + size = 'md', + style = "jelly", + icon = icon("download"), + block = FALSE) + ) + ) + ), + + tabPanel( + title = "Plot", + + br( ), + + shinyjs::useShinyjs(), + + br( ), + + div(style="position:relative; left:calc(5%);", + + shinyWidgets::actionBttn(inputId = "seasFCST_plotButton", + label = " Plot Forecast", + color = "success", + size = 'lg', + style = "jelly", + # style = "color: #fff; background-color: #27ae60; border-color: #fff;", + icon = icon("chart-line"), + block = FALSE) + ), + + br( ), + + plotly::plotlyOutput("seasFCSTplot"), + + br( ), + + ) + + ) + + ) + ) + ) + + ) +} + +#' locFCST Server Functions +#' +#' @noRd +mod_locFCST_server <- function(id){ + moduleServer( id, function(input, output, session){ + ns <- session$ns + + }) +} + +## To be copied in the UI +# mod_locFCST_ui("locFCST_1") + +## To be copied in the server +# mod_locFCST_server("locFCST_1") diff --git a/R/mod_spFCST.R b/R/mod_spFCST.R new file mode 100644 index 0000000..0397a0f --- /dev/null +++ b/R/mod_spFCST.R @@ -0,0 +1,184 @@ +#' spFCST UI Function +#' +#' @description A shiny Module. +#' +#' @param id,input,output,session Internal parameters for {shiny}. +#' +#' @noRd +#' +#' @importFrom shiny NS tagList +#' +mod_spFCST_ui <- function(id){ + ns <- NS(id) + tagList( + + + titlePanel("Seasonal Forecast"), + + sidebarLayout( + sidebarPanel( + title = " ", + + selectInput("FCSTvar.mapview", + label = h4("Choose WSC Variable:"), + selected = 1, + choices = list("onset" = 1, + "cessation" = 2)), + + shinyWidgets::airDatepickerInput( + inputId = "obsWSC_DateStart", + label = "Observation Start Date (yyyy-MM-dd):", + separator = " - ", + dateFormat = "yyyy-MM-dd", + autoClose = TRUE, + view = c("days", "months", "years"), + value = Sys.Date() - 7), + + shinyWidgets::airDatepickerInput( + inputId = "obsWSC_DateEnd", + label = "Observation End Date (yyyy-MM-dd):", + separator = " - ", + dateFormat = "yyyy-MM-dd", + autoClose = TRUE, + view = c("days", "months", "years"), + value = Sys.Date() - 7), + + shinyWidgets::airDatepickerInput( + inputId = "FCST_Date", + label = "Forecast Date (yyyy-MM-dd):", + separator = " - ", + dateFormat = "yyyy-MM-dd", + autoClose = TRUE, + view = c("days", "months", "years"), + value = Sys.Date() - 7), + + fileInput("C1.cdfInput", + "Tercile Rain Probability (C1) : select NetCDF file to import", + accept = ".nc"), + + fileInput("C2.cdfInput", + "Tercile Rain Probability (C2) : select NetCDF file to import", + accept = ".nc"), + + fileInput("C3.cdfInput", + "Tercile Rain Probability (C3) : select NetCDF file to import", + accept = ".nc"), + + fileInput("seasRain.cdfInput", + "Observed Seasonal Rainfall : select NetCDF file to import", + accept = ".nc"), + + fileInput("fcstVAR.cdfInput", + "Observed WSC variable to forecast : select NetCDF file to import", + accept = ".nc"), + + numericInput(inputId = "FCSTresInput", + label = "Set horizontal resolution in decimal degrees (\u00B0) ", + value = 0.0833, + min = 0.001, + max = 2, + width = '100%'), + + p(strong("Select output directory: ")), + shinyFiles::shinyDirButton(id = "dirFCSTsp", + label = " Please select a folder to save data ...", + icon = icon("save", lib = "glyphicon"),, + class = "shinyDirectories btn-default", + title = "Upload ", + type = "button "), + br(), + verbatimTextOutput(outputId = "dirFCSTsp", placeholder = TRUE), + br(), + + shinyWidgets::textInputIcon(inputId = "spFCSToutFilePrefix", + label = "Forecast data output file name prefix:", + placeholder = "Forecast", + width = '100%'), + tags$h1(" "), + shinyWidgets::useSweetAlert(), # /!\ needed with 'progressSweetAlert' + + div(style="position:relative; left:calc(60%);", + + shinyWidgets::actionBttn(inputId = "fcst_runButton", + label = " Run Forecast", + color = "primary", + size = 'md', + style = "jelly", + # style = "color: #fff; background-color: #27ae60; border-color: #fff;", + icon = icon("play"), + block = FALSE) + ) + + ), + + mainPanel( + tabsetPanel( + tabPanel( + title = "Forecast Viewer", + + fluidPage( + + shinyjs::useShinyjs(), + + h4(verbatimTextOutput("FCSTmapTitle", placeholder = TRUE), + align = "center"), + + tags$head(tags$style("#FCSTmapTitle{font-family: times; font-size: 20px; + font-style: bold; color: #3D3C3A;}")), + + leaflet::leafletOutput('FCSTmap', width = "100%", height = 500), + + br(), + + column(4, offset = 0, + + shinyWidgets::textInputIcon(inputId = "FCSTmapFileN", label = "File Name:", + placeholder = "Output file name", width = '100%')), + + column(3, offset = 0, + selectInput("scene", "Select Scene:", + choices = c("CurrentSize", "A4Landscape", "A4Portrait")), + + shinyWidgets::actionBttn(inputId = "printFCSTmap", + label = "Save Map", + # title = "Save's leaflet map to 'Downloads' directory", + color = "success", + size = 'md', + style = "jelly", + # style = "color: #fff; background-color: #27ae60; border-color: #fff;", + icon = icon("save"), + block = TRUE)), + + br() + + ) + + + ) + + ) + + ) + ) + + + + + ) +} + +#' spFCST Server Functions +#' +#' @noRd +mod_spFCST_server <- function(id){ + moduleServer( id, function(input, output, session){ + ns <- session$ns + + }) +} + +## To be copied in the UI +# mod_spFCST_ui("spFCST_1") + +## To be copied in the server +# mod_spFCST_server("spFCST_1") diff --git a/R/mod_spSWB.R b/R/mod_spSWB.R index aaa95b0..691cdfa 100644 --- a/R/mod_spSWB.R +++ b/R/mod_spSWB.R @@ -30,7 +30,7 @@ mod_spSWB_ui <- function(id){ shinyWidgets::airDatepickerInput( inputId = "spSWB_DateEnd", - label = "Start Date (yyyy-MM-dd):", + label = "End Date (yyyy-MM-dd):", separator = " - ", dateFormat = "yyyy-MM-dd", autoClose = TRUE, diff --git a/R/mod_spWSC.R b/R/mod_spWSC.R index 60d69a2..39b8f14 100644 --- a/R/mod_spWSC.R +++ b/R/mod_spWSC.R @@ -32,7 +32,7 @@ mod_spWSC_ui <- function(id){ shinyWidgets::airDatepickerInput( inputId = "spWSC_DateEnd", - label = "Start Date (yyyy-MM-dd):", + label = "End Date (yyyy-MM-dd):", separator = " - ", dateFormat = "yyyy-MM-dd", autoClose = TRUE, @@ -47,7 +47,7 @@ mod_spWSC_ui <- function(id){ shinyWidgets::airDatepickerInput( inputId = "spWSConsetStart", - label = "Start Date (yyyy-MM-dd):", + label = "Onset window start date (yyyy-MM-dd):", separator = " - ", dateFormat = "yyyy-MM-dd", autoClose = TRUE, @@ -56,7 +56,7 @@ mod_spWSC_ui <- function(id){ shinyWidgets::airDatepickerInput( inputId = "spWSConsetEnd", - label = "Start Date (yyyy-MM-dd):", + label = "Onset window end date (yyyy-MM-dd):", separator = " - ", dateFormat = "yyyy-MM-dd", autoClose = TRUE, @@ -65,7 +65,7 @@ mod_spWSC_ui <- function(id){ shinyWidgets::airDatepickerInput( inputId = "spWSCcessEnd", - label = "Start Date (yyyy-MM-dd):", + label = "Cessation window end date (yyyy-MM-dd):", separator = " - ", dateFormat = "yyyy-MM-dd", autoClose = TRUE, diff --git a/inst/app/www/AquaBEHER.png b/inst/app/www/AquaBEHER.png index 7eec78f..c0f9330 100644 Binary files a/inst/app/www/AquaBEHER.png and b/inst/app/www/AquaBEHER.png differ diff --git a/inst/app/www/Home.html b/inst/app/www/Home.html index 30c8362..107eb1c 100644 --- a/inst/app/www/Home.html +++ b/inst/app/www/Home.html @@ -1,19 +1,20 @@ -
- -
The AquaBEHERgui app is maintained by The Center of Plant Sciences -Group at Scuola Superiore Sant'Anna, Pisa, Italy.
-The Center of Plant Sciences Group is a -geographically and culturally diverse research team working on climate -and crop genetics at Scuola Superiore Sant'Anna, Pisa, -Italy.
-You can contact us sending an email to Matteo Dell'Acqua -(mailto:m.dellacqua@santannapisa.it) or Mario Enrico Pe` -(mailto:m.pe@santannapisa.it). You can also visit the -crop genetics (http://www.capitalisegenetics.santannapisa.it/) web -page.
++ +
The AquaBEHERgui app is maintained by the Genetics +Group at the Center of Plant Sciences Scuola +Superiore Sant'Anna, Pisa, Italy.
+The Genetics Group at the Center of Plant Sciences is a +geographically and culturally diverse research team working on +data-drivem agicultural innovation combining crop genetics, climate, and +participatory approaches. We are based at Scuola Superiore +Sant’Anna, Pisa, Italy.
+You can contact us sending an email to Matteo Dell’Acqua (mailto:m.dellacqua@santannapisa.it) or Mario Enrico Pè +(mailto:m.pe@santannapisa.it). You can find out more +about us visiting the group web page (http://www.capitalisegenetics.santannapisa.it/) and +following us on Twitter @GenLab_SSA
We are committed to the free software
and FAIR
principles. This set of repositories collects our latest developments
diff --git a/inst/app/www/Home.md b/inst/app/www/Home.md
index 372e019..59efcf6 100644
--- a/inst/app/www/Home.md
+++ b/inst/app/www/Home.md
@@ -5,27 +5,18 @@ title: "Home"
-
-
-
-
-The AquaBEHERgui app is maintained by The Center of Plant Sciences Group
-at Scuola Superiore Sant\'Anna, Pisa, Italy.
+
+
+ The AquaBEHERgui app is maintained by the **Genetics Group** at the **Center of Plant Sciences**
+ Scuola Superiore Sant\'Anna, Pisa, Italy.
-The **Center of Plant Sciences Group** is a geographically and
-culturally diverse research team working on climate and crop genetics at
-**Scuola Superiore Sant\'Anna**, Pisa, Italy.
+The Genetics Group at the Center of Plant Sciences is a geographically and culturally diverse research team working on data-drivem agicultural innovation combining crop genetics, climate, and participatory approaches. We are based at **Scuola Superiore Sant’Anna**, Pisa, Italy.
-You can contact us sending an email to Matteo Dell\'Acqua
-(mailto:m.dellacqua@santannapisa.it) or Mario Enrico Pe\`
-(mailto:m.pe@santannapisa.it). You can also visit the
-crop genetics (