This question already has an answer here:
-
Plotting bar charts on map using ggplot2?
1 answer
I have found similar answers to questions like this one, but most of them are using packages rworldmap
, ggmap
, ggsubplot
or geom_subplot2d
. See for example here or here.
I'd like to know how I can plot other ggplot-objects such as a bar-chart onto a map, that is created from a shapefile. The one I'm using can be downloaded here.
EDIT
As @beetroot correctly pointed out, the new file which can be downloaded under the link posted above has changed significantly. Therefore the names of the shapefile etc. are adjusted.
library(rgdal)
library(ggplot2)
library(rgeos)
library(maptools)
map.det<- readOGR(dsn="<path to your directory>/swissBOUNDARIES3D100216/swissBOUNDARIES3D/V200/SHAPEFILE_LV03", layer="VECTOR200_KANTONSGEBIET")
map.kt <- map.det[map.det@data$KANTONSNUM=="CH01000000"|map.det@data$KANTONSNUM=="CH19000000",]
#get centroids
map.test.centroids <- gCentroid(map.kt, byid=T)
map.test.centroids <- as.data.frame(map.test.centroids)
map.test.centroids$KANTONSNR <- row.names(map.test.centroids)
#create df for ggplot
kt_geom <- fortify(map.kt, region="KANTONSNUM")
#Plot map
map.test <- ggplot(NULL)+
geom_polygon(data=kt_geom, aes(long, lat, group=group), fill="white")+
coord_fixed()+
geom_path(data=kt_geom, color="gray48", mapping=aes(long, lat, group=group), size=0.2)+
geom_point(data=map.test.centroids, aes(x=x, y=y), size=9, alpha=6/10)
mapp
This results in such a map. So far so good.
![](https://www.manongdao.com/static/images/pcload.jpg)
However, I'm having difficulties combining two plots such as the map map.test
and, for example, this one:
geo_data <- data.frame(who=rep(c(1:2), each=2),
value=as.numeric(sample(1:100, 4, replace=T)),
KANTONSNR=rep(c(1,19), 2))
bar.testplot <- ggplot()+
geom_bar(data=geo_data, aes(factor(id),value,group=who),position='dodge',stat='identity')
The barcharts should lie at the center of the two polygons, i.e. where the two points are. I could produce the barcharts and plot them onto the map separately, if that makes things easier.
I've modified a little your code to make the example more illustrative. I'm plotting not only 2 kantons, but 47.
library(rgdal)
library(ggplot2)
library(rgeos)
library(maptools)
library(grid)
library(gridExtra)
map.det<- readOGR(dsn="c:/swissBOUNDARIES3D/V200/SHAPEFILE_LV03", layer="VECTOR200_KANTONSGEBIET")
map.kt <- map.det[map.det$ICC=="CH" & (map.det$OBJECTID %in% c(1:73)),]
# Merge polygons by ID
map.test <- unionSpatialPolygons(map.kt, map.kt@data$OBJECTID)
#get centroids
map.test.centroids <- gCentroid(map.test, byid=T)
map.test.centroids <- as.data.frame(map.test.centroids)
map.test.centroids$OBJECTID <- row.names(map.test.centroids)
#create df for ggplot
kt_geom <- fortify(map.kt, region="OBJECTID")
#Plot map
map.test <- ggplot(kt_geom)+
geom_polygon(aes(long, lat, group=group), fill="white")+
coord_fixed()+
geom_path(color="gray48", mapping=aes(long, lat, group=group), size=0.2)+
geom_point(data=map.test.centroids, aes(x=x, y=y), size=2, alpha=6/10)
map.test
![](https://www.manongdao.com/static/images/pcload.jpg)
Let's generate data for barplots.
set.seed(1)
geo_data <- data.frame(who=rep(c(1:length(map.kt$OBJECTID)), each=2),
value=as.numeric(sample(1:100, length(map.kt$OBJECTID)*2, replace=T)),
id=rep(c(1:length(map.kt$OBJECTID)), 2))
Now making 47 barplots which should be plotted at center-points later.
bar.testplot_list <-
lapply(1:length(map.kt$OBJECTID), function(i) {
gt_plot <- ggplotGrob(
ggplot(geo_data[geo_data$id == i,])+
geom_bar(aes(factor(id),value,group=who), fill = rainbow(length(map.kt$OBJECTID))[i],
position='dodge',stat='identity', color = "black") +
labs(x = NULL, y = NULL) +
theme(legend.position = "none", rect = element_blank(),
line = element_blank(), text = element_blank())
)
panel_coords <- gt_plot$layout[gt_plot$layout$name == "panel",]
gt_plot[panel_coords$t:panel_coords$b, panel_coords$l:panel_coords$r]
})
Here we convert ggplot
s into gtable
s and then crop them to have only panels of each barplot. You may modify this code to keep scales, add legend, title etc.
We can add this barplots to the initial map with the help of annotation_custom
.
bar_annotation_list <- lapply(1:length(map.kt$OBJECTID), function(i)
annotation_custom(bar.testplot_list[[i]],
xmin = map.test.centroids$x[map.test.centroids$OBJECTID == as.character(map.kt$OBJECTID[i])] - 5e3,
xmax = map.test.centroids$x[map.test.centroids$OBJECTID == as.character(map.kt$OBJECTID[i])] + 5e3,
ymin = map.test.centroids$y[map.test.centroids$OBJECTID == as.character(map.kt$OBJECTID[i])] - 5e3,
ymax = map.test.centroids$y[map.test.centroids$OBJECTID == as.character(map.kt$OBJECTID[i])] + 5e3) )
result_plot <- Reduce(`+`, bar_annotation_list, map.test)
![](https://www.manongdao.com/static/images/pcload.jpg)