First, with the original question, a better way to get a file suitable file for leaflet-shiny. Here's how you can parse a SpatialDataFrame. NOTE: Before you do this you'll need to convert it to Polygons from MultiPolygons and simplify the files. I noticed that the maps in the maps package have far fewer lat-longs than raw CENSUS shapefiles read into SpatialDataFrames. I highly recommend PostGIS, specifically ST_Simplify, ST_GeometryN, ST_Union, ST_Dump, ST_MakeValid, and ST_SnapToGrid. Try this out on ZCTAs and let us know how it goes, it takes very little time on counties that have been previously simplified, but I'm unsure about ZCTAs.
library(sp);library(maptools);library(maps)
getMap <- function(sdf) {
getLatLongs <- function(polygon) {
currentPolygon <- slot(polygon,"Polygons")
# only single Polygons
x <- slot(currentPolygon[[1]],"coords")[,1]
y <- slot(currentPolygon[[1]],"coords")[,2]
return(list("x"=c(x,NA),"y"=c(y,NA)))
}
allLatLongs <- do.call(rbind.data.frame, lapply(slot(sdf, "polygons"), function(x) getLatLongs(x)))
# may need to change NAME to what you want to display
names <- as.vector(slot(sdf,"data")$NAME)
# there's probably a better way to handle the inserted NAs for calculating range
range <- c(range(allLatLongs$x[!is.na(allLatLongs$x)]),range(allLatLongs$y[!is.na(allLatLongs$y)])) prepped <- list("x"=allLatLongs$x,"y"=allLatLongs$y,"range"=range,"names"=names)
attr(prepped , "class") <- "map"
return(prepped)
}
sdfStates <- readShapeSpatial('tl_2013_us_state.shp',proj4string = CRS(Proj4))
saveRDS(getMap(sdfStates),'states.rds')
Now what you'll also really want to do to make the following example complete is to calculate the range of each of the polygons so you can later subset the displayed polygons to the polygons within the viewable map. I have not done this yet, but one method would be to calculate bounds for each polygon and save that as a separate data.frame. Then you would calculate a buffer around input$map_bounds, subset the aforementioned data.frame and let the program reactively run the getMap function, which I imagine will be quick for smaller numbers of polygons. A cleaner route would be to let R access the simplied polygons loaded in PostGIS and conduct a ST_Intersects join with a buffered geometry defined by input$map_bounds. If someone gets around to this, please let me know how fast it is for huge sets of polygons like the ZCTAs.
Anyways, this example shows you how to do the changing polygons by input$map_zoom part:
ui.R
library(shiny);library(leaflet)
shinyUI(navbarPage("Zoom Changing Maps",
tabPanel("Map",
leafletMap("map", "100%", 900,
options=list(center = c(40, -98.85),zoom = 5)))))
server.R
library(shiny);library(leaflet);library(maps);library(RColorBrewer)
states <- map("state", plot=FALSE, fill=TRUE)
counties <- map("county", plot=FALSE, fill=TRUE)
shinyServer(function(input, output, session) {
map <- createLeafletMap(session, "map")
output$map <- reactive(TRUE)
zoomLevel <- reactive({
zoom <- ifelse(is.null(input$map_zoom),5,input$map_zoom)
zoomLevel <- ifelse(zoom > 6,"counties","states")
return(zoomLevel)})
mapData <- reactive({
zoomLevel()
isolate({
if(zoomLevel()=="counties") {mapData <- counties} else {mapData <- states}
return(mapData)})})
observe({
mapData <- mapData()
isolate({session$onFlushed(once=TRUE, function() {
map$clearShapes()
map$addPolygon(as.numeric(mapData$y),as.numeric(mapData$x), mapData$names,
lapply(brewer.pal(9, "Blues"), function(x) {
list(fillColor = x)
}),
list(fill=TRUE, fillOpacity=.5,
stroke=TRUE, opacity=1, color="blue", weight=1
))})})})})