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 ...
Actually, there is a simpler solution. Use ggplot() within the renderPlot() function as you did before, but use aes_string() instead of aes():
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:
The above code is placed outside of the renderPlot call. Many thanks !
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 toaes
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.
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.