From d7fa39eef65f0fa2a8d0a79e3ab4057ab7e1099e Mon Sep 17 00:00:00 2001 From: stefanmet Date: Tue, 30 Jan 2024 04:32:46 +0000 Subject: [PATCH 01/66] create eddy4R.maps package --- pack/eddy4R.maps/.Rbuildignore | 3 +++ pack/eddy4R.maps/DESCRIPTION | 25 +++++++++++++++++++++++++ pack/eddy4R.maps/NAMESPACE | 2 ++ 3 files changed, 30 insertions(+) create mode 100644 pack/eddy4R.maps/.Rbuildignore create mode 100644 pack/eddy4R.maps/DESCRIPTION create mode 100644 pack/eddy4R.maps/NAMESPACE diff --git a/pack/eddy4R.maps/.Rbuildignore b/pack/eddy4R.maps/.Rbuildignore new file mode 100644 index 00000000..6c038333 --- /dev/null +++ b/pack/eddy4R.maps/.Rbuildignore @@ -0,0 +1,3 @@ +^eddy4R\.maps\.Rproj$ +^\.Rproj\.user$ +^data-raw$ diff --git a/pack/eddy4R.maps/DESCRIPTION b/pack/eddy4R.maps/DESCRIPTION new file mode 100644 index 00000000..09f28942 --- /dev/null +++ b/pack/eddy4R.maps/DESCRIPTION @@ -0,0 +1,25 @@ +Package: eddy4R.maps +Title: Eddy-covariance calculation for R: Flux Mapper™ source codes (basic) +Version: 0.0.1 +Authors@R: c( person(given = "Stefan", + family = "Metzger", + role = c("aut", "cre"), + email = "smetzger@atmofacts.com", + comment = c(ORCID = "0000-0002-4201-852X")), + person(given = "Samuel", + family = "Bower", + role = c("aut"), + email = "sbower@atmofacts.com"), + person(given = "David", + family = "Durden", + role = c("aut"), + email = "ddurden@battelleecology.org") + ) +Description: Basic commonalities and tools for working with Flux Maps™ in R. +Depends: + R (>= 4.0.5) +License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +Encoding: UTF-8 +LazyData: true +Roxygen: list(markdown = TRUE) +RoxygenNote: 7.1.1 diff --git a/pack/eddy4R.maps/NAMESPACE b/pack/eddy4R.maps/NAMESPACE new file mode 100644 index 00000000..6ae92683 --- /dev/null +++ b/pack/eddy4R.maps/NAMESPACE @@ -0,0 +1,2 @@ +# Generated by roxygen2: do not edit by hand + From 3668fd82c644e9141cfa6cb9031c5465ae04e921 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Tue, 30 Jan 2024 17:39:49 +0000 Subject: [PATCH 02/66] Add def.plot.flux.spatial.R to Eddy4R.maps --- pack/eddy4R.maps/R/def.plot.flux.spatial.R | 58 ++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 pack/eddy4R.maps/R/def.plot.flux.spatial.R diff --git a/pack/eddy4R.maps/R/def.plot.flux.spatial.R b/pack/eddy4R.maps/R/def.plot.flux.spatial.R new file mode 100644 index 00000000..c1ebea59 --- /dev/null +++ b/pack/eddy4R.maps/R/def.plot.flux.spatial.R @@ -0,0 +1,58 @@ + +# Load necessary libraries +library(ggplot2) +library(ggmap) +library(raster) +library(leaflet) +library(mapview) +library(shiny) + +# Define the function with improved interactive HTML handling +plot_geotiff_on_basemap <- function(input_path, output_path, colormap = 'viridis', opacity = 0.5, basemap_style = 'OpenStreetMap', interactive = FALSE) { + # Check if input_path is a directory or a single file + if (dir.exists(input_path)) { + # Handle directory of TIFF files + tiff_files <- list.files(input_path, pattern = '\\.tif$', full.names = TRUE) + if (interactive) { + # Create an interactive map with layer selection for each TIFF file + map <- leaflet() %>% addProviderTiles(leaflet::providers[[basemap_style]]) + for (file in tiff_files) { + raster_layer <- raster(file) + map <- map %>% addRasterImage(raster_layer, group = basename(file), colors = colormap, opacity = opacity) + } + map <- map %>% addLayersControl(overlayGroups = basename(tiff_files), options = layersControlOptions(collapsed = FALSE)) + output_html_path <- paste0(output_path,basename(file), '.html') + saveWidget(map, file = output_html_path, selfcontained = TRUE) + cat('Interactive map saved to:', output_html_path, ' +') + } else { + # Create a PNG map for each TIFF file + for (file in tiff_files) { + raster_layer <- raster(file) + map <- leaflet() %>% + addProviderTiles(leaflet::providers[[basemap_style]]) %>% + addRasterImage(raster_layer, colors = colormap, opacity = opacity) + mapshot(map, file = paste0(output_path, basename(file), '.png')) + } + } + } else { + # Handle single TIFF file + raster_file <- raster(input_path) + map <- leaflet() %>% + addProviderTiles(leaflet::providers[[basemap_style]]) %>% + addRasterImage(raster_file, colors = colormap, opacity = opacity) + if (!interactive) { + mapshot(map, file = paste0(output_path, '.png')) + } else { + output_html_path <- paste0(output_path, '.html') + saveWidget(map, file = output_html_path, selfcontained = TRUE) + cat('Interactive map saved to:', output_html_path, ' +') + } + } +} + +# Example usage: +path_in = "/home/sbower/eddy/sambower/Documents/AtmoFacts/maps_package/test_data/random_geotiff/footprints" +path_out = "/home/sbower/eddy/sambower/Documents/AtmoFacts/maps_package/test_data/random_geotiff/outputs/" +plot_geotiff_on_basemap(path_in, path_out, 'viridis', opacity = 0.5,'OpenStreetMap', TRUE) From bb8f14cd5719d757609a589332ab7ccdf2a20650 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Tue, 30 Jan 2024 21:39:43 +0000 Subject: [PATCH 03/66] Updates to plotting --- pack/eddy4R.maps/R/def.plot.flux.spatial.R | 39 ++++++++++++++-------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.spatial.R b/pack/eddy4R.maps/R/def.plot.flux.spatial.R index c1ebea59..36873760 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.spatial.R +++ b/pack/eddy4R.maps/R/def.plot.flux.spatial.R @@ -1,11 +1,10 @@ # Load necessary libraries -library(ggplot2) -library(ggmap) -library(raster) -library(leaflet) -library(mapview) -library(shiny) +#library(ggplot2) +#library(ggmap) +#library(raster) +#library(leaflet) +#library(mapview) # Define the function with improved interactive HTML handling plot_geotiff_on_basemap <- function(input_path, output_path, colormap = 'viridis', opacity = 0.5, basemap_style = 'OpenStreetMap', interactive = FALSE) { @@ -15,13 +14,25 @@ plot_geotiff_on_basemap <- function(input_path, output_path, colormap = 'viridis tiff_files <- list.files(input_path, pattern = '\\.tif$', full.names = TRUE) if (interactive) { # Create an interactive map with layer selection for each TIFF file - map <- leaflet() %>% addProviderTiles(leaflet::providers[[basemap_style]]) + map <- leaflet() %>% + #add a basemap based on the parameter basemap_style + addProviderTiles(leaflet::providers[[basemap_style]]) for (file in tiff_files) { + #add the raster layers iteratively raster_layer <- raster(file) - map <- map %>% addRasterImage(raster_layer, group = basename(file), colors = colormap, opacity = opacity) + map <- map %>% + addRasterImage( + raster_layer, + group = basename(file), + colors = colormap, + opacity = opacity) } - map <- map %>% addLayersControl(overlayGroups = basename(tiff_files), options = layersControlOptions(collapsed = FALSE)) - output_html_path <- paste0(output_path,basename(file), '.html') + + map <- map %>% + addLayersControl( + overlayGroups = basename(tiff_files), + options = layersControlOptions(collapsed = FALSE)) + output_html_path <- paste0(output_path, tools::file_path_sans_ext(basename(file)), '.html') saveWidget(map, file = output_html_path, selfcontained = TRUE) cat('Interactive map saved to:', output_html_path, ' ') @@ -32,7 +43,7 @@ plot_geotiff_on_basemap <- function(input_path, output_path, colormap = 'viridis map <- leaflet() %>% addProviderTiles(leaflet::providers[[basemap_style]]) %>% addRasterImage(raster_layer, colors = colormap, opacity = opacity) - mapshot(map, file = paste0(output_path, basename(file), '.png')) + mapshot(map, file = paste0(output_path, tools::file_path_sans_ext(basename(file)), '.png')) } } } else { @@ -42,9 +53,9 @@ plot_geotiff_on_basemap <- function(input_path, output_path, colormap = 'viridis addProviderTiles(leaflet::providers[[basemap_style]]) %>% addRasterImage(raster_file, colors = colormap, opacity = opacity) if (!interactive) { - mapshot(map, file = paste0(output_path, '.png')) + mapshot(map, file = paste0(output_path,tools::file_path_sans_ext(basename(input_path)), '.png')) } else { - output_html_path <- paste0(output_path, '.html') + output_html_path <- paste0(output_path, tools::file_path_sans_ext(basename(input_path)), '.html') saveWidget(map, file = output_html_path, selfcontained = TRUE) cat('Interactive map saved to:', output_html_path, ' ') @@ -53,6 +64,6 @@ plot_geotiff_on_basemap <- function(input_path, output_path, colormap = 'viridis } # Example usage: -path_in = "/home/sbower/eddy/sambower/Documents/AtmoFacts/maps_package/test_data/random_geotiff/footprints" +path_in = "/home/sbower/eddy/sambower/Documents/AtmoFacts/maps_package/test_data/random_geotiff/footprints/" path_out = "/home/sbower/eddy/sambower/Documents/AtmoFacts/maps_package/test_data/random_geotiff/outputs/" plot_geotiff_on_basemap(path_in, path_out, 'viridis', opacity = 0.5,'OpenStreetMap', TRUE) From 4f152a177d6eb3d668e3b913770c62c2c18d863b Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Tue, 30 Jan 2024 23:27:55 +0000 Subject: [PATCH 04/66] Add header --- pack/eddy4R.maps/R/def.plot.flux.spatial.R | 47 +++++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.spatial.R b/pack/eddy4R.maps/R/def.plot.flux.spatial.R index 36873760..98e35dfd 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.spatial.R +++ b/pack/eddy4R.maps/R/def.plot.flux.spatial.R @@ -1,4 +1,44 @@ +############################################################################################## +#' @title Definition function: Plot fluxes on a basemap +#' @author +#' Sam Bower \email{sbower@atmofacts.com} + +#' @description Function definition. This function saves a GeoTiff flux map onto a static basemap or an interactive html map. +#' +#' @param input_path A spatial file or folder of spatial files to plot on the basemap. +#' @param output_path A string for the folder location to save output maps. +#' @param interactive If FALSE - plots the flux(es) onto a static basemap. If TRUE, plots the flux(es) onto an interactive basemap. +#' @param basemap_style For a list of basemap styles use names(providers) + +#' @return +#' +#' + +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @keywords visualization, flux mapping +#' +#' @examples +#' +#' path_in = "/path/to/folder/of/tif/files" +#' path_out = "/path/to/desired/output/location" +#' plot.flux.spatial(path_in, path_out, 'viridis', opacity = 0.5,'OpenStreetMap', TRUE) +#' + + + +#' @seealso Currently none. + +#' @export + +# changelog and author contributions / copyrights +# Stefan Metzger (2011-03-04) +# original creation +# Stefan Metzger (2022-02-08) +# update to eddy4R terminology and modularize into definition function +############################################################################################### # Load necessary libraries #library(ggplot2) #library(ggmap) @@ -7,7 +47,7 @@ #library(mapview) # Define the function with improved interactive HTML handling -plot_geotiff_on_basemap <- function(input_path, output_path, colormap = 'viridis', opacity = 0.5, basemap_style = 'OpenStreetMap', interactive = FALSE) { +plot.flux.spatial <- function(input_path, output_path, colormap = 'viridis', opacity = 0.5, basemap_style = 'OpenStreetMap', interactive = FALSE) { # Check if input_path is a directory or a single file if (dir.exists(input_path)) { # Handle directory of TIFF files @@ -62,8 +102,3 @@ plot_geotiff_on_basemap <- function(input_path, output_path, colormap = 'viridis } } } - -# Example usage: -path_in = "/home/sbower/eddy/sambower/Documents/AtmoFacts/maps_package/test_data/random_geotiff/footprints/" -path_out = "/home/sbower/eddy/sambower/Documents/AtmoFacts/maps_package/test_data/random_geotiff/outputs/" -plot_geotiff_on_basemap(path_in, path_out, 'viridis', opacity = 0.5,'OpenStreetMap', TRUE) From c88bd8a756f0eba31e7ded203fdb7819686c4838 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Tue, 30 Jan 2024 23:38:34 +0000 Subject: [PATCH 05/66] Edit for consistent style --- pack/eddy4R.maps/R/def.plot.flux.spatial.R | 81 +++++++++++----------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.spatial.R b/pack/eddy4R.maps/R/def.plot.flux.spatial.R index 98e35dfd..100d9e82 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.spatial.R +++ b/pack/eddy4R.maps/R/def.plot.flux.spatial.R @@ -34,10 +34,10 @@ #' @export # changelog and author contributions / copyrights -# Stefan Metzger (2011-03-04) +# Sam Bower (2023-01-30) # original creation -# Stefan Metzger (2022-02-08) -# update to eddy4R terminology and modularize into definition function +# +# ############################################################################################### # Load necessary libraries #library(ggplot2) @@ -48,57 +48,60 @@ # Define the function with improved interactive HTML handling plot.flux.spatial <- function(input_path, output_path, colormap = 'viridis', opacity = 0.5, basemap_style = 'OpenStreetMap', interactive = FALSE) { + # Load necessary libraries + library(raster) + library(leaflet) + library(htmlwidgets) + library(mapview) + # Check if input_path is a directory or a single file - if (dir.exists(input_path)) { + if (base::dir.exists(input_path)) { # Handle directory of TIFF files - tiff_files <- list.files(input_path, pattern = '\\.tif$', full.names = TRUE) + tiff_files <- base::list.files(input_path, pattern = '\\.tif$', full.names = TRUE) if (interactive) { # Create an interactive map with layer selection for each TIFF file - map <- leaflet() %>% - #add a basemap based on the parameter basemap_style - addProviderTiles(leaflet::providers[[basemap_style]]) + map <- leaflet::leaflet() + map <- map %>% leaflet::addProviderTiles(leaflet::providers[[basemap_style]]) + for (file in tiff_files) { - #add the raster layers iteratively - raster_layer <- raster(file) - map <- map %>% - addRasterImage( - raster_layer, - group = basename(file), - colors = colormap, - opacity = opacity) + raster_layer <- raster::raster(file) + map <- map %>% leaflet::addRasterImage( + raster_layer, + group = base::basename(file), + colors = colormap, + opacity = opacity) } - map <- map %>% - addLayersControl( - overlayGroups = basename(tiff_files), - options = layersControlOptions(collapsed = FALSE)) - output_html_path <- paste0(output_path, tools::file_path_sans_ext(basename(file)), '.html') - saveWidget(map, file = output_html_path, selfcontained = TRUE) - cat('Interactive map saved to:', output_html_path, ' -') + map <- map %>% leaflet::addLayersControl( + overlayGroups = base::basename(tiff_files), + options = leaflet::layersControlOptions(collapsed = FALSE)) + + output_html_path <- base::paste0(output_path, tools::file_path_sans_ext(base::basename(file)), '.html') + htmlwidgets::saveWidget(map, file = output_html_path, selfcontained = TRUE) + base::cat('Interactive map saved to:', output_html_path, '\n') } else { # Create a PNG map for each TIFF file for (file in tiff_files) { - raster_layer <- raster(file) - map <- leaflet() %>% - addProviderTiles(leaflet::providers[[basemap_style]]) %>% - addRasterImage(raster_layer, colors = colormap, opacity = opacity) - mapshot(map, file = paste0(output_path, tools::file_path_sans_ext(basename(file)), '.png')) + raster_layer <- raster::raster(file) + map <- leaflet::leaflet() %>% + leaflet::addProviderTiles(leaflet::providers[[basemap_style]]) %>% + leaflet::addRasterImage(raster_layer, colors = colormap, opacity = opacity) + mapview::mapshot(map, file = base::paste0(output_path, tools::file_path_sans_ext(base::basename(file)), '.png')) } } } else { # Handle single TIFF file - raster_file <- raster(input_path) - map <- leaflet() %>% - addProviderTiles(leaflet::providers[[basemap_style]]) %>% - addRasterImage(raster_file, colors = colormap, opacity = opacity) - if (!interactive) { - mapshot(map, file = paste0(output_path,tools::file_path_sans_ext(basename(input_path)), '.png')) + raster_file <- raster::raster(input_path) + map <- leaflet::leaflet() %>% + leaflet::addProviderTiles(leaflet::providers[[basemap_style]]) %>% + leaflet::addRasterImage(raster_file, colors = colormap, opacity = opacity) + if (interactive) { + output_html_path <- base::paste0(output_path, tools::file_path_sans_ext(base::basename(input_path)), '.html') + htmlwidgets::saveWidget(map, file = output_html_path, selfcontained = TRUE) + base::cat('Interactive map saved to:', output_html_path, '\n') } else { - output_html_path <- paste0(output_path, tools::file_path_sans_ext(basename(input_path)), '.html') - saveWidget(map, file = output_html_path, selfcontained = TRUE) - cat('Interactive map saved to:', output_html_path, ' -') + mapview::mapshot(map, file = base::paste0(output_path,tools::file_path_sans_ext(base::basename(input_path)), '.png')) } } + return(map) } From e827e9672e016157f4999c1d67c83ea29d20a320 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Tue, 30 Jan 2024 23:40:22 +0000 Subject: [PATCH 06/66] Edit libraries --- pack/eddy4R.maps/R/def.plot.flux.spatial.R | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.spatial.R b/pack/eddy4R.maps/R/def.plot.flux.spatial.R index 100d9e82..2f574252 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.spatial.R +++ b/pack/eddy4R.maps/R/def.plot.flux.spatial.R @@ -39,13 +39,6 @@ # # ############################################################################################### -# Load necessary libraries -#library(ggplot2) -#library(ggmap) -#library(raster) -#library(leaflet) -#library(mapview) - # Define the function with improved interactive HTML handling plot.flux.spatial <- function(input_path, output_path, colormap = 'viridis', opacity = 0.5, basemap_style = 'OpenStreetMap', interactive = FALSE) { # Load necessary libraries From b99e15702ec2de9a38c33aa9950429ab55fb9153 Mon Sep 17 00:00:00 2001 From: stefanmet Date: Wed, 31 Jan 2024 13:48:43 +0000 Subject: [PATCH 07/66] build maps package --- pack/eddy4R.maps/NAMESPACE | 1 + pack/eddy4R.maps/man/plot.flux.spatial.Rd | 49 +++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 pack/eddy4R.maps/man/plot.flux.spatial.Rd diff --git a/pack/eddy4R.maps/NAMESPACE b/pack/eddy4R.maps/NAMESPACE index 6ae92683..98917352 100644 --- a/pack/eddy4R.maps/NAMESPACE +++ b/pack/eddy4R.maps/NAMESPACE @@ -1,2 +1,3 @@ # Generated by roxygen2: do not edit by hand +S3method(plot,flux.spatial) diff --git a/pack/eddy4R.maps/man/plot.flux.spatial.Rd b/pack/eddy4R.maps/man/plot.flux.spatial.Rd new file mode 100644 index 00000000..80d415c3 --- /dev/null +++ b/pack/eddy4R.maps/man/plot.flux.spatial.Rd @@ -0,0 +1,49 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/def.plot.flux.spatial.R +\name{plot.flux.spatial} +\alias{plot.flux.spatial} +\title{Definition function: Plot fluxes on a basemap} +\usage{ +\method{plot}{flux.spatial}( + input_path, + output_path, + colormap = "viridis", + opacity = 0.5, + basemap_style = "OpenStreetMap", + interactive = FALSE +) +} +\arguments{ +\item{input_path}{A spatial file or folder of spatial files to plot on the basemap.} + +\item{output_path}{A string for the folder location to save output maps.} + +\item{basemap_style}{For a list of basemap styles use names(providers)} + +\item{interactive}{If FALSE - plots the flux(es) onto a static basemap. If TRUE, plots the flux(es) onto an interactive basemap.} +} +\value{ + +} +\description{ +Function definition. This function saves a GeoTiff flux map onto a static basemap or an interactive html map. +} +\examples{ + +path_in = "/path/to/folder/of/tif/files" +path_out = "/path/to/desired/output/location" +plot.flux.spatial(path_in, path_out, 'viridis', opacity = 0.5,'OpenStreetMap', TRUE) + +} +\references{ +License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +} +\seealso{ +Currently none. +} +\author{ +Sam Bower \email{sbower@atmofacts.com} +} +\keyword{flux} +\keyword{mapping} +\keyword{visualization,} From 532737adc3ba2a663714ea888ef6178922e0f7f9 Mon Sep 17 00:00:00 2001 From: stefanmet Date: Wed, 31 Jan 2024 14:40:34 +0000 Subject: [PATCH 08/66] update function name --- pack/eddy4R.maps/R/def.plot.flux.spatial.R | 2 +- pack/eddy4R.maps/man/plot.flux.spatial.Rd | 49 ---------------------- 2 files changed, 1 insertion(+), 50 deletions(-) delete mode 100644 pack/eddy4R.maps/man/plot.flux.spatial.Rd diff --git a/pack/eddy4R.maps/R/def.plot.flux.spatial.R b/pack/eddy4R.maps/R/def.plot.flux.spatial.R index 2f574252..e589fe61 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.spatial.R +++ b/pack/eddy4R.maps/R/def.plot.flux.spatial.R @@ -40,7 +40,7 @@ # ############################################################################################### # Define the function with improved interactive HTML handling -plot.flux.spatial <- function(input_path, output_path, colormap = 'viridis', opacity = 0.5, basemap_style = 'OpenStreetMap', interactive = FALSE) { +def.plot.flux.spatial <- function(input_path, output_path, colormap = 'viridis', opacity = 0.5, basemap_style = 'OpenStreetMap', interactive = FALSE) { # Load necessary libraries library(raster) library(leaflet) diff --git a/pack/eddy4R.maps/man/plot.flux.spatial.Rd b/pack/eddy4R.maps/man/plot.flux.spatial.Rd deleted file mode 100644 index 80d415c3..00000000 --- a/pack/eddy4R.maps/man/plot.flux.spatial.Rd +++ /dev/null @@ -1,49 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/def.plot.flux.spatial.R -\name{plot.flux.spatial} -\alias{plot.flux.spatial} -\title{Definition function: Plot fluxes on a basemap} -\usage{ -\method{plot}{flux.spatial}( - input_path, - output_path, - colormap = "viridis", - opacity = 0.5, - basemap_style = "OpenStreetMap", - interactive = FALSE -) -} -\arguments{ -\item{input_path}{A spatial file or folder of spatial files to plot on the basemap.} - -\item{output_path}{A string for the folder location to save output maps.} - -\item{basemap_style}{For a list of basemap styles use names(providers)} - -\item{interactive}{If FALSE - plots the flux(es) onto a static basemap. If TRUE, plots the flux(es) onto an interactive basemap.} -} -\value{ - -} -\description{ -Function definition. This function saves a GeoTiff flux map onto a static basemap or an interactive html map. -} -\examples{ - -path_in = "/path/to/folder/of/tif/files" -path_out = "/path/to/desired/output/location" -plot.flux.spatial(path_in, path_out, 'viridis', opacity = 0.5,'OpenStreetMap', TRUE) - -} -\references{ -License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 -} -\seealso{ -Currently none. -} -\author{ -Sam Bower \email{sbower@atmofacts.com} -} -\keyword{flux} -\keyword{mapping} -\keyword{visualization,} From 4f184237f9797773f9f62b10583ed3d6d2807339 Mon Sep 17 00:00:00 2001 From: stefanmet Date: Wed, 31 Jan 2024 14:41:10 +0000 Subject: [PATCH 09/66] add and build def.algn.foot.rng --- pack/eddy4R.maps/NAMESPACE | 4 +- pack/eddy4R.maps/R/def.algn.foot.rng.R | 103 ++++++++++++++++++ pack/eddy4R.maps/man/def.algn.foot.rng.Rd | 46 ++++++++ pack/eddy4R.maps/man/def.plot.flux.spatial.Rd | 49 +++++++++ 4 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 pack/eddy4R.maps/R/def.algn.foot.rng.R create mode 100644 pack/eddy4R.maps/man/def.algn.foot.rng.Rd create mode 100644 pack/eddy4R.maps/man/def.plot.flux.spatial.Rd diff --git a/pack/eddy4R.maps/NAMESPACE b/pack/eddy4R.maps/NAMESPACE index 98917352..7600e688 100644 --- a/pack/eddy4R.maps/NAMESPACE +++ b/pack/eddy4R.maps/NAMESPACE @@ -1,3 +1,5 @@ # Generated by roxygen2: do not edit by hand -S3method(plot,flux.spatial) +export(def.algn.foot.rng) +export(def.plot.flux.spatial) +importFrom(matlab,padarray) diff --git a/pack/eddy4R.maps/R/def.algn.foot.rng.R b/pack/eddy4R.maps/R/def.algn.foot.rng.R new file mode 100644 index 00000000..023d99f2 --- /dev/null +++ b/pack/eddy4R.maps/R/def.algn.foot.rng.R @@ -0,0 +1,103 @@ +#' @title Align Footprint Map with Remote Sensing Data +#' +#' @author +#' Stefan Metzger \email{smetzger@atmofacts.com} +#' Andrei Serafimovich +#' +#' @description Function definition. This function adjusts the dimensions of a footprint map +#' (PHIcr_pad) to match and align with the dimensions of remote sensing (RS) data. It handles both cases +#' where the footprint map is smaller or larger than the RS data extent in all directions. +#' +#' @param PHIcr_pad Matrix representing the footprint map. +#' @param nortF Numeric vector representing the north extent of the footprint. +#' @param eastF Numeric vector representing the east extent of the footprint. +#' @param RS List containing 'nort' and 'east' vectors for remote sensing data extents. +#' +#' @return List containing the adjusted footprint map matrix and the updated extents (nortF and eastF). +#' +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +#' +#' @keywords visualization, flux mapping +#' +#' @importFrom matlab padarray +#' +#' @examples +#' # Example usage: +#' # result <- alignFootprintMap(PHIcr_pad, nortF, eastF, RS) +#' # aligned_PHIcr_pad <- result$PHIcr_pad +#' # updated_nortF <- result$nortF +#' # updated_eastF <- result$eastF +#' +#' @seealso Currently none. +#' +#' @export +# +# changelog and author contributions / copyrights +# Stefan Metzger (2013-01-01) original creation +# Andrei Serafimovich (2015-06-15) fixed padding for footprint matrices +# Stefan Metzger (2023-01-31) add Roxygen header, AGPL3 publication +# +############################################################################################### + +def.algn.foot.rng <- function( + PHIcr_pad, + nortF, + eastF, + RS) { + + # Pad or trim the footprint matrix to get same dimension as and alignment with RS data + + # Extent in south + if(min(nortF, na.rm=TRUE) > 1) { + #case1: footprint matrix smaller than RS extent + PHIcr_pad <- matlab::padarray(PHIcr_pad, range(nortF)[1]-1,0,"post") + nortF <- c(1:(range(nortF)[1]-1), nortF) + } else if (min(nortF, na.rm=TRUE) < 1) { + #case2: footprint matrix larger than RS extent + # PHIcr_pad <- PHIcr[-which(nortF < 0),] + PHIcr_pad <- PHIcr_pad[-which(nortF <= 0),] + # nortF <- nortF[-which(nortF < 0)] + nortF <- nortF[-which(nortF <= 0)] + } + + # Extent in north + if( max(nortF, na.rm=TRUE) < length(RS$nort) ) { + #case1: footprint matrix smaller than RS extent + PHIcr_pad <- matlab::padarray(PHIcr_pad,length(RS$nort)-range(nortF)[2],0,"pre") + nortF <- c(nortF, (range(nortF)[2]+1):length(RS$nort)) + } else if ( max(nortF, na.rm=TRUE) > length(RS$nort) ) { + #case2: footprint matrix larger than RS extent + PHIcr_pad <- PHIcr_pad[-which(nortF > length(RS$nort)),] + nortF <- nortF[-which(nortF > length(RS$nort))] + } + + # Extent in west + if(min(eastF, na.rm=TRUE) > 1) { + #case1: footprint matrix smaller than RS extent + PHIcr_pad <- cbind(matrix(nrow=nrow(PHIcr_pad), ncol=range(eastF)[1]-1, 0), PHIcr_pad) + eastF <- c(1:(range(eastF)[1]-1), eastF) + } else if (min(eastF, na.rm=TRUE) < 1) { + #case2: footprint matrix larger than RS extent + # PHIcr_pad <- PHIcr_pad[,-which(eastF < 0)] + PHIcr_pad <- PHIcr_pad[,-which(eastF <= 0)] + # eastF <- eastF[-which(eastF < 0)] + eastF <- eastF[-which(eastF <= 0)] + } + + # Extent in east + if( max(eastF, na.rm=TRUE) < length(RS$east) ) { + #case1: footprint matrix smaller than RS extent + PHIcr_pad <- cbind(PHIcr_pad, matrix(nrow=nrow(PHIcr_pad), + ncol=length(RS$east)-range(eastF)[2], 0)) + eastF <- c(eastF, (range(eastF)[2]+1):length(RS$east)) + } else if (max(eastF, na.rm=TRUE) > length(RS$east)) { + #case2: footprint matrix larger than RS extent + PHIcr_pad <- PHIcr_pad[,-which(eastF > length(RS$east))] + # eastF <- eastF[-which(nortF > length(RS$east))] + eastF <- eastF[-which(eastF > length(RS$east))] + } + + # Return the adjusted matrix and extents + return(list(PHIcr_pad = PHIcr_pad, nortF = nortF, eastF = eastF)) +} diff --git a/pack/eddy4R.maps/man/def.algn.foot.rng.Rd b/pack/eddy4R.maps/man/def.algn.foot.rng.Rd new file mode 100644 index 00000000..e6c1cd79 --- /dev/null +++ b/pack/eddy4R.maps/man/def.algn.foot.rng.Rd @@ -0,0 +1,46 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/def.algn.foot.rng.R +\name{def.algn.foot.rng} +\alias{def.algn.foot.rng} +\title{Align Footprint Map with Remote Sensing Data} +\usage{ +def.algn.foot.rng(PHIcr_pad, nortF, eastF, RS) +} +\arguments{ +\item{PHIcr_pad}{Matrix representing the footprint map.} + +\item{nortF}{Numeric vector representing the north extent of the footprint.} + +\item{eastF}{Numeric vector representing the east extent of the footprint.} + +\item{RS}{List containing 'nort' and 'east' vectors for remote sensing data extents.} +} +\value{ +List containing the adjusted footprint map matrix and the updated extents (nortF and eastF). +} +\description{ +Function definition. This function adjusts the dimensions of a footprint map +(PHIcr_pad) to match and align with the dimensions of remote sensing (RS) data. It handles both cases +where the footprint map is smaller or larger than the RS data extent in all directions. +} +\examples{ +# Example usage: +# result <- alignFootprintMap(PHIcr_pad, nortF, eastF, RS) +# aligned_PHIcr_pad <- result$PHIcr_pad +# updated_nortF <- result$nortF +# updated_eastF <- result$eastF + +} +\references{ +License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +} +\seealso{ +Currently none. +} +\author{ +Stefan Metzger \email{smetzger@atmofacts.com} +Andrei Serafimovich +} +\keyword{flux} +\keyword{mapping} +\keyword{visualization,} diff --git a/pack/eddy4R.maps/man/def.plot.flux.spatial.Rd b/pack/eddy4R.maps/man/def.plot.flux.spatial.Rd new file mode 100644 index 00000000..71cf1008 --- /dev/null +++ b/pack/eddy4R.maps/man/def.plot.flux.spatial.Rd @@ -0,0 +1,49 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/def.plot.flux.spatial.R +\name{def.plot.flux.spatial} +\alias{def.plot.flux.spatial} +\title{Definition function: Plot fluxes on a basemap} +\usage{ +def.plot.flux.spatial( + input_path, + output_path, + colormap = "viridis", + opacity = 0.5, + basemap_style = "OpenStreetMap", + interactive = FALSE +) +} +\arguments{ +\item{input_path}{A spatial file or folder of spatial files to plot on the basemap.} + +\item{output_path}{A string for the folder location to save output maps.} + +\item{basemap_style}{For a list of basemap styles use names(providers)} + +\item{interactive}{If FALSE - plots the flux(es) onto a static basemap. If TRUE, plots the flux(es) onto an interactive basemap.} +} +\value{ + +} +\description{ +Function definition. This function saves a GeoTiff flux map onto a static basemap or an interactive html map. +} +\examples{ + +path_in = "/path/to/folder/of/tif/files" +path_out = "/path/to/desired/output/location" +plot.flux.spatial(path_in, path_out, 'viridis', opacity = 0.5,'OpenStreetMap', TRUE) + +} +\references{ +License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +} +\seealso{ +Currently none. +} +\author{ +Sam Bower \email{sbower@atmofacts.com} +} +\keyword{flux} +\keyword{mapping} +\keyword{visualization,} From 8062a37cb2ab7a5c50ebdc98fea10b65f8a89a0b Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Thu, 1 Feb 2024 20:45:53 +0000 Subject: [PATCH 10/66] Separate def.plot.flux.spatial into def.plot.flux.static and def.plot.flux.interactive. --- pack/eddy4R.maps/R/def.plot.flux.interactive.R | 1 + pack/eddy4R.maps/R/def.plot.flux.static.R | 1 + 2 files changed, 2 insertions(+) create mode 100644 pack/eddy4R.maps/R/def.plot.flux.interactive.R create mode 100644 pack/eddy4R.maps/R/def.plot.flux.static.R diff --git a/pack/eddy4R.maps/R/def.plot.flux.interactive.R b/pack/eddy4R.maps/R/def.plot.flux.interactive.R new file mode 100644 index 00000000..1a0b97e2 --- /dev/null +++ b/pack/eddy4R.maps/R/def.plot.flux.interactive.R @@ -0,0 +1 @@ +def.plot.flux.interactive \ No newline at end of file diff --git a/pack/eddy4R.maps/R/def.plot.flux.static.R b/pack/eddy4R.maps/R/def.plot.flux.static.R new file mode 100644 index 00000000..003bac12 --- /dev/null +++ b/pack/eddy4R.maps/R/def.plot.flux.static.R @@ -0,0 +1 @@ +def.plot.flux.static \ No newline at end of file From 6a4f6e08560cadc6d713bc35605d0aa31bcd4e5d Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Thu, 1 Feb 2024 21:32:44 +0000 Subject: [PATCH 11/66] Create def.plot.flux.static framework. --- pack/eddy4R.maps/R/def.plot.flux.static.R | 29 ++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.static.R b/pack/eddy4R.maps/R/def.plot.flux.static.R index 003bac12..ad7ffd3b 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.static.R +++ b/pack/eddy4R.maps/R/def.plot.flux.static.R @@ -1 +1,28 @@ -def.plot.flux.static \ No newline at end of file + + + + +def.plot.flux.static <- function(inputPath, basemapStyle = 'osm', nodataValue = 0, outputPath = 'output') { + +# Change java system parameters to allow for headless operation (to disable under-the-hood java GUI which will not work in this Docker container) . + options(java.parameters = "-Djava.awt.headless=true") + +# Load required libraries. + +# read in single raster file. + +# set nodata value. + +# create basemap layer with OpenStreetMap and tmaptools. + +# superimpose raster layer with given color scheme. + +# save + +# Read in folder of raster files + +# Plot iteratively + +# Save +} + From 8ffcd929e785ad65965fe1b709afe14a86b5a6c2 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Thu, 1 Feb 2024 21:40:31 +0000 Subject: [PATCH 12/66] Check for files in input. --- pack/eddy4R.maps/R/def.plot.flux.static.R | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.static.R b/pack/eddy4R.maps/R/def.plot.flux.static.R index ad7ffd3b..85313bb6 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.static.R +++ b/pack/eddy4R.maps/R/def.plot.flux.static.R @@ -1,16 +1,18 @@ - - def.plot.flux.static <- function(inputPath, basemapStyle = 'osm', nodataValue = 0, outputPath = 'output') { # Change java system parameters to allow for headless operation (to disable under-the-hood java GUI which will not work in this Docker container) . options(java.parameters = "-Djava.awt.headless=true") -# Load required libraries. - # read in single raster file. + if (dir.exists(inputPath)) { + rasterFiles <- list.files(inputPath, pattern = "\\.tif$", full.names = TRUE) + if (length(rasterFiles) == 0) { + stop("No raster files found in the directory.") + } + # set nodata value. # create basemap layer with OpenStreetMap and tmaptools. @@ -24,5 +26,5 @@ def.plot.flux.static <- function(inputPath, basemapStyle = 'osm', nodataValue = # Plot iteratively # Save -} +}} From 88e04024b866ffeaef8779a796bfb5dbe61bcd11 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Thu, 1 Feb 2024 21:47:46 +0000 Subject: [PATCH 13/66] Create plotting function to call iteratively. --- pack/eddy4R.maps/R/def.plot.flux.static.R | 24 ++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.static.R b/pack/eddy4R.maps/R/def.plot.flux.static.R index 85313bb6..64310f0b 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.static.R +++ b/pack/eddy4R.maps/R/def.plot.flux.static.R @@ -4,14 +4,36 @@ def.plot.flux.static <- function(inputPath, basemapStyle = 'osm', nodataValue = # Change java system parameters to allow for headless operation (to disable under-the-hood java GUI which will not work in this Docker container) . options(java.parameters = "-Djava.awt.headless=true") + +# Plotting function with tmap -# read in single raster file. + processRasterStatic <- function(rasterFile, basemapStyle, nodataValue, outputPath) { + rasterLayer <- raster::raster(rasterFile) + rasterLayer[rasterLayer == nodataValue] <- NA + + osm_map <- tmaptools::read_osm(rasterLayer, type = basemapStyle) + map <- tm_shape(osm_map) + + tm_rgb() + + tm_shape(rasterLayer) + + tm_raster(style = "equal", alpha = 0.4) + + tm_layout(legend.outside = FALSE) + + tmap_save(map, file = outputPath) + } +# Read in a raster file or folder of raster files + if (dir.exists(inputPath)) { rasterFiles <- list.files(inputPath, pattern = "\\.tif$", full.names = TRUE) if (length(rasterFiles) == 0) { stop("No raster files found in the directory.") } + for (rasterFile in rasterFiles) { + fileName <- basename(rasterFile) + fileOutputPath <- paste0(outputPath, "/", sub("\\.tif$", ".png", fileName))} + else{ + rasterFile <- raster::raster(inputPath) + } # set nodata value. From 99d01973071b1789e605882a360da56d0e2a1e45 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Thu, 1 Feb 2024 21:51:50 +0000 Subject: [PATCH 14/66] Conditions for single file inputs. --- pack/eddy4R.maps/R/def.plot.flux.static.R | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.static.R b/pack/eddy4R.maps/R/def.plot.flux.static.R index 64310f0b..3236925e 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.static.R +++ b/pack/eddy4R.maps/R/def.plot.flux.static.R @@ -7,7 +7,7 @@ def.plot.flux.static <- function(inputPath, basemapStyle = 'osm', nodataValue = # Plotting function with tmap - processRasterStatic <- function(rasterFile, basemapStyle, nodataValue, outputPath) { + processRaster <- function(rasterFile, basemapStyle, nodataValue, outputPath) { rasterLayer <- raster::raster(rasterFile) rasterLayer[rasterLayer == nodataValue] <- NA @@ -30,11 +30,15 @@ def.plot.flux.static <- function(inputPath, basemapStyle = 'osm', nodataValue = } for (rasterFile in rasterFiles) { fileName <- basename(rasterFile) - fileOutputPath <- paste0(outputPath, "/", sub("\\.tif$", ".png", fileName))} - else{ - rasterFile <- raster::raster(inputPath) - } - + fileOutputPath <- paste0(outputPath, "/", sub("\\.tif$", ".png", fileName)) + processRaster(rasterFile, basemapStyle, nodataValue, fileOutputPath)} + else if file.exists(inputPath) && grepl("\\.tif$", inputPath)) { + processRasterStatic(inputPath, basemapStyle, nodataValue, paste0(outputPath, ".png")){ + processRaster(rasterFile, basemapStyle, nodataValue, fileOutputPath) + } + } + } +} # set nodata value. # create basemap layer with OpenStreetMap and tmaptools. @@ -48,5 +52,5 @@ def.plot.flux.static <- function(inputPath, basemapStyle = 'osm', nodataValue = # Plot iteratively # Save -}} + From 2a0f7c044b70b729b915e76c8ae29f2732c60290 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Thu, 1 Feb 2024 21:58:44 +0000 Subject: [PATCH 15/66] Process output and save path. --- pack/eddy4R.maps/R/def.plot.flux.static.R | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.static.R b/pack/eddy4R.maps/R/def.plot.flux.static.R index 3236925e..db26c436 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.static.R +++ b/pack/eddy4R.maps/R/def.plot.flux.static.R @@ -1,5 +1,3 @@ - - def.plot.flux.static <- function(inputPath, basemapStyle = 'osm', nodataValue = 0, outputPath = 'output') { # Change java system parameters to allow for headless operation (to disable under-the-hood java GUI which will not work in this Docker container) . @@ -32,13 +30,15 @@ def.plot.flux.static <- function(inputPath, basemapStyle = 'osm', nodataValue = fileName <- basename(rasterFile) fileOutputPath <- paste0(outputPath, "/", sub("\\.tif$", ".png", fileName)) processRaster(rasterFile, basemapStyle, nodataValue, fileOutputPath)} - else if file.exists(inputPath) && grepl("\\.tif$", inputPath)) { - processRasterStatic(inputPath, basemapStyle, nodataValue, paste0(outputPath, ".png")){ - processRaster(rasterFile, basemapStyle, nodataValue, fileOutputPath) - } + else if (file.exists(inputPath) && grepl("\\.tif$", inputPath)) { + processRaster(inputPath, basemapStyle, nodataValue, paste0(outputPath, ".png")) } } + else { + stop("Input path is neither a valid raster file nor a directory containing raster files.") + } } + # set nodata value. # create basemap layer with OpenStreetMap and tmaptools. From dd4ddce73dc23b195620d3e2a0e496fc4d785d14 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Thu, 1 Feb 2024 21:59:45 +0000 Subject: [PATCH 16/66] fix syntax errors. --- pack/eddy4R.maps/R/def.plot.flux.static.R | 36 ++++++----------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.static.R b/pack/eddy4R.maps/R/def.plot.flux.static.R index db26c436..aa7f363b 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.static.R +++ b/pack/eddy4R.maps/R/def.plot.flux.static.R @@ -1,10 +1,9 @@ def.plot.flux.static <- function(inputPath, basemapStyle = 'osm', nodataValue = 0, outputPath = 'output') { - -# Change java system parameters to allow for headless operation (to disable under-the-hood java GUI which will not work in this Docker container) . + + # Change java system parameters to allow for headless operation (to disable under-the-hood java GUI which will not work in this Docker container). options(java.parameters = "-Djava.awt.headless=true") - -# Plotting function with tmap + # Plotting function with tmap processRaster <- function(rasterFile, basemapStyle, nodataValue, outputPath) { rasterLayer <- raster::raster(rasterFile) rasterLayer[rasterLayer == nodataValue] <- NA @@ -19,8 +18,7 @@ def.plot.flux.static <- function(inputPath, basemapStyle = 'osm', nodataValue = tmap_save(map, file = outputPath) } -# Read in a raster file or folder of raster files - + # Read in a raster file or folder of raster files if (dir.exists(inputPath)) { rasterFiles <- list.files(inputPath, pattern = "\\.tif$", full.names = TRUE) if (length(rasterFiles) == 0) { @@ -29,28 +27,12 @@ def.plot.flux.static <- function(inputPath, basemapStyle = 'osm', nodataValue = for (rasterFile in rasterFiles) { fileName <- basename(rasterFile) fileOutputPath <- paste0(outputPath, "/", sub("\\.tif$", ".png", fileName)) - processRaster(rasterFile, basemapStyle, nodataValue, fileOutputPath)} - else if (file.exists(inputPath) && grepl("\\.tif$", inputPath)) { - processRaster(inputPath, basemapStyle, nodataValue, paste0(outputPath, ".png")) + processRaster(rasterFile, basemapStyle, nodataValue, fileOutputPath) } - } - else { + } else if (file.exists(inputPath) && grepl("\\.tif$", inputPath)) { + fileOutputPath <- paste0(outputPath, ".png") + processRaster(inputPath, basemapStyle, nodataValue, fileOutputPath) + } else { stop("Input path is neither a valid raster file nor a directory containing raster files.") } } - -# set nodata value. - -# create basemap layer with OpenStreetMap and tmaptools. - -# superimpose raster layer with given color scheme. - -# save - -# Read in folder of raster files - -# Plot iteratively - -# Save - - From 6bae7cc7bafdc890bbd8fccbda07907567d0b993 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Thu, 1 Feb 2024 22:02:57 +0000 Subject: [PATCH 17/66] Add def.plot.flux.interactive later --- pack/eddy4R.maps/R/def.plot.flux.interactive.R | 1 - 1 file changed, 1 deletion(-) delete mode 100644 pack/eddy4R.maps/R/def.plot.flux.interactive.R diff --git a/pack/eddy4R.maps/R/def.plot.flux.interactive.R b/pack/eddy4R.maps/R/def.plot.flux.interactive.R deleted file mode 100644 index 1a0b97e2..00000000 --- a/pack/eddy4R.maps/R/def.plot.flux.interactive.R +++ /dev/null @@ -1 +0,0 @@ -def.plot.flux.interactive \ No newline at end of file From c264e4a215ccaca270a3148b341dc80e2291949c Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 02:36:31 +0000 Subject: [PATCH 18/66] Add function parameters for map style. --- pack/eddy4R.maps/R/def.plot.flux.static.R | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.static.R b/pack/eddy4R.maps/R/def.plot.flux.static.R index aa7f363b..885e6e89 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.static.R +++ b/pack/eddy4R.maps/R/def.plot.flux.static.R @@ -1,4 +1,12 @@ -def.plot.flux.static <- function(inputPath, basemapStyle = 'osm', nodataValue = 0, outputPath = 'output') { +def.plot.flux.static <- function( + inputPath, + outputPath = 'output', + nodataValue = 0, + basemapStyle = 'bing', + alpha = 0.5, + colormap = 'YlOrRd', + style = 'equal', + color_n = 7){ #palette_explorer()) { # Change java system parameters to allow for headless operation (to disable under-the-hood java GUI which will not work in this Docker container). options(java.parameters = "-Djava.awt.headless=true") @@ -12,7 +20,7 @@ def.plot.flux.static <- function(inputPath, basemapStyle = 'osm', nodataValue = map <- tm_shape(osm_map) + tm_rgb() + tm_shape(rasterLayer) + - tm_raster(style = "equal", alpha = 0.4) + + tm_raster(style = style, alpha = alpha, palette=get_brewer_pal(palette = colormap, n = color_n, plot=FALSE)) + tm_layout(legend.outside = FALSE) tmap_save(map, file = outputPath) From 571331482694c1d14911191a8e64dff884288de7 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 03:00:15 +0000 Subject: [PATCH 19/66] Remove embedded parameters. --- pack/eddy4R.maps/R/def.plot.flux.static.R | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.static.R b/pack/eddy4R.maps/R/def.plot.flux.static.R index 885e6e89..6173c2f4 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.static.R +++ b/pack/eddy4R.maps/R/def.plot.flux.static.R @@ -1,18 +1,18 @@ def.plot.flux.static <- function( inputPath, - outputPath = 'output', + outputPath, nodataValue = 0, basemapStyle = 'bing', alpha = 0.5, colormap = 'YlOrRd', style = 'equal', - color_n = 7){ #palette_explorer()) { + color_n = 7) { #palette_explorer()) { # Change java system parameters to allow for headless operation (to disable under-the-hood java GUI which will not work in this Docker container). options(java.parameters = "-Djava.awt.headless=true") - # Plotting function with tmap - processRaster <- function(rasterFile, basemapStyle, nodataValue, outputPath) { + # Function to load and process a single raster file and plot it + plotRaster <- function(rasterFile, outputPath) { rasterLayer <- raster::raster(rasterFile) rasterLayer[rasterLayer == nodataValue] <- NA @@ -20,13 +20,13 @@ def.plot.flux.static <- function( map <- tm_shape(osm_map) + tm_rgb() + tm_shape(rasterLayer) + - tm_raster(style = style, alpha = alpha, palette=get_brewer_pal(palette = colormap, n = color_n, plot=FALSE)) + + tm_raster(style = style, alpha = alpha, palette = get_brewer_pal(palette = colormap, n = color_n, plot = FALSE)) + tm_layout(legend.outside = FALSE) tmap_save(map, file = outputPath) } - # Read in a raster file or folder of raster files + # Check if inputPath is a directory if (dir.exists(inputPath)) { rasterFiles <- list.files(inputPath, pattern = "\\.tif$", full.names = TRUE) if (length(rasterFiles) == 0) { @@ -35,11 +35,12 @@ def.plot.flux.static <- function( for (rasterFile in rasterFiles) { fileName <- basename(rasterFile) fileOutputPath <- paste0(outputPath, "/", sub("\\.tif$", ".png", fileName)) - processRaster(rasterFile, basemapStyle, nodataValue, fileOutputPath) + plotRaster(rasterFile, fileOutputPath) } } else if (file.exists(inputPath) && grepl("\\.tif$", inputPath)) { - fileOutputPath <- paste0(outputPath, ".png") - processRaster(inputPath, basemapStyle, nodataValue, fileOutputPath) + # InputPath is a single file + fileOutputPath <- paste0(outputPath, "/", basename(inputPath), ".png") + plotRaster(inputPath, fileOutputPath) } else { stop("Input path is neither a valid raster file nor a directory containing raster files.") } From f629b5b7a50d64b3a593e350a1e525071cb972d7 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 03:17:13 +0000 Subject: [PATCH 20/66] Add function header. --- pack/eddy4R.maps/R/def.plot.flux.static.R | 58 ++++++++++++++++++++--- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.static.R b/pack/eddy4R.maps/R/def.plot.flux.static.R index 6173c2f4..1556c903 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.static.R +++ b/pack/eddy4R.maps/R/def.plot.flux.static.R @@ -1,8 +1,46 @@ +############################################################################################## +#' @title Definition function: Plot fluxes on a static basemap + +#' @author +#' Sam Bower \email{sbower@atmofacts.com} + +#' @description Function definition. This function creates a flux map visualization on a user defined basemap. +#' +#' @param inputPath A spatial file or folder of spatial files to plot on the basemap. +#' @param outputPath A string for the folder location to save output maps. +#' @param nodata_value If FALSE - plots the flux(es) onto a static basemap. If TRUE, plots the flux(es) onto an interactive basemap. +#' @param basemap_style Basemap styles in OpenStreetMap (more in openmap()) +#' @param alpha Raster opacity +#' @param colormap palette_explorer() for palette options +#' @param style quantile, equal, cont, cat +#' @param color_n color categories in palette + +#' @return +#' +#' + +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @keywords visualization, flux mapping +#' + + +#' @seealso Currently none. + +#' @export + +# changelog and author contributions / copyrights +# Sam Bower (2023-02-01) +# original creation +# +# +############################################################################################### def.plot.flux.static <- function( inputPath, outputPath, - nodataValue = 0, - basemapStyle = 'bing', + nodata_value = 0, + basemap_Style = 'bing', alpha = 0.5, colormap = 'YlOrRd', style = 'equal', @@ -11,35 +49,43 @@ def.plot.flux.static <- function( # Change java system parameters to allow for headless operation (to disable under-the-hood java GUI which will not work in this Docker container). options(java.parameters = "-Djava.awt.headless=true") - # Function to load and process a single raster file and plot it + # Function to load and process a single raster file and plot it with tmap plotRaster <- function(rasterFile, outputPath) { + + #read in raster with raster library rasterLayer <- raster::raster(rasterFile) rasterLayer[rasterLayer == nodataValue] <- NA + + #grab baselayer from OpenStreetMap + osm_map <- tmaptools::read_osm(rasterLayer, type = basemap_style) - osm_map <- tmaptools::read_osm(rasterLayer, type = basemapStyle) + #create tmap object with raster superimposed on basemap map <- tm_shape(osm_map) + tm_rgb() + tm_shape(rasterLayer) + tm_raster(style = style, alpha = alpha, palette = get_brewer_pal(palette = colormap, n = color_n, plot = FALSE)) + tm_layout(legend.outside = FALSE) + #save tmap object tmap_save(map, file = outputPath) } - # Check if inputPath is a directory + # Check if inputPath is a directory and make a list of the files. if (dir.exists(inputPath)) { rasterFiles <- list.files(inputPath, pattern = "\\.tif$", full.names = TRUE) if (length(rasterFiles) == 0) { stop("No raster files found in the directory.") } + #iteratively create and save the maps for (rasterFile in rasterFiles) { fileName <- basename(rasterFile) fileOutputPath <- paste0(outputPath, "/", sub("\\.tif$", ".png", fileName)) plotRaster(rasterFile, fileOutputPath) } + #Save a single file if the input is a single tiff } else if (file.exists(inputPath) && grepl("\\.tif$", inputPath)) { # InputPath is a single file - fileOutputPath <- paste0(outputPath, "/", basename(inputPath), ".png") + fileOutputPath <- paste0(outputPath, "/", tools::file_path_sans_ext(base::basename(inputPath)), ".png") plotRaster(inputPath, fileOutputPath) } else { stop("Input path is neither a valid raster file nor a directory containing raster files.") From 845765b4090d1035586eb6e0509dfd3a3a0b7a9f Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 16:00:20 +0000 Subject: [PATCH 21/66] Add parameters for def.plot.flux.interactive. --- pack/eddy4R.maps/R/def.plot.flux.interactive.R | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 pack/eddy4R.maps/R/def.plot.flux.interactive.R diff --git a/pack/eddy4R.maps/R/def.plot.flux.interactive.R b/pack/eddy4R.maps/R/def.plot.flux.interactive.R new file mode 100644 index 00000000..3db14275 --- /dev/null +++ b/pack/eddy4R.maps/R/def.plot.flux.interactive.R @@ -0,0 +1,11 @@ + + +def.plot.flux.interactive <- function( + inputPath, + outputPath, + nodata_value = 0, + basemap_Style = 'bing', + alpha = 0.5, + colormap = 'YlOrRd', + style = 'equal', + color_n = 7) { \ No newline at end of file From 5cc82a143d943b64df29191b18ec4b0222d0466f Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 16:23:30 +0000 Subject: [PATCH 22/66] Read files. --- .../eddy4R.maps/R/def.plot.flux.interactive.R | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.interactive.R b/pack/eddy4R.maps/R/def.plot.flux.interactive.R index 3db14275..a820d5f4 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.interactive.R +++ b/pack/eddy4R.maps/R/def.plot.flux.interactive.R @@ -1,11 +1,21 @@ def.plot.flux.interactive <- function( - inputPath, - outputPath, + input_path, + output_path, nodata_value = 0, - basemap_Style = 'bing', alpha = 0.5, - colormap = 'YlOrRd', - style = 'equal', - color_n = 7) { \ No newline at end of file + colormap = 'YlOrRd' + ) { + + # List all TIFF files in the folder + rasterFiles <- list.files(input_path, pattern = "\\.tif$", full.names = TRUE) + + + + + + + + +} \ No newline at end of file From 4c2006999cf81a98dbf968b175898121ad3ebf71 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 16:25:24 +0000 Subject: [PATCH 23/66] Function to process and add each raster file to the map. --- pack/eddy4R.maps/R/def.plot.flux.interactive.R | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pack/eddy4R.maps/R/def.plot.flux.interactive.R b/pack/eddy4R.maps/R/def.plot.flux.interactive.R index a820d5f4..08c64836 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.interactive.R +++ b/pack/eddy4R.maps/R/def.plot.flux.interactive.R @@ -11,6 +11,22 @@ def.plot.flux.interactive <- function( # List all TIFF files in the folder rasterFiles <- list.files(input_path, pattern = "\\.tif$", full.names = TRUE) + # Initialize Leaflet map + map <- leaflet() %>% + addProviderTiles("OpenStreetMap", group = "Street") %>% + addProviderTiles("Esri.WorldImagery", group="Imagery") %>% + addProviderTiles("CartoDB.DarkMatter", group= "Dark") + + # Function to process and add each raster file to the map + processAndAddRaster <- function(filePath, map) { + flux <- raster(filePath) + # Replace zeros with NA + flux[flux == 0] <- NA + # Add raster to the map + map <- map %>% addRasterImage(flux, group = base::basename(filePath), colors = 'YlOrRd', opacity = 0.7, layerId = base::basename(filePath)) + return(map) + } + From 39078fe3ecba7e3b9561c1bdd48831ee8420e294 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 16:26:05 +0000 Subject: [PATCH 24/66] Dynamically create groups. --- pack/eddy4R.maps/R/def.plot.flux.interactive.R | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pack/eddy4R.maps/R/def.plot.flux.interactive.R b/pack/eddy4R.maps/R/def.plot.flux.interactive.R index 08c64836..24b9d60a 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.interactive.R +++ b/pack/eddy4R.maps/R/def.plot.flux.interactive.R @@ -27,6 +27,9 @@ def.plot.flux.interactive <- function( return(map) } + # Dynamically create a list of groups for the layers control based on the raster files + rasterGroups <- base::basename(rasterFiles) + From 8b6cab106606f60beaff86e2fccd3150eee318aa Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 16:37:44 +0000 Subject: [PATCH 25/66] Robust input handling. --- .../eddy4R.maps/R/def.plot.flux.interactive.R | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.interactive.R b/pack/eddy4R.maps/R/def.plot.flux.interactive.R index 24b9d60a..0e61c14b 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.interactive.R +++ b/pack/eddy4R.maps/R/def.plot.flux.interactive.R @@ -1,40 +1,48 @@ - - def.plot.flux.interactive <- function( input_path, - output_path, nodata_value = 0, - alpha = 0.5, + alpha = 0.7, # Use specified alpha colormap = 'YlOrRd' - ) { - - # List all TIFF files in the folder - rasterFiles <- list.files(input_path, pattern = "\\.tif$", full.names = TRUE) +) { + # Determine if input_path is a directory or a single file + if (dir.exists(input_path)) { + rasterFiles <- list.files(input_path, pattern = "\\.tif$", full.names = TRUE) + if (length(rasterFiles) == 0) { # Check if no TIFF files found + stop("No TIFF files found in the directory.") + } + } else if (file.exists(input_path) && grepl("\\.tif$", input_path)) { + rasterFiles <- list(input_path) # Ensure rasterFiles is a list + } else { + stop("Input path is neither a valid folder nor a TIFF file.") + } # Initialize Leaflet map map <- leaflet() %>% addProviderTiles("OpenStreetMap", group = "Street") %>% - addProviderTiles("Esri.WorldImagery", group="Imagery") %>% - addProviderTiles("CartoDB.DarkMatter", group= "Dark") + addProviderTiles("Esri.WorldImagery", group = "Imagery") %>% + addProviderTiles("CartoDB.DarkMatter", group = "Dark") # Function to process and add each raster file to the map processAndAddRaster <- function(filePath, map) { flux <- raster(filePath) - # Replace zeros with NA - flux[flux == 0] <- NA - # Add raster to the map - map <- map %>% addRasterImage(flux, group = base::basename(filePath), colors = 'YlOrRd', opacity = 0.7, layerId = base::basename(filePath)) + # Replace nodata_value with NA + flux[flux == nodata_value] <- NA + # Add raster to the map using specified colormap and alpha + map <- map %>% addRasterImage(flux, group = base::basename(filePath), colors = colormap, opacity = alpha, layerId = base::basename(filePath)) return(map) } + # Apply the function to each raster file + for(filePath in rasterFiles) { + map <- processAndAddRaster(filePath, map) + } + # Dynamically create a list of groups for the layers control based on the raster files rasterGroups <- base::basename(rasterFiles) + # Add layers control to the map + map <- map %>% addLayersControl(overlayGroups = rasterGroups, baseGroups = c("Street", "Imagery", "Dark")) - - - - - - -} \ No newline at end of file + # Print the map + print(map) +} From 380338b42964c09fd86b50cf1b8f2433a38dd427 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 16:41:23 +0000 Subject: [PATCH 26/66] Add save functionality. --- pack/eddy4R.maps/R/def.plot.flux.interactive.R | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.interactive.R b/pack/eddy4R.maps/R/def.plot.flux.interactive.R index 0e61c14b..f5d6938c 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.interactive.R +++ b/pack/eddy4R.maps/R/def.plot.flux.interactive.R @@ -1,9 +1,12 @@ def.plot.flux.interactive <- function( input_path, nodata_value = 0, - alpha = 0.7, # Use specified alpha - colormap = 'YlOrRd' + alpha = 0.7, + colormap = 'YlOrRd', + save_path = NULL ) { + + # Determine if input_path is a directory or a single file if (dir.exists(input_path)) { rasterFiles <- list.files(input_path, pattern = "\\.tif$", full.names = TRUE) @@ -43,6 +46,10 @@ def.plot.flux.interactive <- function( # Add layers control to the map map <- map %>% addLayersControl(overlayGroups = rasterGroups, baseGroups = c("Street", "Imagery", "Dark")) - # Print the map - print(map) + if (!is.null(save_path)) { + saveWidget(map, file = save_path, selfcontained = TRUE) + } else { + # Print the map to display it interactively + print(map) + } } From 416ab899779df1bc0184caa290d8c3960c41bcf0 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 16:45:31 +0000 Subject: [PATCH 27/66] Add header. --- .../eddy4R.maps/R/def.plot.flux.interactive.R | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.interactive.R b/pack/eddy4R.maps/R/def.plot.flux.interactive.R index f5d6938c..713f0e91 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.interactive.R +++ b/pack/eddy4R.maps/R/def.plot.flux.interactive.R @@ -1,3 +1,39 @@ +############################################################################################## +#' @title Definition function: Plot fluxes on an interactive map. + +#' @author +#' Sam Bower \email{sbower@atmofacts.com} + +#' @description Function definition. This function creates an interactive flux map visualization that can be used in R viewer or in a web browser. +#' +#' @param input_path A geotiff file or folder of geotiff files to plot on the basemap. +#' @param nodata_value Nodata value from flux data +#' @param alpha Raster opacity +#' @param colormap palette_explorer() for palette options + +#' @return +#' +#' + +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @keywords visualization, flux mapping +#' + + +#' @seealso Currently none. + +#' @export + +# changelog and author contributions / copyrights +# Sam Bower (2023-02-02) +# original creation +# +# +############################################################################################### + + def.plot.flux.interactive <- function( input_path, nodata_value = 0, @@ -6,6 +42,8 @@ def.plot.flux.interactive <- function( save_path = NULL ) { + library(leaflet) + library(raster) # Determine if input_path is a directory or a single file if (dir.exists(input_path)) { @@ -47,7 +85,7 @@ def.plot.flux.interactive <- function( map <- map %>% addLayersControl(overlayGroups = rasterGroups, baseGroups = c("Street", "Imagery", "Dark")) if (!is.null(save_path)) { - saveWidget(map, file = save_path, selfcontained = TRUE) + htmlwidgets::saveWidget(map, file = save_path, selfcontained = TRUE) } else { # Print the map to display it interactively print(map) From 6fa7e63d0ee0f98262ca74c8cfe4879f67bab946 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 16:46:24 +0000 Subject: [PATCH 28/66] fix header parameter wording. --- pack/eddy4R.maps/R/def.plot.flux.static.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.static.R b/pack/eddy4R.maps/R/def.plot.flux.static.R index 1556c903..aaeb64b5 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.static.R +++ b/pack/eddy4R.maps/R/def.plot.flux.static.R @@ -8,7 +8,7 @@ #' #' @param inputPath A spatial file or folder of spatial files to plot on the basemap. #' @param outputPath A string for the folder location to save output maps. -#' @param nodata_value If FALSE - plots the flux(es) onto a static basemap. If TRUE, plots the flux(es) onto an interactive basemap. +#' @param nodata_value The nodata value specified from the flux data. #' @param basemap_style Basemap styles in OpenStreetMap (more in openmap()) #' @param alpha Raster opacity #' @param colormap palette_explorer() for palette options From 9c39e2288076a1cab02d67de9ea3b245abf79eee Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 17:28:50 +0000 Subject: [PATCH 29/66] bug fixes. --- pack/eddy4R.maps/R/def.plot.flux.static.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.static.R b/pack/eddy4R.maps/R/def.plot.flux.static.R index aaeb64b5..df521675 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.static.R +++ b/pack/eddy4R.maps/R/def.plot.flux.static.R @@ -40,7 +40,7 @@ def.plot.flux.static <- function( inputPath, outputPath, nodata_value = 0, - basemap_Style = 'bing', + basemap_style = 'bing', alpha = 0.5, colormap = 'YlOrRd', style = 'equal', @@ -54,7 +54,7 @@ def.plot.flux.static <- function( #read in raster with raster library rasterLayer <- raster::raster(rasterFile) - rasterLayer[rasterLayer == nodataValue] <- NA + rasterLayer[rasterLayer == nodata_value] <- NA #grab baselayer from OpenStreetMap osm_map <- tmaptools::read_osm(rasterLayer, type = basemap_style) From c56f0ca70126ea95e38530e60b3e765fea1c8917 Mon Sep 17 00:00:00 2001 From: stefanmet Date: Fri, 2 Feb 2024 17:52:45 +0000 Subject: [PATCH 30/66] add functionality for binned standard deviation --- pack/eddy4R.base/R/def.bin.R | 37 ++++++++++++++++++++------------- pack/eddy4R.base/man/def.bin.Rd | 14 ++++++------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/pack/eddy4R.base/R/def.bin.R b/pack/eddy4R.base/R/def.bin.R index 5e12748a..d71a51c0 100644 --- a/pack/eddy4R.base/R/def.bin.R +++ b/pack/eddy4R.base/R/def.bin.R @@ -11,10 +11,10 @@ #' @param depe Either a vector or matrix of class numeric or integer containing the dependent variable and of the same length as \code{idep}. [] #' @param RngMinMax An object of class numeric or integer containing the minimum and maximum values of the independent variable. Defaults to NULL. [] #' @param NumBin An object of class numeric or integer containing the number of bins. [] -#' @param widtBin An object of class string containing the functions ("lin", "log10", "exp10", "logExp", "expLog") to determine bin width distribution of the independent variable. [] -#' @param meanFunc An object of class string containing the arithmetic "mean" and "median". [] +#' @param WidtBin An object of class string containing the functions ("lin", "log10", "exp10", "logExp", "expLog") to determine bin width distribution of the independent variable. [] +#' @param Func An object of class string containing the arithmetic "mean", "median", or "sd". [] -#' @return \code{idep} A list object of class "numeric" containing the resulted binning of independent variable and of the same length as {widtBin} and \code{depe} a matrix containing the the resulted binning of dependent variable and of the same length as {widtBin}. \cr +#' @return \code{idep} A list object of class "numeric" containing the resulted binning of independent variable and of the same length as {WidtBin} and \code{depe} a matrix containing the the resulted binning of dependent variable and of the same length as {WidtBin}. \cr #' @references #' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 @@ -22,8 +22,8 @@ #' @keywords bin #' @examples -#' def.bin(idep = rnorm(5000), depe = rnorm(5000), RngMinMax = NULL, NumBin = 23, widtBin = "log10", meanFunc = "mean" ) -#' def.bin(idep = rnorm(500), depe = rnorm(500), RngMinMax = c(0.1, 0.4), NumBin = 12, widtBin = "lin", meanFunc = "median" ) +#' def.bin(idep = rnorm(5000), depe = rnorm(5000), RngMinMax = NULL, NumBin = 23, WidtBin = "log10", Func = "mean" ) +#' def.bin(idep = rnorm(500), depe = rnorm(500), RngMinMax = c(0.1, 0.4), NumBin = 12, WidtBin = "lin", Func = "median" ) #' @seealso Currently none @@ -38,6 +38,8 @@ # Initail naming convention for eddy4R # Natchaya P-Durden (2018-04-03) # update @param format +# Stefan Metzger (2024-02-02) +# add functionality for binned standard deviation ############################################################################################## #BIN DATA @@ -46,15 +48,15 @@ def.bin <- function( depe, RngMinMax = NULL, NumBin, - widtBin = c("lin", "log10", "exp10", "logExp", "expLog"), - meanFunc = c("mean", "median") + WidtBin = c("lin", "log10", "exp10", "logExp", "expLog"), + Func = c("mean", "median", "sd") ) { #aggregation with binning # idep: independent variable, frequency, wavenumber etc. # depe: dependent variable, vector or matrix of same length as idep # RngMinMax: min and max range # NumBin: number of bins - # widtBin: c("lin", "log10", "exp10", "logExp", "expLog") bin width distribution as function of idep + # WidtBin: c("lin", "log10", "exp10", "logExp", "expLog") bin width distribution as function of idep #prepare variables idep <- idep @@ -63,23 +65,28 @@ def.bin <- function( #define boundary if(is.null(RngMinMax)) minMax <- base::range(idep) - if(widtBin == "lin") rng <- base::seq(minMax[1], minMax[2], length.out=(NumBin + 1)) - if(widtBin == "log10") rng <- base::log10(base::seq(10^(minMax[1]), 10^(minMax[2]), length.out=(NumBin + 1))) - if(widtBin == "exp10") rng <- 10^(base::seq(base::log10(minMax[1]), base::log10(minMax[2]), length.out=(NumBin + 1))) - if(widtBin == "logExp") rng <- base::log(base::seq(base::exp(minMax[1]), base::exp(minMax[2]), length.out=(NumBin + 1))) - if(widtBin == "expLog") rng <- base::exp(base::seq(base::log(minMax[1]), base::log(minMax[2]), length.out=(NumBin + 1))) + if(WidtBin == "lin") rng <- base::seq(minMax[1], minMax[2], length.out=(NumBin + 1)) + if(WidtBin == "log10") rng <- base::log10(base::seq(10^(minMax[1]), 10^(minMax[2]), length.out=(NumBin + 1))) + if(WidtBin == "exp10") rng <- 10^(base::seq(base::log10(minMax[1]), base::log10(minMax[2]), length.out=(NumBin + 1))) + if(WidtBin == "logExp") rng <- base::log(base::seq(base::exp(minMax[1]), base::exp(minMax[2]), length.out=(NumBin + 1))) + if(WidtBin == "expLog") rng <- base::exp(base::seq(base::log(minMax[1]), base::log(minMax[2]), length.out=(NumBin + 1))) if(is.null(RngMinMax)) rng[c(1,length(rng))] <- c(0,Inf) #actual binning for(i in 1:(length(rng)-1)) { whrBin <- which(idep > rng[i] & idep <= rng[i+1]) - if(meanFunc == "median") { + if(Func == "median") { idepTmp <- stats::median(idep[whrBin], na.rm=T) depeTmp <- sapply(1:ncol(depe), function(x) stats::median(depe[whrBin,x], na.rm=T)) - } else { + } + if(Func == "mean") { idepTmp <- base::mean(idep[whrBin], na.rm=T) depeTmp <- sapply(1:ncol(depe), function(x) base::mean(depe[whrBin,x], na.rm=T)) } + if(Func == "sd") { + idepTmp <- base::mean(idep[whrBin], na.rm=T) + depeTmp <- sapply(1:ncol(depe), function(x) stats::sd(depe[whrBin,x], na.rm=T)) + } if(i == 1) { idepBin <- idepTmp depeBin <- depeTmp diff --git a/pack/eddy4R.base/man/def.bin.Rd b/pack/eddy4R.base/man/def.bin.Rd index 03064f7f..70527ef3 100644 --- a/pack/eddy4R.base/man/def.bin.Rd +++ b/pack/eddy4R.base/man/def.bin.Rd @@ -9,8 +9,8 @@ def.bin( depe, RngMinMax = NULL, NumBin, - widtBin = c("lin", "log10", "exp10", "logExp", "expLog"), - meanFunc = c("mean", "median") + WidtBin = c("lin", "log10", "exp10", "logExp", "expLog"), + Func = c("mean", "median", "sd") ) } \arguments{ @@ -22,19 +22,19 @@ def.bin( \item{NumBin}{An object of class numeric or integer containing the number of bins. []} -\item{widtBin}{An object of class string containing the functions ("lin", "log10", "exp10", "logExp", "expLog") to determine bin width distribution of the independent variable. []} +\item{WidtBin}{An object of class string containing the functions ("lin", "log10", "exp10", "logExp", "expLog") to determine bin width distribution of the independent variable. []} -\item{meanFunc}{An object of class string containing the arithmetic "mean" and "median". []} +\item{Func}{An object of class string containing the arithmetic "mean", "median", or "sd". []} } \value{ -\code{idep} A list object of class "numeric" containing the resulted binning of independent variable and of the same length as {widtBin} and \code{depe} a matrix containing the the resulted binning of dependent variable and of the same length as {widtBin}. \cr +\code{idep} A list object of class "numeric" containing the resulted binning of independent variable and of the same length as {WidtBin} and \code{depe} a matrix containing the the resulted binning of dependent variable and of the same length as {WidtBin}. \cr } \description{ Function definition. Smooth data using Binning method. } \examples{ -def.bin(idep = rnorm(5000), depe = rnorm(5000), RngMinMax = NULL, NumBin = 23, widtBin = "log10", meanFunc = "mean" ) -def.bin(idep = rnorm(500), depe = rnorm(500), RngMinMax = c(0.1, 0.4), NumBin = 12, widtBin = "lin", meanFunc = "median" ) +def.bin(idep = rnorm(5000), depe = rnorm(5000), RngMinMax = NULL, NumBin = 23, WidtBin = "log10", Func = "mean" ) +def.bin(idep = rnorm(500), depe = rnorm(500), RngMinMax = c(0.1, 0.4), NumBin = 12, WidtBin = "lin", Func = "median" ) } \references{ License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 From 7877e252b8608f9db310510a1cfe71de64cec92d Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 18:26:59 +0000 Subject: [PATCH 31/66] Start def.plot.flux.animate.R --- pack/eddy4R.maps/R/def.plot.flux.animate.R | 1 + 1 file changed, 1 insertion(+) create mode 100644 pack/eddy4R.maps/R/def.plot.flux.animate.R diff --git a/pack/eddy4R.maps/R/def.plot.flux.animate.R b/pack/eddy4R.maps/R/def.plot.flux.animate.R new file mode 100644 index 00000000..542dc9d8 --- /dev/null +++ b/pack/eddy4R.maps/R/def.plot.flux.animate.R @@ -0,0 +1 @@ +def.plot.flux.animate \ No newline at end of file From 513f9ddf7d10445eec2343d0951618e77f053b41 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 18:29:34 +0000 Subject: [PATCH 32/66] Outline parameters --- pack/eddy4R.maps/R/def.plot.flux.animate.R | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.animate.R b/pack/eddy4R.maps/R/def.plot.flux.animate.R index 542dc9d8..71043115 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.animate.R +++ b/pack/eddy4R.maps/R/def.plot.flux.animate.R @@ -1 +1,20 @@ -def.plot.flux.animate \ No newline at end of file +def.plot.flux.animate <- function( + input_folder, + output_file, + nodata_value = 0, + colormap = 'YlOrRd', + basemap_style = 'osm', + palette_style = 'equal', + alpha = 0.4, + legend = TRUE + ) { + + # Load necessary libraries + library(raster) + library(tmap) + library(tmaptools) + library(gifski) + + + +} \ No newline at end of file From e6de19381a31ad8b0003c1616e9944af3dbb4d3b Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 18:32:09 +0000 Subject: [PATCH 33/66] Read in files and start a temporary workspace for .png images. --- pack/eddy4R.maps/R/def.plot.flux.animate.R | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pack/eddy4R.maps/R/def.plot.flux.animate.R b/pack/eddy4R.maps/R/def.plot.flux.animate.R index 71043115..dbb4654a 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.animate.R +++ b/pack/eddy4R.maps/R/def.plot.flux.animate.R @@ -15,6 +15,18 @@ def.plot.flux.animate <- function( library(tmaptools) library(gifski) + # Ensure tmap is in plot mode + tmap_mode("plot") + + # Create a temporary directory for storing map images + temp_dir <- tempfile() + dir.create(temp_dir) + + # List all raster files in the directory + raster_files <- list.files(input_folder, pattern = "\\.tif$", full.names = TRUE) + + # Initialize a list to store file paths of individual map images + temp_files <- vector("character", length(raster_files)) } \ No newline at end of file From 4c96e7d2b4b0dc90d2953063d06566e199c23d30 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 18:33:16 +0000 Subject: [PATCH 34/66] Create static map for rasters like def.plot.flux.static. --- pack/eddy4R.maps/R/def.plot.flux.animate.R | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/pack/eddy4R.maps/R/def.plot.flux.animate.R b/pack/eddy4R.maps/R/def.plot.flux.animate.R index dbb4654a..baf6d6c2 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.animate.R +++ b/pack/eddy4R.maps/R/def.plot.flux.animate.R @@ -28,5 +28,28 @@ def.plot.flux.animate <- function( # Initialize a list to store file paths of individual map images temp_files <- vector("character", length(raster_files)) + # Loop through each raster file to create a static map + for (i in seq_along(raster_files)) { + flux <- raster::raster(raster_files[i]) + flux[flux == nodata_value] <- NA # Apply nodata value + + osm_map <- tmaptools::read_osm(flux, type = basemap_style) + + map <- tm_shape(osm_map) + + tm_rgb() + + tm_shape(flux) + + tm_raster(style = palette_style, alpha = alpha, palette = colormap) + + tm_layout(legend.outside = legend) + + # Generate a temporary file path for the static map image + temp_file <- file.path(temp_dir, paste0("map_", i, ".png")) + temp_files[i] <- temp_file # Store the file path + + # Save the map as an image + tmap_save(map, filename = temp_file, width = 800, height = 600, units = "px") + } + + + } \ No newline at end of file From 1370f49525cc2057b63b0514e23670fa5249ebf5 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 18:34:37 +0000 Subject: [PATCH 35/66] write gif with gifski library --- pack/eddy4R.maps/R/def.plot.flux.animate.R | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pack/eddy4R.maps/R/def.plot.flux.animate.R b/pack/eddy4R.maps/R/def.plot.flux.animate.R index baf6d6c2..13ce755b 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.animate.R +++ b/pack/eddy4R.maps/R/def.plot.flux.animate.R @@ -49,7 +49,12 @@ def.plot.flux.animate <- function( tmap_save(map, filename = temp_file, width = 800, height = 600, units = "px") } + # Use gifski to create the GIF from the PNG files + gifski(png_files = temp_files, gif_file = output_file, width = 800, height = 600, delay = 1, progress = TRUE) + # After creating the GIF, clean up the temporary directory and its contents + unlink(temp_dir, recursive = TRUE) + cat("Animation created at:", output_file, "\n") } \ No newline at end of file From 1e00dd30feb64932768b2989f1a539ec9364788f Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 2 Feb 2024 18:41:36 +0000 Subject: [PATCH 36/66] Add header. --- pack/eddy4R.maps/R/def.plot.flux.animate.R | 57 +++++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/pack/eddy4R.maps/R/def.plot.flux.animate.R b/pack/eddy4R.maps/R/def.plot.flux.animate.R index 13ce755b..9e50cf04 100644 --- a/pack/eddy4R.maps/R/def.plot.flux.animate.R +++ b/pack/eddy4R.maps/R/def.plot.flux.animate.R @@ -1,3 +1,55 @@ +############################################################################################## +#' @title Definition function: Plot fluxes on an animated gif. + +#' @author +#' Sam Bower \email{sbower@atmofacts.com} + +#' @description Function definition. This function creates an animated flux map visualization for temporal flux map data contained in a folder. +#' +#' @param input_folder A folder of geotiff files. +#' @param output_file The path of the output file (.gif) +#' @param nodata_value Nodata value from flux data +#' @param alpha Raster opacity +#' @param colormap palette_explorer() for palette options +#' @param palette_style quantile, equal, cont, cat +#' @param legend Boolean. If legend plots on the map +#' @param delay The delay time in the animation. +#' + +#' @return +#' +#' + +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @keywords visualization, flux mapping, animation +#' +#' @examples +#' +#' def.plot.flux.animation( +#' input_folder = "/path/to/input/folder", # Replace with your input folder path +#' output_file = "/path/to/output/animation.gif", # Replace with your desired output GIF path +#' nodata_value = 0, +#' colormap = 'YlOrRd', +#' basemap_style = 'osm', +#' palette_style = 'equal', +#' alpha = 0.4, +#' legend = TRUE +#' ) + + +#' @seealso Currently none. + +#' @export + +# changelog and author contributions / copyrights +# Sam Bower (2023-02-02) +# original creation +# +# +############################################################################################### + def.plot.flux.animate <- function( input_folder, output_file, @@ -6,7 +58,8 @@ def.plot.flux.animate <- function( basemap_style = 'osm', palette_style = 'equal', alpha = 0.4, - legend = TRUE + legend = TRUE, + delay = 1 ) { # Load necessary libraries @@ -50,7 +103,7 @@ def.plot.flux.animate <- function( } # Use gifski to create the GIF from the PNG files - gifski(png_files = temp_files, gif_file = output_file, width = 800, height = 600, delay = 1, progress = TRUE) + gifski(png_files = temp_files, gif_file = output_file, width = 800, height = 600, delay = delay, progress = TRUE) # After creating the GIF, clean up the temporary directory and its contents unlink(temp_dir, recursive = TRUE) From ced454488a47e91172083f3996240bde8efb654e Mon Sep 17 00:00:00 2001 From: stefanmet Date: Fri, 2 Feb 2024 21:37:04 +0000 Subject: [PATCH 37/66] superseded by individual functions .animate, .interactive, and .static --- pack/eddy4R.maps/R/def.plot.flux.spatial.R | 100 --------------------- 1 file changed, 100 deletions(-) delete mode 100644 pack/eddy4R.maps/R/def.plot.flux.spatial.R diff --git a/pack/eddy4R.maps/R/def.plot.flux.spatial.R b/pack/eddy4R.maps/R/def.plot.flux.spatial.R deleted file mode 100644 index e589fe61..00000000 --- a/pack/eddy4R.maps/R/def.plot.flux.spatial.R +++ /dev/null @@ -1,100 +0,0 @@ -############################################################################################## -#' @title Definition function: Plot fluxes on a basemap - -#' @author -#' Sam Bower \email{sbower@atmofacts.com} - -#' @description Function definition. This function saves a GeoTiff flux map onto a static basemap or an interactive html map. -#' -#' @param input_path A spatial file or folder of spatial files to plot on the basemap. -#' @param output_path A string for the folder location to save output maps. -#' @param interactive If FALSE - plots the flux(es) onto a static basemap. If TRUE, plots the flux(es) onto an interactive basemap. -#' @param basemap_style For a list of basemap styles use names(providers) - -#' @return -#' -#' - -#' @references -#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 - -#' @keywords visualization, flux mapping -#' -#' @examples -#' -#' path_in = "/path/to/folder/of/tif/files" -#' path_out = "/path/to/desired/output/location" -#' plot.flux.spatial(path_in, path_out, 'viridis', opacity = 0.5,'OpenStreetMap', TRUE) -#' - - - -#' @seealso Currently none. - -#' @export - -# changelog and author contributions / copyrights -# Sam Bower (2023-01-30) -# original creation -# -# -############################################################################################### -# Define the function with improved interactive HTML handling -def.plot.flux.spatial <- function(input_path, output_path, colormap = 'viridis', opacity = 0.5, basemap_style = 'OpenStreetMap', interactive = FALSE) { - # Load necessary libraries - library(raster) - library(leaflet) - library(htmlwidgets) - library(mapview) - - # Check if input_path is a directory or a single file - if (base::dir.exists(input_path)) { - # Handle directory of TIFF files - tiff_files <- base::list.files(input_path, pattern = '\\.tif$', full.names = TRUE) - if (interactive) { - # Create an interactive map with layer selection for each TIFF file - map <- leaflet::leaflet() - map <- map %>% leaflet::addProviderTiles(leaflet::providers[[basemap_style]]) - - for (file in tiff_files) { - raster_layer <- raster::raster(file) - map <- map %>% leaflet::addRasterImage( - raster_layer, - group = base::basename(file), - colors = colormap, - opacity = opacity) - } - - map <- map %>% leaflet::addLayersControl( - overlayGroups = base::basename(tiff_files), - options = leaflet::layersControlOptions(collapsed = FALSE)) - - output_html_path <- base::paste0(output_path, tools::file_path_sans_ext(base::basename(file)), '.html') - htmlwidgets::saveWidget(map, file = output_html_path, selfcontained = TRUE) - base::cat('Interactive map saved to:', output_html_path, '\n') - } else { - # Create a PNG map for each TIFF file - for (file in tiff_files) { - raster_layer <- raster::raster(file) - map <- leaflet::leaflet() %>% - leaflet::addProviderTiles(leaflet::providers[[basemap_style]]) %>% - leaflet::addRasterImage(raster_layer, colors = colormap, opacity = opacity) - mapview::mapshot(map, file = base::paste0(output_path, tools::file_path_sans_ext(base::basename(file)), '.png')) - } - } - } else { - # Handle single TIFF file - raster_file <- raster::raster(input_path) - map <- leaflet::leaflet() %>% - leaflet::addProviderTiles(leaflet::providers[[basemap_style]]) %>% - leaflet::addRasterImage(raster_file, colors = colormap, opacity = opacity) - if (interactive) { - output_html_path <- base::paste0(output_path, tools::file_path_sans_ext(base::basename(input_path)), '.html') - htmlwidgets::saveWidget(map, file = output_html_path, selfcontained = TRUE) - base::cat('Interactive map saved to:', output_html_path, '\n') - } else { - mapview::mapshot(map, file = base::paste0(output_path,tools::file_path_sans_ext(base::basename(input_path)), '.png')) - } - } - return(map) -} From a36b4aa5c0ed98278fbac7e2cc3c921c5bcef3e0 Mon Sep 17 00:00:00 2001 From: stefanmet Date: Fri, 2 Feb 2024 21:38:31 +0000 Subject: [PATCH 38/66] rebuild maps package --- pack/eddy4R.maps/NAMESPACE | 4 +- pack/eddy4R.maps/man/def.plot.flux.animate.Rd | 67 +++++++++++++++++++ .../man/def.plot.flux.interactive.Rd | 41 ++++++++++++ pack/eddy4R.maps/man/def.plot.flux.spatial.Rd | 49 -------------- pack/eddy4R.maps/man/def.plot.flux.static.Rd | 52 ++++++++++++++ 5 files changed, 163 insertions(+), 50 deletions(-) create mode 100644 pack/eddy4R.maps/man/def.plot.flux.animate.Rd create mode 100644 pack/eddy4R.maps/man/def.plot.flux.interactive.Rd delete mode 100644 pack/eddy4R.maps/man/def.plot.flux.spatial.Rd create mode 100644 pack/eddy4R.maps/man/def.plot.flux.static.Rd diff --git a/pack/eddy4R.maps/NAMESPACE b/pack/eddy4R.maps/NAMESPACE index 7600e688..d4ed0cbd 100644 --- a/pack/eddy4R.maps/NAMESPACE +++ b/pack/eddy4R.maps/NAMESPACE @@ -1,5 +1,7 @@ # Generated by roxygen2: do not edit by hand export(def.algn.foot.rng) -export(def.plot.flux.spatial) +export(def.plot.flux.animate) +export(def.plot.flux.interactive) +export(def.plot.flux.static) importFrom(matlab,padarray) diff --git a/pack/eddy4R.maps/man/def.plot.flux.animate.Rd b/pack/eddy4R.maps/man/def.plot.flux.animate.Rd new file mode 100644 index 00000000..587e8160 --- /dev/null +++ b/pack/eddy4R.maps/man/def.plot.flux.animate.Rd @@ -0,0 +1,67 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/def.plot.flux.animate.R +\name{def.plot.flux.animate} +\alias{def.plot.flux.animate} +\title{Definition function: Plot fluxes on an animated gif.} +\usage{ +def.plot.flux.animate( + input_folder, + output_file, + nodata_value = 0, + colormap = "YlOrRd", + basemap_style = "osm", + palette_style = "equal", + alpha = 0.4, + legend = TRUE, + delay = 1 +) +} +\arguments{ +\item{input_folder}{A folder of geotiff files.} + +\item{output_file}{The path of the output file (.gif)} + +\item{nodata_value}{Nodata value from flux data} + +\item{colormap}{palette_explorer() for palette options} + +\item{palette_style}{quantile, equal, cont, cat} + +\item{alpha}{Raster opacity} + +\item{legend}{Boolean. If legend plots on the map} + +\item{delay}{The delay time in the animation.} +} +\value{ + +} +\description{ +Function definition. This function creates an animated flux map visualization for temporal flux map data contained in a folder. +} +\examples{ + +def.plot.flux.animation( +input_folder = "/path/to/input/folder", # Replace with your input folder path +output_file = "/path/to/output/animation.gif", # Replace with your desired output GIF path +nodata_value = 0, +colormap = 'YlOrRd', +basemap_style = 'osm', +palette_style = 'equal', +alpha = 0.4, +legend = TRUE +) +} +\references{ +License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +} +\seealso{ +Currently none. +} +\author{ +Sam Bower \email{sbower@atmofacts.com} +} +\keyword{animation} +\keyword{flux} +\keyword{mapping,} +\keyword{visualization,} diff --git a/pack/eddy4R.maps/man/def.plot.flux.interactive.Rd b/pack/eddy4R.maps/man/def.plot.flux.interactive.Rd new file mode 100644 index 00000000..f28fb199 --- /dev/null +++ b/pack/eddy4R.maps/man/def.plot.flux.interactive.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/def.plot.flux.interactive.R +\name{def.plot.flux.interactive} +\alias{def.plot.flux.interactive} +\title{Definition function: Plot fluxes on an interactive map.} +\usage{ +def.plot.flux.interactive( + input_path, + nodata_value = 0, + alpha = 0.7, + colormap = "YlOrRd", + save_path = NULL +) +} +\arguments{ +\item{input_path}{A geotiff file or folder of geotiff files to plot on the basemap.} + +\item{nodata_value}{Nodata value from flux data} + +\item{alpha}{Raster opacity} + +\item{colormap}{palette_explorer() for palette options} +} +\value{ + +} +\description{ +Function definition. This function creates an interactive flux map visualization that can be used in R viewer or in a web browser. +} +\references{ +License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +} +\seealso{ +Currently none. +} +\author{ +Sam Bower \email{sbower@atmofacts.com} +} +\keyword{flux} +\keyword{mapping} +\keyword{visualization,} diff --git a/pack/eddy4R.maps/man/def.plot.flux.spatial.Rd b/pack/eddy4R.maps/man/def.plot.flux.spatial.Rd deleted file mode 100644 index 71cf1008..00000000 --- a/pack/eddy4R.maps/man/def.plot.flux.spatial.Rd +++ /dev/null @@ -1,49 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/def.plot.flux.spatial.R -\name{def.plot.flux.spatial} -\alias{def.plot.flux.spatial} -\title{Definition function: Plot fluxes on a basemap} -\usage{ -def.plot.flux.spatial( - input_path, - output_path, - colormap = "viridis", - opacity = 0.5, - basemap_style = "OpenStreetMap", - interactive = FALSE -) -} -\arguments{ -\item{input_path}{A spatial file or folder of spatial files to plot on the basemap.} - -\item{output_path}{A string for the folder location to save output maps.} - -\item{basemap_style}{For a list of basemap styles use names(providers)} - -\item{interactive}{If FALSE - plots the flux(es) onto a static basemap. If TRUE, plots the flux(es) onto an interactive basemap.} -} -\value{ - -} -\description{ -Function definition. This function saves a GeoTiff flux map onto a static basemap or an interactive html map. -} -\examples{ - -path_in = "/path/to/folder/of/tif/files" -path_out = "/path/to/desired/output/location" -plot.flux.spatial(path_in, path_out, 'viridis', opacity = 0.5,'OpenStreetMap', TRUE) - -} -\references{ -License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 -} -\seealso{ -Currently none. -} -\author{ -Sam Bower \email{sbower@atmofacts.com} -} -\keyword{flux} -\keyword{mapping} -\keyword{visualization,} diff --git a/pack/eddy4R.maps/man/def.plot.flux.static.Rd b/pack/eddy4R.maps/man/def.plot.flux.static.Rd new file mode 100644 index 00000000..cf460c4f --- /dev/null +++ b/pack/eddy4R.maps/man/def.plot.flux.static.Rd @@ -0,0 +1,52 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/def.plot.flux.static.R +\name{def.plot.flux.static} +\alias{def.plot.flux.static} +\title{Definition function: Plot fluxes on a static basemap} +\usage{ +def.plot.flux.static( + inputPath, + outputPath, + nodata_value = 0, + basemap_style = "bing", + alpha = 0.5, + colormap = "YlOrRd", + style = "equal", + color_n = 7 +) +} +\arguments{ +\item{inputPath}{A spatial file or folder of spatial files to plot on the basemap.} + +\item{outputPath}{A string for the folder location to save output maps.} + +\item{nodata_value}{The nodata value specified from the flux data.} + +\item{basemap_style}{Basemap styles in OpenStreetMap (more in openmap())} + +\item{alpha}{Raster opacity} + +\item{colormap}{palette_explorer() for palette options} + +\item{style}{quantile, equal, cont, cat} + +\item{color_n}{color categories in palette} +} +\value{ + +} +\description{ +Function definition. This function creates a flux map visualization on a user defined basemap. +} +\references{ +License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +} +\seealso{ +Currently none. +} +\author{ +Sam Bower \email{sbower@atmofacts.com} +} +\keyword{flux} +\keyword{mapping} +\keyword{visualization,} From 682587824a80b46534d6b7a0ff9294ac60547a8d Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 9 Feb 2024 15:47:46 +0000 Subject: [PATCH 39/66] clean branch --- pack/eddy4R.maps/R/def.plot.flux.animate.R | 113 ------------------ .../eddy4R.maps/R/def.plot.flux.interactive.R | 93 -------------- pack/eddy4R.maps/R/def.plot.flux.static.R | 93 -------------- 3 files changed, 299 deletions(-) delete mode 100644 pack/eddy4R.maps/R/def.plot.flux.animate.R delete mode 100644 pack/eddy4R.maps/R/def.plot.flux.interactive.R delete mode 100644 pack/eddy4R.maps/R/def.plot.flux.static.R diff --git a/pack/eddy4R.maps/R/def.plot.flux.animate.R b/pack/eddy4R.maps/R/def.plot.flux.animate.R deleted file mode 100644 index 9e50cf04..00000000 --- a/pack/eddy4R.maps/R/def.plot.flux.animate.R +++ /dev/null @@ -1,113 +0,0 @@ -############################################################################################## -#' @title Definition function: Plot fluxes on an animated gif. - -#' @author -#' Sam Bower \email{sbower@atmofacts.com} - -#' @description Function definition. This function creates an animated flux map visualization for temporal flux map data contained in a folder. -#' -#' @param input_folder A folder of geotiff files. -#' @param output_file The path of the output file (.gif) -#' @param nodata_value Nodata value from flux data -#' @param alpha Raster opacity -#' @param colormap palette_explorer() for palette options -#' @param palette_style quantile, equal, cont, cat -#' @param legend Boolean. If legend plots on the map -#' @param delay The delay time in the animation. -#' - -#' @return -#' -#' - -#' @references -#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 - -#' @keywords visualization, flux mapping, animation -#' -#' @examples -#' -#' def.plot.flux.animation( -#' input_folder = "/path/to/input/folder", # Replace with your input folder path -#' output_file = "/path/to/output/animation.gif", # Replace with your desired output GIF path -#' nodata_value = 0, -#' colormap = 'YlOrRd', -#' basemap_style = 'osm', -#' palette_style = 'equal', -#' alpha = 0.4, -#' legend = TRUE -#' ) - - -#' @seealso Currently none. - -#' @export - -# changelog and author contributions / copyrights -# Sam Bower (2023-02-02) -# original creation -# -# -############################################################################################### - -def.plot.flux.animate <- function( - input_folder, - output_file, - nodata_value = 0, - colormap = 'YlOrRd', - basemap_style = 'osm', - palette_style = 'equal', - alpha = 0.4, - legend = TRUE, - delay = 1 - ) { - - # Load necessary libraries - library(raster) - library(tmap) - library(tmaptools) - library(gifski) - - # Ensure tmap is in plot mode - tmap_mode("plot") - - # Create a temporary directory for storing map images - temp_dir <- tempfile() - dir.create(temp_dir) - - # List all raster files in the directory - raster_files <- list.files(input_folder, pattern = "\\.tif$", full.names = TRUE) - - # Initialize a list to store file paths of individual map images - temp_files <- vector("character", length(raster_files)) - - # Loop through each raster file to create a static map - for (i in seq_along(raster_files)) { - flux <- raster::raster(raster_files[i]) - flux[flux == nodata_value] <- NA # Apply nodata value - - osm_map <- tmaptools::read_osm(flux, type = basemap_style) - - map <- tm_shape(osm_map) + - tm_rgb() + - tm_shape(flux) + - tm_raster(style = palette_style, alpha = alpha, palette = colormap) + - tm_layout(legend.outside = legend) - - # Generate a temporary file path for the static map image - temp_file <- file.path(temp_dir, paste0("map_", i, ".png")) - temp_files[i] <- temp_file # Store the file path - - # Save the map as an image - tmap_save(map, filename = temp_file, width = 800, height = 600, units = "px") - } - - # Use gifski to create the GIF from the PNG files - gifski(png_files = temp_files, gif_file = output_file, width = 800, height = 600, delay = delay, progress = TRUE) - - # After creating the GIF, clean up the temporary directory and its contents - unlink(temp_dir, recursive = TRUE) - - cat("Animation created at:", output_file, "\n") - -} \ No newline at end of file diff --git a/pack/eddy4R.maps/R/def.plot.flux.interactive.R b/pack/eddy4R.maps/R/def.plot.flux.interactive.R deleted file mode 100644 index 713f0e91..00000000 --- a/pack/eddy4R.maps/R/def.plot.flux.interactive.R +++ /dev/null @@ -1,93 +0,0 @@ -############################################################################################## -#' @title Definition function: Plot fluxes on an interactive map. - -#' @author -#' Sam Bower \email{sbower@atmofacts.com} - -#' @description Function definition. This function creates an interactive flux map visualization that can be used in R viewer or in a web browser. -#' -#' @param input_path A geotiff file or folder of geotiff files to plot on the basemap. -#' @param nodata_value Nodata value from flux data -#' @param alpha Raster opacity -#' @param colormap palette_explorer() for palette options - -#' @return -#' -#' - -#' @references -#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 - -#' @keywords visualization, flux mapping -#' - - -#' @seealso Currently none. - -#' @export - -# changelog and author contributions / copyrights -# Sam Bower (2023-02-02) -# original creation -# -# -############################################################################################### - - -def.plot.flux.interactive <- function( - input_path, - nodata_value = 0, - alpha = 0.7, - colormap = 'YlOrRd', - save_path = NULL -) { - - library(leaflet) - library(raster) - - # Determine if input_path is a directory or a single file - if (dir.exists(input_path)) { - rasterFiles <- list.files(input_path, pattern = "\\.tif$", full.names = TRUE) - if (length(rasterFiles) == 0) { # Check if no TIFF files found - stop("No TIFF files found in the directory.") - } - } else if (file.exists(input_path) && grepl("\\.tif$", input_path)) { - rasterFiles <- list(input_path) # Ensure rasterFiles is a list - } else { - stop("Input path is neither a valid folder nor a TIFF file.") - } - - # Initialize Leaflet map - map <- leaflet() %>% - addProviderTiles("OpenStreetMap", group = "Street") %>% - addProviderTiles("Esri.WorldImagery", group = "Imagery") %>% - addProviderTiles("CartoDB.DarkMatter", group = "Dark") - - # Function to process and add each raster file to the map - processAndAddRaster <- function(filePath, map) { - flux <- raster(filePath) - # Replace nodata_value with NA - flux[flux == nodata_value] <- NA - # Add raster to the map using specified colormap and alpha - map <- map %>% addRasterImage(flux, group = base::basename(filePath), colors = colormap, opacity = alpha, layerId = base::basename(filePath)) - return(map) - } - - # Apply the function to each raster file - for(filePath in rasterFiles) { - map <- processAndAddRaster(filePath, map) - } - - # Dynamically create a list of groups for the layers control based on the raster files - rasterGroups <- base::basename(rasterFiles) - - # Add layers control to the map - map <- map %>% addLayersControl(overlayGroups = rasterGroups, baseGroups = c("Street", "Imagery", "Dark")) - - if (!is.null(save_path)) { - htmlwidgets::saveWidget(map, file = save_path, selfcontained = TRUE) - } else { - # Print the map to display it interactively - print(map) - } -} diff --git a/pack/eddy4R.maps/R/def.plot.flux.static.R b/pack/eddy4R.maps/R/def.plot.flux.static.R deleted file mode 100644 index df521675..00000000 --- a/pack/eddy4R.maps/R/def.plot.flux.static.R +++ /dev/null @@ -1,93 +0,0 @@ -############################################################################################## -#' @title Definition function: Plot fluxes on a static basemap - -#' @author -#' Sam Bower \email{sbower@atmofacts.com} - -#' @description Function definition. This function creates a flux map visualization on a user defined basemap. -#' -#' @param inputPath A spatial file or folder of spatial files to plot on the basemap. -#' @param outputPath A string for the folder location to save output maps. -#' @param nodata_value The nodata value specified from the flux data. -#' @param basemap_style Basemap styles in OpenStreetMap (more in openmap()) -#' @param alpha Raster opacity -#' @param colormap palette_explorer() for palette options -#' @param style quantile, equal, cont, cat -#' @param color_n color categories in palette - -#' @return -#' -#' - -#' @references -#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 - -#' @keywords visualization, flux mapping -#' - - -#' @seealso Currently none. - -#' @export - -# changelog and author contributions / copyrights -# Sam Bower (2023-02-01) -# original creation -# -# -############################################################################################### -def.plot.flux.static <- function( - inputPath, - outputPath, - nodata_value = 0, - basemap_style = 'bing', - alpha = 0.5, - colormap = 'YlOrRd', - style = 'equal', - color_n = 7) { #palette_explorer()) { - - # Change java system parameters to allow for headless operation (to disable under-the-hood java GUI which will not work in this Docker container). - options(java.parameters = "-Djava.awt.headless=true") - - # Function to load and process a single raster file and plot it with tmap - plotRaster <- function(rasterFile, outputPath) { - - #read in raster with raster library - rasterLayer <- raster::raster(rasterFile) - rasterLayer[rasterLayer == nodata_value] <- NA - - #grab baselayer from OpenStreetMap - osm_map <- tmaptools::read_osm(rasterLayer, type = basemap_style) - - #create tmap object with raster superimposed on basemap - map <- tm_shape(osm_map) + - tm_rgb() + - tm_shape(rasterLayer) + - tm_raster(style = style, alpha = alpha, palette = get_brewer_pal(palette = colormap, n = color_n, plot = FALSE)) + - tm_layout(legend.outside = FALSE) - - #save tmap object - tmap_save(map, file = outputPath) - } - - # Check if inputPath is a directory and make a list of the files. - if (dir.exists(inputPath)) { - rasterFiles <- list.files(inputPath, pattern = "\\.tif$", full.names = TRUE) - if (length(rasterFiles) == 0) { - stop("No raster files found in the directory.") - } - #iteratively create and save the maps - for (rasterFile in rasterFiles) { - fileName <- basename(rasterFile) - fileOutputPath <- paste0(outputPath, "/", sub("\\.tif$", ".png", fileName)) - plotRaster(rasterFile, fileOutputPath) - } - #Save a single file if the input is a single tiff - } else if (file.exists(inputPath) && grepl("\\.tif$", inputPath)) { - # InputPath is a single file - fileOutputPath <- paste0(outputPath, "/", tools::file_path_sans_ext(base::basename(inputPath)), ".png") - plotRaster(inputPath, fileOutputPath) - } else { - stop("Input path is neither a valid raster file nor a directory containing raster files.") - } -} From f35b0b067b5617fa26496458753fad373ebf5e7b Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 9 Feb 2024 15:49:07 +0000 Subject: [PATCH 40/66] Add def.spatialize.flux.foot --- pack/eddy4R.maps/R/def.spatialize.flux.foot.R | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 pack/eddy4R.maps/R/def.spatialize.flux.foot.R diff --git a/pack/eddy4R.maps/R/def.spatialize.flux.foot.R b/pack/eddy4R.maps/R/def.spatialize.flux.foot.R new file mode 100644 index 00000000..4947f18c --- /dev/null +++ b/pack/eddy4R.maps/R/def.spatialize.flux.foot.R @@ -0,0 +1,112 @@ +############################################################################################## +#' Spatialize Flux Footprint Data into Raster Format +#' +#' @description This function processes flux footprint data, spatializing it into a raster format +#' suitable for GIS analysis. It supports reading flux data from various formats, setting the +#' spatial extent, and defining the coordinate reference system (CRS). The output can optionally +#' be written to a file in GeoTiff format. +#' +#' @param flux_input The input flux data, which can be a path to an ASCII grid or CSV file, a matrix, +#' or a data frame. +#' @param lower_left_X The X coordinate of the lower left corner of the output raster. +#' @param lower_left_Y The Y coordinate of the lower left corner of the output raster. +#' @param nodata_value The value to be considered as 'no data' in the input flux data. Defaults to 0. +#' @param cell_size The size of each cell in the output raster, assumed to be square. Defaults to 10. +#' @param crs The coordinate reference system for the output raster, specified in PROJ.4 format. +#' Defaults to "+proj=utm +zone=13 +datum=WGS84 +units=m +no_defs". +#' @param write Logical, whether to write the output raster to a file. Defaults to FALSE. +#' @param output_filename The filename (including path) for the output file if write is TRUE. +#' If NULL and write is TRUE, an error is thrown. +#' @param file_format The format of the output file, defaults to 'GTiff'. +#' +#' @return A raster object representing the spatialized flux data. +#' +#' @examples +#' # Example usage: +#' raster <- def.spatialize.flux.foot(flux_input = "path/to/data.csv", +#' lower_left_X = 100000, +#' lower_left_Y = 400000, +#' cell_size = 10, +#' crs = "+proj=utm +zone=13 +datum=WGS84 +units=m +no_defs", +#' write = TRUE, +#' output_filename = "output.tif", +#' file_format = 'GTiff') +#' +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +#' +#' @author Sam Bower \email{sbower@atmofacts.com} +#' @keywords spatial, GIS, raster, flux +#' +#' @seealso \code{\link[raster]{writeRaster}}, \code{\link[raster]{raster}}, \code{\link[sp]{CRS}} +#' +#' @export +#' +#' @changelog +#' - Sam Bower (2023-02-02): Original creation. +############################################################################################### + +# Define the function with an additional CRS argument +def.spatialize.flux.foot <- function( + flux_input, + lower_left_X, + lower_left_Y, + nodata_value = 0, + cell_size = 10, + crs = "+proj=utm +zone=13 +datum=WGS84 +units=m +no_defs", + write = FALSE, + output_filename = NULL, + file_format = 'GTiff' +) { + + # Check if input is a path to a file, a data frame, or a matrix + if (is.character(flux_input)) { + # Assuming flux_input is a filepath to either a .asc or .csv file + file_extension <- tools::file_ext(flux_input) + if (file_extension == "asc") { + flux_matrix <- as.matrix(read.table(flux_input, header = FALSE)) + } else if (file_extension == "csv") { + flux_matrix <- as.matrix(read.csv(flux_input, header = TRUE)) + } else { + stop("Unsupported file format. Please provide a .asc or .csv file.") + } + } else if (is.data.frame(flux_input)) { + flux_matrix <- as.matrix(flux_input) + } else if (is.matrix(flux_input)) { + flux_matrix <- flux_input + } else { + stop("The data input must be a file path, matrix, or a data frame.") + } + + #initialize raster object from flux data input + flux_raster <- raster::raster(flux_matrix) + flux_raster[flux_raster==nodata_value] <- NA + + # Calculate and set the extent based on lower left corner and cell size + cellSizeX <- cell_size + cellSizeY <- cell_size + ncols <- ncol(flux_matrix) + nrows <- nrow(flux_matrix) + extentXmin <- lower_left_X + extentXmax <- lower_left_X + (ncols * cellSizeX) + extentYmin <- lower_left_Y + extentYmax <- lower_left_Y + (nrows * cellSizeY) + raster::extent(flux_raster) <- c(extentXmin, extentXmax, extentYmin, extentYmax) + + + # Set the CRS of the raster object + raster::crs(flux_raster) <- sp::CRS(crs) + + #Write raster to file if write is true. The default file format is a GeoTiff + if (write == TRUE){ + writeRaster( + flux_raster, + filename = output_filename, + format = file_format, + overwrite = TRUE + ) + } + + return(flux_raster) + +} \ No newline at end of file From 37be2f3b61f6741a69ea407e36f0e0d08fbd8ec5 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 9 Feb 2024 15:49:57 +0000 Subject: [PATCH 41/66] Add def.plot.flux.static --- pack/eddy4R.maps/R/def.plot.flux.static.R | 110 ++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 pack/eddy4R.maps/R/def.plot.flux.static.R diff --git a/pack/eddy4R.maps/R/def.plot.flux.static.R b/pack/eddy4R.maps/R/def.plot.flux.static.R new file mode 100644 index 00000000..a13d2b06 --- /dev/null +++ b/pack/eddy4R.maps/R/def.plot.flux.static.R @@ -0,0 +1,110 @@ +############################################################################################## +#' @title Definition function: Plot fluxes on a static basemap + +#' @author +#' Sam Bower \email{sbower@atmofacts.com} + +#' @description Function definition. This function creates a flux map visualization on a user defined basemap. +#' +#' @param inputPath A spatial file or folder of spatial files to plot on the basemap. +#' @param outputPath A string for the folder location to save output maps. +#' @param nodata_value The nodata value specified from the flux data. +#' @param basemap_style Basemap styles in OpenStreetMap (more in openmap()) +#' @param alpha Raster opacity +#' @param colormap palette_explorer() for palette options +#' @param style quantile, equal, cont, cat +#' @param color_n color categories in palette + +#' @return +#' +#' + +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @keywords visualization, flux mapping +#' + + +#' @seealso Currently none. + +#' @export + +# changelog and author contributions / copyrights +# Sam Bower (2023-02-01) +# original creation +# +# +############################################################################################### +def.plot.flux.static <- function( + inputPath, + outputPath, + basemap_style = 'bing', + alpha = 0.5, + nodata_value = 0, + colormap = 'YlOrRd', + style = 'equal', + color_n = 7, + use_basemap = FALSE, + background_color = "white", + units = "Units", + legend_bg_alpha = 1 +) { + + options(java.parameters = "-Djava.awt.headless=true") + library(tmap) + library(tmaptools) + library(OpenStreetMap) + library(RColorBrewer) + + plotRaster <- function(rasterFile, outputPath) { + + rasterFile[rasterFile == nodata_value] <- NA + map <- NULL + + if (use_basemap && basemap_style != 'none') { + osm_map <- tmaptools::read_osm(rasterFile, type = basemap_style) + map <- tm_shape(osm_map) + + tm_rgb() + + tm_shape(rasterFile) + + tm_raster(style = style, alpha = alpha, palette = brewer.pal(n = color_n, name = colormap), title = units) + + tm_layout( + legend.bg.alpha = legend_bg_alpha, + legend.bg.color = 'white', + legend.outside = FALSE + ) + } else { + map <- tm_shape(rasterFile) + + tm_raster(style = style, alpha = alpha, palette = brewer.pal(n = color_n, name = colormap), title = units) + + tm_layout( + bg.color = background_color, + legend.bg.alpha = legend_bg_alpha, + legend.bg.color = 'white', + frame = FALSE + ) + } + + tmap_save(map, file = outputPath) + } + + if (inherits(inputPath, "RasterLayer") || inherits(inputPath, "RasterStack") || inherits(inputPath, "RasterBrick")) { + rasterLayer <- inputPath + fileOutputPath <- paste0(outputPath) + plotRaster(rasterLayer, fileOutputPath) + } else if (dir.exists(inputPath)) { + rasterFiles <- list.files(inputPath, pattern = "\\.tif$", full.names = TRUE) + if (length(rasterFiles) == 0) { + stop("No raster files found in the directory.") + } + for (rasterFile in rasterFiles) { + fileName <- basename(rasterFile) + fileOutputPath <- paste0(outputPath, "/", sub("\\.tif$", ".png", fileName)) + plotRaster(rasterFile, fileOutputPath) + } + } else if (file.exists(inputPath) && grepl("\\.tif$", inputPath)) { + fileOutputPath <- paste0(outputPath, "/", tools::file_path_sans_ext(basename(inputPath)), ".png") + plotRaster(inputPath, fileOutputPath) + } else { + stop("Input path is neither a valid raster object, file, nor a directory containing raster files.") + } +} From 67235959c36f52d19ca8ee174c7bc79e9b739f0a Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 9 Feb 2024 15:50:55 +0000 Subject: [PATCH 42/66] Add def.plot.flux.interactive --- .../eddy4R.maps/R/def.plot.flux.interactive.R | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 pack/eddy4R.maps/R/def.plot.flux.interactive.R diff --git a/pack/eddy4R.maps/R/def.plot.flux.interactive.R b/pack/eddy4R.maps/R/def.plot.flux.interactive.R new file mode 100644 index 00000000..7aad5781 --- /dev/null +++ b/pack/eddy4R.maps/R/def.plot.flux.interactive.R @@ -0,0 +1,113 @@ +############################################################################################## +#' @title Definition function: Plot fluxes on an interactive map. + +#' @author +#' Sam Bower \email{sbower@atmofacts.com} + +#' @description Function definition. This function creates an interactive flux map visualization that can be used in R viewer or in a web browser. +#' +#' @param input_path A geotiff file or folder of geotiff files to plot on the basemap. +#' @param nodata_value Nodata value from flux data +#' @param alpha Raster opacity +#' @param colormap palette_explorer() for palette options + +#' @return +#' +#' + +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @keywords visualization, flux mapping +#' + + +#' @seealso Currently none. + +#' @export + +# changelog and author contributions / copyrights +# Sam Bower (2023-02-02) +# original creation +# +# +############################################################################################### + + +def.plot.flux.interactive <- function( + input_path, + nodata_value = 0, + alpha = 0.7, + colormap = 'YlOrRd', + save_path = NULL +) { + + library(leaflet) + library(raster) + library(RColorBrewer) + + # Initialize Leaflet map + map <- leaflet() %>% + addProviderTiles("OpenStreetMap", group = "Street") %>% + addProviderTiles("Esri.WorldImagery", group = "Imagery") %>% + addProviderTiles("CartoDB.DarkMatter", group = "Dark") + + # Function to process and add a raster object to the map + processAndAddRaster <- function(flux, map, layerName) { + # Replace nodata_value with NA + flux[flux == nodata_value] <- NA + + # Extract values from the raster while maintaining NA for nodata + rasterValues <- getValues(flux) # This will return a vector of all raster values + validValues <- rasterValues[!is.na(rasterValues)] # Filter out NA values + + if (length(validValues) == 0) { + validValues <- c(0) # Fallback to avoid issues with empty data + } + + # Define the color palette function + colorPal <- colorNumeric(palette = colormap, domain = range(validValues, na.rm = TRUE), na.color = "transparent") + + # Add raster to the map with the correct color mapping + map <- map %>% addRasterImage(flux, group = layerName, colors = colorPal, opacity = alpha, layerId = layerName) + return(map) + } + + # Check if input_path is a raster object + if (inherits(input_path, "RasterLayer") || inherits(input_path, "RasterStack") || inherits(input_path, "RasterBrick")) { + layerName <- "CustomRasterLayer" # Modify as needed or generate dynamically + map <- processAndAddRaster(input_path, map, layerName) + rasterGroups <- c(layerName) + } else { + # Determine if input_path is a directory or a single file + if (dir.exists(input_path)) { + rasterFiles <- list.files(input_path, pattern = "\\.tif$", full.names = TRUE) + if (length(rasterFiles) == 0) { # Check if no TIFF files found + stop("No TIFF files found in the directory.") + } + } else if (file.exists(input_path) && grepl("\\.tif$", input_path)) { + rasterFiles <- list(input_path) # Ensure rasterFiles is a list + } else { + stop("Input path is neither a valid raster object, folder, nor a TIFF file.") + } + + # Apply the function to each raster file + rasterGroups <- NULL + for(filePath in rasterFiles) { + layerName <- base::basename(filePath) + flux <- raster(filePath) + map <- processAndAddRaster(flux, map, layerName) + rasterGroups <- c(rasterGroups, layerName) + } + } + + # Add layers control to the map + map <- map %>% addLayersControl(overlayGroups = rasterGroups, baseGroups = c("Street", "Imagery", "Dark")) + + if (!is.null(save_path)) { + htmlwidgets::saveWidget(map, file = save_path, selfcontained = TRUE) + } else { + # Print the map to display it interactively + print(map) + } +} From 0563d36675c39916781f2e52ebcc856625198bc3 Mon Sep 17 00:00:00 2001 From: sambower-atmo Date: Fri, 9 Feb 2024 15:51:44 +0000 Subject: [PATCH 43/66] Add def.plot.flux.animate --- pack/eddy4R.maps/R/def.plot.flux.animate.R | 113 +++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 pack/eddy4R.maps/R/def.plot.flux.animate.R diff --git a/pack/eddy4R.maps/R/def.plot.flux.animate.R b/pack/eddy4R.maps/R/def.plot.flux.animate.R new file mode 100644 index 00000000..94a5f606 --- /dev/null +++ b/pack/eddy4R.maps/R/def.plot.flux.animate.R @@ -0,0 +1,113 @@ +############################################################################################## +#' @title Definition function: Plot fluxes on an animated gif. + +#' @author +#' Sam Bower \email{sbower@atmofacts.com} + +#' @description Function definition. This function creates an animated flux map visualization for temporal flux map data contained in a folder. +#' +#' @param input_folder A folder of geotiff files. +#' @param output_file The path of the output file (.gif) +#' @param nodata_value Nodata value from flux data +#' @param alpha Raster opacity +#' @param colormap palette_explorer() for palette options +#' @param palette_style quantile, equal, cont, cat +#' @param legend Boolean. If legend plots on the map +#' @param delay The delay time in the animation. +#' + +#' @return +#' +#' + +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @keywords visualization, flux mapping, animation +#' +#' @examples +#' +#' def.plot.flux.animation( +#' input_folder = "/path/to/input/folder", # Replace with your input folder path +#' output_file = "/path/to/output/animation.gif", # Replace with your desired output GIF path +#' nodata_value = 0, +#' colormap = 'YlOrRd', +#' basemap_style = 'osm', +#' palette_style = 'equal', +#' alpha = 0.4, +#' legend = TRUE +#' ) + + +#' @seealso Currently none. + +#' @export + +# changelog and author contributions / copyrights +# Sam Bower (2023-02-02) +# original creation +# +# +############################################################################################### + +def.plot.flux.animate <- function( + input_folder, + output_file, + nodata_value = 0, + colormap = 'YlOrRd', + basemap_style = 'osm', + palette_style = 'equal', + alpha = 0.4, + legend = TRUE, + delay = 1 +) { + + # Load necessary libraries + library(raster) + library(tmap) + library(tmaptools) + library(gifski) + + # Ensure tmap is in plot mode + tmap_mode("plot") + + # Create a temporary directory for storing map images + temp_dir <- tempfile() + dir.create(temp_dir) + + # List all raster files in the directory + raster_files <- list.files(input_folder, pattern = "\\.tif$", full.names = TRUE) + + # Initialize a list to store file paths of individual map images + temp_files <- vector("character", length(raster_files)) + + # Loop through each raster file to create a static map + for (i in seq_along(raster_files)) { + flux <- raster::raster(raster_files[i]) + flux[flux == nodata_value] <- NA # Apply nodata value + + osm_map <- tmaptools::read_osm(flux, type = basemap_style) + + map <- tm_shape(osm_map) + + tm_rgb() + + tm_shape(flux) + + tm_raster(style = palette_style, alpha = alpha, palette = colormap) + + tm_layout(legend.outside = legend) + + # Generate a temporary file path for the static map image + temp_file <- file.path(temp_dir, paste0("map_", i, ".png")) + temp_files[i] <- temp_file # Store the file path + + # Save the map as an image + tmap_save(map, filename = temp_file, width = 800, height = 600, units = "px") + } + + # Use gifski to create the GIF from the PNG files + gifski(png_files = temp_files, gif_file = output_file, width = 800, height = 600, delay = delay, progress = TRUE) + + # After creating the GIF, clean up the temporary directory and its contents + unlink(temp_dir, recursive = TRUE) + + cat("Animation created at:", output_file, "\n") + +} \ No newline at end of file From 29f327fb98ca0a86387f16f5e771b2b72e109c5a Mon Sep 17 00:00:00 2001 From: stefanmet Date: Wed, 14 Feb 2024 19:18:26 +0000 Subject: [PATCH 44/66] re-package eddy4R.maps --- pack/eddy4R.maps/NAMESPACE | 1 + pack/eddy4R.maps/man/def.plot.flux.static.Rd | 12 ++- .../man/def.spatialize.flux.foot.Rd | 74 +++++++++++++++++++ 3 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 pack/eddy4R.maps/man/def.spatialize.flux.foot.Rd diff --git a/pack/eddy4R.maps/NAMESPACE b/pack/eddy4R.maps/NAMESPACE index d4ed0cbd..268cf719 100644 --- a/pack/eddy4R.maps/NAMESPACE +++ b/pack/eddy4R.maps/NAMESPACE @@ -4,4 +4,5 @@ export(def.algn.foot.rng) export(def.plot.flux.animate) export(def.plot.flux.interactive) export(def.plot.flux.static) +export(def.spatialize.flux.foot) importFrom(matlab,padarray) diff --git a/pack/eddy4R.maps/man/def.plot.flux.static.Rd b/pack/eddy4R.maps/man/def.plot.flux.static.Rd index cf460c4f..51028e71 100644 --- a/pack/eddy4R.maps/man/def.plot.flux.static.Rd +++ b/pack/eddy4R.maps/man/def.plot.flux.static.Rd @@ -7,12 +7,16 @@ def.plot.flux.static( inputPath, outputPath, - nodata_value = 0, basemap_style = "bing", alpha = 0.5, + nodata_value = 0, colormap = "YlOrRd", style = "equal", - color_n = 7 + color_n = 7, + use_basemap = FALSE, + background_color = "white", + units = "Units", + legend_bg_alpha = 1 ) } \arguments{ @@ -20,12 +24,12 @@ def.plot.flux.static( \item{outputPath}{A string for the folder location to save output maps.} -\item{nodata_value}{The nodata value specified from the flux data.} - \item{basemap_style}{Basemap styles in OpenStreetMap (more in openmap())} \item{alpha}{Raster opacity} +\item{nodata_value}{The nodata value specified from the flux data.} + \item{colormap}{palette_explorer() for palette options} \item{style}{quantile, equal, cont, cat} diff --git a/pack/eddy4R.maps/man/def.spatialize.flux.foot.Rd b/pack/eddy4R.maps/man/def.spatialize.flux.foot.Rd new file mode 100644 index 00000000..7fb60938 --- /dev/null +++ b/pack/eddy4R.maps/man/def.spatialize.flux.foot.Rd @@ -0,0 +1,74 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/def.spatialize.flux.foot.R +\name{def.spatialize.flux.foot} +\alias{def.spatialize.flux.foot} +\title{Spatialize Flux Footprint Data into Raster Format} +\usage{ +def.spatialize.flux.foot( + flux_input, + lower_left_X, + lower_left_Y, + nodata_value = 0, + cell_size = 10, + crs = "+proj=utm +zone=13 +datum=WGS84 +units=m +no_defs", + write = FALSE, + output_filename = NULL, + file_format = "GTiff" +) +} +\arguments{ +\item{flux_input}{The input flux data, which can be a path to an ASCII grid or CSV file, a matrix, +or a data frame.} + +\item{lower_left_X}{The X coordinate of the lower left corner of the output raster.} + +\item{lower_left_Y}{The Y coordinate of the lower left corner of the output raster.} + +\item{nodata_value}{The value to be considered as 'no data' in the input flux data. Defaults to 0.} + +\item{cell_size}{The size of each cell in the output raster, assumed to be square. Defaults to 10.} + +\item{crs}{The coordinate reference system for the output raster, specified in PROJ.4 format. +Defaults to "+proj=utm +zone=13 +datum=WGS84 +units=m +no_defs".} + +\item{write}{Logical, whether to write the output raster to a file. Defaults to FALSE.} + +\item{output_filename}{The filename (including path) for the output file if write is TRUE. +If NULL and write is TRUE, an error is thrown.} + +\item{file_format}{The format of the output file, defaults to 'GTiff'.} +} +\value{ +A raster object representing the spatialized flux data. +} +\description{ +This function processes flux footprint data, spatializing it into a raster format +suitable for GIS analysis. It supports reading flux data from various formats, setting the +spatial extent, and defining the coordinate reference system (CRS). The output can optionally +be written to a file in GeoTiff format. +} +\examples{ +# Example usage: +raster <- def.spatialize.flux.foot(flux_input = "path/to/data.csv", + lower_left_X = 100000, + lower_left_Y = 400000, + cell_size = 10, + crs = "+proj=utm +zone=13 +datum=WGS84 +units=m +no_defs", + write = TRUE, + output_filename = "output.tif", + file_format = 'GTiff') + +} +\references{ +License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +} +\seealso{ +\code{\link[raster]{writeRaster}}, \code{\link[raster]{raster}}, \code{\link[sp]{CRS}} +} +\author{ +Sam Bower \email{sbower@atmofacts.com} +} +\keyword{GIS,} +\keyword{flux} +\keyword{raster,} +\keyword{spatial,} From 21f6bdcd7eff07127c3a2bb1756d6205ffc9bfd6 Mon Sep 17 00:00:00 2001 From: ddurden Date: Wed, 28 Feb 2024 14:19:56 +0000 Subject: [PATCH 45/66] -spatial data grabbing functions from STAC catalogues --- pack/eddy4R.maps/R/def.bbox.R | 71 +++++++++++++++++++++++++ pack/eddy4R.maps/R/def.spat.data.cube.R | 60 +++++++++++++++++++++ pack/eddy4R.maps/R/wrap.spat.data.R | 70 ++++++++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 pack/eddy4R.maps/R/def.bbox.R create mode 100644 pack/eddy4R.maps/R/def.spat.data.cube.R create mode 100644 pack/eddy4R.maps/R/wrap.spat.data.R diff --git a/pack/eddy4R.maps/R/def.bbox.R b/pack/eddy4R.maps/R/def.bbox.R new file mode 100644 index 00000000..b3d357f3 --- /dev/null +++ b/pack/eddy4R.maps/R/def.bbox.R @@ -0,0 +1,71 @@ +############################################################################################## +#' @title definition function to create bounding box + +#' @author David Durden + + +#' @description Function definition. Read in shapefile or create a bounding box +#' by adding an extent to Lat/Lon for grabbing STAC data with a padding option. + +#' @param dir directory where shapefiles are located +#' @param crs coordinate reference system (Defaults to "EPSG:4326") +#' @param pad_box logical to determine if padding should be applied to bounding box (defaults to FALSE) +#' @param pad_degree decimal degree of latitude and longitude to pad the bounding box (defaults to 0.1) + +#' @return list containing shapefile data and bbox + +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @keywords natural constants + +#' @examples Currently none + +#' @seealso Currently none + +#' @export +#' +# changelog and author contributions +# David Durden (2024-01-23) +# original creation +############################################################################################## +def.bbox <- function( + DirInp = NULL, + crs = "EPSG:4326", + pad_box = FALSE, + pad_degree = 0.5, + lat = NULL, + lon = NULL + ){ + + #Initialize list + out <- list() + + ## read shape file + if(!is.null(DirInp)) { + out$shp <- sf::read_sf(dsn = DirInp, "mask") + #generate bounding box + out$bbox <- out$shp |> sf::st_transform(crs=crs) |> sf::st_bbox() + + #pad bounding box + if(pad_box == TRUE){ + out$bbox[1] <- out$bbox$xmin - pad_degree #Padding in degrees + out$bbox[2] <- out$bbox$ymin - pad_degree + out$bbox[3] <- out$bbox$xmax + pad_degree + out$bbox[4] <- out$bbox$ymax + pad_degree + } + + #Create a mask layer + out$maskLayer <- sf::st_geometry(out$shp) |> sf::st_transform(crs=crs) + + } else + { + out$bbox <- as(raster::extent(lon - pad_degree, lon + pad_degree, lat - pad_degree, lat + pad_degree), "SpatialPolygons") + proj4string(out$bbox) <- crs + } + + + + #Return output list with shp and bbox + return(out) +} \ No newline at end of file diff --git a/pack/eddy4R.maps/R/def.spat.data.cube.R b/pack/eddy4R.maps/R/def.spat.data.cube.R new file mode 100644 index 00000000..04f9e511 --- /dev/null +++ b/pack/eddy4R.maps/R/def.spat.data.cube.R @@ -0,0 +1,60 @@ +#' Download data from Microsoft planetary comuputer +#' +#' @param start_date start date as character format yyyy-mm-dd +#' @param end_date end date as character format yyyy-mm-dd +#' @param box numberic vector in the format of (xmin, ymin, xmax, ymax) +#' @param collection name of planetary collection +#' @param asset_name mame of asset +#' @param srs target spatial reference system as a string; can be a proj4 definition, WKT, or in the form "EPSG:XXXX" +#' @param dx size of pixels in x-direction (longitude / easting) +#' @param dy size of pixels in y-direction (latitude / northing) +#' @param dt size of pixels in time-direction, expressed as ISO8601 period string (only 1 number and unit is allowed) such as "P16D" +#' @param aggregation aggregation method as string, defining how to deal with pixels containing data from multiple images, can be "min", "max", "mean", "median", or "first" +#' @param resampling resampling method used in gdalwarp when images are read, can be "near", "bilinear", "bicubic" or others as supported by gdalwarp (see https://gdal.org/programs/gdalwarp.html) +#' @return A data cube proxy object +#' @examples +#' ingest_planetary_data(start_date = "2022-01-01", end_date = "2023-07-01", box = c("xmin" = -123, "ymin" = 39, "xmax" = -122, "ymax" = 40)) +#' @export +#' +def.spat.data.cube <- function(start_date, + end_date, + box, + collection = "modis-15A2H-061", + asset_name = "Lai_500m", + srs = "EPSG:4326", + dx = 0.1, + dy = 0.1, + dt = "P30D", + aggregation = "mean", + resampling = "near"){ + + # check box + assertthat::are_equal(length(box), 4) + + # get STACItemCollection + matches <- + rstac::stac("https://planetarycomputer.microsoft.com/api/stac/v1") |> + stac_search(collections = collection, + datetime = paste(start_date, end_date, sep = "/"), + bbox = c(box)) |> + get_request() |> + items_fetch() |> + items_sign(sign_fn = sign_planetary_computer()) + + # get image collection object + cube <- gdalcubes::stac_image_collection(matches$features, + asset_names = asset_name, + duration = "start") + + # set dimensions of the cube + v <- gdalcubes::cube_view(srs = srs, #lat/lon + extent = list(t0 = as.character(start_date), t1 = as.character(end_date), + left = box[1], right = box[3], + top = box[4], bottom = box[2]), + dx = dx, dy = dy, dt= dt, + aggregation = aggregation, resampling = resampling) + + # create proxy data cube + proxy_cube <- gdalcubes::raster_cube(cube, v) + return(proxy_cube) +} diff --git a/pack/eddy4R.maps/R/wrap.spat.data.R b/pack/eddy4R.maps/R/wrap.spat.data.R new file mode 100644 index 00000000..38d0ebe7 --- /dev/null +++ b/pack/eddy4R.maps/R/wrap.spat.data.R @@ -0,0 +1,70 @@ +#' Generate spatial targets +#' +#' @param dir A directory of .tif files for scoring +#' @param site_id The side ID of the area being forecasted. Currently "august_complex" is the only option. +#' +#' @return message from minio submission +#' @export +#' +#' @examples spat4cast_submit(dir = "targets", site_id = "august_complex") + + + +wrap.spat.data.grab <- function( + dir = "targets", + dateBgn = "2019-05-01", + dateEnd = "2019-11-30", + site = c("cheesehead")[1], + dt = "P1M" + dx = 0.1, + dy = 0.1 +) +{ + + print(paste0("Grabbing spatial data at ", Sys.time(), "for", site)) + + library(here) + library(sf) + library(lubridate) + library(gdalcubes) + library(rstac) + library(stars) + + + + #Source functions + for (f in list.files(here::here("R"), full.names = TRUE)) source (f) + + #Create fire bounding box + bbox <- def.bbox(lat = 45.94625, lon = -90.27276, pad_box = TRUE) + + #Target date + dateBgn <- lubridate::floor_date(as.Date(dateBgn), "month") #first day of the month + dateEnd <- lubridate::ceiling_date(as.Date(dateEnd), "month") + + # Ingest data ------------------------------------------------------------ + gdalcubes::gdalcubes_options(parallel=TRUE) + + # use ingest_planetary_data function to extract raster cube for fire bounding box between Jan 1 2002 and July 1 2023. + raster_cube <- ingest_planetary_data(start_date = dateBgn, + end_date = dateEnd, + box = bbox$bbox, + srs = "EPSG:4326", + dx = dx, + dy = dy, + dt = "P1M", + collection = "modis-15A2H-061", + asset_name = "Lai_500m") + + + + # create target file + target <- create_target_file(cuberast = raster_cube, + site_id = site_id, + date = as.character(date), + dir = tempdir(), + bucket = "efi/spat4cast-targets", + mask = fire_box$maskLayer) + + +} #End of function \ No newline at end of file From cbbffe72c90d6ed9aad8af9bdc179830e637dc47 Mon Sep 17 00:00:00 2001 From: stefanmet Date: Fri, 1 Mar 2024 18:55:15 +0000 Subject: [PATCH 46/66] workaround for native pipe operator to support both R < 4.1 and R >= 4.1 --- pack/eddy4R.maps/R/def.bbox.R | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/pack/eddy4R.maps/R/def.bbox.R b/pack/eddy4R.maps/R/def.bbox.R index b3d357f3..76245446 100644 --- a/pack/eddy4R.maps/R/def.bbox.R +++ b/pack/eddy4R.maps/R/def.bbox.R @@ -1,8 +1,9 @@ ############################################################################################## #' @title definition function to create bounding box -#' @author David Durden - +#' @author +#' David Durden +#' Stefan Metzger \email{smetzger@atmofacts.com} #' @description Function definition. Read in shapefile or create a bounding box #' by adding an extent to Lat/Lon for grabbing STAC data with a padding option. @@ -17,7 +18,7 @@ #' @references #' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 -#' @keywords natural constants +#' @keywords Currently none #' @examples Currently none @@ -28,6 +29,8 @@ # changelog and author contributions # David Durden (2024-01-23) # original creation +# Stefan Metzger (2024-03-01) +# workaround for native pipe operator to support both R < 4.1 and R >= 4.1 ############################################################################################## def.bbox <- function( DirInp = NULL, @@ -45,7 +48,12 @@ def.bbox <- function( if(!is.null(DirInp)) { out$shp <- sf::read_sf(dsn = DirInp, "mask") #generate bounding box - out$bbox <- out$shp |> sf::st_transform(crs=crs) |> sf::st_bbox() + # native pipe operator |>; supported from R >= 4.1 + # out$bbox <- out$shp |> sf::st_transform(crs=crs) |> sf::st_bbox() + # workaround: sequentially chain functions to support both R < 4.1 and R >= 4.1 + tmp01 <- sf::st_transform(out$shp, crs=crs) + out$bbox <- sf::st_bbox(tmp01) + base::rm(tmp01) #pad bounding box if(pad_box == TRUE){ @@ -56,7 +64,19 @@ def.bbox <- function( } #Create a mask layer - out$maskLayer <- sf::st_geometry(out$shp) |> sf::st_transform(crs=crs) + # with pipe operator (R >= 4.1 only) + # out$maskLayer <- sf::st_geometry(out$shp) |> sf::st_transform(crs=crs) + # w/o pipe operator + + # First, extract the geometry from out$shp + tmp01 <- sf::st_geometry(out$shp) + + # Then, transform the geometry with the specified CRS + out$maskLayer <- sf::st_transform(tmp01, crs=crs) + + # clean up + base::rm(tmp01) + } else { From ebb0152b5246815de3a9a1dff61c3ff50d1831fa Mon Sep 17 00:00:00 2001 From: stefanmet Date: Fri, 1 Mar 2024 18:56:19 +0000 Subject: [PATCH 47/66] complete Roxygen header; workaround for native pipe operator to support both R < 4.1 and R >= 4.1 --- pack/eddy4R.maps/R/def.spat.data.cube.R | 67 +++++++++++++++++++++---- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/pack/eddy4R.maps/R/def.spat.data.cube.R b/pack/eddy4R.maps/R/def.spat.data.cube.R index 04f9e511..222cdd61 100644 --- a/pack/eddy4R.maps/R/def.spat.data.cube.R +++ b/pack/eddy4R.maps/R/def.spat.data.cube.R @@ -1,5 +1,12 @@ -#' Download data from Microsoft planetary comuputer -#' +############################################################################################## +#' @title definition function to download data from Microsoft planetary computer + +#' @author +#' David Durden +#' Stefan Metzger \email{smetzger@atmofacts.com} + +#' @description Function definition. Download data from Microsoft planetary computer + #' @param start_date start date as character format yyyy-mm-dd #' @param end_date end date as character format yyyy-mm-dd #' @param box numberic vector in the format of (xmin, ymin, xmax, ymax) @@ -11,11 +18,28 @@ #' @param dt size of pixels in time-direction, expressed as ISO8601 period string (only 1 number and unit is allowed) such as "P16D" #' @param aggregation aggregation method as string, defining how to deal with pixels containing data from multiple images, can be "min", "max", "mean", "median", or "first" #' @param resampling resampling method used in gdalwarp when images are read, can be "near", "bilinear", "bicubic" or others as supported by gdalwarp (see https://gdal.org/programs/gdalwarp.html) + #' @return A data cube proxy object + +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @keywords Currently none + #' @examples #' ingest_planetary_data(start_date = "2022-01-01", end_date = "2023-07-01", box = c("xmin" = -123, "ymin" = 39, "xmax" = -122, "ymax" = 40)) + +#' @seealso Currently none + #' @export #' +# changelog and author contributions +# David Durden (2024-01-23) +# original creation +# Stefan Metzger (2024-03-01) +# complete Roxygen header +# workaround for native pipe operator to support both R < 4.1 and R >= 4.1 +############################################################################################## def.spat.data.cube <- function(start_date, end_date, box, @@ -32,14 +56,37 @@ def.spat.data.cube <- function(start_date, assertthat::are_equal(length(box), 4) # get STACItemCollection - matches <- - rstac::stac("https://planetarycomputer.microsoft.com/api/stac/v1") |> - stac_search(collections = collection, - datetime = paste(start_date, end_date, sep = "/"), - bbox = c(box)) |> - get_request() |> - items_fetch() |> - items_sign(sign_fn = sign_planetary_computer()) + + # Original pipe-based code (R > 4.1 only) + # matches <- + # rstac::stac("https://planetarycomputer.microsoft.com/api/stac/v1") |> + # stac_search(collections = collection, + # datetime = paste(start_date, end_date, sep = "/"), + # bbox = c(box)) |> + # get_request() |> + # items_fetch() |> + # items_sign(sign_fn = sign_planetary_computer()) + + # workaround without pipe statements and using temporary objects: + # Step 1: Initialize STAC client + tmp01 <- rstac::stac("https://planetarycomputer.microsoft.com/api/stac/v1") + + # Step 2: Search STAC + tmp02 <- stac_search(tmp01, collections = collection, + datetime = paste(start_date, end_date, sep = "/"), + bbox = c(box)) + + # Step 3: Send request + tmp03 <- get_request(tmp02) + + # Step 4: Fetch items + tmp04 <- items_fetch(tmp03) + + # Step 5: Sign items + matches <- items_sign(tmp04, sign_fn = sign_planetary_computer()) + + # Cleanup of temporary objects at the end: + base::rm(tmp01, tmp02, tmp03, tmp04) # get image collection object cube <- gdalcubes::stac_image_collection(matches$features, From 1e4ba41f43703212cc4ea144a2231b8ad8e8f7b0 Mon Sep 17 00:00:00 2001 From: stefanmet Date: Fri, 1 Mar 2024 18:57:25 +0000 Subject: [PATCH 48/66] complete Roxygen header, fix attributes list in function call --- pack/eddy4R.maps/R/wrap.spat.data.R | 34 +++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/pack/eddy4R.maps/R/wrap.spat.data.R b/pack/eddy4R.maps/R/wrap.spat.data.R index 38d0ebe7..565a475d 100644 --- a/pack/eddy4R.maps/R/wrap.spat.data.R +++ b/pack/eddy4R.maps/R/wrap.spat.data.R @@ -1,21 +1,41 @@ -#' Generate spatial targets -#' +############################################################################################## +#' @title wrapper function to generate spatial targets + +#' @author +#' David Durden +#' Stefan Metzger \email{smetzger@atmofacts.com} + +#' @description Wrapper definition. Generate spatial targets + #' @param dir A directory of .tif files for scoring #' @param site_id The side ID of the area being forecasted. Currently "august_complex" is the only option. -#' + #' @return message from minio submission -#' @export -#' -#' @examples spat4cast_submit(dir = "targets", site_id = "august_complex") +#' @references +#' License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 + +#' @keywords Currently none +#' @examples spat4cast_submit(dir = "targets", site_id = "august_complex") +#' @seealso Currently none + +#' @export +#' +# changelog and author contributions +# David Durden (2024-01-23) +# original creation +# Stefan Metzger (2024-03-01) +# complete Roxygen header +# fix attributes list in function call +############################################################################################## wrap.spat.data.grab <- function( dir = "targets", dateBgn = "2019-05-01", dateEnd = "2019-11-30", site = c("cheesehead")[1], - dt = "P1M" + dt = "P1M", dx = 0.1, dy = 0.1 ) From ff9e89011a2d8db5a7e0968aaedeca6e73ee3467 Mon Sep 17 00:00:00 2001 From: stefanmet Date: Fri, 1 Mar 2024 19:01:58 +0000 Subject: [PATCH 49/66] repackage eddy4R.maps --- pack/eddy4R.maps/DESCRIPTION | 18 ++++++ pack/eddy4R.maps/NAMESPACE | 3 + pack/eddy4R.maps/man/def.bbox.Rd | 46 +++++++++++++++ pack/eddy4R.maps/man/def.spat.data.cube.Rd | 64 +++++++++++++++++++++ pack/eddy4R.maps/man/wrap.spat.data.grab.Rd | 42 ++++++++++++++ 5 files changed, 173 insertions(+) create mode 100644 pack/eddy4R.maps/man/def.bbox.Rd create mode 100644 pack/eddy4R.maps/man/def.spat.data.cube.Rd create mode 100644 pack/eddy4R.maps/man/wrap.spat.data.grab.Rd diff --git a/pack/eddy4R.maps/DESCRIPTION b/pack/eddy4R.maps/DESCRIPTION index 09f28942..540a61f4 100644 --- a/pack/eddy4R.maps/DESCRIPTION +++ b/pack/eddy4R.maps/DESCRIPTION @@ -18,6 +18,24 @@ Authors@R: c( person(given = "Stefan", Description: Basic commonalities and tools for working with Flux Maps™ in R. Depends: R (>= 4.0.5) +Imports: + gdalcubes (>= 0.3.1), + gifski (>= 1.4.3-1), + here (>= 1.0.1), + htmlwidgets (>= 1.5.3), + leaflet (>= 2.0.4.1), + lubridate (>= 1.9.2), + matlab (>= 1.0.2), + raster (>= 3.4-10), + RColorBrewer (>= 1.1-2), + rstac (>= 0.9.1), + sf (>= 0.9-8), + sp (>= 1.4-5), + stars (>= 0.5-2), + tmap (>= 3.3-1), + tmaptools (>= 3.1-1), + tools (>= 4.0.5) +Comments: Dependency package(s) 'OpenStreetMap (>= 0.3.4)' not available. License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Encoding: UTF-8 LazyData: true diff --git a/pack/eddy4R.maps/NAMESPACE b/pack/eddy4R.maps/NAMESPACE index 268cf719..f1e14836 100644 --- a/pack/eddy4R.maps/NAMESPACE +++ b/pack/eddy4R.maps/NAMESPACE @@ -1,8 +1,11 @@ # Generated by roxygen2: do not edit by hand export(def.algn.foot.rng) +export(def.bbox) export(def.plot.flux.animate) export(def.plot.flux.interactive) export(def.plot.flux.static) +export(def.spat.data.cube) export(def.spatialize.flux.foot) +export(wrap.spat.data.grab) importFrom(matlab,padarray) diff --git a/pack/eddy4R.maps/man/def.bbox.Rd b/pack/eddy4R.maps/man/def.bbox.Rd new file mode 100644 index 00000000..46ab09ac --- /dev/null +++ b/pack/eddy4R.maps/man/def.bbox.Rd @@ -0,0 +1,46 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/def.bbox.R +\name{def.bbox} +\alias{def.bbox} +\title{definition function to create bounding box} +\usage{ +def.bbox( + DirInp = NULL, + crs = "EPSG:4326", + pad_box = FALSE, + pad_degree = 0.5, + lat = NULL, + lon = NULL +) +} +\arguments{ +\item{crs}{coordinate reference system (Defaults to "EPSG:4326")} + +\item{pad_box}{logical to determine if padding should be applied to bounding box (defaults to FALSE)} + +\item{pad_degree}{decimal degree of latitude and longitude to pad the bounding box (defaults to 0.1)} + +\item{dir}{directory where shapefiles are located} +} +\value{ +list containing shapefile data and bbox +} +\description{ +Function definition. Read in shapefile or create a bounding box +by adding an extent to Lat/Lon for grabbing STAC data with a padding option. +} +\examples{ +Currently none +} +\references{ +License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +} +\seealso{ +Currently none +} +\author{ +David Durden +Stefan Metzger \email{smetzger@atmofacts.com} +} +\keyword{Currently} +\keyword{none} diff --git a/pack/eddy4R.maps/man/def.spat.data.cube.Rd b/pack/eddy4R.maps/man/def.spat.data.cube.Rd new file mode 100644 index 00000000..4b072ed8 --- /dev/null +++ b/pack/eddy4R.maps/man/def.spat.data.cube.Rd @@ -0,0 +1,64 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/def.spat.data.cube.R +\name{def.spat.data.cube} +\alias{def.spat.data.cube} +\title{definition function to download data from Microsoft planetary computer} +\usage{ +def.spat.data.cube( + start_date, + end_date, + box, + collection = "modis-15A2H-061", + asset_name = "Lai_500m", + srs = "EPSG:4326", + dx = 0.1, + dy = 0.1, + dt = "P30D", + aggregation = "mean", + resampling = "near" +) +} +\arguments{ +\item{start_date}{start date as character format yyyy-mm-dd} + +\item{end_date}{end date as character format yyyy-mm-dd} + +\item{box}{numberic vector in the format of (xmin, ymin, xmax, ymax)} + +\item{collection}{name of planetary collection} + +\item{asset_name}{mame of asset} + +\item{srs}{target spatial reference system as a string; can be a proj4 definition, WKT, or in the form "EPSG:XXXX"} + +\item{dx}{size of pixels in x-direction (longitude / easting)} + +\item{dy}{size of pixels in y-direction (latitude / northing)} + +\item{dt}{size of pixels in time-direction, expressed as ISO8601 period string (only 1 number and unit is allowed) such as "P16D"} + +\item{aggregation}{aggregation method as string, defining how to deal with pixels containing data from multiple images, can be "min", "max", "mean", "median", or "first"} + +\item{resampling}{resampling method used in gdalwarp when images are read, can be "near", "bilinear", "bicubic" or others as supported by gdalwarp (see https://gdal.org/programs/gdalwarp.html)} +} +\value{ +A data cube proxy object +} +\description{ +Function definition. Download data from Microsoft planetary computer +} +\examples{ +ingest_planetary_data(start_date = "2022-01-01", end_date = "2023-07-01", box = c("xmin" = -123, "ymin" = 39, "xmax" = -122, "ymax" = 40)) +} +\references{ +License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +} +\seealso{ +Currently none +} +\author{ +David Durden +Stefan Metzger \email{smetzger@atmofacts.com} +} +\keyword{Currently} +\keyword{none} diff --git a/pack/eddy4R.maps/man/wrap.spat.data.grab.Rd b/pack/eddy4R.maps/man/wrap.spat.data.grab.Rd new file mode 100644 index 00000000..e2de76ea --- /dev/null +++ b/pack/eddy4R.maps/man/wrap.spat.data.grab.Rd @@ -0,0 +1,42 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/wrap.spat.data.R +\name{wrap.spat.data.grab} +\alias{wrap.spat.data.grab} +\title{wrapper function to generate spatial targets} +\usage{ +wrap.spat.data.grab( + dir = "targets", + dateBgn = "2019-05-01", + dateEnd = "2019-11-30", + site = c("cheesehead")[1], + dt = "P1M", + dx = 0.1, + dy = 0.1 +) +} +\arguments{ +\item{dir}{A directory of .tif files for scoring} + +\item{site_id}{The side ID of the area being forecasted. Currently "august_complex" is the only option.} +} +\value{ +message from minio submission +} +\description{ +Wrapper definition. Generate spatial targets +} +\examples{ +spat4cast_submit(dir = "targets", site_id = "august_complex") +} +\references{ +License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 +} +\seealso{ +Currently none +} +\author{ +David Durden +Stefan Metzger \email{smetzger@atmofacts.com} +} +\keyword{Currently} +\keyword{none} From 8157cfd9ea23666cde2a13064c1d4d3bb30886e4 Mon Sep 17 00:00:00 2001 From: stefanmet Date: Fri, 1 Mar 2024 21:39:13 +0000 Subject: [PATCH 50/66] repackage --- pack/eddy4R.base/man/Logger.Singleton.Rd | 78 +++++++++++++----------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/pack/eddy4R.base/man/Logger.Singleton.Rd b/pack/eddy4R.base/man/Logger.Singleton.Rd index f35b9ad7..394c849f 100644 --- a/pack/eddy4R.base/man/Logger.Singleton.Rd +++ b/pack/eddy4R.base/man/Logger.Singleton.Rd @@ -28,23 +28,29 @@ print(log_entries) \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-Logger.Singleton-new}{\code{Logger.Singleton$new()}} -\item \href{#method-Logger.Singleton-set_logging_level}{\code{Logger.Singleton$set_logging_level()}} -\item \href{#method-Logger.Singleton-set_log_file}{\code{Logger.Singleton$set_log_file()}} -\item \href{#method-Logger.Singleton-log_message}{\code{Logger.Singleton$log_message()}} -\item \href{#method-Logger.Singleton-debug}{\code{Logger.Singleton$debug()}} -\item \href{#method-Logger.Singleton-info}{\code{Logger.Singleton$info()}} -\item \href{#method-Logger.Singleton-warn}{\code{Logger.Singleton$warn()}} -\item \href{#method-Logger.Singleton-error}{\code{Logger.Singleton$error()}} -\item \href{#method-Logger.Singleton-get_log_entries}{\code{Logger.Singleton$get_log_entries()}} -\item \href{#method-Logger.Singleton-get_logging_level}{\code{Logger.Singleton$get_logging_level()}} -\item \href{#method-Logger.Singleton-get_log_file}{\code{Logger.Singleton$get_log_file()}} -\item \href{#method-Logger.Singleton-clone}{\code{Logger.Singleton$clone()}} +\item \href{#method-new}{\code{Logger.Singleton$new()}} +\item \href{#method-set_logging_level}{\code{Logger.Singleton$set_logging_level()}} +\item \href{#method-set_log_file}{\code{Logger.Singleton$set_log_file()}} +\item \href{#method-log_message}{\code{Logger.Singleton$log_message()}} +\item \href{#method-debug}{\code{Logger.Singleton$debug()}} +\item \href{#method-info}{\code{Logger.Singleton$info()}} +\item \href{#method-warn}{\code{Logger.Singleton$warn()}} +\item \href{#method-error}{\code{Logger.Singleton$error()}} +\item \href{#method-get_log_entries}{\code{Logger.Singleton$get_log_entries()}} +\item \href{#method-get_logging_level}{\code{Logger.Singleton$get_logging_level()}} +\item \href{#method-get_log_file}{\code{Logger.Singleton$get_log_file()}} +\item \href{#method-clone}{\code{Logger.Singleton$clone()}} +} +} +\if{html}{ +\out{
Inherited methods} +\itemize{ } +\out{
} } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Logger.Singleton-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-new}{}}} \subsection{Method \code{new()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{Logger.Singleton$new(logging_level = NULL, log_file = NULL)}\if{html}{\out{
}} @@ -61,8 +67,8 @@ print(log_entries) } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Logger.Singleton-set_logging_level}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-set_logging_level}{}}} \subsection{Method \code{set_logging_level()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{Logger.Singleton$set_logging_level(level)}\if{html}{\out{
}} @@ -77,8 +83,8 @@ print(log_entries) } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Logger.Singleton-set_log_file}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-set_log_file}{}}} \subsection{Method \code{set_log_file()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{Logger.Singleton$set_log_file(file)}\if{html}{\out{
}} @@ -93,8 +99,8 @@ print(log_entries) } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Logger.Singleton-log_message}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-log_message}{}}} \subsection{Method \code{log_message()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{Logger.Singleton$log_message(level, message)}\if{html}{\out{
}} @@ -111,8 +117,8 @@ print(log_entries) } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Logger.Singleton-debug}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-debug}{}}} \subsection{Method \code{debug()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{Logger.Singleton$debug(message)}\if{html}{\out{
}} @@ -127,8 +133,8 @@ print(log_entries) } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Logger.Singleton-info}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-info}{}}} \subsection{Method \code{info()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{Logger.Singleton$info(message)}\if{html}{\out{
}} @@ -143,8 +149,8 @@ print(log_entries) } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Logger.Singleton-warn}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-warn}{}}} \subsection{Method \code{warn()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{Logger.Singleton$warn(message)}\if{html}{\out{
}} @@ -159,8 +165,8 @@ print(log_entries) } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Logger.Singleton-error}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-error}{}}} \subsection{Method \code{error()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{Logger.Singleton$error(message)}\if{html}{\out{
}} @@ -175,8 +181,8 @@ print(log_entries) } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Logger.Singleton-get_log_entries}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-get_log_entries}{}}} \subsection{Method \code{get_log_entries()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{Logger.Singleton$get_log_entries()}\if{html}{\out{
}} @@ -184,8 +190,8 @@ print(log_entries) } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Logger.Singleton-get_logging_level}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-get_logging_level}{}}} \subsection{Method \code{get_logging_level()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{Logger.Singleton$get_logging_level()}\if{html}{\out{
}} @@ -193,8 +199,8 @@ print(log_entries) } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Logger.Singleton-get_log_file}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-get_log_file}{}}} \subsection{Method \code{get_log_file()}}{ \subsection{Usage}{ \if{html}{\out{
}}\preformatted{Logger.Singleton$get_log_file()}\if{html}{\out{
}} @@ -202,8 +208,8 @@ print(log_entries) } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Logger.Singleton-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ From 5f19f54d0eabb6b6dac7e51d73e7ce25662e7e76 Mon Sep 17 00:00:00 2001 From: ddurden Date: Sun, 3 Mar 2024 22:51:01 +0000 Subject: [PATCH 51/66] - eddy4R.maps updates for dependencies --- Dockerfile | 2 +- pack/eddy4R.maps/DESCRIPTION | 12 + pack/eddy4R.maps/R/wrap.spat.data.R | 16 +- renv.lock | 859 ++++++++++++++++++++++------ renv/activate.R | 388 ++++--------- renv/settings.json | 3 +- utilities/flow.inst.dock.renv.R | 6 +- utilities/flow.renv.init.rstr.R | 2 +- 8 files changed, 841 insertions(+), 447 deletions(-) diff --git a/Dockerfile b/Dockerfile index 93f57421..84ff27fc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # start with the ropensci image including debian:testing, r-base, rocker/rstudio, rocker/hadleyverse # https://hub.docker.com/r/rocker/ropensci/ -FROM quay.io/battelleecology/rstudio:4.0.5 +FROM ghcr.io/rocker-org/geospatial:4.2.2 WORKDIR /home/eddy/eddy4R diff --git a/pack/eddy4R.maps/DESCRIPTION b/pack/eddy4R.maps/DESCRIPTION index 09f28942..c88908f7 100644 --- a/pack/eddy4R.maps/DESCRIPTION +++ b/pack/eddy4R.maps/DESCRIPTION @@ -18,6 +18,18 @@ Authors@R: c( person(given = "Stefan", Description: Basic commonalities and tools for working with Flux Maps™ in R. Depends: R (>= 4.0.5) +Imports: + assertthat, + raster, + leaflet, + htmlwidgets, + mapview, + here, + sf, + lubridate, + gdalcubes, + rstac, + stars License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Encoding: UTF-8 LazyData: true diff --git a/pack/eddy4R.maps/R/wrap.spat.data.R b/pack/eddy4R.maps/R/wrap.spat.data.R index 38d0ebe7..46c9967d 100644 --- a/pack/eddy4R.maps/R/wrap.spat.data.R +++ b/pack/eddy4R.maps/R/wrap.spat.data.R @@ -15,7 +15,7 @@ wrap.spat.data.grab <- function( dateBgn = "2019-05-01", dateEnd = "2019-11-30", site = c("cheesehead")[1], - dt = "P1M" + dt = "P1M", dx = 0.1, dy = 0.1 ) @@ -23,17 +23,17 @@ wrap.spat.data.grab <- function( print(paste0("Grabbing spatial data at ", Sys.time(), "for", site)) - library(here) - library(sf) - library(lubridate) - library(gdalcubes) - library(rstac) - library(stars) + # library(here) + # library(sf) + # library(lubridate) + # library(gdalcubes) + # library(rstac) + # library(stars) #Source functions - for (f in list.files(here::here("R"), full.names = TRUE)) source (f) + # for (f in list.files(here::here("R"), full.names = TRUE)) source (f) #Create fire bounding box bbox <- def.bbox(lat = 45.94625, lon = -90.27276, pad_box = TRUE) diff --git a/renv.lock b/renv.lock index 195a8def..968f4a40 100644 --- a/renv.lock +++ b/renv.lock @@ -1,7 +1,27 @@ { "R": { - "Version": "4.0.5", + "Version": "4.2.2", "Repositories": [ + { + "Name": "BioCsoft", + "URL": "https://bioconductor.org/packages/3.16/bioc" + }, + { + "Name": "BioCann", + "URL": "https://bioconductor.org/packages/3.16/data/annotation" + }, + { + "Name": "BioCexp", + "URL": "https://bioconductor.org/packages/3.16/data/experiment" + }, + { + "Name": "BioCworkflows", + "URL": "https://bioconductor.org/packages/3.16/workflows" + }, + { + "Name": "BioCbooks", + "URL": "https://bioconductor.org/packages/3.16/books" + }, { "Name": "CRANNew", "URL": "https://packagemanager.rstudio.com/cran/2023-09-22" @@ -21,41 +41,53 @@ ] }, "Bioconductor": { - "Version": "3.12" + "Version": "3.16" }, "Packages": { + "BH": { + "Package": "BH", + "Version": "1.81.0-1", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "68122010f01c4dcfbe58ce7112f2433d" + }, "BiocGenerics": { "Package": "BiocGenerics", - "Version": "0.36.1", + "Version": "0.44.0", "Source": "Bioconductor", + "git_url": "https://git.bioconductor.org/packages/BiocGenerics", + "git_branch": "RELEASE_3_16", + "git_last_commit": "d7cd9c1", + "git_last_commit_date": "2022-11-01", "Requirements": [ "R", "graphics", "methods", - "parallel", "stats", "utils" ], - "Hash": "f628c51eadc74ffd838b801cd22d589a" + "Hash": "0de19224c2cd94f48fbc0d0bc663ce3b" }, "BiocManager": { "Package": "BiocManager", - "Version": "1.30.22", + "Version": "1.30.20", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "utils" ], - "Hash": "d57e43105a1aa9cb54fdb4629725acb1" + "Hash": "a7fca16a50b6ef7771b49d636dd54b57" }, - "BiocVersion": { - "Package": "BiocVersion", - "Version": "3.12.0", - "Source": "Bioconductor", + "DBI": { + "Package": "DBI", + "Version": "1.1.3", + "Source": "Repository", + "Repository": "RSPM", "Requirements": [ - "R" + "R", + "methods" ], - "Hash": "ee4d027afb8f52fec1f46b9f0a8660e9" + "Hash": "b2866e62bab9378c3cc9476a1954226b" }, "DEoptimR": { "Package": "DEoptimR", @@ -81,8 +113,12 @@ }, "EBImage": { "Package": "EBImage", - "Version": "4.32.0", + "Version": "4.40.1", "Source": "Bioconductor", + "git_url": "https://git.bioconductor.org/packages/EBImage", + "git_branch": "RELEASE_3_16", + "git_last_commit": "f2b0b41", + "git_last_commit_date": "2023-04-09", "Requirements": [ "BiocGenerics", "RCurl", @@ -100,7 +136,7 @@ "tiff", "utils" ], - "Hash": "c7e9e12a627de495c8531bfee0f5af4f" + "Hash": "846bb2e1bfdd6b605c729c409d1fc8b5" }, "EMD": { "Package": "EMD", @@ -127,18 +163,18 @@ }, "KernSmooth": { "Package": "KernSmooth", - "Version": "2.23-18", + "Version": "2.23-20", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", "stats" ], - "Hash": "9e703ad8bf0e99f3691f05da32dfe68b" + "Hash": "8dcfa99b14c296bc9f1fd64d52fd3ce7" }, "MASS": { "Package": "MASS", - "Version": "7.3-53.1", + "Version": "7.3-58.1", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -149,11 +185,11 @@ "stats", "utils" ], - "Hash": "4ef21dd0348b9abb7f8bd1d77e4cd0c3" + "Hash": "762e1804143a332333c054759f89a706" }, "Matrix": { "Package": "Matrix", - "Version": "1.3-2", + "Version": "1.5-1", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -165,7 +201,7 @@ "stats", "utils" ], - "Hash": "ff280503079ad8623d3c4b1519b24ea2" + "Hash": "539dc0c0c05636812f1080f473d2c177" }, "R.methodsS3": { "Package": "R.methodsS3", @@ -245,7 +281,7 @@ }, "RCurl": { "Package": "RCurl", - "Version": "1.98-1.12", + "Version": "1.98-1.10", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -253,18 +289,18 @@ "bitops", "methods" ], - "Hash": "1d6ed2d006d483f31c6d5531f3a39923" + "Hash": "35136c52e39f2679ebbe7bf448e3bd5a" }, "Rcpp": { "Package": "Rcpp", - "Version": "1.0.11", + "Version": "1.0.10", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "methods", "utils" ], - "Hash": "ae6cbbe1492f4de79c45fce06f967ce8" + "Hash": "e749cae40fa9ef469b6050959517453c" }, "RcppRoll": { "Package": "RcppRoll", @@ -279,12 +315,16 @@ }, "Rhdf5lib": { "Package": "Rhdf5lib", - "Version": "1.12.1", + "Version": "1.20.0", "Source": "Bioconductor", + "git_url": "https://git.bioconductor.org/packages/Rhdf5lib", + "git_branch": "RELEASE_3_16", + "git_last_commit": "7606799", + "git_last_commit_date": "2022-11-01", "Requirements": [ "R" ], - "Hash": "7dc9be3558a910226d64a2040520dc7f" + "Hash": "66fbe0c49a27fe0a17182554f14f7fbc" }, "Rmisc": { "Package": "Rmisc", @@ -311,13 +351,23 @@ }, "askpass": { "Package": "askpass", - "Version": "1.2.0", + "Version": "1.1", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "sys" ], - "Hash": "cad6cf7f1d5f6e906700b9d3e718c796" + "Hash": "e8a22846fff485f0be3770c2da758713" + }, + "assertthat": { + "Package": "assertthat", + "Version": "0.2.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "tools" + ], + "Hash": "50c838a310445e954bc13f26f26a6ecf" }, "base64enc": { "Package": "base64enc", @@ -348,7 +398,7 @@ }, "boot": { "Package": "boot", - "Version": "1.3-27", + "Version": "1.3-28", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -356,7 +406,7 @@ "graphics", "stats" ], - "Hash": "d9778c960792721e8433daaf3db8f16a" + "Hash": "0baa960e3b49c6176a4f42addcbacc59" }, "brew": { "Package": "brew", @@ -374,7 +424,7 @@ }, "bslib": { "Package": "bslib", - "Version": "0.5.1", + "Version": "0.4.2", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -390,18 +440,18 @@ "rlang", "sass" ], - "Hash": "283015ddfbb9d7bf15ea9f0b5698f0d9" + "Hash": "a7fbf03946ad741129dc81098722fca1" }, "cachem": { "Package": "cachem", - "Version": "1.0.8", + "Version": "1.0.7", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "fastmap", "rlang" ], - "Hash": "c35768291560ce302c0a6589f92e837d" + "Hash": "cda74447c42f529de601fe4d4050daef" }, "callr": { "Package": "callr", @@ -416,16 +466,45 @@ ], "Hash": "9b2191ede20fa29828139b9900922e51" }, + "class": { + "Package": "class", + "Version": "7.3-20", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "MASS", + "R", + "stats", + "utils" + ], + "Hash": "da09d82223e669d270e47ed24ac8686e" + }, + "classInt": { + "Package": "classInt", + "Version": "0.4-9", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "KernSmooth", + "R", + "class", + "e1071", + "grDevices", + "graphics", + "stats" + ], + "Hash": "bee651a42a89633eccb36dca9d9ab413" + }, "cli": { "Package": "cli", - "Version": "3.6.1", + "Version": "3.6.0", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", "utils" ], - "Hash": "89e6d8219950eac806ae0c489052048a" + "Hash": "3177a5a16c243adc199ba33117bd9657" }, "clipr": { "Package": "clipr", @@ -460,20 +539,17 @@ }, "commonmark": { "Package": "commonmark", - "Version": "1.9.0", + "Version": "1.8.1", "Source": "Repository", "Repository": "RSPM", - "Hash": "d691c61bff84bd63c383874d2d0c3307" + "Hash": "b6e3e947d1d7ebf3d2bdcea1bde63fe7" }, "cpp11": { "Package": "cpp11", - "Version": "0.4.6", + "Version": "0.4.3", "Source": "Repository", "Repository": "RSPM", - "Requirements": [ - "R" - ], - "Hash": "707fae4bbf73697ec8d85f9d7076c061" + "Hash": "ed588261931ee3be2c700d22e94a29ab" }, "crayon": { "Package": "crayon", @@ -489,7 +565,7 @@ }, "credentials": { "Package": "credentials", - "Version": "2.0.1", + "Version": "1.3.2", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -499,17 +575,30 @@ "openssl", "sys" ], - "Hash": "c7844b32098dcbd1c59cbd8dddb4ecc6" + "Hash": "93762d0a34d78e6a025efdbfb5c6bb41" + }, + "crosstalk": { + "Package": "crosstalk", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R6", + "htmltools", + "jsonlite", + "lazyeval" + ], + "Hash": "6aa54f69598c32177e920eb3402e8293" }, "curl": { "Package": "curl", - "Version": "5.0.2", + "Version": "5.0.0", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R" ], - "Hash": "511bacbfa153a15251166b463b4da4f9" + "Hash": "e4f97056611e8e6b8b852d13b7400cf1" }, "data.table": { "Package": "data.table", @@ -597,14 +686,14 @@ }, "digest": { "Package": "digest", - "Version": "0.6.33", + "Version": "0.6.31", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", "utils" ], - "Hash": "b18a9cf3c003977b0cc49d5e76ebe48d" + "Hash": "8b708f296afd9ae69f450f9640be8990" }, "dotCall64": { "Package": "dotCall64", @@ -618,7 +707,7 @@ }, "downlit": { "Package": "downlit", - "Version": "0.4.3", + "Version": "0.4.2", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -634,7 +723,7 @@ "withr", "yaml" ], - "Hash": "14fa1f248b60ed67e1f5418391a17b14" + "Hash": "79bf3f66590752ffbba20f8d2da94c7c" }, "downloader": { "Package": "downloader", @@ -649,7 +738,7 @@ }, "dplyr": { "Package": "dplyr", - "Version": "1.1.3", + "Version": "1.1.0", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -668,7 +757,23 @@ "utils", "vctrs" ], - "Hash": "e85ffbebaad5f70e1a2e2ef4302b4949" + "Hash": "d3c34618017e7ae252d46d79a1b9ec32" + }, + "e1071": { + "Package": "e1071", + "Version": "1.7-13", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "class", + "grDevices", + "graphics", + "methods", + "proxy", + "stats", + "utils" + ], + "Hash": "1046cb48d06cb40c2900d8878f03a0fe" }, "ellipsis": { "Package": "ellipsis", @@ -683,14 +788,14 @@ }, "evaluate": { "Package": "evaluate", - "Version": "0.21", + "Version": "0.20", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", "methods" ], - "Hash": "d59f3b464e8da1aef82dc04b588b8dfb" + "Hash": "4b68aa51edd89a0e044a66e75ae3cc6c" }, "fansi": { "Package": "fansi", @@ -790,7 +895,7 @@ }, "fontawesome": { "Package": "fontawesome", - "Version": "0.5.2", + "Version": "0.5.0", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -798,18 +903,32 @@ "htmltools", "rlang" ], - "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + "Hash": "e80750aec5717dedc019ad7ee40e4a7c" }, "fs": { "Package": "fs", - "Version": "1.6.3", + "Version": "1.6.1", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", "methods" ], - "Hash": "47b5f30c720c23999b913a1a635cf0bb" + "Hash": "f4dcd23b67e33d851d2079f703e8b985" + }, + "gdalcubes": { + "Package": "gdalcubes", + "Version": "0.6.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "BH", + "R", + "Rcpp", + "jsonlite", + "ncdf4" + ], + "Hash": "78c2ae3c1caeeb19635eb2ff932c4252" }, "generics": { "Package": "generics", @@ -822,9 +941,34 @@ ], "Hash": "15e9634c0fcd294799e9b2e929ed1b86" }, + "geojsonsf": { + "Package": "geojsonsf", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "Rcpp", + "geometries", + "jsonify", + "rapidjsonr", + "sfheaders" + ], + "Hash": "8d077646c6713838233e8710910ef92e" + }, + "geometries": { + "Package": "geometries", + "Version": "0.2.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "Rcpp" + ], + "Hash": "09604531daa57d9c3884b7a15488bab9" + }, "gert": { "Package": "gert", - "Version": "1.9.3", + "Version": "1.9.2", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -835,11 +979,11 @@ "sys", "zip" ], - "Hash": "b544c397820e05a97d391b2d614a921a" + "Hash": "9122b3958e749badb5c939f498038b57" }, "ggplot2": { "Package": "ggplot2", - "Version": "3.4.3", + "Version": "3.4.1", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -860,7 +1004,7 @@ "vctrs", "withr" ], - "Hash": "85846544c596e71f8f46483ab165da33" + "Hash": "d494daf77c4aa7f084dbbe6ca5dcaca7" }, "gh": { "Package": "gh", @@ -915,18 +1059,14 @@ }, "gtable": { "Package": "gtable", - "Version": "0.3.4", + "Version": "0.3.1", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", - "cli", - "glue", - "grid", - "lifecycle", - "rlang" + "grid" ], - "Hash": "b29cf3031f49b04ab9c852c912547eef" + "Hash": "36b4265fb818f6a342bed217549cd896" }, "highr": { "Package": "highr", @@ -941,7 +1081,7 @@ }, "htmltools": { "Package": "htmltools", - "Version": "0.5.6", + "Version": "0.5.4", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -954,11 +1094,11 @@ "rlang", "utils" ], - "Hash": "a2326a66919a3311f7fbb1e3bf568283" + "Hash": "9d27e99cc90bd701c0a7a63e5923f9b7" }, "htmlwidgets": { "Package": "htmlwidgets", - "Version": "1.6.2", + "Version": "1.6.1", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -969,11 +1109,11 @@ "rmarkdown", "yaml" ], - "Hash": "a865aa85bcb2697f47505bfd70422471" + "Hash": "b677ee5954471eaa974c0d099a343a1a" }, "httpuv": { "Package": "httpuv", - "Version": "1.6.11", + "Version": "1.6.9", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -984,11 +1124,11 @@ "promises", "utils" ], - "Hash": "838602f54e32c1a0f8cc80708cefcefa" + "Hash": "1046aa31a57eae8b357267a56a0b6d8b" }, "httr": { "Package": "httr", - "Version": "1.4.7", + "Version": "1.4.5", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -999,11 +1139,11 @@ "mime", "openssl" ], - "Hash": "ac107251d9d9fd72f0ca8049988f1d7f" + "Hash": "f6844033201269bec3ca0097bc6c97b3" }, "httr2": { "Package": "httr2", - "Version": "0.2.3", + "Version": "0.2.2", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -1018,7 +1158,7 @@ "rlang", "withr" ], - "Hash": "193bb297368afbbb42dc85784a46b36e" + "Hash": "5c09fe33064978ede54de42309c8b532" }, "ini": { "Package": "ini", @@ -1058,19 +1198,31 @@ ], "Hash": "5aab57a3bd297eee1c1d862735972182" }, + "jsonify": { + "Package": "jsonify", + "Version": "1.2.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "Rcpp", + "rapidjsonr" + ], + "Hash": "49a9775e4f8c96c654b6018739067055" + }, "jsonlite": { "Package": "jsonlite", - "Version": "1.8.7", + "Version": "1.8.4", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "methods" ], - "Hash": "266a20443ca13c65688b2116d5220f76" + "Hash": "a4269a09a9b865579b2635c77e572374" }, "knitr": { "Package": "knitr", - "Version": "1.44", + "Version": "1.42", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -1082,33 +1234,33 @@ "xfun", "yaml" ], - "Hash": "60885b9f746c9dfaef110d070b5f7dc0" + "Hash": "8329a9bcc82943c8069104d4be3ee22d" }, "labeling": { "Package": "labeling", - "Version": "0.4.3", + "Version": "0.4.2", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "graphics", "stats" ], - "Hash": "b64ec208ac5bc1852b285f665d6368b3" + "Hash": "3d5108641f47470611a32d0bdf357a72" }, "later": { "Package": "later", - "Version": "1.3.1", + "Version": "1.3.0", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "Rcpp", "rlang" ], - "Hash": "40401c9cf2bc2259dfe83311c9384710" + "Hash": "7e7b457d7766bc47f2a5f21cc2984f8e" }, "lattice": { "Package": "lattice", - "Version": "0.20-41", + "Version": "0.20-45", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -1119,7 +1271,87 @@ "stats", "utils" ], - "Hash": "fbd9285028b0263d76d18c95ae51a53d" + "Hash": "b64cdbb2b340437c4ee047a1f4c4377b" + }, + "lazyeval": { + "Package": "lazyeval", + "Version": "0.2.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "d908914ae53b04d4c0c0fd72ecc35370" + }, + "leafem": { + "Package": "leafem", + "Version": "0.2.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "base64enc", + "geojsonsf", + "htmltools", + "htmlwidgets", + "leaflet", + "methods", + "png", + "raster", + "sf" + ], + "Hash": "db6e565a81ce81f137660467644e6fcd" + }, + "leaflet": { + "Package": "leaflet", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "RColorBrewer", + "base64enc", + "crosstalk", + "htmltools", + "htmlwidgets", + "leaflet.providers", + "magrittr", + "markdown", + "methods", + "png", + "raster", + "scales", + "sp", + "stats", + "viridis" + ], + "Hash": "ac2c7f21c2a6d2579eed8aaae4c42610" + }, + "leaflet.providers": { + "Package": "leaflet.providers", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "d3082a7beac4a1aeb96100ff06265d7e" + }, + "leafpop": { + "Package": "leafpop", + "Version": "0.1.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "base64enc", + "brew", + "htmltools", + "htmlwidgets", + "sf", + "svglite", + "uuid" + ], + "Hash": "0c1e9e9a79598ec5acb1820d1948dae8" }, "lgr": { "Package": "lgr", @@ -1147,14 +1379,14 @@ }, "locfit": { "Package": "locfit", - "Version": "1.5-9.4", + "Version": "1.5-9.7", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", "lattice" ], - "Hash": "760b5b542e8435237d1b3c253bfe18e7" + "Hash": "08c4156abea85c9b6d4e7798427a887d" }, "lubridate": { "Package": "lubridate", @@ -1191,6 +1423,45 @@ ], "Hash": "644a88fb036ab50cee0b715394eefa1a" }, + "mapview": { + "Package": "mapview", + "Version": "2.11.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "base64enc", + "htmltools", + "htmlwidgets", + "lattice", + "leafem", + "leaflet", + "leafpop", + "methods", + "png", + "raster", + "satellite", + "scales", + "servr", + "sf", + "sp", + "webshot" + ], + "Hash": "204c456b6b2742e196209a2a70eb5679" + }, + "markdown": { + "Package": "markdown", + "Version": "1.5", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "commonmark", + "utils", + "xfun" + ], + "Hash": "d209cfd1f4ff7260eae5a7f07da3aa4f" + }, "matlab": { "Package": "matlab", "Version": "1.0.4", @@ -1215,7 +1486,7 @@ }, "mgcv": { "Package": "mgcv", - "Version": "1.8-34", + "Version": "1.8-41", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -1228,7 +1499,7 @@ "stats", "utils" ], - "Hash": "bd4a6c4b600f58651d60d381b0e9a397" + "Hash": "6b3904f13346742caa3e82dd0303d4ad" }, "mime": { "Package": "mime", @@ -1274,6 +1545,13 @@ ], "Hash": "463b268710930f7bffef33147400966a" }, + "ncdf4": { + "Package": "ncdf4", + "Version": "1.21", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "a7b262641ef4659767d415c6bb78413f" + }, "neonUtilities": { "Package": "neonUtilities", "Version": "2.3.0", @@ -1298,7 +1576,7 @@ }, "nlme": { "Package": "nlme", - "Version": "3.1-152", + "Version": "3.1-160", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -1308,17 +1586,17 @@ "stats", "utils" ], - "Hash": "35de1ce639f20b5e10f7f46260730c65" + "Hash": "02e3c6e7df163aafa8477225e6827bc5" }, "openssl": { "Package": "openssl", - "Version": "2.1.0", + "Version": "2.0.6", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "askpass" ], - "Hash": "273a6bb4a9844c296a459d2176673270" + "Hash": "0f7cd2962e3044bb940cca4f4b5cecbe" }, "pbapply": { "Package": "pbapply", @@ -1344,7 +1622,7 @@ }, "pillar": { "Package": "pillar", - "Version": "1.9.0", + "Version": "1.8.1", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -1357,11 +1635,11 @@ "utils", "vctrs" ], - "Hash": "15da5a8412f317beeee6175fbc76f4bb" + "Hash": "f2316df30902c81729ae9de95ad5a608" }, "pkgbuild": { "Package": "pkgbuild", - "Version": "1.4.2", + "Version": "1.4.0", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -1373,9 +1651,10 @@ "desc", "prettyunits", "processx", - "rprojroot" + "rprojroot", + "withr" ], - "Hash": "beb25b32a957a22a5c301a9e441190b3" + "Hash": "d6c3008d79653a0f267703288230105e" }, "pkgconfig": { "Package": "pkgconfig", @@ -1419,7 +1698,7 @@ }, "pkgload": { "Package": "pkgload", - "Version": "1.3.2.1", + "Version": "1.3.2", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -1435,7 +1714,7 @@ "utils", "withr" ], - "Hash": "a7f498a1b2a4a6816148e498509f6e1d" + "Hash": "6b0c222c5071efe0f3baf3dae9aa40e2" }, "plyr": { "Package": "plyr", @@ -1485,7 +1764,7 @@ }, "processx": { "Package": "processx", - "Version": "3.8.2", + "Version": "3.8.0", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -1494,53 +1773,61 @@ "ps", "utils" ], - "Hash": "3efbd8ac1be0296a46c55387aeace0f3" + "Hash": "a33ee2d9bf07564efb888ad98410da84" }, "profvis": { "Package": "profvis", - "Version": "0.3.8", + "Version": "0.3.7", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", "htmlwidgets", - "purrr", - "rlang", - "stringr", - "vctrs" + "stringr" ], - "Hash": "aa5a3864397ce6ae03458f98618395a1" + "Hash": "e9d21e79848e02e524bea6f5bd53e7e4" }, "promises": { "Package": "promises", - "Version": "1.2.1", + "Version": "1.2.0.1", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R6", "Rcpp", - "fastmap", "later", "magrittr", "rlang", "stats" ], - "Hash": "0d8a15c9d000970ada1ab21405387dee" + "Hash": "4ab2c43adb4d4699cf3690acd378d75d" + }, + "proxy": { + "Package": "proxy", + "Version": "0.4-27", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "stats", + "utils" + ], + "Hash": "e0ef355c12942cf7a6b91a6cfaea8b3e" }, "ps": { "Package": "ps", - "Version": "1.7.5", + "Version": "1.7.2", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", "utils" ], - "Hash": "709d852d33178db54b17c722e5b1e594" + "Hash": "68dd03d98a5efd1eb3012436de45ba83" }, "purrr": { "Package": "purrr", - "Version": "1.0.2", + "Version": "1.0.1", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -1551,7 +1838,7 @@ "rlang", "vctrs" ], - "Hash": "1cba04a4e9414bdefc9dcaa99649a8dc" + "Hash": "d71c815267c640f17ddbf7f16144b4bb" }, "ragg": { "Package": "ragg", @@ -1564,6 +1851,13 @@ ], "Hash": "690bc058ea2b1b8a407d3cfe3dce3ef9" }, + "rapidjsonr": { + "Package": "rapidjsonr", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "88b9f48c93d17cdb811b54079a6a414f" + }, "rappdirs": { "Package": "rappdirs", "Version": "0.3.3", @@ -1574,6 +1868,20 @@ ], "Hash": "5e3c5dc0b071b21fa128676560dbe94d" }, + "raster": { + "Package": "raster", + "Version": "3.6-20", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "Rcpp", + "methods", + "sp", + "terra" + ], + "Hash": "ebebd9f0f203a129eb2da96470191b82" + }, "rcmdcheck": { "Package": "rcmdcheck", "Version": "1.4.0", @@ -1608,7 +1916,7 @@ }, "remotes": { "Package": "remotes", - "Version": "2.4.2.1", + "Version": "2.4.2", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -1618,17 +1926,17 @@ "tools", "utils" ], - "Hash": "63d15047eb239f95160112bcadc4fcb9" + "Hash": "227045be9aee47e6dda9bb38ac870d67" }, "renv": { "Package": "renv", - "Version": "1.0.3", + "Version": "0.17.3", "Source": "Repository", - "Repository": "RSPM", + "Repository": "CRAN", "Requirements": [ "utils" ], - "Hash": "41b847654f567341725473431dd0d5ab" + "Hash": "4543b8cd233ae25c6aba8548be9e747e" }, "reshape2": { "Package": "reshape2", @@ -1645,46 +1953,53 @@ }, "rhdf5": { "Package": "rhdf5", - "Version": "2.34.0", + "Version": "2.42.1", "Source": "Bioconductor", + "git_url": "https://git.bioconductor.org/packages/rhdf5", + "git_branch": "RELEASE_3_16", + "git_last_commit": "8df5fc7", + "git_last_commit_date": "2023-04-07", "Requirements": [ "R", "Rhdf5lib", "methods", "rhdf5filters" ], - "Hash": "bf25b880f72c8b6dcbd8719b5ac9113b" + "Hash": "5c03978672acd1d85ce56f9d12f5fe5b" }, "rhdf5filters": { "Package": "rhdf5filters", - "Version": "1.2.1", + "Version": "1.10.1", "Source": "Bioconductor", + "git_url": "https://git.bioconductor.org/packages/rhdf5filters", + "git_branch": "RELEASE_3_16", + "git_last_commit": "ccf950c", + "git_last_commit_date": "2023-03-24", "Requirements": [ "Rhdf5lib" ], - "Hash": "4e9258b05bbcbc1093eda53aa8051264" + "Hash": "63806aa966d50f02b18aba6d0d34e3c8" }, "rlang": { "Package": "rlang", - "Version": "1.1.1", + "Version": "1.0.6", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", "utils" ], - "Hash": "a85c767b55f0bf9b7ad16c6d7baee5bb" + "Hash": "4ed1f8336c8d52c3e750adcdc57228a7" }, "rmarkdown": { "Package": "rmarkdown", - "Version": "2.25", + "Version": "2.20", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", "bslib", "evaluate", - "fontawesome", "htmltools", "jquerylib", "jsonlite", @@ -1697,7 +2012,7 @@ "xfun", "yaml" ], - "Hash": "d65e35823c817f09f4de424fcdfa812a" + "Hash": "716fde5382293cc94a71f68c85b78d19" }, "robfilter": { "Package": "robfilter", @@ -1800,12 +2115,28 @@ ], "Hash": "5f22863b417e1178980a3105fe66bd51" }, + "rstac": { + "Package": "rstac", + "Version": "0.9.2-4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "crayon", + "httr", + "jsonlite", + "lifecycle", + "magrittr", + "utils" + ], + "Hash": "1d4eec2c2df01ea9f524bd713c8e9349" + }, "rstudioapi": { "Package": "rstudioapi", - "Version": "0.15.0", + "Version": "0.14", "Source": "Repository", "Repository": "RSPM", - "Hash": "5564500e25cffad9e22244ced1379887" + "Hash": "690bd2acc42a9166ce34845884459320" }, "rversions": { "Package": "rversions", @@ -1819,9 +2150,21 @@ ], "Hash": "a9881dfed103e83f9de151dc17002cd1" }, + "s2": { + "Package": "s2", + "Version": "1.1.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "Rcpp", + "wk" + ], + "Hash": "5cca323babe990f99d5bc3402f64b905" + }, "sass": { "Package": "sass", - "Version": "0.4.7", + "Version": "0.4.5", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -1831,7 +2174,28 @@ "rappdirs", "rlang" ], - "Hash": "6bd4d33b50ff927191ec9acbf52fd056" + "Hash": "2bb4371a4c80115518261866eab6ab11" + }, + "satellite": { + "Package": "satellite", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "Rcpp", + "grDevices", + "graphics", + "methods", + "plyr", + "raster", + "stats", + "stats4", + "terra", + "tools", + "utils" + ], + "Hash": "6bb739cedb7a0a9e5f9604d599223298" }, "scales": { "Package": "scales", @@ -1851,6 +2215,20 @@ ], "Hash": "906cb23d2f1c5680b8ce439b44c6fa63" }, + "servr": { + "Package": "servr", + "Version": "0.25", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "httpuv", + "jsonlite", + "mime", + "xfun" + ], + "Hash": "fff3eeed2ebb27fc33abbd6d953610a4" + }, "sessioninfo": { "Package": "sessioninfo", "Version": "1.2.2", @@ -1864,6 +2242,41 @@ ], "Hash": "3f9796a8d0a0e8c6eb49a4b029359d1f" }, + "sf": { + "Package": "sf", + "Version": "1.0-10", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "DBI", + "R", + "Rcpp", + "classInt", + "grDevices", + "graphics", + "grid", + "magrittr", + "methods", + "s2", + "stats", + "tools", + "units", + "utils" + ], + "Hash": "63879bf5f4900e021a660a48716278fc" + }, + "sfheaders": { + "Package": "sfheaders", + "Version": "0.4.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "Rcpp", + "geometries" + ], + "Hash": "0b835f43939178a3cac6712fbe8cc2e8" + }, "sfsmisc": { "Package": "sfsmisc", "Version": "1.1-16", @@ -1880,7 +2293,7 @@ }, "shiny": { "Package": "shiny", - "Version": "1.7.5", + "Version": "1.7.4", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -1910,7 +2323,7 @@ "withr", "xtable" ], - "Hash": "438b99792adbe82a8329ad8697d45afe" + "Hash": "c2eae3d8c670fa9dfa35a12066f4a1d5" }, "signal": { "Package": "signal", @@ -1937,6 +2350,23 @@ ], "Hash": "5f5a7629f956619d519205ec475fe647" }, + "sp": { + "Package": "sp", + "Version": "1.6-0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "grDevices", + "graphics", + "grid", + "lattice", + "methods", + "stats", + "utils" + ], + "Hash": "6674e075a078d9c3bde8ba800367347c" + }, "spam": { "Package": "spam", "Version": "2.9-1", @@ -1961,6 +2391,23 @@ ], "Hash": "55cafdb25f64a02bcbbe0467e5611a76" }, + "stars": { + "Package": "stars", + "Version": "0.6-4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "abind", + "classInt", + "methods", + "parallel", + "rlang", + "sf", + "units" + ], + "Hash": "8b4284bf9fddfb481d516877a2045023" + }, "stringi": { "Package": "stringi", "Version": "1.7.12", @@ -1991,12 +2438,24 @@ ], "Hash": "671a4d384ae9d32fc47a14e98bfa3dc8" }, + "svglite": { + "Package": "svglite", + "Version": "2.1.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cpp11", + "systemfonts" + ], + "Hash": "29442899581643411facb66f4add846a" + }, "sys": { "Package": "sys", - "Version": "3.4.2", + "Version": "3.4.1", "Source": "Repository", "Repository": "RSPM", - "Hash": "3a1be13d68d47a8cd0bfd74739ca1555" + "Hash": "34c16f1ef796057bfa06d3f4ff818a5d" }, "systemfonts": { "Package": "systemfonts", @@ -2009,9 +2468,21 @@ ], "Hash": "90b28393209827327de889f49935140a" }, + "terra": { + "Package": "terra", + "Version": "1.7-18", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "Rcpp", + "methods" + ], + "Hash": "5db3d690ad42a1828d3bf9e063c98587" + }, "testthat": { "Package": "testthat", - "Version": "3.1.10", + "Version": "3.1.7", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -2037,7 +2508,7 @@ "waldo", "withr" ], - "Hash": "6f403dc49295610a3a67ea1a9ca64346" + "Hash": "7eb5fd202a61d2fb78af5869b6c08998" }, "textshaping": { "Package": "textshaping", @@ -2053,7 +2524,7 @@ }, "tibble": { "Package": "tibble", - "Version": "3.2.1", + "Version": "3.2.0", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -2068,7 +2539,7 @@ "utils", "vctrs" ], - "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + "Hash": "37695ff125982007d42a59ad10982ff2" }, "tidyr": { "Package": "tidyr", @@ -2132,13 +2603,24 @@ }, "tinytex": { "Package": "tinytex", - "Version": "0.46", + "Version": "0.44", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "xfun" ], - "Hash": "0c41a73214d982f539c56a7773c7afa5" + "Hash": "c0f007e2eeed7722ce13d42b84a22e07" + }, + "units": { + "Package": "units", + "Version": "0.8-1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "Rcpp" + ], + "Hash": "81433967f9b34a120a4f5a5a016cd5ed" }, "urlchecker": { "Package": "urlchecker", @@ -2156,7 +2638,7 @@ }, "usethis": { "Package": "usethis", - "Version": "2.2.2", + "Version": "2.1.6", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -2183,7 +2665,7 @@ "withr", "yaml" ], - "Hash": "60e51f0b94d0324dc19e44110098fa9f" + "Hash": "a67a22c201832b12c036cc059f1d137d" }, "utf8": { "Package": "utf8", @@ -2195,9 +2677,19 @@ ], "Hash": "1fe17157424bb09c48a8b3b550c753bc" }, + "uuid": { + "Package": "uuid", + "Version": "1.1-0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "f1cb46c157d080b729159d407be83496" + }, "vctrs": { "Package": "vctrs", - "Version": "0.6.3", + "Version": "0.5.2", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -2207,21 +2699,35 @@ "lifecycle", "rlang" ], - "Hash": "d0ef2856b83dc33ea6e255caf6229ee2" + "Hash": "e4ffa94ceed5f124d429a5a5f0f5b378" + }, + "viridis": { + "Package": "viridis", + "Version": "0.6.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "ggplot2", + "gridExtra", + "stats", + "viridisLite" + ], + "Hash": "ee96aee95a7a563e5496f8991e9fde4b" }, "viridisLite": { "Package": "viridisLite", - "Version": "0.4.2", + "Version": "0.4.1", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R" ], - "Hash": "c826c7c4241b6fc89ff55aaea3fa7491" + "Hash": "62f4b5da3e08d8e5bcba6cac15603f70" }, "waldo": { "Package": "waldo", - "Version": "0.5.1", + "Version": "0.4.0", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -2234,7 +2740,20 @@ "rlang", "tibble" ], - "Hash": "2c993415154cdb94649d99ae138ff5e5" + "Hash": "035fba89d0c86e2113120f93301b98ad" + }, + "webshot": { + "Package": "webshot", + "Version": "0.5.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "callr", + "jsonlite", + "magrittr" + ], + "Hash": "cfd9342c76693ae53108a474aafa1641" }, "whisker": { "Package": "whisker", @@ -2256,27 +2775,37 @@ ], "Hash": "c0e49a9760983e81e55cdd9be92e7182" }, + "wk": { + "Package": "wk", + "Version": "0.7.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "141385279f2cd7faa6a3eccd8d1279dd" + }, "xfun": { "Package": "xfun", - "Version": "0.40", + "Version": "0.37", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "stats", "tools" ], - "Hash": "be07d23211245fc7d4209f54c4e4ffc8" + "Hash": "a6860e1400a8fd1ddb6d9b4230cc34ab" }, "xml2": { "Package": "xml2", - "Version": "1.3.5", + "Version": "1.3.3", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", "methods" ], - "Hash": "6c40e5cfcc6aefd88110666e18c31f40" + "Hash": "40682ed6a969ea5abfd351eb67833adc" }, "xopen": { "Package": "xopen", @@ -2310,14 +2839,14 @@ }, "zip": { "Package": "zip", - "Version": "2.3.0", + "Version": "2.2.2", "Source": "Repository", "Repository": "RSPM", - "Hash": "d98c94dacb7e0efcf83b0a133a705504" + "Hash": "c42bfcec3fa6a0cce17ce1f8bc684f88" }, "zoo": { "Package": "zoo", - "Version": "1.8-12", + "Version": "1.8-11", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -2328,7 +2857,7 @@ "stats", "utils" ], - "Hash": "5c715954112b45499fb1dadc6ee6ee3e" + "Hash": "874a5b77fe0cfacf2a3450069ae70926" } } } diff --git a/renv/activate.R b/renv/activate.R index cb5401f9..a8fdc320 100644 --- a/renv/activate.R +++ b/renv/activate.R @@ -2,27 +2,11 @@ local({ # the requested version of renv - version <- "1.0.3" - attr(version, "sha") <- NULL + version <- "0.17.3" # the project directory project <- getwd() - # use start-up diagnostics if enabled - diagnostics <- Sys.getenv("RENV_STARTUP_DIAGNOSTICS", unset = "FALSE") - if (diagnostics) { - start <- Sys.time() - profile <- tempfile("renv-startup-", fileext = ".Rprof") - utils::Rprof(profile) - on.exit({ - utils::Rprof(NULL) - elapsed <- signif(difftime(Sys.time(), start, units = "auto"), digits = 2L) - writeLines(sprintf("- renv took %s to run the autoloader.", format(elapsed))) - writeLines(sprintf("- Profile: %s", profile)) - print(utils::summaryRprof(profile)) - }, add = TRUE) - } - # figure out whether the autoloader is enabled enabled <- local({ @@ -76,75 +60,25 @@ local({ # load bootstrap tools `%||%` <- function(x, y) { - if (is.null(x)) y else x - } - - catf <- function(fmt, ..., appendLF = TRUE) { - - quiet <- getOption("renv.bootstrap.quiet", default = FALSE) - if (quiet) - return(invisible()) - - msg <- sprintf(fmt, ...) - cat(msg, file = stdout(), sep = if (appendLF) "\n" else "") - - invisible(msg) - - } - - header <- function(label, - ..., - prefix = "#", - suffix = "-", - n = min(getOption("width"), 78)) - { - label <- sprintf(label, ...) - n <- max(n - nchar(label) - nchar(prefix) - 2L, 8L) - if (n <= 0) - return(paste(prefix, label)) - - tail <- paste(rep.int(suffix, n), collapse = "") - paste0(prefix, " ", label, " ", tail) - + if (is.environment(x) || length(x)) x else y } - startswith <- function(string, prefix) { - substring(string, 1, nchar(prefix)) == prefix + `%??%` <- function(x, y) { + if (is.null(x)) y else x } bootstrap <- function(version, library) { - friendly <- renv_bootstrap_version_friendly(version) - section <- header(sprintf("Bootstrapping renv %s", friendly)) - catf(section) - # attempt to download renv - catf("- Downloading renv ... ", appendLF = FALSE) - withCallingHandlers( - tarball <- renv_bootstrap_download(version), - error = function(err) { - catf("FAILED") - stop("failed to download:\n", conditionMessage(err)) - } - ) - catf("OK") - on.exit(unlink(tarball), add = TRUE) + tarball <- tryCatch(renv_bootstrap_download(version), error = identity) + if (inherits(tarball, "error")) + stop("failed to download renv ", version) # now attempt to install - catf("- Installing renv ... ", appendLF = FALSE) - withCallingHandlers( - status <- renv_bootstrap_install(version, tarball, library), - error = function(err) { - catf("FAILED") - stop("failed to install:\n", conditionMessage(err)) - } - ) - catf("OK") - - # add empty line to break up bootstrapping from normal output - catf("") + status <- tryCatch(renv_bootstrap_install(version, tarball, library), error = identity) + if (inherits(status, "error")) + stop("failed to install renv ", version) - return(invisible()) } renv_bootstrap_tests_running <- function() { @@ -174,6 +108,13 @@ local({ if (!inherits(repos, "error") && length(repos)) return(repos) + # if we're testing, re-use the test repositories + if (renv_bootstrap_tests_running()) { + repos <- getOption("renv.tests.repos") + if (!is.null(repos)) + return(repos) + } + # retrieve current repos repos <- getOption("repos") @@ -217,34 +158,33 @@ local({ renv_bootstrap_download <- function(version) { - sha <- attr(version, "sha", exact = TRUE) - - methods <- if (!is.null(sha)) { - - # attempting to bootstrap a development version of renv - c( - function() renv_bootstrap_download_tarball(sha), - function() renv_bootstrap_download_github(sha) + # if the renv version number has 4 components, assume it must + # be retrieved via github + nv <- numeric_version(version) + components <- unclass(nv)[[1]] + + # if this appears to be a development version of 'renv', we'll + # try to restore from github + dev <- length(components) == 4L + + # begin collecting different methods for finding renv + methods <- c( + renv_bootstrap_download_tarball, + if (dev) + renv_bootstrap_download_github + else c( + renv_bootstrap_download_cran_latest, + renv_bootstrap_download_cran_archive ) - - } else { - - # attempting to bootstrap a release version of renv - c( - function() renv_bootstrap_download_tarball(version), - function() renv_bootstrap_download_cran_latest(version), - function() renv_bootstrap_download_cran_archive(version) - ) - - } + ) for (method in methods) { - path <- tryCatch(method(), error = identity) + path <- tryCatch(method(version), error = identity) if (is.character(path) && file.exists(path)) return(path) } - stop("All download methods failed") + stop("failed to download renv ", version) } @@ -308,6 +248,8 @@ local({ type <- spec$type repos <- spec$repos + message("* Downloading renv ", version, " ... ", appendLF = FALSE) + baseurl <- utils::contrib.url(repos = repos, type = type) ext <- if (identical(type, "source")) ".tar.gz" @@ -324,10 +266,13 @@ local({ condition = identity ) - if (inherits(status, "condition")) + if (inherits(status, "condition")) { + message("FAILED") return(FALSE) + } # report success and return + message("OK (downloaded ", type, ")") destfile } @@ -384,6 +329,8 @@ local({ urls <- file.path(repos, "src/contrib/Archive/renv", name) destfile <- file.path(tempdir(), name) + message("* Downloading renv ", version, " ... ", appendLF = FALSE) + for (url in urls) { status <- tryCatch( @@ -391,11 +338,14 @@ local({ condition = identity ) - if (identical(status, 0L)) + if (identical(status, 0L)) { + message("OK") return(destfile) + } } + message("FAILED") return(FALSE) } @@ -418,7 +368,7 @@ local({ if (!file.exists(tarball)) { # let the user know we weren't able to honour their request - fmt <- "- RENV_BOOTSTRAP_TARBALL is set (%s) but does not exist." + fmt <- "* RENV_BOOTSTRAP_TARBALL is set (%s) but does not exist." msg <- sprintf(fmt, tarball) warning(msg) @@ -427,7 +377,10 @@ local({ } - catf("- Using local tarball '%s'.", tarball) + fmt <- "* Bootstrapping with tarball at path '%s'." + msg <- sprintf(fmt, tarball) + message(msg) + tarball } @@ -454,6 +407,8 @@ local({ on.exit(do.call(base::options, saved), add = TRUE) } + message("* Downloading renv ", version, " from GitHub ... ", appendLF = FALSE) + url <- file.path("https://api.github.com/repos/rstudio/renv/tarball", version) name <- sprintf("renv_%s.tar.gz", version) destfile <- file.path(tempdir(), name) @@ -463,105 +418,26 @@ local({ condition = identity ) - if (!identical(status, 0L)) + if (!identical(status, 0L)) { + message("FAILED") return(FALSE) - - renv_bootstrap_download_augment(destfile) - - return(destfile) - - } - - # Add Sha to DESCRIPTION. This is stop gap until #890, after which we - # can use renv::install() to fully capture metadata. - renv_bootstrap_download_augment <- function(destfile) { - sha <- renv_bootstrap_git_extract_sha1_tar(destfile) - if (is.null(sha)) { - return() } - # Untar - tempdir <- tempfile("renv-github-") - on.exit(unlink(tempdir, recursive = TRUE), add = TRUE) - untar(destfile, exdir = tempdir) - pkgdir <- dir(tempdir, full.names = TRUE)[[1]] - - # Modify description - desc_path <- file.path(pkgdir, "DESCRIPTION") - desc_lines <- readLines(desc_path) - remotes_fields <- c( - "RemoteType: github", - "RemoteHost: api.github.com", - "RemoteRepo: renv", - "RemoteUsername: rstudio", - "RemotePkgRef: rstudio/renv", - paste("RemoteRef: ", sha), - paste("RemoteSha: ", sha) - ) - writeLines(c(desc_lines[desc_lines != ""], remotes_fields), con = desc_path) - - # Re-tar - local({ - old <- setwd(tempdir) - on.exit(setwd(old), add = TRUE) - - tar(destfile, compression = "gzip") - }) - invisible() - } + message("OK") + return(destfile) - # Extract the commit hash from a git archive. Git archives include the SHA1 - # hash as the comment field of the tarball pax extended header - # (see https://www.kernel.org/pub/software/scm/git/docs/git-archive.html) - # For GitHub archives this should be the first header after the default one - # (512 byte) header. - renv_bootstrap_git_extract_sha1_tar <- function(bundle) { - - # open the bundle for reading - # We use gzcon for everything because (from ?gzcon) - # > Reading from a connection which does not supply a 'gzip' magic - # > header is equivalent to reading from the original connection - conn <- gzcon(file(bundle, open = "rb", raw = TRUE)) - on.exit(close(conn)) - - # The default pax header is 512 bytes long and the first pax extended header - # with the comment should be 51 bytes long - # `52 comment=` (11 chars) + 40 byte SHA1 hash - len <- 0x200 + 0x33 - res <- rawToChar(readBin(conn, "raw", n = len)[0x201:len]) - - if (grepl("^52 comment=", res)) { - sub("52 comment=", "", res) - } else { - NULL - } } renv_bootstrap_install <- function(version, tarball, library) { # attempt to install it into project library + message("* Installing renv ", version, " ... ", appendLF = FALSE) dir.create(library, showWarnings = FALSE, recursive = TRUE) - output <- renv_bootstrap_install_impl(library, tarball) - - # check for successful install - status <- attr(output, "status") - if (is.null(status) || identical(status, 0L)) - return(status) - - # an error occurred; report it - header <- "installation of renv failed" - lines <- paste(rep.int("=", nchar(header)), collapse = "") - text <- paste(c(header, lines, output), collapse = "\n") - stop(text) - - } - - renv_bootstrap_install_impl <- function(library, tarball) { # invoke using system2 so we can capture and report output bin <- R.home("bin") exe <- if (Sys.info()[["sysname"]] == "Windows") "R.exe" else "R" - R <- file.path(bin, exe) + r <- file.path(bin, exe) args <- c( "--vanilla", "CMD", "INSTALL", "--no-multiarch", @@ -569,7 +445,19 @@ local({ shQuote(path.expand(tarball)) ) - system2(R, args, stdout = TRUE, stderr = TRUE) + output <- system2(r, args, stdout = TRUE, stderr = TRUE) + message("Done!") + + # check for successful install + status <- attr(output, "status") + if (is.numeric(status) && !identical(status, 0L)) { + header <- "Error installing renv:" + lines <- paste(rep.int("=", nchar(header)), collapse = "") + text <- c(header, lines, output) + writeLines(text, con = stderr()) + } + + status } @@ -779,60 +667,32 @@ local({ } - renv_bootstrap_validate_version <- function(version, description = NULL) { - - # resolve description file - # - # avoid passing lib.loc to `packageDescription()` below, since R will - # use the loaded version of the package by default anyhow. note that - # this function should only be called after 'renv' is loaded - # https://github.com/rstudio/renv/issues/1625 - description <- description %||% packageDescription("renv") + renv_bootstrap_validate_version <- function(version) { - # check whether requested version 'version' matches loaded version of renv - sha <- attr(version, "sha", exact = TRUE) - valid <- if (!is.null(sha)) - renv_bootstrap_validate_version_dev(sha, description) - else - renv_bootstrap_validate_version_release(version, description) - - if (valid) + loadedversion <- utils::packageDescription("renv", fields = "Version") + if (version == loadedversion) return(TRUE) - # the loaded version of renv doesn't match the requested version; - # give the user instructions on how to proceed - remote <- if (!is.null(description[["RemoteSha"]])) { - paste("rstudio/renv", description[["RemoteSha"]], sep = "@") - } else { - paste("renv", description[["Version"]], sep = "@") - } - - # display both loaded version + sha if available - friendly <- renv_bootstrap_version_friendly( - version = description[["Version"]], - sha = description[["RemoteSha"]] - ) + # assume four-component versions are from GitHub; + # three-component versions are from CRAN + components <- strsplit(loadedversion, "[.-]")[[1]] + remote <- if (length(components) == 4L) + paste("rstudio/renv", loadedversion, sep = "@") + else + paste("renv", loadedversion, sep = "@") fmt <- paste( "renv %1$s was loaded from project library, but this project is configured to use renv %2$s.", - "- Use `renv::record(\"%3$s\")` to record renv %1$s in the lockfile.", - "- Use `renv::restore(packages = \"renv\")` to install renv %2$s into the project library.", + "Use `renv::record(\"%3$s\")` to record renv %1$s in the lockfile.", + "Use `renv::restore(packages = \"renv\")` to install renv %2$s into the project library.", sep = "\n" ) - catf(fmt, friendly, renv_bootstrap_version_friendly(version), remote) - - FALSE - } + msg <- sprintf(fmt, loadedversion, version, remote) + warning(msg, call. = FALSE) - renv_bootstrap_validate_version_dev <- function(version, description) { - expected <- description[["RemoteSha"]] - is.character(expected) && startswith(expected, version) - } + FALSE - renv_bootstrap_validate_version_release <- function(version, description) { - expected <- description[["Version"]] - is.character(expected) && identical(expected, version) } renv_bootstrap_hash_text <- function(text) { @@ -858,7 +718,7 @@ local({ hooks <- getHook("renv::autoload") for (hook in hooks) if (is.function(hook)) - tryCatch(hook(), error = warnify) + tryCatch(hook(), error = warning) # load the project renv::load(project) @@ -999,40 +859,6 @@ local({ } - renv_bootstrap_version_friendly <- function(version, shafmt = NULL, sha = NULL) { - sha <- sha %||% attr(version, "sha", exact = TRUE) - parts <- c(version, sprintf(shafmt %||% " [sha: %s]", substring(sha, 1L, 7L))) - paste(parts, collapse = "") - } - - renv_bootstrap_exec <- function(project, libpath, version) { - if (!renv_bootstrap_load(project, libpath, version)) - renv_bootstrap_run(version, libpath) - } - - renv_bootstrap_run <- function(version, libpath) { - - # perform bootstrap - bootstrap(version, libpath) - - # exit early if we're just testing bootstrap - if (!is.na(Sys.getenv("RENV_BOOTSTRAP_INSTALL_ONLY", unset = NA))) - return(TRUE) - - # try again to load - if (requireNamespace("renv", lib.loc = libpath, quietly = TRUE)) { - return(renv::load(project = getwd())) - } - - # failed to download or load renv; warn the user - msg <- c( - "Failed to find an renv installation: the project will not be loaded.", - "Use `renv::activate()` to re-initialize the project." - ) - - warning(paste(msg, collapse = "\n"), call. = FALSE) - - } renv_json_read <- function(file = NULL, text = NULL) { @@ -1172,9 +998,35 @@ local({ # construct full libpath libpath <- file.path(root, prefix) - # run bootstrap code - renv_bootstrap_exec(project, libpath, version) + # attempt to load + if (renv_bootstrap_load(project, libpath, version)) + return(TRUE) + + # load failed; inform user we're about to bootstrap + prefix <- paste("# Bootstrapping renv", version) + postfix <- paste(rep.int("-", 77L - nchar(prefix)), collapse = "") + header <- paste(prefix, postfix) + message(header) + + # perform bootstrap + bootstrap(version, libpath) + + # exit early if we're just testing bootstrap + if (!is.na(Sys.getenv("RENV_BOOTSTRAP_INSTALL_ONLY", unset = NA))) + return(TRUE) + + # try again to load + if (requireNamespace("renv", lib.loc = libpath, quietly = TRUE)) { + message("* Successfully installed and loaded renv ", version, ".") + return(renv::load()) + } + + # failed to download or load renv; warn the user + msg <- c( + "Failed to find an renv installation: the project will not be loaded.", + "Use `renv::activate()` to re-initialize the project." + ) - invisible() + warning(paste(msg, collapse = "\n"), call. = FALSE) }) diff --git a/renv/settings.json b/renv/settings.json index 5d1b7408..d2f7e2b8 100644 --- a/renv/settings.json +++ b/renv/settings.json @@ -7,6 +7,7 @@ "eddy4R.erf", "eddy4R.stor", "eddy4R.qaqc", + "eddy4R.maps", "som", "Noble", "eddy4R.ucrt", @@ -19,8 +20,6 @@ "Depends", "LinkingTo" ], - "ppm.enabled": null, - "ppm.ignored.urls": [], "r.version": null, "snapshot.type": "implicit", "use.cache": true, diff --git a/utilities/flow.inst.dock.renv.R b/utilities/flow.inst.dock.renv.R index c5f7bc1e..0fe734a7 100644 --- a/utilities/flow.inst.dock.renv.R +++ b/utilities/flow.inst.dock.renv.R @@ -101,10 +101,11 @@ renv::consent(provided=TRUE) # 4.2 specify base directory for repo clone NameDirRepo and for packages NameDirRepo <- "/home/eddy/eddy4R" +#NameDirRepo <- "/home/ddurden/eddy/code/eddy4R_ddurden" #Dealing with bioconductor install.packages("BiocManager") -BiocManager::install(version = "3.12") +BiocManager::install(version = "3.16") renv::install("bioc::EBImage") #Restore dependencies using renv @@ -112,7 +113,7 @@ renv::restore(lockfile=paste0(NameDirRepo,"/renv.lock")) #base::library(devtools) -NameDirPack <- c( "pack/eddy4R.base", "pack/eddy4R.turb", "pack/eddy4R.stor", "pack/eddy4R.qaqc","pack/Waves") +NameDirPack <- c( "pack/eddy4R.base", "pack/eddy4R.turb", "pack/eddy4R.stor", "pack/eddy4R.qaqc","pack/eddy4R.maps","pack/Waves") Dir <- paste(NameDirRepo, NameDirPack, sep = "/") # 4.3 actual installation @@ -149,3 +150,4 @@ renv::install(packages = "REddyProc@1.2"#, # repos=c("https://cran.rstudio.com/", # for dependencies on CRAN packages # "http://R-Forge.R-project.org") # for REddyProc on R-Forge ) +#library(stars) diff --git a/utilities/flow.renv.init.rstr.R b/utilities/flow.renv.init.rstr.R index 4adae2b8..21fb1518 100644 --- a/utilities/flow.renv.init.rstr.R +++ b/utilities/flow.renv.init.rstr.R @@ -42,7 +42,7 @@ options(repos = c( CRANNew = "https://packagemanager.rstudio.com/cran/2023-09-22 #dirWork <- '~/NEON-IS-data-processing/pack/NEONprocIS.wq' #dirWork <- '~/R/NEON-IS-data-processing-homeDir/pack/NEONprocIS.base' dirWork <- "/home/ddurden/eddy/code/eddy4R_ddurden" -PackIgnr <- c("eddy4R.base", "eddy4R.turb", "eddy4R.erf", "eddy4R.stor", "eddy4R.qaqc","som","Noble", "eddy4R.ucrt", "Waves", "accs", "NEONprocIS.base") # These should already be in the respective docker containers +PackIgnr <- c("eddy4R.base", "eddy4R.turb", "eddy4R.erf", "eddy4R.stor", "eddy4R.qaqc","eddy4R.maps","som","Noble", "eddy4R.ucrt", "Waves", "accs", "NEONprocIS.base") # These should already be in the respective docker containers # Keep and use the local project that renv creates when creating/updating the lockfile # in dirWork? If TRUE, note .Rprofile and .Rproj files will be created/retained in dirWork, From 54dc41aebb3860211b7c53a5a8356d32d448cd53 Mon Sep 17 00:00:00 2001 From: ddurden Date: Mon, 4 Mar 2024 19:15:06 +0000 Subject: [PATCH 52/66] - repackaging with data --- pack/eddy4R.maps/DESCRIPTION | 2 +- pack/eddy4R.maps/NAMESPACE | 4 ++ pack/eddy4R.maps/data/CHEESEHEAD.rda | Bin 0 -> 2508 bytes pack/eddy4R.maps/data/NEON.rda | Bin 0 -> 585182 bytes pack/eddy4R.maps/man/def.bbox.Rd | 45 +++++++++++++++++ pack/eddy4R.maps/man/def.spat.data.cube.Rd | 52 ++++++++++++++++++++ pack/eddy4R.maps/man/plot.flux.spatial.Rd | 46 +++++++++++++++++ pack/eddy4R.maps/man/wrap.spat.data.grab.Rd | 30 +++++++++++ pack/eddy4R.qaqc/man/def.plau.Rd | 1 - 9 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 pack/eddy4R.maps/data/CHEESEHEAD.rda create mode 100644 pack/eddy4R.maps/data/NEON.rda create mode 100644 pack/eddy4R.maps/man/def.bbox.Rd create mode 100644 pack/eddy4R.maps/man/def.spat.data.cube.Rd create mode 100644 pack/eddy4R.maps/man/plot.flux.spatial.Rd create mode 100644 pack/eddy4R.maps/man/wrap.spat.data.grab.Rd diff --git a/pack/eddy4R.maps/DESCRIPTION b/pack/eddy4R.maps/DESCRIPTION index c88908f7..13311316 100644 --- a/pack/eddy4R.maps/DESCRIPTION +++ b/pack/eddy4R.maps/DESCRIPTION @@ -34,4 +34,4 @@ License: GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Encoding: UTF-8 LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.1.1 +RoxygenNote: 7.2.3 diff --git a/pack/eddy4R.maps/NAMESPACE b/pack/eddy4R.maps/NAMESPACE index 6ae92683..5f3d2d22 100644 --- a/pack/eddy4R.maps/NAMESPACE +++ b/pack/eddy4R.maps/NAMESPACE @@ -1,2 +1,6 @@ # Generated by roxygen2: do not edit by hand +S3method(plot,flux.spatial) +export(def.bbox) +export(def.spat.data.cube) +export(wrap.spat.data.grab) diff --git a/pack/eddy4R.maps/data/CHEESEHEAD.rda b/pack/eddy4R.maps/data/CHEESEHEAD.rda new file mode 100644 index 0000000000000000000000000000000000000000..bbd63c963d5a26d7fb76bead4fe0d67711b8ded5 GIT binary patch literal 2508 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 z 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