I searched already a lot for my specific problem but could not find a solution. Although, I think it is quite easy to solve, but I am new to r.
What i want to do: I would like to plot a map of India held in black and white. And I want to plot all the places I visited with dots and the corresponding name. I managed to do all that with the code below.
My problem: How can I write the code in a simpler way for plotting the visited places? I don't want to create all the variables like visit.x and visit.y. Further, I would have to write for every place the code for geom_point and geom_text. When I tried to plot more than one place at once with e.g.
visited <- c("New Delhi", "Rishikesh")
then I got the error message "Aesthetics must be either length 1 or the same as the data".
My question: How can I just store all the visited places in one variable and plot it in one run? Like that I just need one line for geom_point and not for every place I want to plot.
#Load required packages
library(maps)
library(ggmap)
library(ggplot2)
#Dataframe with country relevant information
map <- fortify(map(fill = T, plot = F, region = "India"))
#Places I want to mark on the map
visited <- c("New Delhi")
visited2 <- c("Rishikesh")
visited3 <- c("Agra")
#Extracting long / lat of the places
visit.x <- geocode(visited)$lon
visit.y <- geocode(visited)$lat
visit.x2 <- geocode(visited2)$lon
visit.y2 <- geocode(visited2)$lat
visit.x3 <- geocode(visited3)$lon
visit.y3 <- geocode(visited3)$lat
#Defining font
font = c("Courier")
font.size = 3
#Specifing the look of the map with ggplot2
map_india <- ggplot(data = map, aes(x = long, y = lat, group = group)) +
geom_polygon(fill = "white") +
geom_path(colour = "black") +
theme(panel.background = element_rect(fill = "#000000"),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.ticks = element_blank(),
axis.text.y = element_blank(),
axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank())
#Plotting the places I want on the map with labels
map_india <- map_india +
geom_point(aes(x = visit.x, y = visit.y)) +
geom_text(data = NULL, x = visit.x - 1, y = visit.y + 0.2, label = "New Delhi", size = font.size, family = font) +
geom_point(aes(x = visit.x2, y = visit.y2)) +
geom_text(data = NULL, x = visit.x2 - 1, y = visit.y2 + 0.2, label = "Rishikesh", size = font.size, family = font) +
geom_point(aes(x = visit.x3, y = visit.y3)) +
geom_text(data = NULL, x = visit.x3, y = visit.y3 + 0.5, label = "Agra", size = font.size, family = font) +
coord_fixed(0.8)
#Creating pdf
pdf("India.pdf", height = 11.69, width = 16.53)
print(map_india)
dev.off()
As #hrbrmstr suggested in the comment above, ggplot2 is designed to work with data.frames, so it works best to keep your data in one throughout. Doing so actually simplifies the code a lot, too:
library(tidyverse) # for ggplot2 and `%>%`
library(ggmap)
library(ggrepel) # for geom_text_repel, though adjust overlaps manually if you prefer
cities <- data_frame(city = c("New Delhi", "Rishikesh", "Agra")) %>% # start data.frame
mutate_geocode(city) # use ggmap function to add lon/lat columns
cities
#> # A tibble: 3 × 3
#> city lon lat
#> <chr> <dbl> <dbl>
#> 1 New Delhi 77.20902 28.61394
#> 2 Rishikesh 78.26761 30.08693
#> 3 Agra 78.00807 27.17667
box <- geocode('India', output = 'more') # get lon/lat for bounding box
box
#> lon lat type loctype address north south east
#> 1 78.96288 20.59368 country approximate india 35.5087 6.753516 97.39556
#> west country
#> 1 68.16289 India
get_stamenmap(bbox = c(left = box$west, # get background tiles, set bounding box
bottom = box$south,
right = box$east,
top = box$north),
maptype = 'toner-background', # set map style
zoom = 5) %>%
ggmap(extent = 'device') + # note switch from %>% to + as moves to ggplot
geom_point(aes(x = lon, y = lat), data = cities) + # add points
geom_text_repel(aes(x = lon, y = lat, label = city), data = cities) # add labels
Adjust as you like.
Related
I am using R as a GIS tool for creating maps.
I wanted to create a contour, or heatmap of a species distribution on a geographical area.
I wanted to see on the map where each species (animal or plant) is present and color the area in a specific color.
I am using a dataset downloaded from GBIF.
You can download the datasets from my GitHub ([https://github.com/RosarioIacono/stackoverflow_question/blob/master/species2t.csv][1]).
species <- read.delim("./species.txt")
library(readr)
species2t <- read_csv("species2t.csv")
View(species2t)
ggmap(map1)+
stat_density_2d(data = subset(species2t, order=="Anseriformes"),
aes( x = decimalLongitude,
y = decimalLatitude,
fill = ..level..),
alpha = .3,
geom = "polygon",
color = species)+
theme(legend.position = "none")
But I get an error:
Error: Aesthetics must be either length 1 or the same as the data (190): colour
I don't have your data frame, but I think your problem comes with one of the groups having n=1. This can be caused by, some of your species_dens's longitude and latitude being out of the map:
library("ggmap")
map <- c(left = -0.7, bottom = 53, right =-0.3 , top =53.6 )
map1<-get_stamenmap(map, maptype = "toner-lite")
#simulate data
species_dens = data.frame(species=c("A","B","A","B"),
decimalLongitude=c(-0.4,-0.5,-0.3,-0.2),
decimalLatitude=c(53.1,53.2,53.3,53.4))
# returns your error
ggmap(map1)+
geom_density_2d(data = species_dens,aes( x = decimalLongitude,
y = decimalLatitude,
colour = species))
From the above, you can see the last data point is off the map, so if you do geom_density with your limits, the species "B" will have n=1. Using your current dataset, if you set the colors to be species, you still end up with n=1:
library(readr)
species2t <- read_csv("species2t.csv")
X=subset(species2t, order=="Anseriformes")
table(X$species)
Anas crecca Anas platyrhynchos Anas strepera Anser anser
1 1 1 1
Aythya fuligula Cygnus olor Tadorna tadorna
1 1 1
This means you cannot colour according to species. But you see how this order is distributed:
ggmap(map1)+
stat_density_2d(data = X,
aes( x = decimalLongitude,
y = decimalLatitude,
fill = ..level..),
alpha = .3,
geom = "polygon")+
theme(legend.position = "none")
I am doing the map of the world showing profit and loss from different countries.
I already plotted the map, I prepared and joined data, Im at the final step.
My dataset is 'data_profit', 'Total_profit' are values Im using (negative and positive ones)- and it's used to fill the map with colors, depending on the value.
Rest of the code is map plotting.
ditch_the_axes <- theme(
axis.text = element_blank(),
axis.line = element_blank(),
axis.ticks = element_blank(),
panel.border = element_blank(),
panel.grid = element_blank(),
axis.title = element_blank()
)
terra<-ggplot(data_profit, aes( x = long, y = lat, group = group )) +
geom_polygon(aes(x = long, y = lat,fill = Total_profit),color = "black")+
coord_fixed(1.3) +
theme_bw() +
ditch_the_axes+
scale_fill_gradient2( low="black", high="red", space ="Lab" )
data_profit <-
structure(list(long = c(-69.8991241455078, -69.8957061767578,
-69.9421920776367, -70.004150390625, -70.0661163330078, -70.0508804321289
), lat = c(12.4520015716553, 12.4229984283447, 12.4385251998901,
12.50048828125, 12.5469722747803, 12.5970697402954), group = c(1,
1, 1, 1, 1, 1), order = 1:6, region = c("Aruba", "Aruba", "Aruba",
"Aruba", "Aruba", "Aruba"), Total_profit = c(0, 0, 0, 0, 0, 0
)), row.names = c(NA, 6L), class = "data.frame")
and this is the output map:
So, the thing is the final map doesn't show the negative values (which should be in shade of black and grey). I checked whether 'Total_profit' values are numeric (with is.finite and is.numeric).
Do you have any idea what to change in the code?
Your question is unfortunately not reproducible, so I created some different sample data. I also suggest using "world map" and geom_map instead of geom_polygon (see below).
I assume the problem in your plot is a combination of missing values and the lack of correct limits in scale_..._gradient. Using scale_...gradientn comes with another problem - the asymmetric midpoint. (see here)
library(tidyverse)
library(scales) #load this for the mid-point problem and using scales::rescale (see below)
WorldData <- map_data('world') #easier way to draw countries than using geom_polygon
# In the next steps I am merging your profit data with the world map. I am creating a different sample data frame
data_profit <- data.frame(region = sample(WorldData$region,6), Total_profit = c(-5, -3, 0, 3, 5, 10))
map_profit <- dplyr::left_join(WorldData, data_profit, by ='region')
#> Warning: Column `region` joining character vector and factor, coercing into
#> character vector
# This plot is the easier one - using scale_fill_gradient(...). No mid-point problem. Note I am specifying the NA color using na.value.
# I am also specifying the limits using limits = ... I am not hard coding this, giving more flexibility for future graphs.
ggplot() +
geom_map(data = map_profit, map = WorldData,
aes(x = long, y = lat, map_id = region, fill = Total_profit, na.rm = FALSE)) +
scale_fill_gradient(low="black", high="red", na.value = 'blue',
limits = c(min(map_profit$Total_profit), max(map_profit$Total_profit)))
#> Warning: Ignoring unknown aesthetics: x, y, na.rm
# The next plot comes with the problem of an asymmetric mid-point
# I therefore rescale the values using rescale
ggplot() +
geom_map(data = map_profit, map = WorldData,
aes(x = long, y = lat, map_id = region, fill = Total_profit, na.rm = FALSE)) +
scale_fill_gradientn(colors = c("black", 'white', "red"), na.value = 'blue',
values = rescale(c(min(map_profit$Total_profit, na.rm = TRUE),0, max(map_profit$Total_profit,na.rm = TRUE))),
limits = c(min(map_profit$Total_profit), max(map_profit$Total_profit)))
#> Warning: Ignoring unknown aesthetics: x, y, na.rm
Created on 2019-07-11 by the reprex package (v0.3.0)
I am plotting a map of my study area and I am having problems to edit the legend title.
I need it to be "Projected fruit productivity in fallows in 40 yrs (fruits ha^-1) written in four lines. I could use bquote() to plot the -1 as a superscript. But it created an extra space that I cannot figure it out how to take it off. The extra space only appears when the title is divided into multiple lines.
Also, expression(atop()) creates the superscript but once I tried to divide it into more than two lines it does not show lines three and four.
This is the Map with the extra space using bquote()
This is the Map with the four line title using expression(atop())
I did try different solutions found on the internet, including this post. But they all plot the fourth line with the extra space or only plot the first or second line.
Bellow is the code I am using. Any help is welcomed.
The comments are different tries.
Data = spatial_dist_fallows.csv
library(sf) #sf = simple feature
library(ggplot2)
library(dplyr)
PAECM_fallows <-read.csv("spatial_dist_fallows.csv")
PAECM_fallows_sp <- st_as_sf(PAECM_fallows,coords = c("X", "Y"),crs = "+proj=longlat +datum=WGS84 +no_defs")
custom_bins_fruit = c(0,60,120,180,240,1400)
PAECM_fallows_fruit <- PAECM_fallows_sp %>%
mutate(prod_cat_fallow = cut(prod_40, breaks= custom_bins_fruit),
age_cat_fallow = cut(age, breaks = c(11,17,22,29,60)))
prod_map_PAECM_fruit<-ggplot()+
geom_sf(data = PAECM_fallows_fruit,aes(size = prod_cat_fallow), shape = 18, show.legend = "point")+
scale_size_manual(values= c(2,3,4,5,6),
# name = "Projected fruit\nproductivity in\nfallows in 40 yrs \n(fruits ha^-1)",
name = bquote("Projected fruit\nproductivity in\nfallows in 40 yrs \n( fruits"*ha^-1*")"),
# name = expression(paste("Projected fruit productivity\nin fallows in 40 yrs\n"),bquote(paste("("*fruits~ha^-1*")"))),#(Fruits/ha)
name = expression(atop("Projected fruit",
"productivity in",
"fallows in 40 yrs",
"( fruits ha"^-1,")")),
breaks= c(NA,"(0,60]","(60,120]","(120,180]","(180,240]","(240,1.4e+03]"),
labels= c("NA","\u2264 60","60 - 120","120 - 180","180 - 240","> 240"),
guide = guide_legend(override.aes = list(linetype = "blank", shape = 18, fill = NA)))+
# labs(size = expression(atop("Projected fruit\nproductivity in\nfallows in 40 yrs\n(fruits"*ha^-1*")", sep="")))+ #comment name line at the scale_size_manual
# labs(size = bquote("Projected fruit productivity \nin fallows in 40 yrs \n( fruits"*ha^-1*")"))+ #comment name line at the scale_size_manual
ggplot2::theme_minimal()+
ggplot2::theme(legend.text.align=0.5,
legend.title.align = 0.5,
plot.background = element_blank(),
panel.grid = element_line(colour = "white"),
panel.background = element_rect(fill = "grey87", color = "white"))+#,
coord_sf(xlim = c(-68.45,-68.2), ylim = c(-11.05,-10.8))
prod_map_PAECM_fruit
Extra question. Once I started to use the bquote I could not align the title text using theme(legend.title.align = 0.5), any other ideas?
After some other tries, I did come up with the following solution for the legend title.
name = expression(atop("",
atop(textstyle("Projected fruit"),
atop(textstyle("productivity in"),
atop(textstyle("fallows in 40 yrs"),
atop(textstyle("(fruits ha"^-1*")"))))))),
I used textstyle() to plot all text with the same size, otherwise it would be plotted smaller every time atop() was called. Atop() creates a space between the first and second line, that is why the first line of the code is atop("", so the first line will be a blank.
This is the final code with the map below.
library(sf) #sf = simple feature
library(ggplot2)
library(dplyr)
PAECM_fallows <-read.csv("spatial_dist_fallows.csv")
PAECM_fallows_sp <- st_as_sf(PAECM_fallows,coords = c("X", "Y"),crs = "+proj=longlat +datum=WGS84 +no_defs")
custom_bins_fruit = c(0,60,120,180,240,1400)
PAECM_fallows_fruit <- PAECM_fallows_sp %>%
mutate(prod_cat_fallow = cut(prod_40, breaks= custom_bins_fruit),
age_cat_fallow = cut(age, breaks = c(11,17,22,29,60)))
prod_map_PAECM_fruit_legend_test<-ggplot()+
geom_sf(data = PAECM_fallows_fruit,aes(size = prod_cat_fallow), shape = 18, show.legend = "point")+
scale_size_manual(values= c(2,3,4,5,6),
name = expression(atop("",
atop(textstyle("Projected fruit"),
atop(textstyle("productivity in"),
atop(textstyle("fallows in 40 yrs"),
atop(textstyle("(fruits ha"^-1*")"))))))),
breaks= c(NA,"(0,60]","(60,120]","(120,180]","(180,240]","(240,1.4e+03]"),
labels= c("NA","\u2264 60","60 - 120","120 - 180","180 - 240","> 240"),
guide = guide_legend(override.aes = list(linetype = "blank", shape = 18, fill = NA)))+
ggplot2::theme_minimal()+
ggplot2::theme(legend.text.align=0.5,
legend.title.align = 0.5,
plot.background = element_blank(),
panel.grid = element_line(colour = "white"),
panel.background = element_rect(fill = "grey87", color = "white"))+#,
coord_sf(xlim = c(-68.45,-68.2), ylim = c(-11.05,-10.8))
prod_map_PAECM_fruit_legend_test
Alternatively, you could use the annotation functions cowplot::draw_label() or ggplot2::annotation_custom(). I think that the explanations about these approaches given in ggplot2 two-line label with expression are helpful here as well.
1) Solution with cowplot::draw_label()
library(ggplot2)
library(cowplot)
#> Warning: package 'cowplot' was built under R version 3.5.2
#>
#> Attaching package: 'cowplot'
#> The following object is masked from 'package:ggplot2':
#>
#> ggsave
# If needed, revert to default theme (cowplot modifies the theme);
theme_set(theme_grey())
# Build a simple plot as example
p <- ggplot(mtcars, aes(x = wt, y = mpg, size = factor(gear))) +
geom_point() +
labs(size = element_blank()) + # remove default legend title
# Make enough space for the custom legend title by tweaking the right margin
theme(legend.margin = margin(t = 0, r = 26, b = 0, l = 0, unit = "mm"))
# Adjust further theme elements if needed, like text size, font, etc
# The lines of text and expression that constitute your custom legend title
lines <- list(
"Projected fruit",
"productivity in",
"fallows in 40 yrs",
expression("(fruits ha" ^-1 ~ ")")
)
# Using relative coordinates ranging from 0 to 1 (relative to the entire canvas).
# There is some guesswork with the coordinates until we get them right.
min_y <- 0.6
step <- 0.04 # dictates the line spacing; need to play with it until you get it right
ys <- seq(from = min_y + step * 4, to = min_y, by = -step)
x <- 0.87
# Add the annotations that will actually constitute the legend title.
gg <- ggdraw(p)
#> Warning: Using size for a discrete variable is not advised.
# Neglect the warning in this example.
for (i in 1:4){
gg <- gg + draw_label(lines[[i]], x = x, y = ys[i])
}
gg
Note that, cowplot::draw_label() can also be used in combination with setting the clipping off, coord_cartesian(clip = "off"), which allows plotting anywhere on the canvas (see next example with ggplot2::annotation_custom()). In such a case, we do not use the relative coordinates anymore, but the ones from the plot/data (the absolute coordinates).
2) Solution with ggplot2::annotation_custom()
Note that, cowplot::draw_label() uses ggplot2::annotation_custom() under the hood, so it is more or less the same annotation technique, but bit more verbose. We need to set clipping off. This time we do not use the relative coordinates anymore, but the ones from the plot/data (the absolute coordinates).
Building upon the p plot example from above:
min_y <- 24
step <- 1 # dictates the line spacing; need to play with it until you get it right
ys <- seq(from = min_y + step * 4, to = min_y, by = -step)
x <- 6.2
# set clipping off - allows plotting anywhere on the canvas
pp <- p + coord_cartesian(clip = "off")
for (i in 1:4){
pp <- pp + annotation_custom(grid::textGrob(lines[[i]]),
xmin = x, xmax = x, ymin = ys[i], ymax = ys[i])
}
pp
#> Warning: Using size for a discrete variable is not advised.
Created on 2019-01-15 by the reprex package (v0.2.1)
I saw yesterday this beautiful map of McDonalds restaurants in USA. I wanted to replicate it for France (I found some data that can be downloaded here).
I have no problem plotting the dots:
library(readxl)
library(ggplot2)
library(raster)
#open data
mac_do_FR <- read_excel("./mcdo_france.xlsx")
mac_do_FR_df <- as.data.frame(mac_do_FR)
#get a map of France
mapaFR <- getData("GADM", country="France", level=0)
#plot dots on the map
ggplot() +
geom_polygon(data = mapaFR, aes(x = long, y = lat, group = group),
fill = "transparent", size = 0.1, color="black") +
geom_point(data = mac_do_FR_df, aes(x = lon, y = lat),
colour = "orange", size = 1)
I tried several methods (Thiessen polygons, heat maps, buffers), but the results I get are very poor. I can't figure out how the shaded polygons were plotted on the American map. Any pointers?
Here's my result, but it did take some manual data wrangling.
Step 1: Get geospatial data.
library(sp)
# generate a map of France, along with a fortified dataframe version for ease of
# referencing lat / long ranges
mapaFR <- raster::getData("GADM", country="France", level=0)
map.FR <- fortify(mapaFR)
# generate a spatial point version of the same map, defining your own grid size
# (a smaller size yields a higher resolution heatmap in the final product, but will
# take longer to calculate)
grid.size = 0.01
points.FR <- expand.grid(
x = seq(min(map.FR$long), max(map.FR$long), by = grid.size),
y = seq(min(map.FR$lat), max(map.FR$lat), by = grid.size)
)
points.FR <- SpatialPoints(coords = points.FR, proj4string = mapaFR#proj4string)
Step 2: Generate a voronoi diagram based on store locations, & obtain the corresponding polygons as a SpatialPolygonsDataFrame object.
library(deldir)
library(dplyr)
voronoi.tiles <- deldir(mac_do_FR_df$lon, mac_do_FR_df$lat,
rw = c(min(map.FR$long), max(map.FR$long),
min(map.FR$lat), max(map.FR$lat)))
voronoi.tiles <- tile.list(voronoi.tiles)
voronoi.center <- lapply(voronoi.tiles,
function(l) data.frame(x.center = l$pt[1],
y.center = l$pt[2],
ptNum = l$ptNum)) %>%
data.table::rbindlist()
voronoi.polygons <- lapply(voronoi.tiles,
function(l) Polygon(coords = matrix(c(l$x, l$y),
ncol = 2),
hole = FALSE) %>%
list() %>%
Polygons(ID = l$ptNum)) %>%
SpatialPolygons(proj4string = mapaFR#proj4string) %>%
SpatialPolygonsDataFrame(data = voronoi.center,
match.ID = "ptNum")
rm(voronoi.tiles, voronoi.center)
Step 3. Check which voronoi polygon each point on the map overlaps with, & calculate its distance to the corresponding nearest store.
which.voronoi <- over(points.FR, voronoi.polygons)
points.FR <- cbind(as.data.frame(points.FR), which.voronoi)
rm(which.voronoi)
points.FR <- points.FR %>%
rowwise() %>%
mutate(dist = geosphere::distm(x = c(x, y), y = c(x.center, y.center))) %>%
ungroup() %>%
mutate(dist = ifelse(is.na(dist), max(dist, na.rm = TRUE), dist)) %>%
mutate(dist = dist / 1000) # convert from m to km for easier reading
Step 4. Plot, adjusting the fill gradient parameters as needed. I felt the result of a square root transformation looks quite good for emphasizing distances close to a store, while a log transformation is rather too exaggerated, but your mileage may vary.
ggplot() +
geom_raster(data = points.FR %>%
mutate(dist = pmin(dist, 100)),
aes(x = x, y = y, fill = dist)) +
# optional. shows outline of France for reference
geom_polygon(data = map.FR,
aes(x = long, y = lat, group = group),
fill = NA, colour = "white") +
# define colour range, mid point, & transformation (if desired) for fill
scale_fill_gradient2(low = "yellow", mid = "red", high = "black",
midpoint = 4, trans = "sqrt") +
labs(x = "longitude",
y = "latitude",
fill = "Distance in km") +
coord_quickmap()
I have already generated a simple map for Nigerian states, and now I would like to highlight in my map the borders for the Nigerian regions (that group Nigerian states).
When I add the layer for the borders with geom_polygon, they appear lines that do not correspond to region borders. I found a similar problem here Map county borders on to a ggmap
but this does not seem to be working for my case.
Here are the shapefiles and the database I am working on:
https://www.dropbox.com/sh/cek92s50jixowfx/AABwIVZKvtff8-9slhfCbxEca?dl=0
The code I am using is
#LOAD SHAPEFILES AND DATABASE
ng_dist <- readShapeSpatial("NGA_adm1.shp")
ng_dist_regions <- readShapeSpatial("NGA_adm_Region.shp")
NG_States <- read.csv("State_color_map.csv", header = TRUE, sep=",")
#VERIFY THE MAPS LOADED PROPERLY
plot(ng_dist)
plot(ng_dist_regions)
# STATE MAP - fortify and match shapefile and database IDs names
intersect(ng_dist$NAME_1, NG_States$STATE)
ng_dist <- fortify(ng_dist, region = "NAME_1")
ng_dist$id[which(ng_dist$id == "Akwa Ibom")] <- "Akwa-ibom"
ng_dist$id[which(ng_dist$id == "Nassarawa")] <- "Nasarawa"
ng_dist$id[which(ng_dist$id == "Cross River")] <- "C/river"
ng_dist$id[which(ng_dist$id == "Federal Capital Territory")] <- "FCT"
intersect(ng_dist$id, NG_States$STATE)
# REGION MAP - fortify
ng_dist_regions <- fortify(ng_dist_regions, region = "Region")
### Convert dummy variable to factor
NG_States$Abia <- as.factor(NG_States$Abia)
#PLOT MAP with coloured Abia State
cols <- c("0" = "#e6e6e6","1" = "#6eab27")
ABIA <- NG_States$Abia
Abia_map <- ggplot(NG_States, aes(fill = ABIA)) +
geom_map(data = NG_States, aes(map_id = NG_States$STATE, fill = ABIA), map = ng_dist, color = "black", size = 0.10) +
expand_limits(x = ng_dist$long, y = ng_dist$lat) +
theme_nothing(legend = FALSE) +
labs(title="Abia") +
coord_map() +
scale_fill_manual(name="", values=cols, labels=c("","Abia"))
Abia_map
#Add layer for region borders
d <- Abia_map +
geom_polygon(aes(x = ng_dist_regions$long, y = ng_dist_regions$lat, group = ng_dist_regions$id, fill = NA), data = ng_dist_regions, color = "red", size = 0.8)
d
Here is my result
Nigerian States and Regions
I have tried to add other options, such as coord_fixed() or expand_limits(x = ng_dist_regions$long, y = ng_dist_regions$lat), but I am quite basic R user and I don't know other solutions.
Using group, instead of id as group seems to solve the problem.
d <- Abia_map +
geom_path(aes(x = long, y = lat, group = group), data = ng_dist_regions, color = "red", size = 0.8, inherit.aes = FALSE)
d