---
title: "Labels and static output"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Labels and static output}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
```

Labels have their own data model in `dragmapr` because draggable plots often
need two kinds of movement:

- region offsets move all geometries in a group;
- label offsets nudge only the text marker after the region has moved.

If users want a static image after the draggable edit, the same data model can
be rendered back through `ggplot2`. This means a Shiny app is optional: once
the region and label offset CSVs exist, `render_dragged_map()` can reconstruct
the layout later in a script, report, or document workflow.

Labels can also be configured or omitted when the draggable helper is written:

```{r, eval = FALSE}
# No labels at all
drag_map_prototype(x, "region", labels = FALSE)

# Labels follow their region but cannot be nudged independently
drag_map_prototype(x, "region", label_col = "name", draggable_labels = FALSE)

# Text only — no visible marker behind the label text
drag_map_prototype(x, "region", label_col = "name", label_marker_shape = "none")

# Circle markers — label_radius only applies when shape is "circle"
drag_map_prototype(x, "region", label_col = "name",
                   label_marker_shape = "circle",
                   label_radius = 16, label_text_size = 14)

# Rounded-box markers (default) with explicit size
drag_map_prototype(x, "region", label_col = "name",
                   label_marker_shape = "rect",
                   label_width = 80, label_height = 32, label_text_size = 12)
```

## Label Concepts

`dragmapr` separates labels into three related concepts:

- `make_region_labels()` derives one default label anchor per group.
- `as_drag_labels()` accepts user-supplied labels and preserves extra metadata.
- `as_drag_annotations()` creates draggable info boxes for longer notes.
- `read_label_state()` and `apply_label_state()` restore label movements from
  CSV state exported by the draggable helper.

By default, region labels use `sf::st_point_on_surface()` so anchors are likely
to fall inside grouped geometry.

```{r}
library(dragmapr)

hhs <- example_hhs_layout()
head(hhs$labels)
```

User-supplied labels are plain data:

```text
label_id,region,label,x,y
```

They may also carry extra columns for Shiny/D3 use:

```{r}
as_drag_labels(data.frame(
  label_id = "note-1",
  region = "3",
  label = "Custom note",
  x = hhs$labels$x[3],
  y = hhs$labels$y[3],
  tooltip = "Shown by a custom D3/Shiny layer"
))
```

## Info Boxes And Text-only Labels

Info boxes are just label rows with `label_type = "box"` plus browser box
dimensions. `as_drag_annotations()` fills those details for you:

```{r}
note <- as_drag_annotations(data.frame(
  label_id = "region-3-note",
  region = "3",
  label = "Region 3 has a longer note that reads better in a box.",
  x = hhs$labels$x[3],
  y = hhs$labels$y[3]
), width_px = 180, height_px = 84)

note
```

Ordinary labels can be rendered with a circular marker or as text-only labels.
Text-only labels remain draggable in the browser helper because `dragmapr` adds
an invisible drag target behind the text.

```{r}
render_dragged_map(
  hhs$states,
  region_offsets = hhs$region_offsets,
  region_col = "hhs_region",
  labels = hhs$labels,
  label_offsets = hhs$label_offsets,
  region_palette = hhs$region_colors,
  region_labels = hhs$region_names,
  show_label_marker = FALSE,
  title = "Text-only labels"
)
```

Use `label_values` when you want to render only selected labels while keeping
the full label table and saved offsets available for later sessions:

```{r}
render_dragged_map(
  hhs$states,
  region_offsets = hhs$region_offsets,
  region_col = "hhs_region",
  labels = hhs$labels,
  label_offsets = hhs$label_offsets,
  label_values = c("1", "2", "3"),
  region_palette = hhs$region_colors,
  region_labels = hhs$region_names,
  title = "Only selected labels"
)
```

## Connector Lines

Labels and annotation boxes can have connector lines. The connector starts from
the original label anchor by default and ends just outside the visible label or
box. Optional `connector_start_x` / `connector_start_y` columns can override the
start point, while `connector_mid_x` / `connector_mid_y` can define a breakpoint
for elbow or curved connectors.

Supported connector styles are `"straight"`, `"elbow"`, `"curve"`, and
`"squiggle"`.

```{r}
note$connector <- TRUE
note$connector_type <- "squiggle"

render_dragged_map(
  hhs$states,
  region_offsets = hhs$region_offsets,
  region_col = "hhs_region",
  labels = note,
  label_offsets = data.frame(
    label_id = "region-3-note",
    region = "3",
    dx_m = 90000,
    dy_m = 70000
  ),
  region_palette = hhs$region_colors,
  region_labels = hhs$region_names,
  connector_linewidth = 0.9,
  connector_color = "#334155",
  connector_linetype = "dashed",
  connector_endpoint = "arrow",
  label_padding = 0.12,
  title = "Annotation box with connector"
)
```

`label_padding` expands static plot limits around displaced labels and
connectors, which helps prevent exported images from clipping callouts.

The exported label-offset table is also plain data:

```text
label_id,region,dx_m,dy_m
```

Read saved region offsets with `read_offsets()` and saved label offsets with
`read_label_state()`. Existing code that uses `read_label_offsets()` or
`apply_label_offsets()` can continue to run because those names are aliases for
the label state helpers.

## Region Movement Plus Label Movement

Region offsets are applied to both geometry and default label anchors. Label
offsets are applied after that.

Use `apply_offsets()` when you need the adjusted `sf` geometry itself, for
example before writing GeoJSON or GeoPackage output from an app.

```{r}
label_offsets <- hhs$label_offsets
label_offsets$dx_m[label_offsets$label_id == "3"] <- -45000
label_offsets$dy_m[label_offsets$label_id == "3"] <- -30000

render_dragged_map(
  hhs$states,
  region_offsets = hhs$region_offsets,
  region_col = "hhs_region",
  labels = hhs$labels,
  label_offsets = label_offsets,
  region_palette = hhs$region_colors,
  region_labels = hhs$region_names,
  title = "HHS regions with label nudge"
)
```

## Legend Filtering And Movement Context

Static exports can include a subset of legend keys without removing geometry
from the map. This is useful when a workflow wants to emphasize a small set of
groups while keeping the full layout visible.

```{r}
render_dragged_map(
  hhs$states,
  region_offsets = hhs$region_offsets,
  region_col = "hhs_region",
  labels = hhs$labels,
  label_offsets = hhs$label_offsets,
  region_palette = hhs$region_colors,
  region_labels = hhs$region_names,
  legend_values = c("1", "2", "3"),
  title = "Selected legend keys"
)
```

Movement context can also be rendered statically. Origin outlines show where
moved regions started, and movement connectors point from the original location
to the current translated location.

```{r}
render_dragged_map(
  hhs$states,
  region_offsets = hhs$region_offsets,
  region_col = "hhs_region",
  labels = hhs$labels,
  label_offsets = hhs$label_offsets,
  region_palette = hhs$region_colors,
  region_labels = hhs$region_names,
  show_origin_outlines = TRUE,
  show_movement_connectors = TRUE,
  movement_connector_color = "#64748b",
  movement_connector_opacity = 0.65,
  movement_connector_linetype = "dotted",
  movement_connector_endpoint = "open",
  title = "Movement context"
)
```

## Saving Static Images

Supplying `file` passes the finished plot to `ggplot2::ggsave()`.

```{r, eval = FALSE}
render_dragged_map(
  hhs$states,
  region_offsets = "drag_region_offsets.csv",
  region_col = "hhs_region",
  labels = hhs$labels,
  label_offsets = "drag_label_offsets.csv",
  file = "hhs_dragged_layout.png",
  width = 9,
  height = 6
)
```

## Rendering A Spatial Studio Project

When the layout comes from Spatial Studio, the project ZIP is the easiest
static handoff. It contains the source geometry, region offsets, label offsets,
labels, palette, metadata, and a generated `recreate-static-map.R` script.

```{r, eval = FALSE}
render_dragmapr_project(
  "dragmapr-project.zip",
  file = "hhs_dragged_layout.png",
  width = 9,
  height = 6,
  dpi = 300
)
```

`render_dragmapr_project()` checks the bundle before rendering. Missing offset
rows are reported and treated as zero movement; malformed CSVs, unknown region
columns, and labels tied to absent regions produce file-specific errors.
