R and graphics devices on Mac and Windows

Today, I tried to plot some spatial data in R and observed some weird behavior. Digging deeper revealed some interesting differences between plotting in R with different operating systems. My work computer is a Mac and my private computer runs Windows, but I still use it primarily for work (for private life – and, well, work too – I have a tablet). Long story short, running the exact same R code on both computers, produces very different image files, because the operating systems use different graphics devices. This post summarizes the differences I found.

The problem

My first approach was running my code on the Mac and saving it via RStudio. Since that took a lot of time (and looked a little weird), I gave it a shot and tried running the same code on my Windows machine with the exact same settings saving it in RStudio as a png. Here are zoomed in versions of the two initial images side-by-side (zoom was done by hand and is not exact).

temp

The left one comes from the Mac, has a size of 1.7MB, and a weird coloring that shouldn’t look like this. The Windows version on the right only has a size of 26KB and seems fine. At least when it comes to color. A careful comparison with the screenshot below of the shapefile in ArcGIS reveals that the border outline isn’t perfect either.

The “literature review

I found other people complaining about slow plotting of spatial objects on a Mac, and this person observed a similar weird behavior (but not as extreme) for plotting on Mac vs. Windows in general (but no real solutions there). Most helpful in understanding the role of the graphics device for plotting issues in R on Mac was this site, which provides examples for the same plot with different graphics devices on a Mac. I will do something similar here, just for Mac and Windows output.

The data

The data is an Austrian regional statistical grid shapefile that consists of 1,353,322 individual polygons, each representing a 250m x 250m cell covering all of Austria. I guess the main issue is: it has a lot of individual polygons. Here is a screenshot of the Shapefile in ArcGIS, zoomed into the same general area as in the images before.

Sample_GIS

The code I started with

I wanted to simply plot the geometry, but fill the polygons in blue and make the border transparent. This was my original code that I ran on both machines. On Windows it produced the expected output, on the Mac it didn’t.

library(sf)

#download and unzip data
download.file("http://data.statistik.gv.at/data/OGDEXT_RASTER_1_STATISTIK_AUSTRIA_L000250_LAEA.zip", "OGDEXT_RASTER_1_STATISTIK_AUSTRIA_L000250_LAEA.zip")
unzip(file.path(".", "OGDEXT_RASTER_1_STATISTIK_AUSTRIA_L000250_LAEA.zip"), exdir=getwd())

#import data
AT_poly <- st_read("STATISTIK_AUSTRIA_L000250_LAEA.shp")

#plot data
plot(st_geometry(AT_poly), border="transparent", col="blue")

The png() options

To change the graphics device for a png file, we can use png() and specify the type and antialias options. Each will produce a slightly different output. On a Mac you get the options type = c("cairo", "cairo-png", "Xlib", "quartz") and here are some more details from the png() help.

type: character string, one of "Xlib" or "quartz" (some macOS builds) or "cairo". The latter will only be available if the system was compiled with support for cairo – otherwise "Xlib" will be used. The default is set by getOption("bitmapType") – the ‘out of the box’ default is "quartz" or "cairo" where available, otherwise "Xlib".

antialias: for type = "cairo", giving the type of anti-aliasing (if any) to be used for fonts and lines (but not fills). See X11. The default is set by X11.options. Also for type = "quartz", where antialiasing is used unless antialias = "none".

On Windows you get the options type = c("windows", "cairo", "cairo-png") and the help offers this additional information.

type: Should be plotting be done using Windows GDI or cairographics?

antialias: Length-one character vector.
For allowed values and their effect on fonts with type = "windows" see windows: for that type if the argument is missing the default is taken from windows.options()$bitmap.aa.win.
For allowed values and their effect (on fonts and lines, but not fills) with type = "cairo" see svg.

The output for choosing different options

Default settings (“windows” vs. “quartz”)
 png(filename="plot1.png", width 3000, height = 2700)
plot(st_geometry(AT_poly), border='transparent', col='blue')
dev.off() 

On Windows this is the same as choosing type = "windows".

 png(filename="plot1.png", width 3000, height = 2700, type = "windows")
plot(st_geometry(AT_poly), border='transparent', col='blue')
dev.off() 

On (my) Mac this is the same as choosing type = "quartz".

 png(filename="plot1.png", width 3000, height = 2700, type = "quartz")
plot(st_geometry(AT_poly), border='transparent', col='blue')
dev.off() 
default_png_1
Default settings for Mac (left – 1.4MB) and Windows (right – 31KB). Zoom done by hand.

Now the default settings with antialias = "none".

 png(filename="plot2.png", width 3000, height = 2700, antialias = "none")
plot(st_geometry(AT_poly), border='transparent', col='blue')
dev.off() 

Again, on Windows this is the same as choosing type = "windows".

 png(filename="plot2.png", width 3000, height = 2700, type = "windows", antialias = "none")
plot(st_geometry(AT_poly), border='transparent', col='blue')
dev.off() 

And also again, on (my) Mac this is the same as choosing type = "quartz".

 png(filename="plot2.png", width 3000, height = 2700, type = "quartz", antialias = "none")
plot(st_geometry(AT_poly), border='transparent', col='blue')
dev.off() 
default_antialias_png_2
Default settings with antialias = “none” for Mac (left – 171KB) and Windows (right – 31KB). Zoom done by hand.
Type “cairo”
png(filename="plot3", width 3000, height = 2700, type = cairo)
plot(st_geometry(AT_poly), border='transparent', col='blue')
dev.off() 
cairo_png_3
type = “cairo” for Mac (left – 31KB) and Windows (right – 172KB). Zoom done by hand.
png(filename="plot4", width 3000, height = 2700, type = "cairo", antialias = "none")
plot(st_geometry(AT_poly), border='transparent', col='blue')
dev.off() 
cairo_antialias_png_4
type = “cairo” and antialias = “none” for Mac (left – 31KB) and Windows (right – 31KB). Zoom done by hand.
Type “cairo-png”
png(filename="plot5", width 3000, height = 2700, type = "cairo-png")
plot(st_geometry(AT_poly), border='transparent', col='blue')
dev.off() 
cairopng_png_5
type = “cairo-png” for Mac (left – 43KB) and Windows (right – 43KB). Zoom done by hand.
png(filename="plot6", width 3000, height = 2700, type = "cairo-png", antialias = "none")
plot(st_geometry(AT_poly), border='transparent', col='blue')
dev.off() 
cairopng_antialias_png_6
type = “cairo-png” and antialias = “none” for Mac (left – 43KB) and Windows (right – 43KB). Zoom done by hand.

Conclusion

png() options Mac Windows
type antialias Quality Size Quality Size
default default Worst 1.4MB Ok 31KB
default “none” Ok 171KB Ok 31KB
“cairo” default Good 31KB Good 172KB
“cairo” “none” Good 31KB Good 31KB
“cairo-png” default Good 43KB Good 43KB
“cairo-png” “none” Good 43KB Good 43KB

Using type = "Xlib" on the Mac kept giving me error messages.

The output differs between the different options as well as between the operating systems. Also the file sizes differed a lot. The file sizes provided are always for the entire image and not just the zoom. The biggest problem were the default settings for the Mac that produced a bad quality image that was also quite large. For code that is supposed to run on both operating systems type = "cairo" with antialias = "none" seems to be the best choice.

Are you wondering how the images would look if we wouldn’t draw the filling, but the borders and tried to plot them at the same resolution? It would obviously be a little screwed up, since the resolution wouldn’t be high enough to display the data correctly. Here is that comparison.

One thought on “R and graphics devices on Mac and Windows

Leave a comment