ggiraph
for simple effectsgeom_*_interactive
with hover, tooltip, onClick aesthetics, plot with ggiraph
.library(ggplot2); suppressPackageStartupMessages(library(ggiraph))africa.spdf <- methods::as(africa, 'Spatial')africa.spdf@data$id <- row.names(africa.spdf@data)africa.tidy <- broom::tidy(africa.spdf)africa.tidy <- dplyr::left_join(africa.tidy, africa.spdf@data, by='id')g <- ggplot(africa.tidy) + geom_polygon_interactive( color='black', aes(long, lat, group=group, fill=internet.usage.2015, tooltip=sprintf("%s<br/>%s",iso_a3,internet.usage.2015))) + ggthemes::theme_map() + colormap::scale_fill_colormap( colormap=colormap::colormaps$copper, reverse = T) + labs(title='Internet Usage in Africa in 2015', subtitle='As Percent of Population', caption='Source: World Bank Open Data.')ggiraph(code=print(g))
ggiraph
plotplotly
htmlwidgets
based wrapper for plot.ly JavaScript library.
Extremely easy and yet powerful. Commercial use requires a paid version.
Plays nicely with ggplot2
library(plotly)usa <- albersusa::usa_sf("laea")usd <- crosstalk::SharedData$new(usa)p <- ggplot(usd) + geom_sf(aes(fill = pop_2010))ggplotly(p) %>% highlight( "plotly_hover", selected = attrs_selected(line = list(color = "black")))
plotly
plot## Setting the `off` event (i.e., 'plotly_doubleclick') to match the `on` event (i.e., 'plotly_hover'). You can change this default via the `highlight()` function.
rBokeh
Port of Python's bokeh library.
Has its own syntax.
library(maps)data(world.cities)library(rbokeh)caps <- dplyr::filter(world.cities, capital == 1)caps$population <- prettyNum(caps$pop, big.mark = ",")suppressWarnings(figure(width = 800, height = 450, padding_factor = 0) %>% ly_map("world", col = "gray") %>% ly_points(long, lat, data = caps, size = 5, hover = c(name, country.etc, population)))
rBokeh
plothighcharter
download_map_data <- memoise::memoise(highcharter::download_map_data)get_data_from_map <- memoise::memoise(highcharter::get_data_from_map)hcmap <- memoise::memoise(highcharter::hcmap)mapdata <- get_data_from_map(download_map_data("countries/au/au-all"))data_fake <- mapdata %>% dplyr::select(code = `hc-a2`) %>% dplyr::mutate(value = 1e5 * abs(rt(nrow(.), df = 10)))hcmap("countries/au/au-all", data = data_fake, value = "value", joinBy = c("hc-a2", "code"), name = "Fake data", dataLabels = list(enabled = TRUE, format = '{point.name}'), borderColor = "#FAFAFA", borderWidth = 0.1, tooltip = list(valueDecimals = 2, valuePrefix = "$", valueSuffix = " AUD")) %>% highcharter::hc_title(text="Economy Down Under")
highcharter
plot
leaflet
Mother of all interactive mappings!
Extremely powerful and Extremely flexible.
Current State
leaflet
: Developed and actively maintained by RStudio.
leaflet.extras
, leaflet.esri
: Developed and maintained by yours truly. Provides additional plotting options using leaflet plugins.
mapview
, mapedit
: Developed and maintained by the good guys at r-spatial. Allow interactive exploratory spatial analysis.
There is another unrelated project leafletR
CRAN Page, but really don't know much about it.
leaflet
One Page Summaryleaflet(data) | leafletProxy() %>% setView(lat, lon, zoom) # Initial View OR fitBounds(lat_se, lon_se, latnw, lon_nw) # Initial Bounds setMaxBounds(lat_se, lon_se, latnw, lon_nw) # Max Bounds addTiles() | addProviderTiles() | addWMSTiles() #Tiles addMarkers() | addCircleMarkers() | addAwesomeMarkers() | addLabelOnlyMarkers() # Markers addPolylines() | addCircles() | addRectangles() | addPolygons() # Shapes addRasterImage(image) # Raster Data addLegend() | addLayersControl() | addControl() # Controls
A Map is built by piping (%>%
) several add* methods.
leaflet()
/addXXX()
methods take an optional options
argument for customization.
leaflet
Base Mapsleaflet() %>% setView(lat = 50.85045, lng = 4.34878, zoom=13) %>% addTiles(group="OSM") %>% addProviderTiles(providers$CartoDB.DarkMatter, group="Dark") %>% addProviderTiles(providers$CartoDB.Positron, group="Light") %>% addLayersControl(baseGroups=c('OSM', 'Dark','Light'))
leaflet
Base Mapsleaflet
Markersleaflet(data) %>% addMarkers( lat = ~latitude, lon = ~longitude, options = markerOptions(), label=~label, labelOptions = labelOptions(), popup=~popup, popupOptions = popupOptions(), clusterOptions = clusterOptions(), group = 'Group-A') # Similarly addCircleMarkers() # Fixed scale Circles addAwesomeMarkers() # More choices for icons addLabelOnlyMarkers() # No icon
leaflet
Markersquakes.df <- quakes %>% dplyr::mutate( mag.level = cut(mag,c(3.5,4.5,5.5,6.5), labels = c('> 3.5 & <=4.5', '>4.5 & <=5.5', '>5.5 & <=6.5'))) %>% split(.$mag.level)l <- leaflet() %>% addProviderTiles(providers$Esri.OceanBasemap)names(quakes.df) %>% purrr::walk( function(df) { l <<- l %>% addMarkers(data=quakes.df[[df]], lng=~long, lat=~lat, label=~as.character(mag), popup=~as.character(mag), group = df, clusterOptions = markerClusterOptions()) })l <- l %>% addLayersControl( overlayGroups = names(quakes.df), options = layersControlOptions(collapsed = FALSE)) %>% addMiniMap(tiles = providers$Esri.OceanBasemap, width = 120, height=80)
leaflet
Markersleaflet
Shapesleaflet(data) %>% addPolygons( label=~label, labelOptions = labelOptions(), popup=~popup, popupOptions = popupOptions(), # Shape Options options = pathOptions(), weight = 1, opacity=0.8, color = "#000000", fillColor="#ff0000", fillOpacity=0.7, # Highlighting on mouse-over highlightOptions = highlightOptions( color='#00ff00', weight = 2, opacity = 1, fillOpacity = 1, bringToFront = TRUE, sendToBack = TRUE), group = 'Group-A') #Similarly addCircles() addPolylines() addRectangles()
# spdf is a sp::SpatialPolygonsDataFrameqpal <- colorQuantile(rev(viridis::viridis(5)), spdf$POPDENSITY, n=5)l <- leaflet(spdf, options = leafletOptions(attributionControl = FALSE, minzoom=1.5)) %>% addPolygons( label=~stringr::str_c( NAME, ' ', formatC(POPDENSITY, big.mark = ',', format='d')), labelOptions= labelOptions(direction = 'auto'), weight=1,color='#333333', opacity=1, fillColor = ~qpal(POPDENSITY), fillOpacity = 1, highlightOptions = highlightOptions( color='#000000', weight = 2, bringToFront = TRUE, sendToBack = TRUE) ) %>% addLegend( "topright", pal = qpal, values = ~POPDENSITY, title = htmltools::HTML("Population Density<br/>(2005)"), opacity = 1 )
rmapshaper::ms_simplify
with impunity!leaflet
Other Misc. StuffaddRasterImage
for adding raster image data. Example.
addMiniMap
for a small map inside the main map at a zoom offset. Example.
addMeasure
to measure distances/area. Example.
addGraticule
adds a graticule. Example.
addEasyButton
/addEasyButtonBar
for customized buttons Example.
addControl
for custom Control element.
addScaleBar
to display a scale.
leaflet
Projections SupportALL MAPS OF EARTH ARE WRONG! Obligatory XKCD ref.
By Default leaflet ...
Which means ...
You can't use tile services which provide tiles in non-spherical-Mercator projections.
You need to convert any vector/raster data in any non-epsg:4326 to epsg:4326 before adding to the leaflet map.
leaflet
+ Proj4LeafletEnter Proj4Leaflet a leaflet plugin allowing use of proj4js to display map in non-spherical-Mercator projection.
Basic use
leaflet(options = leafletOptions(crs = leafletCRS()))
So now you can display data/tiles in a non-spherical-Mercator projection.
But you still need to specify vector/raster data in EPSG:4326 (lat/lon) which will get internally converted to the custom projection specified.
Another caveat: You can have only one projection at a time which is set during initialization. To change projection you need to destroy and rebuild the map.
leaflet
Projections Example 1.crs.molvidde <- leafletCRS( crsClass="L.Proj.CRS", code='ESRI:53009', proj4def= '+proj=moll +lon_0=0 +x_0=0 +y_0=0 +a=6371000 +b=6371000 +units=m +no_defs', resolutions = c(65536, 32768, 16384, 8192, 4096, 2048))l <- leaflet( spdf, options = leafletOptions( maxZoom = 5, crs= crs.molvidde, attributionControl = FALSE)) %>% addGraticule(style= list(color= '#999', weight= 0.5, opacity= 1)) %>% addGraticule(sphere = TRUE, style= list(color= '#777', weight= 1, opacity= 0.25)) %>% addPolygons( label=~stringr::str_c( NAME, ' ', formatC(POPDENSITY, big.mark = ',', format='d')), labelOptions= labelOptions(direction = 'auto'), weight=1,color='#ffffff', opacity=1, fillColor = ~qpal(POPDENSITY), fillOpacity = 1, highlightOptions = highlightOptions( color='#000000', weight = 2, bringToFront = TRUE, sendToBack = TRUE))
spdf <- rmapshaper::ms_simplify(albersusa::usa_composite())pal <- colorNumeric(palette = "Blues", domain = spdf@data$pop_2014)crs.laea <- leafletCRS( crsClass="L.Proj.CRS", code='EPSG:2163', proj4def='+proj=laea +lat_0=45 +lon_0=-100 +x_0=0 +y_0=0 +a=6370997 +b=6370997 +units=m +no_defs', resolutions = c(65536, 32768, 16384, 8192, 4096, 2048,1024, 512, 256, 128))l <- leaflet( options= leafletOptions( worldCopyJump = FALSE, crs=crs.laea, attributionControl = FALSE)) %>% addPolygons( data=spdf, label=~stringr::str_c( name, ' ', formatC(pop_2014, big.mark = ',', format='d')), labelOptions= labelOptions(direction = 'auto'), weight = 1, color = "#000000", fillColor=~pal(pop_2014), fillOpacity=0.7, highlightOptions = highlightOptions( color='#ff0000', opacity = 1, weight = 2, fillOpacity = 1, bringToFront = TRUE, sendToBack = TRUE))
leaflet
+ tilegramsR
l <- leaflet( options=leafletOptions( crs = leafletCRS("L.CRS.Simple"), minZoom = -2, maxZoom = -2, dragging = FALSE, zoomControl = FALSE, attributionControl = FALSE)) %>% addPolygons( data=FiveThirtyEightElectoralCollege, weight=1,color='#000000', fillOpacity = 0.5, opacity=0.2, fillColor= ~factpal(state)) %>% addPolygons( data=FiveThirtyEightElectoralCollege.states, group = 'states', weight=2,color='#000000', fill = T, opacity = 1, fillOpacity = 0, highlightOptions = highlightOptions(weight = 4)) %>% addLabelOnlyMarkers( data=FiveThirtyEightElectoralCollege.centers, label = ~as.character(state), labelOptions = labelOptions( noHide = 'T', textOnly = T, offset=c(-8,-20), textsize = '12px'))
leaflet.esri
Add-on packages for leaflet
. Developed by yours truly to integrate the plethora of leaflet plugins available.
Published to CRAN just before UserR! 2017.
Actively being developed and maintained. Contributions welcome.
leaflet
package will be stable and leaflet.extras
will be very dynamic.
Add/Modify/delete/style markers/shapes using Leaflet.Draw.
Add GeoJSON, TopoJSON, KML, GPX, CSV files directly.
Create Heatmap from point data.
Search Markers. Geo-locate using OSM Nominatum API.
Tiles Caching, GPS, and many more!
library(leaflet.extras)fName <- system.file('extdata','crimes_by_district.topojson', package='user2017.geodataviz')l <- leaflet() %>% addBootstrapDependency() %>% setView(-75.14, 40, zoom = 11) %>% addProviderTiles(providers$CartoDB.Positron) %>% addGeoJSONChoropleth( readr::read_file(fName), valueProperty ='incidents', scale = 'OrRd', mode='q', steps = 5, padding = c(0.2,0), popupProperty = propstoHTMLTable( props = c('dist_numc', 'location', 'incidents', '_feature_id_string'), table.attrs = list(class='table table-striped table-bordered'),drop.na = T), labelProperty = JS('function(feature){return "WARD: " + feature.properties.dist_numc;}'), color='#ffffff', weight=1, fillOpacity = 0.7, highlightOptions = highlightOptions( fillOpacity=1, weight=2, opacity=1, color='#000000', bringToFront=TRUE, sendToBack = TRUE), legendOptions = legendOptions(title='Crimes', position='topright'))
crosstalk
Inter-widget communicationPure JavaScript solution works anywhere a normal htmlwidget
will work.1
Works on the concept of shared data between widgets.
library(crosstalk)library(leaflet)library(DT)# Wrap data frame in SharedDatasd <- SharedData$new(quakes[sample(nrow(quakes), 100),])# Create a filter inputfilter_slider("mag", "Magnitude", sd, column=~mag, step=0.1, width=250)# Use SharedData like a dataframe with Crosstalk-enabled widgetsbscols( leaflet(sd) %>% addTiles() %>% addMarkers(), datatable(sd, extensions="Scroller", style="bootstrap", class="compact", width="100%", options=list(deferRender=TRUE, scrollY=300, scroller=TRUE)))
1: Documentation: https://rstudio.github.io/crosstalk/using.html
Shiny allows you to build visualizations dynamically, be they static or interactive.
You need a Shiny server to run Shiny apps. Free/Commercial versions available from RStudio.
Static visualizations need to destroyed and rebuilt every time the input data for the viz changes.
Interactive visualizations (i.e. htmlwidgets
) however can support manipulating existing viz in response to shiny
events.
Plotly
, leaflet
etc. support changing an existing map.
manipulateWidget
package allows building htmlwidgets
which can respond to input events.
leaflet
+ shiny
Use leafletProxy()
to update already existing map.
Use clear*
methods to remove stuff already on a map.
leaflet package traps many leaflet events and makes then available as shiny events.
Use observeEvent(input$<MAP_ID>_<EVENT_NAME>)
to act on these events.
leaflet
+ shiny
library(shiny); library(leaflet)r_colors <- rgb(t(col2rgb(colors()) / 255))names(r_colors) <- colors()ui <- fluidPage( leafletOutput("mymap"), p(), actionButton("recalc", "New points"))server <- function(input, output, session) { points <- eventReactive(input$recalc, { cbind(rnorm(40) * 2 + 13, rnorm(40) + 48) }, ignoreNULL = FALSE) output$mymap <- renderLeaflet({ leaflet() %>% addProviderTiles(providers$Stamen.TonerLite, options = providerTileOptions(noWrap = TRUE)) %>% addMarkers(data = points()) })}shinyApp(ui, server)
Thus marks the end of 'Geospatial visualization in R' tutorial by Bhaskar V. Karambelkar, the first of his name!
Wait there's more.
So many people to thank....
leaflet
author.mapview
author and my virtual GIS guru.mapedit
and many more widgets author and collaborator for leaflet
and leaflet.extras
tigris
/tidycensus
, and super helpful guy.sf
and many many R GIS pacakges.ggiraph
for simple effectsgeom_*_interactive
with hover, tooltip, onClick aesthetics, plot with ggiraph
.library(ggplot2); suppressPackageStartupMessages(library(ggiraph))africa.spdf <- methods::as(africa, 'Spatial')africa.spdf@data$id <- row.names(africa.spdf@data)africa.tidy <- broom::tidy(africa.spdf)africa.tidy <- dplyr::left_join(africa.tidy, africa.spdf@data, by='id')g <- ggplot(africa.tidy) + geom_polygon_interactive( color='black', aes(long, lat, group=group, fill=internet.usage.2015, tooltip=sprintf("%s<br/>%s",iso_a3,internet.usage.2015))) + ggthemes::theme_map() + colormap::scale_fill_colormap( colormap=colormap::colormaps$copper, reverse = T) + labs(title='Internet Usage in Africa in 2015', subtitle='As Percent of Population', caption='Source: World Bank Open Data.')ggiraph(code=print(g))
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |