Skip to contents

The recommended way to use dragmapr is to treat a layout as an editable composition object – a dragmapr_state – and carry it through three steps:

compute  ->  compose  ->  render
(layout)     (edit)       (static / interactive)

The state is plain, reproducible data: region and label offsets (as metre deltas), the projected crs, a geometry_id, the selected_feature, and a monotonically increasing version. Because the geometry and the editorial state are kept separate, you can recompute the layout while preserving manual edits, and the same state object drives the interactive editor, the static renderer, and any persistence.

The older route – exporting offset CSVs and calling as_dragmapr() – still works, but it is now the low-level escape hatch rather than the main road.

1. Compute

In a full pipeline the layout comes from explodemap, which computes a mathematically valid exploded layout and hands it over as a state:

library(explodemap)

layout <- explode_grouped(my_sf, region_col = "region")
state  <- as_dragmapr_state(layout)   # dx_m/dy_m deltas + crs + geometry_id

For a self-contained example we build the equivalent state directly, so this vignette runs with only dragmapr installed:

library(dragmapr)

make_square <- function(x0, y0, size = 100000) {
  sf::st_polygon(list(rbind(
    c(x0, y0), c(x0 + size, y0), c(x0 + size, y0 + size),
    c(x0, y0 + size), c(x0, y0)
  )))
}

regions <- sf::st_sf(
  region = c("North", "South", "East", "West"),
  geometry = sf::st_sfc(
    make_square(0, 140000), make_square(0, 0),
    make_square(140000, 70000), make_square(-140000, 70000),
    crs = 3857
  )
)

state <- dragmapr_state(
  region_offsets = data.frame(
    region = c("North", "South", "East", "West"),
    dx_m   = c(0, 0, 60000, -60000),
    dy_m   = c(50000, -50000, 0, 0)
  ),
  crs = 3857,
  geometry_id = "synthetic-quad-v1"
)
state
#> $level
#> [1] "region"
#> 
#> $region_offsets
#>   region   dx_m   dy_m
#> 1  North      0  50000
#> 2  South      0 -50000
#> 3   East  60000      0
#> 4   West -60000      0
#> 
#> $label_offsets
#> [1] label_id region   dx_m     dy_m    
#> <0 rows> (or 0-length row.names)
#> 
#> $expanded_groups
#> character(0)
#> 
#> $view
#> NULL
#> 
#> $version
#> [1] 0
#> 
#> $crs
#> [1] 3857
#> 
#> $geometry_id
#> [1] "synthetic-quad-v1"
#> 
#> $selected_feature
#> NULL
#> 
#> attr(,"class")
#> [1] "dragmapr_state"

2. Compose

dragmapr_edit() is the friendly front door to the interactive editor. It accepts a projected sf, an explodemap grouped_exploded_map, or a dragmapr_layout, seeded with the state:

dragmapr_edit(regions, region_col = "region", state = state)

Inside Shiny, the widget reports every edit through an input named paste0(outputId, "_state"). Rebuild a dragmapr_state from it with dragmapr_widget_state() so the server always holds the live composition:

output$map <- renderDragmapr(dragmapr_edit(regions, "region", state = state))

observeEvent(input$map_state, {
  state <- dragmapr_widget_state(input$map_state)
})

# Push a selection from R back into the browser:
updateDragmapr(session, "map", selected_feature = "East")

3. Render

Because the state is just data, you can persist it and reproduce the layout as a static ggplot2 image at any time – no browser session and no recomputation:

path <- tempfile(fileext = ".json")
write_dragmapr_state(state, path)
state2 <- read_dragmapr_state(path)

render_dragged_map(
  regions,
  region_col = "region",
  state = state2,
  title = "Composed layout (state-first)"
)

The state object

A dragmapr_state carries:

  • region_offsets / label_offsets – metre deltas (dx_m, dy_m) keyed by a stable join key;
  • crs – the projected CRS the deltas are expressed in (EPSG or WKT), so a saved state is safe to reapply later;
  • geometry_id – a provenance tag for the geometry the state was composed against;
  • selected_feature, expanded_groups, view – editor/dashboard state;
  • version – a revision that bumps monotonically as edits accumulate.

The full lifecycle (validate_, merge_, snapshot_/restore_, inherit_drag_offsets(), collapse_drag_offsets(), and JSON read_/write_) operates on this object. For a complete runnable round-trip, see the Shiny app in system.file("examples/full_state_roundtrip.R", package = "dragmapr").

For a fuller cross-package example that starts in explodemap, uses layout-quality diagnostics and label-aware parameter search, then composes and renders with dragmapr, run:

source(system.file(
  "examples/explodemap_dragmapr_pipeline.R",
  package = "dragmapr"
))