Spaces:
Running
Running
Update app.R
Browse files
app.R
CHANGED
|
@@ -3011,13 +3011,67 @@ create_location_by_side_plot <- function(data, player_name, batter_side, pitch_c
|
|
| 3011 |
create_location_by_result_plot(data, player_name, batter_side, pitch_colors)
|
| 3012 |
}
|
| 3013 |
|
| 3014 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3015 |
if (length(dev.list()) > 0) try(dev.off(), silent = TRUE)
|
| 3016 |
|
| 3017 |
pitch_colors <- c(
|
| 3018 |
-
"Fastball" = "#FA8072", "FourSeamFastBall" =
|
| 3019 |
-
"
|
| 3020 |
-
"
|
|
|
|
| 3021 |
)
|
| 3022 |
|
| 3023 |
.text_on_fill <- function(hex) {
|
|
@@ -3029,7 +3083,6 @@ create_advanced_pitcher_pdf <- function(game_df, pitcher_name, output_file) {
|
|
| 3029 |
}, error = function(e) "black")
|
| 3030 |
}
|
| 3031 |
|
| 3032 |
-
# Safe cell accessor - handles missing columns gracefully
|
| 3033 |
get_cell_value <- function(df, colname, row_idx) {
|
| 3034 |
if (is.null(df) || !is.data.frame(df)) return(NA)
|
| 3035 |
if (is.null(colname) || !nzchar(colname)) return(NA)
|
|
@@ -3048,9 +3101,24 @@ create_advanced_pitcher_pdf <- function(game_df, pitcher_name, output_file) {
|
|
| 3048 |
return(output_file)
|
| 3049 |
}
|
| 3050 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3051 |
game_day <- tryCatch(parse_game_day(pitcher_df), error = function(e) Sys.Date())
|
| 3052 |
|
| 3053 |
-
# Get summary stats
|
| 3054 |
summary_result <- tryCatch(
|
| 3055 |
create_advanced_pitcher_summary(pitcher_df, pitcher_name),
|
| 3056 |
error = function(e) {
|
|
@@ -3062,7 +3130,7 @@ create_advanced_pitcher_pdf <- function(game_df, pitcher_name, output_file) {
|
|
| 3062 |
summary_stats <- summary_result$stats
|
| 3063 |
summary_colors <- summary_result$colors
|
| 3064 |
|
| 3065 |
-
# Get pitch characteristics
|
| 3066 |
pitch_result <- tryCatch(
|
| 3067 |
create_advanced_pitch_characteristics(pitcher_df, pitcher_name),
|
| 3068 |
error = function(e) {
|
|
@@ -3076,7 +3144,7 @@ create_advanced_pitcher_pdf <- function(game_df, pitcher_name, output_file) {
|
|
| 3076 |
pitch_char <- pitch_result$stats
|
| 3077 |
pitch_colors_matrix <- pitch_result$colors
|
| 3078 |
|
| 3079 |
-
# Handle empty
|
| 3080 |
if (is.null(pitch_char) || nrow(pitch_char) == 0) {
|
| 3081 |
pitch_char <- data.frame(
|
| 3082 |
Pitch = "-", Count = 0, `Usage%` = NA_real_,
|
|
@@ -3093,17 +3161,16 @@ create_advanced_pitcher_pdf <- function(game_df, pitcher_name, output_file) {
|
|
| 3093 |
pitch_colors_matrix <- matrix("#FFFFFF", nrow = 1, ncol = ncol(pitch_char))
|
| 3094 |
}
|
| 3095 |
|
| 3096 |
-
# Limit rows
|
| 3097 |
max_rows_to_show <- min(nrow(pitch_char), 9)
|
| 3098 |
if (nrow(pitch_char) > max_rows_to_show) {
|
| 3099 |
pitch_char <- pitch_char[1:max_rows_to_show, , drop = FALSE]
|
| 3100 |
}
|
| 3101 |
|
| 3102 |
-
# Get final dimensions
|
| 3103 |
num_rows <- nrow(pitch_char)
|
| 3104 |
num_cols <- ncol(pitch_char)
|
| 3105 |
|
| 3106 |
-
#
|
| 3107 |
new_color_matrix <- matrix("#FFFFFF", nrow = num_rows, ncol = num_cols)
|
| 3108 |
if (!is.null(pitch_colors_matrix) && is.matrix(pitch_colors_matrix)) {
|
| 3109 |
rows_to_copy <- min(nrow(pitch_colors_matrix), num_rows)
|
|
@@ -3115,7 +3182,7 @@ create_advanced_pitcher_pdf <- function(game_df, pitcher_name, output_file) {
|
|
| 3115 |
}
|
| 3116 |
pitch_colors_matrix <- new_color_matrix
|
| 3117 |
|
| 3118 |
-
# Create plots
|
| 3119 |
movement_plot <- tryCatch(
|
| 3120 |
create_movement_plot(pitcher_df, pitcher_name, pitch_colors),
|
| 3121 |
error = function(e) ggplot2::ggplot() + ggplot2::theme_void() + ggplot2::ggtitle("Movement Plot Error")
|
|
@@ -3136,11 +3203,8 @@ create_advanced_pitcher_pdf <- function(game_df, pitcher_name, output_file) {
|
|
| 3136 |
create_count_usage_plot(pitcher_df, pitcher_name, pitch_colors),
|
| 3137 |
error = function(e) ggplot2::ggplot() + ggplot2::theme_void() + ggplot2::ggtitle("Count Usage Error")
|
| 3138 |
)
|
| 3139 |
-
extension_height_plot <- tryCatch(
|
| 3140 |
-
create_extension_height_plot(pitcher_df, pitcher_name, pitch_colors),
|
| 3141 |
-
error = function(e) ggplot2::ggplot() + ggplot2::theme_void() + ggplot2::ggtitle("Extension/Height Error")
|
| 3142 |
-
)
|
| 3143 |
|
|
|
|
| 3144 |
relside_height_plot <- tryCatch(
|
| 3145 |
create_relside_height_plot(pitcher_df, pitcher_name, pitch_colors),
|
| 3146 |
error = function(e) ggplot2::ggplot() + ggplot2::theme_void() + ggplot2::ggtitle("RelSide/Height Error")
|
|
@@ -3175,14 +3239,28 @@ create_advanced_pitcher_pdf <- function(game_df, pitcher_name, output_file) {
|
|
| 3175 |
available_for_table <- y_top_char - (base_loc_top + table_margin)
|
| 3176 |
row_h_char <- min(max_row_h, max(min_row_h, available_for_table / max(1, rows_including_header)))
|
| 3177 |
|
| 3178 |
-
#
|
| 3179 |
-
grid::pushViewport(grid::viewport(x = 0.5, y = header_y_top, width = 1, height = 0.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3180 |
grid::grid.text(paste(pitcher_name, "- Advanced Pitcher Report"),
|
|
|
|
| 3181 |
gp = grid::gpar(fontface = "bold", cex = 1.8, col = "#006F71"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3182 |
grid::popViewport()
|
| 3183 |
|
| 3184 |
# Summary section
|
| 3185 |
-
grid::grid.text("Summary", x = 0.5, y = 0.
|
| 3186 |
gp = grid::gpar(fontface = "bold", cex = 1.1, col = "#006F71"))
|
| 3187 |
|
| 3188 |
summary_headers <- names(summary_stats)
|
|
@@ -3191,7 +3269,7 @@ create_advanced_pitcher_pdf <- function(game_df, pitcher_name, output_file) {
|
|
| 3191 |
|
| 3192 |
x_start <- 0.5 - sum(summary_widths)/2
|
| 3193 |
x_pos <- c(x_start, x_start + cumsum(summary_widths[-length(summary_widths)]))
|
| 3194 |
-
y_top <- 0.
|
| 3195 |
row_h <- 0.020
|
| 3196 |
|
| 3197 |
for (i in seq_along(summary_headers)) {
|
|
@@ -3209,6 +3287,7 @@ create_advanced_pitcher_pdf <- function(game_df, pitcher_name, output_file) {
|
|
| 3209 |
gp = grid::gpar(cex = 0.62))
|
| 3210 |
}
|
| 3211 |
|
|
|
|
| 3212 |
grid::pushViewport(grid::viewport(x = 0.25, y = charts_y_top, width = 0.45, height = charts_height, just = c("center","top")))
|
| 3213 |
tryCatch(print(movement_plot, newpage = FALSE), error = function(e) NULL)
|
| 3214 |
grid::popViewport()
|
|
@@ -3217,16 +3296,16 @@ create_advanced_pitcher_pdf <- function(game_df, pitcher_name, output_file) {
|
|
| 3217 |
tryCatch(print(velo_plot, newpage = FALSE), error = function(e) NULL)
|
| 3218 |
grid::popViewport()
|
| 3219 |
|
| 3220 |
-
# Row 2: Count plot (left) | Release
|
| 3221 |
-
# Count plot
|
| 3222 |
-
grid::pushViewport(grid::viewport(x = 0.27, y = count_y_top, width = 0.50, height =
|
| 3223 |
tryCatch(print(count_plot, newpage = FALSE), error = function(e) NULL)
|
| 3224 |
grid::popViewport()
|
| 3225 |
|
| 3226 |
-
|
|
|
|
| 3227 |
tryCatch(print(relside_height_plot, newpage = FALSE), error = function(e) NULL)
|
| 3228 |
grid::popViewport()
|
| 3229 |
-
|
| 3230 |
|
| 3231 |
# Pitch Characteristics table
|
| 3232 |
grid::grid.text("Pitch Characteristics", x = 0.5, y = y_top_char + 0.015,
|
|
@@ -3235,7 +3314,6 @@ grid::pushViewport(grid::viewport(x = 0.77, y = count_y_top - count_height * 0.5
|
|
| 3235 |
char_headers <- names(pitch_char)
|
| 3236 |
num_char_cols <- length(char_headers)
|
| 3237 |
|
| 3238 |
-
# Calculate column widths safely
|
| 3239 |
if (num_char_cols > 1) {
|
| 3240 |
char_widths <- c(0.10, rep((1 - 0.10 - 0.06) / (num_char_cols - 1), num_char_cols - 1))
|
| 3241 |
} else {
|
|
@@ -3258,7 +3336,6 @@ grid::pushViewport(grid::viewport(x = 0.77, y = count_y_top - count_height * 0.5
|
|
| 3258 |
gp = grid::gpar(col = "white", cex = 0.50, fontface = "bold"))
|
| 3259 |
}
|
| 3260 |
|
| 3261 |
-
# Find Pitch column index
|
| 3262 |
i_col_pitch <- match("Pitch", char_headers)
|
| 3263 |
has_pitchcol <- !is.na(i_col_pitch) && i_col_pitch >= 1
|
| 3264 |
|
|
@@ -3273,14 +3350,12 @@ grid::pushViewport(grid::viewport(x = 0.77, y = count_y_top - count_height * 0.5
|
|
| 3273 |
|
| 3274 |
colname <- char_headers[i]
|
| 3275 |
|
| 3276 |
-
# Get background color safely - check bounds explicitly
|
| 3277 |
bg <- "#FFFFFF"
|
| 3278 |
if (r >= 1 && r <= nrow(pitch_colors_matrix) && i >= 1 && i <= ncol(pitch_colors_matrix)) {
|
| 3279 |
bg <- pitch_colors_matrix[r, i]
|
| 3280 |
if (is.na(bg) || !nzchar(bg)) bg <- "#FFFFFF"
|
| 3281 |
}
|
| 3282 |
|
| 3283 |
-
# Override for Pitch column with pitch type color
|
| 3284 |
if (has_pitchcol && identical(colname, "Pitch") && !is.na(pitch_name) && pitch_name %in% names(pitch_colors)) {
|
| 3285 |
bg <- pitch_colors[[pitch_name]]
|
| 3286 |
}
|
|
@@ -3288,7 +3363,6 @@ grid::pushViewport(grid::viewport(x = 0.77, y = count_y_top - count_height * 0.5
|
|
| 3288 |
grid::grid.rect(x = x_pos_char[i], y = y_row, width = char_widths[i]*0.985, height = row_h_char,
|
| 3289 |
just = c("left","top"), gp = grid::gpar(fill = bg, col = "grey80", lwd = 0.3))
|
| 3290 |
|
| 3291 |
-
# Get value safely
|
| 3292 |
val <- get_cell_value(pitch_char, colname, r)
|
| 3293 |
|
| 3294 |
display_val <- if (is.numeric(val)) {
|
|
|
|
| 3011 |
create_location_by_result_plot(data, player_name, batter_side, pitch_colors)
|
| 3012 |
}
|
| 3013 |
|
| 3014 |
+
get_team_logo_path <- function(team_name, logo_dir = "logos") {
|
| 3015 |
+
if (is.null(team_name) || is.na(team_name) || !nzchar(team_name)) return(NULL)
|
| 3016 |
+
|
| 3017 |
+
# Normalize team name for file matching
|
| 3018 |
+
team_clean <- tolower(gsub("[^a-zA-Z0-9]", "_", team_name))
|
| 3019 |
+
team_nospace <- tolower(gsub("[^a-zA-Z0-9]", "", team_name))
|
| 3020 |
+
|
| 3021 |
+
# Check multiple possible paths
|
| 3022 |
+
possible_paths <- c(
|
| 3023 |
+
file.path(logo_dir, paste0(team_clean, ".png")),
|
| 3024 |
+
file.path(logo_dir, paste0(team_nospace, ".png")),
|
| 3025 |
+
file.path(logo_dir, paste0(team_name, ".png")),
|
| 3026 |
+
file.path(logo_dir, paste0(tolower(team_name), ".png")),
|
| 3027 |
+
# Common abbreviations
|
| 3028 |
+
file.path(logo_dir, "coastal_carolina.png"),
|
| 3029 |
+
file.path(logo_dir, "ccu.png")
|
| 3030 |
+
)
|
| 3031 |
+
|
| 3032 |
+
for (path in possible_paths) {
|
| 3033 |
+
if (file.exists(path)) {
|
| 3034 |
+
return(path)
|
| 3035 |
+
}
|
| 3036 |
+
}
|
| 3037 |
+
|
| 3038 |
+
return(NULL)
|
| 3039 |
+
}
|
| 3040 |
+
|
| 3041 |
+
# Function to add logo to the report
|
| 3042 |
+
add_team_logo <- function(logo_path, x, y, width, height) {
|
| 3043 |
+
if (is.null(logo_path) || !file.exists(logo_path)) {
|
| 3044 |
+
return(invisible(NULL))
|
| 3045 |
+
}
|
| 3046 |
+
|
| 3047 |
+
tryCatch({
|
| 3048 |
+
# Read the PNG image
|
| 3049 |
+
img <- png::readPNG(logo_path)
|
| 3050 |
+
|
| 3051 |
+
# Create a raster grob and draw it
|
| 3052 |
+
grid::grid.raster(
|
| 3053 |
+
img,
|
| 3054 |
+
x = x,
|
| 3055 |
+
y = y,
|
| 3056 |
+
width = width,
|
| 3057 |
+
height = height,
|
| 3058 |
+
just = c("center", "center")
|
| 3059 |
+
)
|
| 3060 |
+
}, error = function(e) {
|
| 3061 |
+
message("Could not load logo: ", e$message)
|
| 3062 |
+
invisible(NULL)
|
| 3063 |
+
})
|
| 3064 |
+
}
|
| 3065 |
+
|
| 3066 |
+
|
| 3067 |
+
create_advanced_pitcher_pdf <- function(game_df, pitcher_name, output_file, logo_dir = "logos") {
|
| 3068 |
if (length(dev.list()) > 0) try(dev.off(), silent = TRUE)
|
| 3069 |
|
| 3070 |
pitch_colors <- c(
|
| 3071 |
+
"Fastball" = "#FA8072", "FourSeamFastBall" = "#FA8072", "Four-Seam" = "#FA8072",
|
| 3072 |
+
"Sinker" = "#fdae61", "Slider" = "#A020F0", "Sweeper" = "magenta",
|
| 3073 |
+
"Curveball" = "#2c7bb6", "ChangeUp" = "#90EE90", "Splitter" = "#90EE32",
|
| 3074 |
+
"Cutter" = "red"
|
| 3075 |
)
|
| 3076 |
|
| 3077 |
.text_on_fill <- function(hex) {
|
|
|
|
| 3083 |
}, error = function(e) "black")
|
| 3084 |
}
|
| 3085 |
|
|
|
|
| 3086 |
get_cell_value <- function(df, colname, row_idx) {
|
| 3087 |
if (is.null(df) || !is.data.frame(df)) return(NA)
|
| 3088 |
if (is.null(colname) || !nzchar(colname)) return(NA)
|
|
|
|
| 3101 |
return(output_file)
|
| 3102 |
}
|
| 3103 |
|
| 3104 |
+
# Get pitcher's team for logo
|
| 3105 |
+
pitcher_team <- NULL
|
| 3106 |
+
if ("PitcherTeam" %in% names(pitcher_df)) {
|
| 3107 |
+
pitcher_team <- pitcher_df$PitcherTeam[1]
|
| 3108 |
+
} else if ("Team" %in% names(pitcher_df)) {
|
| 3109 |
+
pitcher_team <- pitcher_df$Team[1]
|
| 3110 |
+
} else if ("HomeTeam" %in% names(pitcher_df)) {
|
| 3111 |
+
# Try to determine team from context
|
| 3112 |
+
pitcher_team <- pitcher_df$HomeTeam[1]
|
| 3113 |
+
}
|
| 3114 |
+
|
| 3115 |
+
# Get logo path
|
| 3116 |
+
|
| 3117 |
+
logo_path <- get_team_logo_path(pitcher_team, logo_dir)
|
| 3118 |
+
|
| 3119 |
game_day <- tryCatch(parse_game_day(pitcher_df), error = function(e) Sys.Date())
|
| 3120 |
|
| 3121 |
+
# Get summary stats
|
| 3122 |
summary_result <- tryCatch(
|
| 3123 |
create_advanced_pitcher_summary(pitcher_df, pitcher_name),
|
| 3124 |
error = function(e) {
|
|
|
|
| 3130 |
summary_stats <- summary_result$stats
|
| 3131 |
summary_colors <- summary_result$colors
|
| 3132 |
|
| 3133 |
+
# Get pitch characteristics
|
| 3134 |
pitch_result <- tryCatch(
|
| 3135 |
create_advanced_pitch_characteristics(pitcher_df, pitcher_name),
|
| 3136 |
error = function(e) {
|
|
|
|
| 3144 |
pitch_char <- pitch_result$stats
|
| 3145 |
pitch_colors_matrix <- pitch_result$colors
|
| 3146 |
|
| 3147 |
+
# Handle empty pitch_char
|
| 3148 |
if (is.null(pitch_char) || nrow(pitch_char) == 0) {
|
| 3149 |
pitch_char <- data.frame(
|
| 3150 |
Pitch = "-", Count = 0, `Usage%` = NA_real_,
|
|
|
|
| 3161 |
pitch_colors_matrix <- matrix("#FFFFFF", nrow = 1, ncol = ncol(pitch_char))
|
| 3162 |
}
|
| 3163 |
|
| 3164 |
+
# Limit rows
|
| 3165 |
max_rows_to_show <- min(nrow(pitch_char), 9)
|
| 3166 |
if (nrow(pitch_char) > max_rows_to_show) {
|
| 3167 |
pitch_char <- pitch_char[1:max_rows_to_show, , drop = FALSE]
|
| 3168 |
}
|
| 3169 |
|
|
|
|
| 3170 |
num_rows <- nrow(pitch_char)
|
| 3171 |
num_cols <- ncol(pitch_char)
|
| 3172 |
|
| 3173 |
+
# Rebuild color matrix
|
| 3174 |
new_color_matrix <- matrix("#FFFFFF", nrow = num_rows, ncol = num_cols)
|
| 3175 |
if (!is.null(pitch_colors_matrix) && is.matrix(pitch_colors_matrix)) {
|
| 3176 |
rows_to_copy <- min(nrow(pitch_colors_matrix), num_rows)
|
|
|
|
| 3182 |
}
|
| 3183 |
pitch_colors_matrix <- new_color_matrix
|
| 3184 |
|
| 3185 |
+
# Create plots
|
| 3186 |
movement_plot <- tryCatch(
|
| 3187 |
create_movement_plot(pitcher_df, pitcher_name, pitch_colors),
|
| 3188 |
error = function(e) ggplot2::ggplot() + ggplot2::theme_void() + ggplot2::ggtitle("Movement Plot Error")
|
|
|
|
| 3203 |
create_count_usage_plot(pitcher_df, pitcher_name, pitch_colors),
|
| 3204 |
error = function(e) ggplot2::ggplot() + ggplot2::theme_void() + ggplot2::ggtitle("Count Usage Error")
|
| 3205 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3206 |
|
| 3207 |
+
# ENLARGED release side plot
|
| 3208 |
relside_height_plot <- tryCatch(
|
| 3209 |
create_relside_height_plot(pitcher_df, pitcher_name, pitch_colors),
|
| 3210 |
error = function(e) ggplot2::ggplot() + ggplot2::theme_void() + ggplot2::ggtitle("RelSide/Height Error")
|
|
|
|
| 3239 |
available_for_table <- y_top_char - (base_loc_top + table_margin)
|
| 3240 |
row_h_char <- min(max_row_h, max(min_row_h, available_for_table / max(1, rows_including_header)))
|
| 3241 |
|
| 3242 |
+
# ===== HEADER WITH LOGO =====
|
| 3243 |
+
grid::pushViewport(grid::viewport(x = 0.5, y = header_y_top, width = 1, height = 0.06, just = c("center","top")))
|
| 3244 |
+
|
| 3245 |
+
# Add team logo on the left if available
|
| 3246 |
+
if (!is.null(logo_path) && file.exists(logo_path)) {
|
| 3247 |
+
add_team_logo(logo_path, x = 0.08, y = 0.5, width = 0.06, height = grid::unit(0.8, "npc"))
|
| 3248 |
+
}
|
| 3249 |
+
|
| 3250 |
+
# Title in center
|
| 3251 |
grid::grid.text(paste(pitcher_name, "- Advanced Pitcher Report"),
|
| 3252 |
+
x = 0.5, y = 0.5,
|
| 3253 |
gp = grid::gpar(fontface = "bold", cex = 1.8, col = "#006F71"))
|
| 3254 |
+
|
| 3255 |
+
# Add team logo on the right if available (mirror)
|
| 3256 |
+
if (!is.null(logo_path) && file.exists(logo_path)) {
|
| 3257 |
+
add_team_logo(logo_path, x = 0.92, y = 0.5, width = 0.06, height = grid::unit(0.8, "npc"))
|
| 3258 |
+
}
|
| 3259 |
+
|
| 3260 |
grid::popViewport()
|
| 3261 |
|
| 3262 |
# Summary section
|
| 3263 |
+
grid::grid.text("Summary", x = 0.5, y = 0.92,
|
| 3264 |
gp = grid::gpar(fontface = "bold", cex = 1.1, col = "#006F71"))
|
| 3265 |
|
| 3266 |
summary_headers <- names(summary_stats)
|
|
|
|
| 3269 |
|
| 3270 |
x_start <- 0.5 - sum(summary_widths)/2
|
| 3271 |
x_pos <- c(x_start, x_start + cumsum(summary_widths[-length(summary_widths)]))
|
| 3272 |
+
y_top <- 0.905
|
| 3273 |
row_h <- 0.020
|
| 3274 |
|
| 3275 |
for (i in seq_along(summary_headers)) {
|
|
|
|
| 3287 |
gp = grid::gpar(cex = 0.62))
|
| 3288 |
}
|
| 3289 |
|
| 3290 |
+
# Row 1: Movement plot (left) | Velocity distribution (right)
|
| 3291 |
grid::pushViewport(grid::viewport(x = 0.25, y = charts_y_top, width = 0.45, height = charts_height, just = c("center","top")))
|
| 3292 |
tryCatch(print(movement_plot, newpage = FALSE), error = function(e) NULL)
|
| 3293 |
grid::popViewport()
|
|
|
|
| 3296 |
tryCatch(print(velo_plot, newpage = FALSE), error = function(e) NULL)
|
| 3297 |
grid::popViewport()
|
| 3298 |
|
| 3299 |
+
# Row 2: Count plot (left) | ENLARGED Release side plot (right)
|
| 3300 |
+
# Count plot on left
|
| 3301 |
+
grid::pushViewport(grid::viewport(x = 0.27, y = count_y_top, width = 0.50, height = count_height, just = c("center","top")))
|
| 3302 |
tryCatch(print(count_plot, newpage = FALSE), error = function(e) NULL)
|
| 3303 |
grid::popViewport()
|
| 3304 |
|
| 3305 |
+
# ENLARGED Release side plot on right - increased width and height
|
| 3306 |
+
grid::pushViewport(grid::viewport(x = 0.77, y = count_y_top, width = 0.44, height = count_height, just = c("center","top")))
|
| 3307 |
tryCatch(print(relside_height_plot, newpage = FALSE), error = function(e) NULL)
|
| 3308 |
grid::popViewport()
|
|
|
|
| 3309 |
|
| 3310 |
# Pitch Characteristics table
|
| 3311 |
grid::grid.text("Pitch Characteristics", x = 0.5, y = y_top_char + 0.015,
|
|
|
|
| 3314 |
char_headers <- names(pitch_char)
|
| 3315 |
num_char_cols <- length(char_headers)
|
| 3316 |
|
|
|
|
| 3317 |
if (num_char_cols > 1) {
|
| 3318 |
char_widths <- c(0.10, rep((1 - 0.10 - 0.06) / (num_char_cols - 1), num_char_cols - 1))
|
| 3319 |
} else {
|
|
|
|
| 3336 |
gp = grid::gpar(col = "white", cex = 0.50, fontface = "bold"))
|
| 3337 |
}
|
| 3338 |
|
|
|
|
| 3339 |
i_col_pitch <- match("Pitch", char_headers)
|
| 3340 |
has_pitchcol <- !is.na(i_col_pitch) && i_col_pitch >= 1
|
| 3341 |
|
|
|
|
| 3350 |
|
| 3351 |
colname <- char_headers[i]
|
| 3352 |
|
|
|
|
| 3353 |
bg <- "#FFFFFF"
|
| 3354 |
if (r >= 1 && r <= nrow(pitch_colors_matrix) && i >= 1 && i <= ncol(pitch_colors_matrix)) {
|
| 3355 |
bg <- pitch_colors_matrix[r, i]
|
| 3356 |
if (is.na(bg) || !nzchar(bg)) bg <- "#FFFFFF"
|
| 3357 |
}
|
| 3358 |
|
|
|
|
| 3359 |
if (has_pitchcol && identical(colname, "Pitch") && !is.na(pitch_name) && pitch_name %in% names(pitch_colors)) {
|
| 3360 |
bg <- pitch_colors[[pitch_name]]
|
| 3361 |
}
|
|
|
|
| 3363 |
grid::grid.rect(x = x_pos_char[i], y = y_row, width = char_widths[i]*0.985, height = row_h_char,
|
| 3364 |
just = c("left","top"), gp = grid::gpar(fill = bg, col = "grey80", lwd = 0.3))
|
| 3365 |
|
|
|
|
| 3366 |
val <- get_cell_value(pitch_char, colname, r)
|
| 3367 |
|
| 3368 |
display_val <- if (is.numeric(val)) {
|