--- title: "Getting started with explodemap" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Getting started with explodemap} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 5 ) ``` ## Overview `explodemap` generates hierarchical exploded-view maps for dense administrative boundary data. It applies rigid-body translations to polygon geometries so that features are visually separated while each feature's internal geometry is preserved exactly. The basic two-level workflow is: 1. Group units into regions using a column in your data. 2. Displace units within and across those regions using a centroid-driven vector field with analytically derived parameters. For the two-level core described in the paper, the package is designed to satisfy three key properties: - **Proposition 1:** each polygon's shape, area, perimeter, and topology are preserved exactly. - **Proposition 2:** the radial ordering of units within each region is preserved. - **Proposition 3:** no unit is displaced by more than `alpha_r + alpha_l` metres. ## Input requirements `explodemap` expects an `sf` object with: - Polygon or multipolygon geometries - A grouping column identifying the parent region for each unit - A **projected** CRS with linear metre units Geographic coordinates such as EPSG:4326 (longitude/latitude) must be transformed before use. For U.S. work, a state plane, UTM, or Albers-type projected CRS is usually appropriate. ```{r setup} library(sf) library(explodemap) ``` ## A minimal example We create a small synthetic dataset with four square polygons in two regions. ```{r synthetic-data} sq <- function(xmin, ymin, size = 1000) { st_polygon(list(matrix( c(xmin, ymin, xmin + size, ymin, xmin + size, ymin + size, xmin, ymin + size, xmin, ymin), ncol = 2, byrow = TRUE ))) } geom <- st_sfc( sq(0, 0), sq(3000, 0), # Region A sq(12000, 0), sq(15000, 0), # Region B crs = 3857 ) x <- st_sf( id = c("a1", "a2", "b1", "b2"), region = c("A", "A", "B", "B"), geometry = geom ) x ``` ## Running the explosion The simplest entry point is `explode_sf()`. Pass your sf object and the name of the grouping column: ```{r explode-basic} result <- explode_sf(x, region_col = "region", plot = FALSE) ``` In Shiny or other non-interactive pipelines, add `quiet = TRUE` to suppress progress messages: ```{r explode-quiet} quiet_result <- explode_sf(x, region_col = "region", plot = FALSE, quiet = TRUE) class(quiet_result) ``` The returned object is an S3 object of class `exploded_map`: ```{r class-check} class(result) names(result) ``` It contains the original and exploded geometries, a WGS84 export-ready version, derived statistics and parameters, plots, and diagnostics. ## Diagnostics `print()` shows geometry statistics and derived parameters: ```{r print} print(result) ``` `summary()` adds implied gamma coefficients that are useful for calibration work: ```{r summary} summary(result) ``` ## Plotting ```{r plot-exploded} plot(result) ``` You can also view both original and exploded layouts side by side: ```{r plot-both, fig.width = 10} plot(result, "both") ``` ## Calibration output `calibration_row()` returns a one-row data frame suitable for combining across datasets when building a cross-dataset calibration table: ```{r calibration} calibration_row(result) ``` ## Manual parameter overrides By default, `explodemap` derives displacement parameters analytically from dataset geometry using the paper's two closed-form results. You can also supply parameters directly. Overrides may be supplied independently, so you can tune regional separation without changing local expansion, or vice versa: ```{r manual} manual <- explode_sf( x, region_col = "region", alpha_r = 100, alpha_l = 200, plot = FALSE ) manual$params ``` ```{r partial-manual} more_region_gap <- explode_sf( x, region_col = "region", alpha_r = result$params$alpha_r * 1.5, plot = FALSE ) more_region_gap$params ``` ## Optional collision refinement The two-level algorithm is the clean paper model. For dense municipal cores, you can add a bounded refinement pass that nudges close same-region neighbors apart after the analytical displacement: ```{r refine-example, eval=FALSE} refined <- explode_sf( x, region_col = "region", refine = TRUE, refine_min_gap = 0.15, refine_max_shift = 0.10, plot = FALSE ) refined$refinement ``` ```{r refine, echo=FALSE} has_refinement <- "refine" %in% names(formals(explode_sf)) if (has_refinement) { refined <- explode_sf( x, region_col = "region", refine = TRUE, refine_min_gap = 0.15, refine_max_shift = 0.10, plot = FALSE ) refined$refinement } else { message("This installed explodemap build does not include refinement yet.") } ``` `refine_max_shift` caps the extra correction per feature, so the refinement remains a small display adjustment rather than a replacement for the displacement model. Use `refine_within = "all"` when the remaining crowding crosses region boundaries. ## Centroid options For irregular or multipart polygons, `"point_on_surface"` may be preferable to the default geometric centroid: ```{r centroid-mode} pos <- explode_sf( x, region_col = "region", centroid_fun = "point_on_surface", plot = FALSE ) ``` ## Using TIGER/Line data for U.S. states `explode_state()` downloads U.S. Census TIGER/Line boundaries automatically and groups municipalities by a county-to-region mapping: ```{r tiger-example, eval = FALSE} nj <- explode_state( state_fips = "34", crs = 32118, region_map = list( North = c("Bergen", "Essex", "Hudson", "Morris", "Passaic", "Sussex", "Union", "Warren"), Central = c("Hunterdon", "Mercer", "Middlesex", "Monmouth", "Somerset"), South = c("Atlantic", "Burlington", "Camden", "Cape May", "Cumberland", "Gloucester", "Ocean", "Salem") ), label = "New Jersey" ) ``` Downloaded data is cached locally so subsequent runs are faster. Use `quiet = TRUE` in app code if downloads and region assignment happen inside a reactive expression. ## Using a lookup table `explode_sf_with_lookup()` joins an external lookup table to your sf object before exploding: ```{r lookup-example, eval = FALSE} groups <- read.csv("region_assignments.csv") result <- explode_sf_with_lookup( my_sf, join_col = "GEOID", lookup = groups, lookup_key = "geoid", region_col = "region" ) ``` Unmatched units are labelled `"Other"`. This is useful when a lookup table is incomplete. You can include or exclude those units using `allow_other`. ## Export The `export` parameter supports three modes: - `NULL` (default): no export - `TRUE`: auto-named GeoJSON file - A file path string: explicit output location ```{r export-example, eval = FALSE} result <- explode_sf( my_sf, region_col = "region", export = "output.geojson" ) ``` ## Interactive focus maps and Shiny `focus_map()` renders raw `sf`, `exploded_map`, or `grouped_exploded_map` objects as an interactive htmlwidget. Click a polygon to zoom and lift it into focus; right-click or press Escape to reset. Information cards can show selected fields without blocking the map. ```{r focus-map, eval=FALSE} focus_map( result, label_col = "id", id_col = "id", group_col = "region", group_palette = c(A = "#4C78A8", B = "#F58518", C = "#54A24B"), info_cols = c("id", "region"), info_card_scale = 0.95 ) ``` In Shiny, use `focusmapOutput()` and `renderFocusmap()`. The widget emits `input$_selected`, which includes the selected feature ID, label, group, and properties. ```{r shiny-focus-map, eval=FALSE} ui <- shiny::fluidPage( focusmapOutput("map", height = "650px"), shiny::verbatimTextOutput("selected") ) server <- function(input, output, session) { exploded <- explode_sf(x, "region", plot = FALSE, quiet = TRUE) output$map <- renderFocusmap({ focus_map( exploded, label_col = "id", id_col = "id", group_col = "region", group_palette = c(A = "#4C78A8", B = "#F58518", C = "#54A24B") ) }) output$selected <- shiny::renderPrint(input$map_selected) } ``` For section drill-downs, use `explode_section()` before rendering. It explodes the selected section and marks the rest of the layer as context: ```{r section-focus-map, eval=FALSE} focused <- explode_section( x, section_col = "region", section = "A", region_col = "county", alpha_r = 900, alpha_l = 600, plot = FALSE, quiet = TRUE ) focus_map( focused, label_col = "id", context_col = ".explodemap_role", context_mode = "fade" ) ``` Small municipal features can use adaptive focus sizing so the selected polygon is not left visually distant after the camera moves: ```{r tiny-feature-focus, eval=FALSE} focus_map( focused, label_col = "id", context_col = ".explodemap_role", context_mode = "fade", min_focus_width = 115, min_focus_height = 88, tiny_feature_threshold = 52, tiny_feature_boost = 1.45, origin_context = "inset", origin_context_position = "bottom-left", focus_context_opacity = 0.14, show_drag_zoom = TRUE ) ``` ## Notes - Always use a projected CRS before running the algorithm. - The two-level core preserves each feature's internal geometry exactly through rigid-body translation. - Parameter derivation is deterministic and reproducible: the same dataset and gamma coefficients always produce the same output. ## Next steps See `vignette("grouped-layouts")` for the three-level extension using `explode_grouped()`, which adds region-block anchor placement for larger multi-region or national layouts.