# Dependencies ------------------------------------------------------------
# install.packages(c("terra","dplyr","ncdf4","shiny","shinyWidgets","bslib",
# "leaflet","leafem","scico","sf"))
library(terra)
library(dplyr)
library(ncdf4)
library(shiny)
library(shinyWidgets)
library(bslib)
library(leaflet)
library(leafem)
library(scico)
library(sf)
# The script behind the Shiny app has 3 groups of codes. The first group of codes
# will load the predictions to be visualized and the metadata of these predictions.
# The second group designs the User Interface of the app and the contents dynamically
# change with any user input. The third group is the server where any user inputs
# activities are processed and cause a change in the UI.
# Load Rasters ----------------------------
<- list.files(path = "./../Models/Predictions", # Load NetCDF file paths
nc_files pattern='.nc$',
all.files=TRUE,
full.names=TRUE)
<- vect("./../Data/Raw/Polygons/Europe.shp") # Load Europe land polygon
land
<- vect("./../Data/Raw/Polygons/mask.shp") # Load masking polygon as SpatVector
mask
<- rast(nc_files) # Load NetCDF files as SpatRasters
predictions <- terra::mask(predictions,land, inverse=TRUE) # Remove raster cells that overlaps with land
predictions
names(predictions) <- substring(names(predictions),1,3) # Rename SpatRaster layers to FAO codes (i.e., SOL and PLE)
<- numeric()
max_values
for(i in unique(names(predictions))){
<- c(max_values,
max_values max(values(predictions[[names(predictions)==i]]),na.rm=TRUE))
}names(max_values) <- unique(names(predictions))
<- mask(predictions,mask) # Keep raster cells within the masking polygon. This will only show predictions where validation data is present.
masked_pred
<- unique(terra::time(predictions)) # extract Dates to be used in the Date slider in the UI
Dates
<- st_read("./../Data/Raw/Polygons/mask.shp") # Reload masking polygon as sf
mask
<- vector(mode="list", length = length(unique(names(predictions)))) # Create a list that will store the prediction raster files
raster_list names(raster_list) <- unique(names(predictions)) # Name the elements of the list with FAO codes
for(i in unique(names(predictions))){ # This for loop will extract a raster from the SpatRaster and convert it to an RasterLayer and store it in the raster_list
<- vector(mode="list", length=2) # Create a list of 2 elements - the whole prediction area and the cropped raster
mask_list names(mask_list) <- c("mask","whole_area")
for(j in names(mask_list)){
<- if(j=="mask") masked_pred else predictions # Choose which SpatRaster to process depending on current middle loop iteration
raster
<- vector(mode="list", length=length(Dates)) # Create a list that will contain the daily predictions RasterLayer
dates_list names(dates_list) <- Dates
for(k in 1:length(Dates)){
<- raster[[names(raster)==i]] # Subset raster based on the current species in the outer loop iteration
subRaster <- subRaster[[terra::time(subRaster)==as.POSIXct(Dates[k])]] # Subset raster based on the current Date in the inner loop iteration
subRaster
<- raster::raster(terra::project(subRaster,"EPSG:3857")) # Project raster to EPSG:3857 and convert to RasterLayer. Leaflet can only visualize RasterLayer and not SpatRaster.
dates_list[[k]]
}
<- dates_list
mask_list[[j]]
}
<- mask_list
raster_list[[i]]
}
rm(mask_list,dates_list) # Clean up to free up some memory
8.2 Interactive Visualisation
The Catch Predictions Dashboard (Figure 1) is build using Shiny for R. Shiny is an R package that makes it easy to develop interactive web applications (apps) straight from R. The Catch Predictions Dashboard is a Shiny app contained in a single R-script. This script comprises of three key components:
- the user interface (
ui
) - the
server
function - a call to the
shinyApp
function
The ui
defines the layout and appearance of the app, the server
function contains the logic to build the app’s outputs based on user inputs, and the shinyApp
function brings the ui
and server
together to create the app. In the following sections we will go through all components of the Shiny app’s code and explain what each does within the Catch Predictions Dashboard. Basic knowledge on Shiny is required and can be found on the developer’s website.
First things first
At the beginning of the Shiny script the NetCDF files containing the model predictions (see subsection 8.1) are loaded and processed to allow displaying catch predictions for common sole (Solea solea) or European plaice (Pleuronectes platessa) on the map, and extraction of metadata that is used for text (see next paragraph) and other components of the app. The code for importing the NetCDF files is placed outside the ui
and server
function as these will be loaded only once during the start-up of the app and not anymore during processes performed within the server. Loading and processing NetCDF files only once at start-up increases computational efficiency of the Shiny App.
All metadata and text that should be displayed in the user interface is created in Dutch and English in a translation directory named translations
. Depending on the input in the user interface ui
a selection of text objects is made from the translations
list in the appropriate language based on the logic defined in the server
of the app. The translation directory translations
is build outside the ui
and server
. Such placing of this code block allows for a cleaner server logic code later on.
# Create Translation Directory ----------------------------
<- vector(mode="list",length=2)
nc_info
for(i in 1:length(nc_files)){ # This for loop will create a list that will contain the model metadata both in English and Dutch
<- nc_open(nc_files[i]) # Open NetCDF file to extract metadata
nc
<- ncatt_get(nc,0)$source # ML algorithm used in model training
source <- ncatt_get(nc,0)$library # R package used in ML model training
library <- ncatt_get(nc,0)$comment # Extra information on the training data and fitting process
comment_en <- ncatt_get(nc,0)$contact # Modeller
contact = gsub("evaluated on ","", # Date of model evaluation
eval_date regmatches(comment_en,
regexpr("evaluated on (\\d{2}-[A-Za-z]{3}-\\d{4})",
comment_en)))
# The following codes extracts more information from the metadata and create
# different language versions. These texts will be used in the UI depending on
# the selected species and language.
if(grepl("plaice",comment_en)){
= "PLE"
Species = "schol (Pleuronectes platessa)"
sp_name = "De vangstwaarden werden getransformeerd met de formule log(CPUE+0,00001)."
log_trans = gsub("hr","uur",
error regmatches(comment_en,
regexpr("\\d+\\.\\d+\\s*kg/hr",comment_en)))
else {
} = "SOL"
Species = "tong (Solea solea)"
sp_name = ""
log_trans = gsub("hr","uur",
error regmatches(comment_en,
regexpr("\\d+\\.\\d+\\s*kg/hr",comment_en)))
}
<- paste0("De vangstgegevens voor de ",sp_name," werden opgehaald uit de ILVO-databases (SmartFish en VISTools)",
comment_nl " en zijn gekoppeld aan oceaanfysica- en biogeochemische gegevens van Marine Copernicus en statische zeebodemvariabelen van EMODnet.",
"Tijdens het trainingsproces werden hyperparameters getuned en recursieve featureselectie toegepast om het model te optimaliseren. ",
" De extrapolatieprestaties van het model werden geëvalueerd met gegevens uit 2024 (07-Jan-2024 to 07-Jun-2024)",
log_trans, ", wat resulteert in een root mean square error (RMSE) van ",
". De modelprestaties werden geëvalueerd op ",eval_date,".")
error,
if(grepl("and decision trees as base learners",source)){
<- sub("and decision trees as base learners","met decision trees als basisleerders",source)
source_nl
if(grepl("with Tweedie regression",source_nl)){
<- sub("with Tweedie regression","en Tweedie-regressie",source_nl)
source_nl
}
}
<- list(
nc_info[[i]] model_info_en = HTML(paste0("The Machine Learning (ML) algorithm used to train the data is ",
", using the R package ", library, ". <br><br>",
source,gsub("\\((\\b[A-Z][a-z]+\\s[a-z]+\\b)\\)","(<i>\\1</i>)",comment_en),
"<br><br>To know more about the model training process, contact ",
"or visit this <a href='https://vistools.quarto.pub/iliad-pilot-documentation/sections/guide/07_01_fish_suit.html' target='blank'>documentation</a>.")),
contact,dash_info_en = HTML(paste0("On average, the predictions made by this model deviate by ","<strong>",
regmatches(comment_en,regexpr("\\d+\\.\\d+\\s*kg/hr",comment_en)),
"</strong>"," from the actual values.")),
model_info_nl = HTML(paste0("Het Machine Learning (ML) algoritme dat is gebruikt om de gegevens te trainen is, ",
" met behulp van de R-package ", library, ". <br><br>",
source_nl,gsub("\\((\\b[A-Z][a-z]+\\s[a-z]+\\b)\\)","(<i>\\1</i>)",comment_nl),
"<br><br>Voor meer informatie over het modeltrainingsproces kunt u contact opnemen met ",
" of ga naar deze <a href='https://vistools.quarto.pub/iliad-pilot-documentation/sections/guide/07_01_fish_suit.html' target='blank'>documentatie</a>."
contact,
)),dash_info_nl = HTML(paste0("Gemiddeld wijken de voorspellingen die door dit model gemaakt worden ","<strong>",
gsub("hr","uur",
regmatches(comment_en,regexpr("\\d+\\.\\d+\\s*kg/hr",comment_en))),
"</strong>"," af van de werkelijke waarden."))
)names(nc_info)[i] <- Species
nc_close(nc)
}
# The following codes will create a directory of texts both in English and Dutch.
# Depending on the selected languages, the UI texts will dynamically change. Codes
# wrapped in <>...</> are HTML codes and these will be rendered as HTML tags in
# the UI instead of just being plain texts.
<- list(
translations en = list(
choose_species = "Choose a Species",
species_mapping = c("Common sole" = "SOL", "European plaice" = "PLE"),
choose_date = "Select Date",
warning_lab = HTML('<i class="fa-solid fa-triangle-exclamation" style="color: #ff9b3d;"></i> <strong>Warning</strong>'),
modal_msg = "The catch values you see here are based on predictions made by the models and, as such,
come with inherent uncertainty and potential error. These values do not perfectly reflect
reality and should not be treated as absolute truths. They serve as a guide, providing useful insights,
but it is important to approach them with caution. Decisions should be made considering these predictions
along with other relevant factors, such as your knowledge of the fisheries, local conditions,
and practical experience. Recognizing the limitations of the model, these insights should be used as one of several inputs in the decision-making process.",
dash_info = list(
PLE = nc_info[["PLE"]][["dash_info_en"]],
SOL = nc_info[["SOL"]][["dash_info_en"]]
),model_info = list(
PLE = nc_info[["PLE"]][["model_info_en"]],
SOL = nc_info[["SOL"]][["model_info_en"]]
),dash_about = "The underlying model was trained on data from a specific set of catch locations.
The default map shows predictions within a 10 km radius of the training data points.
Predictions for areas beyond this range may be unreliable due to lack of validation.
Use the model only in locations where applicability has been confirmed,
and exercise caution when applying it to new areas. To view predictions for the entire region,
please enable the option below.",
dash_show = HTML("Show Entire Prediction Area"),
tab_title = "Model Information",
page_model_title = "Description",
leaflet_unit = "kg/hr",
map_warning = "<div style='background: #fff3cd; padding: 12px; border-radius: 5px; font-size: 18px; color: #856404;'><strong>Note: </strong>Predictions in areas enclosed by the black line are validated.</div>"
),nl = list(
choose_species = "Kies een Soort",
species_mapping = c("Tong" = "SOL", "Schol" = "PLE"),
choose_date = "Selecteer Datum",
warning_lab = HTML('<i class="fa-solid fa-triangle-exclamation" style="color: #ff9b3d;"></i> <strong>Waarschuwing</strong>'),
modal_msg = "De vangstwaarden die je hier ziet, zijn gebaseerd op voorspellingen van de modellen en brengen daardoor inherente onzekerheid en
mogelijke fouten met zich mee. Deze waarden weerspiegelen niet volledig de werkelijkheid en moeten niet worden behandeld als
absolute waarheden. Ze dienen als een gids en bieden nuttige inzichten, maar het is belangrijk om ze met voorzichtigheid
te benaderen. Beslissingen moeten worden genomen door deze voorspellingen te overwegen, samen met andere relevante factoren,
zoals jouw kennis van de visserij, lokale omstandigheden en praktische ervaring. Het model heeft zijn beperkingen en moet
gebruikt worden als één van de verschillende hulpmiddellen in je het besluitvormingsproces.",
dash_info = list(
PLE = nc_info[["PLE"]][["dash_info_nl"]],
SOL = nc_info[["SOL"]][["dash_info_nl"]]
),model_info = list(
PLE = nc_info[["PLE"]][["model_info_nl"]],
SOL = nc_info[["SOL"]][["model_info_nl"]]
),dash_about = "Het onderliggende model is getraind op gegevens van een specifieke lijst van vangstlocaties.
De standaardkaart toont voorspellingen die binnen een straal van 10 km liggen van de trainingsdatapunten.
Voorspellingen voor de gebieden buiten deze straal zijn onbetrouwbaar door het gebrek aan validatie.
Gebruik het model alleen op de locaties waar de validatie en toepasbaarheid bevestigd is,
en wees voorzichtig wanneer het op een nieuw gebied toegepast wordt. Om voorspellingen voor de gehele regio te bekijken,
zet de optie hieronder aan.",
dash_show = HTML("Toon Het Hele <br>Voorspellingsgebied"),
tab_title = "Modelinformatie",
page_model_title = "Beschrijving",
leaflet_unit = "kg/uur",
map_warning = "<div style='background: #fff3cd; padding: 12px; border-radius: 5px; font-size: 18px; color: #856404;'><strong>Opmerking: </strong>Voorspellingen in gebieden omgeven door de zwarte lijn zijn gevalideerd.</div>"
) )
User Interface (UI)
The backbone of the app is defined as the user interface ui
and is a top level navigation bar (page_navbar
) that is used to toggle a set of panels (i.e. nav_panel()
elements contained within the page_navbar
function; Figure 2). Some of the layout of the app is generated through HTML tags
. For instance, tags$link defines the relationship between the shiny app script and an external style sheet that allows the use of Font Awesome. The app is available in Dark and Light mode (hit the button in the upper right corner) and in Dutch and English (choose from the drop-down menu
in the upper-right corner).
# Define the UI ----------------------------
<- page_navbar( # This will render a User Interface with a sidebar navigation and 3 tabs, light-dark switch, and language switch
ui
# import the CSS to use Bootstrap icons
$head( # The tags object in shiny is a list of 100+ simple functions that create HTML tags, some of which are used here to layout the Shiny App
tags$link(rel = "stylesheet",
tagshref = "https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css"),
# import the CSS to use Font Awesome
$link(rel = "stylesheet",
tagshref = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"),
# CSS codes to customize the UI
$style(
tagsHTML("
.modal-dialog {
position: fixed;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
margin: 0 !important;
}
.modal-content {
background-color: #fff3cd;
border: 2px solid #856404;
border-radius: 15px;
padding: 30px;
box-shadow: 0px 4px 15px rgba(0, 0, 0, 0.1);
}
.modal-title {
font-size: 30px;
font-weight: bold;
color: #856404;
}
.modal-body {
font-size: 18px;
color: #6c757d;
padding: 20px 0;
}
.modal-icon {
font-size: 30px;
color: #856404;
text-align: center;
margin-bottom: 20px;
}
.dark-mode select {
background-color: #333; /* Dark background */
color: white; /* Light text */
}
")
)
),
title = tags$a( # The Iliad logo that links to the iliad Marketplace website
href = "https://ocean-twin.eu/marketplace",
target = "_blank",
$img(
tagssrc = "https://vistools.quarto.pub/iliad-pilot-documentation/images/iliad_logo.png",
height = "25px",
style = "padding: 0; margin-right: 10px; "
)
),bg = "#1A1B1E", # The background color of the top level navigation bar
window_title = "Catch Predictions",
theme = bs_theme( # The general theme of the Shiny App
version = 5,
bootswatch = "flatly",
primary = "#0197F6",
base_font = font_google("Open Sans"),
nav_font = font_google("Roboto")
),
# First tab
nav_panel( # The first tab showing the Catch Predictions Dashboard
$head(
tags$style(HTML("
tags .shiny-input-container label {
font-weight: bold;
}
"))
),title = "Dashboard",
layout_sidebar(
sidebar = sidebar( # A floating sidebar displayed on the left-hand side of the page which allows the user to adjust features shown on the map
padding = c(40,30,0,30),
width = "350px",
selectInput( # drop down menu to choose the species that is to be displayed on the map
inputId = "species",
label = textOutput("dash_sp_label"),
width = "280px",
choices = c("Tong" = "SOL", "Schol" = "PLE")),
br(),
chooseSliderSkin("Flat", color="#002F70"), # Slider to select the date for which predictions are to be displayed on the map
sliderInput(inputId = "date",
label = textOutput("dash_date_label"),
min = as.Date(min(Dates)),
max = as.Date(max(Dates)),
value = as.Date(min(Dates)),
step = 1,
width = "280px",
timeFormat = "%d-%b"),
br(),
br(),
br(),
hr(),
p(h4(
uiOutput("dash_warning_label"), inline=TRUE), # Text describing the estimated mean deviation of the predictions versus reality
span(htmlOutput("overlay_text"))),
p(textOutput("dash_about_label")), # Text describing how to use displayed predictions and best deal with uncertainties
materialSwitch(inputId = "whole_area", # switch to turn off (or back on) the mask over the areas with low data availability
label = htmlOutput("dash_showArea_label"),
status="danger",
right=T, inline=T),
br(),
br(),
div( # footnote on developer (ILVO) with custom styling and clickable logo which redirects to developers website
style = "position: fixed; bottom: 0; left: 0; width: 350px; padding: 10px; text-align: left; background-color: #8F9193;",
$span("Developed by", style = "font-size: 14px; font-weight: bold; margin-top: 5px;"),
tags
$a(
tagshref = "https://ilvo.vlaanderen.be/nl/",
target = "_blank",
$img(src = "https://vistools.quarto.pub/iliad-pilot-documentation/images/ILVO_logo.png",
tagswidth = "70px",
style = "display: inline-block; vertical-align: middle; margin-left: 10px;")
)
)
),leafletOutput("map", width = "100%", height = "100%"), # a leaflet map is set as main content within layout_sidebar()
)
),
# Second tab
nav_panel( # Second tab with information text on model development
title = textOutput("nav_info_label"), # The tab name is either "Modelinformatie" or "Model Information" depending on the language
$style(HTML("
tags .styled-text {
margin: 20px 30px 10px 30px;
padding: 15px;
background-color: #f9f9f9;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
color: #333;
}
.styled-text h3 {
font-size: 2em;
color: #1940c3;
font-weight: bold;
}
.styled-text p {
font-size: 1.2em;
line-height: 1.5;
}
")),
div(class="styled-text",
h3(textOutput("info_title_label")), # The tab title is either "Beschrijving" or "Description" depending on the language
p(span(htmlOutput("layer_description"))) # Information on model development, either in Dutch or English
)
),
nav_item(a(href = "https://vistools.be", "VISTools") # Third tab which links to VISTools Analyses dashboard
),
nav_spacer(),
nav_item(input_dark_mode(id = "mode", mode="light")), # A button that toggles between dark and light modes on the right-hand side of the top level navigation bar
nav_menu( # A drop down menu to select the language on the right-hand side of the top level navigation bar
HTML('<i class="bi bi-translate"></i>'),
nav_item(actionLink("lang_nl","NL")),
nav_item(actionLink("lang_en","EN")),
align="right"
) )
Server logic
The server
function contains the logic to build the app’s outputs based on user inputs generated in the user interface ui
. The Catch Predictions Dashboard server controls what text is displayed in what language and what data is shown on the map based on the values that a user selects in the app.
English versus Dutch language through reactiveVal()
and a Translation Directory
All text in the app is either displayed in Dutch or English depending on which list item (either "nl"
or "en"
) is extracted from the translation directory translations
. In which language the text is to be extracted is determined by the reactive value (reactiveVal()
) named lang
which is created in the server logic and is implemented as follows: translations[[lang()]]
. The resulting list is then further subsetted in order to place the appropriate text blocks at the appropriate locations in the user interface. An example is the label dash_sp_label
(Figure 3). This label is contained within the output
object and relates to a textOutput()
function in the user interface ui
that generates the label of a drop-down menu to choose the displayed species on the map. The associated text of the label is contained within translations[[lang()]]$choose_species
.
To a lesser extent, the reactive value lang
is also used in if else
statements and within observe()
and observeEvent()
functions throughout the server logic.
# Define server logic ----------------------------
<- function(input, output, session) {
server
<- reactiveVal("nl") # Set initial language value to "nl". Depending on the given input in the user interface's drop down menu this can change to "en"
lang <- reactiveVal(NULL)
sp <- reactiveVal(NULL)
date <- reactiveVal(NULL)
area
observeEvent(input$lang_en, { # if user changes the language in the drop-down menu to English change the reactive value lang accordingly
lang("en")
})
observeEvent(input$lang_nl, { # Change everything back to Dutch if user switches the language back to Dutch
lang("nl")
})
observe({ # Show the disclaimer in the appropriate language at page entry and when the language is adjusted
if(lang() %in% c("nl","en")){
showModal(
modalDialog(
title = tags$div(
icon("exclamation-triangle", class = "modal-icon"),
"Disclaimer"
),easyClose = TRUE,
lang()]]$modal_msg
translations[[
)
)
}
updateSelectInput(session, "species", choices = translations[[lang()]][["species_mapping"]])
#
})
$dash_sp_label <- renderText({translations[[lang()]]$choose_species}) # Display the text label for the drop-down menu from the sidebar of the first panel either in Dutch or English
output$dash_date_label <- renderText({translations[[lang()]]$choose_date}) # Display the text label for the slider from the sidebar of the first panel either in Dutch or English
output$dash_warning_label <- renderUI({translations[[lang()]]$warning_lab}) # Display the text label on estimated mean deviation of the predictions versus reality from the sidebar of the first panel either in Dutch or English
output$overlay_text <- renderUI({translations[[lang()]][["dash_info"]][[input$species]]})
output$dash_about_label <- renderText({translations[[lang()]]$dash_about}) # Display the text on how to use displayed predictions from the sidebar of the first panel either in Dutch or English
output$dash_showArea_label <- renderUI({translations[[lang()]]$dash_show}) # Display the text label for the switch to turn off (or back on) the mask on the areas with low data availability either in Dutch or English
output$nav_info_label <- renderText({translations[[lang()]]$tab_title}) # Display the name of the second tab either in Dutch or English
output$info_title_label <- renderText({translations[[lang()]]$page_model_title}) # Display the main title in the second tab either in Dutch or English
output$layer_description <- renderUI({translations[[lang()]][["model_info"]][[input$species]]}) # Information on model development that is displayed in tab two, either in Dutch or English, is dependent on the chosen species in tab one output
Building the map
A map is generated by layering geographic location labels and a catch prediction raster on top of a base map which is either dark or light depending on the mode selected by the user. The catch prediction raster is pulled from a raster_list
build out of the netCDF files loaded upon start-up of the app. Which raster is pulled is determined through the reactive expression raster
and is based on the species chosen from a drop-down menu in the sidebar of the first tab and the date selected on a slider. A switch in the user interface determines whether a masked raster is shown with predictions for only the validated areas or the whole area (Figure 4).
observe({ # set the input parameters to generate the appropriate map
sp(input$species)
date(as.character(input$date))
if(input$whole_area) area("whole_area") else area("mask")
})
<- reactive({ # create reactive expression that selects the appropriate raster from the raster list based on the species, area and date user input
raster sp()]][[area()]][[date()]]
raster_list[[
})
<- reactive({ # create reactive expression for the maximum predicted value of the selected raster layer to enable setting the color scale
max_value sp()]
max_values[
})
<- reactive({ # create reactive expression to set the color scale of the map
pal colorNumeric(
palette = scico(n = 10, palette = "batlowW", direction = -1),
domain = c(0, max_value()), na.color = "transparent"
)
})
<- reactive({ # create reactive expression to switch between dark and light base maps depending on the input of the dark and light mode switch
base_map if (input$mode == "light") "CartoDB.PositronNoLabels" else "CartoDB.DarkMatterNoLabels"
})
<- reactive({ # create reactive expression to switch between dark and light base map labels depending on the input of the dark and light mode switch
base_map_label if (input$mode == "light") "CartoDB.PositronOnlyLabels" else "CartoDB.DarkMatterOnlyLabels"
})
$map <- renderLeaflet({ # Create the map
output
leaflet() %>% # Set the location and zoom level for the base map in the first tab; the layers are added in the following lines of code
setView(lng = 1.5, lat = 51.5, zoom = 6)
})
observeEvent(input$mode,{ # If switching between light and dark mode, adjust layers of map accordingly using the reactive values created before
leafletProxy("map") %>% # Creates a map-like object that can be used to customize and control a map that has already been rendered
clearTiles() %>%
clearImages() %>%
clearShapes() %>%
clearControls() %>%
addProviderTiles(base_map(), layerId = "BaseMap") %>% # add the base map (either light or dark)
addRasterImage(raster(), colors = pal(), opacity = 1, group = "Value", project = TRUE, layerId="Raster") %>% # add the appropriate raster layer that was selected based on species, area and date
addProviderTiles(base_map_label(), layerId = "MapLabels") %>% # add the labels on top of the selected raster layer
addLegend("bottomright", title = translations[[lang()]]$leaflet_unit, values = c(0, max_value()), pal = pal(), opacity = 1) %>% # add legend in the appropriate language
::addImageQuery(x = raster(), project = TRUE, group = "Value", prefix = "", position = "bottomright", digits = 2) # Add image query functionality in which value is shown when hoovering the pointer over the map
leafem
if(input$whole_area){ # Depending on the input of the area switch add polygon layer indicating the areas that are verified with VISTools data
leafletProxy("map") %>%
addPolygons(data = mask, layerId = "AreaCover",
color = "black", weight = 2, fill = FALSE) %>%
addControl(html = translations[[lang()]][["map_warning"]],
position = "bottomleft", layerId = "Warning") # If no masking layer is present add a warning message in the bottomleft corner of the map
}
})
observeEvent(list(input$species,input$date,input$whole_area),{ # If the user changes the species, date or area, adjust layers of map accordingly using the reactive values created before
leafletProxy("map") %>%
removeImage(layerId = "Raster") %>%
removeShape(layerId = "AreaCover") %>%
removeTiles(layerId = "MapLabels") %>%
clearControls() %>%
addRasterImage(raster(), colors = pal(), opacity = 1, group = "Value", project = TRUE, layerId="Raster") %>%
addProviderTiles(base_map_label(), layerId = "MapLabels") %>%
addLegend("bottomright", title = translations[[lang()]]$leaflet_unit, values = c(0, max_value()), pal = pal(), opacity = 1) %>%
::addImageQuery(x = raster(), project = TRUE, group = "Value", prefix = "", position = "bottomright", digits = 2)
leafem
if(input$whole_area){
leafletProxy("map") %>%
addPolygons(data = mask, layerId = "AreaCover",
color = "black", weight = 2, fill = FALSE) %>%
addControl(html = translations[[lang()]][["map_warning"]],
position = "bottomleft", layerId = "Warning")
}
})
observeEvent(lang(),{ # If the language changes, adjust the warning text in the bottom left corner of the map accordingly
if(input$whole_area){
leafletProxy("map") %>%
removeControl(layerId = "Warning") %>%
addControl(html = translations[[lang()]][["map_warning"]],
position = "bottomleft", layerId = "Warning")
}
})}
Run the app!
Now we are only one line of code away of making the magic happen, that is, making the app work! Time to see where we can expect to find high catches of the selected flatfish!
shinyApp(ui = ui, server = server)