The following is a grid solution, but very much a work-around. It shows how to apply a non-rectangular clipping region to a ggplot, so that one set of points in your plot is clipped. You weren't too far wrong in your attempt. A couple of points to note:
- You need to
grid.force()
the ggplotGrob object so the grid
can see the grobs.
- Do not define the ggplot grob as a clipping path - the clipping path is the polygon.
- The clipping path is applied to the points grob within the plot panel of the ggplot. This means that other objects in the plot panel, the panel background and grid lines, do not get clipped. Only the data points are clipped.
I've added a blue line to the plot to show that the line too does not need to be clipped; but can be clipped if desired.
There are also commented lines of code that, when uncommented, will draw the clipping region, and move the grid lines and points to the front (that is, in front of the darker grey clipping region).
library(ggplot2)
library(gridSVG)
library(grid)
# Open the graphics device
gridsvg(name = "test.svg")
# Create a plot
p <- ggplot(diamonds[1:300, ], aes(carat, price)) +
geom_point(aes(colour = cut)) +
geom_line(data = data.frame(x = c(.3, .9), y = c(500, 2500)), aes(x,y), col = "skyblue", size = 2)
g <- ggplotGrob(p) # Store the plot as a grob
g = grid.force(g) # So that grid sees all grobs
grid.draw(g) # Draw the plot
# Define the clipping path
pg <- polygonGrob(c(.7, 0, 0, 1, 1),
c(0, .7, 1, 1, 0))
# The clipping path can be nearly any shape you desire.
# Try this for a circular region
# pg = circleGrob(x = .5, y = .6, r = .5)
cp <- clipPath(pg)
# Add the clipping path to the points grob.
# That is, only the points inside the polygon will be visible,
# but the background and grid lines will not be clipped.
# Nor will the blue line be clipped.
# grid.ls(g) # names of the grobs
seekViewport(grep("panel.[0-9]", grid.ls(g)$name, value = TRUE))
grid.clipPath("points", cp, grep = TRUE)
# To clip the blue line, uncomment the next line
# grid.clipPath("GRID.polyline", cp, grep = TRUE)
# To show the clipping region,
# uncomment the next two lines.
# showcp = editGrob(pg, gp = gpar(fill = rgb(0, 0, 0, 0.05), col = "transparent"))
# grid.draw(showcp)
# And to move the grid lines, remaining data points, and blue line in front of the clipping region,
# uncomment the next five lines
# panel = grid.get("panel", grep = TRUE) # Get the panel, and remove the background grob
# panel = removeGrob(panel, "background", grep = TRUE)
# grid.remove("points", grep = TRUE) # Remove points and grid lines from the rendered plot
# grid.remove("line", grep = TRUE, global = TRUE)
# grid.draw(panel) # Draw the edited panel - on top of the clipping region
# Turn off the graphics device
dev.off()
# Find text.svg in your working directory
Edit Defining the clipping region using the coordinate system in which the data points were drawn.
library(ggplot2)
library(gridSVG)
library(grid)
# Open the graphics device
gridsvg(name = "test.svg")
# Create a plot
p <- ggplot(diamonds[1:300, ], aes(carat, price)) +
geom_point(aes(colour = cut)) +
geom_line(data = data.frame(x = c(.3, .9), y = c(500, 2500)), aes(x,y), col = "skyblue", size = 2)
g <- ggplotGrob(p) # Store the plot as a grob
g = grid.force(g) # So that grid sees all grobs
grid.draw(g) # Draw the plot
# Get axis limits (including any expansion)
axis.limits = summarise_layout(ggplot_build(p))[1, c('xmin', 'xmax', 'ymin', 'ymax')]
# Find the 'panel' viewport,
# then push to a new viewport,
# one that exactly overlaps the 'panel' viewport,
# but with limits on the x and y scales that are the same
# as the limits for the original ggplot.
seekViewport(grep("panel.[0-9]", grid.ls(g)$name, value = TRUE))
pushViewport(dataViewport(xscale = axis.limits[1, 1:2],
yscale = axis.limits[1, 3:4]))
# Define the clipping path
pg <- polygonGrob(x = c(.6, 0.3, .3, .8, 1.2),
y = c(500, 1500, 2900, 2900, 1500),
default.units="native")
cp <- clipPath(pg)
# Add the clipping path to the points grob.
# That is, only the points inside the polygon will be visible,
# but the background and grid lines will not be clipped.
# Nor will the blue line be clipped.
# grid.ls(g) # names of the grobs
grid.clipPath("points", cp, grep = TRUE)
# To clip the blue line, uncomment the next line
grid.clipPath("GRID.polyline", cp, grep = TRUE)
# To show the clipping region.
showcp = editGrob(pg, gp = gpar(fill = rgb(0, 0, 0, 0.05), col = "transparent"))
grid.draw(showcp)
# And to move the grid lines and remaining data points in front of the clipping region.
panel = grid.get("panel", grep = TRUE) # Get the panel, and remove the background grob
panel = removeGrob(panel, "background", grep = TRUE)
grid.remove("points", grep = TRUE) # Remove points and grid lines from the rendered plot
grid.remove("line", grep = TRUE, global = TRUE)
grid.draw(panel) # Draw the edited panel - on top of the clipping region
# Turn off the graphics device
dev.off()
# Find text.svg in your working directory