In this document, we use the 14 GIA forward model solutions to find regions where the observations are certain and close to zero. The 14 GIA forward model solutions are given at 1 degree resolution with the mean trend and estimated errors over the period 2005-1015.
In the following we explore the data sets individually as well as in an ensemble approach.
library(ggplot2)
library(gridExtra)
### Load the 14 GIA data
data_path <- "Z:/WP2-SolidEarth/GIAforwardModels/textfiles/"
file_names <- system(paste("ls", data_path), intern = TRUE)[1:14]
GIA_name <- unlist(strsplit(file_names, split = ".txt"))
GIA_priors <- list()
for (i in 1:14){
GIA_priors[[i]] <- GIA_temp <- read.table(paste0(data_path, file_names[i]), header = T)
}
Assume the GIA solutions are GIA plus Gaussian noise and the standard errors reflect the magnitude of the Gaussian noise. We define a point GIA value to be significantly deviated from zero if it is larger than 2 times the corresponding standard errors. Hence the “zero region” will be those with GIA values smaller than 2 times the standard errors. We plot such “zero region” for the 14 GIA solutions and their ensemble means and standard errors.
## setup base
world_map <- map_data("world2")
map_zero1 <- list()
## Individual data sets
for (i in 1:14){
GIA_temp <- GIA_priors[[i]]
GIA_temp$zero <- abs(GIA_temp$trend) < GIA_temp$std * 2
map_temp1 <- ggplot(data=GIA_temp) + geom_raster(aes(x = x_center, y = y_center, fill = trend)) +
coord_fixed() + xlab("Longitude") + ylab("Latitude") +
scale_x_continuous(limits=c(0,359), expand = c(0, 0)) + scale_y_continuous(limits=c(-89.5,89.5), expand = c(0, 0))
map_temp1 <- map_temp1 + geom_polygon(data=world_map, aes(x=long, y=lat, group=group),
colour="grey", fill = NA, alpha = 0.5) + ggtitle(paste(i, GIA_name[i], "-- trend"))
map_temp2 <- ggplot(data=GIA_temp) + geom_raster(aes(x = x_center, y = y_center, fill = std)) +
coord_fixed() + xlab("Longitude") + ylab("Latitude") +
scale_x_continuous(limits=c(0,359), expand = c(0, 0)) + scale_y_continuous(limits=c(-89.5,89.5), expand = c(0, 0))
map_temp2 <- map_temp2 + geom_polygon(data=world_map, aes(x=long, y=lat, group=group),
colour="grey", fill = NA, alpha = 0.5) + ggtitle("standard error")
map_temp3 <- ggplot(data=GIA_temp) + geom_raster(aes(x = x_center, y = y_center, fill = zero)) +
coord_fixed() + xlab("Longitude") + ylab("Latitude") +
scale_x_continuous(limits=c(0,359), expand = c(0, 0)) + scale_y_continuous(limits=c(-89.5,89.5), expand = c(0, 0))
map_temp3 <- map_temp3 + geom_polygon(data=world_map, aes(x=long, y=lat, group=group),
colour="grey", fill = NA, alpha = 0.5) + ggtitle(paste(i, GIA_name[i], "-- zero region"))
map_zero1[[i]] <- map_temp3
## Plot the result
print(map_temp3)
}
Then do the same for the ensemble mean of the 14 data sets.
## create the ensemble data sets
GIA_ensemble <- GIA_priors[[1]]
GIA_ensemble$trend <- rowMeans(sapply(GIA_priors, "[[", "trend"))
GIA_ensemble$sd <- sqrt((rowSums(sapply(GIA_priors, function(x) (x$std)^2)))/14)
GIA_ensemble$zero <- abs(GIA_ensemble$trend) < GIA_ensemble$std*2
## Plot the result
map_ens1 <- ggplot(data=GIA_ensemble) + geom_raster(aes(x = x_center, y = y_center, fill = trend)) +
coord_fixed() + xlab("Longitude") + ylab("Latitude") +
scale_x_continuous(limits=c(0,359), expand = c(0, 0)) + scale_y_continuous(limits=c(-89.5,89.5), expand = c(0, 0))
map_ens1 <- map_ens1 + geom_polygon(data=world_map, aes(x=long, y=lat, group=group),
colour="grey", fill = NA, alpha = 0.5) + ggtitle("ensemble -- trend")
map_ens2 <- ggplot(data=GIA_ensemble) + geom_raster(aes(x = x_center, y = y_center, fill = std)) +
coord_fixed() + xlab("Longitude") + ylab("Latitude") +
scale_x_continuous(limits=c(0,359), expand = c(0, 0)) + scale_y_continuous(limits=c(-89.5,89.5), expand = c(0, 0))
map_ens2 <- map_ens2 + geom_polygon(data=world_map, aes(x=long, y=lat, group=group),
colour="grey", fill = NA, alpha = 0.5) + ggtitle("standard error")
map_ens3 <- ggplot(data=GIA_ensemble) + geom_raster(aes(x = x_center, y = y_center, fill = zero)) +
coord_fixed() + xlab("Longitude") + ylab("Latitude") +
scale_x_continuous(limits=c(0,359), expand = c(0, 0)) + scale_y_continuous(limits=c(-89.5,89.5), expand = c(0, 0))
map_ens3 <- map_ens3 + geom_polygon(data=world_map, aes(x=long, y=lat, group=group),
colour="grey", fill = NA, alpha = 0.5) + ggtitle("zero region")
## Plot the result
map_ens1
map_ens2
map_ens3
The plots based on the statistical criterion is not satisfactory, so we change to use a simple rule based on absolute value smaller than a given threshold, say 0.5mm/year.
## Individual data sets
for (i in 1:14){
GIA_temp <- GIA_priors[[i]]
GIA_temp$zero <- abs(GIA_temp$trend) < 0.5
map_temp3 <- ggplot(data=GIA_temp) + geom_raster(aes(x = x_center, y = y_center, fill = zero)) +
coord_fixed() + xlab("Longitude") + ylab("Latitude") +
scale_x_continuous(limits=c(0,359), expand = c(0, 0)) + scale_y_continuous(limits=c(-89.5,89.5), expand = c(0, 0))
map_temp3 <- map_temp3 + geom_polygon(data=world_map, aes(x=long, y=lat, group=group),
colour="grey", fill = NA, alpha = 0.5) + ggtitle(paste(i, GIA_name[i], "-- zero region"))
map_zero1[[i+14]] <- map_temp3
## Plot the result
print(map_temp3)
}
Then the same for the ensemble mean.
GIA_ensemble$zero2 <- abs(GIA_ensemble$trend) < 0.5
## Plot the result
map_ens3.2 <- ggplot(data=GIA_ensemble) + geom_raster(aes(x = x_center, y = y_center, fill = zero2)) +
coord_fixed() + xlab("Longitude") + ylab("Latitude") +
scale_x_continuous(limits=c(0,359), expand = c(0, 0)) + scale_y_continuous(limits=c(-90,90), expand = c(0, 0))
map_ens3.2 <- map_ens3.2 + geom_polygon(data=world_map, aes(x=long, y=lat, group=group),
colour="grey", fill = NA, alpha = 0.5) + ggtitle("ensemble zero region 2")
## Plot the result
map_ens3.2
The ensemble mean using the absolute value criteria looks reasonable for defining the zero value polygons. Now we convert the pixels in the zero stable area into polygons.
## create a Spatialpixels object
GIA_ens <- GIA_ensemble[,c(1:3,16)]
library(sp)
library(raster)
coordinates(GIA_ens) <- c("x_center", "y_center")
gridded(GIA_ens) <- TRUE
GIA_ensR <- raster(GIA_ens, layer = 2) # 2 is the column no of zero2
GIA_zeroPoly <- rasterToPolygons(GIA_ensR, fun=function(zero2) {zero2 == TRUE}, dissolve=TRUE )
## Loading required namespace: rgeos
plot(GIA_zeroPoly, col = "blue", main = "zero certain regions")
Note that there are holes in the large polygons and check if they are correctly labeled.
sapply(GIA_zeroPoly@polygons[[1]]@Polygons, function(x) slot(x, "hole"))
## [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [12] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [23] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [34] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [45] FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE
## [56] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
We can convert the spatialPolygonDataFrame object to a data frame that can be plotted by ggplot2.
library(broom)
GIA_zeroDF <- tidy(GIA_zeroPoly)
## Regions defined for each Polygons
zeropoly_map <- ggplot()+ coord_fixed()+ geom_polygon(data = GIA_zeroDF, aes(x=long, y=lat, group = group, fill = !hole),colour="black")
print(zeropoly_map)
Given the zero value region, we also want to exclude the region that include real GPS readings. Now we plot the latest GPS data for inspection.
GPS_data <- read.table("Z:/WP2-SolidEarth/GPS/NGL/GPS_version03/GPSdataset_v03c_QC_20170809_comb.txt", header = T)
GPS_data$lon <- ifelse(GPS_data$lon < 0, GPS_data$lon + 359, GPS_data$lon)
map_ensGPS <- map_ens3.2 + geom_point(data=GPS_data, aes(x=lon, y=lat), pch=19,
col = "red", fill = "red", alpha=0.7) + ggtitle("ensemble zero region 2 with GPS")
map_ensGPS
With the GPS added in the plots, it is messy to define polygons for the pseudo observations. One simple way to create the locations is to generate a regular mesh over the entire sphere, then remove vertices outside the zero value region and points that has GPS reading or too close to a GPS reading.
First, generate vertices that evenly distributed over the entire globe. The distance between the vertices is about the same as the correlation length of the of the residual process, which is about 400-500km apart. This is about spreading 1000 points using the Fibonacci spiral on a sphere.
## xyz -- Generate equally distributed points around a sphere -- Fibonacci spiral
fiboSphere1 <- function(n = 1000L) {
phi <- (sqrt(5) + 1) / 2 # golden ratio
i <- seq(-(n-1), (n-1),2)
theta <- 2 * pi * i / phi
sphi <- i/n
cphi <- sqrt((n+i) * (n-i))/n
x <- cphi * sin(theta)
y <- cphi * cos(theta)
z <- sphi
return(cbind(x,y,z))
}
##lonlat -- Generate equally distributed points around a sphere -- Fibonacci spiral
fiboSphere2 <- function(n = 1000L) {
phi <- (sqrt(5) + 1) / 2 - 1 # golden ratio
ga <- phi * 2 * pi # golden angle
i <- 1L:n
lon <- ga * i / (2 * pi)
lon <- 2 * pi * (lon - floor(lon))
lon <- ifelse(lon <= pi, lon, lon - 2 * pi)/pi*180
lat <- asin(-1 + 2 * i / n)/pi*180
cbind(lon = lon, lat = lat)
}
fibopoints <- fiboSphere2(n = 1000)
fibopoints <- data.frame(fibopoints)
fibopoints$lon <- ifelse(fibopoints$lon < 0, fibopoints$lon+359, fibopoints$lon)
## Plot these points
map_fibo <- map_ensGPS + geom_point(data=data.frame(fibopoints), aes(x=lon, y=lat), pch=19,
col = "blue", alpha=0.5) + ggtitle("ensemble zero region 2 with GPS and fibo points")
map_fibo
Then remove points that are not in the zero region polygons.
fibsp <- SpatialPoints(fibopoints, proj4string = CRS("+proj=longlat"))
zeroPolys <- SpatialPolygons(GIA_zeroPoly@polygons, proj4string = CRS("+proj=longlat"))
fibin <- over(fibsp, zeroPolys)
fibin <- ifelse(is.na(fibin), FALSE, TRUE)
## plot these points
fibpoint_in <- data.frame(fibopoints[fibin, ])
map_fibo2 <- zeropoly_map + geom_point(data=fibpoint_in, aes(x=lon, y=lat), pch=19,
col = "blue", alpha=0.5) + ggtitle("fibpoints in the zero value regions")
map_fibo2
Then remove points that are too close to the GPS observations (say within the correlation length 400-500km , that is about 5 degree in longlat).
fibin2 <- apply(fibpoint_in, 1, function(x) all(apply(GPS_data[,2:3], 1, function(y) dist(rbind(y, x)) > 5)))
fibpoint_in2 <- fibpoint_in[fibin2,]
map_fibo3 <- zeropoly_map + geom_point(data=fibpoint_in2, aes(x=lon, y=lat), pch=19,
col = "blue", alpha=0.5) +
geom_point(data=GPS_data, aes(x=lon, y=lat), pch=19,
col = "red", fill = "red", alpha=0.7) +
ggtitle("fibpoints in the zero value regions away from GPS")
map_fibo3
Finally, assemble the pseudo observation and real observation into the same data set. The pseudo observations are simulated from a zero mean Gaussian distribution with sd error half of the smallest real GPS error.
pseudo_error <- min(GPS_data$std)/2
nps <- nrow(fibpoint_in2)
pseudo_obs <- rnorm(nps, sd = pseudo_error)
pseudo_data <- data.frame(ID = rep("pseudo", nps), lon = fibpoint_in2$lon, lat = fibpoint_in2$lat,
trend = pseudo_obs, std = rep(pseudo_error, nps))
obs_all <- rbind(GPS_data, pseudo_data)
obs_all$source <- ifelse(obs_all$ID == "pseudo", "pseudo", "real")
map_final <- ggplot() + coord_fixed() + geom_polygon(data=world_map, aes(x=long, y=lat, group=group), colour="black", fill = NA) +
geom_polygon(data = GIA_zeroDF, aes(x=long, y=lat, group = group), colour="red", fill = NA) +
geom_point(data=obs_all, aes(x=lon, y=lat, colour = source), pch=19) +
ggtitle("Final map of real and pseudo observations.")
map_final
Plot with another projection.
library(mapproj)
## Loading required package: maps
map_final + coord_map(projection = "orthographic")