-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathApp.R
2635 lines (2286 loc) · 140 KB
/
App.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#Mindrum DA model
# Holistic decision analysis model of agrosilvopastoral agroforestry system of Mindrum farm ####
# 14.9744 ha of two strip SRC operation measuring 200 x 20 m each
# Scenario of changing silvoarable to silvopastoral is yet to be introduced, this will be first to introduce livestock elements
# Institutional support needs to reflect UK conditions
# env vs eco vs social influence can be plotted as in the banana paper or smooth graphs back-to-back using compared to the baseline:
# https://github.com/hortibonn/Plotting-High-Dimensional-Data
#### Install Libraries ####
if (!requireNamespace("shiny", quietly = TRUE)) {
install.packages("shiny")
}
library(shiny)
if (!requireNamespace("bslib", quietly = TRUE)) {
install.packages("bslib")
}
library(bslib)
if (!requireNamespace("shinythemes", quietly = TRUE)) {
install.packages("shinythemes")
}
library(shinythemes)
if (!requireNamespace("shinyWidgets", quietly = TRUE)) {
install.packages("shinyWidgets")
}
library(shinyWidgets)
if (!requireNamespace("decisionSupport", quietly = TRUE)) {
install.packages("decisionSupport")
}
library(decisionSupport)
if (!requireNamespace("tidyverse", quietly = TRUE)) {
install.packages("tidyverse")
}
library(tidyverse)
if (!requireNamespace("readr", quietly = TRUE)) {
install.packages("readr")
}
library(readr) # For reading and writing CSV files
if (!requireNamespace("ggridges", quietly = TRUE)) {
install.packages("ggridges")
}
library(ggridges)
if (!requireNamespace("ggplot2", quietly = TRUE)) {
install.packages("ggplot2")
}
library(ggplot2)
if (!requireNamespace("dplyr", quietly = TRUE)) {
install.packages("dplyr")
}
library(dplyr)
if (!requireNamespace("here", quietly = TRUE)) {
install.packages("here")
}
library(here)
# Define user project file count limit
max_files <- 5
# Define list of admin users
admin_users <- c("Adrian", "Prajna", "Christine")
# Pre-requisites to UI ####
# Countries and states ####
country_states <-
list(
"None" = c("none"),
"Germany" = c("Baden-Württemberg", "Bavaria", "Berlin", "Brandenburg", "Bremen",
"Hamburg", "Hesse", "Lower Saxony", "Mecklenburg-Vorpommern",
"North Rhine-Westphalia", "Rhineland-Palatinate", "Saarland",
"Saxony", "Saxony-Anhalt", "Schleswig-Holstein", "Thuringia"),
"Czech Republic" = c("Prague", "Central Bohemian", "South Bohemian", "Plzeň",
"Karlovy Vary", "Ústí nad Labem", "Liberec", "Hradec Králové",
"Pardubice", "Vysočina", "South Moravian", "Olomouc",
"Zlín", "Moravian-Silesian"),
"Belgium" = c("Flanders", "Wallonia", "Brussels"),
"Bulgaria" = c("Blagoevgrad", "Burgas", "Varna"),
"Hungary" = c("Budapest", "Pest", "Csongrád"),
"Denmark" = c("North Jutland", "Central Jutland", "Southern Denmark"),
"Spain" = c("Andalusia", "Catalonia", "Madrid"),
"England" = c("Bedfordshire", "Berkshire", "Bristol", "Buckinghamshire",
"Cambridgeshire", "Cheshire", "Cornwall", "Cumbria", "Derbyshire",
"Devon", "Dorset", "Durham", "East Sussex", "Essex", "Gloucestershire",
"Greater London", "Greater Manchester", "Hampshire", "Herefordshire",
"Hertfordshire", "Isle of Wight", "Kent", "Lancashire",
"Leicestershire", "Lincolnshire", "Merseyside", "Norfolk", "North Yorkshire",
"Northamptonshire", "Northumberland", "Nottinghamshire", "Oxfordshire",
"Rutland", "Shropshire", "Somerset", "South Yorkshire", "Staffordshire",
"Suffolk", "Surrey", "Tyne and Wear", "Warwickshire", "West Midlands",
"West Sussex", "West Yorkshire", "Wiltshire", "Worcestershire"),
"Scotland" = c("Edinburgh", "Glasgow", "Aberdeen"),
"Wales" = c("Cardiff", "Swansea", "Newport"),
"Northern Ireland" = c("Belfast", "Derry", "Lisburn"),
"France" = c("Île-de-France", "Auvergne-Rhône-Alpes", "Nouvelle-Aquitaine"),
"Italy" = c("Lombardy", "Lazio", "Campania")
)
# Funding data per country and state and state ####
# For demonstration purposes, I'll add example funding schemes for a few countries and states
funding_data <-
list(
"None" = list(
"none" = list(
one_time_funding_schemes = c(),
funding_onetime_values_named = setNames(c(),
c()),
funding_onetime_per_ha_schemes = c(),
funding_onetime_per_tree_schemes = c(),
funding_onetime_percentage_schemes = c(),
annual_funding_schemes = c(),
funding_yearly_values_named = setNames(c(),c())
)
),
"England" = list(
"Northumberland" = list(
# one-time
one_time_funding_schemes = c(
"Countryside Stewardship - PA4 Agroforestry [GBP/ha]",
"Countryside Stewardship - AF Woodland trees [GBP/tree]",
"Countryside Stewardship - AF Fruit trees [GBP/tree]",
"Tree guard (TE6) [GBP/guard/tree]",
"Species Diversity Bonus (if more than 5 diff. species) [GBP/tree]",
"Woodland Trust - MOREwoods Scheme (min. 0.5 ha woodland with 500+ trees) [% of initial costs]",
"Woodland Trust - MOREwoods Scheme with contractor (min. 1 ha woodland) [% of initial costs]"
),
funding_onetime_values_named = setNames(c(1268.08, 5.40, 17.83, 3.95, 1.16, 0.75, 0.6),
c("Countryside Stewardship - PA4 Agroforestry [GBP/ha]",
"Countryside Stewardship - AF Woodland trees [GBP/tree]",
"Countryside Stewardship - AF Fruit trees [GBP/tree]",
"Tree guard (TE6) [GBP/guard/tree]",
"Species Diversity Bonus (if more than 5 diff. species) [GBP/tree]",
"Woodland Trust - MOREwoods Scheme (min. 0.5 ha woodland with 500+ trees) [% of initial costs]",
"Woodland Trust - MOREwoods Scheme with contractor (min. 1 ha woodland) [% of initial costs]"
)
),
funding_onetime_per_ha_schemes = c("Countryside Stewardship - PA4 Agroforestry [GBP/ha]"),
funding_onetime_per_tree_schemes = c("Countryside Stewardship - AF Woodland trees [GBP/tree]",
"Countryside Stewardship - AF Fruit trees [GBP/tree]",
"Tree guard (TE6) [GBP/guard/tree]",
"Species Diversity Bonus (if more than 5 diff. species) [GBP/tree]"
),
funding_onetime_percentage_schemes = c("Woodland Trust - MOREwoods Scheme (min. 0.5 ha woodland with 500+ trees) [% of initial costs]",
"Woodland Trust - MOREwoods Scheme with contractor (min. 1 ha woodland) [% of initial costs]"
),
#annual
annual_funding_schemes = c("SFI Premium payment - low density AF on less sensitive land [GBP/ha/year]",
"SFI Premium payment - low density AF on more sensitive land [GBP/ha/year]",
"SFI Premium payment - medium density in-field AF [GBP/ha/year]",
"SFI Premium payment - high density in-field AF [GBP/ha/year]"
),
funding_yearly_values_named = setNames(c(385, 385, 595, 849),
c("SFI Premium payment - low density AF on less sensitive land [GBP/ha/year]",
"SFI Premium payment - low density AF on more sensitive land [GBP/ha/year]",
"SFI Premium payment - medium density in-field AF [GBP/ha/year]",
"SFI Premium payment - high density in-field AF [GBP/ha/year]"
)
)
),
# not real subsidy schemes!!!
"Kent" = list(
one_time_funding_schemes = c("Kent Scheme - Tree Planting [GBP/tree]",
"Kent Scheme - Land Preparation [GBP/ha]"),
funding_onetime_values_named = setNames(c(10, 500),
c("Kent Scheme - Tree Planting [GBP/tree]",
"Kent Scheme - Land Preparation [GBP/ha]")),
funding_onetime_per_ha_schemes = c("Kent Scheme - Land Preparation [GBP/ha]"),
funding_onetime_per_tree_schemes = c("Kent Scheme - Tree Planting [GBP/tree]"),
funding_onetime_percentage_schemes = c(),
annual_funding_schemes = c("Kent Annual Support [GBP/ha/year]" ),
funding_yearly_values_named = setNames(c(100),c("Kent Annual Support [GBP/ha/year]"))
)
),
# Add more countries and states with their funding schemes here - T6.3
"Germany" = list(
"Bavaria" = list(one_time_funding_schemes = c("Bavaria Start-up Aid [EURO/ha]",
"Bavaria Tree Grant [EURO/tree]" ),
funding_onetime_values_named = setNames(c(800, 15),
c( "Bavaria Start-up Aid [EURO/ha]",
"Bavaria Tree Grant [EURO/tree]")),
funding_onetime_per_ha_schemes = c("Bavaria Start-up Aid [EURO/ha]"),
funding_onetime_per_tree_schemes = c("Bavaria Tree Grant [EURO/tree]"),
funding_onetime_percentage_schemes = c(),
annual_funding_schemes = c("Bavaria Annual Payment [EURO/ha/year]"),
funding_yearly_values_named = setNames(c(200),c("Bavaria Annual Payment [EURO/ha/year]") )
)
)
)
### UI ######
ui <- fluidPage(
theme = bs_theme(version = 5,
bootswatch = 'flatly',
base_font = font_google("Roboto")),
# tags$img(src = "data/images/ReFOREST_logo_horizontal.jpg"),
# Include custom CSS for the scroll bar and accordion styling
tags$head(
tags$style(HTML("
/* Custom scrollbar styling */
::-webkit-scrollbar {
width: 10px;}
::-webkit-scrollbar-track {
background: #f1f1f1;}
::-webkit-scrollbar-thumb {
background: skyblue;
}
::-webkit-scrollbar-thumb:hover {
background: linear-gradient(lightblue, skyblue, dodgerblue, blue, darkblue);}
/* For Firefox */
body {
scrollbar-width: thin;
scrollbar-color: skyblue #f1f1f1;}
/* Header styling */
.app-header {
/*background: linear-gradient(135deg, #1E90FF, #00FA9A, #FFD700); */ /* light bue to light green to gold */
/* background: linear-gradient(135deg, #32CD32, #228B22, #006400); LimeGreen to ForestGreen to DarkGreen */
background-color:#228B22;
color: white;
padding: 15px 0;
position: relative;
}
.app-title {
font-size: 48px;
font-weight: bold;
text-align: center;
margin: 0;
}
/* Accordion header styling */
.accordion-button {
background-color: #007BFF;
color: white;
}
.accordion-button:not(.collapsed) {
background-color: #0056b3;
}
.accordion-button:hover {
background-color: #0056b3;
}
.accordion-button:focus {
box-shadow: none;
}
.my-btn {
display: block;
text-align: center;
background: #238a21;
/* rgb(241 119 65 / 75%); */
/* width: 50%; */
/* height: 100%; */
color: white;
/* transform: translateX(-50%); */
/* left: 50%; */
/* font-size: 3rem; */
border-radius: 20px;
position: relative;
margin-top: 25px;
margin-bottom: 25px;
}
"))
),
# Responsive Layout with Animated Width
# layout_column_wrap(
# width = "200px",
# anim_width("100%", "67%"), # Smooth resizing transition
# Application title
titlePanel(
div(class = "app-header",
# Logos column
fluidRow(column(width = 2,
align = "right",
tags$a(href = "https://agroreforest.eu/", # Add URL here
tags$img(src = "ReFOREST_logo_horizontal_transparent.png",
style = "max-width: 100%; height: auto;"
#height = "70px"
),
target="_blank")),
column(width = 8,
align = "center",
h2(class = "app-title",
"Holistic Decision Analysis for an Agrisilvicultural Agroforestry System")),
column(width = 2,
align = "left",
tags$a(href = "https://www.gartenbauwissenschaften.uni-bonn.de/", # Add URL here
tags$img(src = "UniBonnHortiBonn_logo_transparent.png",
style = "max-width: 100%; height: auto;"
#height = "100px"
),
target="_blank")),
windowTitle = "MyPage")
)),
sidebarLayout(
sidebarPanel(width = 4,
# style = "height: 100vh; overflow-y: auto",
style = "height: 100%; overflow-y: auto",
# Collapsible sections
accordion(
id = "collapseSidebar",
open = FALSE,
# Basic Information Panel
accordion_panel(
title = "Basic Information",
icon = icon("info-circle"),
########### save load delete logic ##############
# Admin user selection UI
uiOutput("admin_user_select"),
textInput("project_name", "Project Name:", value = ""),
actionButton("save", "Save Settings"),
# column(width = 6,
# textInput("version", label = "Version", value = "1")),
selectInput("version_select", "Select Version to Load:", choices = NULL),
actionButton("load", "Load Selected Version"),
selectInput("delete_version_select", "Select Version to Delete:", choices = NULL),
actionButton("delete", "Delete Selected Version"),
########### save load delete logic ##############
# dateInput("date", label = "Date input", value = Sys.Date()), # format(Sys.time(), "%Y-%m-%d_%H-%M" for naming the downloaded file
#
# downloadButton("save_data", "Save project data"), # !! enable save local machine and our server
br(),
br(),
actionButton("run_simulation", "Run Model", icon = icon("play"), class = "btn-primary"),
textOutput("validation_message") # To display validation messages
),
### Data entry by users ######
# Basic Farm and Decision Parameters Panel
accordion_panel(
title = "Basic Farm and Decision Parameters",
icon = icon("tractor"),
# System modulators and risks
h4("System Modulators"),
numericInput("num_simulations_c", "Number of Simulations",
min = 10, max = 50000, value = 100),
numericInput("n_years_c", "Simulation period in years",
min = 1, max = 100, value = 40),
sliderInput("discount_rate_p", "Discount rate (%)",
min = 1, max = 50, value = c(1.8,5), step = 1),
sliderInput("var_CV_p", "Coefficient of Variation",
min = 1, max = 50, value = c(5,10), step = 1),
br(),
h4("Farm Details"),
# Dropdown menu of countries, state and funding schemes
selectInput("country", "Select Country:", choices = names(country_states), selected = NULL),
uiOutput("state_ui"),
uiOutput("funding_one_ui"),
uiOutput("funding_yearly_ui"),
numericInput("annual_external_support_c", "Annual private support [GBP/ha]",
min = 1, max = 5000, value = 50),
numericInput("onetime_external_support_c", "One-time private support [GBP]",
min = 1, max = 1000000, value = 500),
numericInput("arable_area_c", "Size of the farm [ha]", min = 0.001, max = 1000, value = 14.9),
sliderInput("subsidy_application_p", "Time spent on application for subsidies [h]",
min = 1, max = 500, value = c(10,40)),
),
accordion_panel(
title = "Existing System",
icon = icon("tractor"),
# h4("Existing System"),
selectInput(
inputId = "treeless_crop_rotation", #"crop_rotation",
label = "Choose a crop rotation scheme:",
choices = c("Crop Rotation 1: 3 years of Herbal Ley followed by Winter wheat" = "rotation_1",
"Crop Rotation 2: 2 yeras of Herbal Ley followed by rotation with Winter wheat, Spring barley, summer beans and Winter oats" = "rotation_2"),
selected = NULL
),
# Checkbox to introduce animals with numbers
# checkboxInput("include_animals_c", "Introduce Animals", FALSE),
# conditionalPanel(
# condition = "input.include_animals_c == true",
# numericInput("treeless_number_of_animals_c", "Number of Animals:", value = 1, min = 1)
# Checkbox to introduce animals with grazing %
checkboxInput("treeless_include_animals_c", "Introduce Animals", FALSE),
conditionalPanel(
condition = "input.treeless_include_animals_c == true",
selectInput("treeless_animal_type", "Select Animal",
choices = c("cattle", "sheep"), #"Goats", "Chickens", "Turkeys"),
selected = NULL,
multiple = TRUE),
uiOutput("treeless_grazing_intensity"), # Dynamic UI for slider inputs (one per selected animal)
textOutput("treeless_grazing_warning"), # Display a warning message if the total exceeds 1
),
),
accordion_panel(
title = "Agroforestry Systems",
icon = icon("tree"),
h4("General preparation"),
sliderInput("tree_planting_p", "Cost of digging tree well & planting tree [GBP/tree]",
min = 1, max = 5000, value = c(10,50)),
sliderInput("farmer_planning_time_p", "Time spent planning by farmer [h]",
min = 0, max = 1000, value = c(10,40)),
sliderInput("planning_consulting_p", "Total payment of hired planner/consultant [GBP]",
min = 0, max = 10000, value = c(50,1000)),
sliderInput("weed_protection_p", "Cost of controlling weeds during establishment [GBP]",
min = 0, max = 5000, value = c(15,25)),
br(),
h4("Design 1"),
numericInput("AF1_tree_row_area_c", "Total area of tree rows [ha]",
min = 0.001, max = 100, value = 0.8),
numericInput("AF1_num_trees_c", "Number of trees",
min = 0, max = 10000, value = 5926),
sliderInput("AF1_plant_protection_p", "Cost of fencing [GBP/tree]",
min = 0, max = 10, value = c(0.843,1.022)),
sliderInput("SRC_cutting_price_p", "Price of willow cuttings [GBP]",
min = 0, max = 10, value = c(0.1,0.6)),
sliderInput("SRC_field_prep_p", "Subsoiling and harrowing cost [h/ha]",
min = 1, max = 10, value = c(2.6,3.8)),
sliderInput("SRC_planting_p", "Cost of planting cuttings mechanically [h/ha]",
min = 1, max = 15, value = c(4,8)),
sliderInput("SRC_machine_rent_p", "Rent of planting machine [GBP]",
min = 1, max = 500, value = c(150,300)),
sliderInput("harvest_interval_SRC_p", "Number of years between SRC harvest [ha]",
min = 1, max = 60, value = c(3,5)),
sliderInput("af1_added_management_time_factor_p", "Extra time for managing AF vs. Baseline [%]",
min = 1, max = 10, value = c(1.05,1.2)),
sliderInput("AF1_soil_loss_water_p", "Soil loss due to water [tons/ha/year]",
min = 1, max = 10, value = c(2,4)),
sliderInput("AF1_soil_loss_wind_p", "Soil loss due to wind [tons/ha/year]",
min = 1, max = 10, value = c(2,4)),
sliderInput("af1_less_grazing_management_time_factor_p", "Reduced livestock mgmt from AF providing partial natural paddock [%]",
min = 0, max = 1, value = c(0.8,0.9)),
sliderInput("AF1_woody_benefit_windreduc_p", " Stress Reduction and Livestock Performance Enhancement [%]",
min = 0, max = 1, value = c(0.01,0.02)),
selectInput(
inputId = "AF1_crop_rotation", #"crop_rotation",
label = "Choose a crop rotation scheme:",
choices = c("Crop Rotation 1: 3 years of Herbal Ley followed by Winter wheat" = "rotation_1",
"Crop Rotation 2: 2 yeras of Herbal Ley followed by rotation with Winter wheat, Spring barley, summer beans and Winter oats" = "rotation_2"),
selected = NULL
),
# Checkbox to introduce animals with numbers
# checkboxInput("AF1_include_animals_c", "Introduce Animals", FALSE),
# conditionalPanel(
# condition = "input.AF1_include_animals_c == true",
# numericInput("AF1_number_of_animals_c", "Number of Animals:", value = 1, min = 1)
# Checkbox to introduce animals with grazing %
checkboxInput("AF1_include_animals_c", "Introduce Animals", FALSE),
conditionalPanel(
condition = "input.AF1_include_animals_c == true",
selectInput("AF1_animal_type", "Select Animal",
choices = c("cattle", "sheep"), #"Goats", "Chickens", "Turkeys"),
selected = NULL,
multiple = TRUE),
uiOutput("AF1_grazing_intensity"), # Dynamic UI for slider inputs (one per selected animal)
textOutput("AF1_grazing_warning"), # Display a warning message if the total exceeds 1
),
br(),
h4("Design 2"),
numericInput("AF2_tree_row_area_c", "Total area of tree rows [ha]",
min = 0.001, max = 100, value = 0.45),
numericInput("num_oak_trees_c", "Number of oak trees",
min = 0, max = 50, value = 30),
sliderInput("oak_tree_cost_p", "Price of an oak sapling [GBP]",
min = 0., max = 500, value = c(1.77,3.49)),
numericInput("num_birch_trees_c", "Number of birch trees",
min = 0, max = 5000, value = 30),
sliderInput("birch_tree_cost_p", "Price of a birch sapling [GBP]",
min = 0., max = 10, value = c(1.77,2.19)),
numericInput("num_rowan_trees_c", "Number of rowan trees",
min = 0, max = 100, value = 30),
sliderInput("rowan_tree_cost_p", "Price of a rowan sapling [GBP]",
min = 0., max = 500, value = c(1.77,2.49)),
numericInput("num_hazel_trees_c", "Number of hazel trees",
min = 0, max = 50, value = 30),
sliderInput("hazel_tree_cost_p", "Price of a hazel sapling [GBP]",
min = 0., max = 15, value = c(1.77,2.6)),
numericInput("num_damson_trees_c", "Number of damson trees",
min = 0, max = 100, value = 30),
sliderInput("damson_tree_cost_p", "Price of a damson sapling [GBP]",
min = 0., max = 50, value = c(15,21.6)),
numericInput("num_bcherry_trees_c", "Number of bird cherry trees",
min = 0, max = 100, value = 30),
sliderInput("bcherry_tree_cost_p", "Price of a bird cherry sapling [GBP]",
min = 0., max = 10, value = c(1.77,1.99)),
numericInput("AF2_num_shrubs_c", "Number of Shrubs",
min = 0, max = 4000, value = 900),
sliderInput("shrub_price_p", "Price per shrub [GBP/shrub]",
min = 0., max = 10, value = c(0.2,0.3)),
sliderInput("AF2_plant_protection_p", "Cost of fencing [GBP/tree]",
min = 1, max = 50, value = c(11.39,14.81)),
sliderInput("af2_added_management_time_factor_p", "Extra time for managing AF vs. Baseline [%]",
min = 1, max = 10, value = c(1.05,1.2)),
sliderInput("AF2_soil_loss_water_p", "soil loss due to water [tons/ha/year]",
min = 1, max = 50, value = c(2,4)),
sliderInput("AF2_soil_loss_wind_p", "soil loss due to wind [tons/ha/year]",
min = 1, max = 50, value = c(2,4)),
sliderInput("af2_less_grazing_management_time_factor_p", "Reduced livestock mgmt from AF providing partial natural paddock [%]",
min = 0, max = 1, value = c(0.8,0.9)),
sliderInput("AF2_woody_benefit_windreduc_p", " Stress Reduction and Livestock Performance Enhancement [%]",
min = 0, max = 1, value = c(0.03,0.055)),
selectInput(
inputId = "AF2_crop_rotation", #"crop_rotation",
label = "Choose a crop rotation scheme:",
choices = c("Crop Rotation 1: 3 years of Herbal Ley followed by Winter wheat" = "rotation_1",
"Crop Rotation 2: 2 yeras of Herbal Ley followed by rotation with Winter wheat, Spring barley, summer beans and Winter oats" = "rotation_2"),
selected = NULL
),
# Checkbox to introduce animals with number of animals
# checkboxInput("AF2_include_animals_c", "Introduce Animals", FALSE),
# conditionalPanel(
# condition = "input.AF2_include_animals_c == true",
# numericInput("AF2_number_of_animals_c", "Number of Animals:", value = 1, min = 1)
# ),
# Checkbox to introduce animals with grazing %
checkboxInput("AF2_include_animals_c", "Introduce Animals", FALSE),
conditionalPanel(
condition = "input.AF2_include_animals_c == true",
selectInput("AF2_animal_type", "Select Animal",
choices = c("cattle", "sheep"), #"Goats", "Chickens", "Turkeys"),
selected = NULL,
multiple = TRUE),
uiOutput("AF2_grazing_intensity"), # Dynamic UI for slider inputs (one per selected animal)
textOutput("AF2_grazing_warning"), # Display a warning message if the total exceeds 1
),
),
accordion_panel(
title = "Annual Crops",
icon = icon("pagelines"),
#h4("Annual Crops"),
h5 ("Wheat"),
sliderInput("winter_wheat_yield_p", "Yield [t/ha]",
min = 0, max = 50, value = c(2,8)),
sliderInput("winter_wheat_value_p", "Sale price [GBP/t]",
min = 0, max = 1000, value = c(450,520)),
h5 ("Barley"),
sliderInput("spring_barley_yield_p", "Yield [t/ha]",
min = 0, max = 50, value = c(1.5,5.5)),
sliderInput("spring_barley_value_p", "Sale price [GBP/t]",
min = 0, max = 1000, value = c(350,470)),
h5 ("Herbal Ley"),
sliderInput("herbal_ley_yield_p", "Yield [t/ha]",
min = 0, max = 100, value = c(5,12)),
sliderInput("herbal_ley_value_p", "Sale price [GBP/t]",
min = 0, max = 100, value = c(5,10)),
# sliderInput("herbal_effect_p", "Beneficial effect on subsequent crops [%]",
# min = 0, max = 10000, value = c(1,5)),
h5 ("Summer Beans"),
sliderInput("summer_beans_yield_p", "Yield [t/ha]",
min = 0, max = 1000, value = c(1.5,4.5)),
sliderInput("summer_beans_value_p", "Sale price [GBP/t]",
min = 0, max = 1000, value = c(500,600)),
h5 ("Winter Oats"),
sliderInput("winter_oats_yield_p", "Yield [t/ha]",
min = 0, max = 50, value = c(1.5,6)),
sliderInput("winter_oats_value_p", "Sale price [GBP/t]",
min = 0, max = 100, value = c(5,10)),
h5 ("Winter Cover Crops"),
sliderInput("winter_cover_crop_yield_p", "Yield [t/ha]",
min = 0, max = 100, value = c(3,6)),
),
accordion_panel(
title = "Animals",
icon = icon("cow"),
#h4("Animals"),
sliderInput("grazing_efficiency_p", "Trampling selective grazing & areas not consumed [%]",
min = 0, max = 1, value = c(0.5,0.8)),
sliderInput("herbal_grazing_labour_p", "labour to accommodate livestock grazing [h/ha over a year]",
min = 1, max = 50, value = c(2,5)),
sliderInput("beef_value_p", "Price of selling live cows [GBP/Kg]",
min = 1, max = 50, value = c(4.2,5.8)),
sliderInput("lamb_value_p", "Price of selling sheep [GBP/Kg]",
min = 1, max = 50, value = c(7.3,8.7)),
),
accordion_panel(
title = "Benefits of AF",
icon = icon("envira"),
#h4("Benefits & Drawbacks of AF"),
# sliderInput("Nonmarket_ES_value_p", "Monetary value of ES in AF system GBP/ ha/ y",
# min = 1, max = 10000, value = c(600,960)),
sliderInput("C_price_p", "Carbon Price [GBP/t C]",
min = 1, max = 500, value = c(20,30)),
sliderInput("winter_grazing_effect_p", "Positive yield effect or fertilization effect because of winter grazing [%]",
min = 1, max = 100, value = c(1.05,1.15)),
# sliderInput("per_market_price_p", "% fluctuation in market price of all products [%]",
# min = 1, max = 100, value = c(1,5)),
),
accordion_panel(
title = "Need clarification",
icon = icon("question-circle"),
#h4("Need clarification"),
sliderInput("irrigation_sys_install_p", "?Cost of installing irrigation system[GBP]",
min = 1, max = 10000, value = c(1000,3000)),
sliderInput("irrigation_planting_shrub_p", "?Water used soon after planting shrub[l/shrub]",
min = 1, max = 50, value = c(1,1.5)),
sliderInput("irrigation_planting_tree_p", "?Water used soon after planting tree[l/tree]",
min = 1, max = 90, value = c(10,20)),
# sliderInput("irrigation_123_p", "?Water use in the frist three years of tree establishment[l/tree]",
# min = 1, max = 10000, value = c(1,5)),
# sliderInput("irrigation_annual_p", "?Annual water use after the first three years[l/tree]",
# min = 1, max = 10000, value = c(1,5)),
sliderInput("compost_planting_shrub_p", "?Compost used at the time of planting shrubs [l/shrub]",
min = 0.1, max = 50, value = c(0.5,1)),
sliderInput("compost_planting_tree_p", "?Compost used at the time of planting tree [l/tree]",
min = 0.1, max = 70, value = c(10,20)),
sliderInput("compost_price_p", "?Price of compost [GBP/l]",
min = 0.1, max = 50, value = c(0.1,0.25)),
sliderInput("rowan_yield_max_p", "? max yield of Rowan [kg/tree]",
min = 1, max = 100, value = c(10,40)),
sliderInput("rowan_value_p", "?Price of Rowan[GBP/kg]",
min = 0, max = 10, value = c(0.5,0.75)),
sliderInput("hazel_yield_max_p", "?Max yield of Hazel [kg/tree]",
min = 1, max = 50, value = c(3,12)),
sliderInput("hazel_value_p", "?Price of Hazel [GBP/kg]",
min = 0, max = 20, value = c(0.89,3.08)),
sliderInput("damson_yield_max_p", "?Max yield of Damson [kg/tree]",
min = 1, max = 100, value = c(11.35,27.24)),
sliderInput("damson_value_p", "?Price of Damson [GBP/kg]",
min = 0, max = 20, value = c(0.88,3.3)),
sliderInput("tree_yield_max_p", "?Max. volume of SRC tree growth [t/ha*yr]",
min = 1, max = 50, value = c(7,12)),
sliderInput("biomass_timber_price_p", "?Price of selling one tonne of SRC willow [GBP/t]",
min = 1, max = 1000, value = c(80,260)),
),
br(),
br(),
actionButton("run_simulation", "Run Model", icon = icon("play"), class = "btn-primary"),
)
),
# define the content of the main panel of the UI
mainPanel(
width = 8,
# Display plots of DA
fluidRow(
column(width = 4),
#textOutput("display_version_1"),
# Added a button to open the URL
# actionButton("open_url", "Click here for latest info on Sustainable Farming Incentive"),
column(width = 4,
tags$a("Click here for latest info on Sustainable Farming Incentive",
href = "https://www.gov.uk/government/publications/sustainable-farming-incentive-scheme-expanded-offer-for-2024",
target="_blank",
class = "my-btn")),
column(width = 4)
),
h5('Probabilistic outcome distributions from Monte Carlo simulation for baseline and the decision intervention.'),
plotOutput("distPlot"),
p(
'The graphs above compare the net present value (NPV) distributions
between baseline and intervention options, expressed in GBP/ha across
the simulation period. The x-axis displays NPV values, representing the
sum of discounted cash flows, while the y-axis presents the different
scenarios being compared.'
), # p('The graph above provides a visual comparison of the outcome distributions
# for the baseline and intervention options in terms of net present value
# (NPV in GBP/ha) over the simulated period. The x-axis represents the NPV,
# calculated as the sum of discounted cash flows for each simulation year.
# The y-axis shows the probability of occurrence, indicating the likelihood
# of different NPV values. A higher value on the y-axis corresponds to a
# greater probability of the associated NPV on the x-axis.'),
downloadButton("save_plot1", "Save Plot"),
br(), # blank line
br(), # blank line
# h5('2. Probabilistic outcome of the decision in terms of NPV over the simulation period.'),
# plotOutput("distPlot2"),
# p(
# 'The graph above illustrates the outcome in terms of NPV in GBP/ha) over
# the simulated years, comparing the baseline scenario with the intervention.
# It highlights the differences in net cash flows between the two optioons.
# The right-skewness of the graph suggests that the intervention is generally
# favorable. However, since the distribution includes both positive and
# negative values, there remains a non-zero probability that the intervention
# may not always yield a more favorable outcome than the baseline. The
# integrated box plot shows that while the interquartile range (IQR) is mostly positive,
# it does include some negative values. The vertical line within the box plot represents the median NPV.'),
# downloadButton("save_plot2", "Save Plot"),
# br(), # blank line
# br(), # blank line
h5('Probabilistic outcome of the decisions'),
plotOutput("distPlot3"),
p('The graph above illustrates NPV outcomes (GBP/ha) over the simulation
period, directly comparing intervention scenarios against the baseline.
The x-axis shows the NPV differential, where negative values indicate
the baseline outperforming interventions and positive values show
interventions outperforming the baseline. The y-axis represents
probability density, with higher values indicating greater likelihood
of achieving the corresponding NPV difference.'),
downloadButton("save_plot3", "Save Plot")
)
)
)
#)
# Define Server
server <- function(input, output, session) {
#### Conditional Reactive variables based on user selection ####
# Funding ReactiveVaules ####
# Ensure reactive values are used correctly
tidy_funding_data <- reactive({
if (exists("funding_data")) {
return(isolate(funding_data()))
}
return(list())
})
funding_data_reactive <- reactive({ return (funding_data) })
# Fix UI rendering to use tagList instead of list
output$state_ui <- renderUI({
if (is.null(input$country)) return(NULL)
req(input$country)
tagList(
selectInput("state", "Select State:", choices = country_states[[input$country]], selected = NULL)
)
})
# Show funding schemes dropdown after state selection
# One-time funding Schemes
output$funding_one_ui <- renderUI({
req(input$country, input$state)
country <- input$country
state <- input$state
if (!is.null(funding_data_reactive()[[country]]) && !is.null(funding_data_reactive() [[country]][[state]])) {
state_data <- funding_data_reactive() [[country]][[state]]
return(tagList(
selectInput("funding_one_ui", "Select One-time Funding Scheme(s):",
choices = if (!is.null(state_data$one_time_funding_schemes)) state_data$one_time_funding_schemes else list(),
multiple = TRUE)
))
}
return(NULL)
})
# Annual funding Schemes
output$funding_yearly_ui <- renderUI({
req(input$state, input$country)
country <- input$country
state <- input$state
if (!is.null(funding_data_reactive() [[country]]) && !is.null(funding_data_reactive() [[country]][[state]])) {
state_data <- funding_data_reactive()[[country]][[state]]
return(tagList(
selectInput("funding_yearly_ui", "Select Annual Funding Scheme(s):",
choices = if (!is.null(state_data$annual_funding_schemes)) state_data$annual_funding_schemes else list(),
multiple = FALSE)
))
}
return(NULL)
})
# Ensure function names are not overwritten
if (!exists("safe_list")) {
safe_list <- list
}
## Animals Reactive Values ####
# Reactive value for introduce_animals
### Treeless System UI ###
# include_animals_c <- reactive({
# if (input$include_animals_c) {1} else {0}
# })
# When Number of animals is one of the parameters required #
# output$animal_quantity <- renderUI({
# req(input$animal_type)
# lapply(input$animal_type, function(animal) {
# numericInput(paste0("A_quantity_", animal, "_c"), paste("Quantity of", animal), value = 0, min = 0)
# })
# })
# When grazing intensity is one of the parameters required #
# Dynamically generate a slider input for each selected animal.
output$treeless_grazing_intensity <- renderUI({
if (!input$treeless_include_animals_c || is.null(input$treeless_animal_type) || length(input$treeless_animal_type) == 0) {
return(NULL)
}
# req(input$treeless_include_animals_c)
tagList(
lapply(input$treeless_animal_type, function(animal) {
sliderInput(
inputId = paste0("treeless_", animal, "_intensity_c"),
label = paste("Percentage of grazing granted for", animal),
min = 0, max = 1, value = 0, step = 0.01
)
})
)
#do.call(tagList, sliders)
})
treeless_total_grazing <- reactive({
if (!input$treeless_include_animals_c || is.null(input$treeless_animal_type) || length(input$treeless_animal_type) == 0) {
return(0)
}
vals <- sapply(input$treeless_animal_type, function(animal) {
val <- input[[paste0("treeless_", animal, "_intensity_c")]]
if (is.null(val)) 0 else val
})
sum(vals)
})
output$treeless_grazing_warning <- renderText({
tg <- treeless_total_grazing()
if (tg > 1) "Error: Total grazing percentage exceeds 1. Please adjust the grazing percentages." else ""
})
### AF1 System UI ###
# AF1_include_animals_c <- reactive({
# if (input$AF1_include_animals_c) {1
# } else {
# 0
# }
# })
output$AF1_grazing_intensity <- renderUI({
#req(input$AF1_include_animals_c)
if (!input$AF1_include_animals_c || is.null(input$AF1_animal_type) || length(input$AF1_animal_type) == 0) {
return(NULL)
}
sliders <- lapply(input$AF1_animal_type, function(animal) {
sliderInput(
inputId = paste0("AF1_", animal, "_intensity_c"),
label = paste("Percentage of grazing granted for", animal),
min = 0, max = 1, value = 0, step = 0.01
)
})
do.call(tagList, sliders)
})
AF1_total_grazing <- reactive({
if (!input$AF1_include_animals_c || is.null(input$AF1_animal_type) || length(input$AF1_animal_type) == 0) {
return(0)
}
vals <- sapply(input$AF1_animal_type, function(animal) {
val <- input[[paste0("AF1_", animal, "_intensity_c")]]
if (is.null(val)) 0 else val
})
sum(vals)
})
output$AF1_grazing_warning <- renderText({
tg <- AF1_total_grazing()
if (tg > 1) "Error: Total grazing percentage exceeds 1. Please adjust the grazing percentages." else ""
})
### AF1 System UI ###
# AF2_include_animals_c <- reactive({
# if (input$AF2_include_animals_c) {
# 1
# } else {
# 0
# }
# })
output$AF2_grazing_intensity <- renderUI({
#req(input$AF2_include_animals_c)
if (!input$AF2_include_animals_c || is.null(input$AF2_animal_type) || length(input$AF2_animal_type) == 0) {
return(NULL)
}
sliders <- lapply(input$AF2_animal_type, function(animal) {
sliderInput(
inputId = paste0("AF2_", animal, "_intensity_c"),
label = paste("Percentage of grazing granted for", animal),
min = 0, max = 1, value = 0, step = 0.01
)
})
do.call(tagList, sliders)
})
AF2_total_grazing <- reactive({
if (!input$AF2_include_animals_c || is.null(input$AF2_animal_type) || length(input$AF2_animal_type) == 0) {
return(0)
}
vals <- sapply(input$AF2_animal_type, function(animal) {
val <- input[[paste0("AF2_", animal, "_intensity_c")]]
if (is.null(val)) 0 else val
})
sum(vals)
})
output$AF2_grazing_warning <- renderText({
tg <- AF2_total_grazing()
if (tg > 1) "Error: Total grazing percentage exceeds 1. Please adjust the grazing percentages." else ""
})
########### save load delete logic ##############
# Determine file directory based on OS
file_dir <- if (Sys.info()[["sysname"]] == "Windows") {
"user-states/Mindrum/" # Use Windows path for local testing
} else {
"/srv/shiny-app-data/user-states/Mindrum/" # Linux path for server
}
# Get user ID or set default for testing
user_id <- session$user %||% "local_test_user"
# user_id <- session$user %||% "Adrian"
is_admin <- user_id %in% admin_users
# Output to determine if user is admin
output$isAdmin <- reactive(is_admin)
outputOptions(output, "isAdmin", suspendWhenHidden = FALSE)
# Admin user selection UI
output$admin_user_select <- renderUI({
if (is_admin) {
user_folders <- list.dirs(file_dir, full.names = FALSE, recursive = FALSE)
if (length(user_folders) == 0) {
user_folders <- user_id
}
div(
class = "admin-panel",
h4("Admin Panel"),
selectInput("admin_selected_user", "Select User Folder:", choices = user_folders, selected = user_id)
)
}
})
# Utility function to ensure directory exists
ensureDirectoryExists <- function(dir) {
if (!dir.exists(dir)) {
dir.create(dir, recursive = TRUE)
}
}
# Reactive expression for current user directory
current_user_dir <- reactive({
user_folder <- if (is_admin && !is.null(input$admin_selected_user)) {
input$admin_selected_user
} else {
user_id
}
dir <- file.path(file_dir, user_folder)
ensureDirectoryExists(dir)
dir
})
# Reactive value to store versions
versions <- reactiveVal()
# Function to update version selections
updateVersionSelections <- function() {
vers <- basename(list.files(current_user_dir(), full.names = TRUE, pattern = "csv"))
versions(vers)
updateSelectInput(session, "version_select", choices = versions())
updateSelectInput(session, "delete_version_select", choices = versions())
}
# Update versions when current_user_dir changes
observe({
updateVersionSelections()
})
# Save settings
observeEvent(input$save, {
files <- list.files(current_user_dir(), full.names = TRUE, pattern = "\\.csv$")
if (length(files) >= max_files) {
showNotification("Error: Maximum number of files reached.", type = "error", duration = 5)
return()
}
if (input$project_name == "") {
showNotification("Error: Please enter a project name before saving.", type = "error", duration = 5)
return()
}
timestamp <- format(Sys.time(), "%y%m%d_%H%M%S")
safe_project_name <- gsub("[^A-Za-z0-9_]+", "_", input$project_name)
file_name <- paste0(timestamp, "_", safe_project_name, ".csv")
file_path <- file.path(current_user_dir(), file_name)
# Exclude non-relevant inputs
exclude_inputs <- c("collapseSidebar", "save", "load", "delete", "confirm_delete",
"admin_selected_user", "project_name", "version_select", "delete_version_select")
# Define variables based on input names
variables <- setdiff(names(input)[grepl("(_c$|_p$|_t$)", names(input))], exclude_inputs)
# Extract lower and upper values
lower_values <- sapply(variables, function(var) {
value <- input[[var]]
if (length(value) == 1) {
as.numeric(value)
} else {
as.numeric(value[1])
}
})
upper_values <- sapply(variables, function(var) {
value <- input[[var]]
if (length(value) == 1) {
as.numeric(value)
} else {
as.numeric(value[2])
}
})
# Determine distributions
distributions <- sapply(variables, function(var) {