{"spec_id":"map-tile-background","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' map-tile-background: Map with Tile Background\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 84/100 | Created: 2026-05-27\n\nlibrary(ggplot2)\nlibrary(ragg)\n\nset.seed(42)\n\n# Theme tokens\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\"\n\n# Map surface colors (visualization area, not chrome)\nOCEAN_BG    <- if (THEME == \"light\") \"#D3E8F5\" else \"#1A2835\"\nLAND_FILL   <- if (THEME == \"light\") \"#E5DFC8\" else \"#2D2D25\"\nLAND_BORDER <- if (THEME == \"light\") \"#B0A890\" else \"#4E4C40\"\n\n# Simplified continental outlines (approximate shapes for geographic context)\ncontinents <- rbind(\n  data.frame(\n    lon   = c(-165, -125, -117, -92, -83,  -65,  -66,  -55,  -55,  -65,  -80, -100, -132, -165),\n    lat   = c(  65,   48,   32,  15,   8,   10,   45,   50,   55,   63,   65,   65,   60,   65),\n    group = \"north_america\"\n  ),\n  data.frame(\n    lon   = c( -80,  -52,  -35,  -40,  -50,  -68,  -72,  -80,  -80),\n    lat   = c(   8,    5,   -5,  -22,  -33,  -55,  -38,   -3,    8),\n    group = \"south_america\"\n  ),\n  data.frame(\n    lon   = c( -9,  -9,   3,  12,  22,  28,  30,  24,  18,   5,  -3,  -9),\n    lat   = c( 36,  44,  52,  57,  62,  70,  60,  57,  55,  52,  44,  36),\n    group = \"europe\"\n  ),\n  data.frame(\n    lon   = c( 14,  16,  18,  20,  18,  16,  14,  12,  14),\n    lat   = c( 56,  58,  63,  70,  71,  68,  63,  58,  56),\n    group = \"scandinavia\"\n  ),\n  data.frame(\n    lon   = c( -5,  15,  30,  42,  43,  40,  35,  28,  18,  10,  -5, -17, -17,  -5),\n    lat   = c( 35,  37,  30,  22,   5,  -3, -15, -30, -35, -35, -25,  -5,  14,  35),\n    group = \"africa\"\n  ),\n  data.frame(\n    lon   = c( 26,  40,  60,  80, 100, 120, 135, 142, 140, 130, 120, 110, 100, 103,  85,  70,  55,  42,  36,  26),\n    lat   = c( 38,  42,  45,  50,  55,  55,  50,  48,  40,  35,  22,  15,   5,   1,   8,  18,  22,  12,  30,  38),\n    group = \"asia\"\n  ),\n  data.frame(\n    lon   = c(114, 116, 125, 135, 145, 152, 152, 145, 135, 125, 114),\n    lat   = c(-22, -35, -38, -36, -38, -35, -20, -15, -14, -20, -22),\n    group = \"australia\"\n  )\n)\n\n# 12 major cities with annual international visitor counts (millions)\ncities <- data.frame(\n  city       = c(\"New York\", \"London\", \"Paris\", \"Tokyo\", \"Dubai\",\n                 \"Singapore\", \"Sydney\", \"Mumbai\", \"Sao Paulo\", \"Toronto\",\n                 \"Cairo\", \"Mexico City\"),\n  lon        = c(-74.01,  -0.13,   2.35, 139.69,  55.27,\n                 103.82, 151.21,  72.88, -46.63, -79.38,\n                  31.24, -99.13),\n  lat        = c( 40.71,  51.51,  48.85,  35.69,  25.20,\n                   1.35, -33.87,  19.08, -23.55,  43.65,\n                  30.04,  19.43),\n  visitors_m = c(66.6, 19.1, 38.0, 14.2, 21.3,\n                 19.8,  4.7, 10.5,  3.0,  3.1,\n                  3.7,  4.4)\n)\n\n# Label the top 6 cities (>= 14M visitors); hand-tuned nudges to avoid overlap\nlabel_cities <- cities[cities$visitors_m >= 14, ]\nlabel_cities$nudge_x <- c(-9,  -6,   5,   9,   8,   9)\nlabel_cities$nudge_y <- c( 5,   6,   6,  -6,   6,  -6)\nlabel_cities$hjust   <- c( 1,   1,   0,   0,   0,   0)\n\ntitle_str  <- \"Global City Tourism · map-tile-background · r · ggplot2 · anyplot.ai\"\ntitle_size <- max(8, round(12 * 67 / nchar(title_str)))\n\np <- ggplot() +\n  geom_polygon(\n    data      = continents,\n    aes(x = lon, y = lat, group = group),\n    fill      = LAND_FILL,\n    color     = LAND_BORDER,\n    linewidth = 0.25\n  ) +\n  geom_point(\n    data   = cities,\n    aes(x = lon, y = lat, fill = visitors_m, size = visitors_m),\n    color  = PAGE_BG,\n    shape  = 21,\n    stroke = 0.5,\n    alpha  = 0.9\n  ) +\n  geom_text(\n    data     = label_cities,\n    aes(x = lon + nudge_x, y = lat + nudge_y, label = city, hjust = hjust),\n    color    = INK,\n    size     = 2.6,\n    fontface = \"bold\"\n  ) +\n  scale_fill_gradient(\n    low  = \"#009E73\",\n    high = \"#4467A3\",\n    name = \"Annual Visitors\\n(millions)\"\n  ) +\n  scale_size_continuous(\n    range = c(3, 10),\n    guide = \"none\"\n  ) +\n  coord_cartesian(\n    xlim   = c(-160, 170),\n    ylim   = c(-55, 80),\n    expand = FALSE\n  ) +\n  labs(\n    title    = title_str,\n    subtitle = \"Top 6 destinations labelled · New York leads at 66.6M visitors/yr\",\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 = OCEAN_BG,    color = NA),\n    panel.grid.major  = element_line(color = INK_SOFT,   linewidth = 0.12, linetype = \"dotted\"),\n    panel.grid.minor  = element_blank(),\n    panel.border      = element_blank(),\n    axis.title        = element_text(color = INK_SOFT,   size = 10),\n    axis.text         = element_text(color = INK_SOFT,   size = 8),\n    plot.title        = element_text(color = INK,        size = title_size, face = \"bold\"),\n    plot.subtitle     = element_text(color = INK_SOFT,   size = 8),\n    legend.background = element_rect(fill = ELEVATED_BG, color = INK_SOFT, 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    plot.margin       = unit(c(0.4, 0.4, 0.4, 0.4), \"cm\")\n  )\n\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"}