All plots gain the theme argument to provide a convenient way to set non-data plot aesthetic features, such as gridlines and fonts. The following options are provided:
"default": the default openair theme.
"dark": a dark-background variant of the default theme for presentations and low-light viewing.
"modern": a minimalist, dashboard-style theme with reduced clutter and horizontal grid emphasis.
"soft": a low-contrast, publication-focused theme with warm tones and gently muted colours for reports and dense plots.
"print": a greyscale-first theme optimised for black-and-white output, with stronger structural elements for improved readability when printed.
Any ggplot2::theme() object, which will be used to modify the "default" theme.
importMeta() can now filter by site code, site name, site type, pollutants measured, and the distance from a given coordinate.
trajLevel(statistic = "pscf", smooth = TRUE) no longer raises an error.
trajCluster() now returns its plot as well as its data and the function call.
importMeta() will no longer return end_date as a character column if year is not given, and will no longer return the start_year and end_year columns if it is.
Add components when single m provided to kzFilter().
Harden WhittakerSmooth() when trailing / leading NA or all NA.
timeVariation() overall titles, subtitles and captions are now centred by default, in line with all other {openair} plotting functions.
Clustering using polarCluster() wrong clustered more data frame columns than necessary. The update should revert results to previous behaviour.
{openair} now suggests {rnaturalearth} over {rnaturalearthdata}. {rnaturalearthdata} is still required for a medium map resolution and {rnaturalearthhires} for a high map resolution, but these are now managed by {rnaturalearth} directly.strip.position, x.relation, and y.relation are now no longer function-level arguments and are handled via ....
The labels argument (as paired with breaks) is deprecated. If passed through ... it will be automatically mapped to its new place in the breakOpts() function.
Refinements to how parameters are passed via ... to plotting functions:
Graphical parameters are now defined using ggplot2 conventions (e.g., shape over pch).
base/lattice paramters are automatically remapped to their ggplot2 equivalent with a warning.
nrow and ncol can now be provided to control facet layout. As above, layout is automatically unpacked into ncol and nrow with a warning.
scales can now be provided to control facet scale restrictions. As above, x.relation and y.relation are automatically combined into scales with a warning.
space, axes, axis.labels, strip.position and switch are now passed to ggplot2::facet_wrap() or ggplot2::facet_grid().
title, subtitle, tag, and caption can be used throughout openair (title replacing main and caption replacing sub). All are passed through quickText() if auto.text = TRUE.
lineend, linejoin and linemitre tweak the appearance of line plots; see ggplot2::geom_line() for more information.
Additional interactions between ... parameters and various plots:
timePlot() now takes shape to add markers to the line chart. This can be a vector to vary with pollutant/group.
timeProp() now takes linewidth and linetype to control border style.
TheilSen() now takes linewidth, linetype, shape and alpha.
smoothTrend() now takes linetype, linewidth, shape and size. These can be vectors to vary based on pollutant. The linewidth of the data will always be half of that of the model.
Refinements to how ref.x and ref.y behave throughout {openair}:
ref.x and ref.y can now take a vector rather than a list, which will just use those values as the x/y intercept with default graphical parameters.
ref.x and ref.y have been added to timeProp() and TheilSen().
ref.x and ref.y can now take {ggplot2}-style parameters (intercept, alpha, colour, linetype, linewidth). The old parameter names (h/v, cols, lty and lwd) are automatically remapped so still work.
The non-intercept arguments passed to ref.x and ref.y (e.g., alpha) are now automatically recycled to the length of intercept, similar to how ... parameters are recycled.
Added refOpts() to help construct values for ref.x and ref.y, similar to windflowOpts().
Refinements to how breaks are implemented in functions like trendLevel():
If breaks doesn't cover the full range of the data being binned, the maximum and minimum breaks will be overwritten so that it does.
If breaks is of length 1, the colour range will be split into breaks categories, defaulting to using the same logic as running cutData() on a numeric column.
breaks can now take a named list, defined using the new breakOpts() function. Most significantly, this allows for the method of binning to change for single-value breaks (quantiles, equal range bins, user-defined bin widths, approximate 'pretty' breaks and wind direction binning at time of writing).
polarPlot(), polarAnnulus(), corPlot() and trajLevel() have gained breaks.
labels is no longer a top-level argument and can be defined by passing a list to breaks. labels given to ... will be converted with a warning.
All functions which take breaks also now contain the trans argument to perform scale transforms on continuous colour scales. This can take FALSE (no scale transform), TRUE (an appropriate default transform - usually "log10"), or a {scales} transform object (or string shorthand).
Refinements to colours in {openair}:
openColours() gains direction, alpha, begin, end, lightness and saturation for better control over colour palettes.
Added colourOpts(). Any cols argument in {openair} can now take the colourOpts() function, which tells each plotting function how to use the new openColours() arguments.
Added openSchemes() which returns a table of available colour palettes.
New palettes:
Completed the set of "viridis" palettes with "rocket" and "mako".
Added additional palettes by Paul Tol; "tol.highcontrast", "tol.vibrant", "tol.mediumcontrast", "tol.pale" and "tol.dark".
Added various palettes based on the work of Fabio Crameri - see openColours() for more details.
Added openColors() and colorOpts() which are synonymous with their British English equivalents openColours() and colourOpts().
New Kolmogorov-Zurbenko (KZ) Filter functions kzFilter() and kzaFilter(). These functions significantly enhance the capability of {openair} by allowing different time components to be separated and analysed separately. The kzFilter() function is considered a good default for a wide range of problems whereas the kzaFilter() function is the adaptive version that is well-suited to capturing abrupt changes, e.g., through an intervention. The range of uses of the filters will be covered in the {openair} book.
TheilSen() and smoothTrend() now use a more straightforward way to input missing data when deseason = TRUE and missing monthly data are present based on monthly linear regression by month. The user is alerted to the imputation and the monthly plot shows the imputed data as a filled grey circle.
smoothTrend() will now use loess when it has insufficient data to fit a GAM.
cutData() gains the wd.res argument, which can take one of 4, 8, or 16, defaulting to 8. 4 cuts the data into North, East, South and West. 16 cuts the data into N, NNE, NE, ENE, etc. All plotting function's type argument now responds to this, showing only four panels when wd.res = 4 and a 5x5 grid of sixteen panels when wd.res = 16.
trajPlot() and trajLevel() regain the map.res argument. This is passed to rnaturalearth::ne_countries() so can take three different resolutions.
polarPlot() will now annotate the identity of the radial axis as a caption, if annotate = TRUE.
ws and/or wd have been added to percentileRose(), polarAnnulus(), polarDiff() and polarFreq() in line with polarPlot().
angle.scale has been added to polarFreq() in line with all other polar coordinate plots.
The default key.position of corPlot() is now "right".
timePlot() has gained the key.title argument.
timeAverage() now has a more robust approach to multi-period averaging time such as "3 day". Bin boundaries are at fixed periods of time, which should ensure consistency across data sets that start at different times. This also means there is less need to use the argument start.date unless there is a need to extend the start date of the data for some reason e.g. to the beginning of a year. THis change may result in slight differences in returned output but should not affect periods such as "day", "hour" and "month".
calendarPlot() will no longer duplicate windflow arrows when type != "default".
calendarPlot() now supports type = "wd".
Fix intercept calculation in TheilSen(). Updated the intercept formula from median(y) - slope * median(x) to median(y - slope * x). This resolves a visual bug where trend lines (especially annual aggregations with negative slopes) appeared horizontally displaced due to inaccurate intercept estimates on sparse data. This fix affects the visual display of the trend lines but not the calculated slopes or uncertainties.
quickText() is once again tolerant of apostrophes.
windflowOpts() will no longer overwrite the default range of functions like calendarPlot() and trendLevel() if range is not supplied by the user.
polarPlot() will no longer produce a square-shaped surface when exclude.missing = FALSE.
fontsize is now correctly passed to scatterPlot(), polarAnnulus(), variationPlot(), and timePlot().
Strings with line breaks (e.g., the result of cutData(type="seasonyear"))) will no longer error when used for facet labels.
name.pol will now correctly map onto the names of pollutants in timeVariation().
windRose() now respects cols when ws2 and wd2 are provided.
xlim is now correctly passed to the coordinates of trajLevel().
openair now depends on R v4.1 and, internally, uses the base R pipe (|>).
openair now imports ggplot2 and scales and suggests sf, geomtextpath, legendry and rnaturalearthdata.
openair no longer imports lattice, latticeExtra, hexbin or mapproj nor suggests mapdata or maps.
All plotting functions are now written in ggplot2. lattice specific options and annotations will no longer work, but many can now be achieved using ggplot2::theme() and ggplot2::annotate().
trajPlot(), trajLevel() and trajCluster() have had their three projection related arguments removed and replaced with a single crs argument, which defaults to lat/lng (4326).
As the above three functions no longer call scatterPlot(), scatterPlot() no longer has the map argument.
drawOpenKey() has been removed due to being lattice-specific.
linearRelation() and calcFno2() have been removed from openair due to using outdated methodology and assumptions.
summaryPlot() has been removed from openair. This function was very old and inconsistent with the rest of openair. It is planned to be replaced in the future with new summary functions.
key.header and key.footer have been replaced with a single key.title. This is due to ggplot2 not supporting a separate "header" and "footer" for guides.
The key argument has been deprecated, as it now only exists to overwrite key.position when it is FALSE. Please use key.position = "none" going forward.
Argument names have been standardised throughout openair. For example, instances of col have been replaced with cols. This may cause some existing code to break, but will ensure each function behaves more similarly going into the future.
timeVariation() has been almost completely rewritten. It is now a thin wrapper around the new variationPlot(), which can take any arbitrary x value - passed to cutData() - to use for its x-axis. Furthermore, it has gained the following changes:
Gained the panels argument. This allows for panels other than "hour.weekday", "hour", "month", and "weekday" to be represented in the plot assembly.
When key is FALSE, no key is shown for any of the four timeVariation() plots. Previously, any value passed to key would cause all four plots to display a key.
(!) BREAKING: The order of xlab and ylim now matches the order of panels. month.last has also been deprecated; if used and TRUE, this will override panels with a warning. The output names output$data will now vary based on panels, and the type column will be named {type}_type (e.g., "hour_type").
(!) BREAKING: The names of the plot and data objects returned by timeVariation() are now named after panels and have a more consistent structure.
timePlot() refinements:
group can now take a character string, passed to cutData() via timeAverage(). This works similarly to group in timeVariation() in that it colours traces within the panel, rather than splitting them into multiple panels.
Gained the x.relation argument, allowing for different x ranges on different panels.
smoothTrend() refinements:
Gained the x.relation, date.format, and key.position arguments, in line with timePlot().
Gained the progress argument, passed to timeAverage().
avg.time is also no longer restricted to just three options (any timeAverage() option is permitted), although too fine a time resolution may obscure the smooth trend for long running data.
calendarPlot() refinements:
Gained the type argument. This can take one type and creates a 2D matrix using month & whatever the user has selected. type = "year" has special handling.
Gained the windflow argument, which deprecates passing "ws" or "wd" to annotate.
Gained the percentile argument, passed on to timeAverage().
Gained the show.year argument, defaulting to TRUE. When FALSE and only one year of data is given, the strip titles will only read, e.g., "January" instead of "January-2000". This can create cleaner plots, as well as being useful for certain edge cases (e.g., if the calendarPlot is showing day-of-year averages over multiple years).
When statistic == "min" and annotation %in% c("ws", "wd"), the ws/wd returned will correspond to the minimum daily pollutant, rather than the minimum daily ws/wd.
timeProp() refinements:
proportion is now treated more like type internally. For a user, this means it can now be passed "default" to avoid any conditioning and create a regular period average barchart.
sub can now be defined via ...; set sub = NA to remove the text annotation which appears by default at the bottom of a timeProp() plot.
Gained the key argument to remove a legend.
"season" is now a permitted avg.time option in timeProp(), better aligning it with the options in timeAverage().
... is now correctly passed to cutData() when using type/proportion.
corPlot() refinements:
Added the annotate argument which can change the correlation annotation to a p-value marker or stars, or remove it entirely.
Added two new arguments triangle and diagonal for controlling the plot appearance.
Added arguments key and key.title for adding and refining a plot legend.
trendLevel() refinements:
(!) BREAKING: type now defaults to "default", in line with other openair functions.
Added windflow and min.bin arguments, in line with similar functions.
Two type values are now supported.
TaylorDiagram() refinements:
pos.cor argument which controls whether the negative correlation quadrant is shown.New function WhittakerSmooth() to do Whittaker-Eilers Smoothing. This is a fast and general smoothing technique, well-suited to a wide range of problems. The function can be used to flexibly smooth and interpolate missing data. Additionally, the function can flexibly define a baseline (and hence increment) for a time series.
New function windflowOpts() which can be passed to the windflow argument of various openair functions to thoroughly customise the "windflow" arrows.
All openair plotting functions have gained strip.position to control the placement of the facet strip.
trajPlot() and trajLevel() have gained the grid.nx and grid.ny arguments which can be used to control the number of ticks on the coordinate grid, or remove it altogether.
cutData() now contains the drop argument. This allows for greater control over factor levels for appended columns. For example, consider a situation in which data only contains dates in March and May and type = "month" is used:
drop = "empty" will ensure the resulting vector only has factor levels "March" and "May".
drop = "none" will ensure the vector has all twelve months (January, February, March, etc.).
drop = "outside" will retain 'inclusive' factor levels within the range of the data - in this case "March", "April", and "May".
drop = "default" is the existing cutData() behaviour - in the case of type = "month", it is equivalent to drop = "empty".
cutData() also gains the "quarter" and "quarteryear"/"yearquarter" type options. These split a year cleanly into quarters, as an alternative to "season" and "seasonyear"/"yearseason". While seasons better align with meteorology, quarters more cleanly fit into a single calendar year and may better align with other relevant periods (e.g., reporting schedules, ratification calendars, economic activity, etc.).
is.axis now has an effect on weekday, season, seasonyear and monthyear.
quickText() now converts air_temp (a common worldmet variable) into "temperature".
timeAverage() is much faster with the bulk of the calculations made using C++.
runRegression() is now much faster with a new algorithm.
timePlot() now allows duplicate dates when time.avg is used. The user will still receive a warning from timeAverage(), which is used internally, but the plot will still be created.
The windflow argument of timePlot() now works when "ws" and/or "wd" are in pollutant.
importUKAQ() now closes its url() connections and generally fails more gracefully when data_type %in% c("annual", "monthly", "daqi"). This was already the case for other data types.
timeAverage() will no longer leave Uu and Vv columns behind when statistic = "data.cap".
timeAverage() now correctly passes ... to cutData().
timeAverage() now properly calculates wind speed and direction when vector.ws = TRUE.
selectByDate() now correctly handles the end date if supplied when in a date format (i.e., dd/mm/yyyy) and selects all hours in that day if present.
importEurope() relies on the same back-end database as the saqgetr package (https://github.com/skgrange/saqgetr), which was retired in February 2024. importEurope() will now warn users of this, and outright error if year >= 2025. Users are instead encouraged to use the EEA Air Quality Download Service https://eeadmz1-downloads-webapp.azurewebsites.net to obtain European data for the time being. An R package, https://github.com/openair-project/euroaq, has been developed to facilitate its use.
The source argument of importUKAQ() now defaults to NULL. This option allows the function to assign the source of each site itself, with some caveats:
Ambiguous codes (e.g., "AD1", which corresponds to a SAQN and locally managed site) will preferentially import from the national networks (AURN, then AQE/SAQN/WAQN/NIAQN) over locally-managed networks. To override this users should manually define source.
Incorrect codes not found in importMeta() will error if importUKAQ() is left to assign the source.
When data_type is one of the aggregate types (e.g., "annual") and a site isn't defined, a source must be provided.
It is likely slightly slower for the function to assign source itself than for users to specify it themselves.
The specific metadata columns appended when importUKAQ(meta = TRUE) can now be controlled using the meta_columns argument. For example, setting meta_columns to c("zone", "agglomeration") will append the zone/agglomeration information instead of the default site type/latitude/longitude.
DAQI information imported using importUKAQ(data_type = "daqi") will be returned with the relevant DAQI band appended as an additional factor column; either "Low" (1-3), "Moderate" (4-6), "High" (7-9), or "Very High" (10). See https://uk-air.defra.gov.uk/air-pollution/daqi for more information.
importImperial() has been added, superseding importKCL(). They are functionally identical, but reflect that londonair is now managed by Imperial College London. Function arguments have been renamed in importImperial() to better match importUKAQ().
cutData() gained numerous new features:
Added the names argument to specify the name of the appended columns. For example, cutData(mydata, "wd", names = c("windDir")) will append a column named "windDir".
Added the suffix argument as an alternative to names. If a new column would otherwise overwrite an existing column, suffix will be appended. For example, cutData(mydata, c("nox", "o3"), suffix = "_cuts") would append nox_cuts and o3_cuts columns.
cutData() is now less destructive and better cleans up after itself. For example, when type = "yearseason", it will no longer leave 'year' and 'season' columns behind, or overwrite existing 'year' and 'season' columns.
cutData() will now give an informative error message if the user provides a type which is in neither an in-built option nor a column in their dataframe.
calcPercentile() gained the following arguments:
Added the type argument, in line with timeAverage().
Added the prefix argument to control the naming of the returned columns.
binData() gained the following arguments:
Added the type argument, passed to cutData().
Added the B and conf.int arguments, passed to bootMeanDF().
selectRunning() gained the following arguments:
Added the type argument, passed to cutData().
Added the name argument, which changes the name of the new column appended by the function.
Added the mode argument, which allows selectRunning() to filter the dataset rather than append a column.
rollingMean() has gained the type argument. This will likely be of most use for distinguishing between - and calculating separate statistics for - different monitoring stations within the same data frame.
splitByDate() can now more consistently take Date / POSIXct inputs as well as characters, and provides more flexibility over inputs with a new format argument.
aqStats() gained the progress argument, in line with timeAverage().
Many 'data utility' functions will now either warn or error if duplicate dates are detected, which is suggestive of a mix of either sites or averaging times within the same dataframe. The following functions have new behaviour:
selectRunning() and rollingMean() will error (duplicate dates break the logic of 'rolling window' functions).
aqStats() will also error, as it relies on rollingMean().
timeAverage() will warn the user but proceed with calculations, as averaging across different sites may be a legitimate action.
Functions which rely on timeAverage() will also warn but not error (notably calcPercentile() but also many plotting functions with avg.time arguments).
Added new features for openColours():
Added new qualitative colour palettes: the "tol" family are colour-blind friendly palettes based on the work of Paul Tol, and "tableau" and "observable" provide access to the "Tableau10" and "Observable10" palettes to aid in consistency with plots made in those platforms.
When n isn't defined for a qualitative palette (e.g., "Dark2"), the full qualitative palette will be returned. Previously this errored with the default of 100.
openColours() will now check whether the provided scheme is either a known scheme name or a vector of valid R colours, and provide an informative error if this is not the case.
polarDiff() has gained the type argument, and correctly responds to main and key.title via the ... options.
trendLevel() has gained new statistic types to match timeAverage(), including "mean", "median", "min", "max", "sd", "sum", "frequency" and "percentile".
trendLevel() will now automatically generate appropriate labels if breaks are provided. The labels argument can still be used to provide custom labels per break.
The formula.label argument of polarPlot() will now control whether concentration information is printed when statistic = "cpf".
Added calm.thresh as an option to windRose(). This change allows users to set a non-zero wind speed threshold that is considered as calm.
Added the map.lwd, map.lty and map.border arguments to trajPlot(), trajLevel() and trajCluster() for greater control over the 'basemap' of each plot.
Fixed repeated day number in calendarPlot() when statistic = max.
Fixed annotate = FALSE in windRose() where axes and labels were not shown
Fixed an issue wherein importUKAQ() would drop sites if importing from local sites and another network.
polarCluster() will no longer error with multiple pollutants and a single n.clusters.
importUKAQ() will correctly append site meta data when meta = TRUE, source is a length greater than 1, and a single site is repeated in more than one source (e.g., importUKAQ(source = c("waqn", "aurn"), data_type = "daqi", year = 2024L)))
calcPercentile() will now correctly pass its arguments (e.g., date.start) to timeAverage().
timeAverage() will now more consistently return NA values rather than NaN or Inf when all values are NA. This specifically affects the "mean" and "min" statistics.
importUKAQ() will now correctly label a measurement as ratified when it is on the day of ratified_to. i.e., if a site is ratified to 2020/01/01, the measurement at 2020/01/01 23:00 will now be labelled as ratified.
Fixed importImperial() URLs.
corPlot() to carry through "use" option in cor.aqStats().trajCluster() that did not transform coordinates before distance matrix was calculated. Thanks to Dan Jaffe.data_type in the importUKAQ() family.importUKAQ() can now be used to import annual, monthly, and DAQI statistics for multiple combinations of source and year.added a new importUKAQ() function, which supersedes importAURN(), importAQE(), importWAQN(), importSAQN(), importNI() and importLocal(). importUKAQ() brings a lot of new functionality to accessing UK air quality data through {openair}.
importUKAQ() has all of the same arguments as the functions it supersedes, as well as "source" to define the specific network of interest. The "source" argument can either be of length 1 or equal to the length of the "site" argument. This means that importUKAQ() can import statistics from multiple different networks at once.
importUKAQ() allows ratified = TRUE and to_narrow = TRUE simultaneously. This will return a tibble with two columns per observation - "value" containing the concentration and "qc" containing TRUE/FALSE which indicates whether the concentration is validated.
The AQE, WAQN, SAQN and NI networks now allow for data_type = "daqi". At time of writing, only data from 2022 is present.
the site and pollutant arguments of importUKAQ() are respected when data_type is "daqi", "annual" or "monthly". The default behaviour to return all available statistics has not changed. (#346)
the various arguments which augment import function outputs now behave more consistently with one another. For example, ratified now respects pollutant and only returns the "_qc" columns for the pollutants of interest.
importAURN(), importAQE(), importWAQN(), importSAQN(), importNI() and importLocal() are still exported by {openair}. These are all simply wrappers around importUKAQ() with forced "source" arguments, and remain for back-compatibility and convenience.
While importKCL() also imports UK air quality data, it is not currently made available through importUKAQ(). Users should continue to import KCL data via importKCL() for the time being.
importMeta() has gained two new "source" options to assist with the new importUKAQ() function:
source = "ukaq" will return metadata for all of the "UKAQ" networks.
source = "all" will return all available metadata (including KCL and Europe).
new function runRegression() for extracting 'dilution lines' from air quality and other data. Online manual will be updated with principles and examples.
calendarPlot() now automatically creates its own labels if breaks are specified. For example, c(0, 10, 20) will create the labels c("0 - 10", "10 - 20"). labels can still be used to override the default values. (#341)
Added the w.abbr.len argument to calendarPlot() which controls the length of the weekday abbreviation. This was requested to help people using written Chinese, but will be more broadly useful (e.g., to use "Mon", "Tue", "Wed" in place of "M", "T", "W"). (#101)
return tibble from timeAverage().
add option panel.gap to leave space between panels in timeAverage day-hour plots. Set to 0 for previous behaviour.
Move regression formula off main plot for polarPlot() for clarity and label slope as 'm'.
Tweak seasonal trend decomposition using STL to allow the seasonal amplitude to vary more. Affects smoothTrend() and TheilSen().
Added colours recommended by the UK Government Analysis Function (https://analysisfunction.civilservice.gov.uk/policy-store/data-visualisation-colours-in-charts/) to openColours().
polarCluster now prints the cluster contributions and returns a data frame of them in the output.
The order of columns in importUKAQ() will remain consistent (metadata, date, pollutants, meteo) regardless of whether hc is TRUE or FALSE.
quickText() will now automatically capitalise "no" to "NO". (#343)
The year argument of importMeta() is now respected when source = "kcl" and "europe".
Several of the directional analysis plot family (e.g., polarFreq()) have been refactored to use is.null() or is.na() over missing(). While predominantly an internal change, this should be make these functions easier to use inside of other functions (e.g., function(data, breaks = NA) polarFreq(data, breaks = breaks)) will now run successfully).
For calendarPlot when annotated with ws or wd arrows, use max ws/wd that corresponds to hour of maximum pollutant concentration and not simple the max ws/wd for a day.
summaryPlot() no longer forces time zones to be GMT. (#356)
add option meteo to importAURN() family of functions. By default modelled wind speed, direction and ambient temperature are returned if available, but not if meteo = FALSE.
added a new column to modStats(), "P", which represents the P-value of the correlation as reported by cor.test().
update processing of ADMS meteorological (.MOP) files to return stability and tidier data.
refined the output of pollutionRose() where the ws2 and wd2 options are provided; instead of the misleading N/E/S/W, the markers become 0, +90, +/-180, -90.
the timeAverage() progress bar is now powered by {cli}/{purrr} and can be silenced using the new progress argument.
the polarCluster() progress bar is now also powered by {cli}/{purrr}.
colours corresponding to the UK daily air quality index (https://uk-air.defra.gov.uk/air-pollution/daqi) have been added as options to openColours().
the {openair} object's data property returned by the trajLevel() function is now consistently formatted regardless of statistic choice, and contains relevant statistic-specific information (e.g., sigma for "SQTBA").
fixed issue with modStats() such that "method" can now be changed (e.g., to "spearman").
fixed issue with aqStats() where output wasn't being properly split by pollutant/type.
fixed issue with conditionalQuantile() where the plot would fail to be produced when "type" was not specified.
fixed issue with cutData() where "season" wouldn't respect system locale (e.g., would still show "(DJF)" on Italian systems, instead of the correct "(gla)"). Note that the season name itself (e.g., "Winter") cannot be automatically converted.
fixed the theilSen() silent argument. The message "taking bootstrap samples. please wait" is also now sent via message() rather than print() and only appears once per function call.
fixed issue where trajCluster() proportions would overlap when clustering forward trajectories. Proportions should now appear at the end of cluster paths, regardless of whether the trajectory is back or forward.
fixed issue where not all {openair} plotting functions would properly return an openair S3 object, and that not all data objects were tibbles.
Fixed issue with timeAverage() where date formatting caused problems, possibly due to latest version of R (4.3.0).
do not use native pipe yet - does not work with old versions of R
fix issue with polarDiff() where the resulting openair object did not contain the plot element.
add year as an option to importMeta. This allows the user to select sites that were only open at some point in the chosen year or duration of years.
make sure full daily gravimetric data are returned for PM10 and PM2.5 if available when using importAURN family of functions. These data will be returned as gr_pm2.5 and gr_pm10 if data_type = "daily"
add alpha argument to all polar directional analysis functions. This is mainly for use in openairmaps but may be of general interest for specific use cases.
fix small bug in smoothTrend in returned fit data when data is missing.
importLocal to access locally-managed automatic monitoring datapollutionRose when single number of breaks given (was ignored)import, import.2, importAURNcsv and kernelExceedsigma to options for SQTBA trajectory analysis for control over plume spread assumptions; set to 1.5 km (in one hour)timeAverage for multi-time period averagestimeProp and simplify codeimportAURN as data_type.importAURN family as data_type.importMetacalendarPlot issue when there are gaps between selected months e.g.month = c(1, 2, 12)calendarPlottimeAverage if DSTresolution in polarPlot --- now uses predictions at a coarser resolution that are interpolated. Will also speed-up plottingtrajlevel), SQTBA --- Simplified Quantitative Transport Bias AnalysispolarPlot when two pollutants supplied for different statisticspolarPlot. This regression approach determines a best fit line when there is error in the 'x' and the 'y'. The openair book will be updated to cover this in more detail.importAURN.importAURN.windRose and pollutionRose.TaylorDiagram for annotation of observed data.timevariation when difference = TRUEtrajCluster issue to do with dplyrimportAURN, importSAQN, importWAQN, importAQE and importNI. New option data_type, which can be "hourly" (default), "annual", "monthly", "daily" and "15min" (for SO~2~). These new data sources should make it much easier to work with long-term time series with many sites. See the openair manual for more details.selectRunning.windRoseselectRunning. Now returns full data frame with a new condition column.month not passed in calendarPlotwindRose where whole period is calmpolarCluster to speed up clustering through option pamonce = 3. This should not appreciably affect results.aqStats due to lubridate time zone issue.TaylorDiagram when group was present.plot.type to summaryPlot to change line style; most useful for vertical lines in time series with plot.type = "h"polarClusterpolarDiff to consider the difference surface between two polar plots.polarCluster to consider clustering of differences in polar plot surfaces.windRose and pass on option for number of significant figures used to annotate plots (dig.lab)percentileRose when statistic = "cpf" and multiple pollutantstimeAverage when type = "season" and avg.time = "season"week to openair default typestimeVariation when considering difference plots with missing datareturnair_temp) to meteorological variables returned from importAURN, importSAQN and importWAQN (using WRF model).importAQE due to strange dplyr join issuesstatistic = "Spearman" to polarPlot as an option when considering two pollutants.method option to corPlot to allow different correlation methods ("pearson", "spearman" or "kendall")importAURN, importSAQN, importWAQN, importAQE.importNI to import data from Northern Ireland.ratified. These functions include importAURN, importSAQN, importWAQN, importAQE and importNIimportMetapercentileRose.importWAQN that would intermittently fail; sometimes crashing R.date.format to TheilSen.calendarPlot slowness on MacOSpolarPlotstatistic = "nwr" in polarPlot that implements the
Non-parametric Wind Regression based on Henry et al. (2009). The
openair implementation is not identical but should yield similar
results.importAQE function to import data from Air Quality
England sites.importEurope to provide access to some the data from the
saqgetr package.TheilSen when no missing data and deseason = TRUEtimeAverage when interval padding dates and date is "Date" class and not "POSIXct"trajCluster, should be n.cluster not n.clusterscutDatatimeAverage, used for avg.time = "season".to_narrow to importAURN, importSAQN, importKCL and importWAQN to stack data into a tidy format. The data are now returned as a 'tibble'importSAQN and importWAQN.TheilSen when conditioning and < 6 annual measurementspolarPlot axes.deseason = TRUE in smoothTrend and TheilSen. This replaces simple linear interpolation.smoothTrend when ci = FALSE (no smooth was fitted).importWAQN to access data from the Welsh air quality network.importMeta.importMeta.aqStats use default data.thresh = 0 rather than 75% to ensure summaries are calculatedtimeVariation when statistic - "median". Revision will result in narrower range.corPlot to plot lower and upper triangles; add lower as an option.timeAverage when interval more than one time unit e.g. "10 day"polarPlot(mydata, cols = "plasma")align to be used in aqStats to determine how rolling means are calculated. Can take the values "centre" (default), "left" and "right".importAURNpolarAnnulusdplyr.lubridate in timeAverage to improve speed / simplicityopenair objectspolarPlot when statistic = "cpf" when using tibblespolarCluster with exported data (date was not correctly merged to produce single date column)mydata to a 'tibble' for easier printing.npoints = NA in trajPlot to suppress plotting of interval points.windRose when ws/wd have different namestimeAverage for wind direction (wd) when statistic = "data.cap"windRose(mydata, col = "cbPalette", breaks = 6). Thanks to Jerry Martin.plot to TheilSen. FALSE can be useful when analysing data to extract the trend components and plot in other ways and when the TheilSen plot is not required.silent to TheilSen to avoid printing updates to trend fitting. By default it is FALSE.timeVariation when more than one pollutantselectByDate.timePlotcalendarPlot to span any time period. The function can now straddle parts of two years or several years.col.arrow to calendarPlot to control colour of the wind speed / direction annotation arrows.selectByDate due to changes in lubridateimportSAQN when no dataimportAURN and point users to importMetascatterPlot when method = "level" due to NSEtidyr in place of reshape2plyr and reshape2percentileRose for method = "cpf" with multiple pollutantspolarCluster resolution to "normal" rather than "fine" to speed up.trajLeveltimeAverage when statistic = "sum" and all data in period was missing (would return 0 rather than NA)conditionalEval plots by using data where there are no missing data for all variables used. This is more important of var.obs and var.mod are supplied and hence additional variables are considered relative to only obs and mod.calendarPlot to do with time zoneswindRose/pollutionRose when two conditioning variables were given (problem in bias correction)angle option to percentileRose to allow wind direction averages for sectors >10 degrees.aqStats if only a few lines of datastatistic to equal "mean", "median" or "frequency" in scatterPlot, when method = "level"scatterPlot when method = "level" to use tensor interaction to allow for better smoothing when x and y are on different scales.polarPlot and suggest setting force.postive = FALSEmaps Suggests rather than DependswindRoseTheilSen and when no trend information is givenbinData to easily summarise mean and 95% confidence intervals for intervals of a variablebootMeanDF, used to calculate the bootstrap uncertainty in the mean of a vector.importAURNpolarPlot to work with pairwise statistics to compare two pollutants. The function can consider Pearson correlation and slopes from ordinary linear regression, robust regression (using MASS function rlm) and quantile regression (requires the quantreg package to be installed).polarPlot plot resolution to "fine".windRose problem with some data due to missing datamapdata package to SuggestsimportAURN and importKCL.polarPlot.y.relation is used when there is no grouping in timePlot. To retain the behaviour of earlier versions use relation = "free"type when used with timePropwindRosetimePlotalpha to polarPlot to control transparency of plotted surface. Mostly useful for overlaying polar plots on leaflet maps (see openairmaps package)grid.line option in windRose so that users can control grid spacing, line type and line colourdownload.file can use libcurl for access to https (used in importAURN)avg.time option to summaryPlot to control the averaging times of the time series lines and print.datacap to control whether the data capture % is shown for each interval.selectByDate where a day number would not workwindRose, polarPlot to avoid ambiguous interpretation of wind directionkey.position option to timePlot to control the location of the key.timeVariation when data for some types is missing.trendLevel issue due to dplyrlubridate package in timeAveragesummaryPlot related to dplyr use (would not plot missing data correctly)windRose mean and statistics returned in data (thanks to Dr Ulrich Quass)importKCL when incomplete time series (would drop site code and site name)pollutionRose due to issue with calmssmoothTrend where model uncertainties were not returnedsummaryPlot where missing data would not be shown correctly when date was not ordered in sequencewindRose when comparing two data setsGoogleMapsPlot is deprecated and will be replaced with a better function.corPlotimportAURNimportMeta for AURN - change in database source formatpollutionRose plot warningmodStatstimeAverage where estimating the time interval in input data could be unreliable due to low data availabilitytimeAveragetype "yearseason" (or "seasonyear"). This will split data by every year / season combination, making sure the seasons are contiguous. For example, in winter in the northern hemisphere December 2010 will be considered part of winter 2011, rather than winter 2010. Thanks to Ralf Weisse for the suggestion.importAURN and importMeta when users are within an organisational network. Problems likely due to move from http to https and SSL Certificates.TheilSen bug when two types.dplyrconditionalEval to avoid plot error.importAURNimportAURN meta data in help function.scatterPlot to have control over plot symbol fill and colour (for symbols 21 to 25). Use cols and fill to control.scatterPlot can now fit more than one linear equation when there is a grouping variabletype such as month, year etc.timeProp, remove box.width optionwindRose bias correction (thanks to Eric Christensen)statistic = "r" in polarPlot for comparing polar plot correlation surfaces between two pollutants using Gaussian kernel weighting.type = "daylight" when time zone not UTC.cutDaylight as a separate function (cutData works for everything)w.shift option to calendarPlot to control the first day of the week and subsequent order (thanks to Giovanni Bonafè)timeAverage when expanding time seriesdplyr warnings in TheilSentype = "season" in TheilSen and smoothTrendtrajClusterdplyr bugs where some functions would fail with two typestrajClusterlinearRelation to use any arbitrary time averaging period.timeVariation when group = "season"NA factors in trajPlottrajPlot that sometimes failed to print map when groupingtrajCluster and option by.typecutDatatimeAveragelubridate for easier / faster date-time manipulationstimeProp (lattice panel.barchart is very slow)windRose (wd can be NA and ws zero i.e. calm)timeVariation when difference = TRUE for some factor levels (were in alphabetical)trendLevel; make sure strip is whitetimeVariation (vector.ws did not work)TaylorDiagram more flexible when using two groups and the
second is date-basedGoogleMapsPlot when pollutant not givenGoogleMapsPlot; write to
temporary file insteadaqStatsTheilSen should always give trend in units/year (would use xlab if
supplied)cutData bug where quantile cuts are madescatterPlot so that factors with no data
still shownTheilSen should always give trend in units/year (would use xlab if supplied)slope.text in TheilSen to allow users to add their
own text i.e. not the default "units/year"aqStatsimportAURNperiod = "months" in summaryPlotmethod = "hexbin" in scatterPlotairbaseStatswindRose when all calmscatterPlotwindflow to scatterPlot and timePlot to allow wind
flow plotssmoothTrendpollutionRose for option normalise to show
probability by wind sector (0 to 1).timeAverage now has an option type similar to other functions. A
common use would be to apply timeAverage to a data frame with
multiple sites where there is a column representing site name
e.g. type = "site".trajLevel and
trajPlot.trend to TheilSen to control how the trend lines
are drawn.calendarPlot when partial month availablecalendarPlot, don't need to cut data firstnpoints option to trajPlot to control time spacing of dots shown on back trajectoriesstatistic = "frequency in timeAveragetimeProp due to point abovetimeVariation with type = "season" when space in pollutant namesummaryPlot - useful for shorter time
seriesdplyr to speed up some of the code
e.g. timeAveragex.inc and y.inc if not supplied by user in scatterPlottrajLevel frequency calculationimportMeta introduced since using dplyrangle.scale to windRose to control placement of radial scale
(helps to avoid clash with wind rose paddles)timePlot when avg.time given (regression)TaylorDiagram to have group of length two. This will show
all group combinations but will only differentiate them by
colour/symbol according to the first grouping variable.timeVariation can now take a ylim list to control the y-limits
on each individual plotmap.res to be "state" to show the US
States.importKCL when date was not at beginning of the yeartimeVariation subsetsscatterPlot for surface modellingimportKCL is file of full yearaqStats when multiple pollutants selectedtimeVariation for y references line(s)corPlottrajLevel (winter period not properly
calculated)timePlot,
scatterPlot, timeVariation and add to smoothTrend together
with full control of their properties. Note - ref.x and
ref.y must now be lists; see help file for details.timeAverage for irregular time
intervalspercentileRose with stat = "cpf" and non-default
type (now uses single percentile based on all data, not
each panel)corPlot (thanks to James Durant for
the suggestion)corPlot when type = "default"pollutionRose when annotate =
FALSE.scatterPlot/trajPlot when user
limits suppliedsummaryPlot; some users
supplied "year" resulting in incorrect statisticspollutionRose when statistic =
"prop.mean" (was erroneously percentage)smoothTrend for more control over
names used for plottingcorPlottimeVariation to consider median + quantiles through
option 'statistic'polarPlotimportKCL where dates were filled if two non contiguous
years were chosenpolarPlot when upper is setsmoothTrend/TheilSen
(was >=)polarPlot, scatterPlot,
polarAnnulus when limits within data rangetimeVariation and polarAnnulus (allow any local time zone to be used;
was just GMT/BST before)smoothTrend
and TheilSenpercentileRose by wind direction by default.percentileRosetrajPlot bug when trajectory data was not at 3-hour intervalssmoothTrendscatterPlot surfacespolarPlot when
statistic = "cpf"scatterPlot when x and/or
y are on a log scaletimeAverage when requested
averaging time is < original. Better handling when 'site' is
presentpolarPlotpolarPlot to extract more
source information.polarPlot to down-weight bins with
few data points - alternative to min.binpolarPlottimePlot when pch supplied and group = TRUE.windRose/pollutionRose to control
extent of radial limitstrajPlot and trajLevel and make default
lower resolution. Make default pollutant 'height' (always
present)calendarPlot, use first year if not
supplied, add option month to allow only selected month(s)timeAveragetrajPlotscatterPlotwindRose (thanks to Philippe Barneoud
from Environment Canada for pointing out the need and
solution)pollutionRose when comparing two met
datasets when ws bias is zerotrajLevelscatterPlotcorPlot - main did not worktimeVariation (was
variable clash). Allow more flexibility when group and type
are usedpolarPlot and
scatterPlot (method = "level")percentileRose.pollutionRose documentation about comparing 2 data
sets (first subtracted from second)timeVariation that showed extra NA level for
certain groups/typestrajLevelwindRose bug when wind direction name was not wdimportAURN when pollutant = "all" was specifiedwindRose bug where all data are missingtrajLevel for hexagonal binning
of trajectory frequenciespollutionRose scaling issue, which sometimes missed the
lower intervaltimeAverage data capture issue - was not always setting
data to NAcalendarPlot as other functions (wrong
month order in non-English locales)corPlot - see example in help filepolarPlot when radial variable was negative
and smallpolarPlotpolarPlot and type is defaultwindRose when interval does not
existtimeVariationwindRose for empty panels and panel labelling when
data missingcalendarPlotpollutionRosepolarPlotcorPlotpolarPlots when there is insufficient data
to calculate a smooth surface.timeAverage to avoid variable clash.trajLevelsummaryPlot: type = "density" broken in recent versionstimePlotimportKCL - now consistent with importAURNcalendarPlot - highlight values
above/below a certain threshold.timeAverage when ws was not available but wd waswindRose when paddle = FALSEpolarPlot grid
linesimportKCL that was introduced in 0.5-21smoothTrend when type = "site"windRose now gives mean ws in each panel rather than counttimePlot for more control over date
format on axispollutionRose to control the width of the
segmentstimeVariation to control the order of
weekdays.importAURN to import new ws/wd from pre-calculated WRF
data at AURN sitestimeAverage by removing date.pad.timeAverage for wind speed.polarPlot allowing variables other than
"ws" to be plotted with wind direction.polarPlot/TaylorDiagrampolarPlot concentrations unless upper is suppliedpercentileRosetimeAverageTheilSen and smoothTrendtimeVariationtimeVariation to show difference
between two variables with bootstrap 95% CI in the meanscatterPlot to avoid clash
with x or y axis labels.corPlot, scatterPlot, smoothTrend, linearRelation,
percentileRose, trajPlot, trajLevel, timeVariation, TaylorDiagram,
timePlot, summaryPlot
improved ... handlingcorPlot
added pollutant option, and openair class outputpercentileRose deal with negative dataTheilSen for all estimates for
consistency. May slightly affect some p estimates.calendarPlot, kernelExceed,
MannKendall and conditionalQuantile
improved ... handling minor update to GoogleMapsPlot
windRose and pollutionRose
stat related annotationpolarPlot, ploarFreq and polarAnnulus
improved ... handlingimportMeta to import site meta data from air
pollution networkswindRose/pollutionRose: added statistic option
"abs.count"; improved scaling of segment widths; improved
... handlingtrajPlot, trajLevel for importing
and plotting pre-defined HYSPLIT back trajectories. These
functions are under active development and are for testing
purposes only!corPlot for correlation matricestimePlotwindRose/pollutionRosepercentileRosescatterPlot - not shown for some methodsscatterPlot for binning data with
optional smoothing, plus other code clean-upstimePlot to allow reference lines to
be addedpollutionRose to show contribution
to counts and contribution to the mean. The latter is useful for
displaying those wind directions that make most contribution to the
overall mean. Panel mean is also now shown.importKCLtimeAverage can now expand data to shorter time periods
e.g. hourly to 15-minute. This makes it more flexible to combine data
sets with differing averaging times. For example, daily mean particle
data can be expanded to 1-hour means and combined with an hourly
meteorological data set.timeAveragetimeAverage (did not include wind
speed in calculations). For most data this will make very little
difference, but will be more important for low wind speeds and/or
variable wind directions.smoothTrendsmoothTrend for "month" or "year" averagestimeAveragescatterPlot type = "wd" labels alignedpolarPlot, which can now consider
"mean", "median", "max" (maximum), "frequency". "stdev"
(standard deviation) or "weighted.mean" in a similar way to polarFreqsmoothTrend/MannKendall;
particularly when there are multiple sitestimePlot would fail with
percentiles)smoothTrend, MannKendall, timePlot and scatterPlotimportKCL, added site classification
to the help file and ensured GMT is exported.smoothTrend and MannKendall to
allow flexible y-scales.percentileRose' for flexible plotting of
percentiles by wind directionselectRunning for selecting run lengths of a numeric
variable above a certain threshold.polarPlot to maximise plotting areawindRose and
percentileRose; new option grid.line to control radial axes.summaryPlot and
timePlotwindRose and pollutionRose on radial scalecalendarPlot main/quickText handling.calendarPlot first day of month error.summaryPlot site ordering when more than one site.scatterPlot (plot.type, lwd, and lty)
making it possible to add points and/or linesAdded new function conditionalQuantile for model
evaluation purposes.
Fixed `importAURN` to account for a change in web domain
address at AEA.