Set Up
Data Preparation
# Keep only the first time each Tree_ID appears
first_measurements <- data %>%
group_by(Tree_ID) %>%
slice(1:n_distinct(Method)) %>% # keep one row per method per first tree occurrence
ungroup()
# Pivot so each method's TH and DBH are in separate columns
wide_data <- first_measurements %>%
select(Tree_ID, Method, TH, DBH) %>%
tidyr::pivot_wider(
names_from = Method,
values_from = c(TH, DBH),
values_fn = list(TH = ~ first(.), DBH = ~ first(.))
)
# Make sure columns are numeric
wide_data <- wide_data %>%
mutate(across(c(TH_Geoslam, TH_Reference, TH_RTC360, TH_BLK360,
TH_Apple,
DBH_Geoslam, DBH_Reference, DBH_RTC360,
DBH_BLK360, DBH_Apple),
~ as.numeric(.)))
Data Analysis
# Calculate accuracy (difference) for each method compared to Reference
accuracy <- wide_data %>%
mutate(
Geoslam_TH_Error = TH_Geoslam - TH_Reference,
RTC360_TH_Error = TH_RTC360 - TH_Reference,
BLK360_TH_Error = TH_BLK360 - TH_Reference,
apple_TH_Error = TH_Apple - TH_Reference,
Geoslam_DBH_Error = DBH_Geoslam - DBH_Reference,
RTC360_DBH_Error = DBH_RTC360 - DBH_Reference,
BLK360_DBH_Error = DBH_BLK360 - DBH_Reference,
apple_DBH_Error = DBH_Apple - DBH_Reference
)
# Summary stats for accuracy
accuracy_summary <- accuracy %>%
summarise(
across(ends_with("_Error"),
list(mean = ~mean(.x, na.rm = TRUE),
sd = ~sd(.x, na.rm = TRUE)))
)
accuracy_summary
## # A tibble: 1 × 16
## Geoslam_TH_Error_mean Geoslam_TH_Error_sd RTC360_TH_Error_mean
## <dbl> <dbl> <dbl>
## 1 -0.399 1.59 -0.448
## # ℹ 13 more variables: RTC360_TH_Error_sd <dbl>, BLK360_TH_Error_mean <dbl>,
## # BLK360_TH_Error_sd <dbl>, apple_TH_Error_mean <dbl>,
## # apple_TH_Error_sd <dbl>, Geoslam_DBH_Error_mean <dbl>,
## # Geoslam_DBH_Error_sd <dbl>, RTC360_DBH_Error_mean <dbl>,
## # RTC360_DBH_Error_sd <dbl>, BLK360_DBH_Error_mean <dbl>,
## # BLK360_DBH_Error_sd <dbl>, apple_DBH_Error_mean <dbl>,
## # apple_DBH_Error_sd <dbl>
Results Table
Summary of Lidar Accuracy
-0.4 |
1.59 |
-0.45 |
0.56 |
-0.83 |
1.29 |
-24.79 |
3.02 |
-0.01 |
0.12 |
-0.01 |
0.04 |
0 |
0.05 |
0.05 |
0.24 |
Accuracy of each method for DBH
ggplot(accuracy, aes(x = Tree_ID)) +
geom_point(aes(y = Geoslam_DBH_Error, color = "Geoslam"), size = 2) +
geom_point(aes(y = RTC360_DBH_Error, color = "RTC360"), size = 2) +
geom_point(aes(y = BLK360_DBH_Error, color = "BLK360"), size = 2) + geom_point(aes(y = apple_DBH_Error, color = "Apple"), size = 2) +
labs(
title = "DBH Accuracy Comparison",
x = "Tree ID",
y = "DBH Error (m)",
color = "Method"
) +
theme_minimal() +
scale_color_manual(values = c("Geoslam" = "blue", "RTC360" = "red", "BLK360" = "green", "Apple" = "orange")) +
theme(
plot.title = element_text(size = 20, face = "bold"),
axis.title.x = element_text(size = 16),
axis.title.y = element_text(size = 16),
axis.text.x = element_text(size = 14),
axis.text.y = element_text(size = 14),
legend.title = element_text(size = 16),
legend.text = element_text(size = 14)
)
## Warning: Removed 8 rows containing missing values or values outside the scale range
## (`geom_point()`).
## Warning: Removed 4 rows containing missing values or values outside the scale range
## (`geom_point()`).
## Warning: Removed 6 rows containing missing values or values outside the scale range
## (`geom_point()`).
## Warning: Removed 25 rows containing missing values or values outside the scale range
## (`geom_point()`).

Plotting the accuracy of each method for TH
ggplot(accuracy, aes(x = Tree_ID)) +
geom_point(aes(y = Geoslam_TH_Error, color = "Geoslam"), size = 2) +
geom_point(aes(y = RTC360_TH_Error, color = "RTC360"), size = 2) +
geom_point(aes(y = BLK360_TH_Error, color = "BLK360"), size = 2) + geom_point(aes(y = apple_TH_Error, color = "Apple"), size = 2) +
labs(
title = "TH Accuracy Comparison",
x = "Tree ID",
y = "TH Error (m)",
color = "Method"
) +
theme_minimal() +
scale_color_manual(values = c("Geoslam" = "blue", "RTC360" = "red", "BLK360" = "green", "Apple" = "orange")) +
theme(
plot.title = element_text(size = 20, face = "bold"),
axis.title.x = element_text(size = 16),
axis.title.y = element_text(size = 16),
axis.text.x = element_text(size = 14),
axis.text.y = element_text(size = 14),
legend.title = element_text(size = 16),
legend.text = element_text(size = 14)
)
## Warning: Removed 8 rows containing missing values or values outside the scale range
## (`geom_point()`).
## Warning: Removed 4 rows containing missing values or values outside the scale range
## (`geom_point()`).
## Warning: Removed 6 rows containing missing values or values outside the scale range
## (`geom_point()`).
## Warning: Removed 25 rows containing missing values or values outside the scale range
## (`geom_point()`).
