I want to create an empty (white) canvas at a certain size (1920×1080). On that canvas, I want to place a filled circle at specific coordinates (e.g., 480, 123).
Even going through the vignette (https://docs.ropensci.org/magick/articles/intro.html), I am not able to create such an image.
The answer needs to work in R.
This one works for me
library(magick)
img <- image_blank(width = 1920, height = 1080, color = "none")
drawing <- image_draw(img)
symbols(480, 123, circles = 5, bg = 'black', inches = FALSE, add = TRUE)
dev.off()
# displaying
# print(drawing)
# saving
image_write(drawing, "img.png")
Related
I want to read an image of 1500x1060px, add margins (aprox 3 cm at right) to it and in this margins plot a text. (It is in a shinny app that let the user change transparency).
If I do the text first and the margins after:
image1 = jpeg::readJPEG(file.path(paste(input$map1,'.jpg', sep='')))
image1 = abind::abind(image1, image1[,,1]) # add an alpha channel
image1[,,4] = input$trans1/100 # set alpha to semi-transparent
image1<- image_read(image1)
image1 <- image_annotate(image1, "Generated with my.webpage", location = "+1550+1000", degrees = 270, size = 40, color = "black", strokecolor = NULL, boxcolor = NULL)
png(outfile, width = 1500, height = 1060, units = 'px', res = 300)
par(mar=c(0,0,0,3))
plot.new()
rasterImage(image1, 0, 0, 1, 1)
dev.off()
The text is annotated in a zone that does not exist yet so it doese not appear.
So im trying to use the image_annotate after the par(mar=()) but I don't now how to do it, as image_annotate has to be used with an object as first variable.
As you presumably do not want to change the image to include rasterized text (poor quality), I would suggest to use the R buitlin function mtext to print the text on the margin, e.g.
img <- jpeg::readJPEG("image.jpeg")
plot.new()
rasterImage(img, rasterImage(img,0,0,1,1)
mtext("comment on image", side=1)
When opening a different device (pdf, png, or what else) before calling plot.new(), you can also save the annotated image to a file as shown in your example (do not forget to close the device with dev.off(), so that it is actually written).
I found that grid.table could be used to plot a dataframe to a pdf file, as described here. I want to save a dataframe to a landscape A4 format, however it seems to not scale the data such that is nicely fits within the borders of the pdf.
Code
library(gridExtra)
set.seed(1)
strings <- c('Wow this is nice', 'I need some coffee', 'Insert something here', 'No ideas left')
table <- as.data.frame(matrix(ncol = 9, nrow = 30, data = sample(strings,30, replace = TRUE)))
pdf("test.pdf", paper = 'a4r')
grid.table(table)
dev.off()
Output
Not the whole table is shown in the pdf:
Question
How can I make sure that the dataframe is scaled to fit within the landscape A4? I don't explicitly need gridExtra or the default pdf save, I can use any other package if these are easier to fix this.
EDIT
I came across this other question, appareantly one can figure out the required height and width of the tableGrob
tg = gridExtra::tableGrob(table)
h = grid::convertHeight(sum(tg$heights), "mm", TRUE)
w = grid::convertWidth(sum(tg$widths), "mm", TRUE)
ggplot2::ggsave("test.pdf", tg, width=w, height=h, units = 'mm')
Here h = 172.2 and w = 444.3 which exceed the size of a standard A4, namely 210 x 279. So I know this causes the problem however still can't figure out to scale down the table to fit it on an A4.
I figured out I could add the scale parameter to ggsave. I wrote a simple function to get the optimal scale:
optimal.scale <- function(w,h, wanted.w, wanted.h) max(c(w/wanted.w, h/wanted.h))
I added 0.1 to the scale to add a margin to the plot such that the text is not directly on the edge of the paper. Then I passed the resulting scale to ggsave
tg = gridExtra::tableGrob(table
h = grid::convertHeight(sum(tg$heights), "mm", TRUE)
w = grid::convertWidth(sum(tg$widths), "mm", TRUE)
scale = optimal.scale(w,h, 279, 210) + 0.1 #A4 = 279 x 210 in landscape
ggplot2::ggsave("test.pdf", tg, width = 279, height = 210, units = 'mm' , scale = scale)
Now my table fits on the A4:
I am creating a map that consists out of 3 map panels. I have set the inner margins to zero in the tm_layout options for all maps, however depending on the dimensions of the output I still have empty white space inside the map frames.
I can reduce it to a minimum if I adjust the setting for width and height with tm_save but I want a clean solution.
I tried to creat a minimum example with the pre-installed tmap package data (though it is here less obvious that there is empty white space):
rm(list=ls(all=TRUE))
library(tmap)
data("NLD_prov")
map1 <- tm_shape(NLD_prov)+tm_fill()+tm_layout(inner.margins = c(0,0,0,0))
map2 <- tm_shape(NLD_prov)+tm_fill()+tm_layout(inner.margins = c(0,0,0,0))
final_map <-tmap_arrange(map1, map2)
final_map
In the original code I have cropped the input shape file to my desired map extent and also used a bounding box but that does not help:
map_extent<-st_bbox(c(xmin = 8, xmax = 9.1,ymin = 53.8, ymax = 55.1),
crs = st_crs(data))
data_cropped<-crop_shape(data, map_extent, polygon = TRUE)
tm_shape(data_cropped, bbox = map_extent)+
...
How can I force the margins to zero?
I have a grid and I want to produce a map out of this grid with some map elements (scale, north arrow, etc). I have no problem drawing the grid and the coloring I need, but the additional map elements won't show on the map. I tried putting first=TRUE to the sp.layout argument according to the sp manual, but still no success.
I reproduced the issue with the integrated meuse dataset, so you may just copy&paste that code. I use those package versions: lattice_0.20-33 and sp_1.2-0
library(sp)
library(lattice) # required for trellis.par.set():
trellis.par.set(sp.theme()) # sets color ramp to bpy.colors()
alphaChannelSupported = function() {
!is.na(match(names(dev.cur()), c("pdf")))
}
data(meuse)
coordinates(meuse)=~x+y
data(meuse.riv)
library(gstat, pos = match(paste("package", "sp", sep=":"), search()) + 1)
data(meuse.grid)
coordinates(meuse.grid) = ~x+y
gridded(meuse.grid) = TRUE
v.uk = variogram(log(zinc)~sqrt(dist), meuse)
uk.model = fit.variogram(v.uk, vgm(1, "Exp", 300, 1))
meuse[["ff"]] = factor(meuse[["ffreq"]])
meuse.grid[["ff"]] = factor(meuse.grid[["ffreq"]])
zn.uk = krige(log(zinc)~sqrt(dist), meuse, meuse.grid, model = uk.model)
zn.uk[["se"]] = sqrt(zn.uk[["var1.var"]])
meuse.sr = SpatialPolygons(list(Polygons(list(Polygon(meuse.riv)),"meuse.riv")))
rv = list("sp.polygons", meuse.sr, fill = "lightblue")
sampling = list("sp.points", meuse.riv, color = "black")
scale = list("SpatialPolygonsRescale", layout.scale.bar(),
offset = c(180500,329800), scale = 500, fill=c("transparent","black"), which = 4)
text1 = list("sp.text", c(180500,329900), "0", cex = .5, which = 4)
text2 = list("sp.text", c(181000,329900), "500 m", cex = .5, which = 4)
arrow = list("SpatialPolygonsRescale", layout.north.arrow(),
offset = c(181300,329800),
scale = 400, which = 4)
library(RColorBrewer)
library(lattice)
trellis.par.set(sp.theme())
precip.pal <- colorRampPalette(brewer.pal(7, name="Blues"))
spplot(zn.uk, "var1.pred",
sp.layout = list(rv, sampling, scale, text1, text2),
main = "log(zinc); universal kriging standard errors",
col.regions=precip.pal,
contour=TRUE,
col='black',
pretty=TRUE,
scales=list(draw = TRUE),
labels=TRUE)
And that's how it looks...all naked:
So my questions:
Where is the scale bar, north arrow, etc hiding? Did I miss something? Every example I could find on the internet looks similar to that. On my own dataset I can see the scale bar and north arrow being drawn at first, but as soon as the grid is rendered, it superimposes the additional map elements (except for the scale text, that is shown on the map - not the bar and north arrow for some reason I don't seem to comprehend).
The error message appearing on the map just shows when I try to add the sampling locations sampling = list("sp.points", meuse.riv, color = "black"). Without this entry, the map shows without error, but also without additional map elements. How can I show the sampling points on the map (e.g. in circles whose size depends on the absolute value of this sampling point)?
This bothered me for many, many hours by now and I can't find any solution to this. In Bivand et al's textbook (2013) "Applied Spatial Data Analysis with R" I could read the following entry:
The order of items in the sp.layout argument matters; in principle objects
are drawn in the order they appear. By default, when the object of spplot has
points or lines, sp.layout items are drawn before the points to allow grids
and polygons drawn as a background. For grids and polygons, sp.layout
items are drawn afterwards (so the item will not be overdrawn by the grid
and/or polygon). For grids, adding a list element first = TRUE ensures that
the item is drawn before the grid is drawn (e.g. when filled polygons are added). Transparency may help when combining layers; it is available for the
PDF device and several other devices.
Function sp.theme returns a lattice theme that can be useful for plots
made by spplot; use trellis.par.set(sp.theme()) after a device is opened
or changed to make this effective.
However, also with this additional information I wasn't able to solve this problem. Glad for any hint!
The elements you miss are being drawn in panel four, which does not exist, so are not being drawn. Try removing the which = 4.
meuse.riv in your example is a matrix, which causes the error message, but should be a SpatialPoints object, so create sampling by:
sampling = list("sp.points", SpatialPoints(meuse.riv), color = "black")
When working from examples, my advice is to choose examples as close as possible to what you need, and only change one thing at a time.
I noticed some weird behavior when resizing the plot window. Consider
library(sp)
library(rgeos)
library(raster)
rst.test <- raster(nrows=300, ncols=300, xmn=-150, xmx=150, ymn=-150, ymx=150, crs="NA")
sap.krog300 <- SpatialPoints(coordinates(matrix(c(0,0), ncol = 2)))
sap.krog300 <- gBuffer(spgeom = sap.krog300, width = 100, quadsegs = 20)
shrunk <- gBuffer(spgeom = sap.krog300, width = -30)
shrunk <- rasterize(x = shrunk, y = rst.test)
shrunk.coords <- xyFromCell(object = rst.test, cell = which(shrunk[] == 1))
plot(shrunk)
points(shrunk.coords, pch = "+")
If you resize the window, plotted points get different extent compared to the underlying raster. If you resize the window and plot shrunk and shrunk.coords again, the plot turns out fine. Can anyone explain this?
If you plot directly with the RasterLayer method for plot the resize problem does not occur.
## gives an error, but still plots
raster:::.imageplot(shrunk)
points(shrunk.coords, pch = ".")
So it must be something in the original plot call before the .imageplot method is called.
showMethods("plot", classes = "RasterLayer", includeDefs = TRUE)
It does occur if we call raster:::.plotraster directly, and this is the function that calls raster:::.imageplot:
raster:::.plotraster(shrunk, col = rev(terrain.colors(255)), maxpixels = 5e+05)
points(shrunk.coords, pch = ".")
It is actually in the axis labels, not the image itself. See with this, this plots faithfully on resize:
raster:::.imageplot(shrunk)
abline(h = c(-80, 80), v = c(-80, 80))
But do it like this, and the lines are no longer at [-80, 80] after resize:
plot(shrunk)
abline(h = c(-80, 80), v = c(-80, 80))
So it is actually the points plotted after the raster that are showing incorrectly: the plot method keeps the aspect ratio fixed, so widening the plot doesn't "stretch" out the raster circle to an ellipse. But, it does something to the points that are added afterwards so the calls to par() must not be handled correctly (probably in raster:::.imageplot).
Another way of seeing the problem is to show that axis() does not know the space being used by the plot, which is the same problem you see when overplotting:
plot(shrunk)
axis(1, pos = 1)
When you resize the x-axis length the two axes are no longer synchronized.
Because you have a raster, try replacing plot() with image(). I had the same problem but this solved it for me.