我已将此问题作为Efficient way to plot data on an irregular grid问题的一部分提出,但一般反馈是将原始问题拆分为更易于管理的问题。因此,这个新问题。
我处理组织在不规则二维网格上的卫星数据,其尺寸是扫描线(沿着轨道尺寸,即Y轴)和地面像素(跨越轨道尺寸,即X轴)。每个中心像素的纬度和经度信息存储在辅助坐标变量中,以及四个角坐标对(纬度和经度坐标在WGS84参考椭球上给出)。
让我们建立一个玩具数据集,包括12x10潜在的不规则网格和相关的表面温度测量。
library(pracma) # for the meshgrid function
library(ggplot2)
num_sl <- 12 # number of scanlines
num_gp <- 10 # number of ground pixels
l <- meshgrid(seq(from=-20, to=20, length.out = num_gp),
seq(from=30, to=60, length.out = num_sl))
lon <- l[[1]] + l[[2]]/10
lat <- l[[2]] + l[[1]]/10
data <- matrix(seq(from = 30, to = 0, length.out = num_sl*num_gp),
byrow = TRUE, nrow = num_sl, ncol = num_gp) +
matrix(runif(num_gp*num_sl)*6, nrow = num_sl, ncol = num_gp)
df <- data.frame(lat=as.vector(lat), lon=as.vector(lon), temp=as.vector(data))
lon
和lat
数据包含原始产品I中提供的中心像素坐标,存储为二维矩阵,其轴为ground_pixel(X轴)和扫描线(Y轴)。 data
矩阵相同的尺寸包含我的测量值。然后我展平三个矩阵并将它们存储在数据框中。
我想在地图上绘制地面像素(作为四边形),相应地填充温度测量值。
使用瓷砖我得到了:
ggplot(df, aes(y=lat, x=lon, fill=temp)) +
geom_tile(width=2, height=2) +
geom_point(size=.1) +
borders('world', colour='gray50', size=.2) +
coord_quickmap(xlim=range(lon), ylim=range(lat)) +
scale_fill_distiller(palette='Spectral') +
theme_minimal()
但那不是我所追求的。我可以使用width
和height
来制作磁贴&#34;触摸&#34;彼此,但当然,这甚至不会接近我想要的目标,即在地图上绘制实际的投影地面像素。
例如,Python的xarray可以在给定像素中心坐标的情况下自动推断像素边界:
有没有办法在R中获得相同的结果,即:从像素中心自动推断像素边界,并将像素绘制为地图上的填充多边形?也许使用sf
包裹?
我可以在这个question的答案中看到它完成但是使用sf
的答案对我来说有点不清楚,因为它处理不同的投影和可能的常规网格,而在我的情况我想我不必重新投射我的数据,而且,我的数据不在常规网格上。
如果这是不可能的,我想我可以使用我的产品中的像素边界信息,但如果这个问题被证明不容易解决,那么这可能是另一个问题的主题。
答案 0 :(得分:7)
这是一种方法。可能会有一些更简单的东西,但这有效。
首先,我将使用光栅包来操纵坐标。我在这里创建的栅格是“非常规的”,因为它们包含的值是位置数据。但是使用栅格而不是矩阵可以访问一些有用的函数,例如extend
,最有用的是resample
,其中使用双线性插值函数来查找顶点
library(raster)
latr = raster(lat)
lonr = raster(lon)
find.vertices = function(m){
r = raster(m)
vertices = raster(matrix(nrow = dim(r)[1]+1, ncol = dim(r)[2]+1))
x = extend(r, c(1,1))
x[1,] = 2*x[2,] - x[3,]
x[dim(x)[1],] = 2*x[dim(x)[1]-1,] - x[dim(x)[1]-2,]
x[,1] = 2*x[,2] - x[,3]
x[,dim(x)[2]] = 2*x[,dim(x)[2]-1] - x[,dim(x)[2]-2,]
extent(vertices) = extent(r) + res(r)
vertices = resample(x, vertices)
}
latv = find.vertices(lat)
lonv = find.vertices(lon)
df2 = data.frame(xc = lonv[], yc = latv[])
让我们绘制这些顶点以检查我们是否正常:
ggplot(df, aes(y=lat, x=lon, fill=temp)) +
geom_tile(width=2, height=2) +
geom_point(size=.1) +
geom_point(aes(xc, yc), data=df2, inherit.aes =F) +
borders('world', colour='gray50', size=.2) +
coord_quickmap(xlim=range(lon), ylim=range(lat)) +
scale_fill_distiller(palette='Spectral') +
theme_minimal()
现在我们从这些顶点
创建一些Polygon
nx = NCOL(latv)
ny = NROW(lonv)
polys = list()
for (i in 1:length(data)) {
x = col(data)[i]
y = row(data)[i]
polys[[i]] = Polygon(cbind(
lonv[c((x-1)*ny + y, (x-1)*ny + y + 1, x*ny + y + 1, x*ny + y, (x-1)*ny + y)],
latv[c((x-1)*ny + y, (x-1)*ny + y + 1, x*ny + y + 1, x*ny + y, (x-1)*ny + y)]
))
}
将Polygon
列表转换为SpatialPolygonsDataFrame
Polys = sapply(1:length(polys), function(i) Polygons(polys[i], i))
SPolys = sapply(1:length(polys), function(i) SpatialPolygons(Polys[i], i))
SPolys = do.call(bind, SPolys)
SPolysdf = SpatialPolygonsDataFrame(SPolys, data.frame(data=as.vector(data)))
要在ggplot中绘制此对象,我们需要转换为传统的data.frame
。传统上,大多数人都使用fortify
。但是ggplot文档警告说这可能会被弃用,建议改用扫帚包。我对扫帚不太熟悉,但我决定这样遵循这样的建议:
library(broom)
ggSPolysdf = tidy(SPolysdf)
ggSPolysdf = cbind(ggSPolysdf, data = rep(as.vector(data), each=5))
最后我们可以绘制:
ggplot(df, aes(y=lat, x=lon, fill=temp)) +
geom_polygon(aes(long,lat,fill=data, group = id), data=ggSPolysdf) +
borders('world', colour='gray50', size=.2) +
coord_quickmap(xlim=range(lon), ylim=range(lat)) +
scale_fill_distiller(palette='Spectral') +
theme_minimal()
答案 1 :(得分:0)
以下解决方案实质上使用了@dww的答案,并进行了一些必要的更改以获取这些数据(至少在我的平台上如此)。这些变化首先涉及到定义最后一个图的“倾斜像素”的多边形的定义;其次,关于如何将多边形压缩为数据框的问题。对于第二个问题,使用@SymbolixAU建议的sf
程序包。
library(raster)
latr = raster(lat)
lonr = raster(lon)
find.vertices = function(m){
r = raster(m)
vertices = raster(matrix(nrow = dim(r)[1]+1, ncol = dim(r)[2]+1))
x = extend(r, c(1,1))
x[1,] = 2*x[2,] - x[3,]
x[dim(x)[1],] = 2*x[dim(x)[1]-1,] - x[dim(x)[1]-2,]
x[,1] = 2*x[,2] - x[,3]
x[,dim(x)[2]] = 2*x[,dim(x)[2]-1] - x[,dim(x)[2]-2,]
extent(vertices) = extent(r) + res(r)
vertices = resample(x, vertices)
}
latv = find.vertices(lat)
lonv = find.vertices(lon)
df2 = data.frame(xc = lonv[], yc = latv[])
让我们绘制这些顶点以检查我们是否在轨道上:
ggplot(df, aes(y=lat, x=lon, fill=temp)) +
geom_tile(width=2, height=2) +
geom_point(size=.1) +
geom_point(aes(xc, yc), data=df2, inherit.aes=F) +
borders('world', colour='gray50', size=.2) +
coord_quickmap(xlim=range(lon), ylim=range(lat)) +
scale_fill_distiller(palette='Spectral') +
theme_minimal()
现在,我们从这些顶点创建多边形:
nx = NCOL(latv)
polys = list()
for (i in 1:length(data)) {
x = col(data)[i]
y = row(data)[i]
polys[[i]] = Polygon(cbind(
lonv[c((y-1)*nx + x, (y-1)*nx + x + 1, y*nx + x + 1, y*nx + x, (y-1)*nx + x)],
latv[c((y-1)*nx + x, (y-1)*nx + x + 1, y*nx + x + 1, y*nx + x, (y-1)*nx + x)]
))
}
将多边形列表转换为SpatialPolygonsDataFrame:
Polys = sapply(1:length(polys), function(i) Polygons(polys[i], i))
SPolys = sapply(1:length(polys), function(i) SpatialPolygons(Polys[i], i))
SPolys = do.call(bind, SPolys)
SPolysdf = SpatialPolygonsDataFrame(SPolys, data.frame(data=as.vector(data)))
使用fortify
转换为数据帧将由以下两行完成:(注意:,如@dww所指出的,{{1不推荐此解决方案}}文档。)
ggplot2
一种替代方法是使用ggSPolysdf_0 = fortify(SPolysdf)
ggSPolysdf = cbind(ggSPolysdf_0, data = rep(as.vector(data), each=5))
包。在以下代码中,命令sf
在st_coordinates
中扮演fortify
的角色。请注意,使用本方法,变量名称将在转换中丢失,需要手工恢复:
ggplot2
最后我们可以绘制:
library(sf)
sfSPolys = st_as_sf(SPolysdf)
coord_xy_SPolys = st_coordinates(sfSPolys)
coord_xyz_SPolys = cbind(coord_xy_SPolys, data = rep(as.vector(data), each=5))
ggSPolysdf = as.data.frame(coord_xyz_SPolys)
colnames(ggSPolysdf) <- c("long", "lat", "piece", "id", "data")