Postal map of Chandigarh (India) using data.gov.in
Combining {sf}, {terra}, {tidyterra} and {osmdata} to make beautiful postal code maps
Maps
Open Street Maps
data.gov.in
{terra}
tidyterra
India
Haryana
Chandigarh
Author
Aditya Dahiya
Published
May 10, 2025
About the Data
The All India Pincode Boundary GeoJSON dataset, available here, provides comprehensive geospatial information delineating postal code boundaries across India. This open data resource, published by the Government of India, includes GeoJSON files that map pincode regions with associated attributes such as pincode numbers, district names, and state names. The dataset is designed to facilitate applications in urban planning, logistics, demographic analysis, and geospatial research. Maintained under the Open Government Data (OGD) Platform India, it ensures accessibility and transparency, allowing developers, researchers, and policymakers to leverage accurate and standardized pincode-level geospatial data for various analytical and operational purposes.
Figure 1: This map visualizes the postal zones and post office boundaries of Chandigarh, India, using open geospatial data from data.gov.in. Each zone is color-coded by post office, with labels displaying office names and pincodes, crafted with {sf}, {terra}, and {ggtext} for precision. A Stadia Maps base layer, fetched via {ggmap}, enhances context, while {ggplot2} and {tidyterra} ensure a polished output.
How I made this graphic?
This map of postal zones and post office boundaries in Chandigarh, India, was crafted using R and a suite of powerful packages for spatial data manipulation and visualization. The process started by loading key packages with pacman::p_load(), including sf for handling vector spatial data, terra for raster data support, tidyterra for integrating raster data with ggplot2, tidyverse for data wrangling, ggmap for fetching base maps, showtext for custom fonts, and ggtext for rich text labeling. I imported the All India Pincode Boundary GeoJSON data using read_sf() from the sf package, transformed it to the EPSG:4326 coordinate system with st_transform(), and filtered it to include only Chandigarh’s postal codes (160001 to 160099). A base map was retrieved from Stadia Maps via get_stadiamap(), using the bounding box of the Chandigarh postal data. The map was then built with ggplot2, layering the base map raster with geom_spatraster_rgb(), postal boundaries with geom_sf(), and post office labels with geom_richtext(). Custom fonts were applied using showtext, and the plot was polished with a minimal theme and saved as a high-resolution PNG using ggsave().
Loading required libraries
Code
pacman::p_load( scales, # Nice Scales for ggplot2 fontawesome, # Icons display in ggplot2 ggtext, # Markdown text support for ggplot2 showtext, # Display fonts in ggplot2 colorspace, # Lighten and Darken colours sf, # Maps, Simple Features in R terra, # Handling Rasters tidyterra, # Rasters with ggplot2 tidyverse, # All Data Wrangling and ggplot2 fontawesome, # Using fonts geodata, # Get Administrative Boundaries Data ggmap # Get Base Raster Maps)india_pin_code <-read_sf( here::here("data","india_map","All_India_pincode_Boundary-19312.geojson" )) |> janitor::clean_names() |>st_transform("EPSG:4326") |>mutate(pincode =parse_number(pincode))haryana_map <-read_sf( here::here("data","haryana_map","HARYANA_DISTRICT_BDY.shp" )) |> janitor::clean_names() |>mutate(district =str_replace_all(district, ">", "A"),district =str_replace_all(district, "\\|", "I"),district =str_to_title(district) ) |>st_transform("EPSG:4326")haryana_outline <-read_sf( here::here("data","haryana_map","HARYANA_STATE_BDY.shp" )) |> janitor::clean_names() |>st_simplify(dTolerance =1000) |>st_transform("EPSG:4326")chandigarh_postal <- india_pin_code |>filter(pincode >=160001& pincode <160100) |>mutate(label_name =str_remove_all(office_name, "SO"),label_name =str_remove_all(label_name, "Chandigarh"),label_name =paste("<b>", label_name, "</b><br><span style='font-size:30pt'>", pincode,"</span>" ) ) |>relocate(label_name)# register_stadiamap("YOUR KEY HERE")get_map_bbox <- chandigarh_postal |>st_bbox()names(get_map_bbox) <-c("left", "bottom", "right", "top")base_map <-get_stadiamap(bbox = get_map_bbox,zoom =14,maptype ="stamen_toner_background") |> terra::rast()# ggplot() +# geom_spatraster_rgb(data = base_map)# Get post offices in Chandigarh# pacman::p_load(osmdata)# # chandigarh_postoffices <- opq(bbox = st_bbox(chandigarh_postal)) |> # add_osm_feature(# key = "amenity",# value = c("post_office", "post_box", "post_depot")# ) |> # osmdata_sf()
Visualization Parameters
Code
# Font for titlesfont_add_google("Saira",family ="title_font") # Font for the captionfont_add_google("Saira Extra Condensed",family ="caption_font") # Font for plot textfont_add_google("Saira Condensed",family ="body_font") showtext_auto()# cols4all::c4a_gui()mypal <- paletteer::paletteer_d("NineteenEightyR::sunset2")[c(5,3,1)]# A base Colourbg_col <-"white"seecolor::print_color(bg_col)# Colour for highlighted texttext_hil <-"grey30"seecolor::print_color(text_hil)# Colour for the texttext_col <-"grey30"seecolor::print_color(text_col)line_col <-"grey30"# 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**: data.gov.in & Stadia Maps"," | **Code:** ", social_caption_1, " | **Graphics:** ", social_caption_2 )rm(github, github_username, xtwitter, xtwitter_username, social_caption_1, social_caption_2)
# Saving a thumbnaillibrary(magick)# Saving a thumbnail for the webpageimage_read(here::here("data_vizs", "viz_chd_postal_codes.png")) |>image_resize(geometry ="x400") |>image_write( here::here("data_vizs", "thumbnails", "viz_chd_postal_codes.png" ) )
Session Info
Code
pacman::p_load( scales, # Nice Scales for ggplot2 fontawesome, # Icons display in ggplot2 ggtext, # Markdown text support for ggplot2 showtext, # Display fonts in ggplot2 colorspace, # Lighten and Darken colours sf, # Maps, Simple Features in R terra, # Handling Rasters tidyterra, # Rasters with ggplot2 tidyverse, # All Data Wrangling and ggplot2 fontawesome, # Using fonts geodata, # Get Administrative Boundaries Data ggmap # Get Base Raster Maps)sessioninfo::session_info()$packages |>as_tibble() |> dplyr::select(package, version = loadedversion, date, source) |> dplyr::arrange(package) |> janitor::clean_names(case ="title" ) |> gt::gt() |> gt::opt_interactive(use_search =TRUE ) |> gtExtras::gt_theme_espn()
Table 1: R Packages and their versions used in the creation of this page and graphics