24  Directional Analysis

Putting polar plots on an interactive map

Author

Jack Davison

Abstract
One of the headline features of openairmaps is creating maps using directional analysis plots as markers. Being able to place a polar plot or similar visualisation on a map can allow us to gain additional insight from our analysis, permitting us to easily compare different sites and further understand their geographic context. This page introduces the polarMap() family of functions, and the suite of customisations available to users.

24.1 Data Requirements

openairmaps contains the polar_data dataset to allow users to test the directional analysis functions. The structure of this data set is provided below, and a summary is given in Table 24.1. The important feature of this data when compared to openair::mydata is latitude and longitude information, which openairmaps needs to place the directional analysis markers in the correct positions.

library(openairmaps)
dplyr::glimpse(polar_data)
Rows: 35,040
Columns: 13
$ date       <dttm> 2009-01-01 00:00:00, 2009-01-01 01:00:00, 2009-01-01 02:00…
$ nox        <dbl> 113, 40, 48, 36, 40, 50, 50, 53, 80, 111, 206, 113, 86, 82,…
$ no2        <dbl> 46, 32, 36, 29, 32, 36, 34, 34, 50, 59, 67, 61, 52, 53, 52,…
$ pm2.5      <dbl> 42, 45, 43, 37, 36, 33, 33, 31, 27, 28, 37, 30, 27, 29, 27,…
$ pm10       <dbl> 46, 49, 46, NA, 38, 32, 36, 32, 30, 32, 39, 37, 32, 33, 34,…
$ site       <chr> "London Bloomsbury", "London Bloomsbury", "London Bloomsbur…
$ lat        <dbl> 51.52229, 51.52229, 51.52229, 51.52229, 51.52229, 51.52229,…
$ lon        <dbl> -0.125889, -0.125889, -0.125889, -0.125889, -0.125889, -0.1…
$ site_type  <chr> "Urban Background", "Urban Background", "Urban Background",…
$ wd         <dbl> 58.92536, 74.46675, 30.00000, 45.00000, 70.00000, 46.63627,…
$ ws         <dbl> 2.066667, 1.900000, 1.550000, 2.100000, 1.500000, 2.100000,…
$ visibility <dbl> 5000.000, 4933.333, 5000.000, 4900.000, 5000.000, 6000.000,…
$ air_temp   <dbl> 0.8666667, 0.8666667, 0.8000000, 0.8500000, 0.8666667, 0.96…
Table 24.1:

A statistical summary of the polar_data dataset.

Characteristic London Bloomsbury, N = 8,7601 London Cromwell Road 2, N = 8,7601 London Marylebone Road, N = 8,7601 London N. Kensington, N = 8,7601
date 2009-01-01 to 2009-12-31 23:00:00 2009-01-01 to 2009-12-31 23:00:00 2009-01-01 to 2009-12-31 23:00:00 2009-01-01 to 2009-12-31 23:00:00
nox 71 (44, 122) 138 (96, 199) 258 (139, 419) 34 (19, 65)
no2 52 (36, 71) 69 (53, 88) 99 (67, 141) 29 (15, 48)
pm2.5 13 (10, 19) NA (NA, NA) 19 (13, 27) 10 (7, 17)
pm10 16 (11, 23) NA (NA, NA) 31 (21, 43) 17 (12, 24)
lat 51.522 51.495 51.523 51.521
lon -0.126 -0.179 -0.155 -0.213
wd 213 (134, 265) 213 (134, 265) 213 (134, 265) 213 (134, 265)
ws 3.77 (2.60, 5.30) 3.77 (2.60, 5.30) 3.77 (2.60, 5.30) 3.77 (2.60, 5.30)
visibility 14,436 (11,177, 16,843) 14,436 (11,177, 16,843) 14,436 (11,177, 16,843) 14,436 (11,177, 16,843)
air_temp 12 (7, 16) 12 (7, 16) 12 (7, 16) 12 (7, 16)
1 Range; Median (IQR); Median

If you would prefer to use data from different sites or years, the import*() functions from openair make it easy to obtain pollution data with associated site latitude/longitude. The key thing to remember is to use the meta = TRUE argument when using a function like importAURN() to have the lat/lon (& site type) appended to your imported data.

sunderland <- openair::importAURN(site = c("sun2", "sunr"), year = 2015, meta = TRUE)
names(sunderland)
 [1] "source"    "site"      "code"      "date"      "nox"       "no2"      
 [7] "no"        "o3"        "pm2.5"     "v2.5"      "nv2.5"     "ws"       
[13] "wd"        "air_temp"  "latitude"  "longitude" "site_type"

By “directional analysis”, we are referring to the outputs from openair functions like polarPlot(). As a reminder as to what these figures look like, see Figure 24.1.

set.seed(123)
openair::polarAnnulus(polar_data)
openair::polarFreq(polar_data)
openair::percentileRose(polar_data)
openair::polarPlot(polar_data)
openair::pollutionRose(polar_data)
openair::windRose(polar_data)
openair::polarDiff(polar_data, dplyr::mutate(polar_data, nox = jitter(nox, factor = 5)))

(a) Polar Annulus

(b) Polar Frequency

(c) Percentile Rose

(d) Polar Plot

(e) Pollution Rose

(f) Wind Rose

(g) Polar Diff

Figure 24.1: All of the directional analysis figures which can be plotted on a map.

24.2 Overview

The easiest way to get polar plots on a map is through the use of the all-in-one mapping functions. These are all named using the pattern {function-name}Map, where {function_name} is a short hand for the equivalent openair function. A reference is provided in Table 24.2.

Table 24.2:

A reference table for openairmaps directional analysis mapping functions.

openair openairmaps scale arguments unique arguments

limits

period

breaks

statistic

percentile

limits

x

breaks

statistic

ws.int, breaks

limits

x

Effectively all of these functions have very similar arguments, although some are unique to the specific function (also shown in Table 24.2). The important ones to pay attention to are:

  • data: The data you would like to map. Ensure that lat/lon information is present.1
  • pollutant: The pollutant(s) of interest. If multiple pollutants are provided, a “layer control” menu will allow readers to swap between them.

  • latitude, longitude: The lat/lon column names. If they are not specified, the functions will attempt to guess them based on common names (e.g., “lon”, “lng”, “long” and “longitude” for longitude).

  • control: A column to use to create a “layer control” menu. Specifying control effectively splits the input data along the specified column, creating multiple separate sets of directional analysis plots. Common columns to pass to control will be those created by openair::cutData() or openair::splitByDate().2

  • popup: A column to be used to create a HTML “popup” that appears when users click the markers. This would be useful to label each marker with its corresponding site name or code, although other information could be usefully included (e.g., site type, average pollutant concentrations, and so on). A more complicated popup can be created using the buildPopup() function.

  • label: Much the same as “popup”, but the message will appear when users hover-over the marker rather than click on it. Labels are often much shorter than popups.

  • provider: The leaflet base map provider(s) you’d like to use. If multiple providers are provided, a “layer control” menu will allow readers to swap between them. Note that you can provide multiple pollutants and providers!

  • The “scale” arguments (e.g., limits for polarMap()). By specifying a scale, all polar markers will use the same colour scale, making them quantitatively comparable to one another. Specifying a scale will also draw a shared legend at the top-right of the plot, unless draw.legend is set to FALSE.

  • alpha: Controls the transparency of the polar markers, as sometimes making them semi-transparent may be desirable (for examples, if they are slightly overlapping, or seeing more of the basemap is useful). alpha should be a number between 0 and 1, where 1 is completely opaque and 0 is completely transparent.

  • The two “marker diameter” arguments, which control the size and resolution of the polar markers. It is assumed that circular markers are desired, so any number provided will be used as the marker width and height. If, for whatever reason, a non-circular marker is desired, a vector in the form c(width, height) can be provided.

  • d.icon changes the actual size of the markers on the map, defaulting to 200.

  • d.fig changes the size of the actual openair figure, defaulting to 3.5 inches. In practice, this translates to changing the resolution of the figure on the map, so you should look to adjust d.fig in the same direction as d.icon so that the axis scales remain readable.

  • ...: Any additional arguments to pass to the equivalent openair function.

24.3 Simple Demonstrations

polarMap() is demonstrated in Figure 24.2. Try clicking on each of the markers to see which sites they correspond to.

polarMap(
  polar_data,
  pollutant = "nox",
  latitude = "lat",
  longitude = "lon",
  popup = "site"
)
Figure 24.2: A demonstration of polarMap().

Another example, this time using annulusMap(), is given in Figure 24.3. Note that this time there are two different pollutants plotted, which can be swapped between using the layer control menu. openairmaps automatically deals with subscripts in common pollutant names.

annulusMap(
  polar_data,
  pollutant = c("nox", "no2"), 
  provider = "CartoDB.Positron",
  latitude = "lat",
  longitude = "lon"
)