## ----include = FALSE---------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>", dev = "svglite", fig.ext = "svg", fig.width = 7, fig.height = 5 ) # Save par settings and restore on exit (CRAN policy) oldpar <- par(no.readonly = TRUE) knitr::knit_hooks$set(document = function(x) { par(oldpar); x }) ## ----setup-------------------------------------------------------------------- library(areaOfEffect) library(sf) ## ----dataframe-example, eval=FALSE-------------------------------------------- # # Your occurrence data # observations <- data.frame( # species = c("Oak", "Beech", "Pine", "Spruce"), # lon = c(14.5, 15.2, 16.8, 20.0), # lat = c(47.5, 48.1, 47.2, 48.5) # ) # # # Classify relative to Austria # result <- aoe(observations, "Austria") # result$aoe_class # #> [1] "core" "core" "halo" ## ----sf-example, eval=FALSE--------------------------------------------------- # result <- aoe(pts_sf, "AT") ## ----austria-visual, fig.cap="Austria (black) with its area of effect (dashed blue). The halo has equal area to the core."---- # Get Austria and transform to equal-area projection austria <- get_country("AT") austria_ea <- st_transform(austria, "ESRI:54009") # Create a point inside Austria dummy_pt <- st_centroid(austria_ea) # Run aoe() to get geometries (uses buffer method by default) result <- aoe(dummy_pt, austria_ea) geoms <- aoe_geometry(result, "both") # Extract geometries austria_geom <- geoms[geoms$type == "original", ] aoe_geom <- geoms[geoms$type == "aoe", ] # Plot par(mar = c(1, 1, 1, 1), bty = "n") plot(st_geometry(aoe_geom), border = "steelblue", lty = 2, lwd = 1.5) plot(st_geometry(austria_geom), border = "black", lwd = 2, add = TRUE) legend("topright", legend = c("Austria (core)", "Area of Effect"), col = c("black", "steelblue"), lty = c(1, 2), lwd = c(2, 1.5), inset = 0.02) ## ----support------------------------------------------------------------------ support <- st_as_sf( data.frame(id = 1), geometry = st_sfc(st_polygon(list( cbind(c(0, 100, 100, 0, 0), c(0, 0, 100, 100, 0)) ))), crs = 32631 ) ## ----points------------------------------------------------------------------- pts <- st_as_sf( data.frame( id = 1:5, value = c(10, 20, 15, 25, 30) ), geometry = st_sfc( st_point(c(50, 50)), # center st_point(c(10, 10)), # near corner st_point(c(95, 50)), # near edge st_point(c(120, 50)), # outside, in halo st_point(c(250, 250)) # far outside ), crs = 32631 ) ## ----aoe---------------------------------------------------------------------- result <- aoe(pts, support) print(result) ## ----class-------------------------------------------------------------------- result$aoe_class ## ----multiple----------------------------------------------------------------- # Two adjacent regions supports <- st_as_sf( data.frame(region = c("A", "B")), geometry = st_sfc( st_polygon(list(cbind(c(0, 50, 50, 0, 0), c(0, 0, 100, 100, 0)))), st_polygon(list(cbind(c(50, 100, 100, 50, 50), c(0, 0, 100, 100, 0)))) ), crs = 32631 ) # Points that may fall in overlapping AoEs pts_multi <- st_as_sf( data.frame(id = 1:3), geometry = st_sfc( st_point(c(25, 50)), # inside A st_point(c(50, 50)), # on boundary st_point(c(75, 50)) # inside B ), crs = 32631 ) result_multi <- aoe(pts_multi, supports) print(result_multi) ## ----mask-example, fig.cap="AoE with land mask. The AoE is clipped to the land boundary."---- # Create a coastal support support_coast <- st_as_sf( data.frame(id = 1), geometry = st_sfc(st_polygon(list( cbind(c(40, 80, 80, 40, 40), c(20, 20, 60, 60, 20)) ))), crs = 32631 ) # Create land mask (irregular coastline) land_mask <- st_as_sf( data.frame(id = 1), geometry = st_sfc(st_polygon(list(cbind( c(0, 100, 100, 70, 50, 30, 0, 0), c(0, 0, 50, 60, 55, 70, 60, 0) )))), crs = 32631 ) # Create some points pts_coast <- st_as_sf( data.frame(id = 1:4), geometry = st_sfc( st_point(c(60, 40)), # core st_point(c(50, 30)), # core st_point(c(30, 40)), # halo (on land) st_point(c(90, 70)) # would be halo but in sea ), crs = 32631 ) # Apply with mask result_coast <- aoe(pts_coast, support_coast, mask = land_mask) # Get geometries for visualization aoe_masked <- aoe_geometry(result_coast, "aoe") support_geom <- aoe_geometry(result_coast, "original") par(mar = c(1, 1, 1, 1), bty = "n") plot(st_geometry(land_mask), col = NA, border = "steelblue", lwd = 2, xlim = c(-10, 110), ylim = c(-10, 90)) plot(st_geometry(aoe_masked), col = rgb(0.5, 0.5, 0.5, 0.3), border = "steelblue", lty = 2, add = TRUE) plot(st_geometry(support_geom), border = "black", lwd = 2, add = TRUE) # Add points with colors cols <- ifelse(result_coast$aoe_class == "core", "forestgreen", "darkorange") plot(st_geometry(result_coast), col = cols, pch = 16, cex = 1.5, add = TRUE) # Show pruned point plot(st_geometry(pts_coast)[4], col = "gray60", pch = 4, cex = 1.2, add = TRUE) text(85, 75, "SEA", col = "steelblue", font = 2, cex = 1.2) legend("topleft", legend = c("Support", "AoE (masked)", "Coastline", "Core", "Halo", "Pruned"), col = c("black", "steelblue", "steelblue", "forestgreen", "darkorange", "gray60"), lty = c(1, 2, 1, NA, NA, NA), lwd = c(2, 1, 2, NA, NA, NA), pch = c(NA, NA, NA, 16, 16, 4), pt.cex = c(NA, NA, NA, 1.5, 1.5, 1.2), inset = 0.02) ## ----portugal-mask, fig.cap="Portugal with land-masked AoE. The halo extends into Spain but not into the Atlantic."---- # Create a point inside Portugal (approximate center of mainland) dummy <- st_as_sf( data.frame(id = 1), geometry = st_sfc(st_point(c(-8, 39.5))), crs = 4326 ) # Without mask result_no_mask <- aoe(dummy, "PT") aoe_no_mask <- aoe_geometry(result_no_mask, "aoe") # With mask + area=1 for equal land area result_masked <- aoe(dummy, "PT", mask = "land", area = 1) aoe_masked <- aoe_geometry(result_masked, "aoe") # Get support geometry support_geom <- aoe_geometry(result_masked, "original") # Transform to equal area for plotting crs_ea <- st_crs("+proj=laea +lat_0=39.5 +lon_0=-8 +datum=WGS84") aoe_no_mask_ea <- st_transform(aoe_no_mask, crs_ea) aoe_masked_ea <- st_transform(aoe_masked, crs_ea) support_ea <- st_transform(support_geom, crs_ea) # Plot - expand xlim for legend, crop bottom margin bbox <- st_bbox(aoe_no_mask_ea) x_range <- bbox[3] - bbox[1] y_range <- bbox[4] - bbox[2] par(mar = c(1, 1, 1, 1), bty = "n") plot(st_geometry(aoe_no_mask_ea), border = "gray50", lty = 2, lwd = 1.5, xlim = c(bbox[1], bbox[3]), ylim = c(bbox[2] + y_range * 0.25, bbox[4]), axes = FALSE, xaxt = "n", yaxt = "n") plot(st_geometry(aoe_masked_ea), col = rgb(0.3, 0.5, 0.7, 0.3), border = "steelblue", lty = 2, lwd = 1.5, add = TRUE) plot(st_geometry(support_ea), border = "black", lwd = 2, add = TRUE) legend("topright", legend = c("Portugal", "AoE (unmasked)", "AoE (land only)"), col = c("black", "gray50", "steelblue"), lty = c(1, 2, 2), lwd = c(2, 1.5, 1.5), bty = "n", inset = 0.05) ## ----scale-------------------------------------------------------------------- # Default: equal core/halo areas (scale = sqrt(2) - 1) result_default <- aoe(pts, support) # Scale = 1: larger halo (3:1 area ratio) result_large <- aoe(pts, support, scale = 1) ## ----area-param, eval=FALSE--------------------------------------------------- # # Halo area = original area (same as scale = sqrt(2) - 1 without mask) # result <- aoe(pts, support, area = 1) # # # Halo area = half of original # result <- aoe(pts, support, area = 0.5) ## ----area-masked, eval=FALSE-------------------------------------------------- # # Target area = 1 means halo = original, even after coastline clipping # result <- aoe(pts, support, area = 1, mask = "land") ## ----expand-example----------------------------------------------------------- # Create sparse data set.seed(42) pts_sparse <- st_as_sf( data.frame(id = 1:15), geometry = st_sfc(c( lapply(1:5, function(i) st_point(c(runif(1, 20, 80), runif(1, 20, 80)))), lapply(1:10, function(i) st_point(c(runif(1, -50, 150), runif(1, -50, 150)))) )), crs = 32631 ) # Expand until at least 10 points are captured result_expand <- aoe_expand(pts_sparse, support, min_points = 10) ## ----expand-caps, eval=FALSE-------------------------------------------------- # # Strict caps # result <- aoe_expand(pts, support, # min_points = 50, # max_area = 1.5, # halo ≤ 1.5× original # max_dist = 500) # max 500m expansion ## ----expand-info-------------------------------------------------------------- info <- attr(result_expand, "expansion_info") info ## ----sample-example----------------------------------------------------------- # Create imbalanced data (many core, few halo) set.seed(42) pts_imbal <- st_as_sf( data.frame(id = 1:60), geometry = st_sfc(c( lapply(1:50, function(i) st_point(c(runif(1, 10, 90), runif(1, 10, 90)))), lapply(1:10, function(i) st_point(c(runif(1, 110, 140), runif(1, 10, 90)))) )), crs = 32631 ) result_imbal <- aoe(pts_imbal, support, scale = 1) # Default: balance core/halo (downsamples core to match halo) set.seed(123) balanced <- aoe_sample(result_imbal) table(balanced$aoe_class) ## ----sample-custom------------------------------------------------------------ # Fixed n with 70/30 split set.seed(123) sampled <- aoe_sample(result_imbal, n = 20, ratio = c(core = 0.7, halo = 0.3)) table(sampled$aoe_class) ## ----sample-support, eval=FALSE----------------------------------------------- # sampled <- aoe_sample(result_multi, by = "support") ## ----border-example, fig.cap="Border classification. Points are classified by side (blue vs orange) and distance (core vs halo) from the border line."---- # Create a diagonal border line border_line <- st_as_sf( data.frame(id = 1), geometry = st_sfc(st_linestring(matrix( c(0, 0, 100, 100), ncol = 2, byrow = TRUE ))), crs = 32631 ) # Create points on both sides set.seed(42) pts_border <- st_as_sf( data.frame(id = 1:30), geometry = st_sfc(c( # Points on side 1 (above the line) lapply(1:15, function(i) st_point(c(runif(1, 10, 90), runif(1, 10, 90) + 20))), # Points on side 2 (below the line) lapply(1:15, function(i) st_point(c(runif(1, 10, 90), runif(1, 10, 90) - 20))) )), crs = 32631 ) # Classify by distance from border result_border <- aoe_border( pts_border, border_line, width = 30, side_names = c("north", "south") ) # Built-in plot method plot(result_border) ## ----border-area, eval=FALSE-------------------------------------------------- # # Each side's core zone has area 5000 (in CRS units²) # result <- aoe_border(pts, border, area = 5000) ## ----border-sample------------------------------------------------------------ # Balance by side (equal north/south) set.seed(123) balanced_side <- aoe_sample(result_border, ratio = c(north = 0.5, south = 0.5)) table(balanced_side$side) # Balance by distance class set.seed(123) balanced_class <- aoe_sample(result_border, by = "class") table(balanced_class$aoe_class) ## ----summary------------------------------------------------------------------ aoe_summary(result)