8

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   
dom_oh
  • 867
  • 2
  • 11
  • 21
  • Could you post your ui.R? Makes it easier to test. – Ciarán Tobin Jul 04 '13 at 10:43
  • Does this help? http://stackoverflow.com/questions/14810409/save-plots-made-in-a-shiny-app – IRTFM Jul 04 '13 at 20:08
  • @DWin I think it is more related to this question http://stackoverflow.com/questions/16645616/using-r-shiny-server-together-with-ggplot however the solution does not work in this case – dom_oh Jul 04 '13 at 22:50
  • I think you'll get more help if you can create a data.frame that resembles what you expect to get back from `poa` and strip out the ODBC stuff from your example. I don't think anyone will be able to reproduce until we know what the data's supposed to look like. Also, wouldn't be a bad idea to add library calls at the top of the scripts for the libraries you're using (shiny for both, ggplot for server, etc.) – Jeff Allen Jul 07 '13 at 14:42
  • @JeffAllen I have cleaned the server.R up now and removed ODBC and SQL stuff. I have just taken 10 records from the original POA as it was very large, and then used dput() so should be fully reproducible now. – dom_oh Jul 07 '13 at 22:52
  • It is also related to this unanswered question here http://stackoverflow.com/questions/17459703/creating-base-layer-for-ggmap-not-recognizing-data-frame where base_layer cannot find a dataframe when ggplot is called inside of a function – dom_oh Jul 09 '13 at 01:08

3 Answers3

5

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."

dom_oh
  • 867
  • 2
  • 11
  • 21
2

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.

Jeff Allen
  • 17,277
  • 8
  • 49
  • 70
  • Thanks for the answer Jeff, unfortunately I don't get the results you do. I am still getting the error `Error in ggplot(data = poa, aes(x = LON, y = LAT), environment = env) : object 'poa' not found`. I have added the output of sessionInfo to the main question to make sure we're using similar settings – dom_oh Jul 08 '13 at 10:19
0

When I change group = order to for instance group = POAOBS the app works.

Jonas Tundo
  • 6,137
  • 2
  • 35
  • 45