ggplot2 Country Map

Author

David Gerbing

Published

Apr 22, 2026, 09:07 am

Access Needed Packages

As always, all contributed packages must first be installed into the R library with install.packages(), also accessed from the RStudio Tools menu. Then, accessed by retrieving them from the library with the library() function.

suppressPackageStartupMessages(library(lessR))
style(suggest=FALSE)
library(sf)
Linking to GEOS 3.13.0, GDAL 3.8.5, PROJ 9.5.1; sf_use_s2() is TRUE
library(rnaturalearth)  # access map data for plotting Italy
library(ggrepel)  # access the function for plotting city names
Loading required package: ggplot2
library(ggplot2)

Overview

Goal: Create a ggplot2 map of Italy with its cities of population size greater than 250,000 plotted on the map as a bubble plot. The size of each bubble reflects the city population.

Our map will consist of three different layers, each layer plotted separately by ggplot2: Italy, the cities, and the city names. The data for each distinct set of information is constructed separately.

Prerequisite: First read the beginning part of the mapview reading where the mapping simple features data frame for the most populous Italian cities is created.

Italy Map Data

Get the Map of Italy

To plot a map of Italy with ggplot2, we also need the shape of Italy and its constituent states. The needed shapes are constructed from polygons, stored in a separate data frame with a special format called a simple features data frame or sf.

The following code lists all the countries in the rnaturalearth package for which mapping information is available.

world <- ne_countries(returnclass="sf")
world[,"name"][[1]]

However, in this example it is not necessary to run this code as we already know to identify Italy by its name.

Obtain these polygons that define the countries and states (or equivalent) from the rnaturalearth package with the ne_states() function. Identify each country in this data set with the value of the country parameter, which is the full country name, such as “United States of America”, “Kenya”, or “Italy”.

Extract and then save only the Italian map data into the simple features data frame. Here, save the shape information in the data frame italy_sf.

italy_sf <- ne_states(country="Italy")

The information contained within the sf type of data frame is more complex than a regular data frame. These data files contain much geometric information that defines the boundaries of each individual state, here within Italy. We could apply the head() function to the created simple features data frame italy_sf. However, this function is not applied here because there is much information that is not particularly meaningful to view.

Get the City Data

The Italian city data frame was already computed in the mapview reading. Here, it is read separately after have been written from that earlier exercise. (You can write your own file from the earlier work, or just repeat those steps.)

cities <- Read("~/Documents/000/521/521Content/Week4/data/cities_sf.xlsx",
               quiet=TRUE)
cities
       city     lat     lng country population
1      Rome 41.8931 12.4828   Italy    2748109
2     Milan 45.4669  9.1900   Italy    1354196
3    Naples 40.8358 14.2486   Italy     913462
4     Turin 45.0792  7.6761   Italy     841600
5   Palermo 38.1157 13.3613   Italy     630167
6     Genoa 44.4072  8.9340   Italy     558745
7   Bologna 44.4939 11.3428   Italy     387971
8  Florence 43.7714 11.2542   Italy     360930
9      Bari 41.1253 16.8667   Italy     316015
10  Catania 37.5027 15.0873   Italy     311584
11   Verona 45.4386 10.9928   Italy     255588
12   Venice 45.4397 12.3319   Italy     250369

Now, transform to simple features, also done earlier.

cities_sf <- st_as_sf(cities, coords=c("lng", "lat"),
                        crs=st_crs(italy_sf), remove=FALSE)

Create the Map

Create this map layer-by-layer.

  1. map of Italy
  2. the ten cities, each plotted as a bubble reflecting population size
  3. text that labels the cities.

To create the map we need the country information, here for Italy, and the city information, here the 10 largest cities in Italy. As such, wneed access to two simple feature data frames that we created: italy_sf and cities_sf.

The name of a ggplot2 plotted geometric object is called a geom. Plot the first two layers with the simple features geom, geom_sf(), which transforms the sf polygon data into a visualization, the corresponding part of the map.

Layer 1: The simple features version of a data frame, italy_sf, contains the polygon information to plot the map of Italy. Reduce the size of the border lines with the size parameter set to a value less than 1, here set at 0.2.

Draw the map of Italy with colors. The ggplot2 functions (and also the lessR functions) apply the parameters fill and color to customize visualizations. The parameter fill specifies the interior color of a polygon, the color that fills the polygon. The parameter color specifies the exterior color of the object, the color as it appears to someone viewing the object from the outside. To view all named colors with the color illustrated, invoke the lessR function showColors(), which directs the output to a pdf file.

geom_sf(data=italy_sf, fill="azure", color="burlywood", size=.2)

Layer 2: The second application of geom_sf() plots the cities, with the corresponding population mapped to the size of the plotted point. Specify a mild transparency for each point by setting the alpha parameter to 0.7. The optional scale_size_area() function instructs the scaling of the dots based on area instead of radius which, I think, makes more interesting sizing of the points.

geom_sf(data=cities_sf, mapping=aes(size=population), alpha=.7)

Layer 3: The geom_text_repel() function from the ggrepl package plots the third layer, the city names, available in the cities_sf data frame. It uses the base R parameter name for color, col, instead of color. The plotted label is the variable city, a variable in the cities.sf simple features version of the data frame. Set the size of the labels with the size parameter.

geom_text_repel(data=cities_sf, aes(longitude, latitude, label=city),  
                size=3.75, col="black")

Following is the complete ggplot2 instructions for visualizing each of the three layers with a single call to the ggplot() function. Because there is a different data frame accessed by each layer, the data parameter is specified for each layer instead of the ggplot() function directly.

ggplot() +
  geom_sf(data=italy_sf, fill="azure", color="burlywood", size=.2) +
  geom_sf(data=cities_sf, mapping=aes(size=population), alpha=.7) +
  scale_size_area() +
  geom_text_repel(data=cities_sf, aes(lng, lat, label=city),
                  size=3.75, col="black")

The text placement algorithm for geom_text_repel() in this ggplot2 function call is insensitive to the mapping of population to the size of plotted point.1 For the large bubbles, the label tends to be too close or overlap with the bubble. To further customize, use the nudge_y parameter to change the default vertical location of each of the ten plotted points. The nudge units are the same as those expressed on the corresponding axis, here the latitude of each city on the vertical axis.

1 There is a way to account for different point sizes but requires the nudge_y() function. See https://ggrepel.slowkow.com/articles/examples.html, which also illustrates many more features of ggrepl functions.

ggplot() +
  geom_sf(data=italy_sf, fill="azure", color="burlywood", size=.2) +
  geom_sf(data=cities_sf, mapping=aes(size=population), alpha=.7) +
  scale_size_area() +
  geom_text_repel(data=cities_sf, aes(lng, lat, label=city),
                  size=3.75, col="black", 
                  nudge_y=c(.5,.4,.5,-.6,-.4,.4,-.2,0,.5,.4,-.4,0))

Now we have the finished map of Italy in custom colors with its ten largest cities located and plotted according to population size.