| Title: | Distance-Weighted Landscape Composition in Buffers Around Point Locations |
|---|---|
| Description: | Characterises the environment surrounding point locations by computing land-cover composition within circular buffers directly from vector polygons, without conversion to a raster grid. For each site and each class it returns the exact surface area inside the buffer and a distance-decay weighted "effective" area in which the kernel is integrated over polygon geometry rather than evaluated at the polygon centroid, avoiding the large bias the centroid approximation introduces for elongated features passing close to the site. Polygons may overlap, so class areas are not constrained to sum to the buffer area. Intended for buffer-based exposure assessment and fine-scale spatial epidemiology, where the relevant scale is tens of metres and global land-cover products are too coarse: land-use regression around air-quality monitors, green space around residential addresses, vector-surveillance traps, and comparable designs. The classification dictionary is user-supplied, and point features and distances to off-buffer reference features are recorded alongside the areas. |
| Authors: | Michele Planta [aut, cre] (ORCID: <https://orcid.org/0009-0000-3353-5368>) |
| Maintainer: | Michele Planta <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 1.3.0 |
| Built: | 2026-09-21 11:12:53 UTC |
| Source: | https://github.com/mplanta-lab/bufferscape |
Runs buffer_composition() over each KML in a directory, writes one workbook
containing every table, one map per sampling site, and the composition and
container charts.
batch_composition( dir = NULL, out_dir = file.path(dir, "Results"), pattern = "\\.(kml|gpkg)$", make_maps = TRUE, make_charts = TRUE, basemap = "none", map_zoom = NA, radii = c(20, 30, 40, 50), kernel = "exponential", lambda = 45, grid_res = 1, epsg = 31983, secondary = "weighted", sec_weight = 0.3, categories = NULL, palette = "aerial", metrics = c("exact", "weighted", "centroid", "distance"), label_ids = 0, kml_dir = NULL )batch_composition( dir = NULL, out_dir = file.path(dir, "Results"), pattern = "\\.(kml|gpkg)$", make_maps = TRUE, make_charts = TRUE, basemap = "none", map_zoom = NA, radii = c(20, 30, 40, 50), kernel = "exponential", lambda = 45, grid_res = 1, epsg = 31983, secondary = "weighted", sec_weight = 0.3, categories = NULL, palette = "aerial", metrics = c("exact", "weighted", "centroid", "distance"), label_ids = 0, kml_dir = NULL )
dir |
Folder holding the |
out_dir |
Output folder; defaults to |
pattern |
Regular expression selecting files. The default takes both
|
make_maps, make_charts
|
Produce figures. |
basemap, map_zoom
|
Passed to the map functions. |
radii, kernel, lambda, grid_res, epsg, secondary, sec_weight
|
Passed to
|
categories |
A category dictionary; see |
palette |
Passed to |
metrics |
Which surface metrics the workbook should carry; see
|
label_ids |
Print the class id on this many of the largest polygons in each map. |
kml_dir |
Deprecated name for |
A KML may hold one, two or three sampling points. Buffer polygons are named
per point (VP_9_Buffer), while land-cover polygons and water containers are
a shared pool for the whole file; each point takes its own elements
geometrically, against its own buffer, so per-site outputs never
contaminate one another.
One malformed file does not stop the batch: it is logged and skipped. Sites
whose name appears in more than one file are reported in a
duplicate_traps sheet, and their figures are given distinct filenames.
Invisibly, a list with wide, long, tanks, distances, qc,
summary, out_dir and failed.
dir.create(d <- tempfile()) file.copy(system.file("extdata", "example_site.kml", package = "bufferscape"), d) out <- batch_composition(d, radii = 50, grid_res = 5, make_maps = FALSE, make_charts = FALSE) out$summarydir.create(d <- tempfile()) file.copy(system.file("extdata", "example_site.kml", package = "bufferscape"), d) out <- batch_composition(d, radii = 50, grid_res = 5, make_maps = FALSE, make_charts = FALSE) out$summary
Reads one or more KML files, and for every sampling point returns the land-cover composition of a circular buffer around it, computed directly from vector polygons.
buffer_composition( path = NULL, categories = NULL, driver = "KML", epsg = 31983, radii = 50, kernel = "exponential", lambda = 45, grid_res = 1, secondary = c("weighted", "primary", "both"), secondary_weight = 0.3, trap_pattern = "^[A-Za-z]{2,}[_ -]?\\d+$", tank_pattern = "tank|caixa|reservat", tank_open_pattern = "open|abert|descoberta|sem[_ ]?tampa", pool_pattern = "pool|piscina|swimming", check_kml_buffer = TRUE, out_xlsx = NULL, verbose = TRUE, kml = NULL )buffer_composition( path = NULL, categories = NULL, driver = "KML", epsg = 31983, radii = 50, kernel = "exponential", lambda = 45, grid_res = 1, secondary = c("weighted", "primary", "both"), secondary_weight = 0.3, trap_pattern = "^[A-Za-z]{2,}[_ -]?\\d+$", tank_pattern = "tank|caixa|reservat", tank_open_pattern = "open|abert|descoberta|sem[_ ]?tampa", pool_pattern = "pool|piscina|swimming", check_kml_buffer = TRUE, out_xlsx = NULL, verbose = TRUE, kml = NULL )
path |
Path, or vector of paths, to |
categories |
A category dictionary. |
driver |
GDAL driver. Leave as |
epsg |
EPSG code of a projected CRS in metres. Default 31983 (SIRGAS 2000 / UTM 23S), appropriate for Rio de Janeiro. |
radii |
Buffer radii in metres. The largest is treated as primary. |
kernel, lambda
|
Distance-decay kernel and its scale; see
|
grid_res |
Resolution in metres of the grid used to integrate the kernel over polygon geometry. Smaller is more exact and slower; 1 m is ample for a 50 m buffer. |
secondary |
How to treat a hyphenated code such as |
secondary_weight |
Share of the area given to the secondary class under
|
trap_pattern, tank_pattern, tank_open_pattern, pool_pattern
|
Regular
expressions matching placemark names for sampling points, water tanks,
the unsealed subset of tanks, and swimming pools. Pools are tested first,
so a name like |
check_kml_buffer |
If |
out_xlsx |
Optional path; writes every table to a workbook. |
verbose |
Print progress. |
kml |
Deprecated name for |
A list with elements wide (one row per site, modelling-ready),
long (site x category x radius), tanks, distances, qc,
unmatched_ids, meta, categories, and the projected sf layers
traps, polygons, tanks_sf, pools_sf, lines, buffer_kml.
File names carry no meaning. Sites are identified from the placemark names
inside the file, so PU_1e2.kml, PU_1_2.kml, PU1ePU2.kml and
PU 1 et 2.kml all behave identically; the name is used only to label the
source_file column and to disambiguate a site that appears in two files.
What matters is that each sampling point is named in a form
trap_pattern matches: PU_1, PU1, PU-1 and PU 1 are all accepted.
Polygons may overlap – a tree crown over a roof is both – so category areas are not constrained to sum to the buffer area and proportions are not computed. A buffer can exceed 100% classified.
The weighted area area_w integrates the kernel over each polygon on a
regular grid, then rescales by the exact sf::st_area(). Evaluating the
kernel at the polygon centroid instead – reported as area_w_centroid for
comparison – is badly biased for elongated features passing close to the
sampling point, whose centroid sits at while most of their
area does not. On Maré data the bias reaches 45% for an internal road, while
compact roofs stay under 1%.
GDAL defaults to LIBKML, which overrides <name> with an ExtendedData field
of the same name. A buffer polygon named VP_21_Buffer then reads as
VP_21, is no longer recognised as reference geometry, and silently enters
the land-cover totals as an unclassified polygon. Forcing driver = "KML"
reads <name> correctly.
Unparseable descriptions, category codes absent from the dictionary, and
points matching no pattern all raise warnings and are reported in the qc
and unmatched_ids tables. Distances that were not measured stay NA and
are never coerced to zero.
batch_composition() to process a folder, class_dictionary() for
custom dictionaries.
kml <- system.file("extdata", "example_site.kml", package = "bufferscape") res <- buffer_composition(kml, radii = 50, grid_res = 5, verbose = FALSE) res$tanks head(res$long[res$long$area_m2 > 0, c("full_name", "area_m2", "area_w")])kml <- system.file("extdata", "example_site.kml", package = "bufferscape") res <- buffer_composition(kml, radii = 50, grid_res = 5, verbose = FALSE) res$tanks head(res$long[res$long$area_m2 > 0, c("full_name", "area_m2", "area_w")])
Recomputes, for every polygon intersecting every buffer, the distance-decay weight two ways – integrated over the polygon's geometry, and evaluated at its centroid – and reports the discrepancy alongside shape descriptors.
centroid_bias( kml, categories = NULL, driver = "KML", epsg = 31983, radii = 50, kernel = "exponential", lambda = 45, grid_res = 1, compact_cut = 0.4, trap_pattern = "^[A-Za-z]{2,}[_ -]?\\d+$", verbose = TRUE )centroid_bias( kml, categories = NULL, driver = "KML", epsg = 31983, radii = 50, kernel = "exponential", lambda = 45, grid_res = 1, compact_cut = 0.4, trap_pattern = "^[A-Za-z]{2,}[_ -]?\\d+$", verbose = TRUE )
kml |
Path, or vector of paths, to KML files. |
categories |
A category dictionary. |
driver |
GDAL driver. Leave as |
epsg |
EPSG code of a projected CRS in metres. Default 31983 (SIRGAS 2000 / UTM 23S), appropriate for Rio de Janeiro. |
radii |
Buffer radii in metres. The largest is treated as primary. |
kernel, lambda
|
Distance-decay kernel and its scale; see
|
grid_res |
Resolution in metres of the grid used to integrate the kernel over polygon geometry. Smaller is more exact and slower; 1 m is ample for a 50 m buffer. |
compact_cut |
Compactness below which a polygon is called elongated. |
trap_pattern |
Regular expression matching the placemark names of sampling points. |
verbose |
Print progress. |
This is the diagnostic behind the claim that centroid weighting is unsafe. The bias is not uniform: it is negligible for compact features and large for elongated ones that pass close to the site, because such a polygon's centroid can sit almost on the sampling point while most of its area does not.
A tibble, one row per polygon per site per radius:
Ovitrap_ID, radius_m, source_file
identifiers
id_primary, label_en
class
area_m2clipped area inside the buffer
d_centroiddistance from the site to the polygon centroid (m)
d_min, d_max
nearest and farthest distance from the site to the polygon (m)
centroid_insideFALSE when the polygon's centroid falls outside
the polygon, as happens for concave features. The centroid weight is then
evaluated at a point that is not part of the feature.
compactness; 1 for a circle, towards 0 as the
outline becomes elongated or convoluted
d_meanarea-weighted mean distance from the site to the polygon (m). This is the quantity the centroid is standing in for.
centroid_offset(d_mean - d_centroid) / lambda; how far the
centroid sits from the polygon's mean distance, in kernel scale lengths.
This is the mechanistic driver of the bias: a polygon whose centroid is
much nearer than its typical point gets over-weighted.
reach_ratio(d_max - d_min) / (d_centroid + 1); the span of the
polygon relative to its centroid distance.
geometry_class"compact" or "elongated", split at
compact_cut
w_integrated, w_centroid
mean kernel weight, both ways
area_w, area_w_centroid
weighted areas
w_nearest, area_w_nearest, bias_nearest_pct
the same, for a weight taken at the polygon's nearest point. Included because it is the obvious alternative to the centroid; it is an upper bound on the mean weight and overestimates more than the centroid does, on both compact and elongated polygons.
bias_pctkml <- system.file("extdata", "example_site.kml", package = "bufferscape") b <- centroid_bias(kml, radii = 50, grid_res = 5) summarise_centroid_bias(b)kml <- system.file("extdata", "example_site.kml", package = "bufferscape") b <- centroid_bias(kml, radii = 50, grid_res = 5) summarise_centroid_bias(b)
A dictionary maps the numeric codes written in the <description> field of
each digitised polygon to a named land-cover category. Everything downstream
– the area columns, the map palette, the legend labels – is driven by this
table, so the workflow is not tied to any one classification scheme.
class_dictionary(x = NULL)class_dictionary(x = NULL)
x |
One of:
|
The dictionary shipped with the package, mare_categories, is the 29-class schema developed for the Complexo da Maré ovitrap study. Supply your own to work in a different setting.
A dictionary must contain:
idinteger, unique, positive. The code written in the KML
<description> field.
categorycharacter. A coarse grouping (for example "cobertura",
"vegetacao"). Used for aggregation and for column names.
descriptioncharacter. The specific class within the grouping.
and may optionally contain:
label_encharacter. The label printed on maps, charts and
legends. Defaults to category description.
fillcharacter. A hex colour for the map. If absent, a colour is generated. Supplying your own is strongly preferred for land cover, because a generated ramp assigns arbitrary hues that carry no meaning.
patterncharacter, one of "none", "dots", "dots2",
"diag", "diag2", "cross", "horiz", "vert", "grid".
Overprints a texture so that classes sharing a base colour stay
distinguishable. Defaults to "none".
A full_name column (id_category_description) is added automatically and
is what the output column names are built from.
A data.frame with columns id, category, description,
label_en, fill, pattern and full_name.
mare_categories for the built-in schema.
# the built-in 29-class schema head(class_dictionary()) # a minimal custom dictionary own <- data.frame( id = 1:3, category = c("water", "built", "vegetation"), description = c("pond", "roof", "canopy"), fill = c("#2C7FB8", "#BDBDBD", "#31A354") ) class_dictionary(own)# the built-in 29-class schema head(class_dictionary()) # a minimal custom dictionary own <- data.frame( id = 1:3, category = c("water", "built", "vegetation"), description = c("pond", "roof", "canopy"), fill = c("#2C7FB8", "#BDBDBD", "#31A354") ) class_dictionary(own)
Turns a class dictionary into the fill and pattern used by the map and
chart functions. Separating this from the drawing code means a scheme can be
inspected, tested and swapped without touching a figure.
class_palette( categories = class_dictionary(), scheme = c("aerial", "colorblind", "viridis", "greyscale"), hues = NULL )class_palette( categories = class_dictionary(), scheme = c("aerial", "colorblind", "viridis", "greyscale"), hues = NULL )
categories |
A dictionary, as returned by |
scheme |
One of:
|
hues |
Optional character vector of hex colours to use as the group
hues under |
A data.frame with columns id, fill and pattern.
A palette of 29 nominal colours cannot be made safe for colour-vision deficiency; the perceptual space is not large enough, and any such palette will contain pairs that converge under deuteranopia. Measured on mare_categories, even a palette built entirely from colour-vision-safe primaries leaves a minimum pairwise distance of roughly 4 under simulated deuteranopia, below a comfortable discrimination threshold.
The "colorblind" scheme therefore stops asking colour to do the whole job.
Colour distinguishes the coarse group only – 12 of them for
mare_categories, where the minimum distance under simulated deuteranopia
rises to about 7.6 – and within a group, members are separated by a
lightness step and by a distinct texture. No class depends on hue as its only
cue, which is the property that makes a figure accessible, and it also
survives greyscale printing.
If a dictionary has more groups than available hues, the bank is cycled with a lightness offset and a warning is issued: at that point the grouping is probably too fine to carry by colour at all.
class_dictionary(), map_composition()
# the appearance-matched default head(class_palette()) # colour-vision-safe alternative head(class_palette(scheme = "colorblind")) # with a custom dictionary own <- data.frame(id = 1:4, category = c("water", "water", "built", "veg"), description = c("pond", "channel", "roof", "canopy")) class_palette(own, scheme = "colorblind")# the appearance-matched default head(class_palette()) # colour-vision-safe alternative head(class_palette(scheme = "colorblind")) # with a custom dictionary own <- data.frame(id = 1:4, category = c("water", "water", "built", "veg"), description = c("pond", "channel", "roof", "canopy")) class_palette(own, scheme = "colorblind")
Weights are bounded on with , so a weighted area is
always between zero and the true area and is directly interpretable as an
"effective" area.
decay_kernel(d, kernel = "exponential", lambda = 45)decay_kernel(d, kernel = "exponential", lambda = 45)
d |
Numeric vector of distances, in the units of the projected CRS (metres for a UTM zone). |
kernel |
One of |
lambda |
Decay scale, in the same units as |
This matters: the ratios area / d and area / d^2 sometimes used for the
same purpose are not kernels. They diverge as a polygon approaches the
sampling point, have no upper bound, are not normalisable, and carry
uninterpretable units, so their coefficients cannot be compared across sites.
The default lambda = 45 follows close-kin genetic estimates of mean
Aedes aegypti dispersal (Jasper et al. 2020, BMC Biology, 45.2 m,
95% CI 39.7-51.3), consistent with a Brazilian mark-release-recapture
estimate of 52.8 m (Winskill et al. 2015). Fine-scale dispersal is
repeatedly described as exponential (Laplacian), which is why that is the
default form. For another taxon or another process, set lambda from the
relevant literature, and report a sensitivity analysis over a grid of values
compared by AIC.
A numeric vector of weights the same length as d.
decay_kernel(c(0, 25, 50), lambda = 45) decay_kernel(c(0, 25, 50), kernel = "gaussian", lambda = 45)decay_kernel(c(0, 25, 50), lambda = 45) decay_kernel(c(0, 25, 50), kernel = "gaussian", lambda = 45)
Imagery with the buffer and the sampling point, and nothing else.
map_basemap( res, trap, radius = max(res$long$radius_m), basemap = "esri", zoom = NA, file = NULL, width = 8, height = 8, dpi = 300 )map_basemap( res, trap, radius = max(res$long$radius_m), basemap = "esri", zoom = NA, file = NULL, width = 8, height = 8, dpi = 300 )
res |
Output of |
trap |
Name of the sampling point to draw. |
radius |
Buffer radius; defaults to the largest computed. |
basemap |
|
zoom |
Tile zoom level; |
file |
Optional output path. |
width, height, dpi
|
Passed to |
Tiles are requested in the CRS the figure is drawn in, so the raster is never resampled and cannot drift relative to the polygons. Requires maptiles and terra; without them the panel renders white and a warning is issued rather than failing.
Google Earth imagery is not offered: its terms permit alteration only inside Google software, and it cannot be relicensed for an open-access figure.
A ggplot object.
Imagery, polygons and legend in a single panel.
map_combined( res, trap, radius = max(res$long$radius_m), basemap = "none", zoom = NA, top_n = 9, poly_col = "#E02020", file = NULL, width = 13, height = 7.6, dpi = 300 )map_combined( res, trap, radius = max(res$long$radius_m), basemap = "none", zoom = NA, top_n = 9, poly_col = "#E02020", file = NULL, width = 13, height = 7.6, dpi = 300 )
res |
Output of |
trap |
Name of the sampling point to draw. |
radius |
Buffer radius; defaults to the largest computed. |
basemap, zoom
|
See |
top_n |
Number of classes listed individually in the legend; the rest are pooled as "other categories". |
poly_col |
Outline and fill colour used for every polygon. |
file |
Optional output path. |
width, height, dpi
|
Passed to |
A ggplot object.
A white-ground figure: polygons coloured by category, water containers, the buffer, and a legend panel giving the surface composition, the container counts and the distances to reference features.
map_composition( res, trap, radius = max(res$long$radius_m), palette = "aerial", top_n = 10, fill_alpha = 0.55, patterns = TRUE, show_unclassified = TRUE, label_ids = 0, legend = c("composition", "containers", "key"), title = NULL, subtitle = NULL, caption = NULL, file = NULL, width = 13, height = 8, dpi = 300 )map_composition( res, trap, radius = max(res$long$radius_m), palette = "aerial", top_n = 10, fill_alpha = 0.55, patterns = TRUE, show_unclassified = TRUE, label_ids = 0, legend = c("composition", "containers", "key"), title = NULL, subtitle = NULL, caption = NULL, file = NULL, width = 13, height = 8, dpi = 300 )
res |
Output of |
trap |
Name of the sampling point to draw. |
radius |
Buffer radius; defaults to the largest computed. |
palette |
Colours and textures. A scheme name – |
top_n |
Number of classes listed individually in the legend; the rest are pooled as "other categories". |
fill_alpha |
Fill transparency. Semi-transparent fills let overlapping polygons show through, which is the point. |
patterns |
Draw the textures defined by the palette. |
show_unclassified |
Draw and quantify the part of the buffer covered by no polygon, hatched in grey. |
label_ids |
Print the class id on this many of the largest polygons.
|
legend |
Which legend sections to show: any of |
title, subtitle, caption
|
Text overrides. |
file |
Optional output path. |
width, height, dpi
|
Passed to |
Polygons are clipped to a square frame set outside the buffer, so features overshoot the circle as they do in the imagery but never run across the legend. Overlapping polygons are drawn largest-first, so small features stay visible. Patterns separate classes that share a base material: dots for debris, crosshatch for degraded mixed slab, diagonals for active construction, and grey hatching for unclassified ground.
A ggplot object, invisibly written to file if given.
The return value is an ordinary ggplot object, so anything not exposed
as an argument can be added afterwards – + ggplot2::labs(),
+ ggplot2::theme(), further layers. One exception is worth knowing: the
legend is drawn as text inside the panel, not as a ggplot guide, so
theme(legend.*) has no effect on it. That is why legend, top_n and
title are explicit arguments.
Drawing deliberately avoids geom_sf(), coord_sf() and composition
packages. Geometry is converted to plain coordinates and drawn with
ggplot2::geom_polygon() under ggplot2::coord_fixed(), which is the most
version-stable combination and cannot lose layers to CRS renegotiation at
render time.
kml <- system.file("extdata", "example_site.kml", package = "bufferscape") res <- buffer_composition(kml, radii = 50, grid_res = 5, verbose = FALSE) if (requireNamespace("ggplot2", quietly = TRUE)) map_composition(res, "SITE_1")kml <- system.file("extdata", "example_site.kml", package = "bufferscape") res <- buffer_composition(kml, radii = 50, grid_res = 5, verbose = FALSE) if (requireNamespace("ggplot2", quietly = TRUE)) map_composition(res, "SITE_1")
The land-cover classification schema developed for oviposition-trap surveillance in the Complexo da Maré favela complex, Rio de Janeiro. It resolves distinctions that global land-cover products cannot at this scale: fibrocement against sealed and unsealed concrete slab, individual water containers, narrow alleys, polluted open channels, and active construction.
mare_categoriesmare_categories
A data frame with 28 rows and 6 columns:
integer code written in the KML <description> field
one of the nine thematic groups
specific class, in Portuguese
English label used on figures
hex colour, chosen to echo the appearance in aerial imagery
overprinted texture: "none", "dots", "dots2",
"diag" or "cross", used where classes share a base material
The 28 classes are arranged in nine thematic groups, given by category.
Agreement between interpreters is reported at both levels, because the groups
are better populated than the individual classes and the difference shows how
much disagreement is confusion within a group rather than across groups.
Class 29 (PVC roofing) was present in an early draft and was removed once the nomenclature stabilised; identifiers stayed contiguous, so no other class was renumbered.
Planta M. Qualitative assessment schema for urban polygons in Rio de Janeiro favelas, interpreted from high-resolution aerial imagery.
head(mare_categories) subset(mare_categories, pattern != "none")head(mare_categories) subset(mare_categories, pattern != "none")
Digitised polygons carry their class in the KML <description> field. A
single code ("7") is unambiguous; a hyphenated pair ("8-6") records a
primary and a secondary class where the interpreter was uncertain or the
surface is genuinely mixed.
parse_class_codes(x)parse_class_codes(x)
x |
Character vector of description fields. |
Naive coercion silently destroys these: as.numeric("8-6") is NA, and an
inner join on NA drops the polygon without warning. In the Maré data that
would discard roughly a fifth of all polygons, and non-randomly – the
ambiguous ones are the mixed-material and transitional surfaces.
A tibble with id_primary, id_secondary and
n_codes.
parse_class_codes(c("7", " 6 ", "8-6", "5 / 7", "", NA))parse_class_codes(c("7", " 6 ", "8-6", "5 / 7", "", NA))
One stacked column per sampling site: sealed tanks, unsealed tanks and swimming pools, so that the abundance and the nature of the larval habitat read together at a glance.
plot_group_points( tanks, community, radius = max(tanks$radius_m), file = NULL, width = 11, height = 6, dpi = 300 )plot_group_points( tanks, community, radius = max(tanks$radius_m), file = NULL, width = 11, height = 6, dpi = 300 )
tanks |
The |
community |
Value of the site-name prefix to plot, for example |
radius |
Buffer radius; defaults to the largest present. |
file |
Optional output path. |
width, height, dpi
|
Passed to |
Segments are ordered by how available the water is to an ovipositing female – sealed, then unsealed, then open pool – on a viridis ramp. Sealed tanks usually dominate by an order of magnitude, so a single unsealed tank would otherwise be a one-pixel sliver: white separators keep every segment legible and the count of apparently open containers is called out above each column.
A ggplot object.
Horizontal bars, one per category present in the buffer, ordered by area. Categories absent from the buffer are omitted, so the panel always fits whatever the size of the dictionary.
plot_site_composition( res, trap, radius = max(res$long$radius_m), palette = "viridis", file = NULL, width = 9, height = 6, dpi = 300 )plot_site_composition( res, trap, radius = max(res$long$radius_m), palette = "viridis", file = NULL, width = 9, height = 6, dpi = 300 )
res |
Output of |
trap |
Name of the sampling point. |
radius |
Buffer radius; defaults to the largest computed. |
palette |
|
file |
Optional output path. |
width, height, dpi
|
Passed to |
A ggplot object.
kml <- system.file("extdata", "example_site.kml", package = "bufferscape") res <- buffer_composition(kml, radii = 50, grid_res = 5, verbose = FALSE) if (requireNamespace("ggplot2", quietly = TRUE)) plot_site_composition(res, "SITE_1")kml <- system.file("extdata", "example_site.kml", package = "bufferscape") res <- buffer_composition(kml, radii = 50, grid_res = 5, verbose = FALSE) if (requireNamespace("ggplot2", quietly = TRUE)) plot_site_composition(res, "SITE_1")
Accepts a scheme name, a data frame, or a named vector of colours, and
returns a canonical palette table. Used internally by the figure functions so
they all take palette the same way.
resolve_palette(palette = "aerial", categories = class_dictionary())resolve_palette(palette = "aerial", categories = class_dictionary())
palette |
A scheme name, a |
categories |
The dictionary in use. |
A data.frame with id, fill, pattern.
resolve_palette("colorblind") resolve_palette(c("1" = "#FF0000", "2" = "#00FF00"))resolve_palette("colorblind") resolve_palette(c("1" = "#FF0000", "2" = "#00FF00"))
Collapses centroid_bias() output into the table a methods paper needs:
the distribution of the discrepancy, split by polygon geometry, and the worst
offenders by class.
summarise_centroid_bias( x, by = c("geometry", "class", "site"), area_weighted = FALSE )summarise_centroid_bias( x, by = c("geometry", "class", "site"), area_weighted = FALSE )
x |
Output of |
by |
Grouping: |
area_weighted |
If |
A tibble.
kml <- system.file("extdata", "example_site.kml", package = "bufferscape") b <- centroid_bias(kml, radii = 50, grid_res = 5) summarise_centroid_bias(b, by = "class")kml <- system.file("extdata", "example_site.kml", package = "bufferscape") b <- centroid_bias(kml, radii = 50, grid_res = 5) summarise_centroid_bias(b, by = "class")
Checks the required columns, fills in the optional ones, and returns a dictionary in canonical form. Exported so a custom dictionary can be checked before a long batch run rather than failing partway through.
validate_dictionary(x)validate_dictionary(x)
x |
A |
The completed dictionary.
validate_dictionary( data.frame(id = 1:2, category = c("a", "b"), description = c("x", "y")) )validate_dictionary( data.frame(id = 1:2, category = c("a", "b"), description = c("x", "y")) )
Anchor colours sampled from the viridis colormap, interpolated with
grDevices::colorRampPalette(). Avoids depending on viridisLite for
what is a handful of hex codes.
viridis_colours(n)viridis_colours(n)
n |
Number of colours. |
A character vector of n hex colours.
viridis_colours(5)viridis_colours(5)
Exports the tables from buffer_composition() or batch_composition() to a
multi-sheet .xlsx file, with control over which radii, which surface
metrics and which sheets are included.
write_composition_report( x, path, radii = NULL, metrics = c("exact", "weighted", "centroid", "distance"), per_radius = TRUE, combined = TRUE, long = TRUE, extras = TRUE, key = TRUE, labels = c("full_name", "label_en"), digits = NULL )write_composition_report( x, path, radii = NULL, metrics = c("exact", "weighted", "centroid", "distance"), per_radius = TRUE, combined = TRUE, long = TRUE, extras = TRUE, key = TRUE, labels = c("full_name", "label_en"), digits = NULL )
x |
The list returned by |
path |
Output |
radii |
Radii to include. |
metrics |
Which per-class surface metrics to export, any of:
Defaults to all four. Dropping |
per_radius |
Write one modelling-ready sheet per radius
( |
combined |
Write the single |
long |
Write the tidy site x class x radius table. |
extras |
Write the supporting sheets: |
key |
Write the |
labels |
Class naming in the wide sheets: |
digits |
Round numeric columns to this many decimals. |
Exposed as its own function so that a workbook can be produced from a single
buffer_composition() call, re-exported with different content without
recomputing, or trimmed before sharing.
Invisibly, the named list of data frames that was written.
buffer_composition(), batch_composition()
kml <- system.file("extdata", "example_site.kml", package = "bufferscape") res <- buffer_composition(kml, radii = c(30, 50), grid_res = 5, verbose = FALSE) out <- file.path(tempdir(), "composition.xlsx") if (requireNamespace("writexl", quietly = TRUE)) write_composition_report(res, out, metrics = c("exact", "weighted"))kml <- system.file("extdata", "example_site.kml", package = "bufferscape") res <- buffer_composition(kml, radii = c(30, 50), grid_res = 5, verbose = FALSE) out <- file.path(tempdir(), "composition.xlsx") if (requireNamespace("writexl", quietly = TRUE)) write_composition_report(res, out, metrics = c("exact", "weighted"))