Mapping India’s Cuisines with R: {sf}, {ggpattern}, {magick} and more
Diverse culinary heritage of India with a vibrant map created in R, blending spatial data from {sf}, creative patterns from {ggpattern}, and precise labeling from {ggrepel}. Images of iconic state cuisines were curated using {httr} and {magick}, seamlessly integrated into the visual using the power of {ggplot2}.
Maps
Geocomputation
{ggpattern}
{magick}
{ggrepel}
India
Images
Author
Aditya Dahiya
Published
January 26, 2025
Discover the rich culinary diversity of India with this map, showcasing each state and union territory filled with its most iconic cuisine, accompanied by state names for easy reference. A harmonious blend of geography and gastronomy, crafted with R.
Step 1: Get India’s Map Data, and make a base tibble
Code
# Data Import and Wrangling Toolslibrary(tidyverse) # All things tidylibrary(sf) # Handling simple features in Rlibrary(here) # Folder management# Final plot toolslibrary(scales) # Nice Scales for ggplot2library(fontawesome) # Icons display in ggplot2library(ggtext) # Markdown text in ggplot2library(showtext) # Display fonts in ggplot2library(colorspace) # Lighten and Darken colourslibrary(ggpattern) # Patterns / Images in geomslibrary(magick) # Handling imageslibrary(httr) # Downloading images from Googleindia_map <-read_sf( here::here("data", "india_map", "India_State_Boundary.shp" )) |> janitor::clean_names() |>st_simplify(dTolerance =1000) |>st_transform("EPSG:4326") |>mutate(id =row_number(),area =st_area(geometry) ) |>relocate(id)india_mapindia_map$state_name
Step 2: Get images of states’ cuisines from Google’s Programme Search Engine through an API
Code
# Get a custom google search engine and API key# Tutorial: https://developers.google.com/custom-search/v1/overview# Tutorial 2: https://programmablesearchengine.google.com/# From:https://developers.google.com/custom-search/v1/overview# google_api_key <- "LOAD YOUR GOOGLE API KEY HERE"# From: https://programmablesearchengine.google.com/controlpanel/all# my_cx <- "GET YOUR CUSTOM SEARCH ENGINE ID HERE"# Define function to download and save movie posterdownload_food_images <-function(i) { api_key <- google_api_key cx <- my_cx# Build the API request URL url <-paste0("https://www.googleapis.com/customsearch/v1?q=", URLencode(paste0( india_map$state_name[i], " state cuisine photo HD" )), "&cx=", cx, "&searchType=image&key=", api_key,"&imgSize=large", # Prioritize larger images"&imgType=photo" )# Make the request response <-GET(url) result <-content(response, "parsed")# Get the URL of the first image result image_url <- result$items[[1]]$link im <- magick::image_read(image_url) |>image_resize("x1000")# set background as whiteimage_write(image = im,path = here::here("geocomputation", "images",paste0("temp_food_india_", i,".png")),format ="png" )}for (i in18:nrow(india_map)) {download_food_images(i)}# Custom run the function for id 1, 2, 10, 20custom_ids <-c(1, 2, 10, 20)
Step 2.1: The same Code improved with ChatGPT
Code
# Add a new column to store food names in the tibbleindia_map <- india_map |>mutate(food_name =NA_character_)# Function to extract food item name from the search query resultextract_food_name <-function(items) {if (length(items) >0) {# Attempt to extract meaningful food names from the title or snippet food_name <- items[[1]]$titlereturn(food_name) } else {return(NA) # Return NA if no items are found }}# Improved function to download and save food imagesdownload_food_images <-function(i) { api_key <- google_api_key cx <- my_cx# Build the API request URL with additional filters url <-paste0("https://www.googleapis.com/customsearch/v1?q=",URLencode(paste0(india_map$state_name[i], " traditional cuisine food photo")),"&cx=", cx,"&searchType=image","&key=", api_key,"&imgSize=large", # Restrict to medium-sized images"&imgType=photo","&num=1"# Fetch only one result )# Make the request response <-GET(url)if (response$status_code !=200) {warning("Failed to fetch data for state: ", india_map$state_name[i])return(NULL) }# Parse the response result <-content(response, "parsed")# Extract the image URLif (!is.null(result$items)) { image_url <- result$items[[1]]$link food_name <-extract_food_name(result$items) } else {warning("No results found for state: ", india_map$state_name[i])return(NULL) }# Validate and process the imagetryCatch({ im <- magick::image_read(image_url) |>image_resize("x1000") # Resize image# Save the imageimage_write(image = im,path =here("geocomputation", "images", paste0("temp_food_india_", i, ".png")),format ="png" )# Add the food name to the tibble india_map$food_name[i] <- food_name }, error =function(e) {warning("Failed to process image for state: ", india_map$state_name[i]) })}# Iterate through each state and download imagesfor (i in1:nrow(india_map)) {download_food_images(i)}
Step 3: Set up Visualization Parameters
Code
# Font for titlesfont_add_google("Rye",family ="title_font") # Font for the captionfont_add_google("Voltaire",family ="caption_font") font_add_google("Saira Extra Condensed",family ="caption_font2") showtext_auto()# A base Colourbg_col <-"grey10"seecolor::print_color(bg_col)# Colour for highlighted texttext_hil <-"grey90"seecolor::print_color(text_hil)# Colour for the texttext_col <-"white"seecolor::print_color(text_col)# Define Base Text Sizebts <-90# Caption stuff for the plotsysfonts::font_add(family ="Font Awesome 6 Brands",regular = here::here("docs", "Font Awesome 6 Brands-Regular-400.otf"))github <-""github_username <-"aditya-dahiya"xtwitter <-""xtwitter_username <-"@adityadahiyaias"social_caption_1 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{github};</span> <span style='color: {text_hil}'>{github_username} </span>")social_caption_2 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{xtwitter};</span> <span style='color: {text_hil}'>{xtwitter_username}</span>")plot_caption <-paste0("**Data:** Google & Census of India", " | **Code:** ", social_caption_1, " | **Graphics:** ", social_caption_2 )rm(github, github_username, xtwitter, xtwitter_username, social_caption_1, social_caption_2)# Add text to plot-------------------------------------------------plot_title <-"Flavors of India:\nA Culinary Map"
Step 4: Use ggpattern to make final map Customizing options here.
Step 5: Save thumbnail and clean up temporary files
Code
# Saving a thumbnaillibrary(magick)# Reducing Image Size - its 15 Mb plusimage_read( here::here("geocomputation", "images","ggpattern_with_sf_india_cuisines.png" ) ) |>image_resize(geometry ="x400")# Clean Up: Do no harm and leave the world an untouched place!# Remove temporary image filesunlink(paste0("geocomputation/images/temp_food_india_", 1:nrow(india_map), ".png"))