如何用ggplot叠加地图与散点图?(附报错及代码)
问题根源分析
你遇到的object 'longitud' not found报错,本质是ggplot的全局美学映射(aes)继承问题:你的gtomap对象在初始化时设置了全局的x = longitud, y = latitud,当你叠加geom_sf(data=map)时,geom_sf会默认尝试从map数据框中读取这两个变量,但你把sf格式的地图数据转成了普通data.frame,既丢失了空间属性,也没有longitud/latitud列,因此触发报错。
另外,将sf对象转成data.frame是完全不必要的——geom_sf需要直接使用sf类型的数据才能正确渲染地图。
解决方案:两种可行的修正方式
方法1:统一使用SF格式处理空间数据(推荐)
将学校的经纬度数据转为sf对象,和地图数据统一用geom_sf绘制,这样能保证坐标系完全匹配,避免错位问题:
# 加载所需包(新增sf包处理空间数据) library(foreach) library(forcats) library(ggplot2) library(dplyr) library(stringr) library(raster) library(readr) library(reshape2) library(sf) # 读取学校数据并处理教育水平分类 gto <- read_csv("http://fs.planeacion.sep.gob.mx/cct/cct11.csv") nivel_ed <- c("Primaria", "Secundaria", "Bachillerato", "Superior") schools_sf <- subset(gto, select = c(longitud, latitud, nnivel)) %>% filter(nnivel %in% c("PRIMARIA", "SECUNDARIA", "BACHILLERATO", "PEDAGOGICA, UNIVERSITARIA O TECNOLOGICA", "PROFESIONAL")) %>% mutate(nnivel = case_when( nnivel == "PEDAGOGICA, UNIVERSITARIA O TECNOLOGICA" ~ "Superior", nnivel == "PROFESIONAL" ~ "Superior", nnivel == "SECUNDARIA" ~ "Secundaria", nnivel == "PRIMARIA" ~ "Primaria", nnivel == "BACHILLERATO" ~ "Bachillerato" )) %>% mutate(nnivel = factor(nnivel, levels = nivel_ed)) %>% # 将经纬度转为SF对象,指定坐标系为WGS84(EPSG:4326,全球通用地理坐标系) st_as_sf(coords = c("longitud", "latitud"), crs = 4326) # 读取地图数据(保留SF格式,不要转成data.frame) mapgto <- sf::read_sf("http://geoinfo.iplaneg.net/geoserver/wms/kml?layers=geonode%3Amgm_gto2010&mode=download", quiet = T) # 绘制叠加图:先画地图底图,再画学校散点 ggplot() + geom_sf(data = mapgto, fill = "lightgray", color = "white") + geom_sf(data = schools_sf, aes(color = nnivel), size = 0.8) + theme_minimal() + theme(legend.title = element_blank()) + labs(x = "", y = "")
方法2:保留原有散点图,关闭美学映射继承
如果你不想修改原有散点图的代码,可以在geom_sf中设置inherit.aes = FALSE,避免它继承全局的x/y映射,同时保留sf格式的地图数据:
# 保留你原来的gtomap代码 library(foreach) library(forcats) library(ggplot2) library(dplyr) library(stringr) library(raster) library(readr) library(reshape2) library(sf) gto <- read_csv("http://fs.planeacion.sep.gob.mx/cct/cct11.csv") nivel_ed <- c("Primaria", "Secundaria", "Bachillerato", "Superior") gtomap <- subset(gto, select = c(longitud, latitud, nnivel)) %>% filter(nnivel %in% c("PRIMARIA", "SECUNDARIA", "BACHILLERATO", "PEDAGOGICA, UNIVERSITARIA O TECNOLOGICA", "PROFESIONAL")) %>% mutate(nnivel = case_when( nnivel == "PEDAGOGICA, UNIVERSITARIA O TECNOLOGICA" ~ "Superior", nnivel == "PROFESIONAL" ~ "Superior", nnivel == "SECUNDARIA" ~ "Secundaria", nnivel == "PRIMARIA" ~ "Primaria", nnivel == "BACHILLERATO" ~ "Bachillerato" )) %>% mutate(nnivel = factor(nnivel, levels = nivel_ed)) %>% melt(id = c("longitud", "latitud")) %>% mutate(value = factor(value, levels = nivel_ed)) %>% ggplot(aes(x = longitud, y = latitud, col = value)) + geom_point() + theme(legend.title = element_blank()) + labs(x = "", y = "") # 读取地图数据(保留SF格式) mapgto <- sf::read_sf("http://geoinfo.iplaneg.net/geoserver/wms/kml?layers=geonode%3Amgm_gto2010&mode=download", quiet = T) # 叠加地图:关闭继承,添加coord_sf确保坐标系匹配 gtomap + geom_sf(data = mapgto, inherit.aes = FALSE, fill = "lightgray", color = "white") + coord_sf()
关键注意事项
- 永远不要把sf对象转成普通
data.frame——sf对象包含空间坐标系信息,转成data.frame会丢失这些关键数据,导致geom_sf无法识别。 - 方法1是更规范的空间数据可视化方式,统一用sf处理能避免坐标系不匹配、图层错位等潜在问题。
内容的提问来源于stack exchange,提问作者Alejandro Carrera
相关产品推荐
相关产品推荐

