install.packages("openairmaps")
22 openairmaps primer
An introduction to openairmaps
and the leaflet
package
openairmaps
package has been designed to create interactive HTML air quality maps. These are useful to understand the geospatial context of openair
-type analysis, and to present data in an engaging way in dashboards and websites. This page gives a broad overview of openairmaps
as well as leaflet
, the package on which it is built.
22.1 Installation
openairmaps is on CRAN, so can be simply downloaded using install.packages()
.
If you would like to access new features and bug fixes before they’re pushed to CRAN, the development version of openairmaps can be downloaded directly from GitHub.
# install.packages("pak")
pak::pak("davidcarslaw/openairmaps")
If you would like to use openairmaps in your analysis, don’t forget to load it using the library()
function.
This chapter was produced using R version 4.4.0 and openairmaps version 0.9.0.9003.
22.2 Background
As the R ecosystem developed, rmarkdown and, more recently, Quarto emerged as capable tools for combining data analysis with document preparation. While these approaches can render typical .docx and .pdf outputs, one of their most common output formats is the HTML document. This format has many strengths, but a key one is interactivity; HTML widgets allow documents to be more informative and engaging. Numerous packages have been developed to easily develop these interactive widgets, such as plotly for plots, DT for tables, and leaflet for maps. The openairmaps package concerns itself with making leaflet maps.
Air quality data analysis — particularly as it pertains to long term monitoring data — naturally lends itself to being visualised spatially on a map. Monitoring networks are geographically distributed, and ignoring their geographical context may lead to incomplete insights at best and incorrect conclusions at worst! Furthermore, many air quality analysis tools are directional, asking questions of the data along the lines of “do elevated concentrations come from the North, South, East or West?” The natural question that follows is “well, what actually is it to the North/South/East/West that could be causing elevated concentrations?” — a map can help answer that question straightforwardly.
22.3 leaflet Primer
While openairmaps can be used without a thorough understanding of the leaflet package, some knowledge can definitely help! Figure 22.1 shows an example leaflet map. Try the following:
Click and drag to move the map around — you can go anywhere in the world!
Use your scroll wheel or the +/- options (top left) to zoom in and out.
Hover over the marker to see its label.
Click on the marker to see a popup.
Use the “layer control” menu (top right) to show and hide the marker, and swap between base maps.
library(leaflet)
leaflet() %>%
addTiles(group = "OSM") %>%
addProviderTiles("CartoDB.Positron", group = "CartoDB.Positron") %>%
addMarkers(
lat = 51.5034,
lng = -0.1276,
label = "10 Downing Street",
popup = "This is where the UK Prime Minister lives!",
group = "Marker"
) %>%
addLayersControl(
baseGroups = c("CartoDB.Positron", "OSM"),
overlayGroups = "Marker"
)
The anatomy of a leaflet map may look a little unusual, so to unpack:
Every leaflet map starts with the
leaflet()
function.Different base maps can be added using
addTiles()
andaddProviderTiles()
. There are many of these base maps — some are minimalist and some are busy, some are colourful and some are black-and-white, some are plain and some are highly stylised!Markers can be added using
addMarkers()
and other similar functions. These markers can have different features such as hover labels and popups.The
addLayersControl()
function creates a menu which allows users to show/hide or swap between certain features of the map.
Don’t worry if you don’t fully understand all of this — the “all-in-one” functions in openairmaps deal with much of this for you! The only leaflet awareness you’ll need to use these functions is knowledge of the available base map providers, which can be accessed using leaflet::providers
. These are shown in the collapsible below (the list is very long!).
leaflet::providers
[1] "OpenStreetMap"
[2] "OpenStreetMap.Mapnik"
[3] "OpenStreetMap.DE"
[4] "OpenStreetMap.CH"
[5] "OpenStreetMap.France"
[6] "OpenStreetMap.HOT"
[7] "OpenStreetMap.BZH"
[8] "MapTilesAPI"
[9] "MapTilesAPI.OSMEnglish"
[10] "MapTilesAPI.OSMFrancais"
[11] "MapTilesAPI.OSMEspagnol"
[12] "OpenSeaMap"
[13] "OPNVKarte"
[14] "OpenTopoMap"
[15] "OpenRailwayMap"
[16] "OpenFireMap"
[17] "SafeCast"
[18] "Stadia"
[19] "Stadia.AlidadeSmooth"
[20] "Stadia.AlidadeSmoothDark"
[21] "Stadia.OSMBright"
[22] "Stadia.Outdoors"
[23] "Stadia.StamenToner"
[24] "Stadia.StamenTonerBackground"
[25] "Stadia.StamenTonerLines"
[26] "Stadia.StamenTonerLabels"
[27] "Stadia.StamenTonerLite"
[28] "Stadia.StamenWatercolor"
[29] "Stadia.StamenTerrain"
[30] "Stadia.StamenTerrainBackground"
[31] "Stadia.StamenTerrainLabels"
[32] "Stadia.StamenTerrainLines"
[33] "Thunderforest"
[34] "Thunderforest.OpenCycleMap"
[35] "Thunderforest.Transport"
[36] "Thunderforest.TransportDark"
[37] "Thunderforest.SpinalMap"
[38] "Thunderforest.Landscape"
[39] "Thunderforest.Outdoors"
[40] "Thunderforest.Pioneer"
[41] "Thunderforest.MobileAtlas"
[42] "Thunderforest.Neighbourhood"
[43] "CyclOSM"
[44] "Jawg"
[45] "Jawg.Streets"
[46] "Jawg.Terrain"
[47] "Jawg.Sunny"
[48] "Jawg.Dark"
[49] "Jawg.Light"
[50] "Jawg.Matrix"
[51] "MapBox"
[52] "MapTiler"
[53] "MapTiler.Streets"
[54] "MapTiler.Basic"
[55] "MapTiler.Bright"
[56] "MapTiler.Pastel"
[57] "MapTiler.Positron"
[58] "MapTiler.Hybrid"
[59] "MapTiler.Toner"
[60] "MapTiler.Topo"
[61] "MapTiler.Voyager"
[62] "TomTom"
[63] "TomTom.Basic"
[64] "TomTom.Hybrid"
[65] "TomTom.Labels"
[66] "Esri"
[67] "Esri.WorldStreetMap"
[68] "Esri.DeLorme"
[69] "Esri.WorldTopoMap"
[70] "Esri.WorldImagery"
[71] "Esri.WorldTerrain"
[72] "Esri.WorldShadedRelief"
[73] "Esri.WorldPhysical"
[74] "Esri.OceanBasemap"
[75] "Esri.NatGeoWorldMap"
[76] "Esri.WorldGrayCanvas"
[77] "OpenWeatherMap"
[78] "OpenWeatherMap.Clouds"
[79] "OpenWeatherMap.CloudsClassic"
[80] "OpenWeatherMap.Precipitation"
[81] "OpenWeatherMap.PrecipitationClassic"
[82] "OpenWeatherMap.Rain"
[83] "OpenWeatherMap.RainClassic"
[84] "OpenWeatherMap.Pressure"
[85] "OpenWeatherMap.PressureContour"
[86] "OpenWeatherMap.Wind"
[87] "OpenWeatherMap.Temperature"
[88] "OpenWeatherMap.Snow"
[89] "HERE"
[90] "HERE.normalDay"
[91] "HERE.normalDayCustom"
[92] "HERE.normalDayGrey"
[93] "HERE.normalDayMobile"
[94] "HERE.normalDayGreyMobile"
[95] "HERE.normalDayTransit"
[96] "HERE.normalDayTransitMobile"
[97] "HERE.normalDayTraffic"
[98] "HERE.normalNight"
[99] "HERE.normalNightMobile"
[100] "HERE.normalNightGrey"
[101] "HERE.normalNightGreyMobile"
[102] "HERE.normalNightTransit"
[103] "HERE.normalNightTransitMobile"
[104] "HERE.reducedDay"
[105] "HERE.reducedNight"
[106] "HERE.basicMap"
[107] "HERE.mapLabels"
[108] "HERE.trafficFlow"
[109] "HERE.carnavDayGrey"
[110] "HERE.hybridDay"
[111] "HERE.hybridDayMobile"
[112] "HERE.hybridDayTransit"
[113] "HERE.hybridDayGrey"
[114] "HERE.hybridDayTraffic"
[115] "HERE.pedestrianDay"
[116] "HERE.pedestrianNight"
[117] "HERE.satelliteDay"
[118] "HERE.terrainDay"
[119] "HERE.terrainDayMobile"
[120] "HEREv3"
[121] "HEREv3.normalDay"
[122] "HEREv3.normalDayCustom"
[123] "HEREv3.normalDayGrey"
[124] "HEREv3.normalDayMobile"
[125] "HEREv3.normalDayGreyMobile"
[126] "HEREv3.normalDayTransit"
[127] "HEREv3.normalDayTransitMobile"
[128] "HEREv3.normalNight"
[129] "HEREv3.normalNightMobile"
[130] "HEREv3.normalNightGrey"
[131] "HEREv3.normalNightGreyMobile"
[132] "HEREv3.normalNightTransit"
[133] "HEREv3.normalNightTransitMobile"
[134] "HEREv3.reducedDay"
[135] "HEREv3.reducedNight"
[136] "HEREv3.basicMap"
[137] "HEREv3.mapLabels"
[138] "HEREv3.trafficFlow"
[139] "HEREv3.carnavDayGrey"
[140] "HEREv3.hybridDay"
[141] "HEREv3.hybridDayMobile"
[142] "HEREv3.hybridDayTransit"
[143] "HEREv3.hybridDayGrey"
[144] "HEREv3.pedestrianDay"
[145] "HEREv3.pedestrianNight"
[146] "HEREv3.satelliteDay"
[147] "HEREv3.terrainDay"
[148] "HEREv3.terrainDayMobile"
[149] "FreeMapSK"
[150] "MtbMap"
[151] "CartoDB"
[152] "CartoDB.Positron"
[153] "CartoDB.PositronNoLabels"
[154] "CartoDB.PositronOnlyLabels"
[155] "CartoDB.DarkMatter"
[156] "CartoDB.DarkMatterNoLabels"
[157] "CartoDB.DarkMatterOnlyLabels"
[158] "CartoDB.Voyager"
[159] "CartoDB.VoyagerNoLabels"
[160] "CartoDB.VoyagerOnlyLabels"
[161] "CartoDB.VoyagerLabelsUnder"
[162] "HikeBike"
[163] "HikeBike.HikeBike"
[164] "HikeBike.HillShading"
[165] "BasemapAT"
[166] "BasemapAT.basemap"
[167] "BasemapAT.grau"
[168] "BasemapAT.overlay"
[169] "BasemapAT.terrain"
[170] "BasemapAT.surface"
[171] "BasemapAT.highdpi"
[172] "BasemapAT.orthofoto"
[173] "nlmaps"
[174] "nlmaps.standaard"
[175] "nlmaps.pastel"
[176] "nlmaps.grijs"
[177] "nlmaps.water"
[178] "nlmaps.luchtfoto"
[179] "NASAGIBS"
[180] "NASAGIBS.ModisTerraTrueColorCR"
[181] "NASAGIBS.ModisTerraBands367CR"
[182] "NASAGIBS.ViirsEarthAtNight2012"
[183] "NASAGIBS.ModisTerraLSTDay"
[184] "NASAGIBS.ModisTerraSnowCover"
[185] "NASAGIBS.ModisTerraAOD"
[186] "NASAGIBS.ModisTerraChlorophyll"
[187] "NLS"
[188] "JusticeMap"
[189] "JusticeMap.income"
[190] "JusticeMap.americanIndian"
[191] "JusticeMap.asian"
[192] "JusticeMap.black"
[193] "JusticeMap.hispanic"
[194] "JusticeMap.multi"
[195] "JusticeMap.nonWhite"
[196] "JusticeMap.white"
[197] "JusticeMap.plurality"
[198] "GeoportailFrance"
[199] "GeoportailFrance.plan"
[200] "GeoportailFrance.parcels"
[201] "GeoportailFrance.orthos"
[202] "OneMapSG"
[203] "OneMapSG.Default"
[204] "OneMapSG.Night"
[205] "OneMapSG.Original"
[206] "OneMapSG.Grey"
[207] "OneMapSG.LandLot"
[208] "USGS"
[209] "USGS.USTopo"
[210] "USGS.USImagery"
[211] "USGS.USImageryTopo"
[212] "WaymarkedTrails"
[213] "WaymarkedTrails.hiking"
[214] "WaymarkedTrails.cycling"
[215] "WaymarkedTrails.mtb"
[216] "WaymarkedTrails.slopes"
[217] "WaymarkedTrails.riding"
[218] "WaymarkedTrails.skating"
[219] "OpenAIP"
[220] "OpenSnowMap"
[221] "OpenSnowMap.pistes"
[222] "AzureMaps"
[223] "AzureMaps.MicrosoftImagery"
[224] "AzureMaps.MicrosoftBaseDarkGrey"
[225] "AzureMaps.MicrosoftBaseRoad"
[226] "AzureMaps.MicrosoftBaseHybridRoad"
[227] "AzureMaps.MicrosoftTerraMain"
[228] "AzureMaps.MicrosoftWeatherInfraredMain"
[229] "AzureMaps.MicrosoftWeatherRadarMain"
[230] "SwissFederalGeoportal"
[231] "SwissFederalGeoportal.NationalMapColor"
[232] "SwissFederalGeoportal.NationalMapGrey"
[233] "SwissFederalGeoportal.SWISSIMAGE"
If you would like to use openairmaps in a more advanced way (for example, using the “marker” class of functions), you may find it useful to learn some leaflet. Its authors have written an excellent free guide to help you get started.
22.4 Functions
openairmaps currently has three main families of functions — those being network visualisation, directional analysis, and trajectory analysis — plus a handful of “utility” functions. These are summarised in Table 22.1. The functions are further divided into two categories:
“All-in-one” functions are most like openair functions in that they will construct a map from the ground-up. These functions provide the quickest route from data to a HTML map.
“Marker” functions are most like leaflet functions in that they add layers onto pre-existing
leaflet
maps. These are more complicated to use, but are more powerful if you are more familiar with how leaflet works.
Family | Description | All-in-one | Markers |
---|---|---|---|
Network Visualisation | Visualises any of the networks made available by importMeta() . |
networkMap() |
addPolarMarkers() |
Directional Analysis | Uses any of the openair directional analysis plots (e.g., polarPlot() ) as markers, allowing them to be viewed in their geospatial context. |
annulusMap() freqMap() percentileMap() polarMap() pollroseMap() windroseMap() |
addTrajPaths() |
Trajectory Analysis | Creates interactive visualisations of HYSPLIT trajectories, allowing for finer investigations when compared with their static openair counterparts. |
trajMap() trajLevelMap() |
addPolarMarkers() |
The next few pages will discuss each of these function families in turn, starting with network visualisation.