Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

shiny app - ggplot can't find data

Tags:

r

ggplot2

shiny

I have made a shiny app which brings static maps back using ggmap. However when I want to overlay postcode boundaries I am encountering an error where ggplot cannot find the data set.

The dataset poa is a dataframe of postcode boundaries i.e. lats and lons with a polygon ID. I have already tried adding environment = environment() but that doesn't solve my problem. I know that the data exists as I call print(str(poa)) which prints to the R console.

Can anyone suggest a work around for me so that ggplot can access the poa dataframe? I apologise that this is not a very reproducible example.

Update: ggplot is able to access the poa dataframe when I use this code:

print(ggmap(map)) + geom_polygon(data = poa, aes(x = LON, y = LAT, group = order), alpha = .5, colour = "black", fill = NA))

But I need to make a nested call to ggplot for the base layer of the map, when I do that ggplot is unable to find the data

Here is my server.R code

I am using isolate as I have an action button in my ui.R and I only want the plot to update when it has been clicked.

library(shiny)
library(ggmap)
library(RODBC)

# Define server logic required to summarize and view the selected dataset
shinyServer(function(input, output) {

output$searchString <- renderText({
    if (input$searchButton == 0)
        return()        
    isolate({input$searchString})
})

mapSourceInput <- reactive({
    switch(input$mapSource
           , "google" = "google"
           , "stamen" = "stamen")
})

mapTypeInput <- reactive({
    switch(input$mapType
           , "terrain" = "terrain"
           , "satellite" = "satellite"
           , "roadmap" = "roadmap"
           , "hybrid" = "hybrid"
           , "toner" = "toner"
           , "watercolor" = "watercolor")
})

overlayInput <- reactive({
    switch(input$overlay
           , "postcodes" = "postcodes"
           , "states" = "states"
           , "nothing" = "nothing")
})

output$map <- renderPlot({
    if (input$searchButton == 0)
        return()

    isolate({
        if (overlayInput() == "nothing"){
            map <- get_map(location = input$searchString, zoom = input$zoom, source = mapSourceInput(), maptype = mapTypeInput())  
            mapPlot <- ggmap(map)
            print(mapPlot)
            #return()            
        } else {
            if (overlayInput() == "postcodes"){
                #postcode boundaries
                map <- get_map(location = input$searchString, zoom = input$zoom, source = mapSourceInput(), maptype = mapTypeInput())  
                poa <- structure(list(POAOBS = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L)
                               , COORD_REF = 1:10
                               , COORD_POL = 1:10
                               , POLYGON = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L)
                               , LON = c(144.951431274414, 144.956451416016, 144.95539855957, 144.955993652344, 144.958465576172, 144.956634521484, 144.956817626953, 144.954727172852, 144.957550048828, 144.958831787109)
                               , LAT = c(-37.8131675720215, -37.8117561340332, -37.8094863891602, -37.8058776855469, -37.8061485290527, -37.8021659851074, -37.8010902404785, -37.7994079589844, -37.7997169494629, -37.799861907959)
                               , POA = c("3000", "3000", "3000", "3000", "3000", "3000", "3000", "3000", "3000", "3000"))
                          , .Names = c("POAOBS", "COORD_REF", "COORD_POL", "POLYGON", "LON", "LAT", "POA")
                          , row.names = c(NA, 10L)
                          , class = "data.frame")

                print(str(poa))

                print(ggmap(map, base_layer = ggplot(data = poa, aes(x = LON, y = LAT), environment = environment()), extent = "normal", maprange = FALSE, environment = .GlobalEnv) +
                          geom_polygon(data = poa, aes(x = LON, y = LAT, group = order), alpha = .5, colour = "black", fill = NA) +   
                          coord_map(projection = "mercator", 
                                    xlim = c(attr(map, "bb")$ll.lon, attr(map, "bb")$ur.lon),
                                    ylim = c(attr(map, "bb")$ll.lat, attr(map, "bb")$ur.lat)))

            } else {
                if(overlayInput() == "states"){
                    return()
                }}}
    })
})

})

edit: added ui.R

library(shiny)
# Define UI for dataset viewer application
shinyUI(pageWithSidebar(

# Application title.
headerPanel("The New Map App"),

# Sidebar with controls
sidebarPanel(        
    textInput("searchString", "Get Map Of", value = "melbourne, australia") 

    , selectInput("mapSource", "Choose a Map Source", choices = c("google", "stamen"))

    , numericInput("zoom", "Zoom Level", 10)

    , helpText("Note: An integer from 3 (continent) to 21 (building), default value 10 (city)")

    , selectInput("mapType", "Choose a Map Type", choices = c("terrain", "satellite", "roadmap", "hybrid", "toner", "watercolor"))

    , helpText("Note: Options available are 'terrain', 'satellite', 'roadmap', and 'hybrid' (google maps), 'watercolor', and 'toner' (stamen maps)")

    , radioButtons("overlay", "Overlay Polygon",
                   list("Postcodes" = "postcodes"
                        , "States" = "states"
                        , "Nothing" = "nothing"))

    , actionButton("searchButton", "Get Map")
    , tags$style(type='text/css', "button#searchButton { margin-bottom: 9px; }")
),

#output panel
mainPanel(
    h3(textOutput("searchString"))
    , plotOutput("map")

)
))

output of sessionInfo:

R version 3.0.1 (2013-05-16)
Platform: x86_64-pc-linux-gnu (64-bit)

locale:
 [1] LC_CTYPE=en_AU.UTF-8       LC_NUMERIC=C               LC_TIME=en_AU.UTF-8            LC_COLLATE=en_AU.UTF-8     LC_MONETARY=en_AU.UTF-8   
 [6] LC_MESSAGES=en_AU.UTF-8    LC_PAPER=C                 LC_NAME=C                      LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_AU.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] RODBC_1.3-7     ggmap_2.3       ggplot2_0.9.3.1 shiny_0.6.0    

loaded via a namespace (and not attached):
 [1] bitops_1.0-5        caTools_1.14        colorspace_1.2-2    dichromat_2.0-0         digest_0.6.3        grid_3.0.1         
 [7] gtable_0.1.2        httpuv_1.0.6.3      labeling_0.1            mapproj_1.2-1           maps_2.3-2          MASS_7.3-26        
[13] munsell_0.4         plyr_1.8            png_0.1-5           proto_0.3-10            RColorBrewer_1.0-5  Rcpp_0.10.4        
[19] reshape2_1.2.2      RgoogleMaps_1.2.0.3 rjson_0.2.12        RJSONIO_1.0-3       scales_0.2.3        stringr_0.6.2      
[25] tools_3.0.1         xtable_1.7-1   
like image 827
dom_oh Avatar asked Jul 04 '13 10:07

dom_oh


2 Answers

So I have finally figured this one out.

To have ggplot be able to find the data set in the nested ggplot query, the data set in question needs to be assigned using <<-

For example the data set poa cannot be found in this nested query when being called inside a function.

print(ggmap(map)) + geom_polygon(data = poa, aes(x = LON, y = LAT, group = order), alpha = .5, colour = "black", fill = NA))

So before you need to use poa in the function use this line

poa <<- poa

From the help: "The operators <<- and ->> are normally only used in functions, and cause a search to made through parent environments for an existing definition of the variable being assigned."

like image 116
dom_oh Avatar answered Nov 10 '22 12:11

dom_oh


This is a tricky one. I might edit this answer as I get a little better acquainted with ggplot's environment nesting. But here's the modified chunk that seems to make it work for me:

        env <- environment()   

        print(ggmap(map, base_layer = ggplot(data = poa, aes(x = LON, y = LAT), environment=env), extent = "normal", maprange = FALSE, environment=environment()) +
                  geom_polygon(data = poa, aes(x = LON, y = LAT), alpha = .5, colour = "black", fill = NA, environment=env) +   
                  coord_map(projection = "mercator", 
                            xlim = c(attr(map, "bb")$ll.lon, attr(map, "bb")$ur.lon),
                            ylim = c(attr(map, "bb")$ll.lat, attr(map, "bb")$ur.lat)))

A few things to note here.

  1. I removed the group = order directive, as that causes a "differing number of rows" error. Not sure what you're after there, but it doesn't look right.
  2. I store the environment in which poa exists in a variable named env. This is the environment you want to use in any function which references poa.
  3. I instruct the calls to geom_polygon and ggplot to use the environment I've just created by specifying a environment=env parameter.
  4. I instruct the outer ggmap call to be executed in the calling environment using environment=environment() as the last parameter in that command. Without this, it won't be able to find the env variable we created.

With those changes, things seem to work properly.

like image 40
Jeff Allen Avatar answered Nov 10 '22 11:11

Jeff Allen