From r-package-skills
Use when code loads mapgl or freestiler, working with .pmtiles files, creating interactive maps in R, choosing between R mapping packages, or working with large spatial datasets for visualization
npx claudepluginhub arthurgailes/r-package-skills --plugin r-package-skillsThis skill uses the workspace's default tool permissions.
**This is a meta-skill** that helps you choose the right R mapping workflow. The R mapping stack consists of 2 specialized packages that work together.
Use when code loads or uses freestiler, working with .pmtiles files, creating or serving PMTiles vector tilesets in R, or preparing spatial data for mapgl/MapLibre visualization
Creates interactive geographic maps, choropleths, and spatial visualizations with GeoViews and GeoPandas. For point/line/polygon data, analysis like buffers/proximity, and tile basemaps.
Provides Mapbox GL JS patterns for choropleth maps, heat maps, 3D extrusions, data-driven styling, animations, and performance optimization with large datasets.
Share bugs, ideas, or general feedback.
This is a meta-skill that helps you choose the right R mapping workflow. The R mapping stack consists of 2 specialized packages that work together.
Install both: install.packages(c("freestiler", "mapgl"))
API keys: Mapbox styles require MAPBOX_PUBLIC_TOKEN; MapLibre works without keys (CARTO/OpenFreeMap)
references/integration-patterns.md - How freestiler and mapgl work togetherFor per-package API docs, invoke the corresponding package skill (/r-freestiler, /r-mapgl).
| Package | Use When | Skill |
|---|---|---|
| freestiler | Convert large spatial data (>10k features) to vector tiles | /r-freestiler |
| mapgl | Create interactive WebGL maps, add layers, build Shiny apps | /r-mapgl |
REQUIRED: After reading this skill, you MUST invoke the relevant sub-skill (r-freestiler or r-mapgl) before writing any code. If the task involves .pmtiles files, invoke r-freestiler. If the task involves map display, invoke r-mapgl. For pipelines, invoke both.
Dataset size determines your path:
| Features | Workflow | Reason |
|---|---|---|
| <10k | sf → mapgl directly | Fast enough without tiling |
| 10k-100k | freestiler → mapgl | Smooth performance with tiles |
| >100k or >1GB | freestiler with streaming | Memory-efficient processing |
Hosting context:
library(mapgl)
library(sf)
nc <- st_read(system.file("shape/nc.shp", package = "sf"))
maplibre(style = carto_style("positron")) |>
fit_bounds(nc) |>
add_fill_layer(
id = "counties",
source = nc,
fill_color = interpolate(
column = "BIR74",
values = c(500, 10000),
stops = c("lightblue", "darkblue")
)
)
library(freestiler)
library(mapgl)
# Step 1: Create tiles (run once)
freestile(
input = large_sf,
output = "tiles.pmtiles",
layer_name = "features"
)
# Step 2: Serve and visualize
view_tiles("tiles.pmtiles") # Quick preview
# Or manual control:
serve_tiles(path = dirname("tiles.pmtiles"))
maplibre() |>
add_pmtiles_source(
id = "src",
url = "http://localhost:8080/tiles.pmtiles"
) |>
add_fill_layer(
source = "src",
source_layer = "features", # Must match layer_name
fill_color = "steelblue"
)
library(freestiler)
library(duckdb)
# Process without loading into memory
freestile_query(
query = "SELECT * FROM read_parquet('huge_spatial.parquet')",
output = "massive.pmtiles",
layer_name = "points",
streaming = "always" # Critical for large datasets
)
library(shiny)
library(mapgl)
ui <- fluidPage(
selectInput("year", "Year", choices = 2010:2020),
maplibreOutput("map", height = "600px")
)
server <- function(input, output, session) {
output$map <- renderMaplibre({
maplibre(style = carto_style("positron")) |>
add_pmtiles_source(id = "data", url = "tiles.pmtiles") |>
add_circle_layer(
id = "points",
source = "data",
source_layer = "features"
)
})
observeEvent(input$year, {
maplibre_proxy("map") |>
set_filter(
"points",
list("==", list("get", "year"), input$year)
)
})
}
Start: Need to map spatial data
├─ <10k features?
│ └─ YES → Use mapgl directly with sf source
├─ 10k-1M features?
│ └─ YES → freestiler (default settings) → mapgl
├─ >1M features OR >500MB file?
│ └─ YES → freestiler with streaming → mapgl
└─ Need static hosting?
└─ YES → freestiler PMTiles → mapgl (no server needed)
st_transform(data, 4326)layer_name in freestiler = source_layer in mapglview_tiles() for quick preview, or serve_tiles() + mapgl for custom controlfile:// URLs with absolute paths in mapglstreaming = "always" for datasets >10M featuresfreestile(layer_name = "x") requires add_*_layer(source_layer = "x")See references/ for:
For package-specific documentation, invoke the individual skills: