Chapter 4 空间分析与可视化
https://nceas.github.io/oss-lessons/spatial-data-gis-law/3-mon-intro-gis-in-r.html
https://desktop.arcgis.com/zh-cn/arcmap/10.3/manage-data/raster-and-images/what-is-raster-data.htm
基本数据类型:矢量与栅格。
矢量数据 | 栅格数据 |
---|---|
数据存储量小 | 数据存储量大 |
空间位置精度高 | 空间位置精度低 |
用网络连接法能完整描述拓扑关系 | 难于建立网络连接关系 |
输出简单容易,绘图细腻、精确、美观 | 输出速度快,但绘图粗糙、不美观 |
可对图形及其属性进行检索、更新和综合 | 便于面状数据处 |
数据结构复杂 | 数据结构简单 |
获取数据慢 | 快速获取大量数据 |
数学模拟困难 | 数学模拟方便 |
多种地图叠合分析困难 | 多种地图叠合分析方便 |
不能直接处理数字图像信息 | 能直接处理数字图像信息 |
空间分析不容易实现 | 空间分析易于进行 |
边界复杂、模糊的事物难以描述 | 容易描述边界复杂、模糊的事物 |
数据输出的费用较高 | 技术开发费用低 |
4.1 矢量数据
- 加载需要的库
library(raster) #raster包用于绘图
library(sp) #sp包用于矢量数据操作
library(rgdal) # rgdal用于读写数据
library(rgeos) # rgeos用于空间分析
- 由坐标产生数据
# ================================================================
# readWKT, 由坐标产生矢量文件
# ================================================================
= list()
g 1]]=readWKT("POINT(6 10)")
g[[2]]=readWKT("LINESTRING(3 4,10 50,20 25)")
g[[3]]=readWKT("POLYGON((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2))")
g[[4]]=readWKT("MULTIPOINT((3.5 5.6),(4.8 10.5))")
g[[5]]=readWKT("MULTILINESTRING((3 4,10 50,20 25),(-5 -8,-10 -8,-15 -4))")
g[[6]]=readWKT("MULTIPOLYGON(((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2)),((6 3,9 2,9 4,6 3)))")
g[[7]]=readWKT("GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(4 6,7 10))")
g[[
par(mfrow=c(2, 4))
for(i in 1:7){
plot(g[[i]], axes=TRUE)
}
# ================================================================
# 读取坐标,并产生SpatialLines文件。
# ================================================================
=read.table('Exercise/Data_text/xy.csv', header = TRUE)
xy=xy[1:100, 1]
x=xy[1:100, 2]
y= paste("LINESTRING(",
str paste(
paste(x, y), collapse = ', '
), ")" )
= readWKT(str)
sp.line crs(sp.line) = sp::CRS("+init=epsg:4326")
graphics.off()
plot(sp.line, axes=TRUE)
points(x, y, col=2, pch=19)
grid()
crs(sp.line)
## Coordinate Reference System:
## Deprecated Proj.4 representation: +proj=longlat +datum=WGS84 +no_defs
## WKT2 2019 representation:
## GEOGCRS["WGS 84",
## ENSEMBLE["World Geodetic System 1984 ensemble",
## MEMBER["World Geodetic System 1984 (Transit)",
## ID["EPSG",1166]],
## MEMBER["World Geodetic System 1984 (G730)",
## ID["EPSG",1152]],
## MEMBER["World Geodetic System 1984 (G873)",
## ID["EPSG",1153]],
## MEMBER["World Geodetic System 1984 (G1150)",
## ID["EPSG",1154]],
## MEMBER["World Geodetic System 1984 (G1674)",
## ID["EPSG",1155]],
## MEMBER["World Geodetic System 1984 (G1762)",
## ID["EPSG",1156]],
## ELLIPSOID["WGS 84",6378137,298.257223563,
## LENGTHUNIT["metre",1],
## ID["EPSG",7030]],
## ENSEMBLEACCURACY[2.0],
## ID["EPSG",6326]],
## PRIMEM["Greenwich",0,
## ANGLEUNIT["degree",0.0174532925199433],
## ID["EPSG",8901]],
## CS[ellipsoidal,2],
## AXIS["longitude",east,
## ORDER[1],
## ANGLEUNIT["degree",0.0174532925199433,
## ID["EPSG",9122]]],
## AXIS["latitude",north,
## ORDER[2],
## ANGLEUNIT["degree",0.0174532925199433,
## ID["EPSG",9122]]],
## USAGE[
## SCOPE["unknown"],
## AREA["World."],
## BBOX[-90,-180,90,180]]]
- 读取shp数据
# ================================================================
# 读取SHP文件
# ================================================================
= readOGR(verbose = FALSE, 'Exercise/Data_spatial/Province.shp')
sp1 = readOGR(verbose = FALSE, 'Exercise/Data_spatial/City.shp')
sp2 = readOGR(verbose = FALSE, 'Exercise/Data_spatial/TibetPlateau.shp')
sp3 par(mfrow = c(1, 2))
plot(sp1)
plot(sp1, col=rainbow(10))
- 渔网 由自定义函数实现渔网功能。
# ================================================================
# 产生渔网矢量文件
# ================================================================
#install.packages('abind') # 此函数需要abind包。
<- function(ext, crs=sp::CRS("+init=epsg:4326"),
fishnet n=10,
dx=diff(ext[1:2])/n, dy=dx
){= ext[1]
xmin = ext[2]
xmax = ext[3]
ymin = ext[4]
ymax = min(dx, diff(ext[1:2]))
dx = min(dy, diff(ext[3:4]))
dy =seq(ext[1], ext[2], by=dx)
xx=seq(ext[3], ext[4], by=dy)
yy= length(xx)
nx = length(yy)
ny =expand.grid(xx,yy)
xy= matrix(xy[,1], nx,ny)
xm = matrix(xy[,2], nx, ny)
ym =abind::abind(as.matrix(xm[-nx, -ny]), as.matrix(xm[-nx, -1]), as.matrix(xm[-1, -1]),
xlocas.matrix(xm[-1, -ny]), as.matrix(xm[-nx, -ny]), along=3)
=abind::abind(as.matrix(ym[-nx, -ny]), as.matrix(ym[-nx, -1]), as.matrix(ym[-1, -1]),
ylocas.matrix(ym[-1, -ny]), as.matrix(ym[-nx, -ny]), along=3)
=data.frame(as.numeric(apply(xloc, 1:2, min)),
dfas.numeric(apply(xloc, 1:2, max)),
as.numeric(apply(yloc, 1:2, min)),
as.numeric(apply(yloc, 1:2, max)))
= data.frame(df, rowMeans(df[,1:2]), rowMeans(df[,1:2+2]) )
df colnames(df) = c('xmin','xmax','ymin', 'ymax','xcenter','ycenter')
=paste('GEOMETRYCOLLECTION(',
strpaste(paste('POLYGON((',
paste(xm[-nx, -ny], ym[-nx, -ny], ',' ),
paste(xm[-nx, -1], ym[-nx, -1], ','),
paste(xm[-1, -1], ym[-1, -1], ','),
paste(xm[-1, -ny], ym[-1, -ny], ','),
paste(xm[-nx, -ny], ym[-nx, -ny], '' ), '))' )
collapse =','),
, ')' )
= rgeos::readWKT(str)
SRL = sp::SpatialPolygonsDataFrame( Sr=SRL, data=df, match.ID = TRUE)
ret ::crs(ret) = crs
rasterreturn(ret)
}
= readOGR(verbose = FALSE, 'Exercise/Data_spatial/TibetPlateau.shp')
sp3 = raster::extent(sp3)
ext = fishnet(ext=ext, dx=2)
sp.fn = gCentroid(sp.fn, byid=TRUE)
cent.xy graphics.off()
plot(sp.fn)
plot(add=TRUE, sp3, border=2)
points(cent.xy, col=3, cex=.5)
- 重投影
# ================================================================
# 重投影
# ================================================================
library(rgeos)
crs(sp.fn)
## Coordinate Reference System:
## Deprecated Proj.4 representation: +proj=longlat +datum=WGS84 +no_defs
## WKT2 2019 representation:
## GEOGCRS["WGS 84",
## ENSEMBLE["World Geodetic System 1984 ensemble",
## MEMBER["World Geodetic System 1984 (Transit)",
## ID["EPSG",1166]],
## MEMBER["World Geodetic System 1984 (G730)",
## ID["EPSG",1152]],
## MEMBER["World Geodetic System 1984 (G873)",
## ID["EPSG",1153]],
## MEMBER["World Geodetic System 1984 (G1150)",
## ID["EPSG",1154]],
## MEMBER["World Geodetic System 1984 (G1674)",
## ID["EPSG",1155]],
## MEMBER["World Geodetic System 1984 (G1762)",
## ID["EPSG",1156]],
## ELLIPSOID["WGS 84",6378137,298.257223563,
## LENGTHUNIT["metre",1],
## ID["EPSG",7030]],
## ENSEMBLEACCURACY[2.0],
## ID["EPSG",6326]],
## PRIMEM["Greenwich",0,
## ANGLEUNIT["degree",0.0174532925199433],
## ID["EPSG",8901]],
## CS[ellipsoidal,2],
## AXIS["longitude",east,
## ORDER[1],
## ANGLEUNIT["degree",0.0174532925199433,
## ID["EPSG",9122]]],
## AXIS["latitude",north,
## ORDER[2],
## ANGLEUNIT["degree",0.0174532925199433,
## ID["EPSG",9122]]],
## USAGE[
## SCOPE["unknown"],
## AREA["World."],
## BBOX[-90,-180,90,180]]]
= spTransform(sp.fn, sp::CRS("+init=epsg:2342")) #西安1980,高斯克吕格99E
spx par(mfrow=c(1, 2))
plot(sp.fn, axes=T)
plot(spx, axes=T)
- 数据分析与操作
#重投影数据
= spTransform(sp1, sp::CRS("+init=epsg:2342"))
s1 = spTransform(sp2, sp::CRS("+init=epsg:2342"))
s2 = spTransform(sp3, sp::CRS("+init=epsg:2342"))
s3 # 获得各省的面积
=gArea(s1, byid=TRUE)
ia
# 各省的几何中心点
= cbind(coordinates(sp1), coordinates(s1))
co1
# 经纬度和投影后的城市坐标
= cbind(coordinates(sp2), coordinates(s2))
co2
graphics.off()
plot(s1, col=rainbow(10));
plot(add=T, s2, pch=18, cex=2) #省会点
points(co1[, 3:4]) # 几何中心点
# 合并省界
= gUnaryUnion(sp1)
sp.cn plot(sp.cn)
# ------------缓冲区
= gBuffer(s3, width=50e3)
buf50 = gBuffer(s3, width=100e3)
buf100 plot(buf100); plot(add=T, border=2, buf50); plot(add=T, border=3, s3)
# ------------简化线条
= gSimplify(s3, tol=2000)
simp2 = gSimplify(s3, tol=20000)
simp20 = gSimplify(s3, tol=50000)
simp50 par(mfrow=c(1, 3))
plot(s3)
plot(simp20)
plot(simp50)
# ------------泰森多边形
# install.packages('deldir') # 需要deldir支持
require(sp)
require(deldir)
= function(layer) {
voronoipolygons = layer@coords
crds = deldir(crds[,1], crds[,2])
z = tile.list(z)
w = vector(mode='list', length=length(w))
polys for (i in seq(along=polys)) {
= cbind(w[[i]]$x, w[[i]]$y)
pcrds = rbind(pcrds, pcrds[1,])
pcrds = Polygons(list(Polygon(pcrds)), ID=as.character(i))
polys[[i]]
}= SpatialPolygons(polys)
SP = SpatialPolygonsDataFrame(SP, data=data.frame(x=crds[,1],
voronoi y=crds[,2], row.names=sapply(slot(SP, 'polygons'),
function(x) slot(x, 'ID'))))
}=voronoipolygons(s2)
vpgraphics.off()
plot(vp, border=4,axes=TRUE)
plot(add=T, s2, col=2, pch=19)
4.2 栅格数据
最简形式的栅格由按行和列(或格网)组织的像元(或像素)矩阵组成,其中的每个像元都包含一个信息值(例如温度)。栅格可以是数字航空像片、卫星影像、数字图片或甚至扫描的地图。(参考什么是栅格数据?
# ================================================================
# 读取数据及等高线
# ================================================================
= raster('Exercise/Data_spatial/DEM.tif')
r
= crop(r, sp3) # 切除包含区域
r.crop = mask(r.crop, sp3) # 蒙版,切除不规则边界外的数据
r.tp
graphics.off()
plot(r.tp)
plot(sp3, add=TRUE)
contour(r.tp, add=T, col='gray30' )
- 重采样
# ================================================================
# 重采样
# ================================================================
= raster()
t01 extent(t01) = extent(r.tp)
=t01
t05res(t01) = 0.1 # 分辨率0.1度
res(t05) = 0.5 # 分辨率0.5度
= resample(r.tp, t01, method = 'bilinear') #双线性插值,0.1度
r1 = resample(r.tp, t05, method = 'ngb') #最临近法,0.5度
r2 = resample(r.tp, t01, method = 'ngb') #最临近法,0.5度
r3
par(mfrow=c(2,2))
plot(r.tp, main=paste0('Original, res=', mean(res(r.tp))))
plot(r1, main='res=0.1, bilinear')
plot(r2, main='res=0.5, ngb')
plot(r3, main='res=0.1, ngb')
- 矢量数据重投影
# ================================================================
# 矢量数据重投影
# ================================================================
= projectRaster(r.tp, crs=crs(s3)) #栅格数据重投影
rp par(mfrow=c(1,2))
plot(rp); plot(add=T, s3)
plot(r.tp); plot(add=T, sp3)
- 空间随机采样和绘图
# ================================================================
# 空间随机采样和绘图
# ================================================================
=50
n=raster::sampleRandom(r.tp, size=n, xy=TRUE)
xyz
graphics.off()
plot(r.tp)
points(xyz[, 1:2],col=2)
# 更复杂的作图效果
= rnorm(n)
xrand = rep(25, n) # 符号
pch > 0] = 17
pch[xrand
=rep(4, n) # 符号颜色
col> 0] = 2
col[xrand
= (abs(xrand)^.6) # 符号大小
cex
=cellStats(r.tp, range)
zlim= round(seq(zlim[1], zlim[2], length.out = n), -2)
brks = colorspace::diverge_hcl(n=n)
colbar
graphics.off()
plot(sp3, axes=TRUE)
# plot(r, add=T, legend=FALSE, alpha=0.5)
plot(r.tp, add=TRUE, col=colbar, brks=brks, legend=FALSE)
points(xyz[, 1:2], col=col, pch=pch, cex=cex)
plot(add=T, sp3)
grid()
plot(r.tp, legend.only=TRUE, breaks=brks, col=colbar,
smallplot=c(c(0.2, 0.8), c(0.0, 0.03)+0.25 ),
# legend.width=5, legend.shrink=.7, cex=5,
horizontal=T,
axis.args=list(col.axis='blue', lwd = 0,
font.axis=4, cex.axis=1.5,tck = 0, line=-.85,
at=brks,
cex.axis=.8)
# ,legend.args=list(side=4, text='mm',col=4,font=2, cex=1.5)
)
- 空间插值
# ================================================================
# 空间插值
# ================================================================
#### Thin plate spline model
# install.packages('fields') #需要fields 支持
library(fields)
= r1
rx <- fields::Tps(xyz[, 1:2], xyz[, 3]) tps
## Warning:
## Grid searches over lambda (nugget and sill variances) with minima at the endpoints:
## (GCV) Generalized Cross-Validation
## minimum at right endpoint lambda = 9.073926e-06 (eff. df= 47.49998 )
<- raster(rx)
p <- interpolate(p, tps)
p <- mask(p, rx)
p graphics.off()
plot(p); points(xyz[, 1:2]); plot(add=T, sp3)
contour(p, add=T, col='gray30')
# -----计算插值的误差。计算较慢-----
# se <- interpolate(p, tps, fun=predictSE)
# se <- mask(se, rx)
# plot(se); points(xyz[, 1:2]); plot(add=T, sp3)
# contour(se, add=T, col='gray30')
- 地形分析
# ================================================================
# 地形分析
# ================================================================
= raster::slopeAspect(r.tp) # 计算坡度和坡向
x par(mfrow=c(1,2))
plot(x[[1]], main=names(x)[1])
plot(x[[2]], main=names(x)[2])
- 矢量栅格化
# ================================================================
# 矢量栅格化
# ================================================================
=rasterize(x=sp1,y=t01)
r.sp1
=1
t01[]par(mfrow=c(1,2))
plot(t01); plot(add=T, sp1)
plot(r.sp1)
- 读取NetCDF数据
# ================================================================
# 读取NetCDF数据
# ================================================================
='Exercise/Data_nc/GLDAS_NOAH10_3H.A20030107.0000.021.nc4'
fn library(ncdf4)
=nc_open(fn) #打开文件
ncidprint(names(ncid))
## [1] "filename" "writable" "id" "error" "safemode"
## [6] "format" "is_GMT" "groups" "fqgn2Rindex" "ndims"
## [11] "natts" "dim" "unlimdimid" "nvars" "var"
= names(ncid$var)
xname print(xname)
## [1] "time_bnds" "Swnet_tavg" "Lwnet_tavg"
## [4] "Qle_tavg" "Qh_tavg" "Qg_tavg"
## [7] "Snowf_tavg" "Rainf_tavg" "Evap_tavg"
## [10] "Qs_acc" "Qsb_acc" "Qsm_acc"
## [13] "AvgSurfT_inst" "Albedo_inst" "SWE_inst"
## [16] "SnowDepth_inst" "SoilMoi0_10cm_inst" "SoilMoi10_40cm_inst"
## [19] "SoilMoi40_100cm_inst" "SoilMoi100_200cm_inst" "SoilTMP0_10cm_inst"
## [22] "SoilTMP10_40cm_inst" "SoilTMP40_100cm_inst" "SoilTMP100_200cm_inst"
## [25] "PotEvap_tavg" "ECanop_tavg" "Tveg_tavg"
## [28] "ESoil_tavg" "RootMoist_inst" "CanopInt_inst"
## [31] "Wind_f_inst" "Rainf_f_tavg" "Tair_f_inst"
## [34] "Qair_f_inst" "Psurf_f_inst" "SWdown_f_tavg"
## [37] "LWdown_f_tavg"
# View(ncid$var[[3]])
=ncvar_get(ncid, varid = 'SoilMoi0_10cm_inst') #读取数据矩阵
mat= ncvar_get(ncid, varid = 'lon') # 读取x坐标
lon = ncvar_get(ncid, varid = 'lat') # 读取y坐标
lat nc_close(ncid) #关闭文件
graphics.off()
image(lon, lat, mat)
# 通过函数将nc转为Raster
<- function(fn, fid=ncdf4::nc_open(fn), plot=TRUE,
read.nc2Raster varid=2, xname=NULL, yname=NULL, flip=TRUE){
= fid$nvars
nv if(is.character(varid)){
= varid
varid else{
}= names(fid$var)
vns if(nv>0){
= vns[varid]
varid else{
}= vns[1]
varid
}
}if(is.null(xname) | is.null(yname)){
= names(fid$dim)
dn = dn[grepl('^lat|^x', tolower(dn) )]
yname = dn[grepl('^lon|^y', tolower(dn) )]
xname
}= ncdf4::ncvar_get(fid, xname)
x = ncdf4::ncvar_get(fid, yname)
y =length(x); ny=length(y);
nx=mean(diff(x)); dy=mean(diff(y))
dx= raster::raster(ncols=nx, nrows=ny)
r ::extent(r) = c(min(x), max(x), min(y), max(y)) + c(-dx, dx, -dy, dy)/2
raster= ncdf4::ncvar_get(fid, varid)
val nc_close(fid)
# dim(val)
if(flip){
= ny:1
idx else{
}= 1:ny
idx
}= length(dim(val))
ndims if(ndims==3){
= dim(val)
nd = list()
rl for(i in 1:nd[3]){
= raster::setValues(r, t(val[, idx, i]) )
r = r
rl[[i]]
}= raster::stack(rl)
rs else{
}= raster::setValues(r, t(val[, idx ]) )
rs
}if(plot){
::plot(rs, main=varid)
raster
}
rs
}
graphics.off()
= read.nc2Raster(fn, varid = 3) r.nc
- 多层NetCDF文件转为Raster
# ================================================================
# 多层NetCDF文件转为Raster,生成动画。
# ================================================================
= 'Exercise/Data_nc/soilw.mon.ltm.v2.nc'
fn library(ncdf4)
library(rasterVis)
=nc_open(fn)
ncidprint(names(ncid))
## [1] "filename" "writable" "id" "error" "safemode"
## [6] "format" "is_GMT" "groups" "fqgn2Rindex" "ndims"
## [11] "natts" "dim" "unlimdimid" "nvars" "var"
= names(ncid$var)
xname print(xname)
## [1] "climatology_bounds" "soilw" "valid_yr_count"
# View(ncid$var[[3]])
= ncid$ndims
ndims names(ncid$dim)
## [1] "lon" "lat" "time" "nbnds"
=ncvar_get(ncid, varid = 'soilw') # 读取土壤湿度数据
mat= ncvar_get(ncid, varid = 'lon')
lon = ncvar_get(ncid, varid = 'lat')
lat nc_close(ncid)
dim(mat)
## [1] 720 360 12
image(mat[, , 1]) #
= apply(mat, 1:2, mean)
x.avg image(x.avg)
#-----------
= read.nc2Raster(fn, flip=F) # 数据返回的是多层矢量。 rx
names(rx) = month.name
- 动画查看
# ================================================================
# 动画查看
# ================================================================
= rev( colorspace::diverge_hcl(n=50) )
cols graphics.off()
animate(rx, col=cols)
- 三维地形图
# 需要rgl支持
# install.packages('rasterVis')
::plot3D(r) rasterVis
4.3 可视化
library(raster)
library(rgdal)
= raster('Exercise/Data_spatial/DEM.tif')
r = readOGR(verbose = FALSE, 'Exercise/Data_spatial/Province.shp')
sp1 = readOGR(verbose = FALSE, 'Exercise/Data_spatial/City.shp')
sp2 = readOGR(verbose = FALSE, 'Exercise/Data_spatial/TibetPlateau.shp')
sp3
plot(r)
plot(add=TRUE, sp1, border='gray60')
plot(add=TRUE, sp2, col='gold', pch=19, cex=2)
plot(add=TRUE, sp3, col = rgb(0.1, 0, 0, alpha=0.1))
- 保存绘图
# png(filename='Map.png') #最简单输入PNG格式
# pnf(filename='Map.pdf') #输出PDF格式
png(filename='Map.png', width=279, height=216, res=200, unit='mm') #精细数据输出
plot(r)
plot(add=TRUE, sp1, border='gray60')
plot(add=TRUE, sp2, col='gold', pch=19, cex=2)
plot(add=TRUE, sp3, col = rgb(0.1, 0, 0, alpha=0.1))
dev.off()
- 开放数据背景
# install.packages('rosm') # 需要安装ROSM
# install.packages('prettymapr') # 需要安装prettymapr
library(rosm)
library(prettymapr)
<- makebbox(37, 104, 33, 103)
ns osm.plot(ns)
= geocode('lanzhou')
loc1 = geocode('maqu')
loc2
prettymap({osm.plot(ns)
osm.points(loc1$lon, loc1$lat, pch=18, cex=1.5, col=2)
osm.points(loc2$lon, loc2$lat, pch=20, cex=1.5, col=3) }, scale.style="ticks", scale.tick.cex=0)