{"spec_id":"hexbin-map-geographic","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' hexbin-map-geographic: Hexagonal Binning Map\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 89/100 | Created: 2026-05-27\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(tibble)\nlibrary(scales)\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\"\nMAP_WATER   <- if (THEME == \"light\") \"#C8DCE8\" else \"#1A2530\"\nMAP_LAND    <- if (THEME == \"light\") \"#E6E0D4\" else \"#2C2A22\"\nMAP_BORDER  <- if (THEME == \"light\") \"#9A9890\" else \"#58584E\"\n\n# NYC metro area taxi pickups — simulated density clusters\n# Centres: Midtown, Greenwich Village, UWS, Chelsea, S.Brooklyn,\n#          Midtown East, Lower Manhattan, JFK-adjacent, LGA-adjacent, Jersey City\ncluster_centers <- tibble::tibble(\n  lat   = c(40.756, 40.730, 40.775, 40.744, 40.676,\n             40.762, 40.710, 40.645, 40.769, 40.727),\n  lon   = c(-73.989, -74.003, -73.981, -73.995, -73.982,\n             -73.971, -73.997, -73.794, -73.863, -74.077),\n  n     = c(5000L, 3200L, 2400L, 2600L, 1800L,\n             1400L, 1000L, 700L, 600L, 500L),\n  s_lat = c(0.010, 0.009, 0.011, 0.009, 0.013,\n             0.010, 0.009, 0.009, 0.008, 0.009),\n  s_lon = c(0.013, 0.011, 0.013, 0.011, 0.015,\n             0.012, 0.011, 0.012, 0.010, 0.011)\n)\n\npickups <- dplyr::bind_rows(\n  lapply(seq_len(nrow(cluster_centers)), function(i) {\n    cc <- cluster_centers[i, ]\n    tibble::tibble(\n      lat = rnorm(cc$n, cc$lat, cc$s_lat),\n      lon = rnorm(cc$n, cc$lon, cc$s_lon)\n    )\n  })\n) |> dplyr::filter(\n  lat >= 40.60, lat <= 40.83,\n  lon >= -74.15, lon <= -73.75\n)\n\n# --- Simplified NYC geographic context (hand-digitized approximate outlines) ---\n# sf/rnaturalearth not available; inline polygons provide coastline context.\n# Water shows as panel background (MAP_WATER).\n\n# New Jersey — land mass west of the Hudson River\nnj_land <- tibble::tibble(\n  group = \"nj\",\n  lon   = c(-74.15, -74.15, -73.960, -73.980, -74.015, -74.034, -74.050,\n            -74.085, -74.115, -74.15),\n  lat   = c(40.60,  40.83,  40.830,  40.800,  40.765,  40.725,  40.700,\n            40.660,  40.630,  40.60)\n)\n\n# Manhattan Island — narrow north-south strip between Hudson and East River.\n# Points trace the eastern shoreline (Battery → Harlem) then west (Harlem → Battery).\nmanhattan_land <- tibble::tibble(\n  group = \"manhattan\",\n  lon   = c(\n    # East shore: south to north\n    -73.971, -73.972, -73.975, -73.971, -73.965, -73.952, -73.940, -73.928,\n    # North clip at lat 40.830 (Inwood not in view)\n    -73.934, -73.943,\n    # West shore: north to south\n    -73.955, -73.965, -73.973, -73.982, -73.991, -74.001, -74.010, -74.014, -73.971\n  ),\n  lat   = c(\n    40.700, 40.725, 40.742, 40.758, 40.764, 40.788, 40.808, 40.830,\n    40.830, 40.822,\n    40.813, 40.800, 40.782, 40.764, 40.749, 40.732, 40.715, 40.703, 40.700\n  )\n)\n\n# Brooklyn and Queens — southeastern land mass south of the East River\nbq_land <- tibble::tibble(\n  group = \"bq\",\n  lon   = c(-74.030, -73.993, -73.975, -73.960, -73.938, -73.880, -73.800,\n            -73.75,  -73.75,  -74.030),\n  lat   = c(40.700,  40.698,  40.684,  40.672,  40.665,  40.651,  40.636,\n            40.628,  40.60,   40.60)\n)\n\nnyc_geo <- dplyr::bind_rows(nj_land, manhattan_land, bq_land)\n\n# --- Plot ---\n\nplot_title <- \"hexbin-map-geographic · r · ggplot2 · anyplot.ai\"\ntitle_size <- max(8L, round(12 * min(1.0, 67 / nchar(plot_title))))\n\np <- ggplot() +\n  # Geographic base map\n  geom_polygon(\n    data      = nyc_geo,\n    aes(x = lon, y = lat, group = group),\n    fill      = MAP_LAND,\n    color     = MAP_BORDER,\n    linewidth = 0.30\n  ) +\n  # Hexbin density layer\n  geom_hex(\n    data  = pickups,\n    aes(x = lon, y = lat),\n    bins  = 38,\n    alpha = 0.88\n  ) +\n  # Storytelling annotation: point to primary hotspot\n  annotate(\"segment\",\n    x = -73.975, xend = -73.989,\n    y = 40.773,  yend = 40.762,\n    color = INK, linewidth = 0.45,\n    arrow = arrow(length = unit(0.18, \"cm\"), type = \"closed\")\n  ) +\n  annotate(\"text\",\n    x = -73.968, y = 40.776,\n    label    = \"Midtown hotspot\",\n    color    = INK,\n    size     = 2.7,\n    fontface = \"italic\",\n    hjust    = 0\n  ) +\n  scale_fill_gradient(\n    low    = \"#009E73\",\n    high   = \"#4467A3\",\n    name   = \"Pickup\\nCount\",\n    labels = scales::comma,\n    guide  = guide_colorbar(barwidth = 0.8, barheight = 8)\n  ) +\n  scale_x_continuous(\n    labels = function(x) sprintf(\"%.2f°W\", -x),\n    expand = expansion(mult = 0.0)\n  ) +\n  scale_y_continuous(\n    labels = function(y) sprintf(\"%.2f°N\", y),\n    expand = expansion(mult = 0.0)\n  ) +\n  coord_fixed(\n    ratio = 1,\n    xlim  = c(-74.15, -73.75),\n    ylim  = c(40.60, 40.83)\n  ) +\n  labs(\n    x     = \"Longitude\",\n    y     = \"Latitude\",\n    title = plot_title\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 = MAP_WATER, color = NA),\n    panel.grid.major  = element_line(color = INK_SOFT, linewidth = 0.10),\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, face = \"bold\"),\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.position   = \"right\",\n    plot.margin       = margin(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"}