diff --git a/DESCRIPTION b/DESCRIPTION index c3f48ea2..8f8bfe7f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: tlf Type: Package Title: TLF Library -Version: 1.1.0 +Version: 1.2.0 Authors@R: c(person(given = "Open-Systems-Pharmacology Community", role = "cph"), @@ -25,7 +25,8 @@ BugReports: https://github.com/open-systems-pharmacology/tlf-library/issues Imports: ggplot2 (>= 3.3.0), R6, - reshape2 + reshape2, + jsonlite Depends: R (>= 3.5) Encoding: UTF-8 @@ -46,9 +47,7 @@ Collate: 'atom-plots.R' 'boxwhisker-datamapping.R' 'boxwhisker-get-measure.R' - 'boxwhisker-plot.R' 'boxwhisker-plotconfiguration.R' - 'data-description.R' 'datamapping-grouping.R' 'datamapping-groupmapping.R' 'datamapping-range.R' @@ -57,7 +56,6 @@ Collate: 'ddiratio-datamapping.R' 'ddiratio-plotconfiguration.R' 'error-checks.R' - 'utils.R' 'font.R' 'histogram-datamapping.R' 'histogram-plotconfiguration.R' @@ -66,9 +64,11 @@ Collate: 'metadata-helper.R' 'obs-vs-pred-datamapping.R' 'obs-vs-pred-plotconfiguration.R' + 'observed-data-mapping.R' 'pkratio-datamapping.R' 'pkratio-get-measure.R' 'pkratio-plotconfiguration.R' + 'plot-boxwhisker.R' 'plot-ddiratio.R' 'plot-histogram.R' 'plot-obs-vs-pred.R' @@ -88,9 +88,11 @@ Collate: 'tlf-env.R' 'tornado-datamapping.R' 'tornado-plotconfiguration.R' + 'utilities-aesthetics.R' 'utilities-axis.R' 'utilities-background.R' 'utilities-export.R' 'utilities-label.R' 'utilities-legend.R' 'utilities-mapping.R' + 'utils.R' diff --git a/NAMESPACE b/NAMESPACE index 650dd90a..7a7d98c5 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -27,13 +27,16 @@ export("median+1.5IQR") export("median+IQR") export("median-1.5IQR") export("median-IQR") +export(AestheticSelectionKeys) export(AggregationInput) export(AggregationSummary) export(AxisConfiguration) export(BackgroundConfiguration) -export(BackgroundElementConfiguration) +export(BackgroundElement) export(BoxWhiskerDataMapping) export(BoxWhiskerPlotConfiguration) +export(ColorMaps) +export(DDIComparisonTypes) export(DDIRatioDataMapping) export(DDIRatioPlotConfiguration) export(ExportConfiguration) @@ -47,16 +50,23 @@ export(LabelConfiguration) export(LegendConfiguration) export(LegendPositions) export(LegendTypes) +export(LineElement) +export(Linetypes) export(ObsVsPredDataMapping) export(ObsVsPredPlotConfiguration) +export(ObservedDataMapping) export(PKRatioDataMapping) export(PKRatioPlotConfiguration) export(PlotConfiguration) export(RangeDataMapping) export(Scaling) +export(Shapes) export(Theme) -export(ThemeAesProperties) +export(ThemeAestheticMaps) +export(ThemeAestheticSelections) +export(ThemeBackground) export(ThemeFont) +export(ThemePlotConfigurations) export(TimeProfileDataMapping) export(TimeProfilePlotConfiguration) export(TornadoDataMapping) @@ -71,17 +81,16 @@ export(addRibbon) export(addScatter) export(addWatermark) export(asLabel) -export(bigTheme) -export(bwTheme) export(createWatermarkGrob) -export(defaultTheme) export(exportPlot) export(getBoxWhiskerMeasure) export(getDefaultCaptions) +export(getGuestValues) export(getLabelWithUnit) export(getLegendCaption) export(getPKRatioMeasure) export(initializePlot) +export(loadThemeFromJson) export(metaDataHelper) export(plotBoxWhisker) export(plotDDIRatio) @@ -90,7 +99,14 @@ export(plotObsVsPred) export(plotPKRatio) export(plotTimeProfile) export(plotTornado) +export(runDDIRatioPlot) +export(runObsVsPredPlot) +export(runPKRatioPlot) +export(runThemeMaker) +export(saveThemeToJson) export(setBackground) +export(setBackgroundPanelArea) +export(setBackgroundPlotArea) export(setCaptionColor) export(setCaptionFill) export(setCaptionLabels) @@ -106,10 +122,10 @@ export(setDefaultExportName) export(setDefaultExportParameters) export(setDefaultLegendPosition) export(setDefaultWatermark) -export(setFontProperties) export(setGrid) export(setLegend) export(setLegendCaption) +export(setLegendFont) export(setLegendPosition) export(setLegendTitle) export(setPlotExport) @@ -118,10 +134,12 @@ export(setPlotExportSize) export(setPlotLabels) export(setWatermark) export(setXAxis) +export(setXGrid) export(setYAxis) +export(setYGrid) export(tlfStatFunctions) -export(tlfTheme) export(updateLegendCaption) export(useTheme) import(ggplot2) +import(jsonlite) import(utils) diff --git a/R/atom-plots.R b/R/atom-plots.R index 1cdbb003..8385c483 100644 --- a/R/atom-plots.R +++ b/R/atom-plots.R @@ -2,7 +2,7 @@ #' @title initializePlot #' @param plotConfiguration -#' \code{PlotConfiguration} class or subclass defining labels, grid, background and watermark +#' \code{PlotConfiguration} objecct defining labels, grid, background and watermark #' This parameter is optional: the \code{tlf} library provides a default configuration according to the current theme #' @description #' Initialize a \code{ggplot} object and set the labels, grid, background and watermark @@ -12,10 +12,6 @@ #' # Initialize an empty plot #' p <- initializePlot() #' -#' # Use a predifined theme -#' useTheme(tlfTheme) -#' p <- initializePlot() -#' #' # Implement a customized configuration using PlotConfiguration #' config <- PlotConfiguration$new(title = "My Plot", xlabel = "x variable", ylabel = "y variable") #' p <- initializePlot(config) @@ -27,8 +23,10 @@ initializePlot <- function(plotConfiguration = NULL) { plotObject$plotConfiguration <- plotConfiguration plotObject <- setWatermark(plotObject) - plotObject <- setBackground(plotObject) - plotObject <- setGrid(plotObject) + plotObject <- setBackgroundPlotArea(plotObject) + plotObject <- setBackgroundPanelArea(plotObject) + plotObject <- setXGrid(plotObject) + plotObject <- setYGrid(plotObject) plotObject <- setPlotLabels(plotObject) return(plotObject) @@ -102,9 +100,8 @@ addScatter <- function(data = NULL, validateIsOfType(dataMapping, XYGDataMapping, nullAllowed = TRUE) validateIsOfType(plotConfiguration, PlotConfiguration, nullAllowed = TRUE) - # If data is not input - # Create new data and its mapping from x and y input - if (is.null(data)) { + # If data is not input, creates data from x and y inputs + if (isOfLength(data, 0)) { validateIsSameLength(x, y) data <- as.data.frame(cbind(x = x, y = y)) dataMapping <- dataMapping %||% XYGDataMapping$new(x = ifnotnull(x, "x"), y = ifnotnull(y, "y"), data = data) @@ -115,15 +112,21 @@ addScatter <- function(data = NULL, } dataMapping <- dataMapping %||% XYGDataMapping$new(x = x, y = y, data = data) - plotConfiguration <- plotConfiguration %||% PlotConfiguration$new(data = data, metaData = metaData, dataMapping = dataMapping) + # Update plotConfiguration if user defined aesthetics + aestheticInputs <- c("color", "shape", "linetype", "size") + aestheticInputsExpression <- parse(text = paste0( + "plotConfiguration$points$", aestheticInputs, + " <- ", aestheticInputs, " %||% plotConfiguration$points$", aestheticInputs + )) + eval(aestheticInputsExpression) # If no plot, initialize empty plot plotObject <- plotObject %||% initializePlot(plotConfiguration) # If no mapping, nor x or y, return plotObject - if (is.null(dataMapping$x) || is.null(dataMapping$y)) { - warning("No mapping found for x or y, scatter layer was not added") + if (any(isOfLength(dataMapping$x, 0), isOfLength(dataMapping$y, 0))) { + warning("No mapping found for x nor y, scatter layer was not added") return(plotObject) } @@ -143,7 +146,7 @@ addScatter <- function(data = NULL, mapping = ggplot2::aes_string(x = mapLabels$x, y = mapLabels$y, shape = "legendLabels", color = "legendLabels", size = "legendLabels"), show.legend = TRUE ) + - ggplot2::geom_line( + ggplot2::geom_path( data = mapData, mapping = ggplot2::aes_string(x = mapLabels$x, y = mapLabels$y, linetype = "legendLabels", color = "legendLabels", size = "legendLabels"), show.legend = TRUE @@ -151,26 +154,21 @@ addScatter <- function(data = NULL, ggplot2::geom_ribbon( data = mapData, mapping = ggplot2::aes_string(x = mapLabels$x, ymin = mapLabels$y, ymax = mapLabels$y, fill = "legendLabels"), + # alpha is 0 so that line can be seen in legend + alpha = 0, show.legend = TRUE ) # Prepare data for merging previous and current legend - # TO DO: base modulo of newLegendProperty on theme aesProperties lengths newLabels <- levels(factor(mapData$legendLabels)) - legendLength <- nrow(plotObject$plotConfiguration$legend$caption) %||% 0 - newLegendProperty <- seq(legendLength + 1, legendLength + length(newLabels)) - 1 %% 6 + 1 - - # Sample LegendType properties based tlfTheme if not input - plotObject <- mergeLegend(plotObject, + # Sample LegendType properties based Theme if not input + try(plotObject <- mergeLegend(plotObject, newLabels = newLabels, - color = color %||% tlfEnv$currentTheme$aesProperties$color[newLegendProperty], - shape = shape %||% tlfEnv$currentTheme$aesProperties$shape[newLegendProperty], - size = size %||% rep(1, length(newLabels)), - linetype = linetype %||% rep("blank", length(newLabels)), - fill = rep(NA, length(newLabels)) - ) - try(suppressMessages(plotObject <- plotObject$plotConfiguration$xAxis$setPlotAxis(plotObject))) - try(suppressMessages(plotObject <- plotObject$plotConfiguration$yAxis$setPlotAxis(plotObject))) + aestheticSelections = plotConfiguration$points + )) + # Try is used to prevent crashes in the final plot due to ggplot2 peculiarities regarding scale functions + try(suppressMessages(plotObject <- setXAxis(plotObject))) + try(suppressMessages(plotObject <- setYAxis(plotObject))) return(plotObject) } @@ -249,9 +247,8 @@ addLine <- function(data = NULL, validateIsOfType(dataMapping, XYGDataMapping, nullAllowed = TRUE) validateIsOfType(plotConfiguration, PlotConfiguration, nullAllowed = TRUE) - # If data is not input - # Create new data and its mapping from x and y input - if (is.null(data)) { + # If data is not input, creates new data and its mapping from x and y input + if (isOfLength(data, 0)) { data <- as.data.frame(cbind(x = x, y = y)) dataMapping <- dataMapping %||% XYGDataMapping$new(x = ifnotnull(x, "x"), y = ifnotnull(y, "y"), data = data) @@ -263,15 +260,22 @@ addLine <- function(data = NULL, } dataMapping <- dataMapping %||% XYGDataMapping$new(x = x, y = y, data = data) - plotConfiguration <- plotConfiguration %||% PlotConfiguration$new(data = data, metaData = metaData, dataMapping = dataMapping) + # Update plotConfiguration if user defined aesthetics + aestheticInputs <- c("color", "shape", "linetype", "size") + aestheticInputsExpression <- parse(text = paste0( + "plotConfiguration$lines$", aestheticInputs, + " <- ", aestheticInputs, " %||% plotConfiguration$lines$", aestheticInputs + )) + eval(aestheticInputsExpression) + # If no plot, initialize empty plot plotObject <- plotObject %||% initializePlot(plotConfiguration) # If no mapping, return plot - if (is.null(dataMapping$x) && is.null(dataMapping$y)) { - warning("No mapping found, line layer was not added") + if (all(isOfLength(dataMapping$x, 0), isOfLength(dataMapping$y, 0))) { + warning("No mapping found for both x and y, line layer was not added") return(plotObject) } @@ -288,67 +292,74 @@ addLine <- function(data = NULL, # y-intercept # geom_blank is used to fill the missing aes properties # This prevents messing up the legend - if (is.null(dataMapping$x) && !is.null(dataMapping$y)) { + if (isOfLength(dataMapping$x, 0) && !isOfLength(dataMapping$y, 0)) { plotObject <- plotObject + ggplot2::geom_hline( data = mapData, - mapping = aes_string(yintercept = mapLabels$y, linetype = "legendLabels", color = "legendLabels", size = "legendLabels"), + mapping = ggplot2::aes_string(yintercept = mapLabels$y, linetype = "legendLabels", color = "legendLabels", size = "legendLabels"), show.legend = TRUE ) + ggplot2::geom_blank( data = mapData, - mapping = aes_string(shape = "legendLabels", fill = "legendLabels") + mapping = ggplot2::aes_string( + shape = "legendLabels", + fill = "legendLabels" + ), + show.legend = TRUE ) } # x-intercept - if (is.null(dataMapping$y) && !is.null(dataMapping$x)) { + if (isOfLength(dataMapping$y, 0) && !isOfLength(dataMapping$x, 0)) { plotObject <- plotObject + ggplot2::geom_vline( data = mapData, - mapping = aes_string(xintercept = mapLabels$x, linetype = "legendLabels", color = "legendLabels", size = "legendLabels"), + mapping = ggplot2::aes_string(xintercept = mapLabels$x, linetype = "legendLabels", color = "legendLabels", size = "legendLabels"), show.legend = TRUE ) + ggplot2::geom_blank( data = mapData, - mapping = aes_string(shape = "legendLabels", fill = "legendLabels") + mapping = ggplot2::aes_string( + shape = "legendLabels", + fill = "legendLabels" + ), + show.legend = TRUE ) } - if (!is.null(dataMapping$x) && !is.null(dataMapping$y)) { + # Case of a line defined by x and y + # geom_path is used instead of geom_line, + # consequently values are connected by their order of appearance and not according to x values + if (all(!isOfLength(dataMapping$x, 0), !isOfLength(dataMapping$y, 0))) { plotObject <- plotObject + ggplot2::geom_point( data = mapData, - mapping = aes_string(x = mapLabels$x, y = mapLabels$y, shape = "legendLabels", color = "legendLabels", size = "legendLabels"), + mapping = ggplot2::aes_string(x = mapLabels$x, y = mapLabels$y, shape = "legendLabels", color = "legendLabels", size = "legendLabels"), show.legend = TRUE ) + - ggplot2::geom_line( + ggplot2::geom_path( data = mapData, - mapping = aes_string(x = mapLabels$x, y = mapLabels$y, linetype = "legendLabels", color = "legendLabels", size = "legendLabels"), + mapping = ggplot2::aes_string(x = mapLabels$x, y = mapLabels$y, linetype = "legendLabels", color = "legendLabels", size = "legendLabels"), show.legend = TRUE ) + ggplot2::geom_ribbon( data = mapData, - mapping = aes_string(x = mapLabels$x, ymin = mapLabels$y, ymax = mapLabels$y, fill = "legendLabels"), + mapping = ggplot2::aes_string(x = mapLabels$x, ymin = mapLabels$y, ymax = mapLabels$y, fill = "legendLabels"), + # alpha is 0 so that line can be seen in legend + alpha = 0, show.legend = TRUE ) } + # Prepare data for merging previous and current legend newLabels <- levels(factor(mapData$legendLabels)) - legendLength <- nrow(plotObject$plotConfiguration$legend$caption) %||% 0 - newLegendProperty <- seq(legendLength + 1, legendLength + length(newLabels)) - 1 %% 6 + 1 - - # Sample LegendType properties based tlfTheme if not input - plotObject <- mergeLegend(plotObject, + # Sample LegendType properties based Theme if not input + try(plotObject <- mergeLegend(plotObject, newLabels = newLabels, - color = color %||% tlfEnv$currentTheme$aesProperties$color[newLegendProperty], - shape = shape %||% rep(-2, length(newLabels)), - size = size %||% rep(1, length(newLabels)), - linetype = linetype %||% tlfEnv$currentTheme$aesProperties$linetype[newLegendProperty], - fill = rep(NA, length(newLabels)) - ) - try(suppressMessages(plotObject <- plotObject$plotConfiguration$xAxis$setPlotAxis(plotObject))) - try(suppressMessages(plotObject <- plotObject$plotConfiguration$yAxis$setPlotAxis(plotObject))) + aestheticSelections = plotConfiguration$lines + )) + try(suppressMessages(plotObject <- setXAxis(plotObject))) + try(suppressMessages(plotObject <- setYAxis(plotObject))) return(plotObject) } @@ -438,13 +449,12 @@ addRibbon <- function(data = NULL, validateIsOfType(dataMapping, RangeDataMapping, nullAllowed = TRUE) validateIsOfType(plotConfiguration, PlotConfiguration, nullAllowed = TRUE) - # If data is not input - # Create new data and its mapping from x, ymin and ymax input - if (is.null(data)) { + # If data is not input, creates data and its mapping from x, ymin and ymax input + if (isOfLength(data, 0)) { data <- as.data.frame(cbind(x = x, ymin = ymin %||% 0, ymax = ymax %||% 0)) # y-intercept ribbon - if (is.null(x)) { + if (isOfLength(x, 0)) { # Redefine data.frame for y-intercept ribbon data <- rbind.data.frame(cbind.data.frame(x = -Inf, data), cbind.data.frame(x = Inf, data)) } @@ -456,15 +466,22 @@ addRibbon <- function(data = NULL, } dataMapping <- dataMapping %||% RangeDataMapping$new(x = x, ymin = ymin, ymax = ymax, data = data) - plotConfiguration <- plotConfiguration %||% PlotConfiguration$new(data = data, metaData = metaData, dataMapping = dataMapping) + # Update plotConfiguration if user defined aesthetics + aestheticInputs <- c("color", "fill", "linetype", "size", "alpha") + aestheticInputsExpression <- parse(text = paste0( + "plotConfiguration$ribbons$", aestheticInputs, + " <- ", aestheticInputs, " %||% plotConfiguration$ribbons$", aestheticInputs + )) + eval(aestheticInputsExpression) + # If no plot, initialize empty plot plotObject <- plotObject %||% initializePlot(plotConfiguration) # If no mapping, return plot - if (is.null(dataMapping$x) && is.null(dataMapping$ymin) && is.null(dataMapping$ymax)) { - warning("No mapping found, ribbon layer was not added") + if (all(isOfLength(dataMapping$x, 0), isOfLength(dataMapping$ymin, 0), isOfLength(dataMapping$ymax, 0))) { + warning("No mapping found for x, ymin and ymax, ribbon layer was not added") return(plotObject) } @@ -478,7 +495,6 @@ addRibbon <- function(data = NULL, } mapData$legendLabels <- caption %||% mapData$legendLabels - # Get mapping and convert labels into characters usable by aes_string mapData <- dataMapping$checkMapData(data) mapLabels <- getAesStringMapping(dataMapping) @@ -490,51 +506,30 @@ addRibbon <- function(data = NULL, mapData$legendLabels <- caption %||% mapData$legendLabels # y-intercept - if (max(is.infinite(mapData[, dataMapping$x])) == 1) { - plotObject <- plotObject + - ggplot2::geom_ribbon( - data = mapData, - mapping = ggplot2::aes_string(x = mapLabels$x, ymin = mapLabels$ymin, ymax = mapLabels$ymax, fill = "legendLabels", color = "legendLabels", size = "legendLabels", linetype = "legendLabels"), - alpha = alpha, - show.legend = TRUE - ) + - ggplot2::geom_blank( - data = mapData, - mapping = aes_string(shape = "legendLabels") - ) - } else { - plotObject <- plotObject + - ggplot2::geom_ribbon( - data = mapData, - mapping = ggplot2::aes_string(x = mapLabels$x, ymin = mapLabels$ymin, ymax = mapLabels$ymax, fill = "legendLabels", color = "legendLabels", size = "legendLabels", linetype = "legendLabels"), - alpha = alpha, - show.legend = TRUE - ) + - ggplot2::geom_blank( - data = mapData, - mapping = aes_string(shape = "legendLabels") - ) - } + plotObject <- plotObject + + ggplot2::geom_ribbon( + data = mapData, + mapping = ggplot2::aes_string(x = mapLabels$x, ymin = mapLabels$ymin, ymax = mapLabels$ymax, fill = "legendLabels"), + alpha = alpha, + show.legend = TRUE + ) + + ggplot2::geom_blank( + data = mapData, + mapping = aes_string(shape = "legendLabels", color = "legendLabels", size = "legendLabels", linetype = "legendLabels") + ) + # Prepare data for merging previous and current legend newLabels <- levels(factor(mapData$legendLabels)) - legendLength <- nrow(plotObject$plotConfiguration$legend$caption) %||% 0 - newLegendProperty <- seq(legendLength + 1, legendLength + length(newLabels)) - 1 %% 6 + 1 - - # Sample LegendType properties based tlfTheme if not input - plotObject <- mergeLegend(plotObject, + # Sample LegendType properties based Theme if not input + try(plotObject <- mergeLegend(plotObject, newLabels = newLabels, - color = color %||% tlfEnv$currentTheme$aesProperties$color[newLegendProperty], - shape = rep(-2, length(newLabels)), - size = size %||% rep(1, length(newLabels)), - linetype = linetype %||% rep("blank", length(newLabels)), - fill = fill %||% tlfEnv$currentTheme$aesProperties$fill[newLegendProperty] - ) - try(suppressMessages(plotObject <- plotObject$plotConfiguration$xAxis$setPlotAxis(plotObject))) - try(suppressMessages(plotObject <- plotObject$plotConfiguration$yAxis$setPlotAxis(plotObject))) + aestheticSelections = plotConfiguration$ribbons + )) + try(suppressMessages(plotObject <- setXAxis(plotObject))) + try(suppressMessages(plotObject <- setYAxis(plotObject))) return(plotObject) } - #' @title addErrorbar #' @param data data.frame containing the errorbar endpoints to be plotted #' @param metaData list of information on \code{data} such as \code{dimension} and \code{unit} of their variables @@ -581,15 +576,14 @@ addErrorbar <- function(data = NULL, dataMapping = NULL, plotConfiguration = NULL, plotObject = NULL) { - validateIsOfType(dataMapping, RangeDataMapping, nullAllowed = TRUE) + validateIsOfType(dataMapping, c("RangeDataMapping", "ObservedDataMapping"), nullAllowed = TRUE) validateIsOfType(plotConfiguration, PlotConfiguration, nullAllowed = TRUE) validateIsLogical(includeCap) + # validateIsIncluded(barLinetype, Linetypes, nullAllowed = TRUE) - # If data is not input - # Create new data and its mapping from x, ymin and ymax input - if (is.null(data)) { + # If data is not input, creates data and its mapping from x, ymin and ymax input + if (isOfLength(data, 0)) { data <- as.data.frame(cbind(x = x, ymin = ymin %||% 0, ymax = ymax %||% 0)) - dataMapping <- dataMapping %||% RangeDataMapping$new(x = "x", ymin = "ymin", ymax = "ymax", data = data) } # Enforce data to be a data.frame for dataMapping @@ -598,15 +592,22 @@ addErrorbar <- function(data = NULL, } dataMapping <- dataMapping %||% RangeDataMapping$new(x = x, ymin = ymin, ymax = ymax, data = data) - plotConfiguration <- plotConfiguration %||% PlotConfiguration$new(data = data, metaData = metaData, dataMapping = dataMapping) + # Update plotConfiguration if user defined aesthetics + aestheticInputs <- c("color", "linetype", "size") + aestheticInputsExpression <- parse(text = paste0( + "plotConfiguration$errorbars$", aestheticInputs, + " <- ", aestheticInputs, " %||% plotConfiguration$errorbars$", aestheticInputs + )) + eval(aestheticInputsExpression) + # If no plot, initialize empty plot plotObject <- plotObject %||% initializePlot(plotConfiguration) # If no mapping, return plot - if (is.null(dataMapping$x) || is.null(dataMapping$ymin) || is.null(dataMapping$ymax)) { - warning("No mapping found for x, ymin or ymax, errorbar layer was not added") + if (all(isOfLength(dataMapping$x, 0), isOfLength(dataMapping$ymin, 0), isOfLength(dataMapping$ymax, 0))) { + warning("No mapping found for x, ymin and ymax, error bar layer was not added") return(plotObject) } @@ -629,58 +630,74 @@ addErrorbar <- function(data = NULL, mapData$legendLabels <- getlegendLabelsCaption(plotObject) } mapData$legendLabels <- caption %||% mapData$legendLabels + legendLength <- length(unique(mapData$legendLabels)) + # Option caps allows to add an horizontal bar at the edges of the error bars if (includeCap) { plotObject <- plotObject + ggplot2::geom_errorbar( data = mapData, - mapping = aes_string( + mapping = ggplot2::aes_string( x = mapLabels$x, ymin = mapLabels$ymin, ymax = mapLabels$ymax, color = "legendLabels", - size = "legendLabels", - linetype = "legendLabels" ), - show.legend = TRUE + size = size %||% getAestheticValues(n = 1, selectionKey = plotConfiguration$errorbars$size, aesthetic = "size"), + linetype = linetype %||% getAestheticValues(n = 1, selectionKey = plotConfiguration$errorbars$linetype, aesthetic = "linetype") ) } if (!includeCap) { plotObject <- plotObject + ggplot2::geom_linerange( data = mapData, - mapping = aes_string( + mapping = ggplot2::aes_string( x = mapLabels$x, ymin = mapLabels$ymin, ymax = mapLabels$ymax, color = "legendLabels", - size = "legendLabels", - linetype = "legendLabels" ), - show.legend = TRUE + size = size %||% getAestheticValues(n = 1, selectionKey = plotConfiguration$errorbars$size, aesthetic = "size"), + linetype = linetype %||% getAestheticValues(n = 1, selectionKey = plotConfiguration$errorbars$linetype, aesthetic = "linetype") ) } + plotObject <- plotObject + + ggplot2::geom_ribbon( + data = mapData, + mapping = ggplot2::aes_string( + x = mapLabels$x, + ymin = mapLabels$ymin, + ymax = mapLabels$ymax, + fill = "legendLabels", + ), + alpha = 0, + show.legend = TRUE + ) + # Add blank lines, points and ribbon to the plot plotObject <- plotObject + ggplot2::geom_blank( data = mapData, - mapping = aes_string(shape = "legendLabels", fill = "legendLabels") + mapping = ggplot2::aes_string( + x = mapLabels$x, + y = mapLabels$ymax, + shape = "legendLabels", + color = "legendLabels", + fill = "legendLabels", + size = "legendLabels", + linetype = "legendLabels" + ) ) + # Prepare data for merging previous and current legend newLabels <- levels(factor(mapData$legendLabels)) - legendLength <- nrow(plotObject$plotConfiguration$legend$caption) %||% 0 - newLegendProperty <- seq(legendLength + 1, legendLength + length(newLabels)) - 1 %% 6 + 1 - - # Sample LegendType properties based tlfTheme if not input - plotObject <- mergeLegend(plotObject, + # Sample LegendType properties based Theme if not input + try(plotObject <- mergeLegend(plotObject, newLabels = newLabels, - color = color %||% tlfEnv$currentTheme$aesProperties$color[newLegendProperty], - shape = rep(-2, length(newLabels)), - size = size %||% rep(1, length(newLabels)), - linetype = linetype %||% tlfEnv$currentTheme$aesProperties$linetype[newLegendProperty], - fill = rep(NA, length(newLabels)) - ) - try(suppressMessages(plotObject <- plotObject$plotConfiguration$xAxis$setPlotAxis(plotObject))) - try(suppressMessages(plotObject <- plotObject$plotConfiguration$yAxis$setPlotAxis(plotObject))) + aestheticSelections = plotConfiguration$errorbars + )) + # Try is used to prevent crashes in the final plot due to ggplot2 peculiarities regarding scale functions + try(suppressMessages(plotObject <- setXAxis(plotObject))) + try(suppressMessages(plotObject <- setYAxis(plotObject))) return(plotObject) } diff --git a/R/boxwhisker-datamapping.R b/R/boxwhisker-datamapping.R index d73823e0..e2928f8a 100644 --- a/R/boxwhisker-datamapping.R +++ b/R/boxwhisker-datamapping.R @@ -43,8 +43,7 @@ BoxWhiskerDataMapping <- R6::R6Class( maxOutlierLimit = tlfStatFunctions$`Percentile75%+1.5IQR`, ...) { super$initialize(x = x, y = y, ...) - - super$groupMapping$color <- super$groupMapping$color %||% super$groupMapping$fill + self$groupMapping$color <- self$groupMapping$color %||% self$groupMapping$fill self$boxWhiskerLimits <- c(ymin, lower, middle, upper, ymax) self$outlierLimits <- c(minOutlierLimit, maxOutlierLimit) @@ -55,7 +54,7 @@ BoxWhiskerDataMapping <- R6::R6Class( #' @return A data.frame with `ymin`, `lower`, `middle`, `upper`, `ymax` variables. getBoxWhiskerLimits = function(data) { # Dummy silent variable if x is NULL - if (is.null(self$x)) { + if (isOfLength(self$x, 0)) { data$legendLabels <- factor("") } @@ -87,7 +86,7 @@ BoxWhiskerDataMapping <- R6::R6Class( getOutliers = function(data) { data <- self$checkMapData(data) # Dummy silent variable if x is NULL - if (is.null(self$x)) { + if (isOfLength(self$x, 0)) { data$legendLabels <- factor("") } diff --git a/R/boxwhisker-plot.R b/R/boxwhisker-plot.R deleted file mode 100644 index 70b61d8a..00000000 --- a/R/boxwhisker-plot.R +++ /dev/null @@ -1,44 +0,0 @@ -#' @title plotBoxWhisker -#' @param data data.frame (or list of data.frames? TO BE DISCUSSED) -#' containing the data to be used for the plot -#' @param metaData list of lists (structure TO BE DISCUSSED) -#' containing complementary information to data (e.g. unit) -#' @param dataMapping R6 class BoxWhiskerDataMapping -#' mapping of x, y axes + mapping of colorGrouping, sizeGrouping, shapeGrouping -#' @param plotConfiguration R6 class BoxWhiskerConfiguration -#' Plot Configuration defining title, subtitle, xlabel, ylabel watermark, and legend -#' @param plotObject -#' ggplot object, if null creates new plot, if not add time profile layers to ggplot -#' @description -#' plotBoxWhisker(data, metaData, dataMapping, plotConfiguration) -#' @return a ggplot graphical object -#' @export -plotBoxWhisker <- function(data, - metaData = NULL, - dataMapping = NULL, - plotConfiguration = NULL, - plotObject = NULL) { - dataMapping <- dataMapping %||% BoxWhiskerDataMapping$new() - plotConfiguration <- plotConfiguration %||% BoxWhiskerPlotConfiguration$new( - data = data, - metaData = metaData, - dataMapping = dataMapping - ) - - validateIsOfType(dataMapping, BoxWhiskerDataMapping) - validateIsOfType(plotConfiguration, BoxWhiskerPlotConfiguration) - validateIsOfType(plotObject, ggplot, nullAllowed = TRUE) - - plotObject <- plotObject %||% initializePlot(plotConfiguration) - - if(nrow(data)==0){ - warning("No data to plot in BoxWhiskers") - return(plotObject) - } - - # Add Plot Configuration layers and box whisker plots - plotObject <- plotConfiguration$addBoxWhisker(plotObject, data, metaData, dataMapping) - plotObject <- plotConfiguration$addOutliers(plotObject, data, metaData, dataMapping) - - return(plotObject) -} diff --git a/R/boxwhisker-plotconfiguration.R b/R/boxwhisker-plotconfiguration.R index 601c9d6c..7f8e4e37 100644 --- a/R/boxwhisker-plotconfiguration.R +++ b/R/boxwhisker-plotconfiguration.R @@ -4,108 +4,41 @@ BoxWhiskerPlotConfiguration <- R6::R6Class( "BoxWhiskerPlotConfiguration", inherit = PlotConfiguration, - public = list( - #' @description Create a new \code{BoxWhiskerPlotConfiguration} object + #' @description Create a new \code{PKRatioPlotConfiguration} object + #' @param ribbons `ThemeAestheticSelections` object defining properties for boxes of boxplot + #' @param points `ThemeAestheticSelections` object defining properties for outlier scatter points + #' @param outliers logical defining if outliers should be included in boxplot #' @param ... parameters inherited from \code{PlotConfiguration} - #' @return A new \code{BoxWhiskerPlotConfiguration} object - initialize = function(...) { + #' @return A new \code{PKRatioPlotConfiguration} object + initialize = function(outliers = TRUE, + ribbons = NULL, + points = NULL, + ...) { super$initialize(...) - }, - - #' @description Add a boxplot layer to a \code{ggplot} object - #' @param plotObject a \code{ggplot} object - #' @param data data.frame - #' @param metaData list of information on \code{data} - #' @param dataMapping R6 class \code{BoxWhiskerDataMapping} - #' @return A \code{ggplot} object - addBoxWhisker = function(plotObject, data, metaData, dataMapping) { - - # Get the box plot quantiles from dataMapping - mapBoxWhiskers <- dataMapping$getBoxWhiskerLimits(data) - - # Convert the mapping into characters usable by aes_string - mapLabels <- getAesStringMapping(dataMapping) - - plotObject <- plotObject + ggplot2::geom_boxplot( - data = mapBoxWhiskers, - mapping = aes_string( - x = mapLabels$x, - ymin = "ymin", - lower = "lower", - middle = "middle", - upper = "upper", - ymax = "ymax", - fill = mapLabels$fill, - color = mapLabels$color, - linetype = mapLabels$linetype, - size = mapLabels$size - ), - alpha = self$theme$aesProperties$alpha[1], - show.legend = TRUE, - stat = "identity" - ) - - plotObject <- plotObject + - ifEqual("legendLabels", mapLabels$fill, guides(fill = "none")) + - ifEqual("legendLabels", mapLabels$color, guides(color = "none")) + - ifEqual("legendLabels", mapLabels$linetype, guides(linetype = "none")) + - ifEqual("legendLabels", mapLabels$size, guides(size = "none")) - return(plotObject) - }, + validateIsOfType(ribbons, "ThemeAestheticSelections", nullAllowed = TRUE) + validateIsOfType(points, "ThemeAestheticSelections", nullAllowed = TRUE) + currentTheme <- tlfEnv$currentTheme$clone(deep = TRUE) + private$.ribbons <- ribbons %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotBoxWhisker$ribbons) + private$.points <- points %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotBoxWhisker$points) - #' @description Add a outlier points layer to a \code{ggplot} object - #' @param plotObject a \code{ggplot} object - #' @param data data.frame - #' @param metaData list of information on \code{data} - #' @param dataMapping R6 class \code{BoxWhiskerDataMapping} - #' @return A \code{ggplot} object - addOutliers = function(plotObject, data, metaData, dataMapping) { - mapOutliers <- dataMapping$getOutliers(data) - - # Convert the mapping into characters usable by aes_string - mapLabels <- getAesStringMapping(dataMapping) - - plotObject <- plotObject + - ggplot2::geom_point( - data = mapOutliers, - mapping = aes_string( - x = mapLabels$x, - y = "maxOutliers", - group = mapLabels$fill, - shape = mapLabels$shape, - color = mapLabels$color, - size = mapLabels$size - ), - show.legend = TRUE, - na.rm = TRUE, - position = position_dodge(width = 0.9) - ) + - ggplot2::geom_point( - data = mapOutliers, - mapping = aes_string( - x = mapLabels$x, - y = "minOutliers", - group = mapLabels$fill, - shape = mapLabels$shape, - color = mapLabels$color, - size = mapLabels$size - ), - show.legend = TRUE, - na.rm = TRUE, - position = position_dodge(width = 0.9) - ) - # position = position_dodge(width = 0.9) aligns points with centers of boxplots - # no matter the number of groups, the value of 0.9 will be always fix, - # otherwise, points won't be centered anymore - - plotObject <- plotObject + - ifEqual("legendLabels", mapLabels$shape, guides(shape = "none")) + - ifEqual("legendLabels", mapLabels$color, guides(color = "none")) + - ifEqual("legendLabels", mapLabels$size, guides(size = "none")) - - return(plotObject) + validateIsLogical(outliers) + private$.outliers <- outliers + } + ), + active = list( + #' @field outliers logical defining if outliers should be included in boxplot + outliers = function(value) { + if (missing(value)) { + return(private$.outliers) + } + validateIsLogical(value, nullAllowed = TRUE) + private$.outliers <- value %||% private$.outliers + return(invisible()) } + ), + private = list( + .outliers = NULL ) ) diff --git a/R/data-description.R b/R/data-description.R deleted file mode 100644 index 9cf54318..00000000 --- a/R/data-description.R +++ /dev/null @@ -1,46 +0,0 @@ -#' Test dataset of PK Ratios for 40 subjects. -#' -#' -#' @format A data frame with 40 rows and 11 variables: -#' \describe{ -#' \item{IndividualID}{} -#' \item{Population}{} -#' \item{Gender}{} -#' \item{Age}{} -#' \item{Compound}{} -#' \item{Dose}{} -#' \item{Organ}{} -#' \item{Compartment}{} -#' \item{Simulated}{} -#' \item{Observed}{} -#' \item{Ratio}{} -#' } -"pkRatioData" - -#' Test metaData for pkRatioData -#' -#' -#' @format A list of lists for the 11 variables of pkRatioData -#' Each variable includes: -#' \describe{ -#' \item{unit}{} -#' \item{dimension}{} -#' } -"pkRatioMetaData" - -#' Test dataset for time profiles with 3 subjects. -#' -#' @format A data frame with 12 rows and 10 variables: -#' \describe{ -#' \item{Time}{} -#' \item{IndividualID}{} -#' \item{Population}{} -#' \item{Gender}{} -#' \item{Age}{} -#' \item{Compound}{} -#' \item{Dose}{} -#' \item{Organ}{} -#' \item{Compartment}{} -#' \item{Simulated}{} -#' } -"timeProfileDataFrame" diff --git a/R/datamapping-range.R b/R/datamapping-range.R index 7e3de807..e70f0b82 100644 --- a/R/datamapping-range.R +++ b/R/datamapping-range.R @@ -48,8 +48,8 @@ RangeDataMapping <- R6::R6Class( #' @description Check that \code{data} variables include map variables #' @param data data.frame to check #' @param metaData list containing information on \code{data} - #' @return A data.frame with map and \code{defaultAes} variables. - #' Dummy variable \code{defaultAes} is necessary to allow further modification of plots. + #' @return A data.frame with map and \code{legendLabels} variables. + #' Dummy variable \code{legendLabels} is necessary to allow further modification of plots. checkMapData = function(data, metaData = NULL) { validateMapping(self$x, data, nullAllowed = TRUE) if (isOfType(self$ymin, "character")) { diff --git a/R/ddiratio-datamapping.R b/R/ddiratio-datamapping.R index 44495991..7d3bb737 100644 --- a/R/ddiratio-datamapping.R +++ b/R/ddiratio-datamapping.R @@ -3,28 +3,29 @@ #' @export DDIRatioDataMapping <- R6::R6Class( "DDIRatioDataMapping", - inherit = XYGDataMapping, - public = list( - #' @field ddiRatioLines numeric vector of ratio limits to plot - ddiRatioLines = NULL, - #' @field deltaGuest numeric value of Guest et al ratio limits - deltaGuest = NULL, - #' @field range 2 elements vector of x limits - range = NULL, + inherit = PKRatioDataMapping, + public = list( + #' @field comparisonType Options for comparison from enum `MappingComparisonTypes` + comparisonType = NULL, + #' @field minRange Mininmum range for guest and ratio lines + minRange = NULL, #' @description Create a new \code{DDIRatioDataMapping} object - #' @param ddiRatioValues list of values for ratio and guest limits to plot - #' @param range 2 elements vector of x limits - #' @param ... parameters inherited from \code{XYGDataMapping} + #' @param comparisonType Options for comparison from enum `DDIComparisonTypes` + #' @param minRange Mininmum range for guest and ratio lines + #' @param lines list of ratio and guest limits to plot as horizontal lines + #' @param ... parameters inherited from \code{PKRatioDataMapping} #' @return A new \code{DDIRatioDataMapping} object - initialize = function(ddiRatioValues = DefaultDataMappingValues$ddiRatio, - range = c(1e-2, 1e2), + initialize = function(comparisonType = DDIComparisonTypes$obsVsPred, + minRange = c(1e-2, 1e2), + lines = DefaultDataMappingValues$ddiRatio, ...) { + validateIsIncluded(comparisonType, MappingComparisonTypes) super$initialize(...) - self$ddiRatioLines <- c(ddiRatioValues$ddiRatio1, ddiRatioValues$ddiRatio2) - self$deltaGuest <- ddiRatioValues$guestLine - self$range <- range + self$lines <- lines + self$comparisonType <- comparisonType + self$minRange <- minRange }, #' @description Create a data.frame with DDI ratio limits @@ -37,7 +38,7 @@ DDIRatioDataMapping <- R6::R6Class( x <- 10^(seq(log10(xmin), log10(xmax), 0.01)) - y <- x * self$ddiRatioLines[1] + y <- x * self$lines$ddiRatioLines[1] ymax <- x * self$ddiRatioLines[2] ymin <- x * self$ddiRatioLines[3] diff --git a/R/ddiratio-plotconfiguration.R b/R/ddiratio-plotconfiguration.R index 3025c8d7..c4d66bf6 100644 --- a/R/ddiratio-plotconfiguration.R +++ b/R/ddiratio-plotconfiguration.R @@ -3,22 +3,5 @@ #' @export DDIRatioPlotConfiguration <- R6::R6Class( "DDIRatioPlotConfiguration", - inherit = PlotConfiguration, - public = list( - #' @field ddiRatioCaption list of properties for DDI ratio plot specific features - ddiRatioCaption = NULL, - - #' @description Create a new \code{DDIRatioPlotConfiguration} object - #' @param ddiRatioCaption list of properties for DDI ratio plot specific features - #' @param ... parameters inherited from \code{PlotConfiguration} - #' @return A new \code{DDIRatioPlotConfiguration} object - initialize = function(ddiRatioCaption = getDefaultCaptionFor("ddiRatio"), - ...) { - validateIsOfType(ddiRatioCaption, "data.frame") - validateIsIncluded(names(ddiRatioCaption), CaptionProperties) - super$initialize(...) - - self$ddiRatioCaption <- ddiRatioCaption - } - ) + inherit = PKRatioPlotConfiguration ) diff --git a/R/font.R b/R/font.R index 789e9139..dcbcb84f 100644 --- a/R/font.R +++ b/R/font.R @@ -1,54 +1,53 @@ #' @title Font -#' @description R6 class defining \code{size}, \code{color}, \code{fontFamily}, \code{fontFace} of font -#' @include utils.R +#' @description R6 class defining font properties +#' @field size numeric defining the size of font +#' @field color character defining the color of font +#' @field fontFamily character defining the family of font +#' @field fontFace character defining the face of font +#' @field angle numeric defining the angle of font #' @export Font <- R6::R6Class( "Font", public = list( - #' @field size numeric size of font size = 12, - #' @field color character color of font color = "black", - #' @field fontFamily character family of font fontFamily = "", - #' @field fontFace character face of font fontFace = "plain", + angle = 0, #' @description Create a new \code{Font} object. #' Default font properties are defined directly in the object field, #' so `NULL` input is allowed will lead to default properties. - #' @param size numeric size of font - #' @param color character color of font - #' @param fontFamily character family of font - #' @param fontFace character face of font + #' @param size numeric defining the size of font + #' @param color character defining the color of font + #' @param fontFamily character defining the family of font + #' @param fontFace character defining the face of font + #' @param angle numeric defining the angle of font #' @return A new \code{Font} object initialize = function(size = NULL, color = NULL, fontFamily = NULL, - fontFace = NULL) { - self$size <- size %||% self$size - self$color <- color %||% self$color - self$fontFamily <- fontFamily %||% self$fontFamily - self$fontFace <- fontFace %||% self$fontFace - }, + fontFace = NULL, + angle = NULL) { + validateIsString(c(color, fontFamily, fontFace), nullAllowed = TRUE) + validateIsNumeric(c(size, angle), nullAllowed = TRUE) - #' @description Print \code{Font} properties. - print = function() { - cat("size:", self$size, "\n", sep = " ") - cat("color:", self$color, "\n", sep = " ") - cat("fontFamily:", self$fontFamily, "\n", sep = " ") - cat("fontFace:", self$fontFace, "\n", sep = " ") - invisible(self) + fieldNames <- c("size", "color", "fontFace", "fontFamily", "angle") + setFontExpression <- parse(text = paste0("self$", fieldNames, " <- ", fieldNames, " %||% self$", fieldNames)) + eval(setFontExpression) }, - #' @description Create an `element_text` for ggplot with \code{Font} properties. - #' @return An `element_text` for ggplot with \code{Font} properties. - setFont = function() { - element_text( + #' @description Create a `ggplot2::element_text` directly convertible by `ggplot2::theme`. + #' @return An `element_text` object. + createPlotFont = function() { + ggplot2::element_text( colour = self$color, size = self$size, face = self$fontFace, - family = self$fontFamily + # TO DO: check why I get the following error messages + # "font family not found in Windows font database" + #family = self$fontFamily, + angle = self$angle ) } ) diff --git a/R/histogram-datamapping.R b/R/histogram-datamapping.R index 00dbd1d0..54b8f924 100644 --- a/R/histogram-datamapping.R +++ b/R/histogram-datamapping.R @@ -6,31 +6,41 @@ HistogramDataMapping <- R6::R6Class( "HistogramDataMapping", inherit = XYGDataMapping, public = list( - #' @field verticalLineGroupings R6 class \code{Grouping} variable - verticalLineGroupings = NULL, - #' @field verticalLineFunctionNames Vector of function name - #' to be indicated in captions of the histogram - verticalLineFunctionNames = NULL, - #' @field verticalLineFunctions List of functions calculated on \code{data} - #' to obtain vertical lines on the histogram - verticalLineFunctions = NULL, + #' @field stack logical defining if histogram bars should be stacked + stack = NULL, + #' @field bins number of bins or binning values/methods passed on `ggplot2::geom_histogram` + bins = NULL, + #' @field lines values or functions to define vertical lines + lines = NULL, + #' @field fitNormalDist logical defining if a normal distribution should be fitted + fitNormalDist = NULL, + #' @field fitDensity logical defining if a density distribution function should be fitted + fitDensity = NULL, #' @description Create a new \code{HistogramDataMapping} object - #' @param verticalLineGroupings R6 class \code{Grouping} variable - #' @param verticalLineFunctionNames Vector of function name to be indicated in captions of the histogram - #' Default value uses `mean` and `median`. - #' @param verticalLineFunctions List of functions calculated on \code{data} - #' Default value uses `mean` and `median`. + #' @param stack logical defining if histogram bars should be stacked + #' @param bins argument passed on `ggplot2::geom_histogram` + #' @param lines values or functions to define vertical lines + #' @param fitNormalDist logical defining if a normal distribution should be fitted + #' @param fitDensity logical defining if a density distribution should be fitted #' @param ... parameters inherited from \code{XYGDataMapping} #' @return A new \code{HistogramDataMapping} object - initialize = function(verticalLineGroupings = NULL, - verticalLineFunctionNames = c("mean", "median"), - verticalLineFunctions = c(mean, median), + initialize = function(stack = FALSE, + bins = NULL, + lines = DefaultDataMappingValues$histogram, + fitNormalDist = FALSE, + fitDensity = FALSE, ...) { super$initialize(...) - self$verticalLineGroupings <- verticalLineGroupings - self$verticalLineFunctionNames <- verticalLineFunctionNames - self$verticalLineFunctions <- verticalLineFunctions + validateIsLogical(stack) + validateIsLogical(fitNormalDist) + validateIsLogical(fitDensity) + self$stack <- stack + self$fitNormalDist <- fitNormalDist + self$fitDensity <- fitDensity + + self$bins <- bins + self$lines <- lines } ) ) diff --git a/R/histogram-plotconfiguration.R b/R/histogram-plotconfiguration.R index 1d2c4b91..c10ab84b 100644 --- a/R/histogram-plotconfiguration.R +++ b/R/histogram-plotconfiguration.R @@ -6,62 +6,24 @@ HistogramPlotConfiguration <- R6::R6Class( inherit = PlotConfiguration, public = list( - #' @field mapData data.frame after dataMapping - mapData = NULL, - #' @field bins numeric vector of bin edges - bins = NULL, - #' @field binWidth numeric value of bin width - binWidth = NULL, - - #' @description Create a new \code{TimeProfilePlotConfiguration} object - #' @param bins numeric vector of bin edges - #' @param binWidth numeric value of bin width + + #' @description Create a new \code{HistogramPlotConfiguration} object + #' @param lines `ThemeAestheticSelections` object defining properties for vertical lines + #' @param ribbons `ThemeAestheticSelections` object defining properties for histogram + #' @param ylabel Histograms default display is "Count" #' @param ... parameters inherited from \code{PlotConfiguration} #' @return A new \code{TimeProfilePlotConfiguration} object - initialize = function(binWidth = NULL, - bins = NULL, + initialize = function(lines = NULL, + ribbons = NULL, + ylabel = "Count", ...) { - super$initialize(...) - - self$binWidth <- binWidth - self$bins <- bins %||% tlfEnv$defaultAggregation$bins - }, + super$initialize(ylabel = ylabel, ...) - #' @description Add histogram as histogram layer to a \code{ggplot} object - #' @param plotObject \code{ggplot} object - #' @param data data.frame - #' @param metaData list of information on \code{data} - #' @param dataMapping R6 class \code{HistogramDataMapping} - #' @param bins numeric vector of bin edges - #' @param binWidth numeric value of bin width - #' @return A \code{ggplot} object with histogram - addHistograms = function(plotObject, - data, - metaData = NULL, - dataMapping, - binWidth = NULL, bins = NULL) { - binWidth <- binWidth %||% self$binWidth - bins <- bins %||% self$bins - - mapData <- dataMapping$checkMapData(data, metaData) - - # Convert the mapping into characters usable by aes_string - mapLabels <- getAesStringMapping(dataMapping) - - plotObject <- plotObject + ggplot2::geom_histogram( - data = mapData, - mapping = ggplot2::aes_string(x = mapLabels$x, fill = mapLabels$fill, color = mapLabels$fill), - show.legend = TRUE, - binwidth = binWidth, - bins = bins, - alpha = self$theme$aesProperties$alpha[1] - ) - - # If no mapping defined, remove dummy aesthetic label from the legend - plotObject <- plotObject + - ifEqual("legendLabels", mapLabels$fill, guides(fill = "none", color="none")) - - return(plotObject) + validateIsOfType(lines, "ThemeAestheticSelections", nullAllowed = TRUE) + validateIsOfType(ribbons, "ThemeAestheticSelections", nullAllowed = TRUE) + currentTheme <- tlfEnv$currentTheme$clone(deep = TRUE) + private$.lines <- lines %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotHistogram$lines) + private$.ribbons <- ribbons %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotHistogram$ribbons) }, #' @description Add statistics as line layer to a \code{ggplot} object diff --git a/R/label.R b/R/label.R index df656c10..cd5606b1 100644 --- a/R/label.R +++ b/R/label.R @@ -4,68 +4,69 @@ Label <- R6::R6Class( "Label", public = list( - #' @field text character text of label - text = NULL, - #' @field font R6 class \code{Font} object - font = NULL, - #' @description Create a new \code{Label} object. - #' @param text character text of label - #' @param font R6 class \code{Font} object - #' @param size numeric size of font - #' @param color character color of font - #' @param fontFamily character family of font - #' @param fontFace character face of font + #' @param text character text of the \code{Label} object + #' @param font \code{Font} object defining the font of the `Label` object + #' @param size numeric defining the size of the `Label` object + #' @param color character defining the color of the `Label` object + #' @param fontFamily character defining the font family of the `Label` object + #' @param fontFace character defining the font face of the `Label` object + #' @param angle numeric defining the angle of the `Label` object #' @return A new \code{Label} object initialize = function(text = "", font = NULL, color = NULL, size = NULL, fontFace = NULL, - fontFamily = NULL) { + fontFamily = NULL, + angle = NULL) { + validateIsNumeric(c(as.numeric(angle), as.numeric(size)), nullAllowed = TRUE) + validateIsString(c(color, fontFace, fontFamily), nullAllowed = TRUE) + validateIsOfType(font, "Font", nullAllowed = TRUE) + self$text <- text - validateEitherOrNullInput( - list("font" = font), - list( - "color" = color, - "size" = size, - "fontFace" = fontFace, - "fontFamily" = fontFamily - ) - ) + self$font <- font %||% Font$new() - self$font <- font %||% Font$new( - color = color, - size = size, - fontFace = fontFace, - fontFamily = fontFamily - ) - validateIsOfType(self$font, Font) + # If font properties are explicitely written, they will overwrite the properties of input Font + fieldNames <- c("size", "color", "fontFace", "fontFamily", "angle") + setFontExpression <- parse(text = paste0("self$font$", fieldNames, " <- ", fieldNames, " %||% self$font$", fieldNames)) + eval(setFontExpression) }, - #' @description Print \code{Label} properties. - print = function() { - cat("text:", self$text, "\n", sep = " ") - cat("font color:", self$font$color, "\n", sep = " ") - cat("font size:", self$font$size, "\n", sep = " ") - cat("font family:", self$font$fontFamily, "\n", sep = " ") - cat("font face:", self$font$fontFace, "\n", sep = " ") - invisible(self) + #' @description Create a `ggplot2::element_text` directly convertible by `ggplot2::theme`. + #' @return An `element_text` or `element_blank`object. + createPlotFont = function() { + if (isOfLength(self$text, 0)) { + return(ggplot2::element_blank()) + } + return(self$font$createPlotFont()) + } + ), + active = list( + #' @field text character text of the label + text = function(value) { + if (missing(value)) { + return(private$.text) + } + validateIsString(value, nullAllowed = TRUE) + private$.text <- value + return(invisible()) }, - - #' @description Set font properties of Label - #' @param size numeric size of font - #' @param color character color of font - #' @param fontFamily character family of font - #' @param fontFace character face of font - setFontProperties = function(color = self$font$color, - size = self$font$size, - fontFamily = self$font$fontFamily, - fontFace = self$font$fontFace) { - self$font$color <- color - self$font$size <- size - self$font$fontFamily <- fontFamily - self$font$fontFace <- fontFace + #' @field font \code{Font} object + font = function(value) { + if (missing(value)) { + return(private$.font) + } + validateIsOfType(value, "Font", nullAllowed = TRUE) + private$.font <- value %||% Font$new() + # Ensures that size and angle are numeric + private$.font$size <- as.numeric(private$.font$size) + private$.font$angle <- as.numeric(private$.font$angle) + return(invisible()) } + ), + private = list( + .text = NULL, + .font = NULL ) ) diff --git a/R/messages.R b/R/messages.R index a3327037..212807c3 100644 --- a/R/messages.R +++ b/R/messages.R @@ -51,5 +51,9 @@ messages <- list( "Conflicting inputs provided. Inputs '", paste0(eitherInput, collapse = ", "), "' and '", paste0(orInput, collapse = ", "), "' can't be provided at the same time." ) + }, + + errorNrowData = function(plotName) { + paste0("nrow(data)=0. ", plotName, " layer could not be added.") } ) diff --git a/R/obs-vs-pred-datamapping.R b/R/obs-vs-pred-datamapping.R index d8398f8a..4e8640d9 100644 --- a/R/obs-vs-pred-datamapping.R +++ b/R/obs-vs-pred-datamapping.R @@ -3,54 +3,32 @@ #' @export ObsVsPredDataMapping <- R6::R6Class( "ObsVsPredDataMapping", - inherit = XYGDataMapping, + inherit = ObservedDataMapping, public = list( - #' @field obsVsPredValues numeric vector of limits to plot - obsVsPredValues = NULL, - #' @field lloq numeric value of lower limit of quantification - lloq = NULL, + #' @field lines list of lines to plot + lines = NULL, + #' @field minRange Mininmum range for the lines + minRange = NULL, #' @field smoother regression function name smoother = NULL, - #' @field range 2 elements vector of x limits - range = NULL, #' @description Create a new \code{ObsVsPredDataMapping} object - #' @param obsVsPredValues list of values for obs vs pred plot - #' @param lloq numeric value of lower limit of quantification + #' @param minRange Mininmum range for guest and ratio lines + #' @param lines list of lines to plot #' @param smoother smoother function or parameter #' To map a loess smoother to the plot, use `smoother`="loess" - #' @param range 2 elements vector of x limits #' @param ... parameters inherited from \code{XYGDataMapping} #' @return A new \code{ObsVsPredDataMapping} object - initialize = function(obsVsPredValues = DefaultDataMappingValues$obsVsPred, - lloq = NULL, - range = NULL, + initialize = function(lines = DefaultDataMappingValues$obsVsPred, + minRange = NULL, smoother = NULL, ...) { + validateIsIncluded(smoother, c("lm", "loess"), nullAllowed = TRUE) + super$initialize(...) - self$obsVsPredValues <- obsVsPredValues - self$lloq <- lloq + self$lines <- lines self$smoother <- smoother - self$range <- range - }, - - #' @description Create a data.frame with of limits to plot - #' This data.frame is necessary in case if log-log plots as - #' \code{geom_abline} doesn't work properly in log scale - #' @param data data.frame of data - #' @return A data.frame - getObsVsPredLines = function(data) { - xmin <- ifnotnull(self$range, min(self$range), 0.8 * min(data[, self$x])) - xmax <- ifnotnull(self$range, max(self$range), 1.2 * max(data[, self$x])) - - x <- seq(xmin, xmax, 0.01) - if (xmin > 0) { - 10^(seq(log10(xmin), log10(xmax), 0.01)) - } - y <- x * self$obsVsPredValues$`y=x` - - obsVsPredLines <- data.frame(x = x, y = y) - return(obsVsPredLines) + self$minRange <- minRange } ) ) diff --git a/R/obs-vs-pred-plotconfiguration.R b/R/obs-vs-pred-plotconfiguration.R index d2f4e5fd..509b51f4 100644 --- a/R/obs-vs-pred-plotconfiguration.R +++ b/R/obs-vs-pred-plotconfiguration.R @@ -5,86 +5,25 @@ ObsVsPredPlotConfiguration <- R6::R6Class( "ObsVsPredPlotConfiguration", inherit = PlotConfiguration, public = list( - #' @field obsVsPredCaption list of properties for obs vs pred plot specific features - obsVsPredCaption = NULL, - #' @description Create a new \code{ObsVsPredPlotConfiguration} object - #' @param obsVsPredCaption list of properties for DDI ratio plot specific features + #' @param lines `ThemeAestheticSelections` object defining properties for lines + #' @param points `ThemeAestheticSelections` object defining properties for scatter points + #' @param errorbars `ThemeAestheticSelections` object defining properties for error bars #' @param ... parameters inherited from \code{PlotConfiguration} - #' @return A new \code{obsVsPredProperties} object - initialize = function(obsVsPredCaption = getDefaultCaptionFor("obsVsPred"), + #' @return A new \code{ObsVsPredPlotConfiguration} object + initialize = function(lines = NULL, + points = NULL, + errorbars = NULL, ...) { - validateIsOfType(obsVsPredCaption, "data.frame") - validateIsIncluded(names(obsVsPredCaption), CaptionProperties) super$initialize(...) - self$obsVsPredCaption <- obsVsPredCaption - }, - - #' @description Add smoother layer to a \code{ggplot} object - #' @param plotObject \code{ggplot} object - #' @param data data.frame - #' @param metaData list of information on \code{data} - #' @param dataMapping R6 class \code{ObsVsPredDataMapping} - #' @return A \code{ggplot} object with smoother - addSmoother = function(plotObject, data, metaData, dataMapping) { - # Check if mapping is included in the data - # Add the group mapping and aesthtics variables in the data.frame - mapData <- dataMapping$checkMapData(data, metaData) - - # Convert the mapping into characters usable by aes_string - mapLabels <- getAesStringMapping(dataMapping) - - plotObject <- plotObject + geom_smooth( - data = mapData, - mapping = aes_string( - x = mapLabels$x, - y = mapLabels$y, - color = mapLabels$color, - linetype = mapLabels$linetype, - size = mapLabels$size - ), - method = dataMapping$smoother, - se = FALSE, - show.legend = TRUE - ) - return(plotObject) - }, - - #' @description Add obs vs pred as scatter layer to a \code{ggplot} object - #' @param plotObject \code{ggplot} object - #' @param data data.frame - #' @param metaData list of information on \code{data} - #' @param dataMapping R6 class \code{ObsVsPredDataMapping} - #' @return A \code{ggplot} object - addObsVsPred = function(plotObject, data, metaData, dataMapping) { - - # Check if mapping is included in the data - # Add the group mapping and aesthtics variables in the data.frame - mapData <- dataMapping$checkMapData(data, metaData) - - # Convert the mapping into characters usable by aes_string - mapLabels <- getAesStringMapping(dataMapping) - - plotObject <- plotObject + geom_point( - data = mapData, - mapping = aes_string( - x = mapLabels$x, - y = mapLabels$y, - color = mapLabels$color, - shape = mapLabels$shape, - size = mapLabels$size - ), - show.legend = TRUE - ) - - # If no mapping defined, remove dummy aesthetic label from the legend - plotObject <- plotObject + - ifEqual("defaultAes", mapLabels$color, guides(color = "none")) + - ifEqual("defaultAes", mapLabels$shape, guides(shape = "none")) + - ifEqual("defaultAes", mapLabels$size, guides(size = "none")) - - return(plotObject) + validateIsOfType(lines, "ThemeAestheticSelections", nullAllowed = TRUE) + validateIsOfType(points, "ThemeAestheticSelections", nullAllowed = TRUE) + validateIsOfType(errorbars, "ThemeAestheticSelections", nullAllowed = TRUE) + currentTheme <- tlfEnv$currentTheme$clone(deep = TRUE) + private$.lines <- lines %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotObsVsPred$lines) + private$.points <- points %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotObsVsPred$points) + private$.errorbars <- errorbars %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotObsVsPred$errorbars) } ) ) diff --git a/R/observed-data-mapping.R b/R/observed-data-mapping.R new file mode 100644 index 00000000..3ba9a767 --- /dev/null +++ b/R/observed-data-mapping.R @@ -0,0 +1,69 @@ +#' @title ObservedDataMapping +#' @description R6 class for mapping \code{x}, \code{y}, of observed data for a time profile plot +#' @export +ObservedDataMapping <- R6::R6Class( + "ObservedDataMapping", + inherit = XYGDataMapping, + public = list( + #' @field lloq mapping lower limit of quantitation variable + lloq = NULL, + #' @field uncertainty mapping error bars around scatter points + uncertainty = NULL, + #' @field mdv mapping missing dependent variable + mdv = NULL, + #' @field ymin mapping error bars around scatter points + ymin = "ymin", + #' @field ymax mapping error bars around scatter points + ymax = "ymax", + + #' @description Create a new \code{PKRatioDataMapping} object + #' @param lloq mapping lower limit of quantitation variable + #' @param uncertainty mapping error bars around scatter points + #' @param mdv mapping missing dependent variable + #' @param ... parameters inherited from \code{XYGDataMapping} + #' @return A new \code{PKRatioDataMapping} object + initialize = function(lloq = NULL, + uncertainty = NULL, + mdv = NULL, + ...) { + validateIsString(lloq, nullAllowed = TRUE) + validateIsString(uncertainty, nullAllowed = TRUE) + validateIsString(mdv, nullAllowed = TRUE) + super$initialize(...) + self$lloq <- lloq + self$uncertainty <- uncertainty + self$mdv <- mdv + }, + + #' @description Check that \code{data} variables include map variables + #' @param data data.frame to check + #' @param metaData list containing information on \code{data} + #' @return A data.frame with map and \code{defaultAes} variables. + #' Dummy variable \code{defaultAes} is necessary to allow further modification of plots. + checkMapData = function(data, metaData = NULL) { + validateIsOfType(data, "data.frame") + validateIsIncluded(self$uncertainty, names(data), nullAllowed = TRUE) + validateIsIncluded(self$lloq, names(data), nullAllowed = TRUE) + validateIsIncluded(self$mdv, names(data), nullAllowed = TRUE) + + mapData <- super$checkMapData(data, metaData) + # Add lloq data + if (!isOfLength(self$lloq, 0)) { + mapData[, self$lloq] <- data[, self$lloq] + mapData$lloq <- data[, self$lloq] + } + # ymin and ymax for uncertainty error bars + # This may change depending of how we want to include options + if (!isOfLength(self$uncertainty, 0)) { + mapData[, self$uncertainty] <- data[, self$uncertainty] + mapData$ymax <- data[, self$y] + data[, self$uncertainty] + mapData$ymin <- data[, self$y] - data[, self$uncertainty] + } + # MDV is a Nonmem notation in which values with MDV==1 are removed + if (!isOfLength(self$mdv, 0)) { + mapData <- mapData[!as.logical(data[, self$mdv]), ] + } + return(mapData) + } + ) +) diff --git a/R/pkratio-datamapping.R b/R/pkratio-datamapping.R index 6bf21ea6..0cab8d39 100644 --- a/R/pkratio-datamapping.R +++ b/R/pkratio-datamapping.R @@ -1,21 +1,45 @@ #' @title PKRatioDataMapping -#' @description R6 class for mapping \code{x}, \code{y}, \code{GroupMapping} and \code{pkRatioLines} variables to \code{data} +#' @description R6 class for mapping \code{x}, \code{y}, \code{GroupMapping} and pkRatio \code{lines} variables to \code{data} #' @export PKRatioDataMapping <- R6::R6Class( "PKRatioDataMapping", inherit = XYGDataMapping, public = list( - #' @field pkRatioValues numeric vector of ratio limits to plot - pkRatioValues = NULL, + #' @field lines list of ratio limits to plot as horizontal lines + lines = NULL, + #' @field uncertainty mapping error bars around scatter points + uncertainty = NULL, #' @description Create a new \code{PKRatioDataMapping} object - #' @param pkRatioValues numeric vector of ratio limits to plot + #' @param lines list of ratio limits to plot as horizontal lines + #' @param uncertainty mapping error bars around scatter points #' @param ... parameters inherited from \code{XYGDataMapping} #' @return A new \code{PKRatioDataMapping} object - initialize = function(pkRatioValues = DefaultDataMappingValues$pkRatio, + initialize = function(lines = DefaultDataMappingValues$pkRatio, + uncertainty = NULL, ...) { + validateIsString(uncertainty, nullAllowed = TRUE) super$initialize(...) - self$pkRatioValues <- pkRatioValues + self$lines <- lines + self$uncertainty <- uncertainty + }, + + #' @description Check that \code{data} variables include map variables + #' @param data data.frame to check + #' @param metaData list containing information on \code{data} + #' @return A data.frame with map and \code{defaultAes} variables. + #' Dummy variable \code{defaultAes} is necessary to allow further modification of plots. + checkMapData = function(data, metaData = NULL) { + validateIsOfType(data, "data.frame") + validateMapping(self$uncertainty, data, nullAllowed = TRUE) + mapData <- super$checkMapData(data, metaData) + # This may change depending of how we want to include options + if (!isOfLength(self$uncertainty, 0)) { + mapData$ymax <- data[, self$y] + data[, self$uncertainty] + mapData$ymin <- data[, self$y] - data[, self$uncertainty] + } + self$data <- mapData + return(mapData) } ) ) diff --git a/R/pkratio-plotconfiguration.R b/R/pkratio-plotconfiguration.R index 0f6a1e0b..66ee5eec 100644 --- a/R/pkratio-plotconfiguration.R +++ b/R/pkratio-plotconfiguration.R @@ -5,20 +5,26 @@ PKRatioPlotConfiguration <- R6::R6Class( "PKRatioPlotConfiguration", inherit = PlotConfiguration, public = list( - #' @field pkRatioCaption list of properties for PK ratio plot specific features - pkRatioCaption = NULL, #' @description Create a new \code{PKRatioPlotConfiguration} object - #' @param pkRatioCaption list of properties for PK ratio plot specific features + #' @param lines `ThemeAestheticSelections` object defining properties for PK ratio horizontal lines + #' @param points `ThemeAestheticSelections` object defining properties for PK ratio scatter points + #' @param errorbars `ThemeAestheticSelections` object defining properties for PK ratio error bars #' @param ... parameters inherited from \code{PlotConfiguration} #' @return A new \code{PKRatioPlotConfiguration} object - initialize = function(pkRatioCaption = getDefaultCaptionFor("pkRatio"), + initialize = function(lines = NULL, + points = NULL, + errorbars = NULL, ...) { - validateIsOfType(pkRatioCaption, "data.frame") - validateIsIncluded(names(pkRatioCaption), CaptionProperties) super$initialize(...) - self$pkRatioCaption <- pkRatioCaption + validateIsOfType(lines, "ThemeAestheticSelections", nullAllowed = TRUE) + validateIsOfType(points, "ThemeAestheticSelections", nullAllowed = TRUE) + validateIsOfType(errorbars, "ThemeAestheticSelections", nullAllowed = TRUE) + currentTheme <- tlfEnv$currentTheme$clone(deep = TRUE) + private$.lines <- lines %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotPKRatio$lines) + private$.points <- points %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotPKRatio$points) + private$.errorbars <- errorbars %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotPKRatio$errorbars) } ) ) diff --git a/R/plot-boxwhisker.R b/R/plot-boxwhisker.R new file mode 100644 index 00000000..4bf09e23 --- /dev/null +++ b/R/plot-boxwhisker.R @@ -0,0 +1,160 @@ +#' @title plotBoxWhisker +#' @param data data.frame (or list of data.frames? TO BE DISCUSSED) +#' containing the data to be used for the plot +#' @param metaData list of lists (structure TO BE DISCUSSED) +#' containing complementary information to data (e.g. unit) +#' @param outliers logical defining if outliers should be included in boxplot +#' @param dataMapping `BoxWhiskerDataMapping` object +#' mapping of x, y axes + mapping of colorGrouping, sizeGrouping, shapeGrouping +#' @param plotConfiguration `BoxWhiskerConfiguration` object +#' Plot Configuration defining title, subtitle, xlabel, ylabel watermark, and legend +#' @param plotObject +#' ggplot object, if null creates new plot, if not add time profile layers to ggplot +#' @description +#' plotBoxWhisker(data, metaData, dataMapping, plotConfiguration) +#' @return a ggplot graphical object +#' @export +plotBoxWhisker <- function(data, + metaData = NULL, + outliers = NULL, + dataMapping = NULL, + plotConfiguration = NULL, + plotObject = NULL) { + dataMapping <- dataMapping %||% BoxWhiskerDataMapping$new(data = data) + plotConfiguration <- plotConfiguration %||% BoxWhiskerPlotConfiguration$new( + data = data, + metaData = metaData, + dataMapping = dataMapping + ) + + validateIsOfType(dataMapping, "BoxWhiskerDataMapping") + validateIsOfType(plotConfiguration, "BoxWhiskerPlotConfiguration") + validateIsOfType(data, "data.frame") + validateIsOfType(plotObject, "ggplot", nullAllowed = TRUE) + + # Overwrites plotConfiguration$outliers if outliers is not null + validateIsLogical(outliers, nullAllowed = TRUE) + plotConfiguration$outliers <- outliers + + plotObject <- plotObject %||% initializePlot(plotConfiguration) + + if (nrow(data) == 0) { + warning(messages$errorNrowData("box whisker plot")) + return(plotObject) + } + + # Add Plot Configuration layers and box whisker plots + plotObject <- addBoxWhisker(data, metaData, dataMapping, plotConfiguration, plotObject) + if (plotConfiguration$outliers) { + plotObject <- addOutliers(data, metaData, dataMapping, plotConfiguration, plotObject) + } + return(plotObject) +} + +#' @title addBoxWhisker +#' @description Add layer of boxes and whiskers to a \code{ggplot} object +#' @param data data.frame +#' @param metaData list of information on \code{data} +#' @param dataMapping \code{BoxWhiskerDataMapping} object +#' @param plotConfiguration \code{BoxWhiskerPlotConfiguration} object +#' @param plotObject a \code{ggplot} object +#' @return A \code{ggplot} object +addBoxWhisker <- function(data, metaData, dataMapping, plotConfiguration, plotObject) { + + # Get the box plot quantiles from dataMapping + mapData <- dataMapping$getBoxWhiskerLimits(data) + # Convert the mapping into characters usable by aes_string + mapLabels <- getAesStringMapping(dataMapping) + + plotObject <- plotObject + + ggplot2::geom_boxplot( + data = mapData, + mapping = ggplot2::aes_string( + x = mapLabels$x, + ymin = "ymin", + lower = "lower", + middle = "middle", + upper = "upper", + ymax = "ymax", + fill = mapLabels$fill, + color = mapLabels$color + ), + alpha = getAestheticValues(n = 1, selectionKey = plotConfiguration$ribbons$alpha, position = 0, aesthetic = "alpha"), + size = getAestheticValues(n = 1, selectionKey = plotConfiguration$ribbons$size, position = 0, aesthetic = "size"), + linetype = getAestheticValues(n = 1, selectionKey = plotConfiguration$ribbons$linetype, position = 0, aesthetic = "linetype"), + show.legend = TRUE, + stat = "identity" + ) + + # Define linetype, color, f# Define shapes and colors based on plotConfiguration$points properties + fillVariable <- gsub("`", "", mapLabels$fill) + colorVariable <- gsub("`", "", mapLabels$color) + fillLength <- length(unique(mapData[, fillVariable])) + colorLength <- length(unique(mapData[, colorVariable])) + + plotObject <- plotObject + + ggplot2::scale_fill_manual(values = getAestheticValues(n = fillLength, selectionKey = plotConfiguration$ribbons$fill, aesthetic = "fill")) + + ggplot2::scale_color_manual(values = getAestheticValues(n = colorLength, selectionKey = plotConfiguration$ribbons$color, aesthetic = "color")) + + # If variable is legendLabel, remove it from legend + if (isIncluded(fillVariable, "legendLabels")) { + plotObject <- plotObject + ggplot2::guides(fill = FALSE) + } + if (isIncluded(colorVariable, "legendLabels")) { + plotObject <- plotObject + ggplot2::guides(color = FALSE) + } + return(plotObject) +} + +#' @title addOutliers +#' @description Add a outlier points layer to a \code{ggplot} object +#' @param data data.frame +#' @param metaData list of information on \code{data} +#' @param dataMapping \code{BoxWhiskerDataMapping} object +#' @param plotConfiguration \code{BoxWhiskerPlotConfiguration} object +#' @param plotObject a \code{ggplot} object +#' @return A \code{ggplot} object +addOutliers <- function(data, metaData, dataMapping, plotConfiguration, plotObject) { + mapData <- dataMapping$getOutliers(data) + # Convert the mapping into characters usable by aes_string + mapLabels <- getAesStringMapping(dataMapping) + + # addScatter cannot be used in this case, + # because position dodge is needed to align boxes and outlier points + # no matter the number of groups, the value of 0.9 will be always fix + # otherwise, points won't be centered anymore + # besides, mapData includes NA instead of non-outlying data, + # na.rm removes these points without sending warning + plotObject <- plotObject + + ggplot2::geom_point( + data = mapData, + mapping = ggplot2::aes_string( + x = mapLabels$x, + y = "maxOutliers", + group = mapLabels$fill, + color = mapLabels$color + ), + size = getAestheticValues(n = 1, selectionKey = plotConfiguration$points$size, position = 0, aesthetic = "size"), + shape = getAestheticValues(n = 1, selectionKey = plotConfiguration$points$shape, position = 0, aesthetic = "shape"), + color = getAestheticValues(n = 1, selectionKey = plotConfiguration$points$color, position = 0, aesthetic = "color"), + show.legend = TRUE, + na.rm = TRUE, + position = position_dodge(width = 0.9) + ) + + ggplot2::geom_point( + data = mapData, + mapping = ggplot2::aes_string( + x = mapLabels$x, + y = "minOutliers", + group = mapLabels$fill, + color = mapLabels$color + ), + size = getAestheticValues(n = 1, selectionKey = plotConfiguration$points$size, position = 0, aesthetic = "size"), + shape = getAestheticValues(n = 1, selectionKey = plotConfiguration$points$shape, position = 0, aesthetic = "shape"), + color = getAestheticValues(n = 1, selectionKey = plotConfiguration$points$color, position = 0, aesthetic = "color"), + show.legend = TRUE, + na.rm = TRUE, + position = position_dodge(width = 0.9) + ) + return(plotObject) +} diff --git a/R/plot-ddiratio.R b/R/plot-ddiratio.R index 44778dea..c25a6212 100644 --- a/R/plot-ddiratio.R +++ b/R/plot-ddiratio.R @@ -14,27 +14,188 @@ plotDDIRatio <- function(data, metaData = NULL, dataMapping = NULL, + comparisonType = NULL, plotConfiguration = NULL, plotObject = NULL) { + validateIsOfType(data, "data.frame") dataMapping <- dataMapping %||% DDIRatioDataMapping$new(data = data) plotConfiguration <- plotConfiguration %||% DDIRatioPlotConfiguration$new(data = data, metaData = metaData, dataMapping = dataMapping) - validateIsOfType(dataMapping, DDIRatioDataMapping) - validateIsOfType(plotConfiguration, DDIRatioPlotConfiguration) + validateIsOfType(dataMapping, "DDIRatioDataMapping") + validateIsOfType(plotConfiguration, "PKRatioPlotConfiguration") + validateIsOfType(plotObject, "ggplot", nullAllowed = TRUE) - ratioData <- dataMapping$getDDIRatioLines() - guestData <- dataMapping$getGuestLines() + if (nrow(data) == 0) { + warning(messages$errorNrowData("PK ratio plot")) + return(plotObject) + } + + # Get transformed data from mapping and convert labels into characters usable by aes_string + mapData <- dataMapping$checkMapData(data) + mapLabels <- getAesStringMapping(dataMapping) plotObject <- plotObject %||% initializePlot(plotConfiguration) - plotObject <- addLine(x = ratioData$x, y = ratioData$y, caption = "ddiRatioLine1", plotObject = plotObject) - plotObject <- addLine(x = ratioData$x, y = ratioData$ymin, caption = "ddiRatioLine2", plotObject = plotObject) - plotObject <- addLine(x = ratioData$x, y = ratioData$ymax, caption = "ddiRatioLine2", plotObject = plotObject) + plotObject <- addDDIRatioLines(data, + metaData, + dataMapping, + plotConfiguration = plotConfiguration, + plotObject = plotObject + ) + + # If uncertainty is defined, add error bars + if (!isOfLength(dataMapping$uncertainty, 0)) { + plotObject <- plotObject + + ggplot2::geom_linerange( + data = mapData, + mapping = aes_string( + x = mapLabels$x, + ymin = "ymin", + ymax = "ymax", + color = mapLabels$color + ), + # Error bar size uses a ratio of 1/4 to match with point size + size = getAestheticValues(n = 1, selectionKey = plotConfiguration$errorbars$size, position = 0, aesthetic = "size"), + linetype = getAestheticValues(n = 1, selectionKey = plotConfiguration$errorbars$linetype, aesthetic = "linetype"), + show.legend = TRUE + ) + } + plotObject <- plotObject + + ggplot2::geom_point( + data = mapData, + mapping = ggplot2::aes_string( + x = mapLabels$x, + y = mapLabels$y, + color = mapLabels$color, + shape = mapLabels$shape + ), + size = getAestheticValues(n = 1, selectionKey = plotConfiguration$points$size, position = 0, aesthetic = "size"), + show.legend = TRUE + ) + + # Define shapes and colors based on plotConfiguration$points properties + shapeVariable <- gsub("`", "", mapLabels$shape) + colorVariable <- gsub("`", "", mapLabels$color) + shapeLength <- length(unique(mapData[, shapeVariable])) + colorLength <- length(unique(mapData[, colorVariable])) + + plotObject <- plotObject + + ggplot2::scale_shape_manual(values = getAestheticValues(n = shapeLength, selectionKey = plotConfiguration$points$shape, aesthetic = "shape")) + + ggplot2::scale_color_manual(values = getAestheticValues(n = colorLength, selectionKey = plotConfiguration$points$color, aesthetic = "color")) + + # If variable is legendLabel, remove it from legend + if (isIncluded(shapeVariable, "legendLabels")) { + plotObject <- plotObject + ggplot2::guides(shape = FALSE) + } + if (isIncluded(colorVariable, "legendLabels")) { + plotObject <- plotObject + ggplot2::guides(color = FALSE) + } + # Try is used to prevent crashes in the final plot due to ggplot2 peculiarities regarding scale functions + try(suppressMessages(plotObject <- setXAxis(plotObject))) + try(suppressMessages(plotObject <- setYAxis(plotObject))) + return(plotObject) +} + +#' @title addDDIRatioLines +#' @param data data.frame (or list of data.frames? TO BE DISCUSSED) +#' containing the data to be used for the plot +#' @param metaData list of lists (structure TO BE DISCUSSED) +#' containing complementary information to data (e.g. unit) +#' @param dataMapping R6 class PKRatioDataMapping +#' mapping of x, y axes + mapping of colorGrouping, sizeGrouping, shapeGrouping +#' @param plotConfiguration R6 class PKRatioPlotConfiguration +#' Plot Configuration defining title, subtitle, xlabel, ylabel watermark, and legend +#' @description +#' addDDIRatioLines(data, metaData, dataMapping, plotConfiguration, plotObject) +#' @return a ggplot graphical object +addDDIRatioLines <- function(data, + metaData = NULL, + dataMapping = NULL, + comparisonType = NULL, + plotConfiguration = NULL, + plotObject = NULL) { + validateIsOfType(dataMapping, "DDIRatioDataMapping") + validateIsOfType(plotConfiguration, "PKRatioPlotConfiguration") + validateIsOfType(plotObject, "ggplot", nullAllowed = TRUE) + validateIsIncluded(comparisonType, DDIComparisonTypes, nullAllowed = TRUE) + + dataMapping$comparisonType <- comparisonType %||% dataMapping$comparisonType - plotObject <- addLine(x = ratioData$x, y = guestData$ymin, caption = "guestLine", plotObject = plotObject) - plotObject <- addLine(x = ratioData$x, y = guestData$ymax, caption = "guestLine", plotObject = plotObject) + # Get range of x values to plot + xmin <- min(dataMapping$minRange, data[, dataMapping$x]) + xmax <- max(dataMapping$minRange, data[, dataMapping$x]) + # Not sure yet if this should be handled this way or using an external option from the dataMapping + x <- 10^(seq(log10(xmin), log10(xmax), 1e-3)) - plotObject <- setLegendCaption(plotObject, plotConfiguration$ddiRatioCaption) - plotObject <- addScatter(data = data, dataMapping = dataMapping, plotObject = plotObject) + ratioData <- list() + + # If comparison type is obs vs pred: to be compared with line of identity + if (isIncluded(dataMapping$comparisonType, DDIComparisonTypes$obsVsPred)) { + ratioData$ddiRatio1 <- data.frame( + x = x, + y = dataMapping$lines$ddiRatio1 * x + ) + ratioData$ddiRatio2 <- data.frame( + x = x, + ymin = min(dataMapping$lines$ddiRatio2) * x, + ymax = max(dataMapping$lines$ddiRatio2) * x + ) + ratioData$guestRatio <- getGuestValues(x, delta = dataMapping$lines$guestRatio) + } + # If comparison type is residuals vs pred: to be compared with line of ratio = 1 + if (isIncluded(dataMapping$comparisonType, DDIComparisonTypes$resVsPred)) { + ratioData$ddiRatio1 <- data.frame( + x = x, + y = dataMapping$lines$ddiRatio1 + ) + ratioData$ddiRatio2 <- data.frame( + x = x, + ymin = min(dataMapping$lines$ddiRatio2), + ymax = max(dataMapping$lines$ddiRatio2) + ) + ratioData$guestRatio <- getGuestValues(x, delta = dataMapping$lines$guestRatio) + ratioData$guestRatio$ymin <- ratioData$guestRatio$ymin / ratioData$guestRatio$x + ratioData$guestRatio$ymax <- ratioData$guestRatio$ymax / ratioData$guestRatio$x + } + + # Use expressions to simplify code + # Goal is to draw ratio lines with specific aesthetic values + ratioLineNames <- c("ddiRatio1", "ddiRatio2", "ddiRatio2", "guestRatio", "guestRatio") + ratioLineVariables <- c("y", "ymin", "ymax", "ymin", "ymax") + ratioLinePositions <- c("0", "1", "1", "2", "2") + + addLineExpression <- parse(text = paste0("plotObject <- plotObject + + ggplot2::geom_path(data = ratioData$", ratioLineNames, ", + mapping = ggplot2::aes_string(x='x', y='", ratioLineVariables, "'), + color = getAestheticValues(n=1, selectionKey = plotConfiguration$lines$color, position = ", ratioLinePositions, ", aesthetic = 'color'), + linetype = getAestheticValues(n=1, selectionKey = plotConfiguration$lines$linetype, position = ", ratioLinePositions, ", aesthetic = 'linetype'), + size = getAestheticValues(n=1, selectionKey = plotConfiguration$lines$size, position = ", ratioLinePositions, ", aesthetic = 'size'))")) + eval(addLineExpression) return(plotObject) } + +#' @title getGuestValues +#' @description Get a data.frame with Guest et al. ratio limits +#' @param x input values of Guest function +#' @param delta parameter of Guest function +#' @return A data.frame with Guest et al. ratio limits as ymin and ymax +#' @export +getGuestValues <- function(x, delta = 1) { + xSym <- x + xSym[x < 1] <- 1 / x[x < 1] + limit <- (delta + 2 * (xSym - 1)) / xSym + ymin <- x / limit + ymax <- x * limit + + guestLines <- data.frame(x, ymin, ymax) + return(guestLines) +} + +#' @title runDDIRatioPlot +#' @description +#' Run shiny app to use `plotDDIRatio()` from user interface +#' @export +runDDIRatioPlot <- function() { + appPath <- system.file("ddi-ratio", package = "tlf") + shiny::runApp(appPath) +} diff --git a/R/plot-histogram.R b/R/plot-histogram.R index f0e75c6f..3f8fdc14 100644 --- a/R/plot-histogram.R +++ b/R/plot-histogram.R @@ -19,29 +19,104 @@ plotHistogram <- function(data, metaData = NULL, dataMapping = NULL, - plotConfiguration = NULL, - binWidth = NULL, bins = NULL, - verticalLineFunctions = NULL, - verticalLineFunctionNames = NULL, + stack = NULL, + fitNormalDist = NULL, + fitDensity = NULL, + plotConfiguration = NULL, plotObject = NULL) { - dataMapping <- dataMapping %||% HistogramDataMapping$new() + dataMapping <- dataMapping %||% HistogramDataMapping$new(data = data) plotConfiguration <- plotConfiguration %||% HistogramPlotConfiguration$new( - binWidth = binWidth, - bins = bins, - ylabel = "Count", data = data, metaData = metaData, - dataMapping = dataMapping) + dataMapping = dataMapping + ) + + validateIsOfType(dataMapping, "HistogramDataMapping") + validateIsOfType(plotConfiguration, "HistogramPlotConfiguration") + validateIsOfType(data, "data.frame") + validateIsOfType(plotObject, "ggplot", nullAllowed = TRUE) - validateIsOfType(dataMapping, HistogramDataMapping) - validateIsOfType(plotConfiguration, HistogramPlotConfiguration) - validateIsOfType(plotObject, ggplot, nullAllowed = TRUE) + # Overwrites plotConfiguration and dataMapping if some inputs are not null + validateIsLogical(stack, nullAllowed = TRUE) + validateIsLogical(fitNormalDist, nullAllowed = TRUE) + validateIsLogical(fitDensity, nullAllowed = TRUE) + dataMapping$stack <- stack %||% dataMapping$stack + dataMapping$fitNormalDist <- fitNormalDist %||% dataMapping$fitNormalDist + dataMapping$fitDensity <- fitDensity %||% dataMapping$fitDensity + dataMapping$bins <- bins %||% dataMapping$bins plotObject <- plotObject %||% initializePlot(plotConfiguration) - plotObject <- plotConfiguration$addHistograms(plotObject, data, metaData, dataMapping, bins, binWidth) - - #plotObject <- plotConfiguration$addVerticalLines(plotObject, data, metaData, dataMapping) + if (nrow(data) == 0) { + warning(messages$errorNrowData("Histogram")) + return(plotObject) + } + + # Get transformed data from mapping and convert labels into characters usable by aes_string + mapData <- dataMapping$checkMapData(data) + mapLabels <- getAesStringMapping(dataMapping) + + position <- ggplot2::position_nudge() + if (dataMapping$stack) { + position <- ggplot2::position_stack() + } + + plotObject <- plotObject + + ggplot2::geom_histogram( + data = mapData, + mapping = ggplot2::aes_string( + x = mapLabels$x, + fill = mapLabels$fill + ), + position = position, + bins = dataMapping$bins %||% tlfEnv$defaultAggregation$bins, + size = getAestheticValues(n = 1, selectionKey = plotConfiguration$ribbons$size, position = 0, aesthetic = "size"), + color = getAestheticValues(n = 1, selectionKey = plotConfiguration$ribbons$color, position = 0, aesthetic = "color"), + alpha = getAestheticValues(n = 1, selectionKey = plotConfiguration$ribbons$alpha, position = 0, aesthetic = "alpha") + ) + + # Vertical lines defined in dataMapping$lines + for (lineIndex in dataMapping$lines) { + plotObject <- plotObject + + ggplot2::geom_vline( + xintercept = dataMapping$lines[lineIndex], + size = getAestheticValues(n = 1, selectionKey = plotConfiguration$lines$size, position = lineIndex - 1, aesthetic = "size"), + color = getAestheticValues(n = 1, selectionKey = plotConfiguration$lines$color, position = lineIndex - 1, aesthetic = "color"), + linetype = getAestheticValues(n = 1, selectionKey = plotConfiguration$lines$linetype, position = lineIndex - 1, aesthetic = "linetype") + ) + } + # Lines fitting a normal distribution + if (dataMapping$fitNormalDist) { + histResult <- graphics::hist(data[, dataMapping$x], breaks = dataMapping$bins %||% tlfEnv$defaultAggregation$bins, plot = FALSE) + scalingFactor <- mean(histResult$counts[histResult$counts > 0] / histResult$density[histResult$counts > 0]) + xmean <- mean(data[, dataMapping$x]) + xsd <- stats::sd(data[, dataMapping$x], na.rm = TRUE) + xDensityData <- seq(xmean - 3 * xsd, xmean + 3 * xsd, 6 * xsd / 100) + yDensityData <- scalingFactor * stats::dnorm(xDensityData, mean = xmean, sd = xsd) + densityData <- data.frame(x = xDensityData, y = yDensityData) + + plotObject <- plotObject + + ggplot2::geom_path( + data = densityData, + mapping = ggplot2::aes_string(x = "x", y = "y"), + size = getAestheticValues(n = 1, selectionKey = plotConfiguration$lines$size, position = 0, aesthetic = "size"), + color = getAestheticValues(n = 1, selectionKey = plotConfiguration$lines$color, position = 0, aesthetic = "color"), + linetype = getAestheticValues(n = 1, selectionKey = plotConfiguration$lines$linetype, position = 0, aesthetic = "linetype") + ) + } + + # Define fill based on plotConfiguration$points properties + fillVariable <- gsub("`", "", mapLabels$fill) + fillLength <- length(unique(mapData[, fillVariable])) + + plotObject <- plotObject + + ggplot2::scale_fill_manual(values = getAestheticValues(n = fillLength, selectionKey = plotConfiguration$ribbons$fill, aesthetic = "fill")) + + # If variable is legendLabel, remove it from legend + if (isIncluded(fillVariable, "legendLabels")) { + plotObject <- plotObject + ggplot2::guides(fill = FALSE) + } + # dataMapping$smoother return(plotObject) } diff --git a/R/plot-obs-vs-pred.R b/R/plot-obs-vs-pred.R index 178eb475..36852b67 100644 --- a/R/plot-obs-vs-pred.R +++ b/R/plot-obs-vs-pred.R @@ -24,25 +24,115 @@ plotObsVsPred <- function(data, dataMapping = NULL, plotConfiguration = NULL, plotObject = NULL) { - - # If no data mapping or plot configuration is input, use default - # metaData <- metaData %||% metaDataHelper(data) + validateIsOfType(data, "data.frame") dataMapping <- dataMapping %||% ObsVsPredDataMapping$new(data = data) plotConfiguration <- plotConfiguration %||% ObsVsPredPlotConfiguration$new(data = data, metaData = metaData, dataMapping = dataMapping) - validateIsOfType(dataMapping, ObsVsPredDataMapping) - validateIsOfType(plotConfiguration, ObsVsPredPlotConfiguration) + validateIsOfType(dataMapping, "ObsVsPredDataMapping") + validateIsOfType(plotConfiguration, "ObsVsPredPlotConfiguration") + validateIsOfType(plotObject, "ggplot", nullAllowed = TRUE) + + if (nrow(data) == 0) { + warning(messages$errorNrowData("Obs vs Pred plot")) + return(plotObject) + } - identityData <- dataMapping$getObsVsPredLines(data) - lloq <- dataMapping$lloq + # Get error bars for uncertainty + if (!isOfLength(dataMapping$uncertainty, 0)) { + plotObject <- addErrorbar( + data = data, + dataMapping = dataMapping, + plotConfiguration = plotConfiguration, + plotObject = plotObject + ) + } + plotObject <- addScatter( + data = data, + dataMapping = dataMapping, + plotConfiguration = plotConfiguration, + plotObject = plotObject + ) - plotObject <- plotObject %||% initializePlot(plotConfiguration) + # Add line of identity depending and smoothing + plotObject <- addObsVsPredLines( + data = data, + metaData, + dataMapping, + plotConfiguration, + plotObject + ) + # LLOQ + if (!isOfLength(dataMapping$lloq, 0)) { + # Overwrite y mapping temporarily + yMapping <- dataMapping$y + dataMapping$y <- dataMapping$lloq - plotObject <- addLine(x = identityData$x, y = identityData$y, caption = "y=x", plotObject = plotObject) - plotObject <- ifnotnull(lloq, addLine(x = c(lloq, lloq), y = c(-Inf, Inf), caption = "lloq", plotObject = plotObject), plotObject) + plotObject <- addLine( + data = data, + dataMapping = dataMapping, + caption = dataMapping$lloq, + plotConfiguration = plotConfiguration, + plotObject = plotObject + ) + dataMapping$y <- yMapping + } + + try(suppressMessages(plotObject <- setXAxis(plotObject))) + try(suppressMessages(plotObject <- setYAxis(plotObject))) + return(plotObject) +} + +#' @title addObsVsPredLines +#' @param data data.frame containing the data to be used for the plot +#' @param metaData list of lists +#' @param dataMapping \code{ObsVsPredDataMapping} object +#' @param plotConfiguration \code{ObsVsPredConfiguration} object +#' @param plotObject \code{ggplot} graphical object +#' @description +#' Add layers of identity line and smoother +#' @return A \code{ggplot} graphical object +addObsVsPredLines <- function(data, + metaData = NULL, + dataMapping = NULL, + plotConfiguration = NULL, + plotObject = NULL) { - plotObject <- setLegendCaption(plotObject, plotConfiguration$obsVsPredCaption) - plotObject <- addScatter(data = data, dataMapping = dataMapping, plotObject = plotObject) + # Get range of x values to plot + xmin <- min(dataMapping$minRange, data[, dataMapping$x], data[, dataMapping$y]) + xmax <- max(dataMapping$minRange, data[, dataMapping$x], data[, dataMapping$y]) + obsVsPredLineData <- data.frame(x = c(xmin, xmax), y = c(xmin, xmax) * dataMapping$lines) + plotObject <- plotObject + + ggplot2::geom_path( + data = obsVsPredLineData, + mapping = ggplot2::aes_string(x = "x", y = "y"), + color = getAestheticValues(n = 1, selectionKey = plotConfiguration$lines$color, position = 0, aesthetic = "color"), + linetype = getAestheticValues(n = 1, selectionKey = plotConfiguration$lines$linetype, position = 0, aesthetic = "linetype"), + size = getAestheticValues(n = 1, selectionKey = plotConfiguration$lines$size, position = 0, aesthetic = "size") + ) + # Get mapping and convert labels into characters usable by aes_string + mapData <- dataMapping$checkMapData(data) + mapLabels <- getAesStringMapping(dataMapping) + if (!isOfLength(dataMapping$smoother, 0)) { + plotObject <- plotObject + + ggplot2::geom_smooth( + data = mapData, + mapping = ggplot2::aes_string(x = mapLabels$x, y = mapLabels$y), + method = dataMapping$smoother, + se = FALSE, + color = getAestheticValues(n = 1, selectionKey = plotConfiguration$lines$color, position = 1, aesthetic = "color"), + linetype = getAestheticValues(n = 1, selectionKey = plotConfiguration$lines$linetype, position = 1, aesthetic = "linetype"), + size = getAestheticValues(n = 1, selectionKey = plotConfiguration$lines$size, position = 1, aesthetic = "size") + ) + } return(plotObject) } + +#' @title runObsVsPredPlot +#' @description +#' Run shiny app to use `plotObsVsPred()` from user interface +#' @export +runObsVsPredPlot <- function() { + appPath <- system.file("obs-vs-pred", package = "tlf") + shiny::runApp(appPath) +} diff --git a/R/plot-pkratio.R b/R/plot-pkratio.R index 90fd9fc3..a83779ae 100644 --- a/R/plot-pkratio.R +++ b/R/plot-pkratio.R @@ -24,21 +24,94 @@ plotPKRatio <- function(data, dataMapping = NULL, plotConfiguration = NULL, plotObject = NULL) { + validateIsOfType(data, "data.frame") dataMapping <- dataMapping %||% PKRatioDataMapping$new(data = data) plotConfiguration <- plotConfiguration %||% PKRatioPlotConfiguration$new(data = data, metaData = metaData, dataMapping = dataMapping) - validateIsOfType(dataMapping, PKRatioDataMapping) - validateIsOfType(plotConfiguration, PKRatioPlotConfiguration) + validateIsOfType(dataMapping, "PKRatioDataMapping") + validateIsOfType(plotConfiguration, "PKRatioPlotConfiguration") + validateIsOfType(plotObject, "ggplot", nullAllowed = TRUE) + + if (nrow(data) == 0) { + warning(messages$errorNrowData("PK ratio plot")) + return(plotObject) + } + + # Get transformed data from mapping and convert labels into characters usable by aes_string + mapData <- dataMapping$checkMapData(data) + mapLabels <- getAesStringMapping(dataMapping) plotObject <- plotObject %||% initializePlot(plotConfiguration) - for (lineIndex in seq_along(dataMapping$pkRatioValues)) { - plotObject <- addLine( - y = dataMapping$pkRatioValues[[lineIndex]], - caption = paste0("pkRatioLine", lineIndex), - plotObject = plotObject + # Include horizontal lines + for (lineIndex in seq_along(dataMapping$lines)) { + # position correspond to the number of layer lines already added + plotObject <- plotObject + + ggplot2::geom_hline( + yintercept = dataMapping$lines[[lineIndex]], + color = getAestheticValues(n = 1, selectionKey = plotConfiguration$lines$color, position = lineIndex - 1, aesthetic = "color"), + linetype = getAestheticValues(n = 1, selectionKey = plotConfiguration$lines$linetype, position = lineIndex - 1, aesthetic = "linetype"), + size = getAestheticValues(n = 1, selectionKey = plotConfiguration$lines$size, position = lineIndex - 1, aesthetic = "size") + ) + } + + # If uncertainty is defined, add error bars + if (!isOfLength(dataMapping$uncertainty, 0)) { + plotObject <- plotObject + + ggplot2::geom_linerange( + data = mapData, + mapping = aes_string( + x = mapLabels$x, + ymin = "ymin", + ymax = "ymax", + color = mapLabels$color + ), + # Error bar size uses a ratio of 1/4 to match with point size + size = getAestheticValues(n = 1, selectionKey = plotConfiguration$errorbars$size, position = 0, aesthetic = "size"), + linetype = getAestheticValues(n = 1, selectionKey = plotConfiguration$errorbars$linetype, aesthetic = "linetype"), + show.legend = TRUE + ) + } + plotObject <- plotObject + + ggplot2::geom_point( + data = mapData, + mapping = ggplot2::aes_string( + x = mapLabels$x, + y = mapLabels$y, + color = mapLabels$color, + shape = mapLabels$shape + ), + size = getAestheticValues(n = 1, selectionKey = plotConfiguration$points$size, position = 0, aesthetic = "size"), + show.legend = TRUE ) + + # Define shapes and colors based on plotConfiguration$points properties + shapeVariable <- gsub("`", "", mapLabels$shape) + colorVariable <- gsub("`", "", mapLabels$color) + shapeLength <- length(unique(mapData[, shapeVariable])) + colorLength <- length(unique(mapData[, colorVariable])) + + plotObject <- plotObject + + ggplot2::scale_shape_manual(values = getAestheticValues(n = shapeLength, selectionKey = plotConfiguration$points$shape, aesthetic = "shape")) + + ggplot2::scale_color_manual(values = getAestheticValues(n = colorLength, selectionKey = plotConfiguration$points$color, aesthetic = "color")) + + # If variable is legendLabel, remove it from legend + if (isIncluded(shapeVariable, "legendLabels")) { + plotObject <- plotObject + ggplot2::guides(shape = FALSE) } - plotObject <- setLegendCaption(plotObject, plotConfiguration$pkRatioCaption) - plotObject <- addScatter(data = data, dataMapping = dataMapping, plotObject = plotObject) + if (isIncluded(colorVariable, "legendLabels")) { + plotObject <- plotObject + ggplot2::guides(color = FALSE) + } + # Try is used to prevent crashes in the final plot due to ggplot2 peculiarities regarding scale functions + try(suppressMessages(plotObject <- setXAxis(plotObject))) + try(suppressMessages(plotObject <- setYAxis(plotObject))) return(plotObject) } + +#' @title runPKRatioPlot +#' @description +#' Run shiny app to use `plotPKRatio()` from user interface +#' @export +runPKRatioPlot <- function() { + appPath <- system.file("pk-ratio", package = "tlf") + shiny::runApp(appPath) +} diff --git a/R/plot-timeprofile.R b/R/plot-timeprofile.R index f5a0ae25..19996ceb 100644 --- a/R/plot-timeprofile.R +++ b/R/plot-timeprofile.R @@ -9,48 +9,87 @@ #' plotTimeProfile(data, metaData = NULL, dataMapping = NULL, plotConfiguration = NULL, plotObject = NULL) #' @return a ggplot graphical object #' @export -plotTimeProfile <- function(data, +plotTimeProfile <- function(data = NULL, metaData = NULL, dataMapping = NULL, observedData = NULL, - dataMappingForObserved = NULL, + observedDataMapping = NULL, plotConfiguration = NULL, plotObject = NULL) { - dataMapping <- dataMapping %||% TimeProfileDataMapping$new(data = data) + validateIsOfType(data, "data.frame", nullAllowed = TRUE) + validateIsOfType(observedData, "data.frame", nullAllowed = TRUE) + if (all(isOfLength(data, 0), isOfLength(observedData, 0))) { + warning("data and observed data are of length 0. Time profile layer was not added.") + return(plotObject) + } + + if (!isOfLength(data, 0)) { + dataMapping <- dataMapping %||% TimeProfileDataMapping$new(data = data) + } + if (!isOfLength(observedData, 0)) { + observedDataMapping <- observedDataMapping %||% ObservedDataMapping$new(data = data) + } + plotConfiguration <- plotConfiguration %||% TimeProfilePlotConfiguration$new(data = data, metaData = metaData, dataMapping = dataMapping) - validateIsOfType(data, "data.frame") - validateIsOfType(dataMapping, TimeProfileDataMapping) + validateIsOfType(dataMapping, TimeProfileDataMapping, nullAllowed = TRUE) + validateIsOfType(observedDataMapping, ObservedDataMapping, nullAllowed = TRUE) validateIsOfType(plotConfiguration, TimeProfilePlotConfiguration) - validateIsOfType(observedData, "data.frame", nullAllowed = TRUE) - validateIsOfType(dataMappingForObserved, XYGDataMapping, nullAllowed = TRUE) - - lloq <- dataMapping$lloq - isRangeTimeProfile <- dataMapping$isRangeTimeProfile + # Initialize plot based on plotConfiguration plotObject <- plotObject %||% initializePlot(plotConfiguration) - if (!is.null(lloq)) { - plotObject <- addLine(y = lloq, caption = "lloq", plotObject = plotObject) - plotObject <- setLegendCaption(plotObject, plotConfiguration$timeProfileCaption) - } - if (isRangeTimeProfile) { - aggregatedData <- getAggregatedData(dataMapping$checkMapData(data), dataMapping$x, dataMapping$y) - rangeData <- aggregatedData - rangeData$Groups <- paste(tlfEnv$defaultAggregation$labels$range, aggregatedData$Groups) - rangeMapping <- RangeDataMapping$new(x = "x", ymin = "ymin", ymax = "ymax", color = "Groups") - plotObject <- addRibbon(data = rangeData, dataMapping = rangeMapping, plotObject = plotObject) - lineMapping <- XYGDataMapping$new(x = "x", y = "y", color = "Groups") - lineData <- aggregatedData - lineData$Groups <- paste(tlfEnv$defaultAggregation$labels$y, aggregatedData$Groups) - plotObject <- addLine(data = lineData, dataMapping = lineMapping, plotObject = plotObject) - } - if (!isRangeTimeProfile) { - plotObject <- addLine(data = data, dataMapping = dataMapping, plotObject) - } - if (!is.null(observedData)) { - dataMappingForObserved <- dataMappingForObserved %||% XYGDataMapping$new(data = observedData) - plotObject <- addScatter(data = observedData, dataMapping = dataMappingForObserved, plotObject = plotObject) + + # Get transformed data from mapping and convert labels into characters usable by aes_string + if (!isOfLength(data, 0)) { + mapData <- dataMapping$checkMapData(data) + if (!any(isOfLength(dataMapping$ymin, 0), isOfLength(dataMapping$ymax, 0))) { + plotObject <- addRibbon( + data = mapData, + dataMapping = dataMapping, + plotConfiguration = plotConfiguration, + plotObject = plotObject + ) + } + if (!isOfLength(dataMapping$y, 0)) { + plotObject <- addLine( + data = mapData, + dataMapping = dataMapping, + plotConfiguration = plotConfiguration, + plotObject = plotObject + ) + } } + if (!isOfLength(observedData, 0)) { + mapObservedData <- observedDataMapping$checkMapData(observedData) + if (!isOfLength(observedDataMapping$uncertainty, 0)) { + plotObject <- addErrorbar( + data = mapObservedData, + dataMapping = observedDataMapping, + plotConfiguration = plotConfiguration, + plotObject = plotObject + ) + } + plotObject <- addScatter( + data = mapObservedData, + dataMapping = observedDataMapping, + plotConfiguration = plotConfiguration, + plotObject = plotObject + ) + # LLOQ + if (!isOfLength(observedDataMapping$lloq, 0)) { + mapObservedData$legendLabels <- paste(mapObservedData$legendLabels, " LLOQ") + plotObject <- addLine( + data = mapObservedData, + dataMapping = XYGDataMapping$new( + x = observedDataMapping$x, + y = "lloq", + color = "legendLabels" + ), + plotConfiguration = plotConfiguration, + plotObject = plotObject + ) + } + } return(plotObject) } diff --git a/R/plot-tornado.R b/R/plot-tornado.R index 3ae32edb..93c66b89 100644 --- a/R/plot-tornado.R +++ b/R/plot-tornado.R @@ -92,11 +92,21 @@ plotTornado <- function(data = NULL, fill = mapLabels$fill, color = mapLabels$color ), - alpha = plotConfiguration$theme$aesProperties$alpha[1], - size = plotConfiguration$theme$aesProperties$size[1], - linetype = plotConfiguration$theme$aesProperties$linetype[1], + alpha = getAestheticValues(n = 1, selectionKey = plotConfiguration$ribbons$alpha, position = 0, aesthetic = "alpha"), + size = getAestheticValues(n = 1, selectionKey = plotConfiguration$ribbons$size, position = 0, aesthetic = "size"), + linetype = getAestheticValues(n = 1, selectionKey = plotConfiguration$ribbons$linetype, position = 0, aesthetic = "linetype"), position = ggplot2::position_dodge(width = plotConfiguration$dodge) ) + + # Define shapes and colors based on plotConfiguration$points properties + fillVariable <- gsub("`", "", mapLabels$fill) + colorVariable <- gsub("`", "", mapLabels$color) + fillLength <- length(unique(mapData[, fillVariable])) + colorLength <- length(unique(mapData[, colorVariable])) + + plotObject <- plotObject + + ggplot2::scale_fill_manual(values = getAestheticValues(n = fillLength, selectionKey = plotConfiguration$ribbons$fill, aesthetic = "fill")) + + ggplot2::scale_color_manual(values = getAestheticValues(n = colorLength, selectionKey = plotConfiguration$ribbons$color, aesthetic = "color")) } if (!plotConfiguration$bar) { # For tornado with points, their shape will be taken from the theme properties @@ -108,31 +118,42 @@ plotTornado <- function(data = NULL, color = mapLabels$color, shape = mapLabels$shape ), - size = plotConfiguration$theme$aesProperties$size[1], + size = getAestheticValues(n = 1, selectionKey = plotConfiguration$points$size, position = 0, aesthetic = "size"), position = ggplot2::position_dodge(width = plotConfiguration$dodge) - ) + - ggplot2::scale_shape_manual(values = tlfEnv$currentTheme$aesProperties$shape) + ) + + # Define shapes and colors based on plotConfiguration$points properties + shapeVariable <- gsub("`", "", mapLabels$shape) + colorVariable <- gsub("`", "", mapLabels$color) + shapeLength <- length(unique(mapData[, shapeVariable])) + colorLength <- length(unique(mapData[, colorVariable])) + + plotObject <- plotObject + + ggplot2::scale_shape_manual(values = getAestheticValues(n = shapeLength, selectionKey = plotConfiguration$points$shape, aesthetic = "shape")) + + ggplot2::scale_color_manual(values = getAestheticValues(n = colorLength, selectionKey = plotConfiguration$points$color, aesthetic = "color")) } # Final plot includes a vertical line in 0 # And optional color palette otherwise use colors from theme - plotObject <- plotObject + + if (!isOfLength(dataMapping$lines, 0)) { + plotObject <- plotObject + ggplot2::geom_vline( - xintercept = dataMapping$tornadoValues, - color = tlfEnv$currentTheme$aesProperties$color[1], - size = tlfEnv$currentTheme$aesProperties$size[1], - linetype = tlfEnv$currentTheme$aesProperties$linetype[1] + xintercept = dataMapping$lines, + color = getAestheticValues(n = length(dataMapping$lines), selectionKey = plotConfiguration$lines$color, position = 0, aesthetic = "color"), + size = getAestheticValues(n = length(dataMapping$lines), selectionKey = plotConfiguration$lines$size, position = 0, aesthetic = "size"), + linetype = getAestheticValues(n = length(dataMapping$lines), selectionKey = plotConfiguration$lines$linetype, position = 0, aesthetic = "linetype") ) - - if(!is.null(plotConfiguration$colorPalette)){ - plotObject <- plotObject + - ggplot2::scale_fill_brewer(palette = plotConfiguration$colorPalette, - aesthetics = c("color", "fill")) - return(plotObject) } - - plotObject <- plotObject + - ggplot2::scale_fill_manual(values = tlfEnv$currentTheme$aesProperties$fill) + - ggplot2::scale_color_manual(values = tlfEnv$currentTheme$aesProperties$color) + + if (!isOfLength(plotConfiguration$colorPalette, 0)) { + try(suppressMessages( + plotObject <- plotObject + + ggplot2::scale_fill_brewer( + palette = plotConfiguration$colorPalette, + aesthetics = c("color", "fill") + ) + )) + } + try(suppressMessages(plotObject <- setXAxis(plotObject))) return(plotObject) } diff --git a/R/plotconfiguration-axis.R b/R/plotconfiguration-axis.R index f12b7ab6..b81a8065 100644 --- a/R/plotconfiguration-axis.R +++ b/R/plotconfiguration-axis.R @@ -3,15 +3,49 @@ #' @export #' @description #' Pre-defined transformation of axes -#' Not that built-in transformations from ggplot2 includes more transformations +#' Not that built-in transformations from `ggplot2` includes more transformations Scaling <- enum(c( - "lin", - "log10", - "log", - "discrete", + "lin", "log", "discrete", "reverse", "sqrt", "time", "date" )) +#' @title createPlotScale +#' @description Translate scale into a value directly usable by `ggplot2` +#' to give more flexibilty in the next functions +#' @param scale character defining the name of the scale +#' @return name of the `ggplot2` scale +createPlotScale <- function(scale) { + validateIsString(scale) + if (isIncluded(tolower(scale), c("identity", "lin", "linear", "default", "normal"))) { + return("identity") + } + if (isIncluded(tolower(scale), c("log", "logarithmic", "log10"))) { + return("log10") + } + validateIsIncluded(tolower(scale), Scaling) + return(tolower(scale)) +} + +#' @title createPlotTicks +#' @description Translate ticks and ticklabels into a value directly usable by `ggplot2` +#' to give more flexibilty in the next functions +#' @param ticks character, numeric or function defining the ticks +#' @return name of the `ggplot2` scale +createPlotTicks <- function(ticks) { + if (isOfLength(ticks, 0)) { + return(waiver()) + } + if (isIncluded(ticks, c("default", "identity"))) { + return(waiver()) + } + if (isIncluded(ticks, c("none"))) { + return(NULL) + } + if (isOfType(ticks, c("numeric", "character", "function"))) { + return(ticks) + } +} + #' @title AxisConfiguration #' @description R6 class defining the configuration of axis @@ -19,66 +53,107 @@ Scaling <- enum(c( AxisConfiguration <- R6::R6Class( "AxisConfiguration", public = list( - #' @field limits numeric vector of axis limits - limits = NULL, - #' @field scale character defining axis scale - scale = NULL, - #' @field ticks numeric vector or function defining where to position axis ticks - ticks = NULL, - #' @field ticklabels character vector or function defining what to print on axis ticks - ticklabels = NULL, - #' @description Create a new \code{AxisConfiguration} object #' @param limits numeric vector of axis limits #' @param scale character defining axis scale #' Use enum `Scaling` to access predefined scales. #' @param ticks numeric vector or function defining where to position axis ticks #' @param ticklabels character vector or function defining what to print on axis ticks + #' @param font \code{Font} object defining the font of ticklabels #' @return A new \code{AxisConfiguration} object initialize = function(limits = NULL, scale = Scaling$lin, - ticks = "default", - ticklabels = "default") { - self$limits <- limits - self$scale <- scale - if (self$scale %in% "lin") { - self$scale <- "identity" - } + ticks = NULL, + ticklabels = NULL, + font = NULL) { + validateIsNumeric(limits, nullAllowed = TRUE) + validateIsOfType(font, "Font", nullAllowed = TRUE) + private$.limits <- limits + + scale <- scale %||% Scaling$lin + private$.scale <- createPlotScale(scale) + private$.ticks <- createPlotTicks(ticks) + private$.ticklabels <- createPlotTicks(ticklabels) - self$ticks <- ticks - if (!isOfLength(ticks, 0)) { - if (ticks[1] %in% "default") { - self$ticks <- waiver() - } + # Default axis font will use theme + defaultFont <- Font$new() + currentTheme <- tlfEnv$currentTheme$clone(deep = TRUE) + if (isOfType(self, "XAxisConfiguration")) { + defaultFont <- currentTheme$fonts$xAxis } - self$ticklabels <- ticklabels - if (!isOfLength(ticklabels, 0)) { - if (ticklabels[1] %in% "default") { - self$ticklabels <- waiver() - } + if (isOfType(self, "YAxisConfiguration")) { + defaultFont <- currentTheme$fonts$yAxis } + private$.font <- font %||% defaultFont + } + ), + active = list( + #' @field limits numeric vector of length 2 defining limits of axis. + #' A value of `NULL` is allowed and lead to default `ggplot2` behaviour + limits = function(value) { + if (missing(value)) { + return(private$.limits) + } + validateIsNumeric(value, nullAllowed = TRUE) + if (isOfLength(value, 0)) { + private$.limits <- NULL + return(invisible()) + } + validateIsOfLength(value, 2) + private$.limits <- value + return(invisible()) }, - - #' @description Print axis properties - #' @return Axis properties - print = function() { - axisProperties <- list( - scale = self$scale, - limits = self$limits, - ticks = self$ticks, - ticklabels = self$ticklabels - ) - if (axisProperties$scale %in% "identity") { - axisProperties$scale <- "linear" + #' @field scale name of axis scale from Enum `Scaling` + #' A value of `NULL` is allowed and will lead to a default linear scale + scale = function(value) { + if (missing(value)) { + return(private$.scale) } - if (length(axisProperties$ticks) == 0 & !is.null(axisProperties$ticks)) { - axisProperties$ticks <- "default" + value <- value %||% Scaling$lin + private$.scale <- createPlotScale(value) + return(invisible()) + }, + #' @field ticks function or values defining where axis ticks are placed + ticks = function(value) { + if (missing(value)) { + return(private$.ticks) } - if (length(axisProperties$ticklabels) == 0 & !is.null(axisProperties$ticklabels)) { - axisProperties$ticklabels <- "default" + private$.ticks <- createPlotTicks(value) + return(invisible()) + }, + #' @field ticklabels function or values defining the axis tick labels + ticklabels = function(value) { + if (missing(value)) { + return(private$.ticklabels) + } + private$.ticklabels <- createPlotTicks(value) + return(invisible()) + }, + #' @field font \code{Font} object defining the font of the ticklabels + font = function(value) { + if (missing(value)) { + return(private$.font) + } + validateIsOfType(value, "Font", nullAllowed = TRUE) + # Default axis font will use theme + defaultFont <- Font$new() + currentTheme <- tlfEnv$currentTheme$clone(deep = TRUE) + if (isOfType(self, "XAxisConfiguration")) { + defaultFont <- currentTheme$fonts$xAxis + } + if (isOfType(self, "YAxisConfiguration")) { + defaultFont <- currentTheme$fonts$yAxis } - return(axisProperties) + private$.font <- value %||% defaultFont + return(invisible()) } + ), + private = list( + .limits = NULL, + .scale = NULL, + .ticks = NULL, + .ticklabels = NULL, + .font = NULL ) ) @@ -89,20 +164,26 @@ XAxisConfiguration <- R6::R6Class( "XAxisConfiguration", inherit = AxisConfiguration, public = list( - #' @description Set axis configuration on a \code{ggplot} object + #' @description Update axis configuration on a \code{ggplot} object #' @param plotObject \code{ggplot} object #' @return A \code{ggplot} object with updated axis properties - setPlotAxis = function(plotObject) { - if (self$scale %in% "discrete") { + updatePlot = function(plotObject) { + validateIsOfType(plotObject, "ggplot") + # Update font properties + plotObject <- plotObject + ggplot2::theme(axis.text.x = private$.font$createPlotFont()) + # Update scales and ticks + if (isIncluded(private$.scale, Scaling$discrete)) { suppressMessages( plotObject <- plotObject + - scale_x_discrete(limits = self$limits, breaks = self$ticks, labels = self$ticklabels) + ggplot2::scale_x_discrete(limits = private$.limits, breaks = private$.ticks, labels = private$.ticklabels) ) return(plotObject) } + # Most of ggplot2 scales lead to unwanted warning messages + # `try` should be added in cases of scale breaking because all the ggplot object elements are not yet in place suppressMessages( plotObject <- plotObject + - scale_x_continuous(trans = self$scale, limits = self$limits, breaks = self$ticks, labels = self$ticklabels) + ggplot2::scale_x_continuous(trans = private$.scale, limits = private$.limits, breaks = private$.ticks, labels = private$.ticklabels) ) return(plotObject) } @@ -117,15 +198,28 @@ YAxisConfiguration <- R6::R6Class( inherit = AxisConfiguration, public = list( #' @field position character poistion of the Y-axis - position = NULL, # TO DO: initialize position, then scale position = "left" or "right" + position = NULL, # TO DO: find a way to include position in y axis, then scale position = "left" or "right" - #' @description Set axis configuration on a \code{ggplot} object + #' @description Update axis configuration on a \code{ggplot} object #' @param plotObject \code{ggplot} object #' @return A \code{ggplot} object with updated axis properties - setPlotAxis = function(plotObject) { + updatePlot = function(plotObject) { + validateIsOfType(plotObject, "ggplot") + # Update font properties + plotObject <- plotObject + ggplot2::theme(axis.text.y = private$.font$createPlotFont()) + # Update scales and ticks + if (isIncluded(private$.scale, Scaling$discrete)) { + suppressMessages( + plotObject <- plotObject + + ggplot2::scale_y_discrete(limits = private$.limits, breaks = private$.ticks, labels = private$.ticklabels) + ) + return(plotObject) + } + # Most of ggplot2 scales lead to unwanted warning messages + # `try` should be added in cases of scale breaking because all the ggplot object elements are not yet in place suppressMessages( plotObject <- plotObject + - scale_y_continuous(trans = self$scale, limits = self$limits, breaks = self$ticks, labels = self$ticklabels) + ggplot2::scale_y_continuous(trans = private$.scale, limits = private$.limits, breaks = private$.ticks, labels = private$.ticklabels) ) return(plotObject) } diff --git a/R/plotconfiguration-background.R b/R/plotconfiguration-background.R index 88edbb3f..35970760 100644 --- a/R/plotconfiguration-background.R +++ b/R/plotconfiguration-background.R @@ -4,131 +4,191 @@ BackgroundConfiguration <- R6::R6Class( "BackgroundConfiguration", public = list( - #' @field outerBackground R6 class \code{BackgroundElementConfiguration} object - outerBackground = NULL, - #' @field innerBackground R6 class \code{BackgroundElementConfiguration} object - innerBackground = NULL, - #' @field grid R6 class \code{BackgroundElementConfiguration} object - grid = NULL, - #' @field watermark R6 class \code{Label} object defining watermark background - watermark = NULL, - #' @description Create a new \code{BackgroundConfiguration} object - #' @param outerBackground R6 class \code{BackgroundElementConfiguration} object - #' @param innerBackground R6 class \code{BackgroundElementConfiguration} object - #' @param grid R6 class \code{BackgroundElementConfiguration} object - #' @param watermark R6 class \code{Label} object defining watermark background - #' @param watermarkFont R6 class \code{Font} object defining watermark font - #' @param theme R6 class \code{Theme} object + #' @param watermark \code{Label} object defining properties of watermark + #' @param plot \code{BackgroundElement} object defining oustide plot background properties + #' @param panel \code{BackgroundElement} object defining panel (inside of plot) background properties + #' @param xAxis \code{LineElement} object defining properties of x-axis + #' @param yAxis \code{LineElement} object defining properties of y-axis + #' @param xGrid \code{LineElement} object defining properties of x-grid + #' @param yGrid \code{LineElement} object defining properties of y-grid #' @return A new \code{BackgroundConfiguration} object - initialize = function(outerBackground = NULL, - innerBackground = NULL, - grid = NULL, - watermark = NULL, - watermarkFont = NULL, - theme = tlfEnv$currentTheme) { - self$outerBackground <- outerBackground %||% BackgroundElementConfiguration$new(theme = theme$background$outer) - self$innerBackground <- innerBackground %||% BackgroundElementConfiguration$new(theme = theme$background$inner) - self$grid <- grid %||% BackgroundElementConfiguration$new(theme = theme$background$grid) + initialize = function(watermark = NULL, + plot = NULL, + panel = NULL, + xAxis = NULL, + yAxis = NULL, + xGrid = NULL, + yGrid = NULL) { + validateIsOfType(watermark, c("character", "Label"), nullAllowed = TRUE) + currentTheme <- tlfEnv$currentTheme$clone(deep = TRUE) + watermark <- watermark %||% currentTheme$background$watermark + # Enforce watermark as Label with value + if (isOfType(watermark, "character")) { + watermark <- asLabel(text = watermark, font = currentTheme$fonts$watermark) + } + private$.watermark <- watermark - self$watermark <- asLabel(watermark %||% theme$background$watermark %||% "") - self$watermark$font <- watermarkFont %||% self$watermark$font - }, + areaFieldNames <- c("plot", "panel") + lineFieldNames <- c("xAxis", "yAxis", "xGrid", "yGrid") - #' @description Print background properties - #' @return Background properties - print = function() { - backgroundProperties <- list( - "outerBackground" = self$outerBackground$print(), - "innerBackground" = self$innerBackground$print(), - "grid" = self$grid$print(), - "watermark" = self$watermark$text - ) - return(backgroundProperties) + validateAreaExpression <- parse(text = paste0("validateIsOfType(", areaFieldNames, ", 'BackgroundElement', nullAllowed = TRUE)")) + validateLineExpression <- parse(text = paste0("validateIsOfType(", lineFieldNames, ", 'LineElement', nullAllowed = TRUE)")) + eval(validateAreaExpression) + eval(validateLineExpression) + + setAreaExpression <- parse(text = paste0("private$.", areaFieldNames, " <- ", areaFieldNames, " %||% currentTheme$background$", areaFieldNames)) + setLineExpression <- parse(text = paste0("private$.", lineFieldNames, " <- ", lineFieldNames, " %||% currentTheme$background$", lineFieldNames)) + eval(setAreaExpression) + eval(setLineExpression) }, - #' @description Set background properties of a \code{ggplot} object + #' @description Update background a \code{ggplot} object from `BackgroundConfiguration` properties #' @param plotObject a \code{ggplot} object #' @return A \code{ggplot} object - setBackground = function(plotObject) { - plotObject <- plotObject + theme( - plot.background = element_rect(fill = self$outerBackground$fill), - panel.background = element_rect( - fill = self$innerBackground$fill, - color = self$innerBackground$color, - size = self$innerBackground$size, - linetype = self$innerBackground$linetype, - ), - panel.grid = element_line( - color = self$grid$color, - size = self$grid$size, - linetype = self$grid$linetype - ) + updatePlot = function(plotObject) { + plotObject <- plotObject + ggplot2::theme( + plot.background = private$.plot$createPlotElement(), + panel.background = private$.panel$createPlotElement(), + axis.line.x = private$.xAxis$createPlotElement(), + axis.line.y = private$.yAxis$createPlotElement(), + panel.grid.major.x = private$.xGrid$createPlotElement(), + panel.grid.major.y = private$.yGrid$createPlotElement() ) - plotObject <- addWatermark(plotObject = plotObject, label = self$watermark) return(plotObject) } - ) + ), + active = list( + #' @field watermark `Label` object + watermark = function(value) { + if (missing(value)) { + return(private$.watermark) + } + validateIsOfType(value, c("character", "Label"), nullAllowed = TRUE) + value <- value %||% private$.watermark$text + # Enforce watermark as Label with value + if (isOfType(value, "character")) { + value <- asLabel(text = value, font = private$.watermark$font) + } + private$.watermark <- value + return(invisible()) + }, + #' @field plot `BackgroundElement` object + plot = function(value) { + requestOnElement(private$.plot, value) + }, + #' @field panel `BackgroundElement` object + panel = function(value) { + requestOnElement(private$.panel, value) + }, + #' @field xAxis `LineElement` object + xAxis = function(value) { + requestOnElement(private$.xAxis, value) + }, + #' @field yAxis `LineElement` object + yAxis = function(value) { + requestOnElement(private$.yAxis, value) + }, + #' @field xGrid `LineElement` object + xGrid = function(value) { + requestOnElement(private$.xGrid, value) + }, + #' @field yGrid `LineElement` object + yGrid = function(value) { + requestOnElement(private$.yGrid, value) + } + ), + private = list( + .watermark = NULL, + .plot = NULL, + .panel = NULL, + .xAxis = NULL, + .yAxis = NULL, + .xGrid = NULL, + .yGrid = NULL + ), ) -#' @title BackgroundElementConfiguration -#' @description R6 class defining the configuration of background elements +#' @title BackgroundElement +#' @description R6 class defining the properties of background elements +#' @field fill character defining the color filling of the background element +#' @field color character defining the color of the background element frame/line +#' @field size numeric defining the size of the background element frame/line +#' @field linetype character defining the size of the background element frame/line #' @export -BackgroundElementConfiguration <- R6::R6Class( - "BackgroundElementConfiguration", +BackgroundElement <- R6::R6Class( + "BackgroundElement", public = list( - #' @field fill character color filling of the background element fill = NULL, - #' @field color character color of the frame of the background element color = NULL, - #' @field size character size of the frame of the background element size = NULL, - #' @field linetype character linetype of the frame of the background element linetype = NULL, - #' @description Create a new \code{BackgroundElementConfiguration} object + #' @description Create a new \code{BackgroundElement} object #' @param fill character color filling of the background element #' @param color character color of the frame of the background element #' @param size character size of the frame of the background element #' @param linetype character linetype of the frame of the background element - #' @param theme R6 class \code{Theme} object - #' @return A new \code{BackgroundElementConfiguration} object + #' @return A new \code{BackgroundElement} object initialize = function(fill = NULL, color = NULL, size = NULL, - linetype = NULL, - theme = NULL) { - self$fill <- fill %||% theme$fill - self$color <- color %||% theme$color - self$size <- size %||% theme$size - self$linetype <- linetype %||% theme$linetype - }, + linetype = NULL) { + validateIsString(c(fill, color, linetype), nullAllowed = TRUE) + validateIsNumeric(size, nullAllowed = TRUE) - #' @description Print background element properties - #' @return Background element properties - print = function() { - backgroundProperties <- NULL + fieldNames <- c("fill", "color", "size", "linetype") + setPropertiesExpression <- parse(text = paste0("self$", fieldNames, " <- ", fieldNames)) + eval(setPropertiesExpression) + }, - # Get properties that are of character type - elementProperties <- unlist(eapply( - self, - function(x) { - isOfType(x, "character") - } - )) - elementNames <- names(elementProperties[as.logical(elementProperties)]) + #' @description Create a `ggplot2::element_rect` directly usable by `ggplot2::theme`. + #' @return An `element_rect` object. + createPlotElement = function() { + ggplot2::element_rect( + fill = self$fill, + colour = self$color, + size = as.numeric(self$size), + linetype = self$linetype + ) + } + ) +) - # Build data.frame of properties while removing NULL values - for (elementName in elementNames) { - backgroundProperties <- rbind.data.frame( - backgroundProperties, - data.frame( - Property = elementName, - Value = self[[elementName]] - ) - ) - } - return(backgroundProperties) +#' @title LineElement +#' @description R6 class defining the properties of background line elements +#' @export +LineElement <- R6::R6Class( + "LineElement", + inherit = BackgroundElement, + public = list( + #' @description Create a `ggplot2::element_line` directly usable by `ggplot2::theme`. + #' @return An `element_line` object. + createPlotElement = function() { + ggplot2::element_line( + colour = self$color, + size = as.numeric(self$size), + linetype = self$linetype + ) } ) ) + +requestOnElement <- function(field, value) { + if (missing(value)) { + return(field) + } + # Update the element partially in case of names list + if (isOfType(value, "list")) { + for (fieldName in c("color", "size", "linetype")) { + field[[fieldName]] <- value[[fieldName]] %||% field[[fieldName]] + } + if (isOfType(field, "BackgroundElement")) { + field[["fill"]] <- value[["fill"]] %||% field[["fill"]] + } + } + # Or update the whole element R6 object is used + if (isOfType(value, "BackgroundElement")) { + field <- value + } +} diff --git a/R/plotconfiguration-label.R b/R/plotconfiguration-label.R index c3188fbb..2387bd6e 100644 --- a/R/plotconfiguration-label.R +++ b/R/plotconfiguration-label.R @@ -4,89 +4,94 @@ LabelConfiguration <- R6::R6Class( "LabelConfiguration", public = list( - #' @field title R6 class \code{Label} object - title = NULL, - #' @field subtitle R6 class \code{Label} object - subtitle = NULL, - #' @field xlabel R6 class \code{Label} object - xlabel = NULL, - #' @field ylabel R6 class \code{Label} object - ylabel = NULL, - #' @description Create a new \code{LabelConfiguration} object - #' @param title R6 class \code{Label} object - #' @param subtitle R6 class \code{Label} object - #' @param xlabel R6 class \code{Label} object - #' @param ylabel R6 class \code{Label} object - #' @param theme R6 class \code{Theme} + #' @param title character or \code{Label} object defining title + #' @param subtitle character or \code{Label} object defining subtitle + #' @param xlabel character or \code{Label} object defining xlabel + #' @param ylabel character or \code{Label} object defining ylabel #' @return A new \code{LabelConfiguration} object initialize = function(title = NULL, subtitle = NULL, xlabel = NULL, - ylabel = NULL, - theme = tlfEnv$currentTheme) { + ylabel = NULL) { inputs <- c("title", "subtitle", "xlabel", "ylabel") validateExpressions <- parse(text = paste0("validateIsOfType(", inputs, ', c("Label", "character"), nullAllowed =TRUE)')) eval(validateExpressions) + currentTheme <- tlfEnv$currentTheme$clone(deep = TRUE) enforceLabelExpressions <- parse(text = paste0( - "if(isOfType(", inputs, ',"character")){', - inputs, "<- asLabel(text = ", inputs, ", font = theme$", inputs, "Font)}" + "if(!isOfType(", inputs, ',"Label")){', + inputs, "<- asLabel(text = ", inputs, ", font = currentTheme$fonts$", inputs, ")}" )) eval(enforceLabelExpressions) - associateExpressions <- parse(text = paste0("self$", inputs, " <- asLabel(", inputs, ")")) + associateExpressions <- parse(text = paste0("private$.", inputs, " <- asLabel(", inputs, ")")) eval(associateExpressions) }, - #' @description Set plot labels properties of a \code{ggplot} object + #' @description Update labels of a \code{ggplot} object and their properties #' @param plotObject a \code{ggplot} object #' @return A \code{ggplot} object - setPlotLabels = function(plotObject) { - - # Titles and axes labels + updatePlot = function(plotObject) { + validateIsOfType(plotObject, "ggplot") + # Update titles and axes labels plotObject <- plotObject + ggplot2::labs( - title = self$title$text, - subtitle = self$subtitle$text, - x = self$xlabel$text, - y = self$ylabel$text + title = private$.title$text, + subtitle = private$.subtitle$text, + x = private$.xlabel$text, + y = private$.ylabel$text ) - - plotObject <- setFontProperties( - plotObject, - titleFont = self$title$font, - subtitleFont = self$subtitle$font, - xAxisFont = self$xlabel$font, - yAxisFont = self$ylabel$font + plotObject <- plotObject + ggplot2::theme( + plot.title = private$.title$createPlotFont(), + plot.subtitle = private$.subtitle$createPlotFont(), + axis.title.x = private$.xlabel$createPlotFont(), + axis.title.y = private$.ylabel$createPlotFont() ) return(plotObject) + } + ), + active = list( + #' @field title \code{Label} object defining the title of the plot + title = function(value) { + if (missing(value)) { + return(private$.title) + } + validateIsOfType(value, c("character", "Label"), nullAllowed = TRUE) + private$.title <- asLabel(value) + return(invisible()) }, - - #' @description Print plot label properties - #' @return Plot label properties - print = function() { - labelProperties <- NULL - - # Get properties that are of Label type - elementProperties <- unlist(eapply( - self, - function(x) { - isOfType(x, "Label") - } - )) - elementNames <- names(elementProperties[as.logical(elementProperties)]) - - # Build data.frame of properties while removing NULL values - for (elementName in elementNames) { - labelProperties <- rbind.data.frame( - labelProperties, - data.frame( - Property = elementName, - Value = self[[elementName]]$text %||% "NULL" - ) - ) + #' @field subtitle \code{Label} object defining the subtitle of the plot + subtitle = function(value) { + if (missing(value)) { + return(private$.subtitle) + } + validateIsOfType(value, c("character", "Label"), nullAllowed = TRUE) + private$.subtitle <- asLabel(value) + return(invisible()) + }, + #' @field xlabel \code{Label} object defining the xlabel of the plot + xlabel = function(value) { + if (missing(value)) { + return(private$.xlabel) + } + validateIsOfType(value, c("character", "Label"), nullAllowed = TRUE) + private$.xlabel <- asLabel(value) + return(invisible()) + }, + #' @field ylabel \code{Label} object defining the ylabel of the plot + ylabel = function(value) { + if (missing(value)) { + return(private$.ylabel) } - return(labelProperties) + validateIsOfType(value, c("character", "Label"), nullAllowed = TRUE) + private$.ylabel <- asLabel(value) + return(invisible()) } + ), + private = list( + .title = NULL, + .subtitle = NULL, + .xlabel = NULL, + .ylabel = NULL ) ) diff --git a/R/plotconfiguration-legend.R b/R/plotconfiguration-legend.R index 44eb3907..c2237186 100644 --- a/R/plotconfiguration-legend.R +++ b/R/plotconfiguration-legend.R @@ -4,39 +4,129 @@ LegendConfiguration <- R6::R6Class( "LegendConfiguration", public = list( - #' @field position character position of the legend - position = NULL, - #' @field title character name of the legend - title = NULL, - #' @field caption data.frame with - caption = NULL, - #' @description Create a new \code{LegendConfiguration} object - #' @param position character position of the legend. - #' Use enum `LegendPositions` to assess available legend positions. - #' @param title character title of the legend caption. - #' Default `NULL` does not provide any legend title. - #' @param caption data.frame containing the legend caption properties + #' @param position position of the legend as defined by enum `LegendPositions` + #' @param caption data.frame containing the properties of the legend caption + #' @param title character title of the legend caption. A value of `NULL` removes the title. + #' @param titleFont \code{Font} object defining the font of the legend title + #' @param font \code{Font} object defining the font of the legend caption + #' @param background \code{BackgroundElement} object defining the background of the legend #' @return A new \code{LegendConfiguration} object - initialize = function(position = tlfEnv$defaultLegendPosition, + initialize = function(position = NULL, + caption = NULL, title = NULL, - caption = NULL) { - validateIsIncluded(position, LegendPositions) + titleFont = NULL, + font = NULL, + background = NULL) { + validateIsIncluded(position, LegendPositions, nullAllowed = TRUE) + validateIsString(title, nullAllowed = TRUE) + validateIsOfType(titleFont, "Font", nullAllowed = TRUE) + validateIsOfType(font, "Font", nullAllowed = TRUE) + validateIsOfType(background, "BackgroundElement", nullAllowed = TRUE) + + currentTheme <- tlfEnv$currentTheme$clone(deep = TRUE) + private$.position <- position %||% currentTheme$background$legendPosition + private$.font <- font %||% currentTheme$fonts$legend + private$.titleFont <- titleFont %||% currentTheme$fonts$legendTitle + private$.background <- background %||% currentTheme$background$legend - self$position <- position - self$title <- title - self$caption <- caption + private$.title <- title + private$.caption <- caption %||% data.frame() }, - #' @description Print legend properties - #' @return Legend properties - print = function() { - legendProperties <- list( - title = self$title, - position = self$position, - caption = self$caption + #' @description Update legend configuration on a \code{ggplot} object + #' @param plotObject \code{ggplot} object + #' @return A \code{ggplot} object with updated axis properties + updatePlot = function(plotObject) { + validateIsOfType(plotObject, "ggplot") + # Update legend background, font and title font + plotObject <- plotObject + + ggplot2::theme( + legend.background = private$.background$createPlotElement(), + legend.text = private$.font$createPlotFont(), + legend.title = private$.titleFont$createPlotFont() + ) + + # For legend title, if no title, element_blank should be used + if (isOfLength(private$.title, 0)) { + plotObject <- plotObject + ggplot2::theme(legend.title = ggplot2::element_blank()) + } + # Update legend position + legendPosition <- getLegendPosition(private$.position) + plotObject <- plotObject + ggplot2::theme( + legend.position = c(legendPosition$xPosition, legendPosition$yPosition), + legend.justification = c(legendPosition$xJustification, legendPosition$yJustification), + legend.direction = "vertical" ) - return(legendProperties) + # TO DO: update caption properties before returning plot + return(plotObject) + } + ), + active = list( + #' @field caption of legend defined as data.frame with caption properties + caption = function(value) { + if (missing(value)) { + return(private$.caption) + } + validateIsOfType(value, "data.frame") + private$.caption <- value + return(invisible()) + }, + #' @field position of legend as defined in Enum `LegendPositions` + position = function(value) { + if (missing(value)) { + return(private$.position) + } + validateIsIncluded(value, LegendPositions) + private$.position <- value + return(invisible()) + }, + #' @field font \code{Font} object defining the font of the legend + font = function(value) { + if (missing(value)) { + return(private$.font) + } + validateIsOfType(value, "Font", nullAllowed = TRUE) + currentTheme <- tlfEnv$currentTheme$clone(deep = TRUE) + private$.font <- value %||% currentTheme$fonts$legend + return(invisible()) + }, + #' @field titleFont \code{Font} object defining the font of the legend title + titleFont = function(value) { + if (missing(value)) { + return(private$.titleFont) + } + validateIsOfType(value, "Font", nullAllowed = TRUE) + currentTheme <- tlfEnv$currentTheme$clone(deep = TRUE) + private$.titleFont <- value %||% currentTheme$fonts$legendTitle + return(invisible()) + }, + #' @field background \code{Background} object defining the background of the legend + background = function(value) { + if (missing(value)) { + return(private$.background) + } + validateIsOfType(value, "BackgroundElement", nullAllowed = TRUE) + currentTheme <- tlfEnv$currentTheme$clone(deep = TRUE) + private$.background <- value %||% currentTheme$background$legend + return(invisible()) + }, + #' @field title character defining title of the legend + title = function(value) { + if (missing(value)) { + return(private$.title) + } + validateIsString(value, nullAllowed = TRUE) + private$.title <- value + return(invisible()) } + ), + private = list( + .position = NULL, + .title = NULL, + .titleFont = NULL, + .font = NULL, + .background = NULL, + .caption = NULL ) ) diff --git a/R/plotconfiguration.R b/R/plotconfiguration.R index 27ecc0cd..e45a9871 100644 --- a/R/plotconfiguration.R +++ b/R/plotconfiguration.R @@ -1,41 +1,33 @@ #' @title PlotConfiguration #' @description R6 class defining the configuration of a \code{ggplot} object +#' @field export R6 class \code{ExportConfiguration} defining export properties #' @export PlotConfiguration <- R6::R6Class( "PlotConfiguration", public = list( - #' @field labels R6 class \code{LabelConfiguration} defining labels properties - labels = NULL, - #' @field legend R6 class \code{LegendConfiguration} defining legend properties - legend = NULL, - #' @field xAxis R6 class \code{XAxisConfiguration} defining X-axis properties - xAxis = NULL, - #' @field yAxis R6 class \code{YAxisConfiguration} defining Y-axis properties - yAxis = NULL, - #' @field background R6 class \code{BackgroundConfiguration} defining background properties - background = NULL, - #' @field export R6 class \code{ExportConfiguration} defining export properties export = NULL, - #' @field theme \code{Theme} R6 class defining theme aesthetic properties - theme = NULL, #' @description Create a new \code{PlotConfiguration} object - #' @param title R6 class \code{Label} object - #' @param subtitle R6 class \code{Label} object - #' @param xlabel R6 class \code{Label} object - #' @param ylabel R6 class \code{Label} object - #' @param legend R6 class \code{LegendConfiguration} object defining legend properties - #' @param legendTitle character legend title - #' @param legendPosition character legend position. + #' @param title character or \code{Label} object defining plot title + #' @param subtitle character or \code{Label} object defining plot subtitle + #' @param xlabel character or \code{Label} object defining plot xlabel + #' @param ylabel character or \code{Label} object defining plot ylabel + #' @param legend \code{LegendConfiguration} object defining legend properties + #' @param legendTitle character defining legend title + #' @param legendPosition character defining legend position. #' Use Enum `LegendPositions` to get a list of available to legend positions. - #' @param xAxis R6 class \code{XAxisConfiguration} object defining X-axis properties - #' @param xScale character defining X-axis scale. Use enum `Scaling` to access predefined scales. - #' @param xLimits numeric vector of X-axis limits - #' @param yAxis R6 class \code{YAxisConfiguration} object defining X-axis properties - #' @param yScale character defining Y-axis scale. Use enum `Scaling` to access predefined scales. - #' @param yLimits numeric vector of Y-axis limits - #' @param background R6 class \code{BackgroundConfiguration} defining background properties - #' @param watermark R6 class \code{Label} object defining watermark background + #' @param xAxis \code{XAxisConfiguration} object defining x-axis properties + #' @param xScale name of X-axis scale. Use enum `Scaling` to access predefined scales. + #' @param xLimits numeric vector of length 2 defining x-axis limits + #' @param yAxis \code{YAxisConfiguration} object defining y-axis properties + #' @param yScale name of y-axis scale. Use enum `Scaling` to access predefined scales. + #' @param yLimits numeric vector of length 2 defining y-axis limits + #' @param background \code{BackgroundConfiguration} object defining background properties + #' @param plotArea \code{BackgroundElement} object defining properties of plot area + #' @param panelArea \code{BackgroundElement} object defining properties of panel area + #' @param xGrid \code{LineElement} object defining properties of x-grid background + #' @param yGrid \code{LineElement} object defining properties of y-grid background + #' @param watermark \code{Label} object defining watermark #' @param export R6 class \code{SaveConfiguration} defining saving properties #' @param format character defining the format of the file to be saved #' @param width numeric values defining the width in `units` of the plot dimensions after saving @@ -44,7 +36,6 @@ PlotConfiguration <- R6::R6Class( #' @param data data.frame used by \code{smartMapping} #' @param metaData list of information on \code{data} #' @param dataMapping R6 class or subclass \code{XYDataMapping} - #' @param theme R6 class \code{Theme} #' @return A new \code{PlotConfiguration} object initialize = function( # Label configuration title = NULL, @@ -65,6 +56,10 @@ PlotConfiguration <- R6::R6Class( yLimits = NULL, # Background configuration background = NULL, + plotArea = NULL, + panelArea = NULL, + xGrid = NULL, + yGrid = NULL, watermark = NULL, # Export configuration export = NULL, @@ -75,13 +70,13 @@ PlotConfiguration <- R6::R6Class( # Smart configuration using metaData data = NULL, metaData = NULL, - dataMapping = NULL, - # Theme - theme = tlfEnv$currentTheme) { - self$labels <- LabelConfiguration$new( + dataMapping = NULL) { + + # Label configuration + # validation of the input is done within the creation of the object + private$.labels <- LabelConfiguration$new( title = title, subtitle = subtitle, - xlabel = xlabel, ylabel = ylabel, - theme = theme + xlabel = xlabel, ylabel = ylabel ) # Smart configuration if xlabel and ylabel @@ -91,39 +86,57 @@ PlotConfiguration <- R6::R6Class( if (!is.null(data)) { dataMapping <- dataMapping %||% XYGDataMapping$new(data = data) } - self$labels$xlabel <- asLabel(xlabel %||% + private$.labels$xlabel <- asLabel(xlabel %||% dataMappingLabel(dataMapping$x, metaData) %||% dataMapping$x %||% - self$labels$xlabel) - self$labels$ylabel <- asLabel(ylabel %||% + private$.labels$xlabel$text, font = private$.labels$xlabel$font) + private$.labels$ylabel <- asLabel(ylabel %||% dataMappingLabel(dataMapping$y, metaData) %||% dataMapping$y %||% dataMappingLabel(dataMapping$ymax, metaData) %||% dataMapping$ymax %||% - self$labels$ylabel) + private$.labels$ylabel, font = private$.labels$ylabel$font) - # Smart configuration if legend is not defined, - self$legend <- legend %||% LegendConfiguration$new() - self$legend$title <- legendTitle %||% self$legend$title + # Legend Configuration, overwrite some properties only if they are defined + validateIsOfType(legend, "LegendConfiguration", nullAllowed = TRUE) + private$.legend <- legend %||% LegendConfiguration$new() + private$.legend$title <- legendTitle %||% private$.legend$title validateIsIncluded(legendPosition, LegendPositions, nullAllowed = TRUE) - self$legend$position <- legendPosition %||% self$legend$position - - # Define X-Axis configuration, overwrite properties only if they are defined - self$xAxis <- xAxis %||% XAxisConfiguration$new(scale = xAxisDefaultScale(self)) - self$xAxis$limits <- xLimits %||% self$xAxis$limits - self$xAxis$scale <- xScale %||% self$xAxis$scale - - # Define Y-Axis configuration, overwrite properties only if they are defined - self$yAxis <- yAxis %||% YAxisConfiguration$new(scale = yAxisDefaultScale(self)) - self$yAxis$limits <- yLimits %||% self$yAxis$limits - self$yAxis$scale <- yScale %||% self$yAxis$scale - - # Set background properties - self$background <- background %||% BackgroundConfiguration$new( - watermark = watermark, - theme = theme - ) - self$background$watermark <- watermark %||% self$background$watermark + private$.legend$position <- legendPosition %||% private$.legend$position + + # X-Axis Configuration, overwrite some properties only if they are defined + validateIsOfType(xAxis, "XAxisConfiguration", nullAllowed = TRUE) + private$.xAxis <- xAxis %||% XAxisConfiguration$new(scale = xAxisDefaultScale(self)) + private$.xAxis$limits <- xLimits %||% private$.xAxis$limits + private$.xAxis$scale <- xScale %||% private$.xAxis$scale + + # Y-Axis configuration, overwrite some properties only if they are defined + validateIsOfType(yAxis, "YAxisConfiguration", nullAllowed = TRUE) + private$.yAxis <- yAxis %||% YAxisConfiguration$new(scale = yAxisDefaultScale(self)) + private$.yAxis$limits <- yLimits %||% private$.yAxis$limits + private$.yAxis$scale <- yScale %||% private$.yAxis$scale + + # Background configuration, overwrite some properties only if they are defined + validateIsOfType(background, "BackgroundConfiguration", nullAllowed = TRUE) + validateIsOfType(plotArea, "BackgroundElement", nullAllowed = TRUE) + validateIsOfType(panelArea, "BackgroundElement", nullAllowed = TRUE) + validateIsOfType(xGrid, "LineElement", nullAllowed = TRUE) + validateIsOfType(yGrid, "LineElement", nullAllowed = TRUE) + validateIsOfType(watermark, c("character", "Label"), nullAllowed = TRUE) + + private$.background <- background %||% BackgroundConfiguration$new() + private$.background$plot <- plotArea %||% private$.background$plot + private$.background$panel <- panelArea %||% private$.background$panel + private$.background$xGrid <- xGrid %||% private$.background$xGrid + private$.background$yGrid <- yGrid %||% private$.background$yGrid + private$.background$watermark <- watermark %||% private$.background$watermark + + # Define atom behaviour from theme + currentTheme <- tlfEnv$currentTheme$clone(deep = TRUE) + private$.lines <- asThemeAestheticSelections(currentTheme$plotConfigurations$addLine) + private$.ribbons <- asThemeAestheticSelections(currentTheme$plotConfigurations$addRibbon) + private$.points <- asThemeAestheticSelections(currentTheme$plotConfigurations$addScatter) + private$.errorbars <- asThemeAestheticSelections(currentTheme$plotConfigurations$addErrorbar) # Define export configuration, overwrite properties only if they are defined self$export <- export %||% ExportConfiguration$new() @@ -131,23 +144,101 @@ PlotConfiguration <- R6::R6Class( self$export$width <- width %||% self$export$width self$export$height <- height %||% self$export$height self$export$units <- units %||% self$export$units - - self$theme <- theme + } + ), + active = list( + #' @field labels \code{LabelConfiguration} object defining properties of labels + labels = function(value) { + if (missing(value)) { + return(private$.labels) + } + validateIsOfType(value, "LabelConfiguration", nullAllowed = TRUE) + private$.labels <- value %||% private$.labels + return(invisible()) }, - - #' @description Print plot configuration - #' @return Plot configuration - print = function() { - plotProperties <- list( - labels = self$labels$print(), - legend = self$legend$print(), - xAxis = self$xAxis$print(), - yAxis = self$yAxis$print(), - background = self$background$print(), - saveConfiguration = self$saveConfiguration$print() - ) - return(plotProperties) + #' @field legend \code{LegendConfiguration} object defining properties of legend + legend = function(value) { + if (missing(value)) { + return(private$.legend) + } + validateIsOfType(value, "LegendConfiguration", nullAllowed = TRUE) + private$.legend <- value %||% private$.legend + return(invisible()) + }, + #' @field xAxis \code{XAxisConfiguration} object defining properties of x-axis + xAxis = function(value) { + if (missing(value)) { + return(private$.xAxis) + } + validateIsOfType(value, "XAxisConfiguration", nullAllowed = TRUE) + private$.xAxis <- value %||% private$.xAxis + return(invisible()) + }, + #' @field yAxis \code{YAxisConfiguration} object defining properties of x-axis + yAxis = function(value) { + if (missing(value)) { + return(private$.yAxis) + } + validateIsOfType(value, "YAxisConfiguration", nullAllowed = TRUE) + private$.yAxis <- value %||% private$.yAxis + return(invisible()) + }, + #' @field background \code{BackgroundConfiguration} object defining properties of x-axis + background = function(value) { + if (missing(value)) { + return(private$.background) + } + validateIsOfType(value, "BackgroundConfiguration", nullAllowed = TRUE) + private$.background <- value %||% private$.background + return(invisible()) + }, + #' @field lines `ThemeAestheticSelections` defining properties of lines + lines = function(value) { + if (missing(value)) { + return(private$.lines) + } + validateIsOfType(value, "ThemeAestheticSelections", nullAllowed = TRUE) + private$.lines <- value %||% private$.lines + return(invisible()) + }, + #' @field ribbons `ThemeAestheticSelections` defining properties of ribbons + ribbons = function(value) { + if (missing(value)) { + return(private$.ribbons) + } + validateIsOfType(value, "ThemeAestheticSelections", nullAllowed = TRUE) + private$.ribbons <- value %||% private$.ribbons + return(invisible()) + }, + #' @field points `ThemeAestheticSelections` defining properties of points + points = function(value) { + if (missing(value)) { + return(private$.points) + } + validateIsOfType(value, "ThemeAestheticSelections", nullAllowed = TRUE) + private$.points <- value %||% private$.points + return(invisible()) + }, + #' @field errorbars `ThemeAestheticSelections` defining properties of error bars + errorbars = function(value) { + if (missing(value)) { + return(private$.errorbars) + } + validateIsOfType(value, "ThemeAestheticSelections", nullAllowed = TRUE) + private$.errorbars <- value %||% private$.errorbars + return(invisible()) } + ), + private = list( + .labels = NULL, + .legend = NULL, + .xAxis = NULL, + .yAxis = NULL, + .background = NULL, + .lines = NULL, + .ribbons = NULL, + .points = NULL, + .errorbars = NULL ) ) diff --git a/R/sysdata.rda b/R/sysdata.rda deleted file mode 100644 index f36d9be0..00000000 Binary files a/R/sysdata.rda and /dev/null differ diff --git a/R/themes.R b/R/themes.R index 82c2d844..e959912f 100644 --- a/R/themes.R +++ b/R/themes.R @@ -1,168 +1,577 @@ # Theme Properties and theme object definitions -# -## ------------------------------------------------- -# Load theme properties from a Json file -load("data/tlfEnvThemesProperties.RData") -# jsonlite::fromJSON("../data/default-themes.json") if imported from a .json - #' @title ThemeFont -#' @description R6 class defining theme fonts -#' @include utils.R +#' @description R6 class defining theme font properties +#' @field title \code{Font} object for font properties title +#' @field subtitle \code{Font} object for font properties of subtitle +#' @field xlabel \code{Font} object for font properties of xlabel +#' @field ylabel \code{Font} object for font properties of ylabel +#' @field watermark \code{Font} object for font properties of watermark +#' @field legendTitle \code{Font} object for font properties of legend title +#' @field legend \code{Font} object for font properties of legend +#' @field xAxis \code{Font} object for font properties of xAxis +#' @field yAxis \code{Font} object for font properties of yAxis #' @export ThemeFont <- R6::R6Class( "ThemeFont", + public = list( - #' @field titleFont R6 class \code{Font} object - titleFont = NULL, - #' @field subtitleFont R6 class \code{Font} object - subtitleFont = NULL, - #' @field xlabelFont R6 class \code{Font} object - xlabelFont = NULL, - #' @field ylabelFont R6 class \code{Font} object - ylabelFont = NULL, - #' @field watermarkFont R6 class \code{Font} object - watermarkFont = NULL, + title = NULL, + subtitle = NULL, + xlabel = NULL, + ylabel = NULL, + watermark = NULL, + legendTitle = NULL, + legend = NULL, + xAxis = NULL, + yAxis = NULL, #' @description Create a new \code{ThemeFont} object - #' @param labelColors list of colors for each label - #' @param labelBaseSize numeric value for theme base size + #' @param title \code{Font} object or list for font properties title + #' @param subtitle \code{Font} object or list for font properties of subtitle + #' @param xlabel \code{Font} object or list for font properties of xlabel + #' @param ylabel \code{Font} object or list for font properties of ylabel + #' @param watermark \code{Font} object or list for font properties of watermark + #' @param legendTitle \code{Font} object or list for font properties of legend + #' @param legend \code{Font} object or list for font properties of legend + #' @param xAxis \code{Font} object or list for font properties of xAxis + #' @param yAxis \code{Font} object or list for font properties of yAxis + #' @param baseColor name of base color of undefined fonts. Default is black. + #' @param baseSize base size of undefined fonts. Default is 12. + #' @param baseFace name of base face of undefined fonts. Default is "plain". + #' @param baseFamily name of base family of undefined fonts. Default is "". + #' @param baseAngle base angle of undefined fonts. Default is 0 degree. #' @return A new \code{ThemeFont} object - initialize = function(labelColors = tlfEnvThemesProperties$default$labelColors, - labelBaseSize = 14) { - - # Set font properties of labels - self$titleFont <- Font$new(color = labelColors$title, size = labelBaseSize * 2, fontFace = "bold") - self$subtitleFont <- Font$new(color = labelColors$subtitle, size = labelBaseSize * 1.5, fontFace = "italic") - self$xlabelFont <- Font$new(color = labelColors$xlabel, size = labelBaseSize) - self$ylabelFont <- Font$new(color = labelColors$ylabel, size = labelBaseSize) - self$watermarkFont <- Font$new(color = labelColors$watermark, size = labelBaseSize) + initialize = function(title = NULL, + subtitle = NULL, + xlabel = NULL, + ylabel = NULL, + watermark = NULL, + legendTitle = NULL, + legend = NULL, + xAxis = NULL, + yAxis = NULL, + baseColor = "black", + baseSize = 12, + baseFace = "plain", + baseFamily = "", + baseAngle = 0) { + # Validate necessary input + validateIsString(baseColor) + validateIsString(baseFace) + validateIsString(baseFamily) + validateIsNumeric(baseSize) + validateIsNumeric(baseAngle) + + # Create all field properties by parsing and evaluating their expression + fieldNames <- c("title", "subtitle", "xlabel", "ylabel", "watermark", "legendTitle", "legend", "xAxis", "yAxis") + setFontExpression <- parse(text = paste0("self$", fieldNames, " <- Font$new( + color = ", fieldNames, "$color %||% baseColor, + size = ", fieldNames, "$size %||% baseSize, + fontFace = ", fieldNames, "$fontFace %||% baseFace, + fontFamily = ", fieldNames, "$fontFamily %||% baseFamily, + angle = ", fieldNames, "$angle %||% baseAngle)")) + eval(setFontExpression) + }, + + #' @description Translate object into a json list + #' @return A list that can be saved into a json file + toJson = function() { + jsonObject <- list() + fieldNames <- c("title", "subtitle", "xlabel", "ylabel", "watermark", "legendTitle", "legend", "xAxis", "yAxis") + setJsonExpression <- parse(text = paste0("jsonObject$", fieldNames, " <- list( + color = self$", fieldNames, "$color, + size = self$", fieldNames, "$size, + angle = self$", fieldNames, "$angle, + fontFace = self$", fieldNames, "$fontFace, + fontFamily = self$", fieldNames, "$fontFamily)")) + eval(setJsonExpression) + return(jsonObject) } ) ) -#' @title ThemeAesProperties -#' @description R6 class defining theme aesthetic properties plots -#' @include utils.R +#' @title ThemeBackground +#' @description R6 class defining theme background properties +#' @field watermark character defining content of watermark +#' @field legendPosition character defining where legend should usually be placed +#' @field plot \code{BackgroundElement} object for plot area properties (outside of panel) +#' @field panel \code{BackgroundElement} object for plot area properties (inside of panel) +#' @field xAxis \code{BackgroundElement} object for x axis properties +#' @field yAxis \code{BackgroundElement} object for y axis properties +#' @field xGrid \code{BackgroundElement} object for x grid properties +#' @field yGrid \code{BackgroundElement} object for y grid properties +#' @field legend \code{BackgroundElement} object for legend area properties #' @export -ThemeAesProperties <- R6::R6Class( - "ThemeAesProperties", +ThemeBackground <- R6::R6Class( + "ThemeBackground", + + public = list( + watermark = NULL, + legendPosition = NULL, + plot = NULL, + panel = NULL, + xAxis = NULL, + yAxis = NULL, + xGrid = NULL, + yGrid = NULL, + legend = NULL, + + #' @description Create a new \code{ThemeBackground} object + #' @param watermark character defining content of watermark + #' @param legendPosition character defining where legend should usually be placed + #' @param plot \code{BackgroundElement} object or list for plot area properties (outside of panel) + #' @param panel \code{BackgroundElement} object or list for plot area properties (inside of panel) + #' @param xAxis \code{BackgroundElement} object or list for x axis properties + #' @param yAxis \code{BackgroundElement} object or list for y axis properties + #' @param xGrid \code{BackgroundElement} object or list for x grid properties + #' @param yGrid \code{BackgroundElement} object or list for y grid properties + #' @param legend \code{BackgroundElement} object or list for legend area properties + #' @param baseFill name of base color fill of undefined background elements. Default is white. + #' @param baseColor name of base color of undefined background elements. Default is black. + #' @param baseSize name of base size of undefined background elements. Default is 0.5. + #' @param baseLinetype name of base size of undefined background elements. Default is "solid". + #' @return A new \code{ThemeFont} object + initialize = function(watermark = NULL, + legendPosition = NULL, + plot = NULL, + panel = NULL, + xAxis = NULL, + yAxis = NULL, + xGrid = NULL, + yGrid = NULL, + legend = NULL, + baseFill = "white", + baseColor = "black", + baseSize = 0.5, + baseLinetype = "solid") { + # Validate necessary input + validateIsString(baseFill) + validateIsString(baseColor) + validateIsString(baseLinetype) + validateIsNumeric(baseSize) + + self$watermark <- watermark %||% "" + self$legendPosition <- legendPosition %||% LegendPositions$outsideRight + + # Create all field properties by parsing and evaluating their expression + areaFieldNames <- c("plot", "panel", "legend") + lineFieldNames <- c("xAxis", "yAxis", "xGrid", "yGrid") + + setAreaExpression <- parse(text = paste0("self$", areaFieldNames, " <- BackgroundElement$new(fill = ", areaFieldNames, "$fill %||% baseFill, color = ", areaFieldNames, "$color %||% baseColor, size = ", areaFieldNames, "$size %||% baseSize, linetype = ", areaFieldNames, "$linetype %||% baseLinetype)")) + setLineExpression <- parse(text = paste0("self$", lineFieldNames, " <- LineElement$new(color = ", lineFieldNames, "$color %||% baseColor, size = ", lineFieldNames, "$size %||% baseSize, linetype = ", lineFieldNames, "$linetype %||% baseLinetype)")) + eval(setAreaExpression) + eval(setLineExpression) + }, + + #' @description Translate object into a json list + #' @return A list that can be saved into a json file + toJson = function() { + jsonObject <- list() + jsonObject$watermark <- self$watermark + jsonObject$legendPosition <- self$legendPosition + areaFieldNames <- c("plot", "panel", "legend") + lineFieldNames <- c("xAxis", "yAxis", "xGrid", "yGrid") + + setJsonAreaExpression <- parse(text = paste0("jsonObject$", areaFieldNames, " <- list( + fill = self$", areaFieldNames, "$fill, + color = self$", areaFieldNames, "$color, + size = self$", areaFieldNames, "$size, + linetype = self$", areaFieldNames, "$linetype)")) + setJsonLineExpression <- parse(text = paste0("jsonObject$", lineFieldNames, " <- list( + color = self$", lineFieldNames, "$color, + size = self$", lineFieldNames, "$size, + linetype = self$", lineFieldNames, "$linetype)")) + eval(setJsonAreaExpression) + eval(setJsonLineExpression) + return(jsonObject) + } + ) +) + +#' @title ThemeAestheticMaps +#' @description R6 class defining theme aesthetic maps +#' @field color color map as character or numeric vector +#' @field fill fill map as character or numeric vector +#' @field size size map as numeric vector +#' @field shape shape map as numeric vector +#' @field linetype linetype as character vector +#' @field alpha map as numeric vector +#' @export +ThemeAestheticMaps <- R6::R6Class( + "ThemeAestheticMaps", + public = list( - #' @field color character vector of color properties color = NULL, - #' @field shape character vector of shape properties + fill = NULL, shape = NULL, - #' @field size numeric vector of size properties size = NULL, - #' @field fill character vector of fill properties - fill = NULL, - #' @field linetype character vector of linetype properties linetype = NULL, - #' @field alpha numeric vector of alpha properties alpha = NULL, - #' @description Create a new \code{ThemeAesProperties} object - #' @param aesProperties list of aesthetic properties - #' @param color character vector of color properties - #' @param shape character vector of shape properties - #' @param size numeric vector of size properties - #' @param fill character vector of fill properties - #' @param linetype character vector of linetype properties - #' @param alpha numeric vector of alpha properties - #' @return A new \code{ThemeAesProperties} object - initialize = function(aesProperties = tlfEnvThemesProperties$default$aesProperties, - color = NULL, + #' @description Create a new \code{ThemeAestheticMaps} object + #' @param color color map as list, character or numeric vector + #' @param fill fill map as list, character or numeric vector + #' @param shape shape map as list, character or numeric vector + #' @param size size map as list, character or numeric vector + #' @param linetype linetype map as list, character or numeric vector + #' @param alpha alpha map as list, character or numeric vector + #' @return A new \code{ThemeAestheticMaps} object + initialize = function(color = NULL, + fill = NULL, shape = NULL, size = NULL, + linetype = NULL, + alpha = NULL) { + + # Validate necessary input + validateIsString(color, nullAllowed = TRUE) + validateIsString(fill, nullAllowed = TRUE) + validateIsString(linetype, nullAllowed = TRUE) + + validateIsOfType(shape, c("numeric", "character"), nullAllowed = TRUE) + + validateIsNumeric(size, nullAllowed = TRUE) + validateIsNumeric(alpha, nullAllowed = TRUE) + + # Default aesthetic maps + self$color <- color %||% ColorMaps$default + self$fill <- fill %||% ColorMaps$default + self$shape <- shape %||% as.numeric(Shapes) + self$linetype <- linetype %||% as.character(Linetypes) + self$size <- size %||% seq(1, 5) + self$alpha <- alpha %||% c(0.75, 0.5, 0.25) + + # Checks shapes and linetype according to ggplot2 standards + self$shape <- asPlotShape(self$shape) + validateIsIncluded(self$linetype, Linetypes) + }, + + #' @description Translate object into a json list + #' @return A list that can be saved into a json file + toJson = function() { + jsonObject <- list() + fieldNames <- names(AestheticProperties) + + setJsonExpression <- parse(text = paste0("jsonObject$", fieldNames, " <- self$", fieldNames)) + eval(setJsonExpression) + return(jsonObject) + } + ) +) + +#' @title ThemeAestheticSelections +#' @description R6 class defining how plot configurations will use aesthetic maps +#' @export +ThemeAestheticSelections <- R6::R6Class( + "ThemeAestheticSelections", + inherit = ThemeAestheticMaps, + public = list( + + #' @description Create a new \code{ThemeAestheticSelections} object + #' @param color selection key or values for choice of color + #' @param fill selection key or values for choice of fill + #' @param shape selection key or values for choice of shape + #' @param size selection key or values for choice of size + #' @param linetype selection key or values for choice of linetype + #' @param alpha selection key or values for choice of alpha + #' @return A new \code{ThemeAestheticSelections} object + initialize = function(color = NULL, fill = NULL, + shape = NULL, + size = NULL, linetype = NULL, alpha = NULL) { - self$color <- color %||% aesProperties$color - self$shape <- shape %||% aesProperties$shape - self$size <- size %||% aesProperties$size - self$fill <- fill %||% aesProperties$fill - self$linetype <- linetype %||% aesProperties$linetype - self$alpha <- alpha %||% aesProperties$alpha + + # Associate to each field its value + initializeExpression <- parse(text = paste0("self$", names(AestheticProperties), " <- ", names(AestheticProperties), " %||% 'first'")) + eval(initializeExpression) + }, + + #' @description Translate object into a json list + #' @return A list that can be saved into a json file + toJson = function() { + jsonObject <- list() + fieldNames <- names(AestheticProperties) + + setJsonExpression <- parse(text = paste0("jsonObject$", fieldNames, " <- self$", fieldNames)) + eval(setJsonExpression) + return(jsonObject) + } + ) +) + +#' @title ThemePlotConfigurations +#' @description R6 class defining theme of plot configuration objects +#' @field addScatter theme properties for `PlotConfiguration` objects as used in function `addScatter()` +#' @field addLine theme properties for `PlotConfiguration` objects as used in function `addLine()` +#' @field addRibbon theme properties for `PlotConfiguration` objects as used in function `addRibbon()` +#' @field addErrorbar theme properties for `PlotConfiguration` objects as used in function `addErrorbar()` +#' @field plotPKRatio theme properties for `PlotConfiguration` objects as used in function `plotPKRatio()` +#' @field plotDDIRatio theme properties for `PlotConfiguration` objects as used in function `plotDDIRatio()` +#' @field plotTimeProfile theme properties for `PlotConfiguration` objects as used in function `plotTimeProfile()` +#' @field plotObsVsPred theme properties for `PlotConfiguration` objects as used in function `plotObsVsPred()` +#' @field plotBoxWhisker theme properties for `PlotConfiguration` objects as used in function `plotBoxWhisker()` +#' @field plotTornado theme properties for `PlotConfiguration` objects as used in function `plotTornado()` +#' @field plotHistogram theme properties for `PlotConfiguration` objects as used in function `plotHistogram()` +#' @export +ThemePlotConfigurations <- R6::R6Class( + "ThemePlotConfigurations", + + public = list( + addScatter = NULL, + addLine = NULL, + addRibbon = NULL, + addErrorbar = NULL, + plotPKRatio = NULL, + plotDDIRatio = NULL, + plotTimeProfile = NULL, + plotObsVsPred = NULL, + plotBoxWhisker = NULL, + plotTornado = NULL, + plotHistogram = NULL, + + #' @description Create a new \code{ThemePlotConfigurations} object + #' @param addScatter theme properties for `PlotConfiguration` objects as used in function `addScatter()` + #' @param addLine theme properties for `PlotConfiguration` objects as used in function `addLine()` + #' @param addRibbon theme properties for `PlotConfiguration` objects as used in function `addRibbon()` + #' @param addErrorbar theme properties for `PlotConfiguration` objects as used in function `addErrorbar()` + #' @param plotPKRatio theme properties for `PlotConfiguration` objects as used in function `plotPKRatio()` + #' @param plotDDIRatio theme properties for `PlotConfiguration` objects as used in function `plotDDIRatio()` + #' @param plotTimeProfile theme properties for `PlotConfiguration` objects as used in function `plotTimeProfile()` + #' @param plotObsVsPred theme properties for `PlotConfiguration` objects as used in function `plotObsVsPred()` + #' @param plotBoxWhisker theme properties for `PlotConfiguration` objects as used in function `plotBoxWhisker()` + #' @param plotTornado theme properties for `PlotConfiguration` objects as used in function `plotTornado()` + #' @param plotHistogram theme properties for `PlotConfiguration` objects as used in function `plotHistogram()` + #' @return A new \code{ThemePlotConfigurations} object + initialize = function(addScatter = NULL, + addLine = NULL, + addRibbon = NULL, + addErrorbar = NULL, + plotPKRatio = NULL, + plotDDIRatio = NULL, + plotTimeProfile = NULL, + plotObsVsPred = NULL, + plotBoxWhisker = NULL, + plotTornado = NULL, + plotHistogram = NULL) { + + # Validate necessary input + atomPlotInputs <- c("addScatter", "addLine", "addRibbon", "addErrorbar") + moleculePlotInputs <- c("plotPKRatio", "plotDDIRatio", "plotTimeProfile", "plotObsVsPred", "plotBoxWhisker", "plotTornado", "plotHistogram") + + validateExpressions <- parse(text = paste0("validateIsOfType(", atomPlotInputs, ", 'ThemeAestheticSelections', nullAllowed = TRUE)")) + eval(validateExpressions) + validateExpressions <- parse(text = paste0("validateIsOfType(c(", moleculePlotInputs, "), 'ThemeAestheticSelections', nullAllowed = TRUE)")) + eval(validateExpressions) + + # Default aesthetic for atom plots + self$addScatter <- addScatter %||% ThemeAestheticSelections$new(color = "next", fill = NA, shape = "next", linetype = "blank", size = "first", alpha = 1) + self$addLine <- addLine %||% ThemeAestheticSelections$new(color = "next", fill = NA, shape = "blank", linetype = "reset", size = "first", alpha = 1) + self$addRibbon <- addRibbon %||% ThemeAestheticSelections$new(color = "next", fill = "next", shape = "blank", linetype = "first", size = "same", alpha = 1) + self$addErrorbar <- addErrorbar %||% ThemeAestheticSelections$new(color = "reset", fill = NA, shape = "blank", linetype = "first", size = "same", alpha = 1) + + # Default aesthetic for molecule plots + self$plotPKRatio <- plotPKRatio %||% list( + lines = ThemeAestheticSelections$new(color = c("#000000", "#0078D7", "#D83B01"), linetype = c("longdash", "longdash", "longdash"), size = 0.5, alpha = 1), + points = ThemeAestheticSelections$new(color = "reset", fill = NA, shape = "reset", linetype = "blank", size = 3), + errorbars = ThemeAestheticSelections$new(color = "reset", fill = NA, shape = "blank", linetype = "solid", size = 1) + ) + self$plotDDIRatio <- plotDDIRatio %||% list( + lines = ThemeAestheticSelections$new(color = c("#000000", "#0078D7", "#D83B01"), linetype = c("longdash", "longdash", "longdash"), size = 0.5, alpha = 1), + points = ThemeAestheticSelections$new(color = "reset", fill = NA, shape = "reset", linetype = "blank", size = 3), + errorbars = ThemeAestheticSelections$new(color = "reset", fill = NA, shape = "blank", linetype = "solid", size = 1) + ) + self$plotTimeProfile <- plotTimeProfile %||% list( + lines = ThemeAestheticSelections$new(color = "reset", fill = "reset", shape = "blank", linetype = "reset", size = 1), + ribbons = ThemeAestheticSelections$new(color = "reset", fill = "reset", shape = "blank", linetype = "blank", size = 1, alpha = "first"), + points = ThemeAestheticSelections$new(color = "reset", fill = NA, shape = "reset", linetype = "blank", size = 3), + errorbars = ThemeAestheticSelections$new(color = "reset", fill = NA, shape = "blank", linetype = "solid", size = 1) + ) + self$plotObsVsPred <- plotObsVsPred %||% list( + lines = ThemeAestheticSelections$new(color = "reset", fill = NA, shape = "blank", linetype = "reset", size = 1), + points = ThemeAestheticSelections$new(color = "reset", fill = NA, shape = "reset", linetype = "blank", size = 3), + errorbars = ThemeAestheticSelections$new(color = "reset", fill = NA, shape = "blank", linetype = "solid", size = 1) + ) + self$plotBoxWhisker <- plotBoxWhisker %||% list( + ribbons = ThemeAestheticSelections$new(color = "#000000", fill = "next", linetype = "solid", size = 1, alpha = "first"), + points = ThemeAestheticSelections$new(color = "#000000", shape = "first", linetype = "blank", size = 1) + ) + self$plotTornado <- plotTornado %||% list( + lines = ThemeAestheticSelections$new(color = "#000000", fill = NA, shape = "blank", linetype = "longdash", size = 1), + ribbons = ThemeAestheticSelections$new(color = "reset", fill = "reset", shape = "blank", linetype = "solid", size = 1, alpha = "first"), + points = ThemeAestheticSelections$new(color = "reset", fill = NA, shape = "reset", linetype = "blank", size = 3) + ) + self$plotHistogram <- plotHistogram %||% list( + lines = ThemeAestheticSelections$new(color = "reset", fill = NA, shape = "blank", linetype = "reset", size = 1), + ribbons = ThemeAestheticSelections$new(color = "#000000", fill = "reset", shape = "blank", linetype = "solid", size = 0.5, alpha = "first") + ) + }, + + #' @description Translate object into a json list + #' @return A list that can be saved into a json file + toJson = function() { + jsonObject <- list() + fieldNames <- c("addScatter", "addLine", "addRibbon", "addErrorbar") + + setJsonExpression <- parse(text = paste0("jsonObject$", fieldNames, " <- self$", fieldNames, "$toJson()")) + eval(setJsonExpression) + return(jsonObject) } ) ) #' @title Theme -#' @description R6 class defining theme -#' @include utils.R +#' @description R6 class defining theme properties #' @export Theme <- R6::R6Class( "Theme", - inherit = ThemeFont, public = list( - #' @field watermark list of font properties for watermark - watermark = NULL, - #' @field background list of aesthetic properties for background configuration - background = NULL, - #' @field aesProperties list of aesthetic properties for plots in general - aesProperties = NULL, - #' @field defaultCaption aesthetic properties for specific plots - defaultCaption = NULL, #' @description Create a new \code{Theme} object - #' @param themesProperties list of aesthetic properties - #' @param labelColors list of colors for each label - #' @param labelBaseSize numeric value for theme base size - #' @param background list of aesthetic properties for background configuration - #' @param watermark list of font properties for watermark - #' @param aesProperties list of aesthetic properties for plots in general - #' @param defaultCaption list of aesthetic properties for specific plot features + #' @param fonts `ThemeFont` object + #' @param background `ThemeBackground` object + #' @param aestheticMaps `ThemeAestheticMaps` object + #' @param plotConfigurations `ThemePlotConfiguration` object #' @return A new \code{Theme} object - initialize = function(themesProperties = tlfEnvThemesProperties$default, - labelColors = NULL, - labelBaseSize = 14, + initialize = function(fonts = NULL, background = NULL, - watermark = NULL, - aesProperties = NULL, - defaultCaption = NULL) { - super$initialize( - labelColors = labelColors %||% themesProperties$labelColors, - labelBaseSize = labelBaseSize - ) + aestheticMaps = NULL, + plotConfigurations = NULL) { + validateIsOfType(fonts, "ThemeFont", nullAllowed = TRUE) + validateIsOfType(background, "ThemeBackground", nullAllowed = TRUE) + validateIsOfType(aestheticMaps, "ThemeAestheticMaps", nullAllowed = TRUE) + validateIsOfType(plotConfigurations, "ThemePlotConfigurations", nullAllowed = TRUE) - self$background <- background %||% themesProperties$background - ifnotnull(watermark, self$background$watermark <- watermark) + private$.fonts <- fonts %||% ThemeFont$new() + private$.background <- background %||% ThemeBackground$new() + private$.aestheticMaps <- aestheticMaps %||% ThemeAestheticMaps$new() + private$.plotConfigurations <- plotConfigurations %||% ThemePlotConfigurations$new() + }, - # Set the color, shape, size maps - self$aesProperties <- aesProperties %||% ThemeAesProperties$new(aesProperties = themesProperties$aesProperties) - - # Set the properties of specific plots - self$defaultCaption <- defaultCaption %||% themesProperties$defaultCaption + #' @description Save `Theme` as a json file + #' @param jsonFile name of json file + save = function(jsonFile) { + validateIsString(jsonFile) + themeContent <- list( + font = private$.fonts$toJson(), + background = private$.background$toJson(), + aestheticMaps = private$.aestheticMaps$toJson(), + plotConfigurations = private$.plotConfigurations$toJson() + ) + # Options added in saved json are to help users reading the output + jsonContent <- jsonlite::toJSON(themeContent, auto_unbox = TRUE, pretty = TRUE) + write(jsonContent, file = jsonFile) + return(invisible()) + } + ), + active = list( + #' @field fonts `ThemeFont` object + fonts = function(value) { + if (missing(value)) { + return(private$.fonts) + } + validateIsOfType(value, "ThemeFont") + private$.fonts <- value + }, + #' @field background `ThemeBackground` object + background = function(value) { + if (missing(value)) { + return(private$.background) + } + validateIsOfType(value, "ThemeBackground") + private$.background <- value + }, + #' @field aestheticMaps `ThemeAestheticMaps` object + aestheticMaps = function(value) { + if (missing(value)) { + return(private$.aestheticMaps) + } + validateIsOfType(value, "ThemeAestheticMaps") + private$.aestheticMaps <- value + }, + #' @field plotConfigurations `ThemePlotConfiguration` object + plotConfigurations = function(value) { + if (missing(value)) { + return(private$.plotConfigurations) + } + validateIsOfType(value, "ThemePlotConfigurations") + private$.plotConfigurations <- value } + ), + private = list( + .fonts = NULL, + .background = NULL, + .aestheticMaps = NULL, + .plotConfigurations = NULL ) ) -## ------------------------------------------------- -# Definition of a few standard themes to be used - -#' @title defaultTheme -#' @description -#' Default theme for plot configuration +#' @title loadThemeFromJson +#' @description Load theme object from json file. +#' A template of a json theme is available at system.file(package= "tlf", "theme-maker","theme-template.json") +#' @param jsonFile path of json file +#' @return A \code{Theme} object #' @export -defaultTheme <- Theme$new() +#' @import jsonlite +loadThemeFromJson <- function(jsonFile) { + # Get the content of json file and define lists of its properties + themeContent <- jsonlite::fromJSON(jsonFile) + themeProperties <- names(themeContent) -#' @title tlfTheme -#' @description -#' tlf theme for plot configuration -#' @export -tlfTheme <- Theme$new(themesProperties = tlfEnvThemesProperties$tlf) + # Define default `Theme` properties, properties defined in json will overwrite those + fonts <- ThemeFont$new() + background <- ThemeBackground$new() + aestheticMaps <- ThemeAestheticMaps$new() + plotConfigurations <- ThemePlotConfigurations$new() -#' @title bwTheme -#' @description -#' Black and White theme for plot configuration -#' @export -bwTheme <- Theme$new(themesProperties = tlfEnvThemesProperties$bw) + for (themeProperty in themeProperties) { + if (!isIncluded(themeProperty, c("fonts", "background", "aestheticMaps", "plotConfigurations"))) { + next + } + propertyFields <- names(themeContent[[themeProperty]]) + for (propertyField in propertyFields) { + inputs <- names(themeContent[[themeProperty]][[propertyField]]) + if (isOfLength(inputs, 0)) { + next + } + # Expressions overwriting the properties: + # For each theme property (w.g. fonts, background ...), associate its values (e.g. size, color ...) + propertyExpression <- parse(text = paste0( + themeProperty, "$", propertyField, "$", inputs, + " <- themeContent$", themeProperty, "$", propertyField, "$", inputs + )) + eval(propertyExpression) + } + # Some specific cases are missing from the expressions + background$watermark <- themeContent$background$watermark + background$legendPosition <- themeContent$background$legendPosition + } + return(Theme$new( + fonts = fonts, + background = background, + aestheticMaps = aestheticMaps, + plotConfigurations = plotConfigurations + )) +} -#' @title bigTheme -#' @description -#' Big theme for plot configuration + +#' @title saveThemeToJson +#' @description Save theme object to a json file. +#' @param jsonFile path of json file +#' @param theme `Theme` object path of json file #' @export -bigTheme <- Theme$new(labelBaseSize = 20, watermark = "Big") +saveThemeToJson <- function(jsonFile, theme = NULL) { + # Check that theme is a Theme + validateIsOfType(theme, "Theme", nullAllowed = TRUE) + validateIsString(jsonFile) + + if (isOfLength(theme, 0)) { + theme <- tlfEnv$currentTheme + } + theme$save(jsonFile) + return(invisible()) +} ## ------------------------------------------------- #' @title useTheme @@ -173,5 +582,17 @@ bigTheme <- Theme$new(labelBaseSize = 20, watermark = "Big") #' @export #' useTheme <- function(theme) { + validateIsOfType(theme, "Theme") tlfEnv$currentTheme <- theme } + +## ------------------------------------------------- +#' @title runThemeMaker +#' @description +#' Run shiny app that allows easy setting of Theme objects. +#' Theme objects drive default properties of plots +#' @export +runThemeMaker <- function() { + appPath <- system.file("theme-maker", package = "tlf") + shiny::runApp(appPath) +} diff --git a/R/timeprofile-datamapping.R b/R/timeprofile-datamapping.R index 72c00959..b9d173ca 100644 --- a/R/timeprofile-datamapping.R +++ b/R/timeprofile-datamapping.R @@ -3,26 +3,61 @@ #' @export TimeProfileDataMapping <- R6::R6Class( "TimeProfileDataMapping", - inherit = XYGDataMapping, - + inherit = RangeDataMapping, public = list( - #' @field lloq numeric value of lower limit of quantification - lloq = NULL, - #' @field isRangeTimeProfile logical to set aggregation of data - isRangeTimeProfile = NULL, - #' @description Create a new \code{TimeProfileDataMapping} object - #' @param lloq numeric value of lower limit of quantification - #' @param isRangeTimeProfile Name of x variable to map - #' @param ... parameters inherited from \code{XYGDataMapping} - #' @return A new \code{TimeProfileDataMapping} object - initialize = function(lloq = NULL, - isRangeTimeProfile = FALSE, - ...) { - validateIsLogical(isRangeTimeProfile) - super$initialize(...) - self$lloq <- lloq - self$isRangeTimeProfile <- isRangeTimeProfile + #' @param x Name of x variable to map + #' @param y Name of y variable to map + #' @param ymin Name of ymin variable to map + #' @param ymax Name of ymax variable to map + #' @param groupMapping R6 class \code{GroupMapping} object + #' @param color R6 class \code{Grouping} object or its input + #' @param fill R6 class \code{Grouping} object or its input + #' @param linetype R6 class \code{Grouping} object or its input + #' @param shape R6 class \code{Grouping} object or its input + #' @param size R6 class \code{Grouping} object or its input + #' @param data data.frame to map used by \code{smartMapping} + #' @return A new \code{RangeDataMapping} object + initialize = function(x = NULL, + y = NULL, + ymin = NULL, + ymax = NULL, + groupMapping = NULL, + color = NULL, + fill = NULL, + linetype = NULL, + shape = NULL, + size = NULL, + data = NULL) { + + # smartMapping is available in utilities-mapping.R + smartMap <- smartMapping(data) + super$initialize( + x = x %||% smartMap$x, + ymin = ymin %||% smartMap$ymin, + ymax = ymax %||% smartMap$ymax, + groupMapping = groupMapping, color = color, fill = fill, + linetype = linetype, shape = shape, size = size + ) + # Since TimeProfileDataMapping inherits from RangeDataMapping + # super$initialize introduce a self$y which is NULL + self$y <- y %||% smartMap$y + }, + #' @description Check that \code{data} variables include map variables + #' @param data data.frame to check + #' @param metaData list containing information on \code{data} + #' @return A data.frame with map and \code{legendLabels} variables. + #' Dummy variable \code{legendLabels} is necessary to allow further modification of plots. + checkMapData = function(data, metaData = NULL) { + validateIsOfType(data, "data.frame") + validateMapping(self$y, data, nullAllowed = TRUE) + mapData <- super$checkMapData(data, metaData) + # This may change depending of how we want to include options + if (!isOfLength(self$y, 0)) { + mapData[, self$y] <- data[, self$y] + } + self$data <- mapData + return(mapData) } ) ) diff --git a/R/timeprofile-plotconfiguration.R b/R/timeprofile-plotconfiguration.R index a9be99a3..fd4e95a8 100644 --- a/R/timeprofile-plotconfiguration.R +++ b/R/timeprofile-plotconfiguration.R @@ -6,20 +6,28 @@ TimeProfilePlotConfiguration <- R6::R6Class( inherit = PlotConfiguration, public = list( - #' @field timeProfileCaption list of properties for time profile plot specific features - timeProfileCaption = NULL, - #' @description Create a new \code{TimeProfilePlotConfiguration} object - #' @param timeProfileCaption list of properties for PK ratio plot specific features + #' @param lines `ThemeAestheticSelections` defining properties of lines + #' @param ribbons `ThemeAestheticSelections` defining properties of ribbons + #' @param points `ThemeAestheticSelections` defining properties of points + #' @param errorbars `ThemeAestheticSelections` defining properties of error bars #' @param ... parameters inherited from \code{PlotConfiguration} #' @return A new \code{TimeProfilePlotConfiguration} object - initialize = function(timeProfileCaption = getDefaultCaptionFor("timeProfile"), + initialize = function(lines = NULL, + ribbons = NULL, + points = NULL, + errorbars = NULL, ...) { - validateIsOfType(timeProfileCaption, "data.frame") - validateIsIncluded(names(timeProfileCaption), CaptionProperties) super$initialize(...) - - self$timeProfileCaption <- timeProfileCaption + validateIsOfType(lines, "ThemeAestheticSelections", nullAllowed = TRUE) + validateIsOfType(ribbons, "ThemeAestheticSelections", nullAllowed = TRUE) + validateIsOfType(points, "ThemeAestheticSelections", nullAllowed = TRUE) + validateIsOfType(errorbars, "ThemeAestheticSelections", nullAllowed = TRUE) + currentTheme <- tlfEnv$currentTheme$clone(deep = TRUE) + private$.lines <- lines %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotTimeProfile$lines) + private$.ribbons <- ribbons %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotTimeProfile$ribbons) + private$.points <- points %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotTimeProfile$points) + private$.errorbars <- errorbars %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotTimeProfile$errorbars) } ) ) diff --git a/R/tlf-env.R b/R/tlf-env.R index 17e1d028..be5ad436 100644 --- a/R/tlf-env.R +++ b/R/tlf-env.R @@ -5,9 +5,6 @@ tlfEnv <- new.env(parent = emptyenv()) # name of the package. This will be used to retrieve information on the package at run time tlfEnv$packageName <- "tlf" -# Set the current theme of the tlf plot configurations -tlfEnv$currentTheme <- defaultTheme - #' @title LegendPositions #' @include enum.R #' @export @@ -42,7 +39,9 @@ tlfEnv$defaultLegendPosition <- LegendPositions$outsideRight #' @export setDefaultLegendPosition <- function(position) { validateIsIncluded(position, LegendPositions) + # TO DO: Line to be deprecated tlfEnv$defaultLegendPosition <- position + tlfEnv$currentTheme$background$legendPosition <- position } tlfEnv$defaultExportParameters <- list(format = "png", width = 16, height = 9, units = "cm") @@ -142,7 +141,14 @@ setDefaultAggregationBins <- function(bins = NULL) { #' @description Set default watermark value for current theme #' @param watermark character or Label class object #' @export -setDefaultWatermark <- function(watermark = NULL){ +setDefaultWatermark <- function(watermark = NULL) { validateIsOfType(watermark, c("Label", "character"), nullAllowed = TRUE) - tlfEnv$currentTheme$background$watermark <- asLabel(watermark) -} \ No newline at end of file + if(isOfType(watermark, "character")){ + tlfEnv$currentTheme$background$watermark <- watermark + } + if(isOfType(watermark, "Label")){ + tlfEnv$currentTheme$background$watermark <- watermark$text + tlfEnv$currentTheme$fonts$watermark <- watermark$font + } + return(invisible()) +} diff --git a/R/tornado-datamapping.R b/R/tornado-datamapping.R index 197d675d..c92cff9d 100644 --- a/R/tornado-datamapping.R +++ b/R/tornado-datamapping.R @@ -5,24 +5,24 @@ TornadoDataMapping <- R6::R6Class( "TornadoDataMapping", inherit = XYGDataMapping, public = list( - #' @field tornadoValues numeric vector of limits to plot - tornadoValues = NULL, + #' @field lines numeric vector of limits to plot + lines = NULL, #' @field sorted logical indicating if values should be sorted sorted = NULL, #' @description Create a new \code{TornadoDataMapping} object - #' @param tornadoValues numeric vector of ratio limits to plot + #' @param lines numeric vector of limits to plot #' @param sorted logical indicating if values should be sorted #' @param x Variable including the values of tornado plot #' @param y Variable including the labels of tornado plot #' @param ... parameters inherited from \code{XYGDataMapping} #' @return A new \code{TornadoDataMapping} object - initialize = function(tornadoValues = DefaultDataMappingValues$tornado, + initialize = function(lines = DefaultDataMappingValues$tornado, sorted = NULL, x = NULL, y = NULL, ...) { - validateIsNumeric(tornadoValues) + validateIsNumeric(lines, nullAllowed = TRUE) validateIsLogical(sorted, nullAllowed = TRUE) super$initialize(x = x, y = y, ...) @@ -44,7 +44,7 @@ TornadoDataMapping <- R6::R6Class( self$groupMapping$color ) - self$tornadoValues <- tornadoValues + self$lines <- lines self$sorted <- sorted %||% TRUE } ) diff --git a/R/tornado-plotconfiguration.R b/R/tornado-plotconfiguration.R index 8f3c331b..cbebdcec 100644 --- a/R/tornado-plotconfiguration.R +++ b/R/tornado-plotconfiguration.R @@ -5,8 +5,6 @@ TornadoPlotConfiguration <- R6::R6Class( "TornadoPlotConfiguration", inherit = PlotConfiguration, public = list( - #' @field tornadoCaption list of properties for tornado plot specific features - tornadoCaption = NULL, #' @field bar logical setting if tornado is uses a bar plot instead of regular points bar = NULL, #' @field colorPalette color palette property from `ggplot2` @@ -16,29 +14,38 @@ TornadoPlotConfiguration <- R6::R6Class( #' @description Create a new \code{TornadoPlotConfiguration} object - #' @param tornadoCaption list of properties for tornado plot specific features #' @param bar logical setting if tornado is uses a bar plot instead of regular points #' @param colorPalette color palette property from `ggplot2` #' @param dodge space between the bars/points + #' @param lines `ThemeAestheticSelections` object defining properties for Tornado vertical lines + #' @param points `ThemeAestheticSelections` object defining properties for scatter points + #' @param ribbons `ThemeAestheticSelections` object defining properties for bars #' @param ... parameters inherited from \code{PlotConfiguration} #' @return A new \code{TornadoPlotConfiguration} object - initialize = function(tornadoCaption = getDefaultCaptionFor("tornado"), - bar = TRUE, + initialize = function(bar = TRUE, colorPalette = NULL, dodge = 0.5, + lines = NULL, + points = NULL, + ribbons = NULL, ...) { - validateIsOfType(tornadoCaption, "data.frame") - #validateIsIncluded(names(tornadoCaption), CaptionProperties) - # Currently the properties from tornadoCaption are not defined in the theme snapshot validateIsLogical(bar) validateIsString(colorPalette, nullAllowed = TRUE) validateIsNumeric(dodge) + validateIsOfType(lines, "ThemeAestheticSelections", nullAllowed = TRUE) + validateIsOfType(points, "ThemeAestheticSelections", nullAllowed = TRUE) + validateIsOfType(ribbons, "ThemeAestheticSelections", nullAllowed = TRUE) super$initialize(...) + + currentTheme <- tlfEnv$currentTheme$clone(deep = TRUE) + private$.lines <- lines %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotTornado$lines) + private$.points <- points %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotTornado$points) + private$.ribbons <- ribbons %||% asThemeAestheticSelections(currentTheme$plotConfigurations$plotTornado$ribbons) + self$bar <- bar self$colorPalette <- colorPalette self$dodge <- dodge - self$tornadoCaption <- tornadoCaption } ) ) diff --git a/R/utilities-aesthetics.R b/R/utilities-aesthetics.R new file mode 100644 index 00000000..e05d9e4c --- /dev/null +++ b/R/utilities-aesthetics.R @@ -0,0 +1,172 @@ +#' @title AestheticProperties +#' @description Enum of aesthetic property names of `ggplot2` +AestheticProperties <- enum(c( + "color", + "fill", + "size", + "shape", + "linetype", + "alpha" +)) + +#' @title Linetypes +#' @description Enum of `ggplot2` linetypes +#' @export +Linetypes <- enum(c( + "solid", + "longdash", + "dotted", + "dashed", + "twodash", + "dotdash", + "blank" +)) + +#' @title Shapes +#' @description List of some `ggplot2` shapes +#' @export +Shapes <- list( + "square" = 15, + "dot" = 20, + "circle" = 19, + "diamond" = 18, + "star" = 8, + "plus" = 3, + "cross" = 4, + "triangle" = 17, + "blank" = -2 +) + +asPlotShape <- function(shapes) { + ggplotShapes <- NULL + for (shape in shapes) { + ggplotShape <- shape + if (isOfType(shape, "character")) { + validateIsIncluded(shape, names(Shapes)) + ggplotShape <- Shapes[[shape]] + } + ggplotShapes <- c(ggplotShapes, ggplotShape) + } + return(ggplotShapes) +} + +asThemeAestheticSelections <- function(themeSelectionObject) { + if (isOfType(themeSelectionObject, "ThemeAestheticSelections")) { + return(themeSelectionObject) + } + newThemeAestheticSelections <- ThemeAestheticSelections$new() + setNewThemeSelectionExpression <- parse(text = paste0( + "newThemeAestheticSelections$", names(themeSelectionObject), "<- themeSelectionObject$", names(themeSelectionObject) + )) + eval(setNewThemeSelectionExpression) + return(newThemeAestheticSelections) +} + +#' @title ColorMaps +#' @description List with some color maps for `Theme` object +#' @export +ColorMaps <- list( + default = c("#0078D7", "#D83B01", "#107C10", "#A80000", "#002050", "#B4009E"), + grays = paste("gray", seq(0, 100, 10), sep = ""), + prism = c("#FF0000", "#FF7F00", "#FFFF00", "#00FF00", "#0000FF", "#4B0082", "#8F00FF"), + blot = c("blue", "magenta", "cyan", "green", "yellow", "red"), + temperature = c("#262A76", "#234990", "#2F8AC3", "#26B0D2", "#FFC1CB", "#EB559", "#AE3535", "8E1F20") +) + +#' @title AestheticSelectionKeys +#' @description List of some `ggplot2` shapes +#' @export +AestheticSelectionKeys <- enum(c( + "next", + "same", + "first", + "reset" +)) + +#' @title getAestheticValues +#' @description Get aesthetic values (e.g color, shape, linetype) based on a selected strategy +#' @param n integer defining size of returned aesthetic vector +#' @param selectionKey value of aesthetic to be returned or key function from enum `AestheticSelectionKeys` +#' @return Vector of aesthetics +getAestheticValues <- function(n, selectionKey = NA, position = 0, aesthetic = "color") { + validateIsIncluded(aesthetic, AestheticProperties) + # Load aesthetics from current `Theme` object + map <- tlfEnv$currentTheme$aestheticMaps[[aesthetic]] + # next is necessary as `next` otherwise R crashes + if (isIncluded(selectionKey, AestheticSelectionKeys$`next`)) { + return(getNextAestheticValues(n, position, map)) + } + if (isIncluded(selectionKey, AestheticSelectionKeys$same)) { + return(getSameAestheticValues(n, position, map)) + } + if (isIncluded(selectionKey, AestheticSelectionKeys$first)) { + return(getFirstAestheticValues(n, map)) + } + if (isIncluded(selectionKey, AestheticSelectionKeys$reset)) { + return(getResetAestheticvalues(n, map)) + } + # TO DO: Add wrappers for validation + # which could translate some selection keys for ggplot 2 + if (isIncluded(aesthetic, AestheticProperties$shape)) { + return(asPlotShape(getNextAestheticValues(n, position = position, map = selectionKey))) + } + if (isIncluded(aesthetic, c(AestheticProperties$size, AestheticProperties$alpha))) { + return(as.numeric(getNextAestheticValues(n, position = position, map = selectionKey))) + } + # If selection key is a specific value/set of values, it becomes the map + return(getNextAestheticValues(n, position = position, map = selectionKey)) +} + +#' @title getNextAestheticValues +#' @description Get the next aesthetic values (e.g color, shape, linetype) from an aesthetic map defined in `Theme` +#' @param n integer defining size of returned aesthetic vector +#' @param position integer defining at which position to look for aesthetic vector +#' @param map Aesthetic (e.g. color, shape, linetype) map from `Theme` object. +#' @return Vector of aesthetics +getNextAestheticValues <- function(n, position = 0, map) { + # Get the map indices of aesthtic values to be output + aesPositions <- seq(position + 1, position + n) + + # In case the map is not long enough to get all the indices + # the indices have to go start back from 1 using modelu function + mapSize <- length(map) + aesPositions <- ((aesPositions - 1) %% mapSize) + 1 + return(map[aesPositions]) +} + +#' @title getSameAestheticValues +#' @description Get the same aesthetic values (e.g color, shape, linetype) from an aesthetic map defined in `Theme` +#' @param n integer defining size of returned aesthetic vector +#' @param position integer defining at which position to look for aesthetic vector +#' @param map Aesthetic (e.g. color, shape, linetype) map from `Theme` object. +#' @return Vector of aesthetics +getSameAestheticValues <- function(n, position = 0, map) { + # In case the map is not long enough to get the indices in position + # the indices have to go start back from 1 using modelu function + mapSize <- length(map) + aesPosition <- (position %% mapSize) + 1 + + # Get the map indices of aesthtic values to be output + aesPositions <- rep(aesPosition, n) + return(map[aesPositions]) +} + +#' @title getResetAestheticvalues +#' @description Get the aesthetic values (e.g color, shape, linetype) from an aesthetic map defined in `Theme`. +#' Reset the value every time it is used. +#' @param n integer defining size of returned aesthetic vector +#' @param map Aesthetic (e.g. color, shape, linetype) map from `Theme` object. +#' @return Vector of aesthetics +getResetAestheticvalues <- function(n, map) { + return(getNextAestheticValues(n, position = 0, map)) +} + +#' @title getFirstAestheticValues +#' @description Get the next aesthetic values (e.g color, shape, linetype) from an aesthetic map defined in `Theme` +#' @param n integer defining size of returned aesthetic vector +#' @param position integer defining at which position to look for aesthetic vector +#' @param map Aesthetic (e.g. color, shape, linetype) map from `Theme` object. +#' @return Vector of aesthetics +getFirstAestheticValues <- function(n, map) { + return(getSameAestheticValues(n, position = 0, map)) +} diff --git a/R/utilities-axis.R b/R/utilities-axis.R index 2fab9f5d..8e1f457b 100644 --- a/R/utilities-axis.R +++ b/R/utilities-axis.R @@ -5,47 +5,35 @@ #' @param limits X-axis limits #' @param ticks X-axis ticks #' @param ticklabels X-axis ticklabels +#' @param font `Font` object defining font of ticklabels #' @return ggplot object with updated X-axis #' @export setXAxis <- function(plotObject, scale = NULL, limits = NULL, ticks = NULL, - ticklabels = NULL) { + ticklabels = NULL, + font = NULL) { validateIsOfType(plotObject, "ggplot") validateIsIncluded(scale, Scaling, nullAllowed = TRUE) + validateIsNumeric(limits, nullAllowed = TRUE) + validateIsOfType(font, "Font", nullAllowed = TRUE) # Clone plotConfiguration into a new plot object # Prevents update of R6 class being spread to plotObject newPlotObject <- plotObject newPlotObject$plotConfiguration <- plotObject$plotConfiguration$clone(deep = TRUE) - # R6 class not cloned will spread modifications into newPlotObject$plotConfiguration$yAxis + # R6 class not cloned will spread modifications into newPlotObject$plotConfiguration$xAxis xAxis <- newPlotObject$plotConfiguration$xAxis - xAxis <- plotObject$plotConfiguration$xAxis$clone() xAxis$limits <- limits %||% xAxis$limits xAxis$scale <- scale %||% xAxis$scale xAxis$ticks <- ticks %||% xAxis$ticks xAxis$ticklabels <- ticklabels %||% xAxis$ticklabels + xAxis$font <- font %||% xAxis$font - if (xAxis$scale %in% "lin") { - xAxis$scale <- "identity" - } - if (!isOfLength(xAxis$ticks, 0)) { - if (xAxis$ticks[1] %in% "default") { - xAxis$ticks <- waiver() - } - } - - if (!isOfLength(xAxis$ticks, 0)) { - if (xAxis$ticklabels[1] %in% "default") { - xAxis$ticklabels <- waiver() - } - } - - newPlotObject <- xAxis$setPlotAxis(newPlotObject) - + newPlotObject <- xAxis$updatePlot(newPlotObject) return(newPlotObject) } @@ -56,15 +44,19 @@ setXAxis <- function(plotObject, #' @param limits Y-axis limits #' @param ticks Y-axis ticks #' @param ticklabels Y-axis ticklabels +#' @param font `Font` object defining font of ticklabels #' @return ggplot object with updated Y-axis #' @export setYAxis <- function(plotObject, scale = NULL, limits = NULL, ticks = NULL, - ticklabels = NULL) { + ticklabels = NULL, + font = NULL) { validateIsOfType(plotObject, "ggplot") validateIsIncluded(scale, Scaling, nullAllowed = TRUE) + validateIsNumeric(limits, nullAllowed = TRUE) + validateIsOfType(font, "Font", nullAllowed = TRUE) # Clone plotConfiguration into a new plot object # Prevents update of R6 class being spread to plotObject @@ -78,25 +70,9 @@ setYAxis <- function(plotObject, yAxis$scale <- scale %||% yAxis$scale yAxis$ticks <- ticks %||% yAxis$ticks yAxis$ticklabels <- ticklabels %||% yAxis$ticklabels + yAxis$font <- font %||% yAxis$font - if (yAxis$scale %in% "lin") { - yAxis$scale <- "identity" - } - - if (!isOfLength(yAxis$ticks, 0)) { - if (yAxis$ticks[1] %in% "default") { - yAxis$ticks <- waiver() - } - } - - if (!isOfLength(yAxis$ticks, 0)) { - if (yAxis$ticklabels[1] %in% "default") { - yAxis$ticklabels <- waiver() - } - } - - newPlotObject <- yAxis$setPlotAxis(newPlotObject) - + newPlotObject <- yAxis$updatePlot(newPlotObject) return(newPlotObject) } diff --git a/R/utilities-background.R b/R/utilities-background.R index 50300ed8..2f258ee9 100644 --- a/R/utilities-background.R +++ b/R/utilities-background.R @@ -1,40 +1,102 @@ #' @title setGrid -#' @description Set grid properties on a ggplot object +#' @description Set x and y grid properties on a ggplot object #' @param plotObject ggplot object to set #' @param color character color of the grid #' @param linetype character linetype of the grid. Use "blank" to remove grid. #' @param size numeric size of the grid lines -#' @return ggplot object with updated Y-axis +#' @return ggplot object #' @export setGrid <- function(plotObject, color = NULL, linetype = NULL, size = NULL) { validateIsOfType(plotObject, "ggplot") - validateIsOfType(color, "character", nullAllowed = TRUE) - validateIsOfType(linetype, "character", nullAllowed = TRUE) - validateIsOfType(size, "numeric", nullAllowed = TRUE) + validateIsString(color, nullAllowed = TRUE) + validateIsIncluded(linetype, Linetypes, nullAllowed = TRUE) + validateIsNumeric(size, nullAllowed = TRUE) # Clone plotConfiguration into a new plot object # Prevents update of R6 class being spread to plotObject newPlotObject <- plotObject newPlotObject$plotConfiguration <- plotObject$plotConfiguration$clone(deep = TRUE) - # R6 class not cloned will spread modifications into newPlotObject$plotConfiguration$grid - grid <- newPlotObject$plotConfiguration$background$grid + # R6 class not cloned will spread modifications into newPlotObject$plotConfiguration + background <- newPlotObject$plotConfiguration$background - grid$color <- color %||% grid$color - grid$linetype <- linetype %||% grid$linetype - grid$size <- size %||% grid$size + background$xGrid$color <- color %||% background$xGrid$color + background$yGrid$color <- color %||% background$yGrid$color + background$xGrid$linetype <- linetype %||% background$xGrid$linetype + background$yGrid$linetype <- linetype %||% background$yGrid$linetype + background$xGrid$size <- color %||% background$xGrid$size + background$yGrid$size <- color %||% background$yGrid$size - newPlotObject <- newPlotObject + theme( - panel.grid = element_line( - color = grid$color, - size = grid$size, - linetype = grid$linetype - ) - ) + newPlotObject <- background$updatePlot(newPlotObject) + return(newPlotObject) +} + +#' @title setXGrid +#' @description Set x-grid properties on a ggplot object +#' @param plotObject ggplot object to set +#' @param color character color of the grid +#' @param linetype character linetype of the grid. Use "blank" to remove grid. +#' @param size numeric size of the grid lines +#' @return ggplot object +#' @export +setXGrid <- function(plotObject, + color = NULL, + linetype = NULL, + size = NULL) { + validateIsOfType(plotObject, "ggplot") + validateIsString(color, nullAllowed = TRUE) + validateIsIncluded(linetype, Linetypes, nullAllowed = TRUE) + validateIsNumeric(size, nullAllowed = TRUE) + # Clone plotConfiguration into a new plot object + # Prevents update of R6 class being spread to plotObject + newPlotObject <- plotObject + newPlotObject$plotConfiguration <- plotObject$plotConfiguration$clone(deep = TRUE) + + # R6 class not cloned will spread modifications into newPlotObject$plotConfiguration + background <- newPlotObject$plotConfiguration$background + + background$xGrid$color <- color %||% background$xGrid$color + background$xGrid$linetype <- linetype %||% background$xGrid$linetype + background$xGrid$size <- color %||% background$xGrid$size + + newPlotObject <- background$updatePlot(newPlotObject) + return(newPlotObject) +} + +#' @title setYGrid +#' @description Set x and y grid properties on a ggplot object +#' @param plotObject ggplot object to set +#' @param color character color of the grid +#' @param linetype character linetype of the grid. Use "blank" to remove grid. +#' @param size numeric size of the grid lines +#' @return ggplot object +#' @export +setYGrid <- function(plotObject, + color = NULL, + linetype = NULL, + size = NULL) { + validateIsOfType(plotObject, "ggplot") + validateIsString(color, nullAllowed = TRUE) + validateIsIncluded(linetype, Linetypes, nullAllowed = TRUE) + validateIsNumeric(size, nullAllowed = TRUE) + + # Clone plotConfiguration into a new plot object + # Prevents update of R6 class being spread to plotObject + newPlotObject <- plotObject + newPlotObject$plotConfiguration <- plotObject$plotConfiguration$clone(deep = TRUE) + + # R6 class not cloned will spread modifications into newPlotObject$plotConfiguration + background <- newPlotObject$plotConfiguration$background + + background$yGrid$color <- color %||% background$yGrid$color + background$yGrid$linetype <- linetype %||% background$yGrid$linetype + background$yGrid$size <- color %||% background$yGrid$size + + newPlotObject <- background$updatePlot(newPlotObject) return(newPlotObject) } @@ -45,20 +107,18 @@ setGrid <- function(plotObject, #' @param color character color of the background frame #' @param linetype character linetype of the background frame #' @param size numeric size of the background frame -#' @param outerBackgroundFill character color fill of the outerBackground -#' @return ggplot object with updated Y-axis +#' @return ggplot object #' @export setBackground <- function(plotObject, fill = NULL, color = NULL, linetype = NULL, - size = NULL, - outerBackgroundFill = NULL) { + size = NULL) { validateIsOfType(plotObject, "ggplot") - validateIsOfType(size, "numeric", nullAllowed = TRUE) - inputs <- c("fill", "color", "linetype", "outerBackgroundFill") - validateExpressions <- parse(text = paste0("validateIsOfType(", inputs, ', "character", nullAllowed =TRUE)')) - eval(validateExpressions) + validateIsString(fill, nullAllowed = TRUE) + validateIsString(color, nullAllowed = TRUE) + validateIsIncluded(linetype, Linetypes, nullAllowed = TRUE) + validateIsNumeric(size, nullAllowed = TRUE) # Clone plotConfiguration into a new plot object # Prevents update of R6 class being spread to plotObject @@ -66,42 +126,109 @@ setBackground <- function(plotObject, newPlotObject$plotConfiguration <- plotObject$plotConfiguration$clone(deep = TRUE) # R6 class not cloned will spread modifications into newPlotObject$plotConfiguration - innerBackground <- newPlotObject$plotConfiguration$background$innerBackground - outerBackground <- newPlotObject$plotConfiguration$background$outerBackground - - innerBackground$fill <- fill %||% innerBackground$fill - innerBackground$color <- color %||% innerBackground$color - innerBackground$linetype <- linetype %||% innerBackground$linetype - innerBackground$size <- size %||% innerBackground$size - outerBackground$fill <- outerBackgroundFill %||% innerBackground$fill - - newPlotObject <- newPlotObject + theme( - plot.background = element_rect(fill = outerBackground$fill), - legend.background = element_rect(fill = outerBackground$fill), - panel.background = element_rect( - fill = innerBackground$fill, - color = innerBackground$color, - size = innerBackground$size, - linetype = innerBackground$linetype, - ) - ) + background <- newPlotObject$plotConfiguration$background + + background$plot$fill <- fill %||% background$plot$fill + background$plot$color <- color %||% background$plot$color + background$plot$linetype <- linetype %||% background$plot$linetype + background$plot$size <- size %||% background$plot$size + + background$panel$fill <- fill %||% background$panel$fill + background$panel$color <- color %||% background$panel$color + background$panel$linetype <- linetype %||% background$panel$linetype + background$panel$size <- size %||% background$panel$size + + newPlotObject <- background$updatePlot(newPlotObject) return(newPlotObject) } +#' @title setBackgroundPanelArea +#' @description Set background panel area properties on a ggplot object +#' @param plotObject ggplot object to set +#' @param fill character color fill of the background +#' @param color character color of the background frame +#' @param linetype character linetype of the background frame +#' @param size numeric size of the background frame +#' @return ggplot object +#' @export +setBackgroundPanelArea <- function(plotObject, + fill = NULL, + color = NULL, + linetype = NULL, + size = NULL) { + validateIsOfType(plotObject, "ggplot") + validateIsString(fill, nullAllowed = TRUE) + validateIsString(color, nullAllowed = TRUE) + validateIsIncluded(linetype, Linetypes, nullAllowed = TRUE) + validateIsNumeric(size, nullAllowed = TRUE) + + # Clone plotConfiguration into a new plot object + # Prevents update of R6 class being spread to plotObject + newPlotObject <- plotObject + newPlotObject$plotConfiguration <- plotObject$plotConfiguration$clone(deep = TRUE) + + # R6 class not cloned will spread modifications into newPlotObject$plotConfiguration + background <- newPlotObject$plotConfiguration$background + + background$panel$fill <- fill %||% background$panel$fill + background$panel$color <- color %||% background$panel$color + background$panel$linetype <- linetype %||% background$panel$linetype + background$panel$size <- size %||% background$panel$size + + newPlotObject <- background$updatePlot(newPlotObject) + return(newPlotObject) +} + +#' @title setBackgroundPlotArea +#' @description Set background plot area properties on a ggplot object +#' @param plotObject ggplot object to set +#' @param fill character color fill of the background +#' @param color character color of the background frame +#' @param linetype character linetype of the background frame +#' @param size numeric size of the background frame +#' @return ggplot object +#' @export +setBackgroundPlotArea <- function(plotObject, + fill = NULL, + color = NULL, + linetype = NULL, + size = NULL) { + validateIsOfType(plotObject, "ggplot") + validateIsString(fill, nullAllowed = TRUE) + validateIsString(color, nullAllowed = TRUE) + validateIsIncluded(linetype, Linetypes, nullAllowed = TRUE) + validateIsNumeric(size, nullAllowed = TRUE) + + # Clone plotConfiguration into a new plot object + # Prevents update of R6 class being spread to plotObject + newPlotObject <- plotObject + newPlotObject$plotConfiguration <- plotObject$plotConfiguration$clone(deep = TRUE) + + # R6 class not cloned will spread modifications into newPlotObject$plotConfiguration + background <- newPlotObject$plotConfiguration$background + + background$plot$fill <- fill %||% background$plot$fill + background$plot$color <- color %||% background$plot$color + background$plot$linetype <- linetype %||% background$plot$linetype + background$plot$size <- size %||% background$plot$size + + newPlotObject <- background$updatePlot(newPlotObject) + return(newPlotObject) +} #' @title addWatermark #' @param plotObject ggplot object to which the watermark is added -#' @param label Character or Label class object corresponding to the watermark text -#' (and its font properties if Label) -#' @param angle Angle in degree from horizontal of the watermark label. Default angle is 30 degrees. +#' @param watermark Character or `Label` object corresponding to the watermark text +#' (and its font properties if `Label`) +#' @param color Color of the watermark label. +#' @param size Size of the watermark label. +#' @param angle Angle of the watermark label (in degree). #' @param alpha Transparency of the watermark label. #' Alpha is a numeric between 0 and 1: 0 label is totally transparent, 1 label is totally opaque. -#' Default alpha is 0.4. #' @return \code{plotObject} ggplot object to which the watermark is added. #' @description #' addWatermark creates a ggplot grob based on the label text and its font properties. #' Then, adds the grob to the ggplot object input \code{plotObject} as a new layer using \code{ggplot2::annotation_custom}. -#' \code{angle} and \code{alpha} are optional input to customize the angle and transparency of the watermark text. #' @import ggplot2 #' @export #' @examples @@ -121,19 +248,37 @@ setBackground <- function(plotObject, #' # Create a sun as background #' for (angle in seq(0, 340, 20)) { #' p <- addWatermark(p, -#' label = Label$new(text = " >", color = "yellow"), -#' angle = angle, alpha = 1 +#' watermark = " >", +#' color = "yellow", angle = angle, alpha = 1 #' ) #' } addWatermark <- function(plotObject, - label, - angle = 30, - alpha = 0.4) { + watermark, + color = NULL, + size = NULL, + angle = NULL, + alpha = NULL) { validateIsOfType(plotObject, "ggplot") - # Ensure label is a Label class - label <- asLabel(label) + validateIsOfType(watermark, c("character", "Label")) + validateIsString(color, nullAllowed = TRUE) + validateIsNumeric(size, nullAllowed = TRUE) + validateIsNumeric(alpha, nullAllowed = TRUE) + validateIsNumeric(angle, nullAllowed = TRUE) + # Transparency from theme aesthetic map if left undefined + alpha <- alpha %||% getAestheticValues( + n = 1, + selectionKey = AestheticSelectionKeys$first, + aesthetic = "alpha" + ) + # Ensure watermark is a Label class + if (isOfType(watermark, "character")) { + watermark <- asLabel(watermark, font = tlfEnv$currentTheme$fonts$watermark) + } + watermark$font$color <- color %||% watermark$font$color + watermark$font$size <- size %||% watermark$font$size + watermark$font$angle <- angle %||% watermark$font$angle - watermark <- createWatermarkGrob(label = label, angle = angle, alpha = alpha) + watermark <- createWatermarkGrob(label = watermark, alpha = alpha) plotObject <- plotObject + ggplot2::annotation_custom(grob = watermark, xmin = -Inf, ymin = -Inf, xmax = Inf, ymax = Inf) @@ -142,22 +287,34 @@ addWatermark <- function(plotObject, #' @title setWatermark #' @param plotObject ggplot object to which the watermark is set -#' @param watermark character or Label class object -#' @param angle Angle in degree from horizontal of the watermark label. Default angle is 30 degrees. +#' @param watermark Character or `Label` object corresponding to the watermark text +#' (and its font properties if `Label`) +#' @param color Color of the watermark label. +#' @param size Size of the watermark label. +#' @param angle Angle of the watermark label (in degree). #' @param alpha Transparency of the watermark label. #' Alpha is a numeric between 0 and 1: 0 label is totally transparent, 1 label is totally opaque. -#' Default alpha is 0.6. #' @return ggplot object to which the watermark is added. -#' @import ggplot2 +#' @import ggplot2 #' @export setWatermark <- function(plotObject, watermark = NULL, - angle = 30, - alpha = 0.4) { + color = NULL, + size = NULL, + angle = NULL, + alpha = NULL) { validateIsOfType(plotObject, "ggplot") - # Angle and alpha can be added as a part of plot configuration later on: - # For this create Watermark R6 class inheriting from Label and which gets fields angle and alpha - validateIsOfType(watermark, c("Label", "character"), nullAllowed = TRUE) + validateIsOfType(watermark, c("character", "Label"), nullAllowed = TRUE) + validateIsString(color, nullAllowed = TRUE) + validateIsNumeric(size, nullAllowed = TRUE) + validateIsNumeric(alpha, nullAllowed = TRUE) + validateIsNumeric(angle, nullAllowed = TRUE) + + alpha <- alpha %||% getAestheticValues( + n = 1, + selectionKey = AestheticSelectionKeys$first, + aesthetic = "alpha" + ) # Clone plotConfiguration into a new plot object # Prevents update of R6 class being spread to plotObject @@ -167,19 +324,18 @@ setWatermark <- function(plotObject, # R6 class not cloned will spread modifications into newPlotObject$plotConfiguration$background$watermark watermarkConfiguration <- newPlotObject$plotConfiguration$background$watermark - if (!is.null(watermark)) { - if (isOfType(watermark, "character")) { - watermark <- asLabel(watermark, watermarkConfiguration$font) - } + if (isOfType(watermark, "character")) { + watermark <- asLabel(text = watermark, font = watermarkConfiguration$font) } - watermarkConfiguration <- watermark %||% watermarkConfiguration + watermarkConfiguration$font$color <- color %||% watermarkConfiguration$font$color + watermarkConfiguration$font$size <- size %||% watermarkConfiguration$font$size + watermarkConfiguration$font$angle <- angle %||% watermarkConfiguration$font$angle # If plot is initialized, addWatermark otherwise update watermark - if (length(plotObject$layers) == 0) { + if (isOfLength(plotObject$layers, 0)) { newPlotObject <- addWatermark(newPlotObject, - label = watermarkConfiguration, - angle = angle, + watermark = watermarkConfiguration, alpha = alpha ) return(newPlotObject) @@ -187,38 +343,41 @@ setWatermark <- function(plotObject, # Using initializePlot, watermark will always be as first layer dummyPlot <- addWatermark(ggplot2::ggplot(), - label = watermarkConfiguration, - angle = angle, + watermark = watermarkConfiguration, alpha = alpha ) - newPlotObject$layers[[1]] <- dummyPlot$layer[[1]] - return(newPlotObject) } #' @title createWatermarkGrob -#' @param label Character or Label class object corresponding to the watermark text +#' @param label Character or Label object corresponding to the watermark text #' (and its font properties if Label) -#' @param angle Angle in degree from horizontal of the watermark label. Default angle is 30 degrees. #' @param alpha Transparency of the watermark label. #' Alpha is a numeric between 0 and 1: 0 label is totally transparent, 1 label is totally opaque. -#' Default alpha is 0.6. +#' Default alpha is defined from theme aesthetic alpha map #' @return Watermark background as a ggplot grob object #' @description #' createWatermarkGrob creates a ggplot grob based on the label text and its font properties. #' @export -createWatermarkGrob <- function(label, angle = 30, alpha = 0.6) { +createWatermarkGrob <- function(label, alpha = NULL) { + validateIsNumeric(alpha, nullAllowed = TRUE) # Ensure label is a Label class label <- asLabel(label) + alpha <- alpha %||% getAestheticValues( + n = 1, + selectionKey = AestheticSelectionKeys$first, + aesthetic = "alpha" + ) + watermark <- ggplot2::ggplot() + ggplot2::theme_void() + ggplot2::annotate( geom = "text", x = 0, y = 0, - label = label$text, + label = label$text %||% "", color = label$font$color, fontface = label$font$fontFace, size = label$font$size, - angle = angle, alpha = alpha + angle = label$font$angle, alpha = alpha ) watermark <- ggplot2::ggplotGrob(watermark) diff --git a/R/utilities-label.R b/R/utilities-label.R index cec59efa..9da219eb 100644 --- a/R/utilities-label.R +++ b/R/utilities-label.R @@ -1,11 +1,11 @@ #' @title setPlotLabels #' @description Set labels properties on a ggplot object #' @param plotObject ggplot object to set -#' @param title character or Label class object -#' @param subtitle character or Label class object -#' @param xlabel character or Label class object -#' @param ylabel character or Label class object -#' @return ggplot object with updated labels +#' @param title character or `Label` object +#' @param subtitle character or `Label` object +#' @param xlabel character or `Label` object +#' @param ylabel character or `Label` object +#' @return ggplot object #' @export setPlotLabels <- function(plotObject, title = NULL, @@ -13,7 +13,6 @@ setPlotLabels <- function(plotObject, xlabel = NULL, ylabel = NULL) { validateIsOfType(plotObject, "ggplot") - # Inputs will undergo the same code, so parse/eval # parse/eval of inputs prevent copy paste of code inputs <- c("title", "subtitle", "xlabel", "ylabel") @@ -29,17 +28,15 @@ setPlotLabels <- function(plotObject, labels <- newPlotObject$plotConfiguration$labels char2LabExpressions <- parse(text = paste0( - "if(!is.null(", inputs, ")){", "if(isOfType(", inputs, ', "character")){', - inputs, " <- asLabel(", inputs, ", labels$", inputs, "$font)}}" + inputs, " <- asLabel(", inputs, ", font = labels$", inputs, "$font)}" )) eval(char2LabExpressions) updateLabelExpressions <- parse(text = paste0("labels$", inputs, " <- ", inputs, " %||% labels$", inputs)) eval(updateLabelExpressions) - newPlotObject <- labels$setPlotLabels(newPlotObject) - + newPlotObject <- labels$updatePlot(newPlotObject) return(newPlotObject) } @@ -69,44 +66,3 @@ asLabel <- function(text = "", font = NULL) { return(text) } - -#' @title setFontProperties -#' @param plotObject ggplot object -#' @param titleFont Font Class for title -#' @param subtitleFont Font Class for subtitle -#' @param xAxisFont Font Class for xaxis and ticks -#' @param yAxisFont Font Class for yaxis and ticks -#' @param legendFont Font Class for legend -#' @return plotObject ggplot object with updated fonts -#' @description -#' setFontProperties set Font Properties on a ggplot object -#' @include font.R -#' @export -#' @examples -#' p <- ggplot2::ggplot() + ggplot2::labs(title = "Title") -#' newFont <- Font$new(color = "blue", size = 20) -#' p <- setFontProperties(plotObject = p, titleFont = newFont) -setFontProperties <- function(plotObject, - titleFont = NULL, - subtitleFont = NULL, - xAxisFont = NULL, - yAxisFont = NULL, - legendFont = NULL) { - if (!is.null(titleFont)) { - plotObject <- plotObject + theme(plot.title = titleFont$setFont()) - } - if (!is.null(subtitleFont)) { - plotObject <- plotObject + theme(plot.subtitle = subtitleFont$setFont()) - } - if (!is.null(xAxisFont)) { - plotObject <- plotObject + theme(axis.title.x = xAxisFont$setFont(), axis.text.x = xAxisFont$setFont()) - } - if (!is.null(yAxisFont)) { - plotObject <- plotObject + theme(axis.title.y = yAxisFont$setFont(), axis.text.y = yAxisFont$setFont()) - } - if (!is.null(legendFont)) { - plotObject <- plotObject + theme(legend.text = legendFont$setFont()) - } - - return(plotObject) -} diff --git a/R/utilities-legend.R b/R/utilities-legend.R index 87606b00..a8f9bc31 100644 --- a/R/utilities-legend.R +++ b/R/utilities-legend.R @@ -2,26 +2,138 @@ #' @param plotObject Graphical object created from ggplot #' @param position legend position. #' Use enum `LegendPositions` to access the list of legend positions. -#' @param title title of legend +#' @param title character or `Label` object +#' @param font `Font` object defining legend font #' @param caption data.frame containing the caption properties of the legend -#' @return A \code{ggplot} graphical object +#' @return A \code{ggplot} object #' @description -#' Set legend position, title and/or caption +#' Set legend position, title, font and/or caption #' @export setLegend <- function(plotObject, - position, + position = NULL, title = NULL, + font = NULL, caption = NULL) { - validateIsIncluded(position, LegendPositions) validateIsOfType(plotObject, "ggplot") + validateIsIncluded(position, LegendPositions, nullAllowed = TRUE) + validateIsOfType(title, c("character", "Label"), nullAllowed = TRUE) + validateIsOfType(font, c("Font"), nullAllowed = TRUE) + validateIsOfType(caption, "data.frame", nullAllowed = TRUE) + + plotObject <- setLegendPosition(plotObject, position = position) + plotObject <- setLegendTitle(plotObject, title = title) + plotObject <- setLegendFont(plotObject, color = font$color, size = font$size, angle = font$angle, fontFace = font$fontFace) + plotObject <- setLegendCaption(plotObject, caption = caption) + return(plotObject) +} + +#' @title setLegendFont +#' @param plotObject ggplot object +#' @param color color of legend font +#' @param size size of legend font +#' @param fontFace color of legend font +#' @param color color of legend font +#' @return A ggplot object +#' @description Set legend font properties +#' @export +setLegendFont <- function(plotObject, + color = NULL, + size = NULL, + fontFace = NULL, + angle = NULL) { + validateIsOfType(plotObject, "ggplot") + validateIsNumeric(size, nullAllowed = TRUE) + validateIsNumeric(angle, nullAllowed = TRUE) + validateIsString(color, nullAllowed = TRUE) + validateIsString(fontFace, nullAllowed = TRUE) - plotObject <- setLegendPosition(plotObject, position) - plotObject <- setLegendTitle(plotObject, title) + # Clone plotConfiguration into a new plot object + # Prevents update of R6 class being spread to plotObject + newPlotObject <- plotObject + newPlotObject$plotConfiguration <- plotObject$plotConfiguration$clone(deep = TRUE) + + # R6 class not cloned will spread modifications into newPlotObject$plotConfiguration + legend <- newPlotObject$plotConfiguration$legend + legend$font$color <- color %||% legend$font$color + legend$font$size <- size %||% legend$font$size + legend$font$angle <- angle %||% legend$font$angle + legend$font$fontFace <- fontFace %||% legend$font$fontFace + + newPlotObject <- legend$updatePlot(newPlotObject) + return(newPlotObject) +} - if (!is.null(caption)) { - plotObject <- setLegendCaption(plotObject, caption) +#' @title setLegendTitle +#' @param plotObject ggplot object +#' @param title character or `Label` object +#' @param color color of legend font +#' @param size size of legend font +#' @param fontFace color of legend font +#' @param color color of legend font +#' @return A ggplot object +#' @description Set legend title +#' @export +setLegendTitle <- function(plotObject, + title = NULL, + color = NULL, + size = NULL, + fontFace = NULL, + angle = NULL) { + validateIsOfType(plotObject, "ggplot") + validateIsOfType(title, c("character", "Label"), nullAllowed = TRUE) + validateIsNumeric(size, nullAllowed = TRUE) + validateIsNumeric(angle, nullAllowed = TRUE) + validateIsString(color, nullAllowed = TRUE) + validateIsString(fontFace, nullAllowed = TRUE) + + # Clone plotConfiguration into a new plot object + # Prevents update of R6 class being spread to plotObject + newPlotObject <- plotObject + newPlotObject$plotConfiguration <- plotObject$plotConfiguration$clone(deep = TRUE) + + # If title is`Label`, reconcile its properties with other inputs + if (isOfType(title, "Label")) { + color <- color %||% title$font$color + size <- size %||% title$font$size + angle <- angle %||% title$font$angle + fontFace <- fontFace %||% title$font$fontFace + title <- title$text } - return(plotObject) + + # R6 class not cloned will spread modifications into newPlotObject$plotConfiguration + legend <- newPlotObject$plotConfiguration$legend + legend$titleFont$color <- color %||% legend$titleFont$color + legend$titleFont$size <- size %||% legend$titleFont$size + legend$titleFont$angle <- angle %||% legend$titleFont$angle + legend$titleFont$fontFace <- fontFace %||% legend$titleFont$fontFace + legend$title <- title %||% legend$title + + newPlotObject <- legend$updatePlot(newPlotObject) + return(newPlotObject) +} + +#' @title setLegendPosition +#' @param plotObject \code{ggplot} graphical object +#' @param position legend position. +#' Use enum `LegendPositions` to access the list of legend positions. +#' @return A \code{ggplot} graphical object +#' @description +#' Set the legend position +#' @export +#' @import ggplot2 +setLegendPosition <- function(plotObject, + position = NULL) { + validateIsOfType(plotObject, "ggplot") + validateIsIncluded(position, LegendPositions, nullAllowed = TRUE) + + newPlotObject <- plotObject + newPlotObject$plotConfiguration <- plotObject$plotConfiguration$clone(deep = TRUE) + + legend <- newPlotObject$plotConfiguration$legend + legend$position <- position %||% legend$position + + newPlotObject <- legend$updatePlot(newPlotObject) + return(newPlotObject) } #' @title setLegendCaption @@ -33,9 +145,15 @@ setLegend <- function(plotObject, #' @export #' @import ggplot2 #' @import utils -setLegendCaption <- function(plotObject, caption) { +setLegendCaption <- function(plotObject, caption = NULL) { validateIsOfType(plotObject, "ggplot") - validateIsOfType(caption, "data.frame") + validateIsOfType(caption, "data.frame", nullAllowed = TRUE) + + # Empty or null captions + if (isOfLength(caption, 0)) { + return(plotObject) + } + validateIsIncluded(names(caption), CaptionProperties) newPlotObject <- plotObject @@ -92,9 +210,7 @@ getLegendCaption <- function(plotObject) { newPlotObject <- plotObject newPlotObject$plotConfiguration <- plotObject$plotConfiguration$clone(deep = TRUE) - caption <- newPlotObject$plotConfiguration$legend$caption - - return(caption) + return(newPlotObject$plotConfiguration$legend$caption) } #' @title setCaptionLabel @@ -265,78 +381,6 @@ setCaptionFill <- function(plotObject, fill, name = NULL) { return(newPlotObject) } -#' @title setLegendTitle -#' @param plotObject \code{ggplot} graphical object -#' @param title title of legend -#' @return A \code{ggplot} graphical object -#' @description -#' Set the legend title -#' @export -#' @import ggplot2 -setLegendTitle <- function(plotObject, title) { - validateIsOfType(plotObject, "ggplot") - - newPlotObject <- plotObject - newPlotObject$plotConfiguration <- plotObject$plotConfiguration$clone(deep = TRUE) - - legend <- newPlotObject$plotConfiguration$legend - legend$title <- title - - # Re-order based on order variable - orderedName <- legend$caption$name[legend$caption$order] - orderedLabel <- legend$caption$label[legend$caption$order] - orderedVisibility <- legend$caption$visibility[legend$caption$order] - - # The next lines need to be looped on every element of LegendTypes - # Otherwise, there can be a title per LegendTypes - for (aestype in LegendTypes) { - # scale_discrete_manual sends warning every time it overwrite something - # besides the function need value input to be filled otherwise crash - # so this line need to be silent - suppressMessages( - newPlotObject <- newPlotObject + - ggplot2::scale_discrete_manual( - aesthetics = aestype, - name = legend$title, - breaks = orderedName[orderedVisibility], - labels = orderedLabel[orderedVisibility], - values = legend$caption[order(legend$caption$name), aestype] - ) - ) - } - return(newPlotObject) -} - -#' @title setLegendTitle -#' @param plotObject \code{ggplot} graphical object -#' @param position legend position. -#' Use enum `LegendPositions` to access the list of legend positions. -#' @return A \code{ggplot} graphical object -#' @description -#' Set the legend position -#' @export -#' @import ggplot2 -setLegendPosition <- function(plotObject, position) { - validateIsOfType(plotObject, "ggplot") - validateIsIncluded(position, LegendPositions) - - newPlotObject <- plotObject - newPlotObject$plotConfiguration <- plotObject$plotConfiguration$clone(deep = TRUE) - - legend <- newPlotObject$plotConfiguration$legend - legend$position <- position - - legendPosition <- getLegendPosition(legend$position) - - newPlotObject <- newPlotObject + theme( - legend.position = c(legendPosition$xPosition, legendPosition$yPosition), - legend.justification = c(legendPosition$xJustification, legendPosition$yJustification), - legend.direction = "vertical" - ) - - return(newPlotObject) -} - # LegendPositions needed to be defined before to tlfEnv$defaultLegendPosition # It was consequently moved from utilities-legend to tlf-env @@ -392,23 +436,54 @@ getLegendPosition <- function(position) { return(legendPosition) } -mergeLegend <- function(plotObject, newLabels, color, shape, size, linetype, fill) { +#' @title mergeLegend +#' @description merge legend caption with existing legend caption +#' @param plotObject ggplot object +#' @param newLabels labels of caption to merge +#' @param aestheticSelections `ThemeAestheticSelections` object +mergeLegend <- function(plotObject, newLabels, aestheticSelections) { validateIsOfType(plotObject, "ggplot") + validateIsOfType(aestheticSelections, "ThemeAestheticSelections") oldCaption <- plotObject$plotConfiguration$legend$caption - legendLength <- nrow(plotObject$plotConfiguration$legend$caption) %||% 0 + oldCaptionLength <- nrow(oldCaption) + newCaptionLength <- length(newLabels) + + # Associate new values of aesthetics based on theme aesthetic selections + newCaptionExpression <- parse(text = paste0( + names(AestheticProperties), " <- getAestheticValues(n = newCaptionLength, + selectionKey = aestheticSelections$", names(AestheticProperties), ", + position = oldCaptionLength, + aesthetic = '", names(AestheticProperties), "')" + )) + eval(newCaptionExpression) + + # Create the new caption newCaption <- data.frame( name = newLabels, label = newLabels, - visibility = rep(TRUE, length(newLabels)), - order = seq(legendLength + 1, legendLength + length(newLabels)), + visibility = rep(TRUE, newCaptionLength), + order = seq(oldCaptionLength + 1, oldCaptionLength + newCaptionLength), color = color, shape = shape, size = size, linetype = linetype, fill = fill, stringsAsFactors = FALSE ) - mergeCaption <- rbind.data.frame(oldCaption, newCaption) - plotObject <- setLegendCaption(plotObject, mergeCaption) - plotObject <- setLegendPosition(plotObject, plotObject$plotConfiguration$legend$position) + if (oldCaptionLength == 0) { + plotObject <- setLegend(plotObject, caption = newCaption) + return(plotObject) + } + # Resolve conflicts between old and new legend caption for merging + # Fill must remain as defined by old (prevent overwriting addRibbon) + for (oldCaptionName in oldCaption$name) { + oldSelectedCaptions <- oldCaption$name %in% oldCaptionName + newSelectedCaptions <- newCaption$name %in% oldCaptionName + newCaption$fill[newSelectedCaptions] <- oldCaption$fill[oldSelectedCaptions] + newCaption$order[newSelectedCaptions] <- oldCaption$order[oldSelectedCaptions] + } + # Remove duplicate old captions for merging + oldCaption <- oldCaption[!(oldCaption$name %in% newCaption$name), ] + mergeCaption <- rbind.data.frame(oldCaption, newCaption, stringsAsFactors = FALSE) + plotObject <- setLegend(plotObject, caption = mergeCaption) return(plotObject) } diff --git a/R/utilities-mapping.R b/R/utilities-mapping.R index 8b706378..ef98f1c2 100644 --- a/R/utilities-mapping.R +++ b/R/utilities-mapping.R @@ -244,10 +244,17 @@ DefaultDataMappingValues <- list( ddiRatio2 = c(2, 1 / 2), guestLine = 1 ), - obsVsPred = list("y=x" = 1), - tornado = 0 + obsVsPred = 1, + resVsPred = 0, + tornado = 0, + histogram = 0 ) +#' @title DDIComparisonTypes +#' @description Options for comparison: residuals vs pred or obs vs pred +#' @export +DDIComparisonTypes <- enum(c("resVsPred", "obsVsPred")) + getAggregatedData <- function(data, xParameterName, yParameterName, diff --git a/R/utils.R b/R/utils.R index 6cec018f..8043cc9e 100644 --- a/R/utils.R +++ b/R/utils.R @@ -64,3 +64,7 @@ ifIncluded <- function(x, y, outputIfIncluded, outputIfNotIncluded = NULL) { outputIfNotIncluded } } + +# Because collate put tlf-env and themes before utils, +# The curretnTheme is defined here: after the definition of %||% +tlfEnv$currentTheme <- Theme$new() diff --git a/appveyor.yml b/appveyor.yml index d1f49e6e..5cedd519 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,7 +12,7 @@ install: - git submodule update --init --recursive environment: - app_version: "1.1" + app_version: "1.2" USE_RTOOLS: true R_ARCH: x64 KEEP_VIGNETTES: true diff --git a/data/pkRatioDataExample.RData b/data/pkRatioDataExample.RData deleted file mode 100644 index be976a86..00000000 Binary files a/data/pkRatioDataExample.RData and /dev/null differ diff --git a/data/simulatedTimeProfiles.rda b/data/simulatedTimeProfiles.rda deleted file mode 100644 index 7aebb0b5..00000000 Binary files a/data/simulatedTimeProfiles.rda and /dev/null differ diff --git a/data/simulatedTimeProfilesMeta.rda b/data/simulatedTimeProfilesMeta.rda deleted file mode 100644 index 5423c46f..00000000 Binary files a/data/simulatedTimeProfilesMeta.rda and /dev/null differ diff --git a/data/simulatedTimeProfilesMetaData.rda b/data/simulatedTimeProfilesMetaData.rda deleted file mode 100644 index a7ee362c..00000000 Binary files a/data/simulatedTimeProfilesMetaData.rda and /dev/null differ diff --git a/data/timeProfileDataFrame.RData b/data/timeProfileDataFrame.RData deleted file mode 100644 index 1454339a..00000000 Binary files a/data/timeProfileDataFrame.RData and /dev/null differ diff --git a/data/tlf-output.RData b/data/tlf-output.RData deleted file mode 100644 index 0bbbdaa9..00000000 Binary files a/data/tlf-output.RData and /dev/null differ diff --git a/data/tlfEnvThemesProperties.RData b/data/tlfEnvThemesProperties.RData deleted file mode 100644 index 5cf467d5..00000000 Binary files a/data/tlfEnvThemesProperties.RData and /dev/null differ diff --git a/inst/boxplot/app.R b/inst/boxplot/app.R new file mode 100644 index 00000000..746523ff --- /dev/null +++ b/inst/boxplot/app.R @@ -0,0 +1,163 @@ +require(shiny) +require(tlf) + +useTheme(loadThemeFromJson(system.file("extdata", "template-theme.json", package = "tlf"))) + +# Define UI page ---- +ui <- fluidPage( + h1("Box Whisker Plot", align = "center"), + column( + 5, + tabsetPanel( + tabPanel( + "Data", + fluidRow( + selectInput("dataType", + label = h4("Data location"), + choices = list("environment" = "environment", "file" = "file") + ), + conditionalPanel( + condition = "input.dataType == 'environment'", + selectInput("dataFromEnv", label = h4("List of environment variables"), choices = sapply(ls(envir = sys.frame()), identity)) + ), + conditionalPanel( + condition = "input.dataType == 'file'", + fileInput("dataFromFile", label = h4("Choose a csv file"), accept = ".csv") + ), + uiOutput("yVariableNames"), + uiOutput("xVariableNames"), + uiOutput("fillVariableNames") + ) + ), + tabPanel( + "Analysis", + fluidRow( + selectInput("ymax", label = "Max whisker", choices = tlfStatFunctions, selected = tlfStatFunctions$`Percentile95%`), + selectInput("upper", label = "Box upper edge", choices = tlfStatFunctions, selected = tlfStatFunctions$`Percentile75%`), + selectInput("middle", label = "Middle edge", choices = tlfStatFunctions, selected = tlfStatFunctions$`Percentile50%`), + selectInput("lower", label = "Box lower edge", choices = tlfStatFunctions, selected = tlfStatFunctions$`Percentile25%`), + selectInput("ymin", label = "Min whisker", choices = tlfStatFunctions, selected = tlfStatFunctions$`Percentile5%`), + br(), + selectInput("outliers", label = "Flag outliers", choices = list(Yes = TRUE, No = FALSE)), + conditionalPanel( + condition = "input.outliers == 'TRUE'", + selectInput("maxOutliers", label = "Max value outlier flag", choices = tlfStatFunctions, selected = tlfStatFunctions$`Percentile75%+1.5IQR`), + selectInput("minOutliers", label = "Min value outlier flag", choices = tlfStatFunctions, selected = tlfStatFunctions$`Percentile25%-1.5IQR`) + ) + ) + ), + tabPanel( + "Axes", + fluidRow( + h4("y axis"), + numericInput("yAxisMin", label = "min", value = NULL), + numericInput("yAxisMax", label = "max", value = NULL), + selectInput("yAxisScale", label = "scale", choices = Scaling, selected = Scaling$log) + ) + ), + tabPanel( + "Labels", + fluidRow( + textInput("xLabel", label = "x label", value = NULL), + textInput("yLabel", label = "y label", value = NULL), + textInput("watermark", label = "watermark", value = NULL) + ) + ) + ) + ), + + # Plot Column + column( + 7, + br(), + plotOutput(outputId = "boxWhiskerPlot"), + br(), + actionButton("exportButton", "Save Plot"), + br(), + align = "center" + ) +) + +# Define server logic required to draw a histogram ---- +server <- function(input, output) { + + getBoxWhiskerData <- reactive({ + boxplotData <- data.frame(`Fill data first` = NULL) + if (input$dataType == "environment") { + if (input$dataFromEnv != "") { + boxplotData <- get(input$dataFromEnv, envir = sys.frame()) + } + } + if (input$dataType == "file") { + if (!isOfLength(input$dataFromFile, 0)) { + boxplotData <- read.csv(input$dataFromFile$datapath, stringsAsFactors = FALSE) + } + } + return(boxplotData) + }) + + getVariableNames <- reactive({ + boxplotData <- getBoxWhiskerData() + return(sapply(names(boxplotData), identity)) + }) + + output$yVariableNames <- renderUI({ + selectInput("yVariableNames2", "Variable in Y", getVariableNames()) + }) + + output$xVariableNames <- renderUI({ + selectInput("xVariableNames2", "Group variable in X", choices = c("none" = "none", getVariableNames())) + }) + + output$fillVariableNames <- renderUI({ + selectInput("fillVariableNames2", "Color group variable", choices = c("none" = "none", getVariableNames())) + }) + + getDataMapping <- reactive({ + xMapping <- input$xVariableNames2 + fillMapping <- input$fillVariableNames2 + if(isIncluded(xMapping, "none")){xMapping <- NULL} + if(isIncluded(fillMapping, "none")){fillMapping <- NULL} + + dataMapping <- BoxWhiskerDataMapping$new(x = xMapping, + y = input$yVariableNames2, + color = fillMapping, + fill = fillMapping, + ymax = input$ymax, + upper = input$upper, + middle = input$middle, + lower = input$lower, + maxOutlierLimit = input$maxOutliers, + minOutlierLimit = input$minOutliers) + return(dataMapping) + }) + + output$boxWhiskerPlot <- renderPlot({ + # Get the data + boxplotData <- getBoxWhiskerData() + if(!isOfType(boxplotData, "data.frame")){return()} + if(nrow(boxplotData)==0){return()} + # Meta Data + # DataMapping + dataMapping <- getDataMapping() + # PlotConfiguration + boxWhiskerPlot <- plotBoxWhisker( + data = boxplotData, + dataMapping = dataMapping, + outliers = as.logical(input$outliers) + ) + yLimits <- c(input$yAxisMin, input$yAxisMax) + if(any(is.na(yLimits))){yLimits <- NULL} + + boxWhiskerPlot <- setYAxis(boxWhiskerPlot, + limits = yLimits, + scale = input$yAxisScale + ) + if(input$xLabel != ""){boxWhiskerPlot <- setPlotLabels(boxWhiskerPlot, xlabel = input$xLabel)} + if(input$yLabel != ""){boxWhiskerPlot <- setPlotLabels(boxWhiskerPlot, ylabel = input$yLabel)} + boxWhiskerPlot <- setWatermark(boxWhiskerPlot, watermark = input$watermark) + boxWhiskerPlot + }) +} + +shinyApp(ui = ui, server = server) diff --git a/inst/ddi-ratio/app.R b/inst/ddi-ratio/app.R new file mode 100644 index 00000000..ffea1b46 --- /dev/null +++ b/inst/ddi-ratio/app.R @@ -0,0 +1,187 @@ +require(shiny) +require(tlf) + +# Use theme template but this could be modified in later versions +useTheme(loadThemeFromJson(system.file("extdata", "template-theme.json", package = "tlf"))) + +# Define UI page ---- +ui <- fluidPage( + h1("DDI Ratio Plot", align = "center"), + column( + 5, + tabsetPanel( + tabPanel( + "Data", + fluidRow( + selectInput("dataType", + label = h4("Data location"), + choices = list("environment" = "environment", "file" = "file") + ), + conditionalPanel( + condition = "input.dataType == 'environment'", + selectInput("dataFromEnv", label = h4("List of environment variables"), choices = sapply(ls(envir = sys.frame()), identity)) + ), + conditionalPanel( + condition = "input.dataType == 'file'", + fileInput("dataFromFile", label = h4("Choose a csv file"), accept = ".csv") + ), + uiOutput("xVariableNames"), + uiOutput("yVariableNames"), + uiOutput("shapeVariableNames"), + uiOutput("colorVariableNames"), + uiOutput("uncertaintyVariableNames") + ) + ), + tabPanel( + "Analysis", + fluidRow( + sliderInput("ratioLine", label = "Ratio Limit", min = 1, max = 10, step = 0.1, value = 2), + sliderInput("guestLine", label = "Guest et al. delta", min = 1, max = 2, step = 0.01, value = 1), + selectInput("comparisonType", label = "Comparison type", choices = DDIComparisonTypes, selected = DDIComparisonTypes$obsVsPred) + ) + ), + tabPanel( + "Axes", + fluidRow( + h4("x axis"), + numericInput("xAxisMin", label = "min", value = NULL), + numericInput("xAxisMax", label = "max", value = NULL), + selectInput("xAxisScale", label = "scale", choices = Scaling, selected = Scaling$log), + h4("y axis"), + numericInput("yAxisMin", label = "min", value = NULL), + numericInput("yAxisMax", label = "max", value = NULL), + selectInput("yAxisScale", label = "scale", choices = Scaling, selected = Scaling$log) + ) + ), + tabPanel( + "Labels", + fluidRow( + textInput("xLabel", label = "x label", value = NULL), + textInput("yLabel", label = "y label", value = NULL), + textInput("watermark", label = "watermark", value = NULL) + ) + ) + ) + ), + + # Plot Column + column( + 7, + br(), + plotOutput(outputId = "ddiRatioPlot"), + br(), + actionButton("exportButton", "Save Plot"), + br(), + tableOutput(outputId = "ddiRatioTable"), + align = "center" + ) +) + +# Define server logic required to draw a histogram ---- +server <- function(input, output) { + + getddiRatioData <- reactive({ + ddiRatioData <- data.frame(`Fill data first` = NULL) + if (input$dataType == "environment") { + if (input$dataFromEnv != "") { + ddiRatioData <- get(input$dataFromEnv, envir = sys.frame()) + } + } + if (input$dataType == "file") { + if (!isOfLength(input$dataFromFile, 0)) { + ddiRatioData <- read.csv(input$dataFromFile$datapath, stringsAsFactors = FALSE) + } + } + return(ddiRatioData) + }) + + getVariableNames <- reactive({ + ddiRatioData <- getddiRatioData() + return(sapply(names(ddiRatioData), identity)) + }) + + output$yVariableNames <- renderUI({ + selectInput("yVariableNames2", "Variable in Y", getVariableNames()) + }) + + output$xVariableNames <- renderUI({ + selectInput("xVariableNames2", "Variable in X", getVariableNames()) + }) + + output$shapeVariableNames <- renderUI({ + selectInput("shapeVariableNames2", "Shape group variable", c("none" = "none", getVariableNames())) + }) + + output$colorVariableNames <- renderUI({ + selectInput("colorVariableNames2", "Color group variable", c("none" = "none", getVariableNames())) + }) + + output$uncertaintyVariableNames <- renderUI({ + selectInput("uncertaintyVariableNames2", "Variable for error bars", c("none" = "none", getVariableNames())) + }) + + getDataMapping <- reactive({ + colorMapping <- input$colorVariableNames2 + shapeMapping <- input$shapeVariableNames2 + uncertaintyMapping <- input$uncertaintyVariableNames2 + if(isIncluded(colorMapping, "none")){colorMapping <- NULL} + if(isIncluded(shapeMapping, "none")){shapeMapping <- NULL} + if(isIncluded(uncertaintyMapping,"none")){uncertaintyMapping <- NULL} + dataMapping <- DDIRatioDataMapping$new(x = input$xVariableNames2, + y = input$yVariableNames2, + color = colorMapping, + shape = shapeMapping, + uncertainty = uncertaintyMapping) + dataMapping$lines$ddiRatio2 <- c(input$ratioLine, 1/input$ratioLine) + dataMapping$lines$guestRatio <- input$guestLine + dataMapping$comparisonType <- input$comparisonType + return(dataMapping) + }) + + output$ddiRatioPlot <- renderPlot({ + # Get the data + ddiRatioData <- getddiRatioData() + if(!isOfType(ddiRatioData, "data.frame")){return()} + if(nrow(ddiRatioData)==0){return()} + # Meta Data + # DataMapping + dataMapping <- getDataMapping() + # PlotConfiguration + ddiRatioPlot <- plotDDIRatio( + data = ddiRatioData, + dataMapping = dataMapping + ) + xLimits <- c(input$xAxisMin, input$xAxisMax) + yLimits <- c(input$yAxisMin, input$yAxisMax) + if(any(is.na(xLimits))){xLimits <- NULL} + if(any(is.na(yLimits))){yLimits <- NULL} + + ddiRatioPlot <- setXAxis(ddiRatioPlot, + limits = xLimits, + scale = input$xAxisScale + ) + ddiRatioPlot <- setYAxis(ddiRatioPlot, + limits = yLimits, + scale = input$yAxisScale + ) + if(input$xLabel != ""){ddiRatioPlot <- setPlotLabels(ddiRatioPlot, xlabel = input$xLabel)} + if(input$yLabel != ""){ddiRatioPlot <- setPlotLabels(ddiRatioPlot, ylabel = input$yLabel)} + ddiRatioPlot <- setWatermark(ddiRatioPlot, watermark = input$watermark) + ddiRatioPlot + }) + + output$ddiRatioTable = renderTable({ + ddiRatioData <- getddiRatioData() + if(!isOfType(ddiRatioData, "data.frame")){return(data.frame())} + if(nrow(ddiRatioData)==0){return(data.frame())} + dataMapping <- getDataMapping() + #ddiRatioTable <- getDDIRatioMeasure(data=ddiRatioData, + # dataMapping = dataMapping, + # ratioLimits = c(input$ratioLine1, input$ratioLine2)) + #ddiRatioTable <- cbind.data.frame(" "= row.names(ddiRatioTable), + # ddiRatioTable) + return(data.frame()) + }) +} + +shinyApp(ui = ui, server = server) diff --git a/inst/extdata/ospsuite-data.csv b/inst/extdata/ospsuite-data.csv new file mode 100644 index 00000000..d714b983 --- /dev/null +++ b/inst/extdata/ospsuite-data.csv @@ -0,0 +1,11 @@ +IndividualId,Gender,Race,Population Name,Organism|Age,Organism|Weight,Organism|BMI,Organism|Gestational age,Organism|Height,Organism|Hematocrit,Organism|VenousBlood|Volume,Organism|ArterialBlood|Volume,Organism|Bone|Specific blood flow rate,Organism|Bone|Volume,Organism|Brain|Volume,Compound,Dose +0,Male,Caucasian,pop_10,14.0688941,54.04230061,0.195913991,40,16.60864445,0.47,0.876713393,0.338426355,0.022446017,10.66401243,1.396369317,Aspirin,6 +1,Male,Caucasian,pop_10,23.4195473,61.2977258,0.215426284,40,16.86835903,0.47,0.813096398,0.31483232,0.028497795,9.444248287,1.453849113,Aspirin,3 +2,Male,Caucasian,pop_10,24.89980573,44.39078282,0.189779636,40,15.294018,0.47,0.805417161,0.365394195,0.028779037,9.399231211,1.524981272,Aspirin,6 +3,Male,Caucasian,pop_10,30.45042863,53.61099397,0.199399381,40,16.39702378,0.47,0.804892445,0.346861454,0.025951289,9.835422969,1.564810183,Sugar,3 +4,Male,Caucasian,pop_10,22.96949291,42.98249667,0.163437843,40,16.21696236,0.43,0.61278102,0.282665445,0.038788192,7.837624313,1.361570067,Sugar,6 +5,Female,Caucasian,pop_10,37.71187351,50.49204631,0.213857614,40,15.36558599,0.41,0.618652748,0.260777401,0.030497307,7.792168874,1.435596199,Aspirin,3 +6,Female,Caucasian,pop_10,50.12875437,54.03716448,0.20959491,40,16.05668585,0.41,0.670054595,0.273566437,0.033728373,8.49320878,1.359598208,Aspirin,6 +7,Female,Caucasian,pop_10,32.53951003,64.24662733,0.248101093,40,16.09202981,0.41,0.800346422,0.305947302,0.031138896,10.24093699,1.381420764,Sugar,3 +8,Female,Caucasian,pop_10,26.86401285,63.05924125,0.263004854,40,15.48433251,0.41,0.600189023,0.273958966,0.032767885,7.964833298,1.227012247,Sugar,6 +9,Female,Caucasian,pop_10,45.97137219,43.53328781,0.17762685,40,15.65512096,0.41,0.73507184,0.288503204,0.032356844,9.296652927,1.191015138,Sugar,3 diff --git a/inst/extdata/ospsuite-metadata.csv b/inst/extdata/ospsuite-metadata.csv new file mode 100644 index 00000000..b02f3176 --- /dev/null +++ b/inst/extdata/ospsuite-metadata.csv @@ -0,0 +1,18 @@ +Variable,Dimension,Unit +IndividualId,, +Gender,, +Race,, +Population Name,, +Organism|Age,Age,yrs +Organism|Weight,Mass,kg +Organism|BMI,,kg/m2 +Organism|Gestational age,Age,week(s) +Organism|Height,Length,dm +Organism|Hematocrit,Volume,l +Organism|VenousBlood|Volume,Volume,l +Organism|ArterialBlood|Volume,Volume,l +Organism|Bone|Specific blood flow rate,Flow,l/min +Organism|Bone|Volume,Volume,l +Organism|Brain|Volume,Volume,l +Compound,, +Dose,Mass,mg diff --git a/inst/extdata/template-theme.json b/inst/extdata/template-theme.json new file mode 100644 index 00000000..38fd69ff --- /dev/null +++ b/inst/extdata/template-theme.json @@ -0,0 +1,366 @@ +{ + "fonts": { + "title": { + "color": "black", + "size": 16, + "fontFace": "bold", + "fontFamily": "", + "angle": 0 + }, + "subtitle": { + "color": "black", + "size": 12, + "fontFace": "plain", + "fontFamily": "", + "angle": 0 + }, + "xlabel": { + "color": "black", + "size": 16, + "fontFace": "plain", + "fontFamily": "", + "angle": 0 + }, + "ylabel": { + "color": "black", + "size": 16, + "fontFace": "plain", + "fontFamily": "", + "angle": 90 + }, + "watermark": { + "color": "grey30", + "size": 12, + "fontFace": "plain", + "fontFamily": "", + "angle": 30 + }, + "legend": { + "color": "black", + "size": 14, + "fontFace": "plain", + "fontFamily": "", + "angle": 0 + }, + "xAxis": { + "color": "black", + "size": 14, + "fontFace": "plain", + "fontFamily": "", + "angle": 0 + }, + "yAxis": { + "color": "black", + "size": 14, + "fontFace": "plain", + "fontFamily": "", + "angle": 0 + } + }, + "background": { + "watermark": "template", + "legendPosition": "outsideRight", + "plot": { + "fill": "white", + "color": "black", + "size": 0.5, + "linetype": "blank" + }, + "panel": { + "fill": "white", + "color": "black", + "size": "0.5", + "linetype": "solid" + }, + "xAxis": { + "color": "black", + "size": "1", + "linetype": "solid" + }, + "yAxis": { + "color": "black", + "size": "1", + "linetype": "solid" + }, + "xGrid": { + "color": "grey", + "size": "0.5", + "linetype": "dashed" + }, + "yGrid": { + "color": "grey", + "size": "0.5", + "linetype": "dashed" + }, + "legend": { + "fill": "white", + "color": "white", + "size": "0.5", + "linetype": "blank" + } + }, + "aestheticMaps": { + "color": [ + "#0078D7", + "#D83B01", + "#107C10", + "#A80000", + "#002050", + "#B4009E" + ], + "fill": [ + "#0078D7", + "#D83B01", + "#107C10", + "#A80000", + "#002050", + "#B4009E" + ], + "shape": [ + "square", + "dot", + "circle", + "diamond", + "star", + "plus", + "cross", + "triangle" + ], + "linetype": [ + "solid", + "longdash", + "dotted", + "dashed", + "twodash", + "dotdash" + ], + "size": [ + 1, + 2, + 3, + 4, + 5 + ], + "alpha": [ + 0.75, + 0.5, + 0.25 + ] + }, + "plotConfigurations": { + "addScatter": { + "color": "next", + "fill": "NA", + "shape": "next", + "linetype": "blank", + "size": "first", + "alpha": 1 + }, + "addLine": { + "color": "next", + "fill": "NA", + "shape": "blank", + "linetype": "same", + "size": "first", + "alpha": 1 + }, + "addRibbon": { + "color": "next", + "fill": "next", + "shape": "blank", + "linetype": "same", + "size": "first", + "alpha": "first" + }, + "addErrorbar": { + "color": "next", + "fill": "NA", + "shape": "blank", + "linetype": "first", + "size": "first", + "alpha": 1 + }, + "plotPKRatio": { + "points": { + "color": "reset", + "fill": "NA", + "shape": "reset", + "linetype": "blank", + "size": 4, + "alpha": 1 + }, + "lines": { + "color": [ + "#000000", + "#0078D7", + "#D83B01" + ], + "fill": "NA", + "shape": "blank", + "linetype": "longdash", + "size": 1, + "alpha": 1 + }, + "errorbars": { + "color": "reset", + "fill": "NA", + "shape": "blank", + "linetype": "solid", + "size": 1, + "alpha": 1 + } + }, + "plotDDIRatio": { + "points": { + "color": "reset", + "fill": "NA", + "shape": "reset", + "linetype": "blank", + "size": 4, + "alpha": 1 + }, + "lines": { + "color": [ + "#000000", + "#0078D7", + "#D83B01" + ], + "fill": "NA", + "shape": "blank", + "linetype": "longdash", + "size": 1, + "alpha": 1 + }, + "errorbars": { + "color": "reset", + "fill": "NA", + "shape": "blank", + "linetype": "solid", + "size": 1, + "alpha": 1 + } + }, + "plotTimeProfile": { + "points": { + "color": "reset", + "fill": "NA", + "shape": "reset", + "linetype": "blank", + "size": 3, + "alpha": 1 + }, + "lines": { + "color": "reset", + "fill": "NA", + "shape": "blank", + "linetype": "reset", + "size": 1, + "alpha": 1 + }, + "ribbons": { + "color": "reset", + "fill": "reset", + "shape": "blank", + "linetype": "blank", + "size": 1, + "alpha": "first" + }, + "errorbars": { + "color": "reset", + "fill": "NA", + "shape": "blank", + "linetype": "solid", + "size": 1, + "alpha": 1 + } + }, + "plotObsVsPred": { + "points": { + "color": "reset", + "fill": "NA", + "shape": "reset", + "linetype": "blank", + "size": 4, + "alpha": 1 + }, + "lines": { + "color": "#111111", + "fill": "NA", + "shape": "blank", + "linetype": "longdash", + "size": 1, + "alpha": 1 + }, + "errorbars": { + "color": "reset", + "fill": "NA", + "shape": "blank", + "linetype": "solid", + "size": 1, + "alpha": 1 + } + }, + "plotBoxWhisker": { + "points": { + "color": "#111111", + "fill": "NA", + "shape": "dot", + "linetype": "blank", + "size": 2, + "alpha": 1 + }, + "ribbons": { + "color": "#000000", + "fill": "reset", + "shape": "blank", + "linetype": "solid", + "size": 1, + "alpha": "first" + } + }, + "plotTornado": { + "lines": { + "color": "#111111", + "fill": "NA", + "shape": "blank", + "linetype": "longdash", + "size": 1, + "alpha": 1 + }, + "points": { + "color": "reset", + "fill": "NA", + "shape": "reset", + "linetype": "blank", + "size": 3, + "alpha": 1 + }, + "ribbons": { + "color": "reset", + "fill": "reset", + "shape": "blank", + "linetype": "solid", + "size": 1, + "alpha": "first" + } + }, + "plotHistogram": { + "lines": { + "color": "reset", + "fill": "NA", + "shape": "blank", + "linetype": "solid", + "size": 1, + "alpha": 1 + }, + "ribbons": { + "color": "#000000", + "fill": "reset", + "shape": "blank", + "linetype": "solid", + "size": 1, + "alpha": "first" + } + } + } +} \ No newline at end of file diff --git a/inst/extdata/test-data.csv b/inst/extdata/test-data.csv new file mode 100644 index 00000000..fd2b98ae --- /dev/null +++ b/inst/extdata/test-data.csv @@ -0,0 +1,51 @@ +ID,Age,Obs,Pred,Ratio,AgeBin,Sex,Country,SD +1,48,4,2.9,0.725,Adults,Male,Canada,0.693 +2,36,4.4,5.75,1.307,Adults,Male,Canada,0.188 +3,52,2.8,2.7,0.964,Adults,Male,Canada,0.984 +4,47,3.75,3.05,0.813,Adults,Male,Canada,0.591 +5,0,1.95,5.25,2.692,Peds,Male,Canada,0.443 +6,48,2.45,5.3,2.163,Adults,Male,Canada,0.072 +7,34,2,4.4,2.2,Adults,Male,Canada,0.489 +8,55,1.05,1.75,1.667,Adults,Male,Canada,0.354 +9,12,3.7,4.3,1.162,Peds,Male,Canada,0.128 +10,54,1.65,4.2,2.545,Adults,Male,Canada,0.096 +11,27,3.95,1.75,0.443,Adults,Male,Canada,0.972 +12,3,1.8,3,1.667,Peds,Male,Canada,0.988 +13,8,2.8,5.95,2.125,Peds,Male,Canada,0.689 +14,12,1.3,3.5,2.692,Peds,Male,Canada,0.845 +15,2,1.45,2.05,1.414,Peds,Male,Canada,0.578 +16,5,4.35,5.95,1.368,Peds,Male,Canada,0.449 +17,22,5.6,2.95,0.527,Adults,Male,Canada,0.631 +18,58,5.3,5.85,1.104,Adults,Male,Canada,0.766 +19,39,5.15,5.45,1.058,Adults,Male,Canada,0.132 +20,21,4.6,3.3,0.717,Adults,Male,Germany,0.713 +21,41,1.05,5.6,5.333,Adults,Male,Germany,0.847 +22,57,2.4,1.65,0.688,Adults,Male,Germany,0.83 +23,1,5.3,2.1,0.396,Peds,Male,Germany,0.94 +24,1,4.05,5.15,1.272,Peds,Male,Germany,0.843 +25,35,3.65,3.25,0.89,Adults,Male,Germany,0.01 +26,39,3.45,3.9,1.13,Adults,Female,Germany,0.414 +27,1,3.9,5.4,1.385,Peds,Female,Germany,0.746 +28,49,3.6,6,1.667,Adults,Female,Germany,0.776 +29,37,6,3.25,0.542,Adults,Female,Germany,0.606 +30,51,1.15,1.8,1.565,Adults,Female,Germany,0.571 +31,38,4,1.4,0.35,Adults,Female,Germany,0.657 +32,54,3.45,4.25,1.232,Adults,Female,Germany,0.923 +33,10,2.2,1.25,0.568,Peds,Female,Germany,0.336 +34,44,4.05,2.35,0.58,Adults,Female,Germany,0.746 +35,11,5,1.3,0.26,Peds,Female,Germany,0.473 +36,28,2.2,3,1.364,Adults,Female,Germany,0.222 +37,28,3.35,3.4,1.015,Adults,Female,Germany,0.867 +38,8,4.15,4.2,1.012,Peds,Female,Germany,0.714 +39,53,3.9,1.25,0.321,Adults,Female,Germany,0.995 +40,13,5.5,5.8,1.055,Peds,Female,Germany,0.803 +41,29,3.8,1.5,0.395,Adults,Female,France,0.058 +42,21,2.65,3.8,1.434,Adults,Female,France,0.884 +43,12,3.4,2.5,0.735,Peds,Female,France,0.304 +44,54,2.4,2.85,1.188,Adults,Female,France,0.423 +45,17,5.75,1.7,0.296,Peds,Female,France,0.058 +46,20,3.75,4,1.067,Adults,Female,France,0.156 +47,23,1.1,3.4,3.091,Adults,Female,France,0.065 +48,48,1.55,3.3,2.129,Adults,Female,France,0.15 +49,29,5.3,1.35,0.255,Adults,Female,France,0.841 +50,53,3.95,1.6,0.405,Adults,Female,France,0.717 diff --git a/inst/histogram/app.R b/inst/histogram/app.R new file mode 100644 index 00000000..1251ac61 --- /dev/null +++ b/inst/histogram/app.R @@ -0,0 +1,158 @@ +require(shiny) +require(tlf) + +useTheme(loadThemeFromJson(system.file("extdata", "template-theme.json", package = "tlf"))) + +# Define UI page ---- +ui <- fluidPage( + h1("Histogram", align = "center"), + column( + 5, + tabsetPanel( + tabPanel( + "Data", + fluidRow( + selectInput("dataType", + label = h4("Data location"), + choices = list("environment" = "environment", "file" = "file") + ), + conditionalPanel( + condition = "input.dataType == 'environment'", + selectInput("dataFromEnv", label = h4("List of environment variables"), choices = sapply(ls(envir = sys.frame()), identity)) + ), + conditionalPanel( + condition = "input.dataType == 'file'", + fileInput("dataFromFile", label = h4("Choose a csv file"), accept = ".csv") + ), + uiOutput("xVariableNames"), + uiOutput("fillVariableNames") + ) + ), + tabPanel( + "Analysis", + fluidRow( + uiOutput("bins"), + selectInput("stack", label = "Stack bars", choices = list(Yes = "Yes", No = "No")), + selectInput("fitNormalDist", label = "Fit normal distribution", choices = list(Yes = "Yes", No = "No")) + ) + ), + tabPanel( + "Axes", + fluidRow( + h4("x axis"), + numericInput("xAxisMin", label = "min", value = NULL), + numericInput("xAxisMax", label = "max", value = NULL), + selectInput("XAxisScale", label = "scale", choices = Scaling, selected = Scaling$lin), + h4("y axis"), + numericInput("yAxisMin", label = "min", value = NULL), + numericInput("yAxisMax", label = "max", value = NULL), + selectInput("yAxisScale", label = "scale", choices = Scaling, selected = Scaling$lin) + ) + ), + tabPanel( + "Labels", + fluidRow( + textInput("xLabel", label = "x label", value = NULL), + textInput("yLabel", label = "y label", value = NULL), + textInput("watermark", label = "watermark", value = NULL) + ) + ) + ) + ), + + # Plot Column + column( + 7, + br(), + plotOutput(outputId = "histogramPlot"), + br(), + actionButton("exportButton", "Save Plot"), + br(), + align = "center" + ) +) + +# Define server logic required to draw a histogram ---- +server <- function(input, output) { + + getHistogramData <- reactive({ + histogramData <- data.frame(`Fill data first` = NULL) + if (input$dataType == "environment") { + if (input$dataFromEnv != "") { + histogramData <- get(input$dataFromEnv, envir = sys.frame()) + } + } + if (input$dataType == "file") { + if (!isOfLength(input$dataFromFile, 0)) { + histogramData <- read.csv(input$dataFromFile$datapath, stringsAsFactors = FALSE) + } + } + return(histogramData) + }) + + getVariableNames <- reactive({ + histogramData <- getHistogramData() + return(sapply(names(histogramData), identity)) + }) + + output$xVariableNames <- renderUI({ + selectInput("xVariableNames2", "Group variable in X", choices = getVariableNames()) + }) + + output$fillVariableNames <- renderUI({ + selectInput("fillVariableNames2", "Color group variable", choices = c("none" = "none", getVariableNames())) + }) + + output$bins <- renderUI({ + histogramData <- getHistogramData() + sliderInput("bins2", "Number of bins", min = 1, max = max(10, nrow(histogramData)), step = 1, value = 10) + }) + + getDataMapping <- reactive({ + xMapping <- input$xVariableNames2 + fillMapping <- input$fillVariableNames2 + if(isIncluded(xMapping, "none")){xMapping <- NULL} + if(isIncluded(fillMapping, "none")){fillMapping <- NULL} + + dataMapping <- HistogramDataMapping$new(stack = as.logical(input$stack %in% "Yes"), + x = input$xVariableNames2, + fill = fillMapping, + bins = input$bins2, + fitNormalDist = as.logical(input$fitNormalDist %in% "Yes")) + return(dataMapping) + }) + + output$histogramPlot <- renderPlot({ + # Get the data + histogramData <- getHistogramData() + if(!isOfType(histogramData, "data.frame")){return()} + if(nrow(histogramData)==0){return()} + # Meta Data + # DataMapping + dataMapping <- getDataMapping() + # PlotConfiguration + histogramPlot <- plotHistogram( + data = histogramData, + dataMapping = dataMapping + ) + xLimits <- c(input$xAxisMin, input$xAxisMax) + yLimits <- c(input$yAxisMin, input$yAxisMax) + if(any(is.na(xLimits))){xLimits <- NULL} + if(any(is.na(yLimits))){yLimits <- NULL} + + histogramPlot <- setXAxis(histogramPlot, + limits = xLimits, + scale = input$xAxisScale + ) + histogramPlot <- setYAxis(histogramPlot, + limits = yLimits, + scale = input$yAxisScale + ) + if(input$xLabel != ""){histogramPlot <- setPlotLabels(histogramPlot, xlabel = input$xLabel)} + if(input$yLabel != ""){histogramPlot <- setPlotLabels(histogramPlot, ylabel = input$yLabel)} + histogramPlot <- setWatermark(histogramPlot, watermark = input$watermark) + histogramPlot + }) +} + +shinyApp(ui = ui, server = server) diff --git a/inst/obs-vs-pred/app.R b/inst/obs-vs-pred/app.R new file mode 100644 index 00000000..0871981c --- /dev/null +++ b/inst/obs-vs-pred/app.R @@ -0,0 +1,183 @@ +require(shiny) +require(tlf) + +# Use theme template but this could be modified in later versions +useTheme(loadThemeFromJson(system.file("extdata", "template-theme.json", package = "tlf"))) + +# Define UI page ---- +ui <- fluidPage( + h1("Obs vs Pred Plot", align = "center"), + column( + 5, + tabsetPanel( + tabPanel( + "Data", + fluidRow( + selectInput("dataType", + label = h4("Data location"), + choices = list("environment" = "environment", "file" = "file") + ), + conditionalPanel( + condition = "input.dataType == 'environment'", + selectInput("dataFromEnv", label = h4("List of environment variables"), choices = sapply(ls(envir = sys.frame()), identity)) + ), + conditionalPanel( + condition = "input.dataType == 'file'", + fileInput("dataFromFile", label = h4("Choose a csv file"), accept = ".csv") + ), + uiOutput("xVariableNames"), + uiOutput("yVariableNames"), + uiOutput("groupingVariableNames"), + uiOutput("uncertaintyVariableNames"), + uiOutput("lloqVariableNames") + ) + ), + tabPanel( + "Analysis", + fluidRow( + selectInput("regression", label = "Regression", choices = list(none = "none", linear = "lm", loess = "loess")) + ) + ), + tabPanel( + "Axes", + fluidRow( + h4("x axis"), + numericInput("xAxisMin", label = "min", value = NULL), + numericInput("xAxisMax", label = "max", value = NULL), + selectInput("xAxisScale", label = "scale", choices = Scaling, selected = Scaling$lin), + h4("y axis"), + numericInput("yAxisMin", label = "min", value = NULL), + numericInput("yAxisMax", label = "max", value = NULL), + selectInput("yAxisScale", label = "scale", choices = Scaling, selected = Scaling$lin) + ) + ), + tabPanel( + "Labels", + fluidRow( + textInput("xLabel", label = "x label", value = NULL), + textInput("yLabel", label = "y label", value = NULL), + textInput("watermark", label = "watermark", value = NULL) + ) + ) + ) + ), + + # Plot Column + column( + 7, + br(), + plotOutput(outputId = "obsVsPredPlot"), + br(), + actionButton("exportButton", "Save Plot"), + br(), + tableOutput(outputId = "obsVsPredTable"), + align = "center" + ) +) + +# Define server logic required to draw a histogram ---- +server <- function(input, output) { + + getObsVsPredData <- reactive({ + obsVsPredData <- data.frame(`Fill data first` = NULL) + if (input$dataType == "environment") { + if (input$dataFromEnv != "") { + obsVsPredData <- get(input$dataFromEnv, envir = sys.frame()) + } + } + if (input$dataType == "file") { + if (!isOfLength(input$dataFromFile, 0)) { + obsVsPredData <- read.csv(input$dataFromFile$datapath, stringsAsFactors = FALSE) + } + } + return(obsVsPredData) + }) + + getVariableNames <- reactive({ + obsVsPredData <- getObsVsPredData() + return(sapply(names(obsVsPredData), identity)) + }) + + output$yVariableNames <- renderUI({ + selectInput("yVariableNames2", "Variable in Y", getVariableNames()) + }) + + output$xVariableNames <- renderUI({ + selectInput("xVariableNames2", "Variable in X", getVariableNames()) + }) + + output$groupingVariableNames <- renderUI({ + selectInput("groupingVariableNames2", "Group variable", c("none" = "none", getVariableNames())) + }) + + output$uncertaintyVariableNames <- renderUI({ + selectInput("uncertaintyVariableNames2", "Variable for error bars", c("none" = "none", getVariableNames())) + }) + output$lloqVariableNames <- renderUI({ + selectInput("lloqVariableNames2", "Variable for LLOQ", c("none" = "none", getVariableNames())) + }) + + getDataMapping <- reactive({ + colorMapping <- input$groupingVariableNames2 + uncertaintyMapping <- input$uncertaintyVariableNames2 + lloqMapping <- input$lloqVariableNames2 + if(isIncluded(colorMapping, "none")){colorMapping <- NULL} + if(isIncluded(uncertaintyMapping,"none")){uncertaintyMapping <- NULL} + if(isIncluded(lloqMapping,"none")){lloqMapping <- NULL} + dataMapping <- ObsVsPredDataMapping$new(x = input$xVariableNames2, + y = input$yVariableNames2, + color = colorMapping, + uncertainty = uncertaintyMapping, + lloq = lloqMapping) + smoother <- input$regression + if(smoother=="none"){smoother <- NULL} + dataMapping$smoother <- smoother + return(dataMapping) + }) + + output$obsVsPredPlot <- renderPlot({ + # Get the data + obsVsPredData <- getObsVsPredData() + if(!isOfType(obsVsPredData, "data.frame")){return()} + if(nrow(obsVsPredData)==0){return()} + # Meta Data + # DataMapping + dataMapping <- getDataMapping() + # PlotConfiguration + obsVsPredPlot <- plotObsVsPred( + data = obsVsPredData, + dataMapping = dataMapping + ) + xLimits <- c(input$xAxisMin, input$xAxisMax) + yLimits <- c(input$yAxisMin, input$yAxisMax) + if(any(is.na(xLimits))){xLimits <- NULL} + if(any(is.na(yLimits))){yLimits <- NULL} + + obsVsPredPlot <- setXAxis(obsVsPredPlot, + limits = xLimits, + scale = input$xAxisScale + ) + obsVsPredPlot <- setYAxis(obsVsPredPlot, + limits = yLimits, + scale = input$yAxisScale + ) + if(input$xLabel != ""){obsVsPredPlot <- setPlotLabels(obsVsPredPlot, xlabel = input$xLabel)} + if(input$yLabel != ""){obsVsPredPlot <- setPlotLabels(obsVsPredPlot, ylabel = input$yLabel)} + obsVsPredPlot <- setWatermark(obsVsPredPlot, watermark = input$watermark) + obsVsPredPlot + }) + + output$obsVsPredTable = renderTable({ + obsVsPredData <- getObsVsPredData() + if(!isOfType(obsVsPredData, "data.frame")){return(data.frame())} + if(nrow(obsVsPredData)==0){return(data.frame())} + dataMapping <- getDataMapping() + R2 <- 1 - var(obsVsPredData[,dataMapping$y]-obsVsPredData[,dataMapping$x])/var(obsVsPredData[,dataMapping$x]) + MFE <- 10^(mean(log10(obsVsPredData[,dataMapping$y])-log10(obsVsPredData[,dataMapping$x]))) + obsVsPredTable <- data.frame(Evaluation = c("R2", "MFE"), + Value = c(R2, MFE)) + return(obsVsPredTable) + }) +} + +shinyApp(ui = ui, server = server) diff --git a/inst/pk-ratio/app.R b/inst/pk-ratio/app.R new file mode 100644 index 00000000..9c09b333 --- /dev/null +++ b/inst/pk-ratio/app.R @@ -0,0 +1,185 @@ +require(shiny) +require(tlf) + +# Use theme template but this could be modified in later versions +useTheme(loadThemeFromJson(system.file("extdata", "template-theme.json", package = "tlf"))) + +# Define UI page ---- +ui <- fluidPage( + h1("PK Ratio Plot", align = "center"), + column( + 5, + tabsetPanel( + tabPanel( + "Data", + fluidRow( + selectInput("dataType", + label = h4("Data location"), + choices = list("environment" = "environment", "file" = "file") + ), + conditionalPanel( + condition = "input.dataType == 'environment'", + selectInput("dataFromEnv", label = h4("List of environment variables"), choices = sapply(ls(envir = sys.frame()), identity)) + ), + conditionalPanel( + condition = "input.dataType == 'file'", + fileInput("dataFromFile", label = h4("Choose a csv file"), accept = ".csv") + ), + uiOutput("xVariableNames"), + uiOutput("yVariableNames"), + uiOutput("shapeVariableNames"), + uiOutput("colorVariableNames"), + uiOutput("uncertaintyVariableNames") + ) + ), + tabPanel( + "Analysis", + fluidRow( + sliderInput("ratioLine1", label = "Ratio Limit #1", min = 1, max = 10, step = 0.1, value = 1.5), + sliderInput("ratioLine2", label = "Ratio Limit #2", min = 1, max = 10, step = 0.1, value = 2) + ) + ), + tabPanel( + "Axes", + fluidRow( + h4("x axis"), + numericInput("xAxisMin", label = "min", value = NULL), + numericInput("xAxisMax", label = "max", value = NULL), + selectInput("xAxisScale", label = "scale", choices = Scaling, selected = Scaling$lin), + h4("y axis"), + numericInput("yAxisMin", label = "min", value = NULL), + numericInput("yAxisMax", label = "max", value = NULL), + selectInput("yAxisScale", label = "scale", choices = Scaling, selected = Scaling$log) + ) + ), + tabPanel( + "Labels", + fluidRow( + textInput("xLabel", label = "x label", value = NULL), + textInput("yLabel", label = "y label", value = NULL), + textInput("watermark", label = "watermark", value = NULL) + ) + ) + ) + ), + + # Plot Column + column( + 7, + br(), + plotOutput(outputId = "pkRatioPlot"), + br(), + actionButton("exportButton", "Save Plot"), + br(), + tableOutput(outputId = "pkRatioTable"), + align = "center" + ) +) + +# Define server logic required to draw a histogram ---- +server <- function(input, output) { + + getPKRatioData <- reactive({ + pkRatioData <- data.frame(`Fill data first` = NULL) + if (input$dataType == "environment") { + if (input$dataFromEnv != "") { + pkRatioData <- get(input$dataFromEnv, envir = sys.frame()) + } + } + if (input$dataType == "file") { + if (!isOfLength(input$dataFromFile, 0)) { + pkRatioData <- read.csv(input$dataFromFile$datapath, stringsAsFactors = FALSE) + } + } + return(pkRatioData) + }) + + getVariableNames <- reactive({ + pkRatioData <- getPKRatioData() + return(sapply(names(pkRatioData), identity)) + }) + + output$yVariableNames <- renderUI({ + selectInput("yVariableNames2", "Variable in Y", getVariableNames()) + }) + + output$xVariableNames <- renderUI({ + selectInput("xVariableNames2", "Variable in X", getVariableNames()) + }) + + output$shapeVariableNames <- renderUI({ + selectInput("shapeVariableNames2", "Shape group variable", c("none" = "none", getVariableNames())) + }) + + output$colorVariableNames <- renderUI({ + selectInput("colorVariableNames2", "Color group variable", c("none" = "none", getVariableNames())) + }) + + output$uncertaintyVariableNames <- renderUI({ + selectInput("uncertaintyVariableNames2", "Variable for error bars", c("none" = "none", getVariableNames())) + }) + + getDataMapping <- reactive({ + colorMapping <- input$colorVariableNames2 + shapeMapping <- input$shapeVariableNames2 + uncertaintyMapping <- input$uncertaintyVariableNames2 + if(isIncluded(colorMapping, "none")){colorMapping <- NULL} + if(isIncluded(shapeMapping, "none")){shapeMapping <- NULL} + if(isIncluded(uncertaintyMapping,"none")){uncertaintyMapping <- NULL} + dataMapping <- PKRatioDataMapping$new(x = input$xVariableNames2, + y = input$yVariableNames2, + color = colorMapping, + shape = shapeMapping, + uncertainty = uncertaintyMapping) + dataMapping$lines[[2]] <- c(input$ratioLine1, 1/input$ratioLine1) + dataMapping$lines[[3]] <- c(input$ratioLine2, 1/input$ratioLine2) + return(dataMapping) + }) + + output$pkRatioPlot <- renderPlot({ + # Get the data + pkRatioData <- getPKRatioData() + if(!isOfType(pkRatioData, "data.frame")){return()} + if(nrow(pkRatioData)==0){return()} + # Meta Data + # DataMapping + dataMapping <- getDataMapping() + # PlotConfiguration + pkRatioPlot <- plotPKRatio( + data = pkRatioData, + dataMapping = dataMapping + ) + xLimits <- c(input$xAxisMin, input$xAxisMax) + yLimits <- c(input$yAxisMin, input$yAxisMax) + if(any(is.na(xLimits))){xLimits <- NULL} + if(any(is.na(yLimits))){yLimits <- NULL} + + pkRatioPlot <- setXAxis(pkRatioPlot, + limits = xLimits, + scale = input$xAxisScale + ) + pkRatioPlot <- setYAxis(pkRatioPlot, + limits = yLimits, + scale = input$yAxisScale + ) + if(input$xLabel != ""){pkRatioPlot <- setPlotLabels(pkRatioPlot, xlabel = input$xLabel)} + if(input$yLabel != ""){pkRatioPlot <- setPlotLabels(pkRatioPlot, ylabel = input$yLabel)} + pkRatioPlot <- setWatermark(pkRatioPlot, watermark = input$watermark) + pkRatioPlot + }) + + output$pkRatioTable = renderTable({ + pkRatioData <- getPKRatioData() + if(!isOfType(pkRatioData, "data.frame")){return(data.frame())} + if(nrow(pkRatioData)==0){return(data.frame())} + dataMapping <- getDataMapping() + pkRatioTable <- getPKRatioMeasure(data=pkRatioData, + dataMapping = dataMapping, + ratioLimits = c(input$ratioLine1, input$ratioLine2)) + pkRatioTable <- cbind.data.frame(" "= row.names(pkRatioTable), + pkRatioTable) + return(pkRatioTable) + }) +} + +shinyApp(ui = ui, server = server) diff --git a/inst/theme-maker/app.R b/inst/theme-maker/app.R new file mode 100644 index 00000000..a55675fa --- /dev/null +++ b/inst/theme-maker/app.R @@ -0,0 +1,514 @@ +require(shiny) +require(tlf) + +jsonTheme <- loadThemeFromJson(system.file("extdata", "template-theme.json", package = "tlf")) +useTheme(jsonTheme) + +# Define UI page ---- +ui <- fluidPage( + h1("Theme Maker", align = "center"), + column( + 5, + tabsetPanel( + tabPanel( + "Labels", + tabsetPanel( + tabPanel("Title", + fluidRow( + textInput("titleColor", label = "color", value = jsonTheme$fonts$title$color), + sliderInput("titleSize", label = "size", min = 1, max = 30, value = jsonTheme$fonts$title$size, step = 0.5), + sliderInput("titleAngle", label = "angle", min = -180, max = 180, value = jsonTheme$fonts$title$angle, step = 5) + )), + tabPanel("Subtitle", + fluidRow( + textInput("subtitleColor", label = "color", value = jsonTheme$fonts$subtitle$color), + sliderInput("subtitleSize", label = "size", min = 1, max = 30, value = jsonTheme$fonts$subtitle$size, step = 0.5), + sliderInput("subtitleAngle", label = "angle", min = -180, max = 180, value = jsonTheme$fonts$subtitle$angle, step = 5) + )), + tabPanel("X-label", + fluidRow( + textInput("xlabelColor", label = "color", value = jsonTheme$fonts$xlabel$color), + sliderInput("xlabelSize", label = "size", min = 1, max = 30, value = jsonTheme$fonts$xlabel$size, step = 0.5), + sliderInput("xlabelAngle", label = "angle", min = -180, max = 180, value = jsonTheme$fonts$xlabel$angle, step = 5) + )), + tabPanel("Y-label", + fluidRow( + textInput("ylabelColor", label = "color", value = jsonTheme$fonts$ylabel$color), + sliderInput("ylabelSize", label = "size", min = 1, max = 30, value = jsonTheme$fonts$ylabel$size, step = 0.5), + sliderInput("ylabelAngle", label = "angle", min = -180, max = 180, value = jsonTheme$fonts$ylabel$angle, step = 5) + )) + )), + tabPanel( + "Watermark", + fluidRow( + textInput("watermarkText", label = h4("text"), value = jsonTheme$background$watermark), + textInput("watermarkColor", label = h4("color"), value = jsonTheme$fonts$watermark$color), + sliderInput("watermarkSize", label = h4("size"), min = 1, max = 30, value = jsonTheme$fonts$watermark$size, step = 0.5), + sliderInput("watermarkAngle", label = h4("angle"), min = -180, max = 180, value = jsonTheme$fonts$watermark$angle, step = 5) + ) + ), + tabPanel( + "Background", + tabsetPanel( + tabPanel("Plot Area", + textInput("plotFill", label = h4("plot area fill"), value = jsonTheme$background$plot$fill), + textInput("plotColor", label = h4("plot area frame color"), value = jsonTheme$background$plot$color), + sliderInput("plotSize", label = h4("plot area frame size"), min = 0, max = 5, value = jsonTheme$background$plot$size, step = 0.05), + selectInput("plotLinetype", label = h4("plot area frame linetype"), choices = Linetypes, selected = jsonTheme$background$plot$linetype) + ), + tabPanel("Panel Area", + textInput("panelFill", label = h4("panel area fill"), value = jsonTheme$background$panel$fill), + textInput("panelColor", label = h4("panel area frame color"), value = jsonTheme$background$panel$color), + sliderInput("panelSize", label = h4("panel area frame size"), min = 0, max = 5, value = jsonTheme$background$panel$size, step = 0.05), + selectInput("panelLinetype", label = h4("panel area frame linetype"), choices = Linetypes, selected = jsonTheme$background$panel$linetype) + ) + )), + tabPanel( + "Axes", + tabsetPanel( + tabPanel("X-axis", + textInput("xAxisColor", label = h4("axis color"), value = jsonTheme$background$xAxis$color), + sliderInput("xAxisSize", label = h4("axis size"), min = 0, max = 5, value = jsonTheme$background$xAxis$size, step = 0.05), + selectInput("xAxisLinetype", label = h4("axis linetype"), choices = Linetypes, selected = jsonTheme$background$xAxis$linetype), + textInput("xAxisTicksColor", label = h4("ticklabels color"), value = jsonTheme$fonts$xAxis$color), + sliderInput("xAxisTicksSize", label = h4("ticklabels size"), min = 0, max = 30, value = jsonTheme$fonts$xAxis$size, step = 0.5), + sliderInput("xAxisTicksAngle", label = h4("ticklabels angle"), min = -180, max = 180, value = jsonTheme$fonts$xAxis$angle, step = 5) + ), + tabPanel("Y-axis", + textInput("yAxisColor", label = h4("y-axis color"), value = jsonTheme$background$yAxis$color), + sliderInput("yAxisSize", label = h4("y-axis size"), min = 0, max = 5, value = jsonTheme$background$yAxis$size, step = 0.05), + selectInput("yAxisLinetype", label = h4("y-axis linetype"), choices = Linetypes, selected = jsonTheme$background$yAxis$linetype), + textInput("yAxisTicksColor", label = h4("ticklabels color"), value = jsonTheme$fonts$xAxis$color), + sliderInput("yAxisTicksSize", label = h4("ticklabels size"), min = 0, max = 30, value = jsonTheme$fonts$yAxis$size, step = 0.5), + sliderInput("yAxisTicksAngle", label = h4("ticklabels angle"), min = -180, max = 180, value = jsonTheme$fonts$yAxis$angle, step = 5) + ) + )), + tabPanel( + "Grid", + tabsetPanel( + tabPanel("X-grid", + textInput("xGridColor", label = h4("color"), value = jsonTheme$background$xGrid$color), + sliderInput("xGridSize", label = h4("size"), min = 0, max = 5, value = jsonTheme$background$xGrid$size, step = 0.05), + selectInput("xGridLinetype", label = h4("linetype"), choices = Linetypes, selected = jsonTheme$background$xGrid$linetype) + ), + tabPanel("Y-Grid", + textInput("yGridColor", label = h4("color"), value = jsonTheme$background$yGrid$color), + sliderInput("yGridSize", label = h4("size"), min = 0, max = 5, value = jsonTheme$background$yGrid$size, step = 0.05), + selectInput("yGridLinetype", label = h4("linetype"), choices = Linetypes, selected = jsonTheme$background$yGrid$linetype) + ) + )), + tabPanel( + "Legend", + # fluidRow( + # h4("Title"), + # textInput("legendTitleText", label = h4("text"), value = "TO DO"), + # textInput("legendTitleColor", label = h4("color"), value = jsonTheme$fonts$legendTitle$color), + # sliderInput("legendTitleSize", label = h4("size"), min = 1, max = 30, value = jsonTheme$fonts$legendTitle$size, step = 0.5), + # sliderInput("legendTitleAngle", label = h4("angle"), min = -180, max = 180, value = jsonTheme$fonts$legendTitle$angle, step = 5) + # ), + tabsetPanel( + tabPanel("Position", + selectInput("legendPosition", label = h4("position"), choices = LegendPositions, selected = jsonTheme$background$legendPosition) + ), + tabPanel("Font", + textInput("legendFontColor", label = h4("color"), value = jsonTheme$fonts$legend$color), + sliderInput("legendFontSize", label = h4("size"), min = 1, max = 30, value = jsonTheme$fonts$legend$size, step = 0.5), + sliderInput("legendFontAngle", label = h4("angle"), min = -180, max = 180, value = jsonTheme$fonts$legend$angle, step = 5) + ), + tabPanel("Background", + textInput("legendFill", label = h4("fill"), value = jsonTheme$background$legend$fill), + textInput("legendColor", label = h4("color"), value = jsonTheme$background$legend$color), + sliderInput("legendSize", label = h4("size"), min = 0, max = 5, value = jsonTheme$background$legend$size, step = 0.05), + selectInput("legendLinetype", label = h4("linetype"), choices = Linetypes, selected = jsonTheme$background$legend$linetype) + ) + )), + tabPanel( + "Aesthetic Maps", + tabsetPanel( + tabPanel("Color", + sliderInput("colorMapIndex", label = h4("rank"), min = 1, max = length(jsonTheme$aestheticMaps$color), value = 1, step = 1), + uiOutput("colorMapValue")), + tabPanel("Fill", + sliderInput("fillMapIndex", label = h4("rank"), min = 1, max = length(jsonTheme$aestheticMaps$fill), value = 1, step = 1), + uiOutput("fillMapValue")), + tabPanel("Linetype", + sliderInput("linetypeMapIndex", label = h4("rank"), min = 1, max = length(jsonTheme$aestheticMaps$linetype), value = 1, step = 1), + uiOutput("linetypeMapValue")), + tabPanel("Shape", + sliderInput("shapeMapIndex", label = h4("rank"), min = 1, max = length(jsonTheme$aestheticMaps$shape), value = 1, step = 1), + uiOutput("shapeMapValue")), + tabPanel("Size", + sliderInput("sizeMapIndex", label = h4("rank"), min = 1, max = length(jsonTheme$aestheticMaps$size), value = 1, step = 1), + uiOutput("sizeMapValue")), + tabPanel("Alpha", + sliderInput("alphaMapIndex", label = h4("rank"), min = 1, max = length(jsonTheme$aestheticMaps$alpha), value = 1, step = 1), + uiOutput("alphaMapValue")) + ) + ), + tabPanel( + "Plot Configurations", + p("The following arguments define how the plot will use the aesthetic map"), + p("Selection key 'next': use next aesthetic value from map"), + p("Selection key 'same': use same aesthetic value from map"), + p("Selection key 'first': use first aesthetic value from map"), + p("Selection key 'reset': use next aesthetic value from map, but reset every time the function is called"), + p("No selection key, the specific value is used every time (e.g. 'blank')"), + conditionalPanel( + condition = "input.plotType == 'addScatter()'", + textInput("addScatterColor", label = h4("color"), value = jsonTheme$plotConfigurations$addScatter$color), + textInput("addScatterLinetype", label = h4("linetype"), value = jsonTheme$plotConfigurations$addScatter$linetype), + textInput("addScatterShape", label = h4("shape"), value = jsonTheme$plotConfigurations$addScatter$shape), + textInput("addScatterSize", label = h4("size"), value = jsonTheme$plotConfigurations$addScatter$size), + textInput("addScatterFill", label = h4("fill"), value = jsonTheme$plotConfigurations$addScatter$fill), + textInput("addScatterAlpha", label = h4("alpha"), value = jsonTheme$plotConfigurations$addScatter$alpha) + ), + conditionalPanel( + condition = "input.plotType == 'addLine()'", + textInput("addLineColor", label = h4("color"), value = jsonTheme$plotConfigurations$addLine$color), + textInput("addLineLinetype", label = h4("linetype"), value = jsonTheme$plotConfigurations$addLine$linetype), + textInput("addLineShape", label = h4("shape"), value = jsonTheme$plotConfigurations$addLine$shape), + textInput("addLineSize", label = h4("size"), value = jsonTheme$plotConfigurations$addLine$size), + textInput("addLineFill", label = h4("fill"), value = jsonTheme$plotConfigurations$addLine$fill), + textInput("addLineAlpha", label = h4("alpha"), value = jsonTheme$plotConfigurations$addLine$alpha) + ), + conditionalPanel( + condition = "input.plotType == 'addRibbon()'", + textInput("addRibbonColor", label = h4("color"), value = jsonTheme$plotConfigurations$addRibbon$color), + textInput("addRibbonLinetype", label = h4("linetype"), value = jsonTheme$plotConfigurations$addRibbon$linetype), + textInput("addRibbonShape", label = h4("shape"), value = jsonTheme$plotConfigurations$addRibbon$shape), + textInput("addRibbonSize", label = h4("size"), value = jsonTheme$plotConfigurations$addRibbon$size), + textInput("addRibbonFill", label = h4("fill"), value = jsonTheme$plotConfigurations$addRibbon$fill), + textInput("addRibbonAlpha", label = h4("alpha"), value = jsonTheme$plotConfigurations$addRibbon$alpha) + ), + conditionalPanel( + condition = "input.plotType == 'addErrorbar()'", + textInput("addErrorbarColor", label = h4("color"), value = jsonTheme$plotConfigurations$addErrorbar$color), + textInput("addErrorbarLinetype", label = h4("linetype"), value = jsonTheme$plotConfigurations$addErrorbar$linetype), + textInput("addErrorbarShape", label = h4("shape"), value = jsonTheme$plotConfigurations$addErrorbar$shape), + textInput("addErrorbarSize", label = h4("size"), value = jsonTheme$plotConfigurations$addErrorbar$size), + textInput("addErrorbarFill", label = h4("fill"), value = jsonTheme$plotConfigurations$addErrorbar$fill), + textInput("addErrorbarAlpha", label = h4("alpha"), value = jsonTheme$plotConfigurations$addErrorbar$alpha) + ), + conditionalPanel( + condition = "input.plotType == 'plotPKRatio()'", + h4("PK Ratio Line of unity"), + textInput("pkRatioLine1.color", label = h4("color"), value = jsonTheme$defaultCaption$pkRatio$color[1]), + selectInput("pkRatioLine1.linetype", label = h4("linetype"), choices = Linetypes, selected = jsonTheme$defaultCaption$pkRatio$linetype[1]), + sliderInput("pkRatioLine1.size", label = h4("size"), min = 0.05, max = 5, value = jsonTheme$defaultCaption$pkRatio$size[1], step = 0.05), + h4("PK Ratio Lines of 1.5 fold error"), + textInput("pkRatioLine2.color", label = h4("color"), value = jsonTheme$defaultCaption$pkRatio$color[2]), + selectInput("pkRatioLine2.linetype", label = h4("linetype"), choices = Linetypes, selected = jsonTheme$defaultCaption$pkRatio$linetype[2]), + sliderInput("pkRatioLine2.size", label = h4("size"), min = 0.05, max = 5, value = jsonTheme$defaultCaption$pkRatio$size[2], step = 0.05), + h4("PK Ratio Lines of 2 fold error"), + textInput("pkRatioLine3.color", label = h4("color"), value = jsonTheme$defaultCaption$pkRatio$color[3]), + selectInput("pkRatioLine3.linetype", label = h4("linetype"), choices = Linetypes, selected = jsonTheme$defaultCaption$pkRatio$linetype[3]), + sliderInput("pkRatioLine3.size", label = h4("size"), min = 0.05, max = 5, value = jsonTheme$defaultCaption$pkRatio$size[3], step = 0.05) + ), + conditionalPanel( + condition = "input.plotType == 'plotTornado()'", + textInput("colorPalette", label = h4("Color Palette"), value = NULL) + ) + ) + ) + ), + + # Plot Column + column( + 7, + h2("Sample Plot", align = "center"), + + plotOutput(outputId = "samplePlot"), + + selectInput( + inputId = "plotType", label = h3("Available plot functions"), + choices = as.list(sapply( + c( + "initializePlot()", + "addScatter()", + "addLine()", + "addRibbon()", + "addErrorbar()", + "plotPKRatio()", + "plotDDIRatio()", + "plotBoxWhisker()", + "plotHistogram()", + "plotTimeProfile()", + "plotTornado()", + "plotObsVsPred()" + ), + identity + )), + selected = 1 + ), + + fileInput("loadTheme", "Load a Theme from Json", accept = ".json"), + textInput("jsonFileName", label = "File name", value = "new-theme.json"), + actionButton("saveTheme", "Save Theme to Json") + ) +) + +# Define server logic required to draw a histogram ---- +server <- function(input, output) { + + output$samplePlot <- renderPlot({ + # Create copy of json template + updatedThemeProperties <- jsonTheme + # Update every template feature + jsonTheme$fonts$title$color <- input$titleColor + jsonTheme$fonts$subtitle$color <- input$subtitleColor + jsonTheme$fonts$xlabel$color <- input$xlabelColor + jsonTheme$fonts$ylabel$color <- input$ylabelColor + + jsonTheme$fonts$title$size <- input$titleSize + jsonTheme$fonts$subtitle$size <- input$subtitleSize + jsonTheme$fonts$xlabel$size <- input$xlabelSize + jsonTheme$fonts$ylabel$size <- input$ylabelSize + + jsonTheme$fonts$title$angle <- input$titleAngle + jsonTheme$fonts$subtitle$angle <- input$subtitleAngle + jsonTheme$fonts$xlabel$angle <- input$xlabelAngle + jsonTheme$fonts$ylabel$angle <- input$ylabelAngle + + jsonTheme$background$watermark <- input$watermarkText + jsonTheme$fonts$watermark$color <- input$watermarkColor + jsonTheme$fonts$watermark$size <- input$watermarkSize + jsonTheme$fonts$watermark$angle <- input$watermarkAngle + + jsonTheme$background$plot$fill <- input$plotFill + jsonTheme$background$plot$color <- input$plotColor + jsonTheme$background$plot$size <- input$plotSize + jsonTheme$background$plot$linetype <- input$plotLinetype + + jsonTheme$background$panel$fill <- input$panelFill + jsonTheme$background$panel$color <- input$panelColor + jsonTheme$background$panel$size <- input$panelSize + jsonTheme$background$panel$linetype <- input$panelLinetype + + jsonTheme$background$xAxis$color <- input$xAxisColor + jsonTheme$background$xAxis$size <- input$xAxisSize + jsonTheme$background$xAxis$linetype <- input$xAxisLinetype + jsonTheme$fonts$xAxis$color <- input$xAxisTicksColor + jsonTheme$fonts$xAxis$size <- input$xAxisTicksSize + jsonTheme$fonts$xAxis$angle <- input$xAxisTicksAngle + + jsonTheme$background$yAxis$color <- input$yAxisColor + jsonTheme$background$yAxis$size <- input$yAxisSize + jsonTheme$background$yAxis$linetype <- input$yAxisLinetype + jsonTheme$fonts$yAxis$color <- input$yAxisTicksColor + jsonTheme$fonts$yAxis$size <- input$yAxisTicksSize + jsonTheme$fonts$yAxis$angle <- input$yAxisTicksAngle + + jsonTheme$background$xGrid$color <- input$xGridColor + jsonTheme$background$xGrid$size <- input$xGridSize + jsonTheme$background$xGrid$linetype <- input$xGridLinetype + + jsonTheme$background$yGrid$color <- input$yGridColor + jsonTheme$background$yGrid$size <- input$yGridSize + jsonTheme$background$yGrid$linetype <- input$yGridLinetype + + jsonTheme$background$legendPosition <- input$legendPosition + + jsonTheme$background$legend$fill <- input$legendFill + jsonTheme$background$legend$color <- input$legendColor + jsonTheme$background$legend$size <- input$legendSize + jsonTheme$background$legend$linetype <- input$legendLinetype + + jsonTheme$fonts$legend$color <- input$legendFontColor + jsonTheme$fonts$legend$size <- input$legendFontSize + jsonTheme$fonts$legend$angle <- input$legendFontAngle + + # jsonTheme$fonts$legendTitle$color <- input$legendTitleColor + # jsonTheme$fonts$legendTitle$size <- input$legendTitleSize + # jsonTheme$fonts$legendTitle$angle <- input$legendTitleAngle + + # Color maps + if(!isOfLength(input$colorMapValue2,0)){jsonTheme$aestheticMaps$color[input$colorMapIndex] <- input$colorMapValue2} + if(!isOfLength(input$fillMapValue2,0)){jsonTheme$aestheticMaps$fill[input$fillMapIndex] <- input$fillMapValue2} + if(!isOfLength(input$linetypeMapValue2,0)){jsonTheme$aestheticMaps$linetype[input$linetypeMapIndex] <- input$linetypeMapValue2} + if(!isOfLength(input$shapeMapValue2,0)){jsonTheme$aestheticMaps$shape[input$shapeMapIndex] <- as.numeric(input$shapeMapValue2)} + if(!isOfLength(input$sizeMapValue2,0)){jsonTheme$aestheticMaps$size[input$sizeMapIndex] <- input$sizeMapValue2} + if(!isOfLength(input$alphaMapValue2,0)){jsonTheme$aestheticMaps$alpha[input$alphaMapIndex] <- input$alphaMapValue2} + + # initializePlot() from Theme + useTheme(jsonTheme) + plotConfiguration <- PlotConfiguration$new(title = "Title", subtitle = "Subtitle", xlabel = "X-label", ylabel = "Y-label") + samplePlot <- initializePlot(plotConfiguration = plotConfiguration) + + # addScatter() from Theme + if (input$plotType %in% "addScatter()") { + jsonTheme$plotConfigurations$addScatter$color <- input$addScatterColor + jsonTheme$plotConfigurations$addScatter$linetype <- input$addScatterLinetype + jsonTheme$plotConfigurations$addScatter$shape <- input$addScatterShape + jsonTheme$plotConfigurations$addScatter$size <- input$addScatterSize + jsonTheme$plotConfigurations$addScatter$fill <- input$addScatterFill + jsonTheme$plotConfigurations$addScatter$alpha <- input$addScatterAlpha + useTheme(jsonTheme) + plotConfiguration <- PlotConfiguration$new(title = "Title", subtitle = "Subtitle", xlabel = "X-label", ylabel = "Y-label") + + sampleData <- data.frame( + x = seq(-3, 3, 0.1), + y = cos(2 * seq(-3, 3, 0.1)) + ) + samplePlot <- addScatter(data = sampleData, plotConfiguration = plotConfiguration) + for (sampleIndex in 1:4) { + sampleData$y <- 1.2 * sampleData$y + samplePlot <- addScatter(data = sampleData, plotObject = samplePlot) + } + } + if (input$plotType %in% "addLine()") { + jsonTheme$plotConfigurations$addLine$color <- input$addLineColor + jsonTheme$plotConfigurations$addLine$linetype <- input$addLineLinetype + jsonTheme$plotConfigurations$addLine$shape <- input$addLineShape + jsonTheme$plotConfigurations$addLine$size <- input$addLineSize + jsonTheme$plotConfigurations$addLine$fill <- input$addLineFill + jsonTheme$plotConfigurations$addLine$alpha <- input$addLineAlpha + useTheme(jsonTheme) + plotConfiguration <- PlotConfiguration$new(title = "Title", subtitle = "Subtitle", xlabel = "X-label", ylabel = "Y-label") + + sampleData <- data.frame( + x = seq(-3, 3, 0.1), + y = cos(2 * seq(-3, 3, 0.1)) + ) + samplePlot <- addLine(data = sampleData, plotConfiguration = plotConfiguration) + for (sampleIndex in 1:4) { + sampleData$y <- 1.2 * sampleData$y + samplePlot <- addLine(data = sampleData, plotObject = samplePlot) + } + } + if (input$plotType %in% "addRibbon()") { + jsonTheme$plotConfigurations$addRibbon$color <- input$addRibbonColor + jsonTheme$plotConfigurations$addRibbon$linetype <- input$addRibbonLinetype + jsonTheme$plotConfigurations$addRibbon$shape <- input$addRibbonShape + jsonTheme$plotConfigurations$addRibbon$size <- input$addRibbonSize + jsonTheme$plotConfigurations$addRibbon$fill <- input$addRibbonFill + jsonTheme$plotConfigurations$addRibbon$alpha <- input$addRibbonAlpha + useTheme(jsonTheme) + plotConfiguration <- PlotConfiguration$new(title = "Title", subtitle = "Subtitle", xlabel = "X-label", ylabel = "Y-label") + + sampleData <- data.frame( + x = seq(-3, 3, 0.1), + ymax = cos(2 * seq(-3, 3, 0.1)), + ymin = 0 + ) + samplePlot <- addRibbon(data = sampleData, plotConfiguration = plotConfiguration) + for (sampleIndex in 1:4) { + sampleData$ymin <- sampleData$ymax + sampleData$ymax <- 1.2 * sampleData$ymax + samplePlot <- addRibbon(data = sampleData, plotObject = samplePlot) + } + } + if (input$plotType %in% "addErrorbar()") { + jsonTheme$plotConfigurations$addErrorbar$color <- input$addErrorbarColor + jsonTheme$plotConfigurations$addErrorbar$linetype <- input$addErrorbarLinetype + jsonTheme$plotConfigurations$addErrorbar$shape <- input$addErrorbarShape + jsonTheme$plotConfigurations$addErrorbar$size <- input$addErrorbarSize + jsonTheme$plotConfigurations$addErrorbar$fill <- input$addErrorbarFill + jsonTheme$plotConfigurations$addErrorbar$alpha <- input$addErrorbarAlpha + useTheme(jsonTheme) + + plotConfiguration <- PlotConfiguration$new(title = "Title", subtitle = "Subtitle", xlabel = "X-label", ylabel = "Y-label") + + sampleData <- data.frame( + x = seq(-3, 3, 0.1), + ymax = cos(2 * seq(-3, 3, 0.1)), + ymin = 0 + ) + samplePlot <- addErrorbar(data = sampleData, plotConfiguration = plotConfiguration) + } + if (input$plotType %in% "plotPKRatio()") { + pkRatioCaption <- data.frame( + name = c("pkRatioLine1", "pkRatioLine2", "pkRatioLine3"), + label = c("pkRatioLine1", "pkRatioLine2", "pkRatioLine3"), + visibility = rep(FALSE, 3), order = c(1, 2, 3), + color = c(input$pkRatioLine1.color, input$pkRatioLine2.color, input$pkRatioLine3.color), + shape = rep(-2, 3), + size = c(input$pkRatioLine1.size, input$pkRatioLine2.size, input$pkRatioLine3.size), + linetype = c(input$pkRatioLine1.linetype, input$pkRatioLine2.linetype, input$pkRatioLine3.linetype), + fill = NA, + stringsAsFactors = FALSE + ) + + plotConfiguration <- PKRatioPlotConfiguration$new( + pkRatioCaption = pkRatioCaption, + title = "Title", + subtitle = "Subtitle", + xlabel = Label$new("X-label", color = input$xlabelColor, size = input$xlabelSize), + ylabel = Label$new("Y-label", color = input$ylabelColor, size = input$ylabelSize), + theme = updatedTheme + ) + samplePlot <- plotPKRatio( + data = sampleData, + plotConfiguration = plotConfiguration + ) + } + if (input$plotType %in% "plotDDIRatio()") { + plotConfiguration <- DDIRatioPlotConfiguration$new( + title = "Title", + subtitle = "Subtitle", + xlabel = Label$new("X-label", color = input$xlabelColor, size = input$xlabelSize), + ylabel = Label$new("Y-label", color = input$ylabelColor, size = input$ylabelSize), + theme = updatedTheme + ) + samplePlot <- plotDDIRatio(data = sampleData, plotConfiguration = plotConfiguration) + } + if (input$plotType %in% "plotBoxWhisker()") { + # plotConfiguration <- BoxWhiskerPlotConfiguration$new( + # samplePlot <- plotBoxWhisker(data = sampleData, plotConfiguration = plotConfiguration) + } + if (input$plotType %in% "plotHistogram()") { + # plotConfiguration <- HistogramPlotConfiguration$new( + # samplePlot <- plotHistogram(data = sampleData, plotConfiguration = plotConfiguration) + } + if (input$plotType %in% "plotTimeProfile()") { + # plotConfiguration <- TimeProfilePlotConfiguration$new( + # samplePlot <- plotTimeProfile(data = sampleData, plotConfiguration = plotConfiguration) + } + if (input$plotType %in% "plotObsVsPred()") { + # plotConfiguration <- ObsVsPredPlotConfiguration$new( + # samplePlot <- plotBoxWhisker(data = sampleData, plotConfiguration = plotConfiguration) + } + + if (input$plotType %in% "plotTornado()") { + plotConfiguration <- TornadoPlotConfiguration$new( + title = "Title", + subtitle = "Subtitle", + xlabel = Label$new("X-label", color = input$xlabelColor, size = input$xlabelSize), + ylabel = Label$new("Y-label", color = input$ylabelColor, size = input$ylabelSize), + theme = updatedTheme, + colorPalette = input$colorPalette + ) + samplePlot <- plotTornado( + x = c(1, 0.5, -0.25, -0.2, -0.1), + y = c("A", "B", "C", "D", "E"), + plotConfiguration = plotConfiguration + ) + } + samplePlot + }) + + # Interactive way of updating aesthetic map element + getColorMapValue <- reactive({jsonTheme$aestheticMaps$color[input$colorMapIndex]}) + getFillMapValue <- reactive({jsonTheme$aestheticMaps$fill[input$fillMapIndex]}) + getLinetypeMapValue <- reactive({jsonTheme$aestheticMaps$linetype[input$linetypeMapIndex]}) + getShapeMapValue <- reactive({jsonTheme$aestheticMaps$shape[input$shapeMapIndex]}) + getSizeMapValue <- reactive({jsonTheme$aestheticMaps$size[input$sizeMapIndex]}) + getAlphaMapValue <- reactive({jsonTheme$aestheticMaps$alpha[input$alphaMapIndex]}) + + output$colorMapValue <- renderUI({textInput("colorMapValue2", "value", getColorMapValue())}) + output$fillMapValue <- renderUI({textInput("fillMapValue2", "value", getFillMapValue())}) + output$linetypeMapValue <- renderUI({ + selectInput("linetypeMapValue2", "value", choices = Linetypes, selected = getLinetypeMapValue())}) + output$shapeMapValue <- renderUI({ + selectInput("shapeMapValue2", "value", choices = Shapes, selected = getShapeMapValue())}) + output$sizeMapValue <- renderUI({numericInput("sizeMapValue2", "value", getSizeMapValue())}) + output$alphaMapValue <- renderUI({numericInput("alphaMapValue2", "value", getAlphaMapValue())}) + + # Load/save Theme + observeEvent(input$loadTheme, { + jsonTheme <- loadThemeFromJson(input$loadTheme$datapath) + useTheme(jsonTheme) + }) + observeEvent(input$saveTheme, {saveThemeToJson(jsonFile = input$jsonFileName, theme = jsonTheme)}) +} + +shinyApp(ui = ui, server = server) diff --git a/inst/tornado/app.R b/inst/tornado/app.R new file mode 100644 index 00000000..4244d221 --- /dev/null +++ b/inst/tornado/app.R @@ -0,0 +1,166 @@ +require(shiny) +require(tlf) + +# Use theme template but this could be modified in later versions +useTheme(loadThemeFromJson(system.file("extdata", "template-theme.json", package = "tlf"))) + +# Define UI page ---- +ui <- fluidPage( + h1("Tornado Plot", align = "center"), + column( + 5, + tabsetPanel( + tabPanel( + "Data", + fluidRow( + selectInput("dataType", + label = h4("Data location"), + choices = list("environment" = "environment", "file" = "file") + ), + conditionalPanel( + condition = "input.dataType == 'environment'", + selectInput("dataFromEnv", label = h4("List of environment variables"), choices = sapply(ls(envir = sys.frame()), identity)) + ), + conditionalPanel( + condition = "input.dataType == 'file'", + fileInput("dataFromFile", label = h4("Choose a csv file"), accept = ".csv") + ), + uiOutput("xVariableNames"), + uiOutput("yVariableNames"), + uiOutput("colorVariableNames"), + conditionalPanel( + condition = "input.bar == 'No'", + uiOutput("shapeVariableNames") + ) + ) + ), + tabPanel( + "Analysis", + fluidRow( + selectInput("bar", label = "Tornado as bar plot", choices = list(Yes = "Yes", No = "No")), + selectInput("sort", label = "Sort values", choices = list(Yes = "Yes", No = "No")), + selectInput("colorPalette", label = "Color palette", choices = list(none = "none", + Spectral = "Spectral", + Greys = "Greys", + Blues = "Blues", + RedToBlue = "RdBu", + RedToGreen = "RdYlGn")) + ) + ), + tabPanel( + "Axes", + fluidRow( + h4("x axis"), + numericInput("xAxisMin", label = "min", value = NULL), + numericInput("xAxisMax", label = "max", value = NULL), + selectInput("xAxisScale", label = "scale", choices = Scaling, selected = Scaling$lin) + ) + ), + tabPanel( + "Labels", + fluidRow( + textInput("xLabel", label = "x label", value = NULL), + textInput("yLabel", label = "y label", value = NULL), + textInput("watermark", label = "watermark", value = NULL) + ) + ) + ) + ), + + # Plot Column + column( + 7, + br(), + plotOutput(outputId = "tornadoPlot"), + br(), + actionButton("exportButton", "Save Plot"), + align = "center" + ) +) + +# Define server logic required to draw a histogram ---- +server <- function(input, output) { + + getTornadoData <- reactive({ + tornadoData <- data.frame(`Fill data first` = NULL) + if (input$dataType == "environment") { + if (input$dataFromEnv != "") { + tornadoData <- get(input$dataFromEnv, envir = sys.frame()) + } + } + if (input$dataType == "file") { + if (!isOfLength(input$dataFromFile, 0)) { + tornadoData <- read.csv(input$dataFromFile$datapath, stringsAsFactors = FALSE) + } + } + return(tornadoData) + }) + + getVariableNames <- reactive({ + tornadoData <- getTornadoData() + return(sapply(names(tornadoData), identity)) + }) + + output$yVariableNames <- renderUI({ + selectInput("yVariableNames2", "Variable in Y", getVariableNames()) + }) + + output$xVariableNames <- renderUI({ + selectInput("xVariableNames2", "Variable in X", getVariableNames()) + }) + + output$shapeVariableNames <- renderUI({ + selectInput("shapeVariableNames2", "Shape group variable", c("none" = "none", getVariableNames())) + }) + + output$colorVariableNames <- renderUI({ + selectInput("colorVariableNames2", "Color group variable", c("none" = "none", getVariableNames())) + }) + + getDataMapping <- reactive({ + colorMapping <- input$colorVariableNames2 + shapeMapping <- input$shapeVariableNames2 + if(isIncluded(colorMapping, "none")){colorMapping <- NULL} + if(isIncluded(shapeMapping, "none")){shapeMapping <- NULL} + dataMapping <- TornadoDataMapping$new(x = input$xVariableNames2, + y = input$yVariableNames2, + color = colorMapping, + fill = colorMapping, + shape = shapeMapping) + dataMapping$sorted <- (input$sort == 'Yes') + return(dataMapping) + }) + + output$tornadoPlot <- renderPlot({ + # Get the data + tornadoData <- getTornadoData() + if(!isOfType(tornadoData, "data.frame")){return()} + if(nrow(tornadoData)==0){return()} + # Meta Data + # DataMapping + dataMapping <- getDataMapping() + # PlotConfiguration + colorPalette <- input$colorPalette + if(colorPalette %in% "none"){colorPalette <- NULL} + tornadoPlot <- plotTornado( + data = tornadoData, + dataMapping = dataMapping, + bar = (input$bar == "Yes"), + colorPalette = colorPalette + ) + xLimits <- c(input$xAxisMin, input$xAxisMax) + if(any(is.na(xLimits))){xLimits <- NULL} + + tornadoPlot <- setXAxis(tornadoPlot, + limits = xLimits, + scale = input$xAxisScale + ) + + if(input$xLabel != ""){tornadoPlot <- setPlotLabels(tornadoPlot, xlabel = input$xLabel)} + if(input$yLabel != ""){tornadoPlot <- setPlotLabels(tornadoPlot, ylabel = input$yLabel)} + tornadoPlot <- setWatermark(tornadoPlot, watermark = input$watermark) + tornadoPlot + }) +} + +shinyApp(ui = ui, server = server) diff --git a/man/AestheticProperties.Rd b/man/AestheticProperties.Rd new file mode 100644 index 00000000..70a578d5 --- /dev/null +++ b/man/AestheticProperties.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utilities-aesthetics.R +\docType{data} +\name{AestheticProperties} +\alias{AestheticProperties} +\title{AestheticProperties} +\format{ +An object of class \code{list} of length 6. +} +\usage{ +AestheticProperties +} +\description{ +Enum of aesthetic property names of \code{ggplot2} +} +\keyword{datasets} diff --git a/man/AestheticSelectionKeys.Rd b/man/AestheticSelectionKeys.Rd new file mode 100644 index 00000000..ccf5f21f --- /dev/null +++ b/man/AestheticSelectionKeys.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utilities-aesthetics.R +\docType{data} +\name{AestheticSelectionKeys} +\alias{AestheticSelectionKeys} +\title{AestheticSelectionKeys} +\format{ +An object of class \code{list} of length 4. +} +\usage{ +AestheticSelectionKeys +} +\description{ +List of some \code{ggplot2} shapes +} +\keyword{datasets} diff --git a/man/AxisConfiguration.Rd b/man/AxisConfiguration.Rd index 36eda93d..bef781d2 100644 --- a/man/AxisConfiguration.Rd +++ b/man/AxisConfiguration.Rd @@ -6,16 +6,20 @@ \description{ R6 class defining the configuration of axis } -\section{Public fields}{ -\if{html}{\out{