{"spec_id":"cartogram-area-distortion","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' cartogram-area-distortion: Cartogram with Area Distortion by Data Value\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 82/100 | Created: 2026-06-08\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(scales)\nlibrary(ragg)\n\nset.seed(42)\n\n# Theme tokens (Imprint palette)\nTHEME       <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG     <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nELEVATED_BG <- if (THEME == \"light\") \"#FFFDF6\" else \"#242420\"\nINK         <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT    <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\nINK_MUTED   <- if (THEME == \"light\") \"#6B6A63\" else \"#A8A79F\"\n\n# Imprint sequential colormap: brand green -> blue\nSEQ_LOW  <- \"#009E73\"  # Imprint position 1\nSEQ_HIGH <- \"#4467A3\"  # Imprint position 3\n\n# 48 contiguous US states — approximate centroids + 2023 population (millions)\n# and approximate GDP per capita in USD thousands (~2022)\nstates <- tibble::tibble(\n  abbr = c(\n    \"CA\", \"TX\", \"FL\", \"NY\", \"PA\", \"IL\", \"OH\", \"GA\", \"NC\", \"MI\",\n    \"NJ\", \"VA\", \"WA\", \"AZ\", \"TN\", \"MA\", \"IN\", \"MO\", \"MD\", \"WI\",\n    \"CO\", \"MN\", \"SC\", \"AL\", \"LA\", \"KY\", \"OR\", \"OK\", \"CT\", \"UT\",\n    \"IA\", \"NV\", \"AR\", \"MS\", \"KS\", \"NM\", \"NE\", \"ID\", \"WV\", \"ME\",\n    \"NH\", \"MT\", \"RI\", \"DE\", \"SD\", \"ND\", \"VT\", \"WY\"\n  ),\n  lon = c(\n    -119.4,  -99.9,  -81.5,  -74.0,  -77.2,  -89.2,  -82.9,  -83.6,  -79.4,  -84.7,\n     -74.4,  -78.7, -120.7, -111.7,  -86.7,  -71.4,  -86.3,  -92.3,  -76.6,  -89.5,\n    -105.5,  -94.3,  -81.2,  -86.8,  -92.3,  -84.3, -122.1,  -97.5,  -72.7, -111.1,\n     -93.5, -116.4,  -92.4,  -89.7,  -98.4, -106.0,  -99.9, -114.5,  -80.5,  -69.4,\n     -71.6, -110.5,  -71.5,  -75.5, -100.2, -100.5,  -72.7, -107.6\n  ),\n  lat = c(\n    36.8, 31.1, 28.1, 42.2, 40.6, 40.6, 40.4, 32.2, 35.5, 43.3,\n    40.1, 37.8, 47.4, 34.2, 35.9, 42.2, 40.0, 38.5, 39.0, 44.5,\n    38.9, 46.4, 33.8, 32.8, 31.2, 37.5, 44.6, 35.6, 41.6, 39.4,\n    42.0, 38.5, 35.0, 32.7, 38.5, 34.5, 41.5, 44.4, 38.9, 44.9,\n    43.7, 47.0, 41.7, 39.0, 44.4, 47.5, 44.0, 43.1\n  ),\n  population = c(\n    39.0, 30.5, 22.6, 19.6, 12.96, 12.6, 11.8, 10.9, 10.7, 10.0,\n     9.3,  8.7,  7.8,  7.4,  7.1,  7.0,  6.8,  6.2,  6.2,  5.9,\n     5.8,  5.7,  5.3,  5.1,  4.6,  4.5,  4.3,  4.0,  3.6,  3.4,\n     3.2,  3.2,  3.0,  2.96, 2.94,  2.1,  2.0,  1.96, 1.78, 1.40,\n     1.39, 1.12, 1.10, 1.02, 0.91, 0.78, 0.65, 0.58\n  ),\n  gdp_pc = c(\n    78, 67, 58, 95, 62, 71, 57, 58, 64, 52,\n    72, 66, 78, 60, 57, 89, 56, 57, 72, 60,\n    70, 63, 55, 51, 54, 53, 63, 56, 80, 60,\n    60, 64, 52, 47, 59, 51, 60, 54, 46, 55,\n    70, 56, 67, 71, 56, 62, 64, 70\n  )\n)\n\n# Label ~24 most populous states (population >= 5M) for better geographic coverage\nlabeled_states <- states %>% filter(population >= 5)\n\n# Title with length-aware font scaling (baseline 67 chars at 12pt)\nplot_title <- paste0(\n  \"US States by Population · cartogram-area-distortion\",\n  \" · r · ggplot2 · anyplot.ai\"\n)\ntitle_size <- max(8L, round(12 * 67 / nchar(plot_title)))\n\n# Reference inset — equal-area dots at geographic centroids (no population distortion)\n# Provides visual comparison: all states equal size vs. cartogram where size = population\nref_map_plot <- ggplot(states, aes(x = lon, y = lat)) +\n  geom_point(\n    fill = INK_MUTED, color = PAGE_BG,\n    shape = 21, size = 1.8, stroke = 0.3, alpha = 0.85\n  ) +\n  coord_fixed(ratio = 1.3) +\n  labs(title = \"Reference:\\nequal-area\") +\n  theme_void(base_size = 5) +\n  theme(\n    plot.background  = element_rect(fill = ELEVATED_BG, color = INK_SOFT, linewidth = 0.4),\n    panel.background = element_rect(fill = ELEVATED_BG, color = NA),\n    plot.title       = element_text(color = INK_SOFT, size = 4.5, hjust = 0.5,\n                                    margin = margin(t = 2, b = 1)),\n    plot.margin      = margin(2, 3, 3, 3, \"pt\")\n  )\nref_grob <- ggplotGrob(ref_map_plot)\n\n# Dorling-style cartogram: circles at geographic centroids, area proportional to population\n# annotation_custom placed first so data circles render on top of the inset\np <- ggplot(states, aes(x = lon, y = lat)) +\n  annotation_custom(\n    grob = ref_grob,\n    xmin = -130, xmax = -111, ymin = 22, ymax = 32\n  ) +\n  geom_point(\n    aes(size = population, fill = gdp_pc),\n    shape  = 21,\n    color  = PAGE_BG,\n    stroke = 0.5,\n    alpha  = 0.88\n  ) +\n  geom_text(\n    data     = labeled_states,\n    aes(label = abbr),\n    color    = \"white\",\n    size     = 2.2,\n    fontface = \"bold\"\n  ) +\n  scale_size_area(\n    name     = \"Population\\n(millions)\",\n    max_size = 20,\n    breaks   = c(2, 5, 10, 20, 40),\n    labels   = c(\"2\", \"5\", \"10\", \"20\", \"40\")\n  ) +\n  scale_fill_gradient(\n    name   = \"GDP per capita\\n(USD thousands)\",\n    low    = SEQ_LOW,\n    high   = SEQ_HIGH,\n    breaks = c(50, 60, 70, 80, 95),\n    labels = scales::label_number(suffix = \"k\")\n  ) +\n  guides(\n    size = guide_legend(\n      override.aes = list(fill = INK_MUTED, color = PAGE_BG, alpha = 0.9)\n    )\n  ) +\n  coord_fixed(ratio = 1.3, xlim = c(-130, -64), ylim = c(22, 51)) +\n  labs(\n    title = plot_title,\n    x     = \"Longitude\",\n    y     = \"Latitude\"\n  ) +\n  theme_minimal(base_size = 8) +\n  theme(\n    plot.background   = element_rect(fill = PAGE_BG,    color = PAGE_BG),\n    panel.background  = element_rect(fill = PAGE_BG,    color = NA),\n    panel.border      = element_blank(),\n    panel.grid.major  = element_line(color = INK_MUTED, linewidth = 0.2),\n    panel.grid.minor  = element_blank(),\n    axis.title        = element_text(color = INK,       size = 10),\n    axis.text         = element_text(color = INK_SOFT,  size = 8),\n    plot.title        = element_text(color = INK,       size = title_size,\n                                     face = \"bold\",\n                                     margin = margin(b = 10)),\n    legend.background = element_rect(fill  = ELEVATED_BG, color = INK_SOFT,\n                                     linewidth = 0.3),\n    legend.text       = element_text(color = INK_SOFT,  size = 8),\n    legend.title      = element_text(color = INK,       size = 9),\n    legend.key        = element_rect(fill = NA, color = NA),\n    legend.position   = \"right\",\n    plot.margin       = margin(t = 10, r = 10, b = 10, l = 10, unit = \"pt\")\n  )\n\n# Save\nggsave(\n  filename = sprintf(\"plot-%s.png\", THEME),\n  plot     = p,\n  device   = ragg::agg_png,\n  width    = 8,\n  height   = 4.5,\n  units    = \"in\",\n  dpi      = 400\n)\n"}