使用ggplot绘制栅格地图:栅格单元横竖畸变的原因及解决方法
问题描述
我正在绘制多个已投影的栅格对象(SpatRaster或栅格栈,采用等面积投影EPSG:3035),所有对象范围一致,但绘制出的栅格单元出现水平或垂直畸变(非正方形),或显示尺寸过大。
出现畸变的示例代码:
# 垂直畸变 ggplot2::ggplot() + tidyterra::geom_spatraster(data = terra::rast(ExampleMap[[1]])) + paletteer::scale_fill_paletteer_c(na.value = NA, "viridis::plasma")
【对应截图】
# 水平畸变 ggplot2::ggplot() + tidyterra::geom_spatraster(data = terra::rast(ExampleMap[[2]])) + paletteer::scale_fill_paletteer_c(na.value = NA, "viridis::plasma")
【对应截图】
为排查问题,我用不同geom绘制单个图层的完整脚本如下:
require(raster) require(stara) require(ggplot2) ExampleMap2 <- ExampleMap[[1]] %>% trim()
使用geom_stars:
ggplot2::ggplot() + stars::geom_stars(data = stars::st_as_stars(ExampleMap2)) + ggplot2::coord_equal() + ggplot2::theme_void() + ggplot2::scale_fill_continuous(na.value = NA) + guides(fill = guide_legend(NULL)) + theme(aspect.ratio = 1) + labs(title = "stars::geom_stars")
【对应截图】
使用tidyterra::geom_spatraster:
ggplot2::ggplot() + tidyterra::geom_spatraster(data = terra::rast(ExampleMap2)) + ggplot2::theme_void() + ggplot2::scale_fill_continuous(na.value = NA) + guides(fill = guide_legend(NULL)) + theme(aspect.ratio = 1) + labs(title = "tidyterra::geom_spatraster")
【对应截图】
使用ggplot2::geom_raster:
DT <- as.data.frame(ExampleMap2, xy = TRUE) ggplot2::ggplot() + ggplot2::geom_raster(aes(x = x, y = y, fill = as.numeric(as.character(ExampleMap1))), data = DT) + ggplot2::theme_void() + ggplot2::scale_fill_continuous(na.value = NA) + guides(fill = guide_legend(NULL)) + coord_equal() + theme(aspect.ratio = 1) + labs(title = "ggplot2::geom_raster")
【对应截图】
使用ggplot2::geom_tile(此方法绘图符合预期):
ggplot2::ggplot() + ggplot2::geom_tile(aes(x = x, y = y, fill = as.numeric(as.character(ExampleMap1))), data = DT) + ggplot2::theme_void() + ggplot2::scale_fill_continuous(na.value = NA) + guides(fill = guide_legend(NULL)) + coord_equal() + labs(title = "ggplot2::geom_tile")
【对应截图】
请问出现这种畸变的原因是什么?如何在使用tidyterra::geom_spatraster或ggplot2::geom_raster时避免该问题?
问题原因与解决方法
原因分析
geom_raster和tidyterra::geom_spatraster默认会自动拉伸栅格单元以填满绘图区域,不会严格遵循栅格原始的宽高比。哪怕设置了theme(aspect.ratio = 1)也没用——这个参数只控制绘图区域的整体宽高比,管不了单个栅格单元的形状。geom_tile则是严格按照数据中x、y对应的栅格单元尺寸绘制,配合coord_equal()强制x、y轴单位长度一致,所以能保证单元是正方形。
解决方法
修复ggplot2::geom_raster的畸变
调用geom_raster时,显式指定hjust和vjust为0.5,同时用栅格的实际分辨率设置width和height参数,再配合coord_equal():
DT <- as.data.frame(ExampleMap2, xy = TRUE) # 获取栅格的分辨率 res <- terra::res(ExampleMap2) ggplot2::ggplot() + ggplot2::geom_raster( aes(x = x, y = y, fill = as.numeric(as.character(ExampleMap2))), data = DT, hjust = 0.5, vjust = 0.5, width = res[1], height = res[2] ) + ggplot2::theme_void() + ggplot2::scale_fill_continuous(na.value = NA) + guides(fill = guide_legend(NULL)) + ggplot2::coord_equal() + labs(title = "ggplot2::geom_raster(修正后)")
修复tidyterra::geom_spatraster的畸变
直接使用tidyterra::coord_terra(),它会自动适配SpatRaster的投影和分辨率,保证栅格单元形状正确:
ggplot2::ggplot() + tidyterra::geom_spatraster(data = terra::rast(ExampleMap2)) + tidyterra::coord_terra() + ggplot2::theme_void() + ggplot2::scale_fill_continuous(na.value = NA) + guides(fill = guide_legend(NULL)) + labs(title = "tidyterra::geom_spatraster(修正后)")
也可以用coord_sf配合投影参数:
ggplot2::ggplot() + tidyterra::geom_spatraster(data = terra::rast(ExampleMap2)) + ggplot2::coord_sf(datum = sf::st_crs(3035)) + ggplot2::coord_equal() + ggplot2::theme_void() + ggplot2::scale_fill_continuous(na.value = NA) + guides(fill = guide_legend(NULL)) + labs(title = "tidyterra::geom_spatraster(修正后)")
注意:theme(aspect.ratio = 1)可以移除,因为coord_equal()或coord_terra()已经会强制坐标轴单位长度一致,确保栅格单元为正方形。
内容的提问来源于stack exchange,提问作者Ahmed El-Gabbas
相关产品推荐
相关产品推荐

