/
monitor_ggTimeseries.R
202 lines (173 loc) · 5.61 KB
/
monitor_ggTimeseries.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#' @title Create a timeseries plot for one or more monitors
#'
#' @description
#' This function assembles various layers to create a production-ready
#' timeseries plot for one or more monitors.
#'
#' @param monitor A \emph{mts_monitor} object.
#' @param startdate Desired start date (integer or character in ymd format or
#' POSIXct).
#' @param enddate Desired end date (integer or character in ymd format or
#' POSIXct).
#' @param id vector of deviceDeploymentIDs to include in the plot. If more than
#' one, different monitors will be plotted in different colors.
#' @param style Plot style. \code{small} or \code{large}. \code{style = small}
#' is appropriate for plots 450x450px or smaller; \code{style = large} is
#' appropriate for plots larger than 450x450px.
#' @param title Plot title. If NULL, a suitable title will be constructed.
#' @param timezone Olson timezone name for x-axis scale and date parsing. If
#' NULL the timezone of the specified monitor will be used.
#' @param ... Arguments passed onto \code{\link{ggplot_pm25Timeseries}}.
#'
#' @return A \emph{ggplot} object.
#'
#' @import ggplot2
#' @importFrom rlang .data
#'
#' @export
#'
#' @examples
#' library(AirMonitorPlots)
#'
#' AirMonitor::NW_Megafires %>%
#' monitor_ggTimeseries(
#' startdate = 20150815,
#' enddate = 20150831,
#' id = "d01b991c53583048_160690012_03"
#' )
#'
#' AirMonitor::Carmel_Valley %>%
#' monitor_ggTimeseries(
#' startdate = 20160801,
#' enddate = 20160810
#' )
#'
monitor_ggTimeseries <- function(
monitor,
startdate = NULL,
enddate = NULL,
id = NULL,
style = c("small", "large"),
title = NULL,
timezone = NULL,
...
) {
# ----- Validate Parameters --------------------------------------------------
MazamaCoreUtils::stopIfNull(monitor)
# Convert monitor to tidy structure
if ( AirMonitor::monitor_isValid(monitor) ) {
mts_tidy <- monitor_toTidy(monitor)
} else {
stop("monitor is not a mts_monitor object.")
}
# Check style
style <- match.arg(style)
# Check deviceDeploymentIDs
if ( any(!id %in% unique(mts_tidy$deviceDeploymentID)) ) {
invalidIDs <- id[which(!id %in% unique(mts_tidy$deviceDeploymentID))]
stop(paste0(
"Invalid ids specified. 'monitor' does not contain deviceDeploymentIDs: ",
paste0(invalidIDs, collapse = ", ")
))
}
# Determine the timezone (code borrowed from custom_pm25TimeseriesScales.R)
if ( is.null(timezone) ) {
if ( length(unique(mts_tidy$timezone)) > 1 ) {
timezone <- "UTC"
} else {
timezone <- mts_tidy$timezone[1]
}
}
# Check timezone
if ( !is.null(timezone) ) {
if ( !timezone %in% OlsonNames() ) {
stop("Invalid timezone")
}
}
if ( !is.null(startdate) ) {
startdate <- MazamaCoreUtils::parseDatetime(startdate, timezone = timezone)
if ( startdate > range(mts_tidy$datetime)[2] ) {
stop("startdate is outside of data date range")
}
}
if ( !is.null(enddate) ) {
enddate <- MazamaCoreUtils::parseDatetime(enddate, timezone = timezone)
if ( enddate < range(mts_tidy$datetime)[1] ) {
stop("enddate is outside of data date range")
}
}
# ----- Prepare data ---------------------------------------------------------
if ( !is.null(id) ) {
mts_tidy <- dplyr::filter(mts_tidy, .data$deviceDeploymentID %in% id)
}
pm25LegendLabel <- "Hourly PM2.5 Values"
nowcastLegendLabel <- "NowCast"
if (length(unique(mts_tidy$deviceDeploymentID)) > 1) {
mapping_pm25 <- mapping_nowcast <- aes_(color = ~deviceDeploymentID)
if (is.null(title)) title <- ""
} else {
mapping_pm25 <- aes(color = !!pm25LegendLabel)
mapping_nowcast <- aes(color = !!nowcastLegendLabel)
if (is.null(title)) {
title <- paste0("Hourly PM2.5 Values and NowCast\n",
"Site: ", unique(mts_tidy$locationName))
}
}
# ----- Style ----------------------------------------------------------------
if (style == "large") {
pointsize <- 2
linesize <- 0.8
date_labels <- "%b %d"
minor_break_width <- NULL
base_size <- 15
} else if (style == "small") {
pointsize <- 2
linesize <- 0.8
date_labels <- "%b\n%d"
minor_break_width <- "6 hours"
base_size <- 11
}
# ----- Create plot ----------------------------------------------------------
plot <-
ggplot_pm25Timeseries(
mts_tidy,
startdate = startdate,
enddate = enddate,
includeFullEnddate = FALSE,
date_labels = date_labels,
minor_break_width = minor_break_width,
base_size = base_size,
...
) +
custom_aqiLines(size = 1, alpha = .8) +
geom_pm25Points(mapping_pm25, size = pointsize) +
stat_nowcast(mapping_nowcast, size = linesize) +
custom_aqiStackedBar() +
ggtitle(title)
# ----- Handle legend --------------------------------------------------------
# Add legend when plotting multiple monitors
if (length(unique(mts_tidy$deviceDeploymentID)) == 1) {
values <- c(1, 1)
names(values) <- c(pm25LegendLabel, nowcastLegendLabel)
scale <- scale_color_manual(name = "", values = values, labels = names(values))
guide <- guides(
color = guide_legend(
title = "",
override.aes = list(
color = c(1, 1),
size = c(pointsize, linesize),
linetype = c(NA, 1),
shape = c(16, NA),
alpha = c(0.3, 1)
)
)
)
plot <- plot + scale + guide
# Single monitor
} else {
plot <- plot + scale_color_brewer(palette = "Dark2")
}
plot <- plot + theme_timeseriesPlot_airfire(size = style)
# ----- Return ---------------------------------------------------------------
return(plot)
}