The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
rDeckgl provides R bindings for deck.gl 9.2.2, a WebGL-powered framework for visualizing large datasets. This vignette demonstrates the basic usage of rDeckgl with working examples.
This example demonstrates deck.gl rendering performance with ~10,000 points using a viridis color palette:
library(rDeckgl)
library(scales)
# Generate a spaced-out grid of ~10K points around San Francisco
set.seed(42)
grid_size <- 100L
lon_seq <- seq(-122.515, -122.355, length.out = grid_size)
lat_seq <- seq(37.70, 37.82, length.out = grid_size)
grid <- expand.grid(lon = lon_seq, lat = lat_seq)
grid$value <- rnorm(nrow(grid), mean = 0, sd = 1)
# Add light jitter
jitter_strength <- 0.0007
grid$lon <- grid$lon + runif(nrow(grid), -jitter_strength, jitter_strength)
grid$lat <- grid$lat + runif(nrow(grid), -jitter_strength, jitter_strength)
grid$radius <- runif(nrow(grid), 25, 80)
points_data <- grid
# Map 'value' to viridis color palette
domain_range <- range(points_data$value)
palette_fun <- col_numeric(viridis_pal(option = "B")(256), domain_range)
rgba <- col2rgb(palette_fun(points_data$value))
points_data$color_r <- rgba[1, ]
points_data$color_g <- rgba[2, ]
points_data$color_b <- rgba[3, ]
spec <- list(
`@@type` = "DeckGL",
initialViewState = list(
longitude = mean(range(points_data$lon)),
latitude = mean(range(points_data$lat)),
zoom = 11.5,
pitch = 20,
bearing = 0
),
tooltip = list(
html = "<div><strong>Value:</strong> {value}<br/><strong>Radius:</strong> {radius} m</div>",
style = list(
backgroundColor = "#0e1119",
color = "#FFFFFF",
fontSize = "12px"
)
),
views = list(
list(
`@@type` = "MapView",
controller = TRUE,
mapStyle = "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json"
)
),
layers = list(
list(
`@@type` = "ScatterplotLayer",
id = "scatterplot",
data = list(
type = "duckdb",
query = "SELECT lon, lat, radius, value, color_r, color_g, color_b FROM points"
),
getPosition = "@@=[lon, lat]",
getRadius = "@@=radius",
getFillColor = "@@=[color_r, color_g, color_b, 200]",
pickable = TRUE,
autoHighlight = TRUE,
radiusUnits = "meters"
)
)
)
deckgl(
spec = spec,
data = list(points = points_data),
width = "100%",
height = "600px"
)This example uses remote data from the deck.gl website to create a 3D hexagon heatmap:
library(rDeckgl)
hexagon_spec <- list(
initialViewState = list(
longitude = -1.4157267858730052,
latitude = 52.232395363869415,
zoom = 6.6,
pitch = 40.5,
bearing = -27.396674584323023
),
views = list(
list(
`@@type` = "MapView",
controller = TRUE,
mapStyle = "https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json"
)
),
layers = list(
list(
`@@type` = "HexagonLayer",
id = "heatmap",
data = "https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv",
coverage = 1,
pickable = TRUE,
elevationRange = c(0, 3000),
elevationScale = 50,
extruded = TRUE,
getPosition = "@@=[lng,lat]",
radius = 1000,
colorRange = list(
c(1, 152, 189),
c(73, 227, 206),
c(216, 254, 181),
c(254, 237, 177),
c(254, 173, 84),
c(209, 55, 78)
)
)
)
)
deckgl(
spec = hexagon_spec,
width = "100%",
height = "600px"
)This example reproduces the kind of visualization used in spatial omics analysis — cell-boundary polygons rendered via the fast GeoArrow path, with a centroid scatter layer coloured by simulated expression level. All data is generated in-memory; no external files are required.
library(rDeckgl)
library(DBI)
library(duckdb)
# ── 1. Generate mock spatial omics data ────────────────────────────────────────
set.seed(42)
n_cells <- 300
field_w <- 8000 # microns
field_h <- 6000
# Random centroids
cx <- runif(n_cells, 200, field_w - 200)
cy <- runif(n_cells, 200, field_h - 200)
# Simulated per-cell metrics
total_expr <- pmax(0, round(rnorm(n_cells, 200, 80)))
cluster <- paste0("c", sample.int(6L, n_cells, replace = TRUE))
# Colour centroids by cluster
pal <- grDevices::hcl.colors(6, "Dark 3")
rgb_ <- grDevices::col2rgb(pal[as.integer(factor(cluster))])
cr <- as.integer(rgb_[1, ])
cg <- as.integer(rgb_[2, ])
cb <- as.integer(rgb_[3, ])
centroids <- data.frame(
cell_ID = sprintf("CELL_%04d", seq_len(n_cells)),
x = cx, y = cy,
total_expr, cluster, r = cr, g = cg, b = cb
)
# Build irregular hexagonal WKT polygons
angles <- seq(0, 2 * pi, length.out = 7)[-7] # 6 vertices
wkt_polygons <- vapply(seq_len(n_cells), function(i) {
radius <- runif(1, 80, 200)
jitter <- runif(6, 0.80, 1.20)
vx <- cx[i] + radius * jitter * cos(angles)
vy <- cy[i] + radius * jitter * sin(angles)
pts <- paste(sprintf("%f %f", c(vx, vx[1]), c(vy, vy[1])), collapse = ", ")
sprintf("POLYGON((%s))", pts)
}, character(1))
polygon_df <- data.frame(
cell_ID = centroids$cell_ID,
wkt = wkt_polygons
)
# ── 2. Load data into in-memory DuckDB with spatial extension ──────────────────
con <- dbConnect(duckdb(), dbdir = ":memory:")
for (sql in c("INSTALL spatial", "LOAD spatial",
"INSTALL nanoarrow FROM community", "LOAD nanoarrow",
"CALL register_geoarrow_extensions()")) {
try(dbExecute(con, sql), silent = TRUE)
}
dbWriteTable(con, "poly_raw", polygon_df, overwrite = TRUE)
dbExecute(con, "
CREATE TABLE cells AS
SELECT cell_ID,
ST_GeomFromText(wkt) AS geometry
FROM poly_raw
WHERE wkt IS NOT NULL
")
# ── 3. Build the deck.gl spec ─────────────────────────────────────────────────
# Orthographic (Cartesian) view — same as spatial omics viewers
cx_mid <- field_w / 2
cy_mid <- field_h / 2
zoom <- log2(600 / field_w) # fit ~600 px wide
polygon_query <- "SELECT cell_ID, geometry FROM cells WHERE geometry IS NOT NULL"
spec <- list(
views = list(
list(
`@@type` = "OrthographicView",
controller = TRUE
)
),
initialViewState = list(
target = list(cx_mid, cy_mid, 0),
zoom = zoom,
minZoom = -5,
maxZoom = 8
),
layers = list(
list(
`@@type` = "GeoArrowSolidPolygonLayer",
id = "cell-polygons",
data = list(
type = "duckdb",
query = polygon_query,
format = "geoarrow"
),
geometryColumn = "geometry",
getFillColor = c(100L, 80L, 220L, 100L),
stroked = FALSE,
coordinateSystem = "@@#COORDINATE_SYSTEM.CARTESIAN",
positionFormat = "XY",
pickable = TRUE
),
list(
`@@type` = "ScatterplotLayer",
id = "cell-centroids",
data = list(
type = "duckdb",
query = "SELECT cell_ID, x, y, r, g, b FROM centroids"
),
getPosition = "@@=[x, y]",
getFillColor = "@@=[r, g, b]",
getRadius = 2,
radiusUnits = "pixels",
coordinateSystem = "@@#COORDINATE_SYSTEM.CARTESIAN",
positionFormat = "XY",
opacity = 0.85,
pickable = TRUE
)
)
)
widget <- deckgl(
spec = spec,
con = con,
data = list(centroids = centroids),
width = "100%",
height = "600px"
)
DBI::dbDisconnect(con)
widgetrDeckgl automatically creates an in-memory DuckDB database for efficient data handling:
data
argumenttype = "duckdb" and SQL queries
in your specUse rDeckgl in Shiny applications with reactive bindings. Here’s a complete working example:
library(shiny)
library(rDeckgl)
library(scales)
ui <- fluidPage(
titlePanel("Deck.gl in Shiny"),
deckglOutput("myDeckgl", width = "100%", height = "600px")
)
server <- function(input, output, session) {
output$myDeckgl <- renderDeckgl({
# Generate sample data
set.seed(42)
n_points <- 500
points_data <- data.frame(
lon = runif(n_points, -122.5, -122.3),
lat = runif(n_points, 37.7, 37.85),
value = rnorm(n_points),
radius = runif(n_points, 50, 150)
)
# Add colors
palette_fun <- col_numeric(viridis_pal(option = "B")(256), range(points_data$value))
rgba <- col2rgb(palette_fun(points_data$value))
points_data$color_r <- rgba[1, ]
points_data$color_g <- rgba[2, ]
points_data$color_b <- rgba[3, ]
# Create spec
spec <- list(
`@@type` = "DeckGL",
initialViewState = list(
longitude = -122.4,
latitude = 37.78,
zoom = 11,
pitch = 0,
bearing = 0
),
views = list(
list(
`@@type` = "MapView",
controller = TRUE,
mapStyle = "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json"
)
),
layers = list(
list(
`@@type` = "ScatterplotLayer",
id = "points",
data = list(
type = "duckdb",
query = "SELECT lon, lat, radius, color_r, color_g, color_b FROM points"
),
getPosition = "@@=[lon, lat]",
getRadius = "@@=radius",
getFillColor = "@@=[color_r, color_g, color_b, 180]",
pickable = TRUE,
radiusUnits = "meters"
)
)
)
deckgl(spec = spec, data = list(points = points_data))
})
}
shinyApp(ui, server)Note: This example generates data within the server
function. For reactive/dynamic visualizations, wrap your data generation
in reactive() and use observe() or
observeEvent() to update the visualization based on user
input.
The following self-contained scripts mirror our internal GeoArrow
tests. Copy them into your own session—no local Examples/
folder required.
library(rDeckgl)
set.seed(42)
n_points <- 1000
points_data <- data.frame(
id = 1:n_points,
lon = runif(n_points, -122.5, -122.3),
lat = runif(n_points, 37.7, 37.85),
value = rnorm(n_points, mean = 0.5, sd = 0.3),
radius = runif(n_points, 50, 200)
)
points_data$value <- pmax(0, pmin(1, points_data$value))
spec <- list(
`@@type` = "DeckGL",
initialViewState = list(
longitude = -122.4,
latitude = 37.78,
zoom = 12,
pitch = 0,
bearing = 0
),
views = list(
list(
`@@type` = "MapView",
controller = TRUE,
mapStyle = "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json"
)
),
layers = list(
list(
`@@type` = "ScatterplotLayer",
id = "points",
data = list(
type = "duckdb",
query = "SELECT id, lon, lat, value, radius FROM points_data"
),
getPosition = "@@=[lon, lat]",
getRadius = "@@=radius",
getFillColor = "@@=[value * 255, (1 - value) * 255, 100, 200]",
radiusUnits = "meters",
pickable = TRUE,
autoHighlight = TRUE
)
)
)
deckgl(
spec = spec,
data = list(points_data = points_data),
width = "100%",
height = "600px"
)This script renders the Utah portion of the Microsoft USBuildingFootprints dataset as GeoArrow polygons. Create a GeoParquet with GeoArrow-encoded geometries from the upstream ZIP:
ogr2ogr \
Utah.parquet \
/vsizip/Utah.geojson.zip \
-dialect SQLite \
-sql "SELECT geometry FROM 'Utah.geojson'" \
-lco COMPRESSION=BROTLI \
-lco GEOMETRY_ENCODING=GEOARROW \
-lco POLYGON_ORIENTATION=COUNTERCLOCKWISE \
-lco ROW_GROUP_SIZE=9999999library(rDeckgl)
library(arrow)
parquet_path <- "Utah.parquet"
if (!file.exists(parquet_path)) {
stop("Parquet file not found: ", parquet_path)
}
parquet_data <- arrow::read_parquet(parquet_path)
max_rows <- 1000000
sample_data <- if (nrow(parquet_data) > max_rows) {
head(parquet_data, max_rows)
} else {
parquet_data
}
# Locate the densest cluster of buildings to centre the initial view
bbox_data <- sample_data$GEOMETRY_bbox
if (!is.null(bbox_data)) {
cx <- (as.numeric(bbox_data$xmin) + as.numeric(bbox_data$xmax)) / 2
cy <- (as.numeric(bbox_data$ymin) + as.numeric(bbox_data$ymax)) / 2
grid_key <- paste(floor(cx / 0.1), floor(cy / 0.1), sep = ",")
densest_cell <- names(which.max(table(grid_key)))
idx <- which(grid_key == densest_cell)
center_lon <- mean(cx[idx], na.rm = TRUE)
center_lat <- mean(cy[idx], na.rm = TRUE)
} else {
center_lon <- -110.4144
center_lat <- 39.4991
}
# Encode the Arrow table as base64 IPC for inline delivery
arrow_table <- arrow::arrow_table(sample_data)
temp_file <- tempfile(fileext = ".arrows")
arrow::write_ipc_stream(arrow_table, temp_file)
arrow_bytes <- readBin(temp_file, "raw", n = file.info(temp_file)$size)
unlink(temp_file)
arrow_b64 <- base64enc::base64encode(arrow_bytes)
if (length(arrow_bytes) / 1024 / 1024 > 50) {
warning("Encoded payload exceeds 50 MB and may cause browser issues.")
}
spec <- list(
`@@type` = "DeckGL",
width = 1024,
height = 768,
initialViewState = list(
longitude = center_lon,
latitude = center_lat,
zoom = 12,
pitch = 0,
bearing = 0
),
controller = TRUE,
layers = list(
list(
`@@type` = "GeoArrowSolidPolygonLayer",
id = "utah-buildings",
data = list(`__arrow` = arrow_b64),
geometryColumn = "GEOMETRY",
getFillColor = c(255, 100, 0, 200),
extruded = FALSE,
pickable = TRUE,
`_normalize` = FALSE,
`_windingOrder` = "CCW"
)
)
)
deckgl(spec = spec, width = 1024, height = 768)This example demonstrates rendering GeoArrow polygons in Shiny using
the native GeoArrowPolygonLayer, which passes Arrow tables
directly to the layer without converting to binary format:
library(shiny)
library(rDeckgl)
library(arrow)
# Prepare the data file
parquet_path <- "Utah.parquet"
if (!file.exists(parquet_path)) {
stop("Parquet file not found: ", parquet_path)
}
# Read Parquet and create Arrow IPC file
parquet_data <- arrow::read_parquet(parquet_path)
# Sample for reasonable performance
max_rows <- 1000000
if (nrow(parquet_data) > max_rows) {
sample_data <- head(parquet_data, max_rows)
} else {
sample_data <- parquet_data
}
# Compute density-based center
bbox_data <- sample_data$GEOMETRY_bbox
if (!is.null(bbox_data)) {
xmin <- as.numeric(bbox_data$xmin)
xmax <- as.numeric(bbox_data$xmax)
ymin <- as.numeric(bbox_data$ymin)
ymax <- as.numeric(bbox_data$ymax)
cx <- (xmin + xmax) / 2
cy <- (ymin + ymax) / 2
grid_size <- 0.1
grid_x <- floor(cx / grid_size)
grid_y <- floor(cy / grid_size)
grid_key <- paste(grid_x, grid_y, sep = ",")
grid_counts <- table(grid_key)
densest_cell <- names(which.max(grid_counts))
densest_indices <- which(grid_key == densest_cell)
center_lon <- mean(cx[densest_indices], na.rm = TRUE)
center_lat <- mean(cy[densest_indices], na.rm = TRUE)
} else {
center_lon <- -110.4144
center_lat <- 39.4991
}
# Write Arrow IPC file to temporary location
temp_arrow <- tempfile(fileext = ".arrows")
arrow_table <- arrow::arrow_table(sample_data)
arrow::write_ipc_stream(arrow_table, temp_arrow)
# Set up static file serving
static_root <- dirname(temp_arrow)
static_name <- "utahdata"
addResourcePath(static_name, static_root)
arrow_url <- paste0(static_name, "/", basename(temp_arrow))
# UI
ui <- fluidPage(
titlePanel("Utah Buildings - Native GeoArrow Rendering"),
deckglOutput("map", width = "100%", height = "600px")
)
# Server
server <- function(input, output, session) {
output$map <- renderDeckgl({
spec <- list(
`@@type` = "DeckGL",
width = 1024,
height = 600,
initialViewState = list(
longitude = center_lon,
latitude = center_lat,
zoom = 12,
pitch = 0,
bearing = 0
),
controller = TRUE,
layers = list(
list(
# Use GeoArrowPolygonLayer for native rendering (no binary conversion)
`@@type` = "GeoArrowPolygonLayer",
id = "utah-buildings-native",
data = list(`__arrow_url` = arrow_url),
geometryColumn = "GEOMETRY",
getFillColor = c(255, 100, 0, 200),
getLineColor = c(255, 255, 255, 100),
filled = TRUE,
stroked = TRUE,
extruded = FALSE,
pickable = TRUE,
autoHighlight = TRUE
)
)
)
deckgl(spec = spec, width = "100%", height = "600px")
})
# Cleanup on session end
onSessionEnded(function() {
if (file.exists(temp_arrow)) {
unlink(temp_arrow)
}
})
}
# Run app
shinyApp(ui, server)Key differences from binary conversion:
GeoArrowPolygonLayer instead of
GeoArrowSolidPolygonLayer_normalize or _windingOrder flags
neededThese binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.