Create 2D-Histogram and Geographic Map in ggplot2 to Visualize Atlantic Tropical Storm Activities
In this article, we’ll visualize the North Atlantic hurricane activities. We’ll create a 2D histogram to summarize the frequency of hurricanes at each degree of latitude and longitude, with a world map overlay to provide the geographical context.
The dataset storms used in this work is built in the dplyr package.
library(ggplot2)library(dplyr)library(tidyr) # set up the default global themetheme_set(theme_classic(base_size =14)) head(storms, 3)
Output:
# A tibble: 3 × 13 name year month day hour lat long status category wind pressure <chr> <dbl> <dbl> <int> <dbl> <dbl> <dbl> <fct> <dbl> <int> <int> 1 Amy 1975 6 27 0 27.5 -79 tropical de… NA 25 1013 2 Amy 1975 6 27 6 28.5 -79 tropical de… NA 25 1013 3 Amy 1975 6 27 12 29.5 -79 tropical de… NA 25 1013 # ℹ 2 more variables: tropicalstorm_force_diameter <int>, # hurricane_force_diameter <int>
Visualization
Create a 2D histogram summarizing the relative frequency of hurricanes. Most hurricanes were measured every six hours during the lifetime of the storm, and involved location changes in their latitude (lac) and longitude (long). Here we use row counts to approximate the hurricane exposure in different geographical locations. geom_bin2d creates a 2D histogram, and counts the number of observations falling within each cell. Here binwidth = 1 dictates each cell in the plot to be one unit in width and height, corresponding to a geographical resolution of 1° in latitude and longitude.
Create a world map overlay on the 2D histogram. The map data map_data is built in ggplot2. It has columns of longitude (long) and latitude (lat) (same column names as in the storm dataset), and the grouping variable group to create maps with geom_polygon. The map is sketched out and the default fill is removed with fill = NA.
m <-map_data("world") %>%as_tibble() p2 <- p1 +geom_polygon(data = m, aes(group = group), fill =NA, color ="snow3", linewidth = .5) p2
Zoom-in over the north Atlantic region with an aspect ratio of 1.5, and the desired range of longitude (xlim) and latitude (ylim).
Apply a dark grey background to bring out the map outline and the storm heatmap, without complete merging with the low-activity storm data (black cells).
p5 <- p4 +# relabel the x-axisscale_x_continuous(breaks =seq(-120, 20, 40)) +# revise / add titleslabs(x ="longitude", y ="Latitude", title ="Atlantic hurricanes 1975 - 2021",subtitle ="data source: https://www.nhc.noaa.gov/data/#hurdat",fill ="relative frequency of hurricanes", # change title of legend color bar ) +# customize the legend color barguides(fill =guide_colorbar(barheight =unit(250, "pt"),barwidth =unit(10, "pt"),title.position ="left",title.theme =element_text(angle =90, hjust = .5))) p5
A final touch-up. Here we use geom_hline() and geom_vline() to create subtle grid lines to aid in visualization. Grid lines added this way lie on top of the data (the map and storm heatmap). In comparison, grids generated by theme(panel.grid = ...) reside below the data and is invisible in this example.
p6 <- p5 +theme(plot.title =element_text(size =14, face ="italic"),plot.subtitle =element_text(size =10, face ="italic", color ="snow4")) +# lay grids on top of the datageom_hline(yintercept =seq(-90, 90, 10), color ="snow4", linewidth = .1) +geom_vline(xintercept =seq(-120, 20, 20), color ="snow4", linewidth = .1) p6
library(ggplot2)library(dplyr)library(tidyr) theme_set(theme_classic(base_size =14)) p1 <- storms %>%ggplot(aes(long, lat)) +geom_bin2d(binwidth =1) +scale_fill_viridis_c(option ="A", n.breaks =6) p1 # add world mapm <-map_data("world") %>%as_tibble() p2 <- p1 +geom_polygon(data = m, aes(group = group), fill =NA, color ="snow3", linewidth = .5) p2 # Zoom in to the north Atlantic regionp3 <- p2 +coord_fixed(1.5, xlim =c(-125, 5), ylim =c(0, 65)) p3 # Apply a dark background p4 <- p3 +theme(panel.background =element_rect(fill ="grey25"))p4 # Revise axial labels, titles, and legends. p5 <- p4 +# relabel the x-axisscale_x_continuous(breaks =seq(-120, 20, 40)) +# revise / add titleslabs(x ="longitude", y ="Latitude", title ="Atlantic hurricanes 1975 - 2021",subtitle ="data source: https://www.nhc.noaa.gov/data/#hurdat",fill ="relative frequency of hurricanes", # change title of legend color bar ) +# customize the legend color barguides(fill =guide_colorbar(barheight =unit(250, "pt"),barwidth =unit(10, "pt"),title.position ="left",title.theme =element_text(angle =90, hjust = .5))) p5 # A final touch-up. Grids added with `geom_hline` and `geom_vline` reside on top of the data. In comparison, grids by `theme(panel.grid = ...)` lies below the data and is invisible in this example. p6 <- p5 +theme(plot.title =element_text(size =14, face ="italic"),plot.subtitle =element_text(size =10, face ="italic", color ="snow4")) +# lay grids on top of the datageom_hline(yintercept =seq(-90, 90, 10), color ="snow4", linewidth = .1) +geom_vline(xintercept =seq(-120, 20, 20), color ="snow4", linewidth = .1) p6
Continue Exploring — 🚀 one level up!
In the storm visualization above, geom_bin2d() is used to count observations on continuous scales (the latitude and longitude). Besides, it can be also applied for discrete scales, binned on each categorical level. Check out the following 2D histogram on discrete scales that shows the population proportion at different levels of happiness and wealth status in the United States in 2018.
While a heatmap can be created in the form of a 2D histogram with geom_bin2d(), as illustrated above, it is often created using the generic function geom_tile() or geom_raster(). Check out the following heatmap created with geom_tile() that visualizes the outbreaks of the polio disease before and after the vaccine introduction.