A very common procedure is to transform lines and borders into SpatialPolygons objects using the Polygon functions from the sp package. But is it possible to transform other object classes into SpatialPolygons? I use the function circles from dismo to create a circumference with specific radius distance from a known spatial point. This function returns an object of class CirclesRange.
circ<-circles(spcoords,d=100000)
class(circ)
[1] "CirclesRange"
attr(,"package")
[1] "dismo"
When I try to convert the CirclesRange object into SpatialPolygons, the following error occurs:
Error: is.integer(pO) is not TRUE
Then, I have searched other ways to transform this object, but I have not been successful. I think that first it is necessary to transform "circ" into another class and then try to convert it to SpatialPolygons, but I can't find information about this.
Have a look at str(circ), the desired SpatialPolygons object is already part of the created object. You simply need to run circ#polygons to extract the polygon. Here is some sample code based on the meuse dataset.
## sample data
data(meuse)
coordinates(meuse) <- ~ x + y
proj4string(meuse) <- CRS("+init=epsg:28992")
## circle around the first 'meuse' feature (top-right corner)
circ <- circles(meuse[1, ], d = 1000, lonlat = FALSE)
poly <- circ#polygons
proj4string(poly) <- proj4string(meuse)
## display data
library(latticeExtra)
spplot(meuse, "elev", scales = list(draw = TRUE),
col.regions = topo.colors(100), key.space = "right") +
as.layer(spplot(poly, fill = "transparent", lwd = 2))
Related
Problem
I want to rasterize spatial lines and assign the maximum value of all lines that touch/intersect a raster cell to that cell.
I work in terra with lines as a SpatVect object, I would hence prefer a terra::rasterize solution. I would also be happy with a solution using stars::st_rasterize (see also this question).
The documentation of terra::rasterize seems to suggest it does not support lines properly so far - using the fun argument seems to be limited to points vector data only. I tried with terra::rasterize nevertheless, see the example below.
I'm aware of raster::rasterize, but it seems a bit outdated since it's still sp object based. Or is it the only way to do it atm?
Example
Here you can see that neither the max nor the mean function seems to work properly when rasterizing via terra::rasterize(... fun = "max"/"mean"):
library("terra")
### Example data ###
f <- system.file("ex/lux.shp", package="terra")
v <- vect(f)
lns <- as.lines(v)
r <- rast(v, res=.2)
### Rasterize via terra::rasterize ###
x_max <- rasterize(lns, r, fun="max", field = "POP")
x_mean <- rasterize(lns, r, fun="mean", field = "POP")
### Plot results ###
fontsize <- 0.7
par(mfrow=c(1,3))
plot(lns, y = "POP", main = "Lines with original values")
text(lns, "POP", cex = fontsize)
plot(x_max, main = "Rasterized via fun 'max'")
text(x_max, cex = fontsize)
plot(lns, add = T)
plot(x_mean, main = "Rasterized via fun 'mean'")
text(x_mean, cex = fontsize)
plot(lns, add = T)
I have found a somewhat hacky solution.
As proposed in the comments, I turned the lines into points by sampling along them.
I used sf::st_line_sample() for this instead of rgeos::gInterpolate() - it was cleaner and easier. Then terra::rasterize() can handle the points correctly and applies the max fun as expected.
Confirmed by the plot below.
Example solution
library("terra")
library("dplyr")
library("sf")
library("units")
### Example data ###
f <- system.file("ex/lux.shp", package="terra")
v <- vect(f)
lns <- as.lines(v)
r <- rast(v, res=.2)
### Turn SpatVector lines into sf object
lns_sf <- lns %>%
st_as_sf() %>%
st_transform(2169) # reprojection needed for st_line_sample
### Sample points along all lines every kilometer
pts_geometries <- lns_sf %>%
st_line_sample(density = units::set_units(1, 1/km))
### Add attributes and make MULTIPOINTs simple POINTS
pts_sf <- st_sf(lns_sf,
geometry = pts_geometries) %>%
st_cast("POINT") %>%
st_transform(crs(lns))
### Go back to terra: Turn sf into SpatVector object
pts <- pts_sf %>%
vect()
### Now rasterization works and "max" function is applied corretly
x_max <- rasterize(pts, r, fun="max", field = "POP")
fontsize <- 0.7
par(mfrow=c(1,3))
plot(lns, y = "POP", main = "Lines with original values")
text(lns, "POP", cex = fontsize)
plot(x_max, main = "Rasterized with fun 'max' using points generated from lines")
text(x_max, cex = fontsize)
plot(lns, add = T)
plot(pts, main = "Points used for rasterization")
My problem: I want to draw a map obtained via rastermap package with ggplot2.
Searching for alternatives to ggmap package I found the rastermap package which provides an easy way to obtain maps from external sources. The readme provides a very simple example:
# install.packages("devtools")
devtools::install_github("hadley/rastermap")
houston <- fetch_region(c(-95.80204, -94.92313), c(29.38048, 30.14344),
stamen("terrain"))
houston
plot(houston)
The problem comes whether I try to plot using ggplot. So far I've tried several ways but none of them seems to work. Is it possible? Any idea?
rastermap generates a matrix of colours in hexadecimal strings (#RRGGBB format). It may be simplest to convert this to a more common form for spatial data, a multiband raster brick, with separate layers for the red, green and blue.
We can write a short helper function to convert hexadecimal strings into the separate integer values (i.e. this is the reverse of the rgb() function):
un_rgb = function (x) {
x = unlist(str_split(x, ''))
r = paste0(x[2], x[3])
g = paste0(x[4], x[5])
b = paste0(x[6], x[7])
strtoi(c(r,g,b), 16)
}
Using this function we convert the rastermap matrix into a three band raster brick:
library(raster)
m = as.matrix(houston)
l=lapply(m[], un_rgb)
r=g=b=matrix(, dim(m)[1], dim(m)[2])
r[] = sapply(l, function(i) i[1])
g[] = sapply(l, function(i) i[2])
b[] = sapply(l, function(i) i[3])
rgb.brick = brick(raster(r), raster(g), raster(b))
And set the extent of the new raster to that of the original rastermap
extent(rgb.brick) = extent(matrix(unlist(attributes(houston)$bb), nrow=2))
Now that we have a more usual form of raster object, we can do various things with it. For example, we can plot it in ggplot using library(RStoolbox):
ggRGB(rgb.brick, r=1, g=2, b=3)
Or we can save it as an image to use as an annotation background in ggplot:
png('test.png', dim(rgb.brick)[2], dim(rgb.brick)[1])
plotRGB(rgb.brick, 1, 2, 3)
dev.off()
img <- png::readPNG("test.png")
gr <- grid::rasterGrob(img, interpolate=TRUE)
ggplot() + annotation_custom(gr, -Inf, Inf, -Inf, Inf)
Why would you want an alternative? You can get a stamen map from ggmap:
library(ggmap)
ggmap(get_stamenmap(c(-95.80204, 29.38048, -94.92313, 30.14344))) +
# some points to plot
geom_point(aes(x = seq(-95.5, -95.2, 0.1), y = seq(29.7, 30, 0.1)), color = "red")
I'm attempting to create a map in R using library(tmap) with a full-color RGB, masked Landsat image. The NAs however appear as black. Here's what I did.
Using library(sf) I calculated the centroids of 5 polygons and buffered them by 5000m. Following this I used library(raster) to mask a Landsat image by the buffered centroids. The code looks like this and works perfectly.
# Read in the data
polys <- st_read("nybb.shp")
rast <- brick("LC08_L1TP_013032_20171027_20171027_01_RT.tif")
# Transform polys, crop raster, calculate centroids
polys <- st_transform(polys, crs = crs(rast, asText = TRUE))
rast <- crop(rast, as(polys, "Spatial"))
cent <- st_centroid(polys) %>% st_buffer(., 5000) %>% as(., "Spatial")
# Mask the raster using buffered centroids
r <- mask(rast, cent)
I can accomplish what I want using base R and library(raster) -- BUT I would prefer to do this using tmap.
# Code that works
plot(polys$geometry, col = "grey", border = "white")
plotRGB(r, bgalpha = 0, add = TRUE)
# Code that does not work
# The NAs in the masked raster appear as black
# even when using the colorNA argument
tm_shape(polys) + tm_polygons() +
tm_shape(r, bbox = polys) +
tm_rgb(colorNA = "red")
Any idea how to show the masked raster using tmap's tm_rgb() function without showing the NAs as black?
To create a basemap with tmap, you can use the read_osm function, from the tmaptools package as follows. Note that you must first transform the data into a geographical CRS: epsg=4326
library(tmaptools)
library(OpenStreetMap)
rast <-projectRaster(rast,crs="+init=epsg:4326",method = "ngb") #(you can use method=method="bilinear")
polys <- spTransform(polys, CRS("+init=epsg:4326"))
background <- read_osm(bbox(rast))
tm_shape(background) + tm_raster() +
tm_shape(polys) + tm_polygons() +
tm_shape(r, bbox = polys) +
tm_rgb(colorNA = "red")
I have this dataset: https://www.dropbox.com/s/k06n9l05t25r6x2/newdata.csv?dl=0
(Sample)
"","row","col","flagrv"
"1",2361,530,2
"2",2378,531,2
"3",2360,531,2
"4",2355,531,2
"5",2363,532,2
"6",2359,532,2
"7",2368,533,2
"8",2367,533,2
"10",2359,533,2
And if I plot using this code:
gs.pal <- colorRampPalette(c("blue", "green","yellow","orange","red"),bias=1,space="rgb")
ggplot(data=ndata,aes(x=col,y=row,color=flagrv)) +
geom_point(size = 0.01)+
scale_colour_gradientn(name = "Scale",colours = gs.pal(5))+
xlab('Longitude')+
ylab('Latitude')+
theme_bw()+
theme(line = element_blank())+
theme(legend.position = c(.93,.20),panel.grid.major = element_line(colour = "#854440"))+
ggsave("test.png",width=10, height=8,dpi=300)
We get this figure:
Now, the problem is I don't have Lat-Long values. I want to overlay the state boundaries but can't use the Maps package. Someone suggested I used gdal but I don't know how. Could you please tell me how I can map this into the Lat-Long domain so that I can easily manipulate it.
Edit:
I learnt from someone else that I can use this:
gdal_translate -a_srs EPSG:4269 FILE.asc FILE.tif
#
Errors for answers 1
Error: unexpected ']' in "spdf = SpatialPointsDataFrame(coords, all_data[, c("flagrv"]"
Then I changed the code to:
spdf = SpatialPointsDataFrame(coords, all_data[, c("flagrv")])
But now I have this error:
Error in validObject(.Object) : invalid class “SpatialPointsDataFrame” object: invalid object for slot "data" in class "SpatialPointsDataFrame": got class "integer", should be or extend class "data.frame"
Without knowing at least the projection and datum of the dataset (but hopefully more info such as resolution and extent), there is no easy way to do this. If this is a derived map, try to find what was used to generate it.
With this information you can then use the projection function in the raster package to define the projection of the dataset.
EDIT (based on additional info provided, there is a working solution):
Here is a working solution given that the lower left corner of the dataset has a 24.55, -130 coordinate, spacing among row/col is 0.01 degrees and projection is nad83. Note that the metadata info provided was wrong, as the min lat value was not 20 degrees but could be estimated from the southernmost point (key west) as 24.55.
#load dataset
all_data=(read.csv('new_data.csv',header=T, stringsAsFactors=F))
res=0.01 #spacing of row and col coords pre-specified
#origin_col_row=c(0, 0)
origin_lat_lon=c(24.55, -130)
all_data$row=(all_data$row)*res+origin_lat_lon[1]
all_data$col=(all_data$col)*res+origin_lat_lon[2]
#now that we have real lat/lon, we can just create a spatial dataframe
library(rgdal)
library(sp)
coords = cbind(all_data$col, all_data$row)
spdf = SpatialPointsDataFrame(coords, data=all_data) #sp = SpatialPoints(coords)
proj4string(spdf) <- CRS("+init=epsg:4269")
r seems to choke trying to plot that many points, so to check if the answer made sense, I saved the dataset as a shapefile and plotted it on arcgis:
writeOGR(spdf,"D:/tmp_shapefile4.shp", "flagrv", driver="ESRI Shapefile")
I managed to plot it using ggplot2 with the code below, just be patient as it takes a while to plot it:
df=as.data.frame(spdf)
library(ggplot2)
ggplot(data=df,aes(x=col,y=row,color=flagrv))+
geom_point(size = 0.01)+
xlab('Longitude')+
ylab('Latitude')
I'd like to use spplot + sp.lines (lattice) instead of plot + segments. Do you know a simple way to realise this, e.g. R: Calculating the shortest distance between two point layers
library(dismo)
require(rgdal)
require(FNN)
laurus <- gbif("Laurus", "nobilis")
locs <- subset(laurus, !is.na(lat) & !is.na(lon),
select = c("country", "lat", "lon"))
locs.uk <- subset(locs, locs$country=="United Kingdom")
locs.ire <- subset(locs, locs$country=="Ireland")
uk_coord <- SpatialPoints(locs.uk[,c("lon","lat")])
ire_coord <- SpatialPoints(locs.ire[,c("lon","lat")])
crs.geo<-CRS("+proj=longlat +ellps=WGS84 +datum=WGS84")
proj4string(uk_coord) <- crs.geo
proj4string(ire_coord) <- crs.geo
uk_coord <- spTransform(uk_coord, CRS("+init=epsg:27700"))
ire_coord <- spTransform(ire_coord, CRS("+init=epsg:27700"))
g = get.knnx(coordinates(uk_coord), coordinates(ire_coord),k=1)
to visualise this
plot(uk_coord, col=2, xlim=c(-1e5,6e5))
plot(ire_coord, add=TRUE)
segments(coordinates(ire_coord)[,1],
coordinates(ire_coord)[,2],
coordinates(uk_coord[g$nn.index[,1]])[,1],
coordinates(uk_coord[g$nn.index[,1]])[,2])
can probably converted to something like
ire <- list("sp.points", ire_coord)
spplot(uk_coord, sp.layout=list(ire))
but is there a easy way to convert segments to SpatialLines i.e. list("sp.lines", Lines(...))
Try panel.segments() from the lattice-package:
library("lattice")
spplot(rbind(uk_coord, ire_coord), auto.key=FALSE,
panel=function(...) {
panel.xyplot(...)
panel.segments(coordinates(ire_coord)[,1],
coordinates(ire_coord)[,2],
coordinates(uk_coord[g$nn.index[,1]])[,1],
coordinates(uk_coord[g$nn.index[,1]])[,2])
})
Understanding panel functions is more powerful than relying on sp.layout in spplot -- and so is using lattice or grid functions directly. The solution with sp.layout could look like this:
spplot(uk_coord, auto.key=FALSE, col.regions = 'black',
sp.layout = list(ire,
list("panel.segments",
coordinates(ire_coord)[,1],
coordinates(ire_coord)[,2],
coordinates(uk_coord[g$nn.index[,1]])[,1],
coordinates(uk_coord[g$nn.index[,1]])[,2])),
xlim = c(-140000,700000))
note that it is not restricted to the sp.lines etc functions; in upcoming sp 1.1-0, quotes around function names can also be omitted.
spplot tries to plot attributes of features in color by default, which is not meaningful here, so what you basically want is an xyplot with controlled aspect ratio (asp="iso").