Skip to content

Commit

Permalink
Support for Leaflet.heat plugin. Fixes #1.
Browse files Browse the repository at this point in the history
Also fixed gradient in webgl heatmap.
  • Loading branch information
Bhaskar Karambelkar committed Feb 3, 2017
1 parent 5f42c5e commit 1085c6f
Show file tree
Hide file tree
Showing 14 changed files with 759 additions and 35 deletions.
7 changes: 7 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,22 @@ export(activateGPS)
export(addAwesomeMarkersDependencies)
export(addBootstrapDependency)
export(addCSV)
export(addCSVHeatmap)
export(addControlGPS)
export(addDrawToolbar)
export(addFullscreenControl)
export(addGPX)
export(addGPXHeatmap)
export(addGeoJSONChoropleth)
export(addGeoJSONHeatmap)
export(addGeoJSONv2)
export(addGeodesicPolylines)
export(addGreatCircles)
export(addHash)
export(addHeatmap)
export(addKML)
export(addKMLChoropleth)
export(addKMLHeatmap)
export(addMeasurePathToolbar)
export(addPulseMarkers)
export(addSearchMarker)
Expand All @@ -29,6 +34,7 @@ export(addWebGLGPXHeatmap)
export(addWebGLGeoJSONHeatmap)
export(addWebGLHeatmap)
export(addWebGLKMLHeatmap)
export(clearHeatmap)
export(clearWebGLHeatmap)
export(csvParserOptions)
export(deactivateGPS)
Expand All @@ -53,6 +59,7 @@ export(pulseIconList)
export(pulseIcons)
export(removeControlGPS)
export(removeDrawToolbar)
export(removeHeatmap)
export(removeStyleEditor)
export(removeWebGLHeatmap)
export(searchMarkersOptions)
Expand Down
220 changes: 220 additions & 0 deletions R/heatmap.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@

# Source https://github.com/Leaflet/Leaflet.heat
heatmapDependency <- function() {
list(
htmltools::htmlDependency(
"Leaflet.heat",version = "0.1.0",
system.file("htmlwidgets/lib/heat", package = "leaflet.extras"),
script = c("leaflet-heat.js", "heat-bindings.js")
)
)
}

#' Add a heatmap
#' @param map the map widget.
#' @param lng a numeric vector of longitudes, or a one-sided formula of the form
#' \code{~x} where \code{x} is a variable in \code{data}; by default (if not
#' explicitly provided), it will be automatically inferred from \code{data} by
#' looking for a column named \code{lng}, \code{long}, or \code{longitude}
#' (case-insensitively)
#' @param lat a vector of latitudes or a formula (similar to the \code{lng}
#' argument; the names \code{lat} and \code{latitude} are used when guessing
#' the latitude column from \code{data})
#' @param intensity intensity of the heat. A vector of numeric values or a formula.
#' @param minOpacity minimum opacity at which the heat will start
#' @param max maximum point intensity. The default is \code{1.0}
#' @param radius radius of each "point" of the heatmap. The default is
#' \code{25}.
#' @param blur amount of blur to apply. The default is \code{15}.
#' \code{blur=1} means no blur.
#' @param gradient palette name from \code{RColorBrewer} or an array of
#' of colors to be provided to \code{\link{colorNumeric}}, or
#' a color mapping function returned from \code{colorNumeric}
#' @param cellSize the cell size in the grid. Points which are closer
#' than this may be merged. Defaults to `radius / 2`.s
#' Set to `1` to do almost no merging.
#' @param layerId the layer id
#' @param group the name of the group the newly created layers should belong to
#' (for \code{\link{clearGroup}} and \code{\link{addLayersControl}} purposes).
#' Human-friendly group names are permitted--they need not be short,
#' identifier-style names. Any number of layers and even different types of
#' layers (e.g. markers and polygons) can share the same group name.
#' @param data the data object from which the argument values are derived; by
#' default, it is the \code{data} object provided to \code{leaflet()}
#' initially, but can be overridden
#' @rdname heatmap
#' @export
addHeatmap = function(
map, lng = NULL, lat = NULL, intensity = NULL, layerId = NULL, group = NULL,
minOpacity = 0.05,
max = 1.0, radius = 25,
blur = 15, gradient = NULL, cellSize = NULL,
data = leaflet::getMapData(map)
) {
map$dependencies <- c(map$dependencies,
heatmapDependency())

#convert gradient to expected format from leaflet
if (!is.null(gradient) && !is.function(gradient)) {
gradient <- colorNumeric( gradient, 0:1 )
gradient <- as.list(gradient(0:20 / 20))
names(gradient) <- as.character(0:20 / 20)
}

pts = leaflet::derivePoints(
data, lng, lat, missing(lng), missing(lat), "addHeatmap")

if(is.null(intensity)) {
points <- cbind(pts$lat, pts$lng)
} else {
if(inherits(intensity,'formula')) {
intensity <- eval(intensity[[2]], data, environment(intensity))
}
points <- cbind(pts$lat, pts$lng, intensity)
}

leaflet::invokeMethod(
map, data, 'addHeatmap', points,
layerId, group,
leaflet::filterNULL(list(
minOpacity = minOpacity,
max = max,
radius = radius,
blur = blur,
gradient = gradient,
cellSize = cellSize
))
) %>% leaflet::expandLimits(pts$lat, pts$lng)
}

#' Adds a heatmap with data from a GeoJSON/TopoJSON file/url
#' @param geojson The geojson or topojson url or contents as string.
#' @param intensityProperty The property to use for determining the intensity at a point.
#' Can be a 'string' or a JS function, or NULL.
#' @rdname heatmap
#' @export
addGeoJSONHeatmap = function(
map, geojson, layerId = NULL, group = NULL,
intensityProperty = NULL,
minOpacity = 0.05,
max = 1.0, radius = 25,
blur = 15, gradient = NULL, cellSize = NULL
) {
map$dependencies <- c(map$dependencies, omnivoreDependencies())
map$dependencies <- c(map$dependencies, heatmapDependency())

leaflet::invokeMethod(
map, leaflet::getMapData(map),
'addGeoJSONHeatmap', geojson, intensityProperty,
layerId, group,
leaflet::filterNULL(list(
minOpacity = minOpacity,
max = max,
radius = radius,
blur = blur,
gradient = gradient,
cellSize = cellSize
)))
}

#' Adds a heatmap with data from a KML file/url
#' @param kml The KML url or contents as string.
#' @rdname heatmap
#' @export
addKMLHeatmap = function(
map, kml, layerId = NULL, group = NULL,
intensityProperty = NULL,
minOpacity = 0.05,
max = 1.0, radius = 25,
blur = 15, gradient = NULL, cellSize = NULL
) {
map$dependencies <- c(map$dependencies, omnivoreDependencies())
map$dependencies <- c(map$dependencies, heatmapDependency())

leaflet::invokeMethod(
map, leaflet::getMapData(map),
'addKMLHeatmap', kml, intensityProperty,
layerId, group,
leaflet::filterNULL(list(
minOpacity = minOpacity,
max = max,
radius = radius,
blur = blur,
gradient = gradient,
cellSize = cellSize
)))
}

#' Adds a heatmap with data from a CSV file/url
#' @param csv The CSV url or contents as string.
#' @param csvParserOptions options for parsing the CSV.
#' Use \code{\link{csvParserOptions}}() to supply csv parser options.
#' @rdname heatmap
#' @export
addCSVHeatmap = function(
map, csv, csvParserOptions, layerId = NULL, group = NULL,
intensityProperty = NULL,
minOpacity = 0.05,
max = 1.0, radius = 25,
blur = 15, gradient = NULL, cellSize = NULL
) {
map$dependencies <- c(map$dependencies, omnivoreDependencies())
map$dependencies <- c(map$dependencies, heatmapDependency())

leaflet::invokeMethod(
map, leaflet::getMapData(map),
'addCSVHeatmap', csv, intensityProperty,
layerId, group,
leaflet::filterNULL(list(
minOpacity = minOpacity,
max = max,
radius = radius,
blur = blur,
gradient = gradient,
cellSize = cellSize
)),
csvParserOptions)
}

#' Adds a heatmap with data from a GPX file/url
#' @param gpx The GPX url or contents as string.
#' @rdname heatmap
#' @export
addGPXHeatmap = function(
map, gpx, layerId = NULL, group = NULL,
intensityProperty = NULL,
minOpacity = 0.05,
max = 1.0, radius = 25,
blur = 15, gradient = NULL, cellSize = NULL
) {
map$dependencies <- c(map$dependencies, omnivoreDependencies())
map$dependencies <- c(map$dependencies, heatmapDependency())

leaflet::invokeMethod(
map, leaflet::getMapData(map),
'addGPXHeatmap', gpx, intensityProperty,
layerId, group,
leaflet::filterNULL(list(
minOpacity = minOpacity,
max = max,
radius = radius,
blur = blur,
gradient = gradient,
cellSize = cellSize
)))
}


#' removes the heatmap
#' @rdname heatmap
#' @export
removeHeatmap = function(map, layerId) {
leaflet::invokeMethod(map, leaflet::getMapData(map), 'removeHeatmap', layerId)
}

#' clears the heatmap
#' @rdname heatmap
#' @export
clearHeatmap = function(map) {
leaflet::invokeMethod(map, NULL, 'clearHeatmap')
}
13 changes: 11 additions & 2 deletions R/webGLHeatmap.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ webGLHeatmapDependency <- function() {
htmltools::htmlDependency(
"webgl-heatmap",version = "0.1.0",
system.file("htmlwidgets/lib/webgl-heatmap", package = "leaflet.extras"),
script = c("webgl-heatmap.js", "webgl-heatmap-leaflet.js", "webgl-heatmap-bindings.js")
script = c("webgl-heatmap.js", "webgl-heatmap-leaflet.js",
"webgl-heatmap-bindings.js"),
attachment = c("skyline" = "skyline-gradient.png",
"deep-sea" = "deep-sea-gradient.png")
)
)
}
Expand All @@ -30,7 +33,8 @@ webGLHeatmapDependency <- function() {
#' @param size in meters or pixels
#' @param units either 'm' or 'px'
#' @param opacity for the canvas element
#' @param gradientTexture image url or image
#' @param gradientTexture Alternative colors for heatmap.
#' allowed values are "skyline", "deep-sea"
#' @param alphaRange adjust transparency by changing to value between 0 and 1
#' @param data the data object from which the argument values are derived; by
#' default, it is the \code{data} object provided to \code{leaflet()}
Expand All @@ -49,6 +53,11 @@ addWebGLHeatmap = function(
map$dependencies <- c(map$dependencies,
webGLHeatmapDependency())

if(!is.null(gradientTexture) &&
!gradientTexture %in% c("skyline", "deep-sea")) {
stop("Only allowed values for gradientTexture are 'skyline' and 'deep-sea'")
}

pts = leaflet::derivePoints(
data, lng, lat, missing(lng), missing(lat), "addWebGLHeatmap")

Expand Down
41 changes: 14 additions & 27 deletions inst/examples/heatmaps.R
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
library(leaflet.extras)
library(magrittr)

#' Just by number of quakes
#' Quakes
#'
#'
leaflet(quakes) %>% addProviderTiles(providers$CartoDB.DarkMatter) %>%
addWebGLHeatmap(lng=~long, lat=~lat, size = 60000)


#' <br/><br/>By magnitude
#'
#'
leaflet(quakes) %>% addProviderTiles(providers$CartoDB.DarkMatter) %>%
addWebGLHeatmap(lng=~long, lat=~lat, intensity = ~mag, size=60000)
setView( 178, -20, 5 ) %>%
addHeatmap(lng = ~long, lat = ~lat, intensity = ~mag,
blur = 20, max = 0.05, radius = 15)

#' <br/><br/>
#' Roughly 1500 points dataset
Expand All @@ -24,19 +19,17 @@ v8$source(jsURL)
geoJson <- geojsonio::as.json(v8$get('pubsGeoJSON'))
spdf <- geojsonio::geojson_sp(geoJson)

#' <br/><br/>Size in meters
#'
#'
leaflet(spdf) %>%
addProviderTiles(providers$Thunderforest.TransportDark) %>%
addWebGLHeatmap(size=60000)
addHeatmap(blur = 20, max = 0.05, radius = 15)

#' <br/><br/>Size in Pixels
#'
#'
leaflet(spdf) %>%
addProviderTiles(providers$Thunderforest.TransportDark) %>%
addWebGLHeatmap(size=25,units='px')
addProviderTiles(providers$Thunderforest.Transport) %>%
addHeatmap(blur = 20, max = 0.05, radius = 15, gradient = 'Greys')

#' <br/><br/>10,000 points
#'
Expand All @@ -46,7 +39,7 @@ v8 <- V8::v8()
v8$source(jsURL)

df <- data.frame(v8$get('addressPoints'), stringsAsFactors = F) %>%
set_colnames(c('lat', 'lng', 'intensity')) %>%
magrittr::set_colnames(c('lat', 'lng', 'intensity')) %>%
dplyr::mutate(
lat = as.numeric(lat),
lng = as.numeric(lng)
Expand All @@ -57,14 +50,8 @@ df <- data.frame(v8$get('addressPoints'), stringsAsFactors = F) %>%
#'
leaflet(df) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addWebGLHeatmap(lng=~lng, lat=~lat,size=1000)

#' <br/><br/>Size in Pixels
#'
#'
leaflet(df) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addWebGLHeatmap(lng=~lng, lat=~lat,size=20,units='px')
addHeatmap(lng=~lng, lat=~lat,
blur = 20, max = 0.05, radius = 15 )

#' <br/><br/>

Expand All @@ -77,7 +64,7 @@ london.crimes <- suppressMessages(
~readr::read_csv(.) %>%
dplyr::select(Latitude, Longitude) %>%
dplyr::filter(!is.na(Latitude))) %>%
set_names(basename(Sys.glob(
magrittr::set_names(basename(Sys.glob(
paste0(system.file('examples/data/London-Crimes', package='leaflet.extras'),
'/2016*')))))

Expand All @@ -88,11 +75,11 @@ purrr::walk(
names(london.crimes),
function(month) {
leaf <<- leaf %>%
addWebGLHeatmap(
addHeatmap(
data = london.crimes[[month]],
layerId = month, group = month,
lng=~Longitude, lat=~Latitude,size=40,units='px'
)
lng=~Longitude, lat=~Latitude,
blur = 20, max = 0.05, radius = 15)
})

leaf %>%
Expand Down
Loading

0 comments on commit 1085c6f

Please sign in to comment.