From points to buffer passing through a smoothed line using sf package - r

I have a lot of shapefiles of points that I have to manipulate in R.
My aim is to link all the points with a line, smooth it (to recreate a kind of path through points), segmentize the smoothed line in small sections (every section must have a precise length) and then create a buffer for every segments (then transform from lines to polygon) and finally count the points inside the polygons.
I start importing the points:
p <- st_read("/points.shp")
then I create the line:
l <- p %>% st_coordinates() %>% st_linestring()
From the line to the smoothed line:
ls <- smooth(l, method = "ksmooth")
Then I have created the segmentized smoothed line:
sls = st_segmentize(ls, 50)
And finally my buffer:
mybuf <- st_buffer(sls, dist= 30, endCapStyle="ROUND")
Unfortunately with this last command I can create only one buffer but I have to obtain a "segmented" buffer with a length of 50 meters and a height of 30m for each section.
I'm working with WGS84/UTM zone 32 N, epsg 32632 projection and my buffers must have the same projection.
Maybe there is another way to to that? Thanks...
Here the link to download a subset of the shapefile

From what I can tell, the main issue to surmount was that your code defines the line through your points as a single feature, so st_buffer was drawing a buffer around the whole line, rather than each segment between points. My goal was to figure out how to make sure each 50 meter segment was a unique feature.
library(sf)
library(smoothr)
cols <- c("red", "blue", "yellow")
source_file <- "./punti.shp"
par(mfrow = c(2,4))
p <- st_read(source_file)
plot(st_geometry(p), col = cols, main = "points")
l <- p %>% st_coordinates() %>% st_linestring()
plot(st_geometry(l), col = cols, main = "line")
l <- st_as_sf(data.frame(id = 1, geom=st_geometry(l)))
ls <- smooth(l, method = "ksmooth")
plot(st_geometry(ls), col = cols, main = "smoothed line")
# Note that segmentize doesn't slice a line
# Instead, it seems to just increases the number of vertices
sls <- st_segmentize(ls, 50)
slsp <- st_cast(sls, "POINT")
plot(st_geometry(slsp), col = cols, main = "segmented line vertices")
# Draw line between pairs of consecutive points
slsp <- st_as_sf(data.frame(id = 1, geom=st_geometry(slsp)))
slsp2 <- cbind(slsp[-nrow(slsp),"geometry"], slsp[-1,"geometry"])
ll <- st_sfc(mapply(function(a,b){
st_cast(st_union(a,b),"LINESTRING")}, slsp2$geometry, slsp2$geometry.1, SIMPLIFY=FALSE))
plot(st_geometry(ll), col = cols, main = "manually segmented line")
plot(st_geometry(head(ll)), col = cols, main = "man. segmented line, 1-10")
# Assign crs
st_crs(ll)
st_crs(ll) <- st_crs(p)
# Calculate buffers
mybuf <- st_buffer(ll, dist= 30, endCapStyle="ROUND")
plot(st_geometry(mybuf), col = cols, main = "buffers, all")
plot(st_geometry(head(mybuf)), col = cols, main = "buffers, 1-10")
# Count points in buffers
lengths(st_intersects(mybuf, p))

Related

select raster within a specified distance from polygon boundary

I want to select raster cells that are within a certain distance (for e.g. 1 km or 5 km) from the boundary of a polygon. I ultimately want to take an average of only those raster cells that are within the specified distance from the boundary of shapefile inwards.
The way I thought I would approach is to create a negative buffer inwards, and subtract the original polygon and the buffer. Then mask and crop the raster using the new polygon and take the average.
Here's sample data demonstrating what I want to do.
library(raster)
# raster
r <- raster(xmn=1035792, xmx= 1116792, ymn=825303.6, ymx=937803.6, resolution = 12.5,crs = "+init=epsg:3174")
r <- setValues(r, 0)
# polygon
x <- c(1199999, 1080000, 1093067, 1090190, 1087977, 1070419, 1180419)
y <- c(957803.6,937803.6, 894366.9, 872153.9, 853703.0, 825353.6, 805353.6)
poly.lake <- SpatialPolygons(list(Polygons(list(Polygon(data.frame(x,y))), ID = 1)))
r <- mask(r, poly.lake)
r <- crop(r, poly.lake)
plot(poly.lake)
plot(r, add = T)
Instead of taking average of the resulting raster r, I only want to average raster cells which are within a certain specified distance from the boundary.
The example data but using "terra"
library(terra)
r <- rast(xmin=1035792, xmax= 1116792, ymin=825303.6, ymax=937803.6, resolution = 125, crs = "epsg:3174")
values(r) <- 1:ncell(r)
# polygon
x <- c(1199999, 1080000, 1093067, 1090190, 1087977, 1070419, 1180419)
y <- c(957803.6,937803.6, 894366.9, 872153.9, 853703.0, 825353.6, 805353.6)
p <- vect(cbind(x, y), "polygons", crs = "epsg:3174")
r <- mask(r, p)
r <- crop(r, p)
You can now take the internal buffer of p
b <- buffer(p, -10000)
x <- mask(r, b, inverse=TRUE)
global(x, mean,na.rm=T)
# mean
#lyr.1 296549.9
Or you can take both sides like this
bb <- buffer(as.lines(p), 10000)
y <- mask(r, bb)
global(y, mean,na.rm=T)
# mean
#lyr.1 296751.3
So there is a slight difference between these two approaches; I think because the first uses inverse=TRUE; I would go with the second approach.
Your drawing (and Chris' answer) suggests that you only want the distance to the western border. In that case, you can first find the start and end nodes you need (from 2 to 6)
plot(p)
points(p)
text(as.points(p), pos=2)
Select the segments in between these nodes and create a line type SpatVector.
g <- geom(p)
k <- vect(g[2:6,], "lines", crs=crs(p))
lines(k, col="red", lwd=2)
And now do as above.
bk <- buffer(k, 10000)
z <- mask(r, bk)
global(z, mean,na.rm=T)
# mean
#lyr.1 297747
If you wanted to get the part of buffer bk that is inside the original polygon p you can do
bki <- intersect(bk, p)
To complete the plot
polys(bk, lty=3, border=NA, col=adjustcolor("light blue", alpha.f = 0.4))
lines(bki, lty=3)
Finding which segments of a polygon to buffer was what puzzled me, and this seems a decent approach cast_poly_to_subsegments. Taking your poly.lake as poly_sf:
geom <- lapply(
1:(length(st_coordinates(poly_sf)[, 1]) - 1),
function(i) {
rbind(
as.numeric(st_coordinates(poly_sf)[i, 1:2]),
as.numeric(st_coordinates(poly_sf)[i + 1, 1:2])
)
}
+ ) |>
st_multilinestring() |>
st_sfc(crs=st_crs(rt)) |>
st_cast('LINESTRING')
gives us
which is a little surprising, the 'green and red', that I assumed would be 'green'. It is wound clockwise so the desired segments to buffer are 4 & 5.
lns_buf4 <- st_buffer(st_geometry(geom)[4], 1000, singleSide = TRUE)
lns_buf5 <- st_buffer(st_geometry(geom)[5], 1000, singleSide= TRUE)
lns_buf5_neg <- st_buffer(st_geometry(geom)[5], -1000, singleSide= TRUE)
plot(st_geometry(geom), col = c('red', 'yellow', 'blue', 'green'))
plot(lns_buf4, col = 'black', add = TRUE)
plot(lns_buf5, col = 'green', add = TRUE)
plot(lns_buf5_neg, col = 'blue', add = TRUE)
Whether +/-1000 is sufficient is a further intersection test between the buffer poly(s) and the other boundary. If the desired sampling area is not rectangular, steps can be taken to construct a sampling polygon from the buffer and intersection.
#library(lwgeom)
# on poly_sf
new_line <- draw(x = 'line', col ='blue', lwd = 2, n = 10)
lns_buf5_10k_neg <- st_buffer(st_geometry(geom)[5], -10000, singleSide= TRUE)
new_line_sf <- st_as_sf(new_line, crs = st_crs(lns_buf5_10k_neg))
buf5_nline_split <- lwgeom::st_split(lns_buf5_10k_neg, new_line_sf$geometry)
irreg_smp_area <- st_collection_extract(buf5_nline_split)[1]
Though I'm happy to see it all done in terra.

R - Finding least cost path through raster image (maze)?

How can I find a non-linear path through raster image data? e.g., least cost algorithm? Starting and ending points are known and given as:
Start point = (0,0)
End point = (12,-5)
For example, extract the approximate path of a winding river through a (greyscale) raster image.
# fake up some noisy, but reproducible, "winding river" data
set.seed(123)
df <- data.frame(x=seq(0,12,by=.01),
y=sapply(seq(0,12,by=.01), FUN = function(i) 10*sin(i)+rnorm(1)))
# convert to "pixels" of raster data
# assumption: image color is greyscale, only need one numeric value, v
img <- data.frame(table(round(df$y,0), round(df$x,1)))
names(img) <- c("y","x","v")
img$y <- as.numeric(as.character(img$y))
img$x <- as.numeric(as.character(img$x))
## take a look at the fake "winding river" raster image...
library(ggplot2)
ggplot(img) +
geom_raster(aes(x=x,y=y,fill=v))
As I was writing up my example, I stumbled upon an answer using the 'gdistance' r package... hopefully others will find this useful.
library(gdistance)
library(sp)
library(ggplot2)
# convert to something rasterFromXYZ() understands
spdf <- SpatialPixelsDataFrame(points = img[c("x","y")], data = img["v"])
# use rasterFromXYZ to make a RasterLayer
r <- rasterFromXYZ(spdf)
# make a transition layer, specifying a sensible function and the number of connection directions
tl <- transition(r, function(x) min(x), 8)
## mean(x), min(x), and max(x) produced similar results for me
# extract the shortest path as something we can plot
sPath <- shortestPath(tl, c(0,0), c(12,-5), output = "SpatialLines")
# conversion for ggplot
sldf <- fortify(SpatialLinesDataFrame(sPath, data = data.frame(ID = 1)))
# plot the original raster, truth (white), and the shortest path solution (green)
ggplot(img) +
geom_raster(aes(x=x,y=y,fill=v)) +
stat_function(data=img, aes(x=x), fun = function(x) 10*sin(x), geom="line", color="white") +
geom_path(data=sldf, aes(x=long,y=lat), color="green")
I wanted to make sure that I wasn't just giving myself too easy of a problem... so I made a noisier version of the image.
img2 <- img
img2$v <- ifelse(img2$v==0, runif(sum(img2$v==0),3,8), img2$v)
spdf2 <- SpatialPixelsDataFrame(points = img2[c("x","y")], data = img2["v"])
r2 <- rasterFromXYZ(spdf2)
# for this noisier image, I needed a different transition function.
# The one from the vignette worked well enough for this example.
tl2 <- transition(r2, function(x) 1/mean(x), 8)
sPath2 <- shortestPath(tl2, c(0,0), c(12,-5), output = "SpatialLines")
sldf2 <- fortify(SpatialLinesDataFrame(sPath2, data = data.frame(ID = 1)))
ggplot(img2) +
geom_raster(aes(x=x,y=y,fill=v)) +
stat_function(data=img2, aes(x=x), fun = function(x) 10*sin(x), geom="line", color="white") +
geom_path(data=sldf2, aes(x=long,y=lat), color="green")
UPDATE: using real raster data...
I wanted to see if the same workflow would work on an actual real-world raster image and not just fake data, so...
library(jpeg)
# grab some river image...
url <- "https://c8.alamy.com/comp/AMDPJ6/fiji-big-island-winding-river-aerial-AMDPJ6.jpg"
download.file(url, "river.jpg", mode = "wb")
jpg <- readJPEG("./river.jpg")
img3 <- melt(jpg, varnames = c("y","x","rgb"))
img3$rgb <- as.character(factor(img3$rgb, levels = c(1,2,3), labels=c("r","g","b")))
img3 <- dcast(img3, x + y ~ rgb)
# convert rgb to greyscale
img3$v <- img3$r*.21 + img3$g*.72 + img3$b*.07
For rgb to greyscale, see: https://stackoverflow.com/a/27491947/2371031
# define some start/end point coordinates
pts_df <- data.frame(x = c(920, 500),
y = c(880, 50))
# set a reference "grey" value as the mean of the start and end point "v"s
ref_val <- mean(c(subset(img3, x==pts_df[1,1] & y==pts_df[1,2])$v,
subset(img3, x==pts_df[2,1] & y==pts_df[2,2])$v))
spdf3 <- SpatialPixelsDataFrame(points = img3[c("x","y")], data = img3["v"])
r3 <- rasterFromXYZ(spdf3)
# transition layer defines "conductance" between two points
# x is the two point values, "v" = c(v1, v2)
# 0 = no conductance, >>1 = good conductance, so
# make a transition function that encourages only small changes in v compared to the reference value.
tl3 <- transition(r3, function(x) (1/max(abs((x/ref_val)-1))^2)-1, 8)
sPath3 <- shortestPath(tl3, as.numeric(pts_df[1,]), as.numeric(pts_df[2,]), output = "SpatialLines")
sldf3 <- fortify(SpatialLinesDataFrame(sPath3, data = data.frame(ID = 1)))
# plot greyscale with points and path
ggplot(img3) +
geom_raster(aes(x,y, fill=v)) +
scale_fill_continuous(high="white", low="black") +
scale_y_reverse() +
geom_point(data=pts_df, aes(x,y), color="red") +
geom_path(data=sldf3, aes(x=long,y=lat), color="green")
I played around with different transition functions before finding one that worked. This one is probably more complex than it needs to be, but it works. You can increase the power term (from 2 to 3,4,5,6...) and it continues to work. It did not find a correct solution with the power term removed.
Alternative solution using igraph package.
Found an alternative set of answers using 'igraph' r package. I think it is important to note that one of the big differences here is that 'igraph' supports n-dimensional graphs whereas 'gdistance' only supports 2D graphs. So, for example, extending this answer into 3D is relatively easy.
library(igraph)
# make a 2D lattice graph, with same dimensions as "img"
l <- make_lattice(dimvector = c(length(unique(img$y)),
length(unique(img$x))), directed=F, circular=F)
summary(l)
# > IGRAPH ba0963d U--- 3267 6386 -- Lattice graph
# > + attr: name (g/c), dimvector (g/n), nei (g/n), mutual (g/l), circular (g/l)
# set vertex attributes
V(l)$x = img$x
V(l)$y = img$y
V(l)$v = img$v
# "color" is a known attribute that will be used by plot.igraph()
V(l)$color = grey.colors(length(unique(img$v)))[img$v+1]
# compute edge weights as a function of attributes of the two connected vertices
el <- get.edgelist(l)
# "weight" is a known edge attribute, and is used in shortest_path()
# I was confused about weights... lower weights are better, Inf weights will be avoided.
# also note from help: "if all weights are positive, then Dijkstra's algorithm is used."
E(l)$weight <- 1/(pmax(V(l)[el[, 1]]$v, V(l)[el[, 2]]$v))
E(l)$color = grey.colors(length(unique(E(l)$weight)))[E(l)$weight+1]
Edge weights calculation courtesy of: https://stackoverflow.com/a/27446127/2371031 (thanks!)
# find the start/end vertices
start = V(l)[V(l)$x == 0 & V(l)$y == 0]
end = V(l)[V(l)$x == 12 & V(l)$y == -5]
# get the shortest path, returning "both" (vertices and edges)...
result <- shortest_paths(graph = l, from = start, to = end, output = "both")
# color the edges that were part of the shortest path green
V(l)$color = ifelse(V(l) %in% result$vpath[[1]], "green", V(l)$color)
E(l)$color = ifelse(E(l) %in% result$epath[[1]], "green", E(l)$color)
# color the start and end vertices red
V(l)$color = ifelse(V(l) %in% c(start,end), "red", V(l)$color)
plot(l, vertex.shape = "square", vertex.size=2, vertex.frame.color=NA, vertex.label=NA, curved=F)
Second (noisier) example requires a different formula to compute edge weights.
img2 <- img
img2$v <- ifelse(img2$v==0, runif(sum(img2$v==0),3,8), img2$v)
l <- make_lattice(dimvector = c(length(unique(img2$y)),
length(unique(img2$x))), directed=F, circular=F)
# set vertex attributes
V(l)$x = img2$x
V(l)$y = img2$y
V(l)$v = img2$v
V(l)$color = grey.colors(length(unique(img2$v)))[factor(img2$v)]
# compute edge weights
el <- get.edgelist(l)
# proper edge weight calculation is the key to a good solution...
E(l)$weight <- (pmin(V(l)[el[, 1]]$v, V(l)[el[, 2]]$v))
E(l)$color = grey.colors(length(unique(E(l)$weight)))[factor(E(l)$weight)]
start = V(l)[V(l)$x == 0 & V(l)$y == 0]
end = V(l)[V(l)$x == 12 & V(l)$y == -5]
# get the shortest path, returning "both" (vertices and edges)...
result <- shortest_paths(graph = l, from = start, to = end, output = "both")
# color the edges that were part of the shortest path green
V(l)$color = ifelse(V(l) %in% result$vpath[[1]], "green", V(l)$color)
E(l)$color = ifelse(E(l) %in% result$epath[[1]], "green", E(l)$color)
# color the start and end vertices red
V(l)$color = ifelse(V(l) %in% c(start,end), "red", V(l)$color)
plot(l, vertex.shape = "square", vertex.size=2, vertex.frame.color=NA, vertex.label=NA, curved=F)

R plotly - Different colorscales for two scatter on the same plots

I'm trying to plot two different set of data on the same figure, using two àdd_trace commands. I specify a different colorscales for each, but the second one is ignored , so my second scatter plot has the same color gradient than the first one. How can I fix this ?
I tried the solution here, but it doesn't work (I got a warning saying 'scatter' objects don't have these attributes: 'colorscale').
My code (with a dataframe with random numbers for testing) :
library(plotly)
library(FactoMineR)
n <- 10 ; m <- 20 ; reps <- 6
a <- as.data.frame(cbind(matrix(seq_len(m), n, m/n),
replicate(reps, sample(c(0, 1), n, replace = TRUE))))
res.pca = PCA(a, scale.unit=TRUE, graph=F, axes=c(1,2))
ind <- as.data.frame(res.pca$ind$coord)
cos2 <- as.data.frame(res.pca$ind$cos2)
var <- as.data.frame(res.pca$var$coord)
cos2_v <- as.data.frame(res.pca$var$cos2)
biplot <- plot_ly(ind) %>%
add_trace(x=ind[,1],
y=ind[,2],
type='scatter',
text=rownames(a),
textposition='top',
mode="markers+text",
color=cos2[,1],
colors="OrRd",
marker=list(symbol=27, size=11)) %>%
add_trace(var,
x=var[,1],
y=var[,2],
type = 'scatter',
text=colnames(a),
textposition='top',
mode="markers+text",
color=cos2_v[,1],
colors="BuGn",
marker=list(symbol=4, size=11))
Thanks in advance (the actual result is on the picture below).
This works. If you do something custom you have to format the arguments according to plotly::schema(). The colors argument in plotly is a helper to streamline the more complicated plotly.js syntax. Note how color and all the other marker arguments have to be within a list called marker, under which you have to manually format both the colorscale (to get the colors you want) and the colorbar (to get the colorscale positioned correctly). Note also that the legend is for the shape, while the colorscale is for the color (confusingly the colorscale is not the legend).
library(plotly)
library(FactoMineR)
n <- 10 ; m <- 20 ; reps <- 6
a <- as.data.frame(cbind(matrix(seq_len(m), n, m/n),
replicate(reps, sample(c(0, 1), n, replace = TRUE))))
res.pca = PCA(a, scale.unit=TRUE, graph=F, axes=c(1,2))
ind <- as.data.frame(res.pca$ind$coord)
cos2 <- as.data.frame(res.pca$ind$cos2)
var <- as.data.frame(res.pca$var$coord)
cos2_v <- as.data.frame(res.pca$var$cos2)
biplot <- plot_ly(ind,showlegend=F) %>%
add_trace(x=ind[,1],
y=ind[,2],
type='scatter',
text=rownames(a),
textposition='top',
mode="markers+text",
marker=list(symbol=27, size=11
,color=cos2[,1]
,colorscale=list(
list(0,RColorBrewer::brewer.pal(3,'OrRd')[1])
,list(1,RColorBrewer::brewer.pal(3,'OrRd')[3])
)
,colorbar=list(yanchor='bottom',len=.5)
)) %>%
add_trace(x=var[,1],
y=var[,2],
type='scatter',
text=colnames(a),
textposition='top',
mode="markers+text",
marker=list(symbol=4, size=11
,color=cos2_v[,1]
,colorscale=list(
list(0,RColorBrewer::brewer.pal(3,'BuGn')[1])
,list(1,RColorBrewer::brewer.pal(3,'BuGn')[3])
)
,colorbar=list(yanchor='top',len=.5)
))
biplot
result with two colorscales

sample points on polygon edge in R

If I have a spatialpolygons object in R, how can I generate a set of n points that are on the edge of that polygon?
I originally thought I could just sample from the polygon vertices, but it looks like there are sometimes stretches where there are no vertices because the polygon edge is a straight line...
A simple solution would be to use st_segmentize() from package sf that adds points to straight lines, then sample along these finer points.
st_segmentize() has an argument dfMaxLength that defines the max distance to allow along a line. The smaller you will set, the more points you will have. It should at least be as small as the minimum distance between any two points.
library(sf)
library(tidyverse)
## original form
poly <- st_polygon(x=list(cbind(x=c(1,2,3,1),y=c(1,2,1,1))))
# segmentize, then convert to points
poly_points <- st_segmentize(poly, dfMaxLength = 0.1) %>%
st_coordinates() %>%
as.data.frame() %>%
select(X, Y) %>%
st_as_sf(coords = c("X", "Y"))
## plot: you can just use sample() now on your point dataset
plot(poly, reset = FALSE, main = "segmentize (black point), then sample 5 (red points)")
plot(poly_points, reset = FALSE, add = TRUE)
plot(poly_points[sample(1:nrow(poly_points), size = 5),], add = TRUE, col = 2, pch = 19)
To get the minimum distance between any two points (beware of zero):
poly %>%
st_coordinates() %>%
as.data.frame() %>%
st_as_sf(coords = c("X", "Y")) %>%
st_distance() %>% c() %>%
unique() %>%
sort
Assuming you want to draw points around the perimeter, I would split this into two parts:
P(Point p on Edge e) = P(point p | Edge e) P(Edge e)
with P(Edge e) proportional to its length. So first sample an edge, then sample a point
on it.
Here's an example triangle:
poly <- Polygon(list(x=c(1,2,3,1),y=c(1,2,1,1)))
We'll calculate the lengths of the sides:
require(gsl) #for fast hypot function
xy <- poly#coords
dxy <- diff(xy)
h <- hypot(dxy[,"x"], dxy[,"y"])
and draw a random side:
e <- sample(nrow(dxy), 1, probs=h)
and then draw a point on that edge:
u <- runif(1)
p <- xy[e,] + u * dxy[e,]
Wrapping the whole thing in a function, we have:
rPointOnPerimeter <- function(n, poly) {
xy <- poly#coords
dxy <- diff(xy)
h <- hypot(dxy[,"x"], dxy[,"y"])
e <- sample(nrow(dxy), n,replace=TRUE, prob=h)
u <- runif(n)
p <- xy[e,] + u * dxy[e,]
p
}
with a demo:
plot( rPointOnPerimeter(100,poly) )

world map - map halves of countries to different colors

I am using the example here for discussion:
ggplot map with l
library(rgdal)
library(ggplot2)
library(maptools)
# Data from http://thematicmapping.org/downloads/world_borders.php.
# Direct link: http://thematicmapping.org/downloads/TM_WORLD_BORDERS_SIMPL-0.3.zip
# Unpack and put the files in a dir 'data'
gpclibPermit()
world.map <- readOGR(dsn="data", layer="TM_WORLD_BORDERS_SIMPL-0.3")
world.ggmap <- fortify(world.map, region = "NAME")
n <- length(unique(world.ggmap$id))
df <- data.frame(id = unique(world.ggmap$id),
growth = 4*runif(n),
category = factor(sample(1:5, n, replace=T)))
## noise
df[c(sample(1:100,40)),c("growth", "category")] <- NA
ggplot(df, aes(map_id = id)) +
geom_map(aes(fill = growth, color = category), map =world.ggmap) +
expand_limits(x = world.ggmap$long, y = world.ggmap$lat) +
scale_fill_gradient(low = "red", high = "blue", guide = "colorbar")
Gives the following results:
I would like to map one variable to the left "half" of a country and a different variable to the right "half" of the country. I put "half" in quotes because it's not clearly defined (or at least I'm not clearly defining it). The answer by Ian Fellows might help (which gives an easy way to get the centroid). I'm hoping for something so that I can do aes(left_half_color = growth, right_half_color = category) in the example. I'm also interested in top half and bottom half if that is different.
If possible, I would also like to map the individual centroids of the halves to something.
This is a solution without ggplot that relies on the plot function instead. It also requires the rgeos package in addition to the code in the OP:
EDIT Now with 10% less visual pain
EDIT 2 Now with centroids for east and west halves
library(rgeos)
library(RColorBrewer)
# Get centroids of countries
theCents <- coordinates(world.map)
# extract the polygons objects
pl <- slot(world.map, "polygons")
# Create square polygons that cover the east (left) half of each country's bbox
lpolys <- lapply(seq_along(pl), function(x) {
lbox <- bbox(pl[[x]])
lbox[1, 2] <- theCents[x, 1]
Polygon(expand.grid(lbox[1,], lbox[2,])[c(1,3,4,2,1),])
})
# Slightly different data handling
wmRN <- row.names(world.map)
n <- nrow(world.map#data)
world.map#data[, c("growth", "category")] <- list(growth = 4*runif(n),
category = factor(sample(1:5, n, replace=TRUE)))
# Determine the intersection of each country with the respective "left polygon"
lPolys <- lapply(seq_along(lpolys), function(x) {
curLPol <- SpatialPolygons(list(Polygons(lpolys[x], wmRN[x])),
proj4string=CRS(proj4string(world.map)))
curPl <- SpatialPolygons(pl[x], proj4string=CRS(proj4string(world.map)))
theInt <- gIntersection(curLPol, curPl, id = wmRN[x])
theInt
})
# Create a SpatialPolygonDataFrame of the intersections
lSPDF <- SpatialPolygonsDataFrame(SpatialPolygons(unlist(lapply(lPolys,
slot, "polygons")), proj4string = CRS(proj4string(world.map))),
world.map#data)
##########
## EDIT ##
##########
# Create a slightly less harsh color set
s_growth <- scale(world.map#data$growth,
center = min(world.map#data$growth), scale = max(world.map#data$growth))
growthRGB <- colorRamp(c("red", "blue"))(s_growth)
growthCols <- apply(growthRGB, 1, function(x) rgb(x[1], x[2], x[3],
maxColorValue = 255))
catCols <- brewer.pal(nlevels(lSPDF#data$category), "Pastel2")
# and plot
plot(world.map, col = growthCols, bg = "grey90")
plot(lSPDF, col = catCols[lSPDF#data$category], add = TRUE)
Perhaps someone can come up with a good solution using ggplot2. However, based on this answer to a question about multiple fill scales for a single graph ("You can't"), a ggplot2 solution seems unlikely without faceting (which might be a good approach, as suggested in the comments above).
EDIT re: mapping centroids of the halves to something: The centroids for the east ("left") halves can be obtained by
coordinates(lSPDF)
Those for the west ("right") halves can be obtained by creating an rSPDF object in a similar way:
# Create square polygons that cover west (right) half of each country's bbox
rpolys <- lapply(seq_along(pl), function(x) {
rbox <- bbox(pl[[x]])
rbox[1, 1] <- theCents[x, 1]
Polygon(expand.grid(rbox[1,], rbox[2,])[c(1,3,4,2,1),])
})
# Determine the intersection of each country with the respective "right polygon"
rPolys <- lapply(seq_along(rpolys), function(x) {
curRPol <- SpatialPolygons(list(Polygons(rpolys[x], wmRN[x])),
proj4string=CRS(proj4string(world.map)))
curPl <- SpatialPolygons(pl[x], proj4string=CRS(proj4string(world.map)))
theInt <- gIntersection(curRPol, curPl, id = wmRN[x])
theInt
})
# Create a SpatialPolygonDataFrame of the western (right) intersections
rSPDF <- SpatialPolygonsDataFrame(SpatialPolygons(unlist(lapply(rPolys,
slot, "polygons")), proj4string = CRS(proj4string(world.map))),
world.map#data)
Then information could be plotted on the map according to the centroids of lSPDF or rSPDF:
points(coordinates(rSPDF), col = factor(rSPDF#data$REGION))
# or
text(coordinates(lSPDF), labels = lSPDF#data$FIPS, cex = .7)

Resources