1

I'm trying to create a ggplot 2 choropleth when the user inputs specific data in my RShiny app. I merged my spatial dataframe with my data as shown below, and I think that worked properly:

# inputting data in RShiny app:
output$contents <- renderTable({
    file <- input$uploaded_data
    ext <- tools::file_ext(file$datapath)
    
    req(file)
    
    counties_and_trash <- read_csv(file$datapath)
    
    mean_each_type <- counties_and_trash %>%
      group_by(COUNTY) %>%
      summarise(mean_plastics = mean(PLASTICS, na.rm = TRUE),
                mean_papers = mean(PAPERS, na.rm = TRUE),
                mean_metals = mean(METALS, na.rm = TRUE))
})

# merging spdf and data:
spdf <- geojson_read('County_Boundaries_of_NJ.geojson',  what = "sp")
merged <- merge(spdf, mean_each_type, by.x = 'COUNTY', by.y = 'mean_plastics')

Please note that, as of right now, I'm only trying to plot "mean_plastics" in the choropleth. The other ones ("mean_papers" and "mean_metals") are irrelevant at the moment. I tried to output the ggplot2 choropleth plot in the RShiny app, as shown below:

output$plot <- renderPlot({
    ggplot(merged, aes(x = x, y = y)) + 
    geom_polygon(aes(fill = mean_plastics), data = mean_each_type, x = 'COUNTY', y = 'mean_plastics') +
    theme_void() +
    coord_map()
  })

When I try to output that, I get the following error:

Warning: Error in geom_polygon: Problem while converting geom to grob.
ℹ Error occurred in the 1st layer.
Caused by error in `lat * pi`:
! non-numeric argument to binary operator

I'm certain there's something wrong with the "output$plot..." block of code, but I'm not sure how to fix it.

If more information is needed, I can provide it. I apologize if I didn't explain it well enough, this is my first time doing a more complicated R project. Thank you for any and all help!

EDIT: I've added the minimal reproducible example below.

The only file you'll have to download for the code to work is at the following link (the downloaded file should have the name "County_Boundaries_of_NJ.geojson"):

download link for NJ county boundaries geojson

Here's the minimal reproducible code:

library(shiny)
library(tidyverse)
library(ggplot2)
library(geojsonio)
library(broom)
library(sp)
library(sf)

ui <- fluidPage(
  fluidRow(
    h1(strong('NJ Beachsweep Mapping with R'), align = 'center'),
    column(6,
           # this empty column is just to put the other column in the right place
    ),
    column(6,
           tableOutput("contents")
    ),
    mainPanel(
      plotOutput("plot")
    )
  )
)

server <- function(input, output, session) {
  counties_and_trash <- structure(list(COUNTY = c("Atlantic", "Atlantic", "Bergen", "Burlington", 
                                                  "Burlington", "Essex", "Middlesex", "Cape May", "Cape May", "Cape May", 
                                                  "Monmouth", "Monmouth", "Monmouth", "Monmouth", "Monmouth", "Monmouth", 
                                                  "Ocean"), PLASTICS = c(340, 300, 325, 467, 545, 354, 433, 325, 
                                                                         324, 653, 768, 457, 486, 944, 356, 457, 568), PAPERS = c(260, 
                                                                                                                                  210, 453, 223, 235, 356, 324, 274, 540, 346, 475, 462, 342, 354, 
                                                                                                                                  435, 346, 234), METALS = c(45, 35, 123, 124, 224, 124, 134, 342, 
                                                                                                                                                             230, 243, 324, 125, 323, 122, 334, 421, 401)), row.names = c(NA, 
                                                                                                                                                                                                                          -17L), spec = structure(list(cols = list(COUNTY = structure(list(), class = c("collector_character", 
                                                                                                                                                                                                                                                                                                        "collector")), PLASTICS = structure(list(), class = c("collector_double", 
                                                                                                                                                                                                                                                                                                                                                              "collector")), PAPERS = structure(list(), class = c("collector_double", 
                                                                                                                                                                                                                                                                                                                                                                                                                  "collector")), METALS = structure(list(), class = c("collector_double", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                      "collector"))), default = structure(list(), class = c("collector_guess", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            "collector")), delim = ","), class = "col_spec"), class = c("spec_tbl_df", 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        "tbl_df", "tbl", "data.frame"))
  mean_each_type <- counties_and_trash %>%
    group_by(COUNTY) %>%
    summarise(mean_plastics = mean(PLASTICS, na.rm = TRUE),
              mean_papers = mean(PAPERS, na.rm = TRUE),
              mean_metals = mean(METALS, na.rm = TRUE))
  
  output$contents <- renderTable({
    counties_and_trash
  })
  
  spdf <- geojson_read('County_Boundaries_of_NJ.geojson',  what = "sp") 
  merged <- merge(spdf, mean_each_type, by.x = 'COUNTY', by.y = 'mean_plastics')
  
  output$plot <- renderPlot({ # this block of code is where the issue most likely is
    ggplot(merged, aes(x = x, y = y)) + 
      geom_polygon(aes(fill = mean_plastics)) +
      theme_void() +
      coord_map()
  })
}
shinyApp(ui = ui, server = server)

Thank you so much!

  • 1
    Try `geom_polygon(aes(fill = mean_plastics))`. If that does not fix your issue I would suggest to first try your code outside of your shiny app. For more help I would suggest Ito provide [a minimal reproducible example](https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) including a snippet of your data or some fake data and a source for your shapefile. – stefan Mar 03 '23 at 22:36
  • @stefan Thank you so much for your comment. I tried using the code snippet you provided, but unfortunately it didn't work. I'm working on getting a minimal reproducible example ready right now, but I had one question about it. One of the files needed to run my code is a geojson file of New Jersey counties, and it's a pretty large file (large enough that I don't think I could feasibly reproduce it with dput, unless I'm wrong?). I do have the download link for the geojson file though. Should I edit my original post with the reproducible code and the link to the geojson file site? Thanks again! – anna_pitera Mar 04 '23 at 01:34
  • Hi Anna. Yep. Best would be to provide the link to download the geojson. – stefan Mar 04 '23 at 08:27
  • @stefan Thank you! Sorry for the wait, I've had work. I've added the minimal reproducible example in the edit of the original post. I used the code that you provided in the example (the code from your first comment), so it does come up with a different error than what my original post had. This time it has the error "mean_plastics not found". Let me know if you need any more information or if you have any ideas. – anna_pitera Mar 05 '23 at 13:20

1 Answers1

0

There are multiple issues with your code but none is related to shiny. First, the shapefile your read is a sp object. For this type of object neither simply merging your data nor plotting via geom_polygon works. Instead convert to a sf object which behave fairly identical to standard dataframes and which can be easily plotted using geom_sf. Second, merging on mean_plastics doesn't make sense as there is no such column in the spatial dataset. Third, merging will only work if there is an identical key in both datasets not just the same column name, i.e. you have to convert the COUNTY column to upper-case in the mean_each_type dataset. Otherwise there are no matches.

library(shiny)
library(tidyverse)
library(geojsonio)
library(sp)
library(sf)

ui <- fluidPage(
  fluidRow(
    h1(strong("NJ Beachsweep Mapping with R"), align = "center"),
    column(
      6,
      plotOutput("plot")
    ),
    column(
      6,
      tableOutput("contents")
    )
  )
)

server <- function(input, output, session) {
  mean_each_type <- counties_and_trash %>%
    group_by(COUNTY) %>%
    summarise(
      mean_plastics = mean(PLASTICS, na.rm = TRUE),
      mean_papers = mean(PAPERS, na.rm = TRUE),
      mean_metals = mean(METALS, na.rm = TRUE)
    ) |>
    mutate(COUNTY = toupper(COUNTY))

  output$contents <- renderTable({
    counties_and_trash
  })

  spdf <- geojson_read("County_Boundaries_of_NJ.geojson", what = "sp")
  # Convert to sf
  spdf <- sf::st_as_sf(spdf)

  merged <- merge(spdf, mean_each_type, by = "COUNTY")

  output$plot <- renderPlot({ # this block of code is where the issue most likely is
    ggplot(merged) +
      # Use geom_sf
      geom_sf(aes(fill = mean_plastics)) +
      theme_void()
  })
}
shinyApp(ui = ui, server = server)

enter image description here

stefan
  • 90,330
  • 6
  • 25
  • 51
  • 1
    I just received the notification for your comment. I sincerely appreciate everything you’ve done for me, because this is exactly how I wanted it to work! I didn’t even think of making the sp object into an sf. Now I can focus on adding more options for the map customization. Thank you so, so much. – anna_pitera Mar 05 '23 at 21:07
  • I'm sorry for asking another question, but do you think you could help me with changing the "counties_and_trash" dataframe into an uploadable CSV file? I think I've worked out the actual file uploading part with RShiny. I've also worked out displaying the uploaded data as a table. However, when I try to display the data in the map, I get an error from merge: "'by' must specify a uniquely valid column". I can post the updated code in a new post as well, just let me know if you're able to help. Thank you again, I really appreciate it. – anna_pitera Mar 09 '23 at 21:00
  • Hi Anna. Best would be to ask a new question based on your present code and data. – stefan Mar 09 '23 at 22:59
  • Thank you for your quick response! Here is the link to the new post: https://stackoverflow.com/questions/75691054/using-uploaded-file-to-create-choropleth-map-gives-error-by-must-specify-a-u – anna_pitera Mar 09 '23 at 23:46