3
votes

I am trying to develop a shiny app that will mount a map based heat map on a browser and allow you to change the variable shown on the heat map. The map is of a geographic region with GIS shape files and the variable chosen is then shown on the map as a heat map. Unfortunately I have a problem in that the variable is not being passed to ggplot() correctly and my map fails. The server.r code below as a straight R script runs successfully without issue but when adapted to Shiny it fails.

The issue happens with the following ggplot() code in server.r:

myplot1 <- myplot1 + aes(long, lat, group = group, fill = input$var) + ...

I get an error:

Error in eval(expr, envir, enclos) : object 'input' not found

This has to do with fill = input$var where it does not recognize input$var that is being passed from ui.r. input$var is the chosen variable (var1, var2, etc.) in ui.r to show on the heat map. This is the only instance I know of in the code where input$var is not recognized. I have used print(str(input$var)) before this line and it clearly holds the desired variable's name. If I hard code it in (e.g. fill=var1), then ggplot() works fine and the map is shown correctly.

I have also used environment = environment() in the ggplot() but this generates another error:

Error : Discrete value supplied to continuous scale

I am interpreting to mean that it is looking for a data frame described by the variable but instead it gets a single value.

I get the feeling that it is something simple I am missing - something I should declare or re-assign. I would be grateful for any insight, guidance or feedback folks have on this. Many thanks !!

# server.R

library(shiny)

library(maps)
library(mapdata)
library(sp)
library(maptools)
library(scales)
library(RColorBrewer)
library(ggplot2)
library(rgeos)
library(plyr)
library(reshape)
library(mapproj)
library(rgdal)
library(grid)
library(gridExtra)

setwd("C:/Shiny")


# Step 1 Read/loading the target shapefile
gregion = readOGR(dsn="C:/Shiny", layer="duid")

# Step 2 Get row numbers from .dbf / explicitly identifies attribute rows by the .dbf offset.
gregion@data$id = rownames(gregion@data)

# Step 3 Makes centroid (point layer) from polygon "FORTIFY"
gregion.points = fortify(gregion, region="id")

# Step 4 Reading in .csv which will be joined to .dbf using "MERGE"
mydata <- read.csv("c:/Shiny/dataset.txt")

# Step 5 Joins the points to their corresponding attributes and finalizes the data preparation 
gregion.df = join(gregion.points, gregion@data, by="id")

# Step 6 Merge makes an inner join of the shapefile's data frame and the .csv on a common item (usually the spatial key)
mygeomdata <- merge(gregion.df, mydata, by.x="UID", by.y="UID")


# Define server logic required to plot various variables as heatmap
# Step 7 Create map
shinyServer(function(input, output) {

  # Compute the forumla text in a reactive expression since it is 
  # shared by the output$caption and output$mapPlot expressions

  formulaText <- reactive({
    paste("Variable:", input$var)
  })

  # Return the formula text for printing as a caption
  output$caption <- renderText({
    formulaText()
  })


  output$mapPlot <- renderPlot({

    myplot1 <- ggplot(mygeomdata)
    myplot1 <- myplot1 + aes(long, lat, group = group, fill = input$var) + labs(x = "Easting", y = "Northing") + scale_fill_gradient(low = "ghostwhite", high = "steelblue")
    myplot1 <- myplot1 + geom_polygon()
    myplot1 <- myplot1 + coord_equal()

    print(myplot1)

  })

})

#ui.R

library(shiny)

shinyUI(pageWithSidebar(

  # Application title
  headerPanel("Mapping"),

  # Sidebar with controls to select the variable to plot
  #

  sidebarPanel(
    selectInput("var", "Variable:",
                list("Variable 1" = "var1",
                     "Variable 2" = "var2"))
  ),

  # Show the caption and plot of the requested variable

  mainPanel(
    h3(textOutput("caption")),

    plotOutput("mapPlot")
  )
))

A sample of the dataset, mydata <- read.csv("c:/Shiny/dataset.txt") is:

UID var1    var2    var3    var4    var5    var6    var7
1   0   0.001   0   0   0   0   0
2   0   0   0   0   1   0   0
3   0   0   0   0   0   0   0
4   0   0   0   0   1   0   0
5   0   0   0   0   1   0   0
6   0   0   0   0   1   0   0
7   0   0   0   0   0   0   0
8   0   0.004   0.026   0   0   0   0
9   0.499   0.014   0   0.499   1   0   0.033
10  0.573   0.002   0.015   0.573   1   0   0.427
11  1   0.003   0.01    1   1   0   0

mygeomdata has the following structure:

 $ UID       : int  1 1 1 1 1 1 1 1 1 1 ...
 $ long      : num  393121 392895 392895 392840 392839 ...
 $ lat       : num  5501404 5502275 5502275 5502489 5502494 ...
 $ order     : int  1 2 3 4 5 6 7 8 9 10 ...
 $ hole      : logi  FALSE FALSE FALSE FALSE FALSE FALSE ...
 $ piece     : Factor w/ 4 levels "1","2","3","4": 1 1 1 1 1 1 1 1 1 1 ...
 $ group     : Factor w/ 5693 levels "0.1","1.1","10.1",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ id        : chr  "0" "0" "0" "0" ...
 $ DUID      : Factor w/ 5656 levels "130023362","130023367",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ PC        : Factor w/ 3617 levels "0","ZZZ0A3","ZZZ0A4",..: 3271 3271 3271 3271 3271 3271 3271 3271 3271 3271 ...
 $ DUIDAREA  : num  21687 21687 21687 21687 21687 ...
 $ ELEV      : num  14.8 14.8 14.8 14.8 14.8 ...
 $ GroupUp   : int  2 2 2 2 2 2 2 2 2 2 ...
 $ GroupUpT  : Factor w/ 2 levels "A","B": 2 2 2 2 2 2 2 2 2 2 ...
 $ var1      : num  0 0 0 0 0 0 0 0 0 0 ...
 $ var2      : num  0.001 0.001 0.001 0.001 0.001 0.001 0.001 0.001 0.001 0.001 ...
 $ var3      : num  0 0 0 0 0 0 0 0 0 0 ...
 $ var4      : num  0 0 0 0 0 0 0 0 0 0 ...
 $ var5      : int  0 0 0 0 0 0 0 0 0 0 ...
 $ var6      : num  0 0 0 0 0 0 0 0 0 0 ...
 $ var7      : num  0 0 0 0 0 0 0 0 0 0 ...
4

4 Answers

2
votes

Even though you are getting this error as part of your attempt to port it to Shiny, this is related to ggplot. In a nutshell, you are using local variables and function arguments to aes and that is trickier than it first seems.

Please see this SO question for a full discussion and several good options.

One thing to keep in mind: For ggplot, to the extent that you keep everything needed as part of the data frame, you will find it that much easier.

agstudy's response in this question explains what is going on.

Hope that helps.

2
votes

Thank you Ram Narasimhan. You pointed me to some very useful posts and I was able to after some digging arrive at a solution. Here is the modified section:

environment<-environment() 
  myplot1 <- ggplot(mygeomdata, aes(long, lat, group = group, fill = get(input$var)), environment = environment) 

The above code is placed outside of the renderPlot call. Many thanks !

1
votes

Actually, there is a simpler solution. Use ggplot() within the renderPlot() function as you did before, but use aes_string() instead of aes():

output$mapPlot <- renderPlot({
    ggplot(mygeomdata, aes_string(x=long, y=lat, group = group, fill = input$var)) + geom_point()
})
1
votes

aes_string(x=input$x, y=input$y) also works. Just make sure that both inputs are taken as reactive inputs (e.g. input$). ggplot() isn't allowing one of the axes to be manual and the other as reactive.