Exporting a contoured Kernel density estimation plot to raster or shapefile format - r

I'm trying to perform Kernel density estimation in R using some GPS data that I have. My aim is to create a contoured output with each line representing 10% of the KDE. From here i want to import the output (as a shapefile or raster) into either QGIS or arcmap so I can overlay the output on top of existing environmental layers.
So far i have used AdehabitatHR to create the following output using the below code:
kud<-kernelUD(locs1[,1], h="href")
vud<-getvolumeUD(kud)
vud <- estUDm2spixdf(vud)
xyzv <- as.image.SpatialGridDataFrame(vud)
contoured<-contour(xyzv, add=TRUE)
Aside from being able to remove the colour, this is how i wish the output to appear (or near to). However i am struggling to figure out how i can export this as either a shapefile or raster? Any suggestions would be gratefully received.

With the amt package this should be relatively straightforward:
library(adehabitatHR)
library(sf)
library(amt)
data("puechabonsp")
relocs <- puechabonsp$relocs
hr <- as.data.frame(relocs) %>% make_track(X, Y, name = Name) %>%
hr_kde(trast = raster(amt::bbox(., buffer = 2000), res = 50)) %>%
hr_isopleths(level = seq(0.05, 0.95, 0.1))
# Use the sf package to write a shape file, or any other supported format
st_write(hr, "~/tmp/home_ranges.shp")
Note, it is also relatively easy to plot
library(ggplot2)
ggplot(hr) + geom_sf(fill = NA, aes(col = level))

Related

Trying to plot in tmap shapefile with attribute

I am trying to work with municipality data in Norway, and I'm totally new to QGIS, shapefiles and plotting this in R. I download the municipalities from here:
Administrative enheter kommuner / Administrative units municipalities
Reproducible files are here:
Joanna's github
I have downloaded QGIS, so I can open the GEOJson file there and convert it to a shapefile. I am able to do this, and read the data into R:
library(sf)
test=st_read("C:/municipality_shape.shp")
head(test)
I have on my own given the different municipalities different values/ranks that I call faktor, and I have stored this classification in a dataframe that I call df_new. I wish to merge this "classification" on to my "test" object above, and wish to plot the map with the classification attribute onto the map:
test33=merge(test, df_new[,c("Kommunekode_str","faktor")],
by=c("Kommunekode_str"), all.x=TRUE)
This works, but when I am to plot this with tmap,
library(tmap)
tmap_mode("view")
tm_shape(test33) +
tm_fill(col="faktor", alpha=0.6, n=20, palette=c("wheat3","red3")) +
tm_borders(col="#000000", lwd=0.2)
it throws this error:
Error in object[-omit, , drop = FALSE] : incorrect number of
dimensions
If I just use base plot,
plot(test33)
I get the picture:
You see I get three plots. Does this has something to do with my error above?
I think the main issue here is that the shapes you are trying to plot are too complex so tmap is struggling to load all of this data. ggplot also fails to load the polygons.
You probably don't need so much accuracy in your polygons if you are making a choropleth map so I would suggest first simplifying your polygons. In my experience the best way to do this is using the package rmapshaper:
# keep = 0.02 will keep just 2% of the points in your polygons.
test_33_simple <- rmapshaper::ms_simplify(test33, keep = 0.02)
I can now use your code to produce the following:
tmap_mode("view")
tm_shape(test_33_simple) +
tm_fill(col="faktor", alpha=0.6, n=20, palette=c("wheat3","red3")) +
tm_borders(col="#000000", lwd=0.2)
This produces an interactive map and the colour scheme is not ideal to tell differences between municipalities.
static version
Since you say in the comments that you are not sure if you want an interactive map or a static one, I will give an example with a static map and some example colour schemes.
The below uses the classInt package to set up breaks for your map. A popular break scheme is 'fisher' which uses the fisher-jenks algorithm. Make sure you research the various different options to pick one that suits your scenario:
library(ggplot2)
library(dplyr)
library(sf)
library(classInt)
breaks <- classIntervals(test_33_simple$faktor, n = 6, style = 'fisher')
#label breaks
lab_vec <- vector(length = length(breaks$brks)-1)
rounded_breaks <- round(breaks$brks,2)
lab_vec[1] <- paste0('[', rounded_breaks[1],' - ', rounded_breaks[2],']')
for(i in 2:(length(breaks$brks) - 1)){
lab_vec[i] <- paste0('(',rounded_breaks[i], ' - ', rounded_breaks[i+1], ']')
}
test_33_simple <- test_33_simple %>%
mutate(faktor_class = factor(cut(faktor, breaks$brks, include.lowest = T), labels = lab_vec))
# map
ggplot(test_33_simple) +
geom_sf(aes(fill = faktor_class), size= 0.2) +
scale_fill_viridis_d() +
theme_minimal()

Add points (Lat-Lon) to a marmap plot

I would like to plot a series of lat-lon points of a seal track, each coloured according to an attribute, on to a map that shows the bathymetry (100m contours) and coastline. I learnt how to create a map to show the bathymetry+coastline using marmap and ggplot2. The code is here:
dat <- getNOAA.bathy(-58,-62.5,43,46.0,res=0, keep=TRUE)
plot(dat,image=TRUE,bpal = list(c(min(dat), 0, "darkblue", "blue","lightblue"), c(0, max(dat), "gray90","gray10")),drawlabels=TRUE,deep=c(-500,200,0),shallow=c(-500,100,0),step=c(500,100,0),lwd=c(1,1,1),lty=c(1,1,1),land=TRUE)+
scaleBathy(dat, deg=1.232, x="bottomleft", inset=5) #100km
This created a useful map. However, I am stalled over how to add the seal track on to this map.
I could do this in ggmap (using the code below) but I much prefer the marmap map
myLocation <- c(-62.5,43,-58,46)
seal_map2<-get_map(location=myLocation,maptype="watercolor",source="stamen",zoom=10)
ggmap(seal_map2)+
geom_point(data=sealtrack,aes(color=category),size=0.5)+
scale_color_gradientn(colours=rainbow(6), breaks=seq(1,6,by=1))
Any guidance will be much appreciated
You should be able to add the bathymetric info from marmap as a contour layer on your plot after "fortifying" it. Without your data it's difficult to make sure that it works (and the NOAA server is down for me right now):
library(ggplot2)
library(marmap)
dat <- getNOAA.bathy(-58,-62.5,43,46.0,res=0, keep=TRUE)
dat <- fortify(dat)
ggmap(seal_map2) +
geom_contour(dat, aes(x = x, y = y, z = z)) +
geom_point(data=sealtrack,aes(color=category),size=0.5) +
scale_color_gradientn(colours=rainbow(6), breaks=seq(1,6,by=1))

Overlap image plot on a Google Map background in R

I'm trying to add this plot of a function defined on Veneto (italian region)
obtained by an image and contour:
image(X,Y,evalmati,col=heat.colors(100), xlab="", ylab="", asp=1,zlim=zlimits,main=title)
contour(X,Y,evalmati,add=T)
(here you can find objects: https://dl.dropboxusercontent.com/u/47720440/bounty.RData)
on a Google Map background.
I tried two ways:
PACKAGE RGoogleMaps
I downloaded the map mbackground
MapVeneto<-GetMap.bbox(lonR=c(10.53,13.18),latR=c(44.7,46.76),size = c(640,640),MINIMUMSIZE=TRUE)
PlotOnStaticMap(MapVeneto)
but i don't know the commands useful to add the plot defined by image and contour to the map
PACKAGE loa
I tried this way:
lat.loa<-NULL
lon.loa<-NULL
z.loa<-NULL
nx=dim(evalmati)[1]
ny=dim(evalmati)[2]
for (i in 1:nx)
{
for (j in 1:ny)
{
if(!is.na(evalmati[i,j]))
{
lon.loa<-c(lon.loa,X[i])
lat.loa<-c(lat.loa,Y[j])
z.loa<-c(z.loa,evalmati[i,j])
}
}
}
GoogleMap(z.loa ~ lat.loa*lon.loa,col.regions=c("red","yellow"),labels=TRUE,contour=TRUE,alpha.regions=list(alpha=.5, alpha=.5),panel=panel.contourplot)
but the plot wasn't like the first one:
in the legend of this plot I have 7 colors, and the plot use only these values. image plot is more accurate.
How can I add image plot to GoogleMaps background?
If the use of a GoogleMap map is not mandatory (e.g. if you only need to visualize the coastline + some depth/altitude information on the map), you could use the package marmap to do what you want. Please note that you will need to install the latest development version of marmap available on github to use readGEBCO.bathy() since the format of the files generated when downloading GEBCO files has been altered recently. The data from the NOAA servers is fine but not very accurate in your region of interest (only one minute resolution vs half a minute for GEBCO). Here is the data from GEBCO I used to produce the map : GEBCO file
library(marmap)
# Get hypsometric and bathymetric data from either NOAA or GEBCO servers
# bath <- getNOAA.bathy(lon1=10, lon2=14, lat1=44, lat2=47, res=1, keep=TRUE)
bath <- readGEBCO.bathy("GEBCO_2014_2D_10.0_44.0_14.0_47.0.nc")
# Create color palettes for sea and land
blues <- c("lightsteelblue4", "lightsteelblue3", "lightsteelblue2", "lightsteelblue1")
greys <- c(grey(0.6), grey(0.93), grey(0.99))
# Plot the hypsometric/bathymetric map
plot(bath, land=T, im=T, lwd=.03, bpal = list(c(0, max(bath), greys), c(min(bath), 0, blues)))
plot(bath, n=1, add=T, lwd=.5) # Add coastline
# Transform your data into a bathy object
rownames(evalmati) <- X
colnames(evalmati) <- Y
class(evalmati) <- "bathy"
# Overlay evalmati on the map
plot(evalmati, land=T, im=T, lwd=.1, bpal=col2alpha(heat.colors(100),.7), add=T, drawlabels=TRUE) # use deep= shallow= step= to adjust contour lines
plot(outline.buffer(evalmati),add=TRUE, n=1) # Outline of the data
# Add cities locations and names
library(maps)
map.cities(country="Italy", label=T, minpop=50000)
Since your evalmati data is now a bathy object, you can adjust its appearance on the map like you would for the map background (adjust the number and width of contour lines, adjust the color gradient, etc). plot.bath() uses both image() and contour() so you should be able to get the same results as when you plot with image(). Please take a look at the help for plot.bathy() and the package vignettes for more examples.
I am not realy inside the subject, but Lovelace, R. "Introduction to visualising spatial data in R" might help you
https://github.com/Robinlovelace/Creating-maps-in-R/raw/master/intro-spatial-rl.pdf From section "Adding base maps to ggplot2 with ggmap" with small changes and data from https://github.com/Robinlovelace/Creating-maps-in-R/archive/master.zip
library(dplyr)
library(ggmap)
library(rgdal)
lnd_sport_wgs84 <- readOGR(dsn = "./Creating-maps-in-R-master/data",
layer = "london_sport") %>%
spTransform(CRS("+init=epsg:4326"))
lnd_wgs84_f <- lnd_sport_wgs84 %>%
fortify(region = "ons_label") %>%
left_join(lnd_sport_wgs84#data,
by = c("id" = "ons_label"))
ggmap(get_map(location = bbox(lnd_sport_wgs84) )) +
geom_polygon(data = lnd_wgs84_f,
aes(x = long, y = lat, group = group, fill = Partic_Per),
alpha = 0.5)

plot raster with discrete colors using rasterVis

I have a few rasters I would like to plot using gplot in the rasterVis package. I just discovered gplot (which is fantastic and so much faster than doing data.frame(rasterToPoints(r))). However, I can't get a discrete image to show. Normally if r is a raster, I'd do:
rdf=data.frame(rasterToPoints(r))
rdf$cuts=cut(rdf$value,breaks=seq(0,max(rdf$value),length.out=5))
ggplot(rdf)+geom_raster(aes(x,y,fill=cuts))
But is there a way to avoid the call to rasterToPoints? It is very slow with large rasters. I did find I could do:
cuts=cut_interval(r#data#values,n=5)
but if you set the fill to cuts it plots the integer representation of the factors.
Here is some reproducible data:
x=seq(-107,-106,.1)
y=seq(33,34,.1)
coords=expand.grid(x,y)
rdf=data.frame(coords,depth=runif(nrow(coords),0,2)))
names(rdf)=c('x','y','value')
r=rasterFromXYZ(rdf)
Thanks
gplot is a very simple wrapper around ggplot so don't expect too
much from it. Instead, you can use part of its code to build your own
solution. The main point here is to use sampleRegular to reduce the
number of points to be displayed.
library(raster)
library(ggplot2)
x <- sampleRegular(r, size=5000, asRaster = TRUE)
dat <- as.data.frame(r, xy=TRUE)
dat$cuts <- cut(dat$value,
breaks=seq(0, max(dat$value), length.out=5))
ggplot(aes(x = x, y = y), data = dat) +
geom_raster(aes(x, y, fill=cuts))
However, if you are open to plot without ggplot2 you may find useful
this other
answer.

How can I plot shapefile loaded through fastshp in ggplot2?

I stumbled upon fastshp library and according to description (and my quick cursory tests) it really does offer improvements in time of reading large shapefiles compared to three other methods.
I'm using read.shp function to load exemplary dataset from maptools package:
library("maptools")
setwd(system.file("shapes", package="maptools"))
shp <- read.shp("columbus.shp", format="polygon")
I chose 'polygon' format since accordng to docs:
This is typically the preferred format for plotting.
My question is how can I plot these polygons using ggplot2 package?
Since read.shp in the fastshp package returns the polygon data in the form of a list of lists, it is then a matter of reducing it to a single dataframe required for plotting in ggplot2.
library(fastshp)
library(ggplot2)
setwd(system.file("shapes", package="maptools"))
shp <- read.shp("columbus.shp", format="polygon")
shp.list <- sapply(shp, FUN = function(x) do.call(cbind, x[c("id","x","y")]))
shp.df <- as.data.frame(do.call(rbind, shp.list))
shp.gg <- ggplot(shp.df, aes(x = x, y=y, group = id))+geom_polygon()
EDIT: Based on #otsaw's comment regarding polygon holes, the following solution requires a couple of more steps but ensures that the holes are plotted last. It takes advantage that shp.df$hole is logical and polygons with hole==TRUE will be plotted last.
shp.list <- sapply(shp, FUN = function(x) Polygon(cbind(lon = x$x, lat = x$y)))
shp.poly <- Polygons(shp.list, "area")
shp.df <- fortify(shp.poly, region = "area")
shp.gg <- ggplot(shp.df, aes(x = long, y=lat, group = piece, order = hole))+geom_polygon()

Resources