Created
June 5, 2025 15:28
-
-
Save walkerke/2b62551c5fecfd88575a1b5de8793bda to your computer and use it in GitHub Desktop.
Revisions
-
walkerke created this gist
Jun 5, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,179 @@ library(mapgl) # pak::pak("walkerke/mapgl") library(tidycensus) library(dplyr) library(viridisLite) # Get viridis colors viridis_colors <- viridis(5) # Get median household income by county in Texas tx_income <- get_acs( geography = "county", state = "TX", variables = "B19013_001", year = 2023, geometry = TRUE ) %>% mutate( # Format income with comma separator income_formatted = scales::dollar(estimate, accuracy = 1), # Create income categories for styling income_category = case_when( estimate < 50000 ~ "low", estimate < 75000 ~ "medium", estimate < 100000 ~ "high", TRUE ~ "very high" ), # Calculate margin of error percentage moe_percent = round((moe / estimate) * 100, 1), # Calculate percentile for progress bar (0-100) income_percentile = round(percent_rank(estimate) * 100) ) # Create the map with an awesome styled popup mapboxgl(style = mapbox_style("dark"), bounds = tx_income) %>% add_fill_layer( id = "income", source = tx_income, fill_color = interpolate( column = "estimate", values = c(25000, 50000, 75000, 100000, 125000), stops = viridis_colors, na_color = "#808080" ), popup = concat( '<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); ', 'padding: 20px; border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,0.4); ', 'color: white; font-family: -apple-system, BlinkMacSystemFont, sans-serif; ', 'max-width: 300px; position: relative; overflow: hidden;">', # CSS animations '<style>', '@keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } }', '@keyframes slideIn { from { width: 0%; } to { width: ', get_column("income_percentile"), '%; } }', '@keyframes shimmer { 0% { background-position: -200% center; } 100% { background-position: 200% center; } }', '.income-badge { animation: pulse 2s ease-in-out infinite; }', '.progress-fill { animation: slideIn 1s ease-out forwards; }', '.shimmer { background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); ', 'background-size: 200% 100%; animation: shimmer 2s linear infinite; }', '</style>', # Decorative background element with rotation animation '<div style="position: absolute; top: -50px; right: -50px; width: 150px; height: 150px; ', 'background: rgba(255,255,255,0.1); border-radius: 50%; ', 'animation: pulse 4s ease-in-out infinite;"></div>', # County name with icon '<h2 style="margin: 0 0 15px 0; font-size: 24px; font-weight: 700; ', 'text-shadow: 2px 2px 4px rgba(0,0,0,0.3); display: flex; align-items: center; gap: 10px;">', '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">', '<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>', '<circle cx="12" cy="10" r="3"></circle></svg>', get_column("NAME"), '</h2>', # Income display with animated badge '<div class="income-badge" style="background: rgba(255,255,255,0.2); padding: 15px; border-radius: 8px; ', 'margin-bottom: 15px; backdrop-filter: blur(10px); position: relative;">', '<div class="shimmer" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; ', 'border-radius: 8px; pointer-events: none;"></div>', '<div style="font-size: 14px; opacity: 0.9; margin-bottom: 5px;">Median Household Income</div>', '<div style="font-size: 32px; font-weight: 700; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);">', get_column("income_formatted"), '</div>', '</div>', # Stats grid with conditional coloring '<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 15px;">', # MOE card '<div style="background: rgba(255,255,255,0.15); padding: 10px; border-radius: 6px; text-align: center;">', '<div style="font-size: 12px; opacity: 0.8; margin-bottom: 3px;">Margin of Error</div>', '<div style="font-size: 18px; font-weight: 600;">±', get_column("moe_percent"), '%</div>', '</div>', # Category card with conditional background (using viridis-inspired colors) '<div style="background: ', 'rgba(', match_expr( column = "income_category", values = c("low", "medium", "high", "very high"), stops = c("68,1,84", "59,82,139", "33,145,140", "253,231,37") ), ',0.3); padding: 10px; border-radius: 6px; text-align: center;">', '<div style="font-size: 12px; opacity: 0.8; margin-bottom: 3px;">Income Level</div>', '<div style="font-size: 16px; font-weight: 600; text-transform: uppercase;">', get_column("income_category"), '</div>', '</div>', '</div>', # Animated progress bar with cleaner percentile display '<div style="margin-bottom: 10px;">', '<div style="font-size: 12px; opacity: 0.8; margin-bottom: 5px;">', 'Percentile: ', get_column("income_percentile"), '</div>', '<div style="background: rgba(0,0,0,0.3); height: 12px; border-radius: 6px; overflow: hidden; position: relative;">', '<div class="progress-fill" style="background: linear-gradient(90deg, ', viridis_colors[1], ' 0%, ', viridis_colors[2], ' 25%, ', viridis_colors[3], ' 50%, ', viridis_colors[4], ' 75%, ', viridis_colors[5], ' 100%); ', 'height: 100%; width: ', get_column("income_percentile"), '%; ', 'box-shadow: 0 0 10px rgba(255,255,255,0.5); border-radius: 6px;"></div>', '<div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; ', 'background: repeating-linear-gradient(45deg, transparent, transparent 10px, rgba(255,255,255,0.1) 10px, rgba(255,255,255,0.1) 20px); ', 'animation: slideIn 1s ease-out forwards;"></div>', '</div>', '</div>', # Data source footer '<div style="font-size: 11px; opacity: 0.7; text-align: center; ', 'padding-top: 10px; border-top: 1px solid rgba(255,255,255,0.2);">', '<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" style="vertical-align: middle; margin-right: 3px;">', '<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.94-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/>', '</svg>', 'American Community Survey 2023 (5-year estimates)', '</div>', '</div>' ) ) %>% add_continuous_legend( colors = viridis_colors, values = c("$25k", "$50k", "$75k", "$100k", "$125k"), legend_title = "Median Household Income", position = "bottom-left", margin_bottom = 30 )