Adding point locations to a 3D DEM plot in R - r

I have some point locations which include UTMs and Elevation as a data frame
I also have a DEM layer.
I have figured out how to plot the DEM in 3D using plot3D in rgl.
I can also plot the points in 3D using points3d.
I have been able to put them in the same plot using points3d with add=TRUE
however the points and DEM are radically far away from each other.
In the code below I also tried to change this to a spatial data frame but rgl doesn't seem to like that.
Is it possible to plot them together with the points laying over the DEM?
I have searched and searched for a solution to this.
Here is the R code I have used so far:
> library(raster)
> library(rgdal)
> library(maptools)
> library(rgeos)
> library(lattice)
> library(latticeExtra)
> library(sp)
> library(rasterVis)
> library(rgl)
>
> # taking data read from a .csv of UTM and elevation values
>
> Points.Sp <- data.frame(Points=Rawdata$PointName, UTM.N=Rawdata$UTM.N, UTM.W=Rawdata$UTM.W, Elevation=Rawdata$Elevation)
> Points.Sp <- unique(Points.Sp) #weeding out duplicates
> Points.Sp <- Points.Sp[,c(3,2,4)] #getting rid of point names # I realize this looks messy but it gets what I want
> head(Points.Sp)
UTM.W UTM.N Elevation
1 275815 3879223 1340
8 274813 3879727 1325
29 275312 3879727 1258
45 275812 3879724 1169
66 276313 3879727 1067
75 276813 3879727 1208
>
> dem.in <- raster("D:/Thesis/SouthernApps/Coweeta/Coweeta/DEM_30m_wgs84.img") # reading in DEM
> plot(dem.in) # check in 2D # takes a long time very large, need to crop
>
> dem.crop <- crop(dem.in, c(272000, 282000, 3878000, 3884000))
> plot(dem.crop) # check in 2D, looks good.
>
> plot3D(dem.crop) # plot in 3D looks like exactly what I want
>
> points3d(Points.Sp, pch=19, cex=2, col="black", add=TRUE) # adds the points to plot but in wrong place
>
> #attempting to set a CRS in case this is the problem.
> coordinates(Points.Sp)=c(1,2)
> proj4string(Points.Sp)=CRS("++proj=utm +zone=17") # set CRS
> str(Points.Sp)
Formal class 'SpatialPointsDataFrame' [package "sp"] with 5 slots
..# data :'data.frame': 71 obs. of 1 variable:
.. ..$ Elevation: int [1:71] 1340 1325 1258 1169 1067 1208 1256 1089 1031 959 ...
..# coords.nrs : num [1:2] 1 2
..# coords : num [1:71, 1:2] 275815 274813 275312 275812 276313 ...
.. ..- attr(*, "dimnames")=List of 2
.. .. ..$ : chr [1:71] "1" "8" "29" "45" ...
.. .. ..$ : chr [1:2] "UTM.W" "UTM.N"
..# bbox : num [1:2, 1:2] 274309 3878440 279876 3883732
.. ..- attr(*, "dimnames")=List of 2
.. .. ..$ : chr [1:2] "UTM.W" "UTM.N"
.. .. ..$ : chr [1:2] "min" "max"
..# proj4string:Formal class 'CRS' [package "sp"] with 1 slot
.. .. ..# projargs: chr "+proj=utm +zone=17 +ellps=WGS84"
>
> # trying this a different way after setting CRS
> x <- Points.Sp#coords[1:71,1]
> y <- Points.Sp#coords[1:71,2]
> z <- Points.Sp#data$Elevation
> m <- data.frame(x=x,y=y,z=z)
>
> plot3D(dem.crop) #again, plot in 3D looks like exactly what I want
> points3d(m, pch=19, cex=2, col="black", add=TRUE) # still adds the points to plot but in wrong place
This code reproduces the problem.
## define a Raster object
data(volcano)
r <- raster(volcano)
extent(r) <- c(0, 610, 0, 870)
## extract sample points
xy <- sampleRandom(r1, 100, xy = TRUE)
r1<-data.frame(x=seq(0, 500, length=(71)), y=seq(0, 500, length=(71)), z=seq(0,500, length=(71)))
## display them
plot3D(r, adjust = FALSE)
points3d(r1, add=TRUE)

As documented in the help page, both the x-axis and y-axis are adjusted with the z values. You can disable this default setting with adjust = FALSE:
library(rgl)
library(rasterVis)
## define a Raster object
data(volcano)
r <- raster(volcano)
extent(r) <- c(0, 610, 0, 870)
## extract sample points
xy <- sampleRandom(r, 100, xy = TRUE)
## display them
plot3D(r, adjust = FALSE)
points3d(xy)

## define a Raster object
data(volcano)
r <- raster(volcano)
extent(r) <- c(0, 610, 0, 870)
## extract sample points
xy <- sampleRandom(r1, 100, xy = TRUE)
#must extract the data from the raster and recombine with the xy data.
#I don't know why this is different than simply using the raw values but it
#provides the desired effect.
r1<-data.frame(x=seq(0, 500, length=(71)), y=seq(0, 500, length=(71)))
z<-extract(r, r1)
r1$z<-z
## display them
plot3D(r, adjust = FALSE)
points3d(r1, add=TRUE)
#points now lie flat on 3d image.
Points flush to 3d Image
Image for original problem

Related

Colouring brain surface with heat map

library(rgl)
library(brainR)
template <- readNIfTI(system.file("MNI152_T1_2mm_brain.nii.gz",
package = "brainR"), reorient = FALSE)
misc3d::contour3d(template, level = 4500, alpha = .7, draw = T)
With the above code one can can generate a 3D model of the brain.
The argument draw = FALSE asks contour3d to compute and return the contour surface as a triangle mesh object without drawing it.
a <- misc3d::contour3d(template, level = 4500, alpha = .7, draw = F)
str(a)
List of 10
$ v1 : num [1:110433, 1:3] 45 45 46 46 47 47 43 43 44 44 ...
$ v2 : num [1:110433, 1:3] 44.1 46 46 47 47 ...
$ v3 : num [1:110433, 1:3] 45 45 45 46 46 ...
$ color : chr "white"
$ color2 : logi NA
$ fill : logi TRUE
$ material: chr "default"
$ col.mesh: logi NA
$ alpha : num 0.7
$ smooth : num 0
- attr(*, "class")= chr "Triangles3D"
I would like to use the external surface of the above object to project heat maps, or say, colouring the surface... I also wonder how to determine certain positions in this model, e.g. EEG channel positions. Is it possible to generate only the surface with a$v1, a$v2 ... using function rgl:::surface3d? Thank you in advance,
I was able to draw the triangle mesh in colors with rgl. Some considerations:
I'm using rgl::triangles3d With this function, points are taken in consecutive triplets, each point v1 v2 v3 a triangle vertex (see
?triangles3d) so i had to extract the points and reorder them.
colors are mapped to vertex, taken in groups of three also. I created a simple color_map based on the x position of each vertex. Of course you must create your desired color map.
Hope this will help you.
data<-misc3d::contour3d(template, level = 4500, alpha = .7, draw = F)
points <- rbind(data$v1, data$v2, data$v3)
points <- points[order(rep(1:(nrow(points)/3),3), rep(1:3, nrow(points)/3)),]
color_map <- c("red","blue","green")[cut(points[,1], 3, labels=F)]
rgl::open3d()
rgl::triangles3d(points, alpha=.7, color = color_map)
Edit
Coordinates x, y, z are in the scale of the positions of the array template i.e. point c(5.4, 10.8, 30.1) is related to the level of the array around [5, 10, 30], that is, that point should be around level=4500

Changing the raster spatial extent in R

I have two rasters and I want to make the spatial extent of one to another. Then save it as a new raster. I used following code. However, I cannot save the 2013 images with new spatial extent as a new raster. Any guidance is greatly appreciated.
raster_2013 <- raster("avgt2013.tif")
extent(raster_2013)
class : Extent
xmin : 112.91
xmax : 153.64
ymin : -43.75
ymax : -9
> res(raster_2013)
[1] 0.01 0.01
>
> raster_2015 <- raster("avgt2015.tif")
> extent(raster_2015)
class : Extent
xmin : 112
xmax : 154
ymin : -44
ymax : -9
> res(raster_2015)
[1] 0.01 0.01
>
> e <- extent(112, 154, -44, -9)
>
> ex = extent(raster_2015)
> r2 = crop(raster_2013, ex)
>
>
> new_2013 <- alignExtent(e, raster_2013, snap='near')
> str(new_2013)
Formal class 'Extent' [package "raster"] with 4 slots
..# xmin: num 112
..# xmax: num 154
..# ymin: num -44
..# ymax: num -9
>
> rc <- crop(raster_2013, e, snap='near')
> extent(rc)
class : Extent
xmin : 112.91
xmax : 153.64
ymin : -43.75
ymax : -9
First, please make a simple reproducible example to ask a question.
library(raster)
set.seed(11)
raster_2013 = raster(ext=extent(112.91, 153.64, -43.75, -9), res=c(0.01, 0.01))
raster_2013[] = rnorm(ncell(raster_2013))
raster_2015 = raster(ext=extent(112, 154, -44, -9), res=c(0.01, 0.01))
raster_2015[] = rnorm(ncell(raster_2015))
Then, there are several issues with your code.
In your case, alignExtent is useless since the two rasters have the same resolution and their extents correspond with regards to this resolution.
If your goal is to give the extent of raster_2015 to raster_2013, you need to realize that extent(raster_2015) is shorter (smaller) with respect to xmin, but larger or equal elsewhere. So cropping alone will just affect xmin of raster_2013. You first need to extend and second to crop in order to have the exact same extent:
new_2013 <- crop(extend(raster_2013, raster_2015), raster_2015)
all.equal(extent(raster_2015), extent(new_2013))
#[1] TRUE
As #Geo-sp mentions, you can also resample raster_2013, but you would typically use this if the rwo rasters are not aligned (and be aware that it would, in such case, result in modified data due to the interpolation). Here, since they are, it would give the same result as crop(extend()), but it would be much slower and more resource-consuming:
system.time(new_2013 <- crop(extend(raster_2013, raster_2015), raster_2015))
# user system elapsed
# 0.676 0.036 0.712
system.time(new_2013_res <- resample(raster_2013, raster_2015))
# user system elapsed
# 10.324 0.536 10.869
all.equal(new_2013, new_2013_res)
# [1] TRUE
Finally, in order to save it, well... you can use writeRaster, as reading the documentation would have lead you to ;-)
writeRaster(new_2013, "raster_2013_extent2015.grd")

Plot from package "lomb" in ggplot2

I am using the package "lomb" to calculate Lomb-Scargle Periodograms, a method for analysing biological time series data. The package does create a plot if you tell it to do so. However, the plots are not too nice (compared to ggplot2 plots). Therefore, I would like to plot the results with ggplot. However, I do not know how to access the function for the curve plotted...
This is a sample code for a plot:
TempDiff <- runif(4033, 3.0, 18) % just generate random numbers
Time2 <- seq(1,4033) % Time vector
Rand.LombScargle <- randlsp(repeats=10, TempDiff, times = Time2, from = 12, to = 36,
type = c("period"), ofac = 10, alpha = 0.01, plot = T,
trace = T, xlab="period", main = "Lomb-Scargle Periodogram")
I have also tried to find out something about the function looking into the function randlsp itself, but could not really find anything that seemed useful to me there...
getAnywhere(randlsp)
A single object matching ‘randlsp’ was found
It was found in the following places
package:lomb
namespace:lomb
with value
function (repeats = 1000, x, times = NULL, from = NULL, to = NULL,
type = c("frequency", "period"), ofac = 1, alpha = 0.01,
plot = TRUE, trace = TRUE, ...)
{
if (is.ts(x)) {
x = as.vector(x)
}
if (!is.vector(x)) {
times <- x[, 1]
x <- x[, 2]
}
if (plot == TRUE) {
op <- par(mfrow = c(2, 1))
}
realres <- lsp(x, times, from, to, type, ofac, alpha, plot = plot,
...)
realpeak <- realres$peak
pks <- NULL
if (trace == TRUE)
cat("Repeats: ")
for (i in 1:repeats) {
randx <- sample(x, length(x))
randres <- lsp(randx, times, from, to, type, ofac, alpha,
plot = F)
pks <- c(pks, randres$peak)
if (trace == TRUE) {
if (i/10 == floor(i/10))
cat(i, " ")
}
}
if (trace == TRUE)
cat("\n")
prop <- length(which(pks >= realpeak))
p.value <- prop/repeats
if (plot == TRUE) {
mx = max(c(pks, realpeak)) * 1.25
hist(pks, xlab = "Peak Amplitude", xlim = c(0, mx), main = paste("P-value: ",
p.value))
abline(v = realpeak)
par(op)
}
res = realres[-(8:9)]
res = res[-length(res)]
res$random.peaks = pks
res$repeats = repeats
res$p.value = p.value
class(res) = "randlsp"
return(invisible(res))
Any idea will be appreciated!
Best,
Christine
PS: Here an example of the plot with real data.
The key to getting ggplot graphs out of any returned object is to convert the data that you need in to some sort of data.frame. To do this, you can look at what kind of object your returned value is and see what sort of data you can immediately extract into a data.frame
str(Rand.LombScargle) # get the data type and structure of the returned value
List of 12
$ scanned : num [1:2241] 12 12 12 12 12 ...
$ power : num [1:2241] 0.759 0.645 0.498 0.341 0.198 ...
$ data : chr [1:2] "times" "x"
$ n : int 4033
$ type : chr "period"
$ ofac : num 10
$ n.out : int 2241
$ peak : num 7.25
$ peak.at : num [1:2] 24.6908 0.0405
$ random.peaks: num [1:10] 4.99 9.82 7.03 7.41 5.91 ...
$ repeats : num 10
$ p.value : num 0.3
- attr(*, "class")= chr "randlsp"
in the case of randlsp, its a list, which is usually what is returned from statistical functions. Most of this information can also be obtained from ?randlsp too.
It looks as if Rand.LombScargle$scanned and Rand.LombScargle$power contains most of what is needed for the first graph:
There is also a horizontal line on the Periodogram, but it doesn't correspond to anything that was returned by randlsp. Looking at the source code that you provided, it looks as if the Periodogram is actually generated by lsp().
LombScargle <- lsp( TempDiff, times = Time2, from = 12, to = 36,
type = c("period"), ofac = 10, alpha = 0.01, plot = F)
str(LombScargle)
List of 12
$ scanned : num [1:2241] 12 12 12 12 12 ...
$ power : num [1:2241] 0.759 0.645 0.498 0.341 0.198 ...
$ data : chr [1:2] "Time2" "TempDiff"
$ n : int 4033
$ type : chr "period"
$ ofac : num 10
$ n.out : int 2241
$ alpha : num 0.01
$ sig.level: num 10.7
$ peak : num 7.25
$ peak.at : num [1:2] 24.6908 0.0405
$ p.value : num 0.274
- attr(*, "class")= chr "lsp"
I am guessing that, based on this data, the line is indicating the significance level LombScargle$sig.level
Putting this together, we can create our data to pass to ggplot from lsp:
lomb.df <- data.frame(period=LombScargle$scanned, power=LombScargle$power)
# use the data frame to set up the line plot
g <- ggplot(lomb.df, aes(period, power)) + geom_line() +
labs(y="normalised power", title="Lomb-Scargle Periodogram")
# add the sig.level horizontal line
g + geom_hline(yintercept=LombScargle$sig.level, linetype="dashed")
For the histogram, it looks like this is based on the vector Rand.LombScargle$random.peaks from randlsp:
rpeaks.df <- data.frame(peaks=Rand.LombScargle$random.peaks)
ggplot(rpeaks.df, aes(peaks)) +
geom_histogram(binwidth=1, fill="white", colour="black") +
geom_vline(xintercept=Rand.LombScargle$peak, linetype="dashed") +
xlim(c(0,12)) +
labs(title=paste0("P-value: ", Rand.LombScargle$p.value),
x="Peak Amplitude",
y="Frequency")
Play around with these graphs to get them looking to your taste.

Plotting density points on a map in R

I've been trying to plot the number of tourists from different regions in Canada on a map in R using ggplot2 and openstreet map, but I seem to be missing a step as my points all end up in the bottom right corner of the map and my map shrinks in size.
Here are some of the data I used in the dataset map.tourists.
id Nb.Touristes Nb.Nuitees
1001 939.9513 1879.903
1004 1273.4336 2546.867
1006 776.5203 3882.602
1010 3118.4872 18598.194
1102 921.7354 3971.677
1103 622.8770 1245.754
And here is the code I have until now. The data with the coordinates is in the Statistic Canada file that I download in the code below.
download.file("http://www12.statcan.gc.ca/census-recensement/2011/geo/bound-limit/files-fichiers/gcd_000b11a_e.zip", destfile="gcd_000b11a_e.zip")
unzip("gcd_000b11a_e.zip")
library(maptools)
canada<-readShapeSpatial("gcd_000b11a_e")
library(GISTools)
CDCenters <- coordinates(canada)
CDCenters <- SpatialPointsDataFrame(coords=canada, data=canada#data,
proj4string=CRS("+proj=longlat +ellps=clrk66"))
CDCenters=data.frame(CDCenters, row.names=NULL , id=CDCenters$CDUID)
canada_map <- merge(CDCenters, map.tourists, by="id")
list <- ls()
list <- list[-grep("canada_map", list)]
rm(list=list)
rm(list)
Sys.setenv(NOAWT=1)
library(OpenStreetMap)
library(rgdal)
library(stringr)
library(ggplot2)
mp <- openmap(c(71, -143), c(40, -50), zoom=4, type="osm",mergeTiles=TRUE)
library(ggplot2)
autoplot(mp) +
geom_point(data=canada_map, alpha = I(8/10), aes(x=coords.x1,y=coords.x2, size=Nb.Touristes, color=Nb.Touristes)) +
theme(axis.line=element_blank(), axis.text.x=element_blank(), axis.text.y=element_blank(), axis.ticks=element_blank(), axis.title.x=element_blank(), axis.title.y=element_blank()) +
scale_size_continuous(range= c(1, 25)) +
scale_colour_gradient(low="blue", high="red") +
labs(title="Nombre de touristes à Montréal en 2010 selon la division de recensement d’origine")
I'd post the image that I get, but I don't have enough reputation yet !
I get two legends, the map is concentrated in the top left corner and all the points seem to be in the bottom right corner...
What can I do?
Thank you!!
I went through your code and did my best to see what is happening here. In short, I would recommend you to use the ggmap package. I am not an expert in GIS, but it seems to me that the map you got (i.e., mp) is not something ggplot likes.
library(maptools)
library(GISTools)
library(ggmap)
library(ggplot2)
### Following the OP here
download.file("http://www12.statcan.gc.ca/census-recensement/2011/geo/bound-limit/files-fichiers/gcd_000b11a_e.zip", destfile="gcd_000b11a_e.zip")
unzip("gcd_000b11a_e.zip")
canada<-readShapeSpatial("gcd_000b11a_e")
CDCenters <- coordinates(canada)
CDCenters <- SpatialPointsDataFrame(coords=canada, data=canada#data,
proj4string=CRS("+proj=longlat +ellps=clrk66"))
CDCenters <- data.frame(CDCenters, row.names=NULL , id=CDCenters$CDUID)
### Tourist data
dat <- structure(list(id = c(1001L, 1004L, 1006L, 1010L, 1102L, 1103L
), Nb.Touristes = c(939.9513, 1273.4336, 776.5203, 3118.4872,
921.7354, 622.877), Nb.Nuitees = c(1879.903, 2546.867, 3882.602,
18598.194, 3971.677, 1245.754)), .Names = c("id", "Nb.Touristes",
"Nb.Nuitees"), class = "data.frame", row.names = c(NA, -6L))
### Merge the map data and tourist data
canada_map <- merge(CDCenters, dat, by="id")
### OK, now I want to get maps in two different ways.
### This is by the OP
mp <- openmap(c(71, -143), c(40, -50), zoom=4, type="osm",mergeTiles=TRUE)
#str(mp)
#List of 2
# $ tiles:List of 1
# ..$ :List of 5
# .. ..$ colorData : chr [1:701964] "#B5D0D0" "#B5D0D0" "#B5D0D0" "#B5D0D0" ...
# .. ..$ bbox :List of 2
# .. .. ..$ p1: num [1:2] -15918687 11402272
# .. .. ..$ p2: num [1:2] -5565975 4865942
# .. ..$ projection:Formal class 'CRS' [package "sp"] with 1 slots
# .. .. .. ..# projargs: chr "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=#null +no_defs"
# .. ..$ xres : int 666
# .. ..$ yres : int 1054
# .. ..- attr(*, "class")= chr "osmtile"
# $ bbox :List of 2
# ..$ p1: num [1:2] -15918687 11402272
# ..$ p2: num [1:2] -5565975 4865942
# - attr(*, "zoom")= int 4
# - attr(*, "class")= chr "OpenStreetMap"
Here I do not see lon and lat like 40,50, and 60. This somehow makes me think that ggplot may not like the values.
Here is another map using ggmap When I printed out the image, lon and lat are in numbers I expected.
### Get openstreetmap using ggmap
ca.map2 <- get_openstreetmap(bbox= c(left = -143, bottom = 40, right = -50, top = 71),
scale = 69885283, format = "png")
#str(ca.map2)
#chr [1:334, 1:529] "#B5D0D0" "#B5D0D0" "#B5D0D0" "#B5D0D0" "#B5D0D0" "#B5D0D0" "#B5D0D0" ...
#- attr(*, "class")= chr [1:2] "ggmap" "raster"
#- attr(*, "bb")='data.frame': 1 obs. of 4 variables:
#..$ ll.lat: num 40
#..$ ll.lon: num -143
#..$ ur.lat: num 71
#..$ ur.lon: num -50
So, I am guessing that coords.x1 and coords.x2 in canada_map are probably not matching with the numbers in the object, mp. At least, there is something screwy happening due to the difference in lon and lat values between mp and canada_map. In order to make lon and lat values consistent in your data (canada_map) and a map, I used the ggmap object (ca.map2) and drew a graphic. If you want to have colour and size in one legend this is the way you do it. In summary, you may want to stick to ggmap and ggplot in order to avoid similar problems in the future.
ggmap(ca.map2) +
geom_point(data = canada_map,
aes(x=coords.x1,y=coords.x2, size = Nb.Touristes, color = Nb.Touristes)) +
guides(colour = guide_legend())

R: strange map format

I got a file containing the following data:
str(dat)
List of 2
$ x: Named num [1:28643] 2714769 2728569 NA 2728569 2740425 ...
..- attr(*, "names")= chr [1:28643] "h" "h" "" "h" ...
$ y: Named num [1:28643] 925000 925000 NA 925000 925000 ...
..- attr(*, "names")= chr [1:28643] "h" "h" "" "h" ...
- attr(*, "class")= chr [1:2] "bor" "list"
dat$x[1:10]
h h h h h h h
2714769 2728569 NA 2728569 2740425 NA 2740425 2751585 NA 2751585
dat$y[1:10]
h h h h h h h
925000 925000 NA 925000 925000 NA 925000 925000 NA 925000
class(dat)
"bor" "list"
table(names(dat$x))
h
479 28164
table(names(dat$y))
h
479 28164
plot(dat, type='l') results in a nice map.
I read about an old/simple form of line-'objects' used in S in "Applied Spatial Data Analysis with R" (Bivand, Pebesma, Gomez-Rubio; Springer 2008) on Page 38, which seem to have similarities to my file. This format defines a line as "start-point; end-point; NA" triplet.
Do you know this format?
How can I convert it to an sp-object?
Thanks in advance
Based on your information, here is one possilbe way to go:
Assuming that your data represent lines and that the NA values indicate the end of each line, you can convert your data to spatial lines doing the following:
# Creating artificial data for the example
dat <- list()
dat$x <- rnorm(1000) + rep(c(rep(0, 99), NA), 10)
dat$y <- dat$x + rnorm(1000)
# For simplicity, convert to data frame
# (this would be the first step for you to do with your data)
mydat <- data.frame(x = dat$x, y = dat$y)
# Convert each part to a line, using the NA values as breaks
mylines <- list()
last <- 1
for(i in 1:nrow(mydat)){
if(is.na(mydat$x[i])){
print(i)
mylines[[as.character(i)]] <- Lines(Line(mydat[last:(i-1),]), ID = as.character(i))
last <- i+1
}
}
# Convert to spatial lines object
mylines <- SpatialLines(mylines)
# Plot to see if it worked
plot(mylines)

Resources