stackoverflowuser2010
stackoverflowuser2010

Reputation: 40969

How can I resize maps to fill the plot window?

I started using R's map() function to plot maps. I noticed that when I resize the plot window, the image does not scale to fill the window. How can I get the map image to automatically resize bigger or smaller, depending on how big I drag my window?

I am using R version 3.0.2 on MacOS.

For example, here is a map where I've dragged the plot window smaller and bigger. Notice that the map image's size does not change.

library(maps)
map("state")

enter image description here

enter image description here

enter image description here

On the other hand, the usual plot() command does resize the graphic to fit the window.

plot(1:100, 201:300)

enter image description here

enter image description here

enter image description here

Upvotes: 4

Views: 7436

Answers (2)

Gavin Simpson
Gavin Simpson

Reputation: 174853

I note that Josh has provided an acceptable solution to the problem, but it might be useful to understand why map() has the behaviour you describe. Essentially it comes down to map() setting the size of the plotting region based on the current size & aspect ratio of the device (the figure region more specifically) at draw time.

As such, one solution, without converting to another format, as Josh nicely demonstrates, is just to redraw the map after you've rescaled the device to the desired size. You could avoid some guesswork by doing a couple of computations based on the aspect ratio of par("usr") and then set the device to a width that is compatible with that aspect ratio.

Probably more hassle than @Josh's solution, but it does explain the behaviour. A more detailed description of the issue is given below.


The reason that the drawn map doesn't "fill" the device (up to the specified margins) is due to the code in map() setting the size of the plotting region to have a particular aspect ratio based on the size of the device etc. The resulting plotting region is the sized such that it fits within the device, but preserves the correct aspect ratio so may not entirely fill it.

The key section of code is this:

        else {
            par(mar = mar)
            p <- par("fin") - as.vector(matrix(c(0, 1, 1, 
              0, 0, 1, 1, 0), nrow = 2) %*% par("mai"))
            par(pin = p)
            p <- par("pin")
            p <- d * min(p/d)
            par(pin = p)
            d <- d * myborder + ((p/min(p/d) - d)/2)/aspect
            usr <- c(xrange, yrange) + rep(c(-1, 1), 2) * 
              rep(d, c(2, 2))
            par(usr = usr)
        }

with d defined slightly earlier as:

        d <- c(diff(xrange), diff(yrange)) * (1 + 2 * myborder) * 
            aspect

(for the example you give). The second line of the else branch is getting the current size of the figure region in inches. The figure region is the size on the device of the region containing the margins and the plot region but not any outer margin. In effect, if there is no outer margin active, this code is grabbing the size of the device (and makes an adjustment). This result is then use to set the size of the plotting region, which gets updated.

The intention seems to be to take the size of the current figure region, and use that to update the region into which the map is drawn. The size of that plotting region is in that sense controlled via the aspect ratio of the device; if you start with a wide but short window, then the computed plotting region will not need to use all the available width (if it did the aspect ratio would be wrong) and hence the plotting region is set to a size smaller than the available space.

As to why this doesn't update when you resize the window, well that is because at draw-time the size of the plotting region is set absolutely in inches. If you resize the device, the size of the plotting region remains the same and hence the map gets cropped if you shrink the device sufficiently, or uses less and less of the device space if you enlarge the device.

Upvotes: 3

Josh O&#39;Brien
Josh O&#39;Brien

Reputation: 162391

It takes a bit of work, but by converting the maps object to a SpatialPolygonsDataFrame, and then spplot()'ing that, you can get a dynamically resizing map.

FWIW, I suspect this works better because spplot() is based on grid (via lattice), and the grid graphical system supports much more sophisticated ways of handling dimensions within plot objects than does R's base graphical system.

library(maps)
library(maptools)  ## For map2SpatialPolygons()

## Convert data from a "maps" object to a "SpatialPolygonsDataFrame" object
mp <- map("state", fill = TRUE, plot = FALSE)
SP <- map2SpatialPolygons(mp, IDs = mp$names, 
                          proj4string = CRS("+proj=longlat +datum=WGS84"))
DATA <- data.frame(seq_len(length(SP)), row.names = names(SP))
SPDF <- SpatialPolygonsDataFrame(SP, data = DATA)

## Plot it
spplot(SPDF, col.regions = "transparent", colorkey = FALSE,
       par.settings = list(axis.line = list(col = "transparent")))

Here are a couple of screenshots to show that it works:

enter image description here

enter image description here

Upvotes: 7

Related Questions