igroffman commited on
Commit
00e6a6e
·
verified ·
1 Parent(s): 313d0a1

Update app.R

Browse files
Files changed (1) hide show
  1. app.R +155 -122
app.R CHANGED
@@ -17,7 +17,6 @@ library(tidyr)
17
  library(stringr)
18
  library(ggplot2)
19
  library(scales)
20
- library(jsonlite)
21
 
22
  # ============================================================
23
  # READ IN DATA
@@ -174,7 +173,7 @@ spring26_ncaa <- download_master_dataset("CoastalBaseball/2026MasterDataset", "n
174
  college_join <- download_private_csv("CoastalBaseball/DefenseAppDataset", "college_join.csv")
175
 
176
  # Load Catcher 2026 data
177
- Catcher2026 <- read_parquet("Catcher2026.parquet")
178
  Catcher2026 <- Catcher2026 %>%
179
  mutate(
180
  Date = as.Date(Date),
@@ -190,14 +189,24 @@ Catcher2026 <- Catcher2026 %>%
190
  )
191
 
192
  # Classify framing: Strike Added / Strike Lost
193
- # Use broader matching for PitchCall values
 
 
 
 
194
  Catcher2026 <- Catcher2026 %>%
195
  mutate(
 
 
196
  frame = case_when(
197
- is_swing == 1 ~ NA_character_,
198
- grepl("ball", PitchCall, ignore.case = TRUE) & in_zone == 1 ~ "Strike Lost",
199
- grepl("strike", PitchCall, ignore.case = TRUE) & !grepl("foul|swing", PitchCall, ignore.case = TRUE) & in_zone == 0 ~ "Strike Added",
200
  TRUE ~ NA_character_
 
 
 
 
 
201
  )
202
  )
203
 
@@ -1030,10 +1039,10 @@ app_ui <- fluidPage(
1030
  fluidRow(
1031
  column(6,
1032
  h4("Strikes Added", class = "section-header"),
1033
- plotlyOutput("recv_strikes_added", height = "480px")),
1034
  column(6,
1035
  h4("Strikes Lost", class = "section-header"),
1036
- plotlyOutput("recv_strikes_lost", height = "480px"))
1037
  ),
1038
  hr(),
1039
  h4("Framing Log", class = "section-header"),
@@ -1086,7 +1095,7 @@ app_ui <- fluidPage(
1086
  plotlyOutput("block_ground_plot", height = "500px")),
1087
  column(6,
1088
  h4("Blocking (Zone View)", class = "section-header"),
1089
- plotlyOutput("block_zone_plot", height = "500px"))
1090
  ),
1091
  hr(),
1092
  h4("Blocking Log", class = "section-header"),
@@ -1709,7 +1718,12 @@ server <- function(input, output, session) {
1709
  })
1710
 
1711
 
1712
- # Pure plotly zone plot helper — no ggplotly conversion issues
 
 
 
 
 
1713
  plotly_zone_shapes <- function() {
1714
  list(
1715
  list(type="rect", x0=-.83083, x1=.83083, y0=1.5, y1=3.3775,
@@ -1722,87 +1736,111 @@ server <- function(input, output, session) {
1722
  )
1723
  }
1724
 
1725
- make_recv_plot <- function(data, title_text, view_mode) {
1726
- if (nrow(data) == 0) {
1727
- return(plot_ly() %>% layout(title=list(text=title_text, font=list(size=14)),
1728
- xaxis=list(visible=FALSE), yaxis=list(visible=FALSE),
1729
- annotations=list(list(text="No data", showarrow=FALSE, font=list(size=16)))))
1730
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1731
 
1732
- if (view_mode == "Catch Position (Overhead)") {
1733
- data <- data %>% filter(!is.na(CatchPositionX) & !is.na(CatchPositionZ))
1734
- if (nrow(data) == 0) return(plot_ly() %>% layout(title="No catch position data"))
1735
- data <- data %>% mutate(row_num = row_number(), cpx = as.numeric(CatchPositionX), cpz = as.numeric(CatchPositionZ))
1736
- pt_colors <- sapply(data$TaggedPitchType, function(pt) {
1737
- if (!is.na(pt) && pt %in% names(pitch_colors)) pitch_colors[pt] else "grey60"
1738
- })
1739
- p <- plot_ly(data, x=~cpx, y=~cpz, type="scatter", mode="markers",
1740
- marker=list(size=10, color=pt_colors, line=list(color="black", width=1)),
1741
- text=~paste0("#",row_num,"<br>Pitch: ",TaggedPitchType,
1742
- "<br>Pitcher: ",Pitcher,"<br>Batter: ",Batter,"<br>Date: ",Date),
1743
- hoverinfo="text") %>%
1744
- layout(title=list(text=paste(title_text,"- Catch Position"), font=list(size=14)),
1745
- xaxis=list(title="X (ft)", range=c(-4,4), scaleanchor="y"),
1746
- yaxis=list(title="Z (ft)", range=c(-5,3)),
1747
- shapes=list(
1748
- list(type="rect", x0=-3.5, x1=-0.708, y0=-4.5, y1=0.15,
1749
- line=list(color="black",width=1), fillcolor="rgba(200,200,200,0.3)"),
1750
- list(type="rect", x0=0.708, x1=3.5, y0=-4.5, y1=0.15,
1751
- line=list(color="black",width=1), fillcolor="rgba(200,200,200,0.3)"),
1752
- list(type="path",
1753
- path="M -0.708 0.15 L 0.708 0.15 L 0.708 0.3 L 0 0.5 L -0.708 0.3 Z",
1754
- line=list(color="black",width=1.5), fillcolor="rgba(240,240,240,0.5)"))
1755
- )
1756
- return(p)
1757
- }
1758
 
1759
- if (view_mode == "Heatmap") {
1760
- data <- data %>% filter(!is.na(PlateLocSide) & !is.na(PlateLocHeight))
1761
- if (nrow(data) < 3) return(plot_ly() %>% layout(title=paste(title_text,"- Not enough data")))
1762
- p <- plot_ly(data, x=~PlateLocSide, y=~PlateLocHeight, type="histogram2dcontour",
1763
- colorscale=list(c(0,"white"),c(0.25,"#0551bc"),c(0.5,"#03ff00"),c(0.75,"#fbff00"),c(1,"#dc1100")),
1764
- contours=list(showlabels=FALSE), showscale=FALSE) %>%
1765
- layout(title=list(text=title_text, font=list(size=14)),
1766
- xaxis=list(title="", range=c(-2,2), scaleanchor="y", showticklabels=FALSE),
1767
- yaxis=list(title="", range=c(0,4.5), showticklabels=FALSE),
1768
- shapes=plotly_zone_shapes())
1769
- return(p)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1770
  }
1771
-
1772
- # Default: Points mode — pure plotly
1773
- data <- data %>% mutate(row_num = row_number())
1774
- pt_colors <- sapply(data$TaggedPitchType, function(pt) {
1775
- if (!is.na(pt) && pt %in% names(pitch_colors)) pitch_colors[pt] else "grey60"
1776
- })
1777
- p <- plot_ly(data, x=~PlateLocSide, y=~PlateLocHeight, type="scatter", mode="markers+text",
1778
- marker=list(size=14, color=pt_colors, line=list(color="black", width=1.2)),
1779
- text=~as.character(row_num), textfont=list(size=8, color="white"),
1780
- textposition="middle center",
1781
- hovertext=~paste0("#",row_num,"<br>Pitch: ",TaggedPitchType,
1782
- "<br>Pitcher: ",Pitcher,"<br>Batter: ",Batter,
1783
- "<br>Velo: ",round(RelSpeed,1)," mph",
1784
- "<br>Count: ",Balls,"-",Strikes,
1785
- "<br>RV: ",round(mean_DRE,3),"<br>Date: ",Date),
1786
- hoverinfo="text") %>%
1787
- layout(title=list(text=title_text, font=list(size=14)),
1788
- xaxis=list(title="", range=c(-1.8,1.8), zeroline=FALSE, showticklabels=FALSE, scaleanchor="y"),
1789
- yaxis=list(title="", range=c(0,4.5), zeroline=FALSE, showticklabels=FALSE),
1790
- shapes=plotly_zone_shapes(),
1791
- showlegend=FALSE)
1792
- p
1793
- }
1794
-
1795
- output$recv_strikes_added <- renderPlotly({
1796
- d <- recv_data() %>% filter(frame == "Strike Added")
1797
- make_recv_plot(d, "Strikes Added", input$recv_view_mode)
1798
  })
1799
-
1800
- output$recv_strikes_lost <- renderPlotly({
1801
- d <- recv_data() %>% filter(frame == "Strike Lost")
1802
- make_recv_plot(d, "Strikes Lost", input$recv_view_mode)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1803
  })
1804
-
1805
- output$recv_log_table <- renderDT({
1806
  d <- recv_data() %>%
1807
  filter(!is.na(frame)) %>%
1808
  dplyr::select(Date, Inning, Pitcher, Batter, TaggedPitchType,
@@ -2149,48 +2187,43 @@ server <- function(input, output, session) {
2149
  ggplotly(p, tooltip="text")
2150
  })
2151
 
2152
- output$block_zone_plot <- renderPlotly({
2153
  bd <- block_data() %>%
2154
  filter(!is.na(PlateLocSide) & !is.na(PlateLocHeight)) %>%
2155
  mutate(row_num = row_number())
2156
-
2157
  view_mode <- input$block_view_mode
2158
-
2159
- if (nrow(bd) == 0) {
2160
- return(plot_ly() %>% layout(title="Blocking (Zone)", xaxis=list(visible=FALSE), yaxis=list(visible=FALSE)))
2161
- }
2162
-
2163
  if (view_mode == "Heatmap" && nrow(bd) >= 3) {
2164
- p <- plot_ly(bd, x=~PlateLocSide, y=~PlateLocHeight, type="histogram2dcontour",
2165
- colorscale=list(c(0,"white"),c(0.25,"#0551bc"),c(0.5,"#03ff00"),c(0.75,"#fbff00"),c(1,"#dc1100")),
2166
- showscale=FALSE) %>%
2167
- layout(title=list(text="Blocking (Zone) - Heatmap", font=list(size=14)),
2168
- xaxis=list(title="", range=c(-2.5,2.5), scaleanchor="y", showticklabels=FALSE),
2169
- yaxis=list(title="", range=c(-1,4.5), showticklabels=FALSE),
2170
- shapes=plotly_zone_shapes())
2171
- return(p)
2172
  }
2173
-
2174
- # Points mode — pure plotly
2175
- pt_colors <- sapply(bd$TaggedPitchType, function(pt) {
2176
- if (!is.na(pt) && pt %in% names(pitch_colors)) pitch_colors[pt] else "grey60"
2177
- })
2178
- symbols <- ifelse(bd$block_type == "PB/WP", "triangle-up", "circle")
2179
- p <- plot_ly(bd, x=~PlateLocSide, y=~PlateLocHeight, type="scatter", mode="markers+text",
2180
- marker=list(size=14, color=pt_colors, symbol=symbols, line=list(color="black", width=1.2)),
2181
- text=~as.character(row_num), textfont=list(size=8, color="white"),
2182
- textposition="middle center",
2183
- hovertext=~paste0("#",row_num,"<br>Pitch: ",TaggedPitchType,
2184
- "<br>Type: ",block_type,
2185
- "<br>Pitcher: ",Pitcher,"<br>Batter: ",Batter,
2186
- "<br>Velo: ",round(RelSpeed,1)," mph",
2187
- "<br>RV: ",round(mean_DRE,3),"<br>Date: ",Date),
2188
- hoverinfo="text") %>%
2189
- layout(title=list(text="Blocking (Zone)", font=list(size=14)),
2190
- xaxis=list(title="", range=c(-2.5,2.5), scaleanchor="y", showticklabels=FALSE),
2191
- yaxis=list(title="", range=c(-1,4.5), showticklabels=FALSE),
2192
- shapes=plotly_zone_shapes(), showlegend=FALSE)
2193
- p
 
 
 
 
 
2194
  })
2195
 
2196
  output$block_log_table <- renderDT({
 
17
  library(stringr)
18
  library(ggplot2)
19
  library(scales)
 
20
 
21
  # ============================================================
22
  # READ IN DATA
 
173
  college_join <- download_private_csv("CoastalBaseball/DefenseAppDataset", "college_join.csv")
174
 
175
  # Load Catcher 2026 data
176
+ Catcher2026 <- download_private_parquet("CoastalBaseball/DefenseAppDataset", "Catcher2026.parquet")
177
  Catcher2026 <- Catcher2026 %>%
178
  mutate(
179
  Date = as.Date(Date),
 
189
  )
190
 
191
  # Classify framing: Strike Added / Strike Lost
192
+ STRIKE_CALLS <- c("StrikeCalled","Called Strike","strike_called","StrikeSwinging",
193
+ "FoulBall","InPlay","Foul","FoulBallNotFieldable",
194
+ "FoulBallFieldable","FoulTip","Foul Tip")
195
+ BALL_CALLS <- c("BallCalled","Ball","ball_called","BallinDirt","BallIntentional",
196
+ "HitByPitch","ball","AutomaticBall")
197
  Catcher2026 <- Catcher2026 %>%
198
  mutate(
199
+ StolenStrike = as.integer(in_zone == 0 & PitchCall %in% STRIKE_CALLS),
200
+ StrikeLost = as.integer(in_zone == 1 & PitchCall %in% BALL_CALLS),
201
  frame = case_when(
202
+ in_zone == 1 & PitchCall %in% BALL_CALLS ~ "Strike Lost",
203
+ in_zone == 0 & PitchCall %in% STRIKE_CALLS ~ "Strike Added",
 
204
  TRUE ~ NA_character_
205
+ ),
206
+ frame_numeric = case_when(
207
+ in_zone == 1 & PitchCall %in% BALL_CALLS ~ -1,
208
+ in_zone == 0 & PitchCall %in% STRIKE_CALLS ~ 1,
209
+ TRUE ~ NA_real_
210
  )
211
  )
212
 
 
1039
  fluidRow(
1040
  column(6,
1041
  h4("Strikes Added", class = "section-header"),
1042
+ plotOutput("recv_strikes_added", height = "480px")),
1043
  column(6,
1044
  h4("Strikes Lost", class = "section-header"),
1045
+ plotOutput("recv_strikes_lost", height = "480px"))
1046
  ),
1047
  hr(),
1048
  h4("Framing Log", class = "section-header"),
 
1095
  plotlyOutput("block_ground_plot", height = "500px")),
1096
  column(6,
1097
  h4("Blocking (Zone View)", class = "section-header"),
1098
+ plotOutput("block_zone_plot", height = "500px"))
1099
  ),
1100
  hr(),
1101
  h4("Blocking Log", class = "section-header"),
 
1718
  })
1719
 
1720
 
1721
+ # Zone plot geometry
1722
+ szr <- data.frame(xmin=-.83083, xmax=.83083, ymin=1.5, ymax=3.3775)
1723
+ bzr <- data.frame(xmin=-.9975, xmax=.9975, ymin=1.3775, ymax=3.5)
1724
+ hp_recv <- data.frame(x=c(-.60,.60,.60,0,-.60), y=c(.15,.15,.27,.42,.27))
1725
+
1726
+ # Plotly zone shapes for catch position overhead
1727
  plotly_zone_shapes <- function() {
1728
  list(
1729
  list(type="rect", x0=-.83083, x1=.83083, y0=1.5, y1=3.3775,
 
1736
  )
1737
  }
1738
 
1739
+ # Core zone plot function (ggplot, NO plotly)
1740
+ make_zone_plot <- function(data, title) {
1741
+ if (!nrow(data)) return(
1742
+ ggplot() + theme_void() + ggtitle(title) +
1743
+ theme(plot.title = element_text(hjust=.5, size=10, face="bold")))
1744
+ apt <- unique(data$TaggedPitchType[!is.na(data$TaggedPitchType)])
1745
+ ggplot(data, aes(PlateLocSide, PlateLocHeight)) +
1746
+ geom_rect(data=bzr, aes(xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax),
1747
+ fill=NA, color="gray50", linewidth=.5, linetype="dotted", inherit.aes=FALSE) +
1748
+ geom_rect(data=szr, aes(xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax),
1749
+ fill=NA, color="black", linewidth=.7, inherit.aes=FALSE) +
1750
+ geom_polygon(data=hp_recv, aes(x=x,y=y),
1751
+ fill=NA, color="gray40", linewidth=.4, inherit.aes=FALSE) +
1752
+ geom_point(aes(fill=TaggedPitchType), shape=21, size=5,
1753
+ color="black", stroke=.6, alpha=.95, na.rm=TRUE) +
1754
+ geom_text(aes(label=row_num), size=2.0, fontface="bold", color="white", na.rm=TRUE) +
1755
+ scale_fill_manual(values=pitch_colors, na.value="grey60",
1756
+ name="Pitch Type", drop=FALSE, limits=apt) +
1757
+ coord_equal() +
1758
+ scale_x_continuous(limits=c(-1.8,1.8)) +
1759
+ scale_y_continuous(limits=c(0,4.5)) +
1760
+ labs(title=title) + theme_classic() +
1761
+ theme(axis.title=element_blank(), axis.text=element_blank(),
1762
+ axis.ticks=element_blank(), axis.line=element_blank(),
1763
+ panel.grid=element_blank(),
1764
+ plot.title=element_text(hjust=.5, size=10, face="bold"),
1765
+ plot.margin=margin(2,2,2,2), legend.position="none")
1766
+ }
1767
 
1768
+ # Heatmap zone plot function (ggplot, NO plotly)
1769
+ make_zone_heatmap <- function(data, title) {
1770
+ if (nrow(data) < 3) return(
1771
+ ggplot() + theme_void() + ggtitle(paste(title, "- Not enough data")) +
1772
+ theme(plot.title = element_text(hjust=.5, size=10, face="bold")))
1773
+ ggplot(data, aes(x=PlateLocSide, y=PlateLocHeight)) +
1774
+ stat_density_2d(aes(fill=after_stat(density)), geom="raster", contour=FALSE) +
1775
+ scale_fill_gradientn(colours=c("white","#0551bc","#02fbff","#03ff00",
1776
+ "#fbff00","#ffa503","#ff1f02","#dc1100"),
1777
+ name="Density") +
1778
+ geom_rect(data=szr, aes(xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax),
1779
+ fill=NA, color="black", linewidth=1, inherit.aes=FALSE) +
1780
+ geom_polygon(data=hp_recv, aes(x=x,y=y),
1781
+ fill=NA, color="black", linewidth=.8, inherit.aes=FALSE) +
1782
+ coord_fixed(ratio=1) + xlim(-2,2) + ylim(0,4.5) +
1783
+ ggtitle(title) + theme_void() +
1784
+ theme(legend.position="right", plot.margin=margin(3,3,3,3),
1785
+ plot.title=element_text(hjust=0.5, size=12, face="bold"))
1786
+ }
 
 
 
 
 
 
 
1787
 
1788
+ output$recv_strikes_added <- renderPlot({
1789
+ d <- recv_data() %>% filter(frame == "Strike Added") %>% mutate(row_num = row_number())
1790
+ vm <- input$recv_view_mode
1791
+ if (vm == "Heatmap") {
1792
+ make_zone_heatmap(d, "Strikes Added")
1793
+ } else if (vm == "Catch Position (Overhead)") {
1794
+ # Render as ggplot with batters box
1795
+ d2 <- d %>% filter(!is.na(CatchPositionX) & !is.na(CatchPositionZ)) %>%
1796
+ mutate(cpx = as.numeric(CatchPositionX), cpz = as.numeric(CatchPositionZ))
1797
+ if (nrow(d2) == 0) return(ggplot() + theme_void() + ggtitle("No catch position data"))
1798
+ bi <- 1; bo <- 4; bymin <- -2.5; bymax <- 3.7
1799
+ plate <- data.frame(x=c(-.708,.708,.708,0,-.708), y=c(1.417,1.417,.708,0,.708))
1800
+ apt <- unique(d2$TaggedPitchType[!is.na(d2$TaggedPitchType)])
1801
+ ggplot(d2, aes(x=cpx, y=cpz)) +
1802
+ annotate("rect", xmin=bi, xmax=bo, ymin=bymin, ymax=bymax, fill=NA, color="black", linewidth=.8) +
1803
+ annotate("rect", xmin=-bo, xmax=-bi, ymin=bymin, ymax=bymax, fill=NA, color="black", linewidth=.8) +
1804
+ geom_polygon(data=plate, aes(x=x,y=y), fill=NA, color="black", linewidth=.8, inherit.aes=FALSE) +
1805
+ geom_point(aes(fill=TaggedPitchType), shape=21, size=4, color="black", stroke=.6, alpha=.95) +
1806
+ geom_text(aes(label=row_num), size=2.0, fontface="bold", color="white") +
1807
+ scale_fill_manual(values=pitch_colors, na.value="grey60", name="Pitch Type", drop=FALSE, limits=apt) +
1808
+ coord_fixed() + labs(title="Strikes Added - Catch Position") +
1809
+ theme_void() + theme(plot.title=element_text(hjust=.5, size=10, face="bold"),
1810
+ legend.position="bottom")
1811
+ } else {
1812
+ make_zone_plot(d, "Strikes Added")
1813
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1814
  })
1815
+
1816
+ output$recv_strikes_lost <- renderPlot({
1817
+ d <- recv_data() %>% filter(frame == "Strike Lost") %>% mutate(row_num = row_number())
1818
+ vm <- input$recv_view_mode
1819
+ if (vm == "Heatmap") {
1820
+ make_zone_heatmap(d, "Strikes Lost")
1821
+ } else if (vm == "Catch Position (Overhead)") {
1822
+ d2 <- d %>% filter(!is.na(CatchPositionX) & !is.na(CatchPositionZ)) %>%
1823
+ mutate(cpx = as.numeric(CatchPositionX), cpz = as.numeric(CatchPositionZ))
1824
+ if (nrow(d2) == 0) return(ggplot() + theme_void() + ggtitle("No catch position data"))
1825
+ bi <- 1; bo <- 4; bymin <- -2.5; bymax <- 3.7
1826
+ plate <- data.frame(x=c(-.708,.708,.708,0,-.708), y=c(1.417,1.417,.708,0,.708))
1827
+ apt <- unique(d2$TaggedPitchType[!is.na(d2$TaggedPitchType)])
1828
+ ggplot(d2, aes(x=cpx, y=cpz)) +
1829
+ annotate("rect", xmin=bi, xmax=bo, ymin=bymin, ymax=bymax, fill=NA, color="black", linewidth=.8) +
1830
+ annotate("rect", xmin=-bo, xmax=-bi, ymin=bymin, ymax=bymax, fill=NA, color="black", linewidth=.8) +
1831
+ geom_polygon(data=plate, aes(x=x,y=y), fill=NA, color="black", linewidth=.8, inherit.aes=FALSE) +
1832
+ geom_point(aes(fill=TaggedPitchType), shape=21, size=4, color="black", stroke=.6, alpha=.95) +
1833
+ geom_text(aes(label=row_num), size=2.0, fontface="bold", color="white") +
1834
+ scale_fill_manual(values=pitch_colors, na.value="grey60", name="Pitch Type", drop=FALSE, limits=apt) +
1835
+ coord_fixed() + labs(title="Strikes Lost - Catch Position") +
1836
+ theme_void() + theme(plot.title=element_text(hjust=.5, size=10, face="bold"),
1837
+ legend.position="bottom")
1838
+ } else {
1839
+ make_zone_plot(d, "Strikes Lost")
1840
+ }
1841
  })
1842
+
1843
+ output$recv_log_table <- renderDT({
1844
  d <- recv_data() %>%
1845
  filter(!is.na(frame)) %>%
1846
  dplyr::select(Date, Inning, Pitcher, Batter, TaggedPitchType,
 
2187
  ggplotly(p, tooltip="text")
2188
  })
2189
 
2190
+ output$block_zone_plot <- renderPlot({
2191
  bd <- block_data() %>%
2192
  filter(!is.na(PlateLocSide) & !is.na(PlateLocHeight)) %>%
2193
  mutate(row_num = row_number())
 
2194
  view_mode <- input$block_view_mode
2195
+ if (nrow(bd) == 0) return(
2196
+ ggplot() + theme_void() + ggtitle("Blocking (Zone)") +
2197
+ theme(plot.title = element_text(hjust=.5, size=10, face="bold")))
 
 
2198
  if (view_mode == "Heatmap" && nrow(bd) >= 3) {
2199
+ return(make_zone_heatmap(bd, "Blocking (Zone) - Heatmap"))
 
 
 
 
 
 
 
2200
  }
2201
+ sl <- unique(bd$block_type)
2202
+ sv <- c("Block"=21, "PB/WP"=24)[sl]
2203
+ slb <- c("Block"="Block", "PB/WP"="PB/WP")[sl]
2204
+ tc <- bd %>% count(TaggedPitchType) %>% mutate(label=paste0(TaggedPitchType,": ",n))
2205
+ cl <- setNames(tc$label, tc$TaggedPitchType)
2206
+ ggplot(bd, aes(PlateLocSide, PlateLocHeight)) +
2207
+ geom_rect(data=bzr, aes(xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax),
2208
+ fill=NA, color="gray50", linewidth=.6, linetype="dotted", inherit.aes=FALSE) +
2209
+ geom_rect(data=szr, aes(xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax),
2210
+ fill=NA, color="black", linewidth=.8, inherit.aes=FALSE) +
2211
+ geom_polygon(data=hp_recv, aes(x=x,y=y),
2212
+ fill=NA, color="gray40", linewidth=.5, inherit.aes=FALSE) +
2213
+ geom_point(aes(fill=TaggedPitchType, shape=block_type), size=4, color="black", stroke=.6, na.rm=TRUE) +
2214
+ geom_text(aes(label=row_num), size=2.0, fontface="bold", color="white", na.rm=TRUE) +
2215
+ scale_fill_manual(values=pitch_colors, na.value="grey60", name="Pitch Type", labels=cl) +
2216
+ scale_shape_manual(values=sv, name=NULL, labels=slb) +
2217
+ coord_equal(xlim=c(-2.5,2.5), ylim=c(-1,4.5)) +
2218
+ labs(title="Blocking (Zone)") + theme_void() +
2219
+ theme(plot.title=element_text(hjust=.5, size=10, face="bold"),
2220
+ legend.position="bottom", legend.box="vertical",
2221
+ legend.spacing.y=unit(2,"pt"),
2222
+ legend.title=element_text(size=7, face="bold"),
2223
+ legend.text=element_text(size=6),
2224
+ legend.key.size=unit(.5,"lines")) +
2225
+ guides(fill=guide_legend(order=1, nrow=2, override.aes=list(size=3, shape=21)),
2226
+ shape=guide_legend(order=2, nrow=1, override.aes=list(size=3, fill="#4472C4")))
2227
  })
2228
 
2229
  output$block_log_table <- renderDT({