Here we apply Rcaline
to San Francisco data provided by the Bay Area Air Quality Management District (BAAQMD). This vignette illustrates the basic steps of constructing, running, and visualizing a model with Rcaline
. We discuss how to load, visualize, and export model data with the use of several third-party packages.
library(knitr) options(width = 80, digits = 3, continue = " ") opts_chunk$set(echo = FALSE, message = FALSE, cache = TRUE) knit_hooks$set(small.mar = function(before, options, envir) { if (before) par(mar = c(.1, .1, .1, .1)) # smaller margin on top and right })
Example traffic data has been supplied for you in the form of a SpatialLinesDataFrame
called SF_highways
.
library(Rcaline)
data(SanFrancisco, package = "Rcaline") summary(geometry(SF_highways)) str(SF_highways@data)
It was originally imported from a shapefile. Should you have traffic data of your own in shapefile format, you can use readOGR()
from the rgdal
package to import it. The next section will describe how to set it up for use in your model.
See
help(readOGR, package = "rgdal")
andhelp(SpatialLinesDataFrame, package = "sp")
for more on these data structures and their applications.
Another example input comes in the form of an ASCII file, met_5801.isc
, that contains $24 \times 365 = 8760$ records containing a year's worth of data on:
... at 1-hour scale.
met_file <- system.file("extdata", "BayArea", "Meteorology", "met_5801.isc", package = "Rcaline") met_data <- ISCFile(met_file)
If you have meteorology data of your own in the same "ISC-ready" file format, you can import it with ISCFile(...)
and use it instead. You can also run Rcaline
with "manually" specified meteorology. Just modify the code above, substituting your own data.frame
for met_data
. (Make sure that your data.frame
has appropriately named columns. See help(Meteorology)
for more.)
SF_conditions <- Meteorology(met_data, use = "urban") summary(SF_conditions)
You will be warned if there are any wind speeds less than 1.0 m/s. CALINE3 is premised on the assumption that advection dominates diffusion---in other words, that conditions are not calm.
Rcaline
will still compute results for non-calm conditions. Care should be taken to ensure that the proportion of calm conditions is very small.
SF_highways
has an attribute TRVol2009
that describes the Annual Average Daily Traffic (AADT) volume along polylines, each of which may be composed of one or more straight segments or links.
CALINE3
models dispersion at the level of individual links, so Rcaline
splits each polyline into individual segments, assigning to each segment the attributes of the polyline to which it belonged.
SF_links <- FreeFlowLinks( SF_highways, vehiclesPerHour = TRVol2009 / 24, # convert from 24 h to 1 h scale emissionFactor = 1.0, # in grams per vehicle-mile width = 30.0) # in meters
Model inputs can be specified in the form of expressions that depend on the data (as with vehiclesPerHour
, above), or as fixed numeric constants. In this example---for illustrative purposes only---we specify a unform road width ($30$ meters) and a uniform emission factor ($1.0 \frac{g}{veh \cdot mi}$).
In California, pollutant-specific emission factors can be obtained from EMFAC.
Receptors are the locations at which the dispersed pollutant concentration will be calculated. Since CALINE3
is a steady-state Gaussian plume model, rather than a numerical grid-based model, the locations of receptors can be freely specified.
You might want to compute predicted concentrations at a set of specific locations, like geocoded street addresses. Load them as follows:
## Not run: # fn <- file.choose() # layer_name <- sub("\\.shp$", "", fn, ignore.case = TRUE) # features <- spTransform(readOGR(dirname(fn), layer_name), CRS(proj4string(SF_county))) # stopifnot(inherits(features, "SpatialPoints")) # receptors <- Receptors(features)
Here we show how to generate receptors on a regular Cartesian grid within 1 km of highway centerlines:
SF_receptor_grid <- ReceptorGrid( SF_links, resolution = 250, maxDistance = 1e3)
However, relying on a Cartesian grid can result in apparent "hot spots" that are just the result of certain grid points falling very close to links.
An alternative approach is to generate receptor locations at regular distances from the road network. This approach also allows receptors to be packed more densely close to the roadway, so that modeling resources can be concentrated on the more interesting locations (i.e., those closer to the roadways).
SF_receptor_rings <- ReceptorRings( SF_links, distances = c(100, 250, 500, 1000))
plot(SF_county, col = "light gray", border = NA) lines(SF_links) points(SF_receptor_grid, pch = '+', cex = 0.5) plot(SF_county, col = "light gray", border = NA) lines(SF_links) points(SF_receptor_rings, pch = '+', cex = 0.5)
If you were interested in estimating aggregate exposures at a population level, you could also construct an irregular grid by sampling locations from predefined regions, like ZIP codes or census tracts.
For detailed information on terrain and pollutant characteristics, consult the CALINE3 User's Guide. Here we specify some reasonable default values:
urban_terrain <- Terrain(surfaceRoughness = 80.0)
fine_PM <- Pollutant("PM2.5", molecularWeight = NA) show(fine_PM)
The CALINE3 algorithm is CPU-intensive. Although it is beyond the scope of this example, you can use the foreach
package to do computations in parallel, using multiple cores or networked hosts. (A future vignette will illustrate this technique.)
We use the predict
method to actually run the model. Since the model will actually be run once for every meteorological condition we supply ($n = r nrow(SF_conditions)
$), it can be helpful to make a first pass using only a small sample of the meteorology.
sample_rows <- function(x, p) { x[sample(1:nrow(x), p * nrow(x)),] } SF_model <- Caline3Model( SF_links, sample_rows(SF_conditions, p = 0.01), SF_receptor_rings, urban_terrain, fine_PM) pred_matrix <- predict(SF_model, units = "mg/m^3")
The result of running the model is an $M \times N$ HourlyConcentrations
matrix, where $M$ is the number of meteorological conditions and $N$ is the number of receptors. Each cell describes the predicted concentration at that receptor during those conditions.
The aggregate.HourlyConcentrations
helper function will compute several statistics by default, including the mean and maximum.
pred_stats <- aggregate(pred_matrix) show(colnames(pred_stats))
Casting the resulting AggregatedConcentrations
object to a SpatialPointsDataFrame
then re-binds the statistics to the receptor locations.
pred_spatial <- as(pred_stats, "SpatialPointsDataFrame") show(pred_spatial[1:3, c("distance", "min", "mean", "median", "max")])
Afterwards, we can select a statistic of interest and explore the distribution. Here we focus on exploring results graphically, although they could also be tabulated or subjected to statistical tests.
Within the results is a variable, distance
, that describes the distance-to-roadway for each receptor. (Recall that we specified these distances when constructing the receptor grid.) Using this, we can group the receptors into specific classes and explore the distribution of predicted concentrations in each.
suppressPackageStartupMessages(library(ggplot2)) ggplot(pred_spatial@data) + geom_histogram(aes(mean)) + facet_wrap(~ distance)
Rcaline
provides a helper function ggplot.AggregatedConcentrations
that automates the re-binding of predicted concentrations with receptor locations, as well as the plotting of model inputs such as receptors and links. Here is an example of a "bubble" plot generated with ggplot.AggregatedConcentrations
:
label_kilo <- function (x) sprintf("%0.0f", x / 1000.0) ggplot(pred_stats) + geom_point(aes(x, y, size = mean, color = mean, order = mean, alpha = mean)) + scale_color_gradient(expression(paste(bold(hat(Z)(s)), " ", mg/m^3)), low = "#BEBEBE00", high = "brown") + scale_size(guide = "none") + scale_alpha(guide = "none") + scale_x_continuous("UTM10 Easting (km)", labels = label_kilo) + scale_y_continuous("UTM10 Northing (km)", labels = label_kilo)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.