---
layout: post
title:  "Plotting gridded data with sp"
date:   2016-03-08 10:00:00 +0100
comments: true
author: Edzer Pebesma
categories: r
---

TOC

[DOWNLOADHERE]

### Introduction

The developments below are in the
[github](https://github.com/edzer/sp) version of `sp`, and will
become part of sp release 1.2-3 and higher.

Plotting spatial grids using the `plot` method was up till now not
much fun, and limited to either showing a grid, as in

```{r fig=TRUE, fig.path = "images/", label="grd1"}
library(sp)
demo(meuse,ask=FALSE,echo=FALSE)
plot(geometry(meuse.grid))
```

which only shows the cell geometry, or using `image` as in

```{r fig=TRUE, fig.path = "images/", label="grd2"}
image(meuse.grid["dist"])
```

For more advanced plots, showing color scales, one needed to
use `spplot`. `ssplot` is a powerful function, but may also be
challenging to use, plots may be challenging to fine manipulate.

The are base plots coming from `plot` can be improved by all the
bells and whistles of base plot, using `par` and by incrementally
adding features. The big missing thing though is a colour legend
showing the z-values.  Both `raster::plot` and `spatstat::plot`
provide colour scales, and it is about time to add
this to `sp`'s plot methods for `SpatialGridDataFrame` and
`SpatialPixelsDataFrame` objecs as well. With the help of [this blog
entry](http://menugget.blogspot.de/2013/12/new-version-of-imagescale-function.html),
this wasn't too much work.

```{r fig=TRUE, fig.path = "images/", label="grd3"}
plot(meuse.grid["dist"], zlim = c(0,1))
```

which shows a grid using a larger part of the plotting area, and a
color scale bar. `zlim` has been set here to ensure the scale goes
to 1 (the data go to 0.99).

The rest of this blog post shows the options now available to this plot method.

### Incrementally adding elements

The new plot method uses `layout` to make two plotting areas, one for the grid and one for the scale. 
Since the last element plotted is the grid, we can add to it:

```{r fig=TRUE, fig.path = "images/", label="grd4"}
plot(meuse.grid["dist"], zlim = c(0,1))
title("distance to river Meuse (normalized)")
points(meuse, col = 'green')
box()
```

### Axes, scale placement and size
Axes can be added by specifying `axes = TRUE`, the location of the scale by specifying `axis.pos`, which follows the numbering of `?axis`. Note how the size of the scale adapts to the axes:

```{r fig=TRUE, fig.path = "images/", label="grd5"}
plot(meuse.grid["dist"], zlim = c(0,1), axes = TRUE)
plot(meuse.grid["dist"], zlim = c(0,1), axes = TRUE, axis.pos = 1)
```

The scale size can be reduced by specifying `scale.shrink`; its
thickness can be modified by `scale.size`:

```{r fig=TRUE, fig.path = "images/", label="grd5x"}
plot(meuse.grid["dist"], zlim = c(0,1), axes = TRUE, scale.shrink = 1)
plot(meuse.grid["dist"], zlim = c(0,1), axes = TRUE, scale.size = lcm(2.4))
```

where `lcm(2)` indicates 2 cm, meaning that it isn't affected by
changing plot size. If a numeric values is given here, e.g. 1/6,
the size is relative to the area area occupied by the grid.

to increase the white space around the gridded area, `xaxs = "r"`
can be passed, which adds the usual 4% on each side.

### Specifying the scale color breaks, tics and labels
Color breaks are specified by `breaks`, specifying `breaks` requires that
a color ramp of matching length is specified (one less than the
number of breaks).  Tics and tic labels are defined by parameter
`at`. When specifying `breaks`, `zlim` becomes obsolete.

```{r fig=TRUE, fig.path = "images/", label="grd6"}
b = c(0, 0.25, 0.5, 0.6, 0.7, 0.8, 0.9, 1)
col = rev(bpy.colors(length(b)-1))
plot(meuse.grid["dist"], breaks = b, col = col, at = b)
```

### Adding grid lines
Grid lines can be added by specifying a grid cell `border` color, as in

```{r fig=TRUE, fig.path = "images/", label="grd7"}
plot(meuse.grid["dist"], border = grey(0.6))
```

This also enlightens the difference between `SpatialPixelsDataFrame` (as plotted above), and `SpatialGridDataFrame` which has the complete set of grid cells in a rectangular area:

```{r fig=TRUE, fig.path = "images/", label="grd8"}
library(methods)
plot(as(meuse.grid, "SpatialGridDataFrame")["dist"], border = grey(0.6))
```

### Plotting scale only, or image only

Sometimes, a user needs an image of the scale only, e.g. to glue it onto a leaflet plot when it does not properly support legends. In the following, the figure (device) width was set to 2 (inch), so that the scale still looks nice:

```{r fig=TRUE, fig.path = "images/", label="grd9", fig.width=1.2}
plot(meuse.grid["dist"], what = "scale", zlim = c(0,1))
```

The other option is to only plot the grid, without scale:

```{r fig=TRUE, fig.path = "images/", label="grd10"}
plot(meuse.grid["dist"], what = "image")
```

### Plotting gridded categorical (factor) variables

Categorical (`factor`) variables need a different treatement,
as continuous color change does not work for them, and neither
do continuous color scales. The default color scale used in
this case is `Set2` from the `RColorBrewer` package; see also
the [colorbrewer](http://colorbrewer2.org/) and the command
`RColorBrewer::display.brewer.all()`.

```{r fig=TRUE, fig.path = "images/", label="grd11"}
plot(meuse.grid["ffreq"])
plot(meuse.grid["ffreq"], axis.pos = 1)
```

### Factors: changing the density and width of scales, label width

The amount of space taken up by a colored field in a legend class
can be manipulated. Argument `scale.n` determines how many fields
would fill a complete side (default 15), increasing it decreases the
(here vertical) size of a color field:

```{r fig=TRUE, fig.path = "images/", label="grd12"}
plot(meuse.grid["ffreq"], scale.n = 25)
```

Argument `scale.frac` determines which fraction of the scale area is
taken by the coloured bar; in this case (`axis.pos=4`) it determines
the width of the color area (default 0.3):

```{r fig=TRUE, fig.path = "images/", label="grd13"}
plot(meuse.grid["ffreq"], scale.frac = 0.5)
```

In the following example, wide labels need extra space; this is created by

* increasing `scale.size`, so there is more space for the scale + labels
* decreasing `scale.frac` so that the absolute size of the bar remains the same

```{r fig=TRUE, fig.path = "images/", label="grd14"}
levels(meuse.grid$ffreq) = c("frequent", "moderately frequent", "infrequent")
plot(meuse.grid["ffreq"], scale.size = lcm(5.8), scale.frac = 0.15)
```

### Relative size of the categorical scale

The relative size of a scale compared to the rest of the image,
including its fonts, can be manipulated by changing the total size
of the graph. In the following, the size is set to 12 x 12 (inch);
the (automatic) resizing of the final image gives a seemingly smaller
scale:

```{r fig=TRUE, fig.path = "images/", label="grd11rsize", fig.width=12, fig.height=12}
plot(meuse.grid["dist"])
```

### Arranging multiple plots

Multiple plots can be arranged on a single device using
`layout`. Since `plot` already uses `layout` when arranging the
image and the scale, simply putting two images with scales side
by side is not simple -- layouts cannot be nested. A solution is
to create plots of only image and scale (taking care of `zlim`),
and have them precede by an appropriate `layout` statement. For
a shared legend, e.g. by

```{r fig=TRUE, fig.path = "images/", label="grd15", fig.height = 4}
layout(matrix(1:3, 1, 3), widths = c(4,4,1))
plot(meuse.grid, what = "image", zlim = c(0,1))
plot(meuse.grid["dist"], what = "image", zlim = c(0,1))
plot(meuse.grid["dist"], what = "scale", zlim = c(0,1))
```

and for separate legends e.g. by

```{r fig=TRUE, fig.path = "images/", label="grd16", fig.height = 5}
layout(matrix(1:4, 2, 2), heights = c(4,1))
levels(meuse.grid$ffreq) = c("fr", "mo", "inf")
plot(meuse.grid["ffreq"], what = "image")
plot(meuse.grid["ffreq"], what = "scale", axis.pos = 1)
plot(meuse.grid["dist"], what = "image", zlim = c(0,1))
plot(meuse.grid["dist"], what = "scale", axis.pos = 1, zlim = c(0,1))
```

More powerful arrangment methods are obtained when using plotting
methods available in package `grid`, or higher-level plotting
functions such as `spplot` or `ggplot2::ggplot`.

### Challenges

* integrate this with the plotting of other classes with attributes, in particular `SpatialPolygonsDataFrame` and `SpatialLinesDataFrame`
* get the text labels along vertical continuous scales horizontal, meaning don't use `axis` and more managment of space
* unify the scaling of the scale bars for the two different scale types (continuous, categorical)
* deal with the multiple plot issue more elegantly
