This document provides deterministic rules for AI coding agents (GitHub Copilot, Claude Code, Cursor, etc.) and human developers when writing new geospatial visualizations, migrating leaflet / mapview code to glmap, or extending the glmap package itself.
1. Golden Rules When Generating R Mapping Code with glmap
-
Use
glmap()as the primary entry point (glmap()andglmap()are also supported as aliases). -
Never construct manual palette functions (
leaflet::colorNumeric,colorFactor,colorBin):- In
glmap, pass the column formula (~col_name) or column name string ("col_name") directly tocolor =orfill_color =, along withpalette = "viridis"(or"plasma","magma","turbo","terrain","rdylbu","Spectral"). -
glmapautomatically computes a 256-entry RGB lookup table in R/C++ and attaches a matching continuous or categorical legend automatically.
- In
-
Never convert
sfto GeoJSON orterra::SpatRasterto PNG before callingglmap:- Pass
sf,sfc,wk::xy,wk::wkb,nanoarrow_array,terra::SpatRaster,terra::SpatVector,stars, ordata.frameobjects directly toglmap(),add_sf(),add_points(),add_lines(),add_polygons(), oradd_raster(). - C++17 functions (
cpp_pack_points_xy,cpp_pack_sfc_binary,cpp_pack_raster_float32) extract coordinates and triangulate polygons (including interior holes viamapbox/earcut.hpp) directly from R memory pointers.
- Pass
-
Choose the right transport mode for dataset scale:
-
Up to ~500k features / standalone HTML / Quarto / RMarkdown: Keep the default
transport = "auto"(useszlib-compressed binary inline buffers decoded via browserDecompressionStream). -
500k to 10M+ features in interactive R / Shiny sessions: Use
transport = "server"oradd_points(..., dynamic_viewport = TRUE)so the C++17 spatial index (gl_spatial_index()) streams only the visible bounding box overhttpuv(127.0.0.1). -
10M to 100M+ rows in DuckDB or
.parquetfiles: Useadd_duckdb(con, source = "file.parquet", lon = "lon", lat = "lat", color = ~val, where = "...")to push bounding-box and SQLWHEREfilters directly into DuckDB’s vectorized engine without loading the full table into R memory.
-
Up to ~500k features / standalone HTML / Quarto / RMarkdown: Keep the default
-
In Shiny apps, always use
glmap_proxy()for incremental updates:- Never re-run
renderGlmap()just to change a raster palette, toggle hillshading, hide a layer, or move the camera. - Use
glmap_proxy("map_id") |> set_raster_style("layer_id", palette = ..., hillshade = ...)for 0 ms GPU uniform updates.
- Never re-run
2. Deterministic leaflet glmap Refactoring Rules for Agents
When asked to migrate an R script or Shiny app from leaflet to glmap, apply these exact transformations:
leaflet Pattern |
Replace With (glmap) |
Notes |
|---|---|---|
library(leaflet) |
library(glmap) |
Drop leafgl / leafem imports as well |
leaflet(data) |
glmap(data, basemap = "dark") |
Or basemap = "positron" for light mode |
|> addTiles() |
|> set_basemap("osm") |
Or pass basemap = "osm" to glmap()
|
|> addProviderTiles(providers$CartoDB.Positron) |
|> set_basemap("positron") |
Supported: "dark", "positron", "voyager", "osm", "satellite", "none"
|
|> addCircleMarkers(lng = ~x, lat = ~y, radius = 5, color = ~pal(z), fillOpacity = 0.8) |
|> add_points(lon = ~x, lat = ~y, radius = 5, color = ~z, palette = "viridis", opacity = 0.8) |
Delete pal <- colorNumeric(...)
|
|> addMarkers(lng = ~x, lat = ~y, popup = ~lbl) |
|> add_points(lon = ~x, lat = ~y, popup = ~lbl) |
Rendered on WebGL2 GPU |
|> addLabelOnlyMarkers(lng = ~x, lat = ~y, label = ~lbl) |
|> add_labels(lon = ~x, lat = ~y, text = ~lbl) |
Crisp halo-outlined labels |
|> addPolylines(data = lines_sf, color = "blue", weight = 2) |
|> add_lines(data = lines_sf, color = "blue", width = 2) |
Note weight width
|
|> addPolygons(data = poly_sf, fillColor = ~pal(v), fillOpacity = 0.7, color = "white", weight = 1) |
|> add_polygons(data = poly_sf, fill_color = ~v, palette = "viridis", fill_opacity = 0.7, line_color = "white", line_width = 1) |
C++17 mapbox/earcut triangulates automatically |
|> addRasterImage(r, colors = pal, opacity = 0.8) |
|> add_raster(r, palette = "terrain", opacity = 0.8, hillshade = TRUE) |
Uploads raw Float32 texture to GPU |
|> addLegend(...) |
(Remove unless custom legend needed) | Legends are auto-generated when color / fill_color is mapped; use add_legend() only for custom overrides |
|> addLayersControl(...) |
(Remove) | Interactive GPU Layer Switcher is enabled by default |
|> setView(lng, lat, zoom) |
|> set_view(lng, lat, zoom) |
Snake_case |
|> fitBounds(lng1, lat1, lng2, lat2) |
|> fit_bounds(c(lng1, lat1, lng2, lat2)) |
Also accepts sf or SpatRaster directly |
leafletOutput("map", height = 600) |
glmapOutput("map", height = "600px") |
Shiny UI |
renderLeaflet({ ... }) |
renderGlmap({ ... }) |
Shiny Server |
leafletProxy("map") |> clearShapes() |
glmap_proxy("map") |> clear_layers() |
Or clear_layer("id")
|
leafletProxy("map") |> hideGroup("g") |
glmap_proxy("map") |> set_layer_visibility("g", FALSE) |
Instant GPU toggle |
3. Repository Architecture (For Agents Modifying glmap Internals)
-
src/glmap_cpp.cpp&src/cpp11.cpp:- C++17 backend using
cpp11. - Binary packet formats:
-
GLMP(48-byte packed header +Float32coords +Uint32vertex feature IDs +Uint32ring starts +Uint32earcut triangle indices +Uint8RGBA + optionalFloat32radius & elevation). -
GLMR(48-byte packed header +Float32raster band values).
-
- Note: On Windows without
decorinstalled, keepsrc/cpp11.cppandR/cpp11.Rsynchronized manually when adding new[[cpp11::register]]functions, and compile viaR CMD INSTALL --no-multiarch ..
- C++17 backend using
-
R/transport.R:-
gl_pack_sf_binary()(supportssf,sfc,wk_xy,wk_wkb,wk_wkt,nanoarrow_array,data.frame),gl_pack_raster_binary()(supportsterra::SpatRasterandmatrix),gl_spatial_index(), andhttpuvdaemon server (gl_serve_start(),gl_serve_stop()).
-
-
R/map.R,R/scales.R,R/controls.R:- User-facing pipeable API (
glmap(),add_sf(),add_points(),add_lines(),add_polygons(),add_raster(),add_hexbin(),add_heatmap(),add_pmtiles(),add_mvt()).
- User-facing pipeable API (
-
R/plugins.R&R/shiny.R:- Extension registry (
register_plugin(),use_plugin(),add_webgl_shader_layer(),add_deck_layer()) and Shiny proxy (glmap_proxy()).
- Extension registry (
-
inst/htmlwidgets/glmap.js:- Zero-dependency WebGL2 + MapLibre GL JS engine,
GLMP/GLMRbinary parser,DecompressionStream("deflate")decoder, custom GLSL shaders (SDF instanced points, triangulated polygon mesh, additive Gaussian heatmap, multi-bandFloat32raster LUT + surface-normal hillshader), and interactive Measure / Box Brush / PNG Export tools.
- Zero-dependency WebGL2 + MapLibre GL JS engine,