plot hexbin density over map with base graphics in R - r

I have spatial coordinate data, and I would like to plot a density map of these coordinates, overlaying a map. The hexbin R package looks promising, but I am struggling to figure out how to plot hexbin data over a map. I would also prefer to stick with base graphics, rather than ggplot.
Here is a simple example of what I am trying to do (that doesn't work):
library(hexbin)
library(sf)
library(maps)
x <- rnorm(10000, mean = 0, sd = 40)
y <- rnorm(10000, mean = 0, sd = 20)
bin <- hexbin(x, y)
pts <- st_as_sf(cbind.data.frame(x,y), coords = 1:2, crs = "+init=epsg:4326")
map(col = gray(0.95), fill=T, lwd=0.2)
plot(pts, add=TRUE, pch = 3, cex=0.5)
Here, the hexbin data are not plotting in the coordinate space of the map.
map(col = gray(0.95), fill=T, lwd=0.2)
plot(bin, style = "colorscale", newpage=FALSE)
Any sugggestions? Thanks!

Related

Plotting polygons with rasters in base R, ggplot2 or levelplot

I am trying to plot in R a raster layer with lines/polygon objects in R and each time I fail miserably with errors. I tried to do this in base R, ggplot2 and using levelplot but can't get the right result.
Source data can be found here.
What I need to do in the plot (all in one plot) is to:
1) zoom in a certain area defined as NIG. T
2) Display raster r values on a scale with cuts intervals.
3) Plot the country boundaries(shpAfr in base R and ggplot2 or world.outlines.spin levelplot). 4) Finally, include shpWater polygon layer (with col="blue" fill and contours).
library(raster)
library(maptools)
library(rasterVis)
library(viridis)
library(sf)
library(rgdal)
library(ggplot2)
r <- raster("raster_example.tif")
crs(r) <- "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +to wgs84=0,0,0"
NIG <- c(2,14.5,4,14)
Reg_name <- "Nigeria"
shpAfr <- readOGR(dsn="Africa.shp")
proj4string(shpAfr) # describes data’s current coordinate reference system
#st_read(system.file("shape/nc.shp", package="sf"))
# Import water polygon
shpWater <- readOGR(dsn="waterbodies_africa.shp")
shpWater.count <- nrow(shpWater#data)
shpWater$id <- 1:shpWater.count
shpWater.fort <- fortify(shpWater, region='id')
# Import Africa admin map
shpAfr <- readOGR(dsn="Africa.shp")
shpAfr.count <- nrow(shpAfr#data)
shpAfr$id <- 1:shpAfr.count
shpAfr.fort <- fortify(shpAfr, region='id')
# Set colour intervals for plotting:
cuts=seq(0,1,0.1) #set breaks
Trying in base R, my problem is I can get the water shape fill in the right colour (fill and contour should be blue). If I try to plot both wrld_simpl and shpWater as polygon() I get into even bigger troubles.
plot(r, xlim = NIG[1:2], ylim = NIG[3:4],
breaks=cuts, col = rev(plasma(11)))
lines(wrld_simpl,lwd = 1.5)
lines(shpWater, col="blue") # works but cannot fill the polygon
polygon(shpWater, col = "blue", border = "blue") # getting error here
Error in as.double(y) :
cannot coerce type 'S4' to vector of type 'double'
Ok, so now I try ggplot2, but I can't find a way to include a raster here without getting an error.
lon <- seq(r#extent#xmin,r#extent#xmax,
(r#extent#xmax-r#extent#xmin)/r#ncols)
lat <- seq(r#extent#ymin,r#extent#ymax,
(r#extent#ymax-r#extent#ymin)/r#nrows)
Plot1 <- ggplot()+
geom_polygon(aes(x = long, y = lat, group=id),
data = shpAfr.fort, color ="grey27", fill ="grey",
alpha = .4, size = .2)+
geom_raster(data = test, aes(fill=values))+ ## here it goes bad
#geom_tile(data=test_df, aes(x=x, y=y, fill=value), alpha=0.8) +
#scale_fill_viridis() +
geom_polygon(aes(x = long, y = lat, group=id),
data = shpWater.fort, color ="lightskyblue2", fill ="lightskyblue2",
size = .2)+coord_equal()+
theme_minimal()+
coord_map(xlim = Region[[3]][1:2],ylim = Region[[3]][3:4])
plot(Plot1)
Finally, I tried the levelplot and AGAIN failed.
mapTheme <- rasterTheme(region = rev(brewer.pal(10, "RdBu")))
# Get world outlines:
world.outlines <- map("world", plot=FALSE)
world.outlines.sp <- map2SpatialLines(world.outlines, proj4string = CRS("+proj=longlat"))
# Plot raster and polygon:
Plot2 <- levelplot(r,par.settings = mapTheme,pretty=TRUE,margin = F,
xlim = NIG[1:2],ylim = NIG[3:4],
col.regions=colorRampPalette(c("light blue","blue", "red")),
main=paste0("test")) + layer(sp.lines(world.outlines.sp, col = "black", lwd = 0.5))
plot(Plot2 + layer(sp.lines(world.outlines.sp, col = "black", lwd = 0.5))
#Error: Attempted to create layer with no stat.
My results so far:
1) first image does not have the polygons filled with blue
2) second image has clearly world outlines not in the right location
:
You would have probably have had answers a lot earlier if you had made a simple reprex, e.g. like this
library(raster)
r <- raster(res=1/12)
values(r) <- sample(100, ncell(r), replace=TRUE)
filename <- system.file("external/lux.shp", package="raster")
v <- shapefile(filename)
zoom in a certain area
One way to zoom is to use crop (alternatively use the ext argument in plot)
x <- crop(r, v)
Display raster r values on a scale with cuts intervals
cuts <- c(0,20,60,100)
plot(x, breaks=cuts, col=rainbow(3))
or
y <- cut(x, cuts)
Plot the country boundaries
lines(v)
Finally, include polygon layer (with col="blue" fill and contours).
plot(v[c(1,3),], col="blue", border="red", lwd=2, add=TRUE)
6 months later but I feel this question. My two thoughts are (1) I have had luck with plotting geom_sf and geom_stars together. You have to change your raster to a df before changing to a geom_stars. and (2) regardless of method, you need all datasets in the same projection - check with crs() and set all to the same with st_transform()
I didn't actually test this with your data but something like:
make raster into a df
test.df = as.data.frame (test, xy=TRUE) # Convert to data.frame, keeping the
coordinates
class(test.df)
convert to geom_stars
test.stars = st_as_stars(test.df)
try your plot
Plot1 <- ggplot()+
geom_stars(data = test, aes(fill=values))+ #need to plot raster first I think?
scale_fill_identity( name = "", breaks = cuts,labels = "")+
geom_sf(data = shpAfr.fort, color ="grey27", size = .2)+
geom_sf(data = shpWater.fort, color ="lightskyblue2", fill
="lightskyblue2", size = .2)+
theme_minimal()+
coord_sf( xlim = NIG[1:2], ylim = NIG[3:4]),expand = FALSE)
Plot1

Can't limit boundaries of a plotted shapefile properly

I can't really set the boundaries of my plotted shapefile. I'm plotting the shapefile first to get nice x- and y- labels in degrees first, plotting the data afterwards. In the end, I'm plotting my shapefile over the data again. ylim is changeable, but xlim seems to be solely dependend on ylim changes, because I cant vary xlim itself. It only varies, when I change ylim without changing xlim, like as it is an aspect ratio issue.
I want to limit the x-axis between 8.5 and 11.5 degrees.
A link to the shapefile and raster in question: https://www.dropbox.com/sh/l42mty01mwtm8qc/AADqjNbGkmNwx3o9aFceGrkya?dl=0
My code:
library(rgeos)
library(rgdal)
library(raster)
Sys.setlocale(category = "LC_ALL", locale = "C") # In the case German Umlauts are a problem while reading the shapefile
map <- readOGR("C:\\Path\\NATRAUM_MR_utm32.shp")
# Exclude unnecessary regions
map <- subset(map, NATREGNR != 3)
map <- subset(map, NATREGNR != 4)
map <- subset(map, NATREGNR != 9)
map_wgs84 <- spTransform(map, crs(raster.percent.change.rcp85.1971_2005.2071_2100))
# Pixelplot
par(mar = c(3, 3, 2, 1)) # For saving pictures through a device like pdf
m <- plot(map_wgs84, axes=TRUE, xlim = c(8.5, 11.5), ylim=c(51.35, 53.15), cex = .5,
bty = "n")
# Colortable for legend
colTab <- c("#0033CC", "#3366FF", "#6699FF","#99FFFF", "#FFCC99","#FF9933", "#FF4D00","#660000")
N <- length(colTab)
breaks <- seq(-2, 2, length.out= N+1 )
plot(raster.percent.change.rcp85.1971_2005.2071_2100, col = colTab, breaks = breaks,
axis.args = list(cex.axis = 1, at = breaks, labels = breaks, mgp = c(1, 0, 0), tck = 0.1),
legend.args = list(text='Change [%]', side=4, font=2, line=1.75, cex=0.7),
add = TRUE
)
plot(map_wgs84, add = TRUE) # Plotting shapefile over data
You need to resize your plotting window. You can use par(fin=c(x,y)), or png() to set a ratio that works, and fix that in code.
This is because for maps, the correct aspect (ratio of vertical and horizontal extent) is enforced. For planar data the aspect is 1. For angular data (longitude/latitude) it varies by latitude.
With some data.
library(raster)
p <- shapefile(system.file("external/lux.shp", package="raster"))
Compare:
par(fin=c(6,6))
plot(p, axes=T, xlim=c(6,6.2), ylim=c(49.6, 49.8))
par(fin=c(4, 6))
plot(p, axes=T, xlim=c(6,6.2), ylim=c(49.6, 49.8))

Plotting 3D bars on top of the map using R

I've found a way to plot 3D bar chart (ggplot2 3D Bar Plot). Thank you #jbaums
However, is there a way to change the bottom facet to a map? So I can clearly visualize, for example, the population density using bar chart on a map to show the differences between different parts? Thank you in advance. plotting 3D bars on top of the map
Here's one way
# Plotting 3D maps using OpenStreetMap and RGL. For info see:
# http://geotheory.co.uk/blog/2013/04/26/plotting-3d-maps-with-rgl/
map3d <- function(map, ...){
if(length(map$tiles)!=1){stop("multiple tiles not implemented") }
nx = map$tiles[[1]]$xres
ny = map$tiles[[1]]$yres
xmin = map$tiles[[1]]$bbox$p1[1]
xmax = map$tiles[[1]]$bbox$p2[1]
ymin = map$tiles[[1]]$bbox$p1[2]
ymax = map$tiles[[1]]$bbox$p2[2]
xc = seq(xmin,xmax,len=ny)
yc = seq(ymin,ymax,len=nx)
colours = matrix(map$tiles[[1]]$colorData,ny,nx)
m = matrix(0,ny,nx)
surface3d(xc,yc,m,col=colours, ...)
return(list(xc=xc, yc=yc, colours=colours))
}
require(rgl)
require(OpenStreetMap)
map <- openproj(openmap(c(52.5227,13.2974),c(52.4329,13.5669), zoom = 10))
set.seed(1)
n <- 30
bbox <- unlist(map$bbox, use.names = F)
x <- do.call(runif, c(list(n), as.list(bbox[c(1,3)])))
y <- do.call(runif, c(list(n), as.list(bbox[c(4,2)])))
z <- runif(n, 0, .1)
m <- rbind(cbind(x,y,z=0), cbind(x,y,z))
m <- m[as.vector(mapply(c, 1:n, (n+1):(2*n))),]
open3d(windowRect=c(100,100,800,600))
coords <- map3d(map, lit=F)
segments3d(m, col="red", add=T)
which gives you something like:
And another way, which you can extend to use box3D to maybe make it more look like your example:
library(plot3D)
with(coords, {
image3D(
z = 0, x = xc, y = yc, colvar = colours, zlim = c(0,max(z)),
scale=F, theta = 0, bty="n")
segments3D(x,y,rep(0,length(x)),x,y,z, col="red", add=T)
})

Plot georeferenced raster images in Plotly (R API)

I would like to use Plotly in R to create 3D modells of trenches of archaeological excavations. I'm quite successful to plot point and surface data (Example: Vignette of the R package I'm working on), but I would also like to add raster information of the georeferenced profile pictures of the trenches.
I didn't find any way to plot raster data in Plotlys 3D environment. The only solution I came up with so far (thanks to this post) was to create a 3D modell of the profile with SFM using Photoscan, export the coloured mesh as .ply file, fix the header of this file and import it into R to do the plotting with the following example code:
library(geomorph)
library(plotly)
#load data
mesh <- read.ply("plotly/expply8_corr.ply", ShowSpecimen = FALSE)
# extract vertex coordinates
x <- mesh$vb["xpts",]
y <- mesh$vb["ypts",]
z <- mesh$vb["zpts",]
# plot
plot_ly(
x = x, y = y, z = z,
i = mesh$it[1,]-1, j = mesh$it[2,]-1, k = mesh$it[3,]-1,
facecolor = c(mesh$material$color[1, ]),
type = "mesh3d"
)
You'll find the example data here.
Unfortunately this scales really badly. If you increase the mesh resolution everything becomes to slow. I would really like to just add a simple georeferenced raster to keep the performance high and avoid the necessity to create 3D modells of the profiles. Is there a workflow to achieve this with Plotly or an other plotting library?
I found a nice solution with the package rgl. Example:
library(rgl)
library(jpeg)
# download and load picture
download.file(
url = 'https://upload.wikimedia.org/wikipedia/en/6/6d/Chewbacca-2-.jpg',
destfile = "chewbacca.jpg",
mode = 'wb'
)
chewie <- readJPEG("chewbacca.jpg", native = TRUE)
# create some sample data
x <- sort(rnorm(1000))
y <- rnorm(1000)
z <- rnorm(1000) + atan2(x, y)
# plot sample data
plot3d(x, y, z, col = rainbow(1000), size = 5)
# add picture
show2d(
# plot raster
{
par(mar = rep(0, 4))
plot(
0:1, 0:1, type="n",
ann = FALSE, axes = FALSE,
xaxs = "i", yaxs = "i"
)
rasterImage(chewie, 0, 0, 1, 1)
},
# image position and extent
# coordinate order: lower left, lower right, upper right and upper left
x = c(-2, 1, 1, -2),
y = c(-1, -1, 1, 1),
z = c(-3, -3, 2, 2)
)
The pictures have to be georeferenced with other software (photogrammetry in GIS/CAD). If you have the georeferenced raster you just need the coordinates of its corner points to plot it.

plotting and coloring data on irregular grid

I have data in the form (x, y, z) where x and y are not on a regular grid. I wish to display a 2D colormap of these data, with intensity (say, grey scale) mapped to the z variable. An obvious solution is to interpolate (see below) on a regular grid,
d <- data.frame(x=runif(1e3, 0, 30), y=runif(1e3, 0, 30))
d$z = (d$x - 15)^2 + (d$y - 15)^2
library(akima)
d2 <- with(d, interp(x, y, z, xo=seq(0, 30, length = 30),
yo=seq(0, 30, length = 50), duplicate="mean"))
pal1 <- grey(seq(0,1,leng=500))
with(d2, image(sort(x), sort(y), z, useRaster=TRUE, col = pal1))
points(d$x, d$y, col="white", bg=grey(d$z/max(d$z)), pch=21, cex=1,lwd=0.1)
However, this loses the information of the initial mesh (position of the points with actual data), which could be very fine or very rough at certain locations. My preference would be for a delaunay tiling with triangles, which accurately represents the actual location and density of the original data points.
Ideally the solution would
compute the tesselation outside of the plotting function, so that the resulting polygons may be plotted with either ggplot2, lattice, or base graphics
be fast. In my real-life example (~1e5 points), the calculation of the tesselation via deldir can be really slow.
By "tesselation" I mean either Delaunay triangles or Voronoi diagrams, although my preference would be for the former. However it bring the additional complexity of interpolating the colour of each triangle based on the original data points.
Here's a solution based on dirichlet from the maptools package,
d <- data.frame(x=runif(1e3, 0, 30), y=runif(1e3, 0, 30))
d$z = (d$x - 15)^2 + (d$y - 15)^2
library(spatstat)
library(maptools)
W <- ripras(df, shape="rectangle")
W <- owin(c(0, 30), c(0, 30))
X <- as.ppp(d, W=W)
Y <- dirichlet(X)
Z <- as(Y, "SpatialPolygons")
plot(Z, col=grey(d$z/max(d$z)))
I'm still unsure of the way to extract the polygons from this SpatialPolygons class.
Also if there's an easy way to produce the "correct" colors for the associated delaunay tesselation I'd like to hear it.
Here is a lattice solution using deldir
d <- data.frame(x=runif(1e3, 0, 30), y=runif(1e3, 0, 30))
d$z = (d$x - 15)^2 + (d$y - 15)^2
pal1 <- grey(seq(0,1,leng=500))
library(latticeExtra)
levelplot(z~x*y, data=d,
panel = function(...) panel.voronoi(..., points=FALSE),
interpolate=TRUE,
col.regions = colorRampPalette(pal1)(1e3), cut=1e3)

Resources