--- title: "Frontier Walls, Labour Energetics and Qin Imperial Collapse" author: - Zehao Li - Giacomo Fontana - Andrew Bevan - Rujin Li date: "2024-03-01" keep_md: true output: html_document: toc: true toc_float: true --- # Setup ```{r} library(knitr) # For dynamic report generation library(tidyverse) # Comprehensive suite for data manipulation and visualization library(sf) # Handling of spatial data and geometries library(units) # Support for measurement units, useful when dropping geometry for spatial data library(shotGroups) # Analysis of shot groupings, including minimum bounding box calculation library(cluster) # Various algorithms for clustering data library(factoextra) # Visualization tools for clustering library(ggplot2) # Core plotting system in the tidyverse library(ggpubr) # Enhancements for ggplot2 to easily create publication-ready plots library(compareGroups) # Facilitates comparison of groups, mainly through plots library(ggspatial) # Adds spatial features like scale bars to ggplot2 plots library(ggsci) # Provides scientific journal and sci-fi themed color palettes library(kableExtra) # Enables creation and customization of HTML or LaTeX tables library(scales) # For scaling functions for visualizations library(ggstatsplot) # Extension of ggplot2 for creating statistical plots easily library(statsExpressions) # Simplifies the process of creating expressions with statistical details # Custom theme definition for ggplot2 plots mytheme <- theme(line = element_blank(), # Remove line elements panel.grid.major = element_blank(), # No major grid lines panel.grid.minor = element_blank(), # No minor grid lines panel.background = element_blank(), # Transparent background axis.text.x = element_blank(), # Hide x-axis text axis.text.y = element_blank(), # Hide y-axis text axis.title.x = element_blank(), # Hide x-axis title axis.title.y = element_blank(), # Hide y-axis title plot.title = element_text(size = 8), # Small plot title legend.key.height = unit(1, 'cm'), # Custom legend key height legend.key.width = unit(5, 'cm'), # Custom legend key width legend.title = element_text(size=10), # Custom size for legend title legend.text = element_text(size = 10), # Custom size for legend text legend.position="bottom", # Place legend at the bottom legend.box = "horizontal", # Arrange legend items horizontally plot.margin = margin(t = 0, # Top margin r = 0, # Right margin b = 0, # Bottom margin l = 0)) # Left margin # Custom scale annotation for adding to plots (like scale bars) myscale <- annotation_scale(aes(width_hint=0.1, style="ticks", location='br'), # Aesthetic settings for the scale bar line_width=1.5, # Line width of the scale bar height=unit(0.6, "cm"), # Height of the scale bar pad_y=unit(0.9, "cm"), # Vertical padding pad_x=unit(2.1, "cm"), # Horizontal padding text_cex = 1.5) # Text size multiplier ``` ## Load data ```{r} #| label: Load data #| output: false # Load the 'wall' shapefile into R as a spatial dataframe, and then perform a series of transformations: wall <- st_read("Data/wall.shp") %>% # Use st_read to import the shapefile rename(Site = Site_ID) %>% # Rename the 'Site_ID' column to 'Site' for consistency or clarity mutate(ID = row_number()) # Add a new column 'ID' that contains a unique identifier for each row/feature # After loading and transforming, replace any zero values in the 'Depth' column with NA (to handle missing or undefined depths) wall$Depth[wall$Depth == 0] <- NA # Load the 'wallgap' shapefile as a spatial dataframe. This dataset likely represents gaps in the wall. # Similar to the first, it renames 'Site_ID' to 'Site' for consistency across datasets. wallgap <- st_read("Data/gap.shp") %>% rename(Site = Site_ID) # Load the 'wallfilled' shapefile as a spatial dataframe. This file likely represents areas of the wall that have been filled in. # It also renames 'Site_ID' to 'Site', maintaining consistency with the previous datasets. wallfilled <- st_read("Data/filled.shp") %>% rename(Site = Site_ID) ``` # Stone-built Wall Models Generation ```{r} #| fig-width: 15 # This chunk option sets the default figure width to 15 inches for the output. #| fig-height: 14 # This chunk option sets the default figure height to 14 inches for the output. # Begin creating a plot using ggplot2 package, with 'wall' data. fig.walls <- ggplot(wall) + geom_sf(size = 0.1) + # Adds spatial data to the plot. 'geom_sf' is used for simple features objects, with a line size of 0.1. mytheme + # Adds a custom ggplot2 theme, stored in the variable 'mytheme', to style the plot. myscale + # Adds custom scales for the plot, stored in the variable 'myscale'. Could adjust colors, axes, etc. # Annotates the plot with text at specified locations. annotate("text", x = -14, y = 4, size=7.5, label = "Hayehutong") + annotate("text", x = -14, y = -2, size=7.5, label = "Sichenggong") + annotate("text", x = -14, y = -8, size=7.5, label = "Xiniwusu") + annotate("text", x = -14, y = -14, size=7.5, label = "Yonghegong") + annotate("text", x = -14, y = -20, size=7.5, label = "Bayinnuru") # Each 'annotate' function call adds a label at specified coordinates with a given text size and label. fig.walls # This line actually generates and displays the plot stored in 'fig.walls'. # Saves the 'fig.walls' plot to a PNG file in the specified path with given dimensions and resolution. ggsave("fig.walls.png", path="images", width = 25, height = 15, units = "in", dpi=600) ``` ## Models ```{r} #| fig-width: 9 # Sets the default figure width to 9 inches for the output. #| fig-height: 7 # Sets the default figure height to 7 inches for the output (corrected to fig-height). # Set the site of interest for the visualization. example <- "Hayehutong" # Create a plot 'a' showing the original model for the specified site. a <- ggplot(data = (subset(wall, wall$Site == example))) + geom_sf(size = 0.1) + # Plot spatial features with a border size of 0.1. mytheme + # Apply custom styling from the 'mytheme' variable. ggtitle(paste(example, "model")) # Title the plot with the site name and "model". # Create a plot 'b' showing the filled model for the specified site. b <- ggplot(data = (subset(wallfilled, wallfilled$Site == example))) + geom_sf(size = 0.1) + # Plot spatial features with a border size of 0.1. mytheme + # Apply the same custom styling. ggtitle(paste(example, "filled model")) # Title this plot with "filled model". # Create a plot 'c' showing the gap model for the specified site. c <- ggplot(data = (subset(wallgap, wallgap$Site == example))) + geom_sf(size = 0.1) + # Plot spatial features with a border size of 0.1. mytheme + # Apply the same custom styling. ggtitle(paste(example, "gap model")) + # Title this plot with "gap model". annotation_scale(aes(width_hint=0.11, style="ticks", location='br'), # Add a scale annotation to the plot. line_width=1.5, height=unit(0.45, "cm"), pad_y=unit(0, "cm"), pad_x=unit(0, "cm"), text_cex = 1.5) # Arrange the three plots vertically in a single figure. fig.models <- ggarrange(a, b, c, nrow = 3, align = "v") fig.models # Display the arranged figure. # Save the arranged figure to a PNG file with specified dimensions and resolution. ggsave("fig.models.png", path="images", width = 15, height = 7, units = "in", dpi=600) ``` ## Area Calculation ```{r} #| label: Area Calculation #| output: false wall$Area <- st_area(wall) # Calculates the area of each feature in the 'wall' spatial object and assigns these values to a new column 'Area' in the 'wall' dataset. wall <- drop_units(wall) # Removes the units from the 'Area' values calculated above, making the 'wall' dataset easier to work with for further analysis or visualization. ``` ### Area plot ```{r} #| fig-width: 9 #| fig.height: 7 # Extract the 'Area' column from 'wall' dataset. x <- wall$Area # Create a vector containing the minimum and maximum values of 'Area'. ls_val <- c(min(x), max(x)) # Define a gradient fill scale for visualization, from light gray to black, based on the 'Area' values. myfill <- scale_fill_gradient(limits = c(min(x),max(x)), low = "gray80", high = "black", na.value = "white", breaks = ls_val, labels=c(round(min(x), digits=2), round(max(x), digits=2))) # Construct the plot using 'ggplot2' and 'sf' packages. fig.area <- ggplot(wall, aes(fill = Area)) + # Set 'Area' as the fill aesthetic. geom_sf(size = 0.06) + # Plot the spatial data with a border size of 0.06. mytheme + # Apply custom theme settings. myscale + # Apply custom scale settings. myfill + # Apply the gradient fill defined earlier. # Adjust the colorbar guide to match specified aesthetics. guides(fill = guide_colourbar(barwidth = 0.5, barheight = 2, label.position = "left"), ticks = "none") + theme(legend.position = c(0.935, 0.25)) + # Position the legend. # Annotate specific locations with labels. annotate("text", x = -14, y = 4, size=2.5, label = "Hayehutong") + annotate("text", x = -14, y = -2, size=2.5, label = "Sichenggong") + annotate("text", x = -14, y = -8, size=2.5, label = "Xiniwusu") + annotate("text", x = -14, y = -14, size=2.5, label = "Yonghegong") + annotate("text", x = -14, y = -20, size=2.5, label = "Bayinnuru") # Display the plot. fig.area ``` ### Calculate rectangularity using minimum bounding box ```{r} #| output: false #| cache: true # Add columns mbb_Area and index # Initialize new columns in 'wall' with NA values wall["mbb_Area"] <- NA wall["Rectangularity"] <- NA # Initialize a text progress bar to monitor the loop's progress pb <- txtProgressBar(min=1, max=nrow(wall), style=3) # Iterate over each row in 'wall' for(i in 1:nrow(wall)) { # Extract the matrix of coordinates for the i-th feature's geometry mat <- wall[i, ][[3]][[1]][[1]] # Calculate the minimum bounding box (mbb) for the feature mbb <- getMinBBox(mat) # Calculate the area of the mbb and store it in the 'mbb_Area' column wall[i,]$mbb_Area <- mbb$width*mbb$height # Calculate the Rectangularity index (Area of the shape / Area of the mbb) and store it wall[i,]$Rectangularity <- wall[i,]$Area/wall[i,]$mbb_Area # Update the progress bar setTxtProgressBar(pb, i) } # Close the progress bar after the loop completes close(pb) ``` ### Extremes in rectangularity ```{r} #| fig-width: 9 #| fig.height: 7 #| # Identifies the index of the feature with the minimum rectangularity (worst fit). n <- which.min(wall$Rectangularity) # Extracts the coordinates matrix for this feature to plot its original geometry. mat <- wall[n,][[3]][[1]][[1]] # Calculates the oriented minimum bounding box (MBB) for this feature. mbb <- getMinBBox(mat) # Creates a plot 'a' visualizing this feature, its points, and its MBB. a <- ggplot(data = wall[n,]) + geom_sf() + # Plots the feature using simple features geometry. geom_point(data=(as.data.frame(mat)), aes(x = V1, y = V2), size=0.6) + # Plots the original points of the feature. geom_polygon(data=as.data.frame(mbb$pts), aes(x = V1, y = V2), color="black", size=0.8, fill="transparent") + # Plots the MBB as a transparent polygon. mytheme + # Applies a custom theme for consistent styling. labs(caption = c(paste("Worst rectangularity fit =", round(wall[n,]$Rectangularity, digits = 2)))) # Adds a caption with the rectangularity index. # Identifies the index of the feature with the maximum rectangularity (best fit). n <- which.max(wall$Rectangularity) # The next few lines repeat the process for the best fit, similar to the worst fit. mat <- wall[n,][[3]][[1]][[1]] mbb <- getMinBBox(mat) # Creates a plot 'b' for the best fit. b <- ggplot(data = wall[n,]) + geom_sf() + geom_point(data=(as.data.frame(mat)), aes(x = V1, y = V2), size=0.6) + geom_polygon(data=as.data.frame(mbb$pts), aes(x = V1, y = V2), color="black", size=0.8, fill="transparent") + mytheme + labs(caption = c(paste("Best rectangularity fit =", round(wall[n,]$Rectangularity, digits = 2)))) # Combines plots 'a' and 'b' side by side. fig.extremes <- ggarrange(a, b, ncol = 2, align = "hv") + theme(plot.margin = unit(c(1.5,1.5,1.5,1.5),"cm")) fig.extremes ``` ### Visualizing rectangularity ```{r} #| fig-width: 9 #| fig.height: 7 # Extract the 'Rectangularity' column from the 'wall' dataset. x <- wall$Rectangularity # Determine the minimum and maximum values of rectangularity for setting the scale. ls_val <- c(min(x), max(x)) # Create a gradient fill scale for coloring the features based on their rectangularity values. myfill <- scale_fill_gradient(limits = c(min(x),max(x)), low = "gray90", high = "gray20", na.value = "white", breaks = ls_val, labels=c(round(min(x), digits=2), round(max(x), digits=2))) # Construct the plot fig.rectangularity <- ggplot(wall, aes(fill = Rectangularity)) + # Base plot with 'Rectangularity' as fill geom_sf(size = 0.06) + # Draw the features with a border size of 0.06 mytheme + # Apply custom theme settings myscale + # Apply custom scale settings myfill + # Apply the gradient fill defined earlier # Adjust the colorbar guide to match specified aesthetics guides(fill = guide_colourbar(barwidth = 0.5, barheight = 2, label.position = "left"), ticks = "none") + theme(legend.position = c(0.935, 0.25)) + # Position the legend # Annotate specific locations with labels annotate("text", x = -14, y = 4, size=4.5, label = "Hayehutong") + annotate("text", x = -14, y = -2, size=4.5, label = "Sichenggong") + annotate("text", x = -14, y = -8, size=4.5, label = "Xiniwusu") + annotate("text", x = -14, y = -14, size=4.5, label = "Yonghegong") + annotate("text", x = -14, y = -20, size=4.5, label = "Bayinnuru") # Display the plot fig.rectangularity ``` ## Gap Calculation ```{r} #| label: Variable gap calculation #| output: false #| cache: true # Create a temporary copy of the 'wall' dataset. tmp <- wall # Generate a buffer around each feature in 'wall' with a specified distance. tmp$buffer <- st_buffer(wall, dist = 0.01) # Determine which buffered features are entirely within the 'wallfilled' dataset. # 'st_covered_by' checks if the buffer is within 'wallfilled'; 'lengths > 0' ensures there is an overlap. tmp$covered <- st_covered_by(tmp$buffer, wallfilled) %>% lengths > 0 # Filter 'tmp' to keep only features not at the edge, i.e., those covered by 'wallfilled'. tmp <- subset(tmp, tmp$covered[{TRUE}]) # Initialize a 'Gap' column in 'tmp' to store calculated gap areas. tmp["Gap"] <- NA # Initialize a text progress bar for loop monitoring. pb <- txtProgressBar(min=1, max=nrow(wallgap), style=3) # Loop through each feature in 'tmp'. for(i in 1:nrow(tmp)) { # Identify gaps touching the current feature using 'st_touches' and extract them. tmp1 <- st_touches(tmp, wallgap)[[i]] tmp2 <- st_sf(wallgap$geometry[tmp1]) # Calculate the area of each identified gap. tmp2$area <- st_area(tmp2) # Sum the areas of all gaps touching the current feature and store in 'Gap'. tmp[i,]$Gap <- sum(tmp2$area) # Update progress bar. setTxtProgressBar(pb, i) } # Close the progress bar after completing the loop. close(pb) ``` #Join gap area to data frame dataframe ```{r} # Join the gap area information from 'tmp' back to the original 'wall' dataset. wall <- left_join(wall, # Remove the geometry column from 'tmp' to prepare for the join. st_drop_geometry(tmp) %>% # Select only the 'ID' and 'Gap' columns from 'tmp' for the join. dplyr::select(ID, Gap), # Specify the column ('ID') to use for matching rows between the two datasets. by = "ID") ``` #Plot gap area ```{r} #| fig-width: 7 #| fig.height: 3 # Subset the 'wall' dataset to include only rows where 'Gap' is not NA, then extract 'Gap' values. x <- subset(wall, !is.na(Gap))$Gap # Determine the minimum and maximum values of 'Gap' for use in the color gradient. ls_val <- c(min(x), max(x)) # Define a color gradient for the 'Gap' values from light gray to dark gray. myfill <- scale_fill_gradient(limits = c(min(x),max(x)), low = "gray90", high = "gray20", na.value = "white", breaks = ls_val, labels=c(round(min(x), digits=2), round(max(x), digits=2))) # Construct the plot. fig.gap <- ggplot(wall, aes(fill = Gap)) + # Base plot with 'Gap' determining the fill color. geom_sf(size = 0.06) + # Draw the spatial features with a border size of 0.06. mytheme + # Apply custom theme settings for consistent styling. myscale + # Apply custom scale settings for consistent scaling. myfill + # Apply the defined color gradient to the plot. # Define the appearance of the colorbar legend. guides(fill = guide_colourbar(barwidth = 0.5, barheight = 2, label.position = "left"), ticks = "none") + theme(legend.position = c(0.935, 0.25)) + # Position the legend. # Annotate specific locations on the map for additional context. annotate("text", x = -14, y = 4, size=2.5, label = "Hayehutong") + annotate("text", x = -14, y = -2, size=2.5, label = "Sichenggong") + annotate("text", x = -14, y = -8, size=2.5, label = "Xiniwusu") + annotate("text", x = -14, y = -14, size=2.5, label = "Yonghegong") + annotate("text", x = -14, y = -20, size=2.5, label = "Bayinnuru") # Display the plot. fig.gap ``` ## General statistics ```{r} #| label: General statistics #| output: false #| cache: true # Prepare the dataset for total statistics by duplicating 'wall' and setting a uniform site label. wallstats <- wall wallstats$Site <- "Total sample" # Assign a common site label for aggregate statistics. # Group the data by 'Site' after removing geometric data, then calculate various statistics. wallstats <- group_by(st_drop_geometry(wallstats), Site) %>% summarise( count = n(), # Count of features. "Area_mean" = mean(Area, na.rm = TRUE), # Mean of the Area column, excluding NA values. "Area_sd" = sd(Area, na.rm = TRUE), # Standard deviation of the Area column, excluding NA values. "Area_IQR" = IQR(Area, na.rm = TRUE), # Interquartile range of the Area column, excluding NA values. "Rectangularity_mean" = mean(Rectangularity, na.rm = TRUE), # Mean of Rectangularity, excluding NA values. "Rectangularity_sd" = sd(Rectangularity, na.rm = TRUE), # Standard deviation of Rectangularity, excluding NA values. "Rectangularity_IQR" = IQR(Rectangularity, na.rm = TRUE), # Interquartile range of Rectangularity, excluding NA values. "Gap_mean" = mean(Gap, na.rm = TRUE), # Mean of Gap areas, excluding NA values. "Gap_sd" = sd(Gap, na.rm = TRUE), # Standard deviation of Gap areas, excluding NA values. "Gap_IQR" = IQR(Gap, na.rm = TRUE), # Interquartile range of Gap areas, excluding NA values. "Depth_mean" = mean(Depth, na.rm = TRUE), # Mean of Depth, excluding NA values. "Depth_sd" = sd(Depth, na.rm = TRUE), # Standard deviation of Depth, excluding NA values. "Depth_IQR" = IQR(Depth, na.rm = TRUE) # Interquartile range of Depth, excluding NA values. ) # Calculate statistics for each site separately by grouping the data without geometries. sitestats <- group_by(st_drop_geometry(wall), Site) %>% summarise( # Repeat the same set of statistical calculations for each site. count = n(), "Area_mean" = mean(Area, na.rm = TRUE), "Area_sd" = sd(Area, na.rm = TRUE), "Area_IQR" = IQR(Area, na.rm = TRUE), "Rectangularity_mean" = mean(Rectangularity, na.rm = TRUE), "Rectangularity_sd" = sd(Rectangularity, na.rm = TRUE), "Rectangularity_IQR" = IQR(Rectangularity, na.rm = TRUE), "Gap_mean" = mean(Gap, na.rm = TRUE), "Gap_sd" = sd(Gap, na.rm = TRUE), "Gap_IQR" = IQR(Gap, na.rm = TRUE), "Depth_mean" = mean(Depth, na.rm = TRUE), "Depth_sd" = sd(Depth, na.rm = TRUE), "Depth_IQR" = IQR(Depth, na.rm = TRUE) ) # Combine the site-specific statistics and the overall statistics into a single data frame. statistics <- sitestats statistics[nrow(statistics) + 1,] = wallstats # Append the total statistics row to the 'statistics' data frame. ``` ## Figure of stone blocks variability across the different sites. ```{r} #| label: fig-variablesDistribution #| fig-cap: Stone blocks variability across the different sites. #| fig-width: 14 #| fig.height: 10 # Create a density plot for the 'Area' variable, colored by 'Site'. a <- ggplot(wall, aes(x=Area, color=Site)) + geom_density() + # Adds a density plot scale_color_simpsons() + # Applies a color scale based on 'The Simpsons' (assumes a custom scale or package) theme_minimal() + # Uses a minimal theme for aesthetics xlab("Area") + # Label for the x-axis ylab("Density") # Label for the y-axis # Create a density plot for the 'Rectangularity' variable, colored by 'Site'. b <- ggplot(wall, aes(x=Rectangularity, color=Site)) + geom_density() + # Adds a density plot scale_color_simpsons() + # Applies the same Simpsons color scale theme_minimal() + # Uses the same minimal theme ylab(NULL) # Removes the y-axis label # Create a density plot for the 'Gap' variable (excluding NA values), colored by 'Site'. c <- ggplot(data=subset(wall, !is.na(Gap)), aes(x=Gap, color=Site)) + geom_density() + # Adds a density plot scale_color_simpsons() + # Applies the same Simpsons color scale theme_minimal() + # Uses the same minimal theme ylab(NULL) # Removes the y-axis label # Arrange the three plots side by side with a common legend at the bottom. fig.variablesDistribution <- ggarrange(a, b, c, ncol = 3, common.legend = TRUE, legend ="bottom") # Display the arranged plots. fig.variablesDistribution ``` ## Results of the Shapiro-Wilk normality test ```{r} #| label: tbl-normalDistribution #| tbl-cap: Results of the Shapiro-Wilk normality test. # Perform Shapiro-Wilk normality tests for 'Area' at each site. a <- shapiro.test(wall$Area[wall$Site == "Hayehutong"]) b <- shapiro.test(wall$Area[wall$Site == "Sichenggong"]) c <- shapiro.test(wall$Area[wall$Site == "Yonghegong"]) d <- shapiro.test(wall$Area[wall$Site == "Xiniwusu"]) e <- shapiro.test(wall$Area[wall$Site == "Bayinnuru"]) # Extract and combine the test statistics (W) into a vector. tmp1 <- unname(c(a$statistic,b$statistic,c$statistic,d$statistic,e$statistic)) # Extract and combine the p-values into a vector. tmp2 <- unname(c(a$p.value,b$p.value,c$p.value,d$p.value,e$p.value)) # Create a data frame with the test statistics and p-values. results <- data.frame(tmp1, tmp2) colnames(results) <- c("w","p-value") # Rename columns for clarity. # Set row names to correspond to the sites tested. rownames(results) <- c("Hayehutong","Sichenggong","Yonghegong","Xiniwusu","Bayinnuru") # Format the p-values for readability. results$`p-value` <- format(results$`p-value`, digits = 3) # Use 'kable' to create a formatted table and 'kable_styling' to apply styling options. kable(results) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ## Results of the Fligner-Killeen test of homogeneity of variances ```{r} #| label: tbl-homogeneity #| tbl-cap: Results of the Fligner-Killeen test of homogeneity of variances. # Perform Fligner-Killeen test to check homogeneity of variances for 'Area' across 'Site'. a <- fligner.test(Area ~ Site, data = wall) # Perform Fligner-Killeen test for 'Rectangularity' across 'Site'. b <- fligner.test(Rectangularity ~ Site, data = wall) # Perform Fligner-Killeen test for 'Gap' across 'Site'. c <- fligner.test(Gap ~ Site, data = wall) # Combine the median chi-squared test statistics from each test into a vector. tmp1 <- unname(c(a$statistic, b$statistic, c$statistic)) # Combine the degrees of freedom (df) from each test into a vector. tmp2 <- unname(c(a$parameter, b$parameter, c$parameter)) # Combine the p-values from each test into a vector. tmp3 <- unname(c(a$p.value, b$p.value, c$p.value)) # Create a data frame from the combined test statistics, degrees of freedom, and p-values. results <- data.frame(tmp1, tmp2, tmp3) # Rename the columns for better readability. colnames(results) <- c("med chi-squared", "df", "p-value") # Assign row names corresponding to the variables tested. rownames(results) <- c("Area", "Rectangularity", "Gap") # Format the p-values in the results for better readability. results$`p-value` <- format(results$`p-value`, digits = 3) # Create a formatted table of the results using 'kable' from 'knitr' and style it with 'kable_styling' from 'kableExtra'. kable(results) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ## Results of the Kruskal-Wallis test and Dunn pairwise comparison ```{r} #| label: fig-statistics #| fig-cap: Results of the Kruskal-Wallis test and Dunn pairwise comparison. #| fig-width: 16 #| fig.height: 8 # Drop geometry information from 'wall' to work with statistical data only. dat <- st_drop_geometry(wall) # Generate a box plot for 'Area' across different 'Site', including statistical analysis. dat_res <- ggbetweenstats(data = dat, x = Site, y = Area, plot.type = "box", type = "nonparametric", pairwise.comparisons = TRUE, # Perform post-hoc tests if more than 2 groups pairwise.display = "significant", # Display only significant differences ggsignif.args = list(textsize = 4, tip_length = 0.01), # Customize significance labels bf.message = FALSE, # Disable Bayes Factor message centrality.plotting = FALSE, # Disable plotting of central measure package = "ggsci", # Use 'ggsci' package for aesthetics palette = "springfield_simpsons", # Simpsons theme palette xlab = "") # Remove x-axis label a <- dat_res + labs(title = dat_res[["labels"]][["subtitle"]][[2]], # Use generated subtitle as title subtitle = dat_res[["labels"]][["subtitle"]][[3]]) + # Use generated subtitle theme_minimal() + # Use minimal theme for plot theme(plot.title = element_text(size = 10), legend.position = "none") # Customize plot title and remove legend # For 'Rectangularity' dat_res <- ggbetweenstats(data = dat, x = Site, y = Rectangularity, plot.type = "box", type = "nonparametric", pairwise.comparisons = TRUE, # Perform post-hoc tests if more than 2 groups pairwise.display = "significant", # Display only significant differences ggsignif.args = list(textsize = 4, tip_length = 0.01), # Customize significance labels bf.message = FALSE, # Disable Bayes Factor message centrality.plotting = FALSE, # Disable plotting of central measure package = "ggsci", # Use 'ggsci' package for aesthetics palette = "springfield_simpsons", # Simpsons theme palette xlab = "") # Remove x-axis label b <- dat_res + labs(title = dat_res[["labels"]][["subtitle"]][[2]], # Use generated subtitle as title subtitle = dat_res[["labels"]][["subtitle"]][[3]]) + # Use generated subtitle theme_minimal() + # Use minimal theme for plot theme(plot.title = element_text(size = 10), legend.position = "none") # Customize plot title and remove legend # For 'Gap', making sure to handle any NAs in 'Gap' similarly dat_res <- ggbetweenstats(data = dat, x = Site, y = Gap, plot.type = "box", type = "nonparametric", pairwise.comparisons = TRUE, # Perform post-hoc tests if more than 2 groups pairwise.display = "significant", # Display only significant differences ggsignif.args = list(textsize = 4, tip_length = 0.01), # Customize significance labels bf.message = FALSE, # Disable Bayes Factor message centrality.plotting = FALSE, # Disable plotting of central measure package = "ggsci", # Use 'ggsci' package for aesthetics palette = "springfield_simpsons", # Simpsons theme palette xlab = "") # Remove x-axis label c <- dat_res + labs(title = dat_res[["labels"]][["subtitle"]][[2]], # Use generated subtitle as title subtitle = dat_res[["labels"]][["subtitle"]][[3]]) + # Use generated subtitle theme_minimal() + # Use minimal theme for plot theme(plot.title = element_text(size = 10), legend.position = "none") # Customize plot title and remove legend ``` ## Cluster calculation ```{r} #| label: cluster calculation #| output: false # Create a subset of 'wall' with only ID, Area, and Rectangularity columns. subset_wall <- wall[c("ID", "Area", "Rectangularity")] # Remove rows with missing values in any of the specified columns. subset_wall <- na.omit(subset_wall) # Remove spatial geometry and units from the subset for analysis. subset_wall <- drop_units(st_drop_geometry(subset_wall)) # Scale 'Area' and 'Rectangularity' to have zero mean and unit variance. subset_wall <- subset_wall %>% mutate(Area = scale(Area)) %>% mutate(Rectangularity = scale(Rectangularity)) # Determine the optimal number of clusters using the silhouette method. clust <- fviz_nbclust(subset_wall[c('Area', 'Rectangularity')], kmeans, method = "silhouette", linecolor = "gray30") # Extract the data from the fviz_nbclust result to find the maximum silhouette score. n_clust <- clust$data max_cluster <- as.numeric(n_clust$clusters[which.max(n_clust$y)]) # Perform k-means clustering with the optimal number of clusters found. Cluster <- kmeans(subset_wall[c('Area', 'Rectangularity')], centers = max_cluster, nstart = 25) # Append the cluster assignments to the subset. subset_wall <- subset_wall %>% mutate(Cluster = Cluster$cluster) # Join the cluster assignments back to the original 'wall' dataset using 'ID' as the key. wall <- left_join(wall, subset_wall %>% dplyr::select(ID, Cluster), by = "ID") # Convert the 'Cluster' column to a factor for potential categorical analysis. wall$Cluster = as.factor(wall$Cluster) ``` ### Optimal number of clusters (a) and scatter plot of the consequent division in clusters (b) ```{r} #| label: fig-clusterGraph #| fig-cap: Results of the silhouette method with indicated the optimal number of clusters (a) and scatter plot of the consequent division in clusters (b). #| fig-width: 14 #| fig.height: 10 # Define custom colors for different clusters using a manual color scale. myfill <- scale_color_manual(values = c("#D2af81ff", "#b7bbbfff", "#1a9993ff")) # Assuming 'clust' is a ggplot object created earlier, apply a minimal theme and title. a <- clust + theme_minimal() + ggtitle("a") # Note: Replace "a" with a more descriptive title if necessary. # Create a scatter plot of 'Area' vs. 'Rectangularity', colored by 'Cluster'. b <- ggplot(wall, aes(x=Area, y=Rectangularity, color=Cluster)) + geom_point() + # Plot points myfill + # Apply the defined custom colors for clusters theme_minimal() + # Use a minimal theme for a clean look ggtitle("b") # Note: Replace "b" with a more descriptive title if necessary. # Arrange the two plots vertically in a single figure. fig.clusterGraph <- ggarrange(a, b, nrow = 2) # Display the arranged figure. fig.clusterGraph ``` ```{r} #| label: cluster statistics #| output: false # Prepare the dataset for total statistics by copying 'wall' and assigning a uniform label "Total sample" to all observations. wallstats <- wall wallstats$Cluster <- "Total sample" # Calculate overall statistics after dropping geometry data, grouping by the newly assigned label. wallstats <- group_by(st_drop_geometry(wallstats), Cluster) %>% summarise( count = n(), # Calculate the total number of observations "Area_mean" = mean(Area, na.rm = TRUE), # Calculate the mean of 'Area', excluding missing values "rectangularity_mean" = mean(Rectangularity, na.rm = TRUE), # Calculate the mean of 'Rectangularity', excluding missing values "Depth_mean" = mean(Depth, na.rm = TRUE) # Calculate the mean of 'Depth', excluding missing values ) # Calculate statistics for each existing cluster within the dataset. clusterstats <- group_by(st_drop_geometry(wall), Cluster) %>% summarise( count = n(), # Count of observations within each cluster "Area_mean" = mean(Area, na.rm = TRUE), # Mean of 'Area' by cluster, excluding NA "rectangularity_mean" = mean(Rectangularity, na.rm = TRUE), # Mean of 'Rectangularity' by cluster, excluding NA "Depth_mean" = mean(Depth, na.rm = TRUE) # Mean of 'Depth' by cluster, excluding NA ) # Ensure the 'Cluster' column in 'clusterstats' is treated as character for consistent data manipulation. clusterstats$Cluster <- as.character(clusterstats$Cluster) # Initialize 'clusterstatistics' with the cluster-specific stats. clusterstatistics <- clusterstats # Append the overall statistics to the end of 'clusterstatistics', creating a comprehensive summary. clusterstatistics[nrow(clusterstatistics) + 1,] = wallstats ``` ### Plots of the identified clusters on the wall stretches ```{r} #| label: fig-clusterPlot #| fig-cap: Plotting of the identified clusters on the wall stretches. #| fig-width: 14 #| fig.height: 8 # Define custom fill colors for different clusters. myfill <- scale_fill_manual(values = c("#D2af81ff", "#b7bbbfff", "#1a9993ff")) # Create a spatial plot with 'Cluster' determining the fill color. fig.clusterPlot <- ggplot(wall, aes(fill = Cluster)) + geom_sf(size = 0.06) + # Add spatial data with specified border size. mytheme + # Apply a custom theme for styling. myscale + # Apply custom scaling settings. myfill + # Apply the defined custom fill colors for clusters. theme(legend.position = c(0.935, 0.25)) + # Set legend position. # Add annotations for specific locations on the map for context. annotate("text", x = -14, y = 4, size=7.5, label = "Hayehutong") + annotate("text", x = -14, y = -2, size=7.5, label = "Sichenggong") + annotate("text", x = -14, y = -8, size=7.5, label = "Xiniwusu") + annotate("text", x = -14, y = -14, size=7.5, label = "Yonghegong") + annotate("text", x = -14, y = -20, size=7.5, label = "Bayinnuru") # Display the plot. fig.clusterPlot # Save the plot as a PNG file with specified dimensions and resolution. ggsave("fig.clusterplot.png", path="images", width = 25, height = 15, units = "in", dpi=600) ``` ### Scatter plot of depth and area distribution by site type (a) and depth distribution by cluster (b) ```{r} #| label: fig-depth #| fig-cap: Scatter plot of depth and area distribution by site type (a) and depth distribution by cluster (b). #| fig-width: 10 #| fig.height: 4.5 # Remove 'Gap' column from the dataset to avoid analysis on this variable. wall_noNull <- wall wall_noNull$Gap <- NULL # Define custom fill colors for clusters and custom color scales for sites. myfill <- scale_fill_manual(values = c("#D2af81ff", "#b7bbbfff", "#1a9993ff", "#c6fe39", "#3963fe")) mycol <- scale_color_manual(values = c("#FED439FF", "#709AE1FF", "#8A9197FF", "#ff9993", "#9993ff")) # Plot 'a': Scatter plot of Area vs. Depth, colored by 'Site'. a <- ggplot(na.omit(wall_noNull), aes(x=Area, y=Depth)) + geom_point(aes(color=Site)) + # Points colored by 'Site'. geom_smooth(method=lm, color="black") + # Linear regression line across the scatter plot. theme(legend.position = "none") + # Remove legend for cleaner presentation. mycol + # Apply custom color scale for sites. theme_minimal() + # Use a minimal theme for a clean look. ggtitle("Area vs. Depth by Site") # Title for the plot. # Plot 'b': Boxplot of Depth by 'Cluster'. b <- ggplot(na.omit(wall_noNull), aes(x=Cluster, y=Depth, fill=Cluster)) + geom_boxplot() + # Boxplot showing distribution of 'Depth' by 'Cluster'. theme(legend.position = "none") + # Remove legend. myfill + # Apply custom fill colors for clusters. theme_minimal() + # Use a minimal theme. ylab(NULL) + # Remove y-axis label for a cleaner look. ggtitle("Depth Distribution by Cluster") + # Title for the plot. annotate("text", x = 1:length(table(na.omit(wall_noNull)$Cluster)), # X positions for annotations. y = aggregate(Depth ~ Cluster, na.omit(wall_noNull), median)[ , 2], # Y positions for annotations. label = paste("n=", table(na.omit(wall_noNull)$Cluster)), # Annotation text showing counts per cluster. vjust = -0.5, cex = 2.5) # Adjust vertical position and size of annotation text. # Combine the two plots side by side. fig.depth <- ggarrange(a, b, ncol = 2) # Display the combined figure. fig.depth ``` ### Volume by cluster ```{r} #| label: tbl-sideSize #| tbl-cap: Properties of the average stone block by cluster. # Calculate volumes using cluster-specific depth means. wall <- wall %>% mutate(clustdepth = case_when( Cluster == 1 ~ clusterstatistics$Depth_mean[1], # Assign mean depth for Cluster 1. Cluster == 2 ~ clusterstatistics$Depth_mean[2], # Assign mean depth for Cluster 2. Cluster == 3 ~ clusterstatistics$Depth_mean[3] # Assign mean depth for Cluster 3. )) wall$volume <- wall$Area * wall$clustdepth # Compute volume as Area * depth. # Define a function to calculate the cuberoot, considering negative values. cuberoot = function(x) { if(x < 0) { - (-x)^(1/3) } # Correct cuberoot for negative numbers. else { x^(1/3) } # Standard cuberoot for positive numbers. } # Calculate overall statistics, treating the dataset as a single "cluster". wallstats <- wall wallstats$Cluster <- "Total sample" wallstats <- group_by(st_drop_geometry(wallstats), Cluster) %>% summarise( Count = n(), "Mean volume (m^3)" = mean(volume, na.rm = TRUE), "SD volume (m^3)" = sd(volume, na.rm = TRUE), "IQR volume (m^3)" = IQR(volume, na.rm = TRUE), "Average stone Side (m)" = cuberoot(mean(volume, na.rm = TRUE)) # Calculate average side length from volume. ) # Calculate statistics for each cluster separately. clusterstats <- group_by(st_drop_geometry(wall), Cluster) %>% summarise( Count = n(), "Mean volume (m^3)" = mean(volume, na.rm = TRUE), "SD volume (m^3)" = sd(volume, na.rm = TRUE), "IQR volume (m^3)" = IQR(volume, na.rm = TRUE), "Average stone side (m)" = cuberoot(mean(volume, na.rm = TRUE)) # Calculate average side length from volume. ) # Combine the statistics for individual clusters with the overall statistics. clusterstats$Cluster <- as.character(clusterstats$Cluster) # Ensure 'Cluster' is character for consistency. clusterstatistics_volume <- clusterstats clusterstatistics_volume[nrow(clusterstatistics_volume) + 1,] = wallstats # Append overall stats to cluster-specific stats. # Display the combined statistics in a table with custom formatting. kable(clusterstatistics_volume, digits = 4) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ## Facade volume analysis and composition by wall type ```{r} #| label: tbl-percentage #| tbl-cap: Percentage of facade composition divided by blocks below x, between x and y, above y and gap area of walls of Type-A, Type-B and Type-C. # Calculate the area of wallgap and wallfilled using sf functions and remove the units for simplicity wallgap$Area_wallgap <- drop_units(st_area(wallgap)) wallfilled$Area_wallfilled <- drop_units(st_area(wallfilled)) # Calculate volumes for different types of wall sections, excluding specific sites for Type A ## Type-A: Exclude 'Hayehutong' and 'Yonghegong' sites tA_wall <- subset(wall, wall$Site != "Hayehutong" & wall$Site != "Yonghegong") tA_wallgap <- subset(wallgap, wallgap$Site != "Hayehutong" & wallgap$Site != "Yonghegong") tA_wallfilled <- subset(wallfilled, wallfilled$Site != "Hayehutong" & wallfilled$Site != "Yonghegong") # Calculate the percentage of gap area relative to the filled area and total volume for type A tA_volperc_gap <- sum(tA_wallgap$Area_wallgap) * 100 / (sum(tA_wallfilled$Area_wallfilled)) tA_volgap <- sum(tA_wall$volume) * tA_volperc_gap / 100 tA_voltotal <- sum(tA_wall$volume) + tA_volgap ## Type-B: Specific calculations for 'Hayehutong' site tB_wall <- subset(wall, wall$Site == "Hayehutong") tB_wallgap <- subset(wallgap, wallgap$Site == "Hayehutong") tB_wallfilled <- subset(wallfilled, wallfilled$Site == "Hayehutong") # Calculate the percentage of gap area and total volume for type B tB_volperc_gap <- sum(tB_wallgap$Area_wallgap) * 100 / (sum(tB_wallfilled$Area_wallfilled)) tB_volgap <- sum(tB_wall$volume) * tB_volperc_gap / 100 tB_voltotal <- sum(tB_wall$volume) + tB_volgap ## Type-C: Specific calculations for 'Yonghegong' site tC_wall <- subset(wall, wall$Site == "Yonghegong") tC_wallgap <- subset(wallgap, wallgap$Site == "Yonghegong") tC_wallfilled <- subset(wallfilled, wallfilled$Site == "Yonghegong") # Calculate the percentage of gap area and total volume for type C tC_volperc_gap <- sum(tC_wallgap$Area_wallgap) * 100 / (sum(tC_wallfilled$Area_wallfilled)) tC_volgap <- sum(tC_wall$volume) * tC_volperc_gap / 100 tC_voltotal <- sum(tC_wall$volume) + tC_volgap ##Calculation by linear m ### type-A tA_volperc_upto0.02 <- sum(tA_wall$volume[tA_wall$volume<=0.02])*100/tA_voltotal tA_volperc_0.02to0.05 <- sum(tA_wall$volume[tA_wall$volume>0.02 & tA_wall$volume<=0.05])*100/tA_voltotal tA_volperc_over0.05 <- sum(tA_wall$volume[tA_wall$volume>0.05])*100/tA_voltotal ### type-B tB_volperc_upto0.02 <- sum(tB_wall$volume[tB_wall$volume<=0.02])*100/tB_voltotal tB_volperc_0.02to0.05 <- sum(tB_wall$volume[tB_wall$volume>0.02 & tB_wall$volume<=0.05])*100/tB_voltotal tB_volperc_over0.05 <- sum(tB_wall$volume[tB_wall$volume>0.05])*100/tB_voltotal ### type-C tC_volperc_upto0.02 <- sum(tC_wall$volume[tC_wall$volume<=0.02])*100/tC_voltotal tC_volperc_0.02to0.05 <- sum(tC_wall$volume[tC_wall$volume>0.02 & tC_wall$volume<=0.05])*100/tC_voltotal tC_volperc_over0.05 <- sum(tC_wall$volume[tC_wall$volume>0.05])*100/tC_voltotal ### fa?ade composition matrix composition <- matrix(1:12, nrow = 4, ncol = 3) composition <- rbind(c(tA_volperc_upto0.02,tB_volperc_upto0.02,tC_volperc_upto0.02), c(tA_volperc_0.02to0.05,tB_volperc_0.02to0.05,tC_volperc_0.02to0.05), c(tA_volperc_over0.05,tB_volperc_over0.05,tC_volperc_over0.05), c(tA_volperc_gap,tB_volperc_gap,tC_volperc_gap)) rownames(composition) <- c("upto0.02","0.02to0.05","over0.05", "gap") colnames(composition) <- c("tA","tB","tC") ### facade composition table compositiontab <- composition rownames(compositiontab) <- c("Percentage of blocks up to 0.02m^3","Percentage of blocks between 0.02 and 0.05m^3", "Percentage of blocks over 0.05m^3","Gaps") colnames(compositiontab) <- c("Type-A","Type-B","Type-C") kable(compositiontab, digits = 4) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ### Wall volumes by block ```{r} #| label: tbl-volume #| tbl-cap: Type-A, Type-B and Type-C wall volumes divided by block composition. # For Type-A: Calculate average volumes and total facade volume based on block size categories and overall area tA_avervol_upto0.02 <- tA_voltotal / sum(tA_wallfilled$Area) * composition["upto0.02", "tA"] / 100 tA_avervol_0.02to0.05 <- tA_voltotal / sum(tA_wallfilled$Area) * composition["0.02to0.05", "tA"] / 100 tA_avervol_over0.05 <- tA_voltotal / sum(tA_wallfilled$Area) * composition["over0.05", "tA"] / 100 tA_facadevol <- tA_avervol_upto0.02 + tA_avervol_0.02to0.05 + tA_avervol_over0.05 # For Type-B: Repeat the calculation for blocks and total facade volume tB_avervol_upto0.02 <- tB_voltotal / sum(tB_wallfilled$Area) * composition["upto0.02", "tB"] / 100 tB_avervol_0.02to0.05 <- tB_voltotal / sum(tB_wallfilled$Area) * composition["0.02to0.05", "tB"] / 100 tB_avervol_over0.05 <- tB_voltotal / sum(tB_wallfilled$Area) * composition["over0.05", "tB"] / 100 tB_facadevol <- tB_avervol_upto0.02 + tB_avervol_0.02to0.05 + tB_avervol_over0.05 # For Type-C: Repeat the calculation for blocks and total facade volume tC_avervol_upto0.02 <- tC_voltotal / sum(tC_wallfilled$Area) * composition["upto0.02", "tC"] / 100 tC_avervol_0.02to0.05 <- tC_voltotal / sum(tC_wallfilled$Area) * composition["0.02to0.05", "tC"] / 100 tC_avervol_over0.05 <- tC_voltotal / sum(tC_wallfilled$Area) * composition["over0.05", "tC"] / 100 tC_facadevol <- tC_avervol_upto0.02 + tC_avervol_0.02to0.05 + tC_avervol_over0.05 # Create a matrix to store the calculated volumes for each type and category stonevolume <- rbind( c(tA_avervol_upto0.02, tB_avervol_upto0.02, tC_avervol_upto0.02), c(tA_avervol_0.02to0.05, tB_avervol_0.02to0.05, tC_avervol_0.02to0.05), c(tA_avervol_over0.05, tB_avervol_over0.05, tC_avervol_over0.05), c(tA_facadevol, tB_facadevol, tC_facadevol) ) rownames(stonevolume) <- c("upto0.02", "0.01to0.05", "over0.05", "total") colnames(stonevolume) <- c("tA", "tB", "tC") # Convert the matrix into a table and format it for presentation stonevolumetab <- stonevolume rownames(stonevolumetab) <- c("Volume of blocks up to 0.02 (m^3)", "Volume of blocks between 0.02 and 0.05 (m^3)", "Volume of blocks over 0.05 (m^3)", "Total (m^3)") colnames(stonevolumetab) <- c("Type-A", "Type-B", "Type-C") kable(stonevolumetab, digits = 4) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ### Total volume of stones and rubble for a wall section by type ```{r} #| label: tbl-total #| tbl-cap: Total volume of stones and rubble for a wall section 2.2 m deep and 1 m high of Type-A, Type-B and Type-C walls. # Define fixed dimensions for the wall section sectiondepth <- 2.2 # Depth of the wall section in meters sectionheight <- 1 # Height of the wall section in meters volsection <- 1 * sectiondepth * sectionheight # Calculate volume of the section # Calculate volumes of stones and rubble for Type-A tA_volstones <- tA_facadevol * 2 * sectionheight # Calculate stone volume for Type-A tA_volrubble <- volsection - tA_volstones # Calculate remaining rubble volume for Type-A # Calculate volumes of stones and rubble for Type-B tB_volstones <- tB_facadevol * 2 * sectionheight # Calculate stone volume for Type-B tB_volrubble <- volsection - tB_volstones # Calculate remaining rubble volume for Type-B # Calculate volumes of stones and rubble for Type-C tC_volstones <- tC_facadevol * 2 * sectionheight # Calculate stone volume for Type-C tC_volrubble <- volsection - tC_volstones # Calculate remaining rubble volume for Type-C # Prepare matrix for the volumes of stones and rubble for each type stonevolume <- rbind( c(tA_volstones, tB_volstones, tC_volstones), # Row for stone volumes c(tA_volrubble, tB_volrubble, tC_volrubble) # Row for rubble volumes ) rownames(stonevolume) <- c("volstones", "volrubble") # Assign row names colnames(stonevolume) <- c("tA", "tB", "tC") # Assign column names for each type # Convert the matrix to a table and apply formatting for presentation stonevolumetab <- stonevolume # Create a table from the matrix rownames(stonevolumetab) <- c("Stone volume (m^3)", "Rubble volume (m^3)") # Set descriptive row names colnames(stonevolumetab) <- c("Type-A", "Type-B", "Type-C") # Set column names to indicate wall types # Display the table using kable from knitr and apply kableExtra styling kable(stonevolumetab, digits = 4) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ### Table of independent rates ```{r} #| label: tbl-independentrates #| tbl-cap: Energetics rates employed in the analysis independent of wall height. # Define the energetics index for each activity in person-hours per cubic meter (ph/m³) in_quarrywall <- 0.09 # For quarrying wall stones in_quarryrubble <- 0.5 # For quarrying rubble in_leveling <- 0.3 # For leveling activities in_buildrubbleearth <- 0.375 # For assembling rubble and earth fill in_quarrylost <- 0.15 # 15% increase of the effective volume due to loss, not directly added to the matrix # Construct the energetics matrix with the defined indices energetics <- matrix(1:4, nrow = 4, ncol = 1) # Initial placeholder matrix creation, later overwritten energetics <- rbind( in_quarrywall, in_quarryrubble, in_leveling, in_buildrubbleearth ) # Assign meaningful row names to the energetics matrix rownames(energetics) <- c( "Stone quarry (m^3/ph)", "Rubble quarry (m^3/ph)", "Leveling (m^3/ph)", "Assembly rubble and earth fill (m^3/ph)" ) # Define column name for the matrix colnames(energetics) <- "Rates" # Display the energetics matrix as a formatted table using kable and kable_styling for aesthetics kable(energetics, digits = 4) %>% # Use 4 decimal places for precision kable_styling( bootstrap_options = c("condensed"), # Use a condensed style for the table full_width = T, # Extend the table to the full width of the container position = "left" # Align the table to the left ) ``` ### Table of dependent rates ```{r} #| label: tbl-dependentrates #| tbl-cap: Energetics rates employed in the analysis dependent on wall height. # Define the energetics index for assembling stone blocks of various sizes a <- 4 b <- 0.3 h <- 3 w <- 2.9 in_buildstone0.02to0.05 <- 1 / (6 * (a/2 + 0.2*a*(h-1) + b/(2*w))) # For blocks between 0.02 and 0.05 m³ in_buildstoneupto0.02 <- 1.42 * in_buildstone0.02to0.05 # For blocks up to 0.02 m³ in_buildstoneover0.05 <- 0.79 * in_buildstone0.02to0.05 # For blocks above 0.05 m³ # Create a matrix to summarize the energetics indices energetics <- matrix(1:3, nrow = 3, ncol = 1) # Placeholder matrix, to be overwritten energetics <- rbind( c(in_buildstoneupto0.02), c(in_buildstone0.02to0.05), c(in_buildstoneover0.05) ) # Assign meaningful row names to the matrix to describe each energetics index rownames(energetics) <- c( "Assembly up to 0.02 m^3 (m^3/ph)", "Assembly between 0.02 and 0.05 m^3 (m^3/ph)", "Assembly above 0.05 m^3 (m^3/ph)" ) # Define a column name for the matrix to indicate the measurement unit colnames(energetics) <- c("Rates (m³/ph)") # Display the energetics matrix as a formatted table using the kable function and apply styling for aesthetics kable(energetics, digits = 4) %>% # Use 4 decimal places for precision in displaying rates kable_styling( bootstrap_options = c("condensed"), # Use a condensed styling option for the table full_width = T, # Extend the table to the full width of the container position = "left" # Align the table to the left ) ``` ## Table of costs by type ```{r} #| label: tbl-costs #| tbl-cap: Costs estimated for building wall Type-A, Type-B and Type-C expressed as a linear rate of ph per m. # Set wall dimensions used in calculations wallheight <- 3 wallwidth <- 2.9 ## Material acquisition calculations for each wall type, incorporating quarry loss ### Type-A tA_materialstone <- (tA_volstones + tA_volstones * in_quarrylost) * wallheight / in_quarrywall tA_materialrubble <- tA_volrubble * wallheight / in_quarryrubble tA_materialtotal <- tA_materialstone + tA_materialrubble ### Type-B tB_materialstone <- (tB_volstones + tB_volstones * in_quarrylost) * wallheight / in_quarrywall tB_materialrubble <- tB_volrubble * wallheight / in_quarryrubble tB_materialtotal <- tB_materialstone + tB_materialrubble ### Type-C tC_materialstone <- (tC_volstones + tC_volstones * in_quarrylost) * wallheight / in_quarrywall tC_materialrubble <- tC_volrubble * wallheight / in_quarryrubble tC_materialtotal <- tC_materialstone + tC_materialrubble ## Leveling the terrain calculations for each type, using a fixed energetics index tA_leveling <- wallwidth / in_leveling tB_leveling <- wallwidth / in_leveling tC_leveling <- wallwidth / in_leveling ## Wall assembly calculations, including facade and fill, for each wall type ### Type-A tA_assemblyfacade <- (((tA_facadevol * (composition["upto0.02", "tA"] / 100)) / in_buildstoneupto0.02) + ((tA_facadevol * (composition["0.02to0.05", "tA"] / 100)) / in_buildstone0.02to0.05) + ((tA_facadevol * (composition["over0.05", "tA"] / 100)) / in_buildstoneover0.05)) * wallheight * 2 tA_assemblyfill <- tA_volrubble * wallheight / in_buildrubbleearth tA_assemplytotal <- tA_assemblyfacade + tA_assemblyfill ### Type-B tB_assemblyfacade <- (((tB_facadevol * (composition["upto0.02", "tB"] / 100)) / in_buildstoneupto0.02) + ((tB_facadevol * (composition["0.02to0.05", "tB"] / 100)) / in_buildstone0.02to0.05) + ((tB_facadevol * (composition["over0.05", "tB"] / 100)) / in_buildstoneover0.05)) * wallheight * 2 tB_assemblyfill <- tB_volrubble * wallheight / in_buildrubbleearth tB_assemplytotal <- tB_assemblyfacade + tB_assemblyfill ### Type-C tC_assemblyfacade <- (((tC_facadevol * (composition["upto0.02", "tC"] / 100)) / in_buildstoneupto0.02) + ((tC_facadevol * (composition["0.02to0.05", "tC"] / 100)) / in_buildstone0.02to0.05) + ((tC_facadevol * (composition["over0.05", "tC"] / 100)) / in_buildstoneover0.05)) * wallheight * 2 tC_assemblyfill <- tC_volrubble * wallheight / in_buildrubbleearth tC_assemplytotal <- tC_assemblyfacade + tC_assemblyfill ## Total construction effort calculations for each type tA_total <- tA_materialtotal + tA_leveling + tA_assemplytotal tB_total <- tB_materialtotal + tB_leveling + tB_assemplytotal tC_total <- tC_materialtotal + tC_leveling + tC_assemplytotal ## Calculate mean values for comparison across types meanmaterialstone <- mean(c(tA_materialstone, tB_materialstone, tC_materialstone)) meanmaterialrubble <- mean(c(tA_materialrubble, tB_materialrubble, tC_materialrubble)) meanleveling <- mean(c(tA_leveling, tB_leveling, tC_leveling)) meanassemblyfacade <- mean(c(tA_assemblyfacade, tB_assemblyfacade, tC_assemblyfacade)) meanassemblyfill <- mean(c(tA_assemblyfill, tB_assemblyfill, tC_assemblyfill)) meantotal <- mean(c(tA_total, tB_total, tC_total)) ## Construct and display an energetic matrix summarizing the efforts for material acquisition, leveling, and assembly estimates <- rbind( c(tA_materialstone, tB_materialstone, tC_materialstone, meanmaterialstone), c(tA_materialrubble, tB_materialrubble, tC_materialrubble, meanmaterialrubble), c(tA_leveling, tB_leveling, tC_leveling, meanleveling), c(tA_assemblyfacade, tB_assemblyfacade, tC_assemblyfacade, meanassemblyfacade), c(tA_assemblyfill, tB_assemblyfill, tC_assemblyfill, meanassemblyfill), c(tA_total, tB_total, tC_total, meantotal) ) rownames(estimates) <- c("Stone quarry (ph/m)", "Rubble quarry (ph/m)", "Leveling (ph/m)", "Assembly facade (ph/m)", "Assembly fill (ph/m)", "Total wall construction (ph/m)") colnames(estimates) <- c("Type-A", "Type-B", "Type-C", "Average") # Display the estimates in a styled table kable(estimates, digits = 4) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ## Table of total cost in person-hour and in days ```{r} #| label: tbl-results #| tbl-cap: Total costs estimated in person-hour and in days, the latter considering a hypothetical 5 h a day. # Calculate the average total labour cost needed for wall construction in person-hours per meter averagewalltotal <- mean(c(tA_total, tB_total, tC_total)) # Define the lengths of different wall sections in meters qin_stonelength <- 345053 han_n_stonelength <- 8533 han_s_stonelength <- 10012 # Specify the daily working hours workinghour <- 5 # Calculate total effort in person-hours and convert to person-days for each wall section qin_stonetotal <- averagewalltotal * qin_stonelength / workinghour han_n_stonetotal <- averagewalltotal * han_n_stonelength / workinghour han_s_stonetotal <- averagewalltotal * han_s_stonelength / workinghour # Organize results into a matrix and label appropriately for clarity results <- matrix(1:6, nrow = 3, ncol = 2) # Initial placeholder matrix, to be overwritten results <- rbind( c(averagewalltotal * qin_stonelength, qin_stonetotal), c(averagewalltotal * han_n_stonelength, han_n_stonetotal), c(averagewalltotal * han_s_stonelength, han_s_stonetotal) ) rownames(results) <- c("Qin Wall (Stone section)", "Han north wall (Stone Section)", "Han south wall (Stone Section)") colnames(results) <- c("Person-hour", "Person-days") # Display the calculated results in a formatted table using kable and kableExtra for styling kable(results, digits = 0) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` # Rammed Earth Wall ```{r} #| label: tbl-earth # Sum the confirmed and uncertain lengths for earth sections at each site qin_earth <- 79481 han_n_earth <- 468398 han_s_earth <- 398739 qin_unsure <- 43212 han_n_unsure <- 26544 han_s_unsure <- 0 qin_earthlength <- qin_earth + qin_unsure han_n_earthlength <- han_n_earth + han_n_unsure han_s_earthlength <- han_s_earth + han_s_unsure # Calculate the volume of the wall per meter using trapezoidal formula upper <- 1.5 # Upper width of the wall in meters under <- 2.9 # Bottom width of the wall in meters height <- 3 # Height of the wall in meters wallvolume <- 1 * ((upper + under) / 2) * height # Volume formula for trapezoidal wall section # Define the rate of earth construction in cubic meters per person-day based on experimental data earthratehour <- 0.62 # Work rate in m^3 per person-hour, calculated from Xie et al 2021 earthrate <- earthratehour * workinghour # Convert to m^3 per person-day based on working hours # Calculate the total volume for earth sections at each site qin_earthvolume <- qin_earthlength * wallvolume han_n_earthvolume <- han_n_earthlength * wallvolume han_s_earthvolume <- han_s_earthlength * wallvolume # Calculate the total person-days required for constructing the earth sections at each site qin_earthtotal <- qin_earthvolume / earthrate han_n_earthtotal <- han_n_earthvolume / earthrate han_s_earthtotal <- han_s_earthvolume / earthrate # Organize the results into a matrix and label appropriately for clarity results <- matrix(1:3, nrow = 3, ncol = 1) # Initial placeholder matrix, to be overwritten results <- rbind( c(qin_earthtotal), c(han_n_earthtotal), c(han_s_earthtotal) ) rownames(results) <- c("Qin Wall (Earth section)", "Han north wall (Earth Section)", "Han south wall (Earth Section)") colnames(results) <- c("Person-days") # Display the calculated results in a formatted table using kable and apply styling for aesthetics kable(results, digits = 0) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` # Mixed Wall ```{r} #| label: tbl-mixed # Define the total lengths of mixed wall sections at each site in meters qinstoneearthlength <- 6521 han_n_stoneearthlength <- 14822 han_s_stoneearthlength <- 110676 # Calculate the total person-days required for each mixed wall section # The calculations incorporate average rates for material acquisition (stone), leveling, assembly (facade), and earthwork # Dividing by working hours converts the effort into person-days qinstoneearthtotal <- ((meanmaterialstone + meanleveling + meanassemblyfacade) * qinstoneearthlength / workinghour + (wallvolume / earthrate) * qinstoneearthlength) han_n_stoneearthtotal <- ((meanmaterialstone + meanleveling + meanassemblyfacade) * han_n_stoneearthlength / workinghour + (wallvolume / earthrate) * han_n_stoneearthlength) han_s_stoneearthtotal <- ((meanmaterialstone + meanleveling + meanassemblyfacade) * han_s_stoneearthlength / workinghour + (wallvolume / earthrate) * han_s_stoneearthlength) # Organize the calculated total person-days for each mixed wall section into a matrix results <- matrix(1:3, nrow = 3, ncol = 1) # Initial placeholder matrix, to be overwritten results <- rbind( c(qinstoneearthtotal), c(han_n_stoneearthtotal), c(han_s_stoneearthtotal) ) rownames(results) <- c("Qin Wall (mixed wall)", "Han north wall (mixed wall)", "Han south wall (mixed wall)") colnames(results) <- c("Person-days") # Display the results in a formatted table using kable and apply styling for better readability kable(results, digits = 0) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ## Table of total labour cost of wall ```{r} #| label: tbl-sumup # Calculate total labour required for each wall by adding up the person-days for stone, earth, and mixed sections. qinlabour <- qin_stonetotal + qin_earthtotal + qinstoneearthtotal han_n_labour <- han_n_stonetotal + han_n_earthtotal + han_n_stoneearthtotal han_s_labour <- han_s_stonetotal + han_s_earthtotal + han_s_stoneearthtotal # Organize the labour calculations into a matrix for display. Each row represents a different wall (Qin, Han North, Han South), # and each column represents a different type of construction within that wall (Stone wall, Earth wall, Mixed wall, and the Total labour). results <- matrix(1:12, nrow = 3, ncol = 4) # Initial placeholder matrix, to be overwritten results <- rbind( c(qin_stonetotal, qin_earthtotal, qinstoneearthtotal, qinlabour), c(han_n_stonetotal, han_n_earthtotal, han_n_stoneearthtotal, han_n_labour), c(han_s_stonetotal, han_s_earthtotal, han_s_stoneearthtotal, han_s_labour) ) rownames(results) <- c("Qin Wall (Person days)", "Han north wall (Person days)", "Han south wall (Person days)") colnames(results) <- c("Stone wall", "Earth wall", "Stone Earth wall", "Total") # Display the results in a formatted table using the `kable` function. kable(results, digits = 0) %>% # Use zero digits after the decimal point for cleaner number formatting kable_styling(bootstrap_options = c("condensed"), # Use a condensed table style for a more compact display full_width = T, # Extend the table to the full width of its container position = "left") # Align the table to the left side of its container ``` # Workforce on the Wall # Beacons ```{r} # Define the number of beacons made of stone, earth, and a combination of earth and stone for the Qin wall qinbeacon_stone <- 353 qinbeacon_earth <- 438 qinbeacon_earthstone <- 37 + qinbeacon_stone # Define the number of beacons for the Han north wall han_n_beacon_stone <- 5 han_n_beacon_earth <- 2 han_n_beacon_earthstone <- 4 + han_n_beacon_stone # Define the number of beacons for the Han south wall han_s_beacon_stone <- 37 han_s_beacon_earth <- 56 han_s_beacon_earthstone <- 5 + han_s_beacon_stone # Calculate the total number of beacons for each wall by adding the earth beacons and mixed (earth+stone) beacons qinbeacon_total <- qinbeacon_earth + qinbeacon_earthstone han_n_beacon_total <- han_n_beacon_earth + han_n_beacon_earthstone han_s_beacon_total <- han_s_beacon_earth + han_s_beacon_earthstone # Organize the beacon counts into a matrix for display results <- matrix(1:9, nrow = 3, ncol = 3) # Initial placeholder matrix, to be overwritten results <- rbind( c(qinbeacon_earth, qinbeacon_earthstone, qinbeacon_total), c(han_n_beacon_earth, han_n_beacon_earthstone, han_n_beacon_total), c(han_s_beacon_earth, han_s_beacon_earthstone, han_s_beacon_total) ) rownames(results) <- c("Qin Wall Beacon", "Han north wall Beacon", "Han south wall Beacon") colnames(results) <- c("Earth", "Stone Earth", "Total") # Display the results in a formatted table using the `kable` function from the knitr package and apply styling with `kable_styling` from the kableExtra package kable(results, digits = 4) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ## Stone composition of beacon yard wall per 0.5 m\^3 ```{r} # Calculate the mean facade volume. The stones used for beacons are similar with for beacons mean_facadevol <- (tA_facadevol + tB_facadevol + tC_facadevol) / 3 # Yard wall section dimensions and volumes yardsectiondepth <- 0.5 yardsectionheight <- 1 yardvolsection <- 1 * yardsectiondepth * yardsectionheight # Volume of the section yard_volstones <- mean_facadevol * 2 * yardsectionheight # Calculated stone volume for yard wall yard_volrubble <- yardvolsection - yard_volstones # Remaining volume is considered as rubble # Prepare matrix to store the volumes of stones and rubble for the yard wall stonevolume <- rbind( c(yard_volstones), c(yard_volrubble) ) rownames(stonevolume) <- c("volstones", "volrubble") colnames(stonevolume) <- c("Yard wall") # Convert the matrix into a table and apply formatting for presentation stonevolumetab <- stonevolume rownames(stonevolumetab) <- c("Stone volume (m^3)", "Rubble volume (m^3)") colnames(stonevolumetab) <- c("Yard wall") # Display the stone and rubble volume calculations for the yard wall in a formatted table kable(stonevolumetab, digits = 4) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ### Labour rates for beacon yard wall ```{r} #| label: tbl-costs-yardwalls # Define yard wall dimensions and rates yardwallheight <- 2 yardwallwidth <- 0.5 yardwalllength <- 65.25 in_buildyard <- 1 / (6 * (4/2 + 0.2*4*(2-1) + 0.3/(2*0.5))) # Material acquisition calculations yard_materialstone <- (yard_volstones + yard_volstones * in_quarrylost) * yardwallheight / in_quarrywall yard_materialrubble <- yard_volrubble * yardwallheight / in_quarryrubble yard_materialtotal <- yard_materialstone + yard_materialrubble # Leveling the terrain calculations yard_leveling <- yardwallwidth / in_leveling # Wall assembly calculations yard_assemblyfacade <- (mean_facadevol / in_buildyard) * yardwallheight * 2 yard_assemblyfill <- yard_volrubble * yardwallheight / in_buildrubbleearth yard_assemplytotal <- yard_assemblyfacade + yard_assemblyfill # Total construction effort calculation yard_total <- yard_materialtotal + yard_leveling + yard_assemplytotal # Energetic matrix for yard wall construction estimates <- matrix(1:7, nrow = 7, ncol = 1) estimates <- rbind( c(yard_materialstone), c(yard_materialrubble), c(yard_leveling), c(yard_assemblyfacade), c(yard_assemblyfill), c(yard_total) ) rownames(estimates) <- c("Stone quarry (ph/m)", "Rubble quarry (ph/m)", "Leveling (ph/m)", "Assembly facade (ph/m)", "Assembly fill (ph/m)", "Total wall construction (ph/m)") colnames(estimates) <- c("Yard wall") # Displaying the energetic matrix in a table kable(estimates, digits = 4) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ### Beacon yard wall labour cost in person hour and person day ```{r} #| label: qin stone beacon yard labour cost qinbeacon_yardcost <- qinbeacon_total * yardwalllength * yard_total qinbeacon_yardcost #Results results <- matrix(1:2, nrow = 1, ncol = 2) results <- rbind(c(qinbeacon_yardcost,qinbeacon_yardcost/workinghour)) rownames(results) <- c("Qin Wall") colnames(results) <- c("Person-hour","Person-day") kable(results, digits = 4) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ## Rammed earth beaon labour cost ```{r} #| label: Earth beaon labour cost # Calculate beacon volumes # Qin Wall beacon volume using the formula for a truncated cone qinbeaconr1 <- 3 qinbeaconr2 <- 5 qinbeaconheight <- 4 qin_beaconvolume <- (1/3) * pi * qinbeaconheight * (qinbeaconr1^2 + qinbeaconr2^2 + (qinbeaconr1 * qinbeaconr2)) # Han Walls beacon volumes using the formula for a rectangular prism han_n_beaconvolume <- 7.3 * 6.5 * 3 # Dimensions provided for a single beacon han_s_beaconvolume <- 7.3 * 6.5 * 3 # Same dimensions assumed for simplicity # Calculate the labour required for constructing beacon rammed earth platforms # Qin beacon labour calculation, corrected to include the earthrate for conversion to person-days qinbeacon_earthcost <- qin_beaconvolume / earthrate * qinbeacon_total # Han beacon labour calculations, assuming the `han_n_beacon_total` and `han_s_beacon_total` represent the total number of beacons han_n_beacon_earthcost <- han_n_beaconvolume / earthrate * han_n_beacon_total han_s_beacon_earthcost <- han_s_beaconvolume / earthrate * han_s_beacon_total # Prepare the results for presentation results <- matrix(1:3, nrow = 3, ncol = 1) # Initial placeholder matrix, to be overwritten results <- rbind( c(qinbeacon_earthcost), c(han_n_beacon_earthcost), c(han_s_beacon_earthcost) ) rownames(results) <- c("Qin Wall", "Han north wall", "Han south wall") colnames(results) <- c("Beacon rammed earth platform (Person-days)") # Display the calculated results in a formatted table using kable and apply styling for aesthetics kable(results, digits = 0) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ## Stone composition of beacon beacon facade per 0.3 m\^3 ```{r} #| label: qin earth beacon stone cover labour cost # Calculate the slant height of the truncated cone (beacon tower) l <- sqrt((qinbeaconr1 - qinbeaconr2)^2 + qinbeaconheight^2) # Calculate the surface area (s) to be covered with stones, excluding the base s <- pi * (qinbeaconr1^2 + qinbeaconr2^2 + qinbeaconr1 * l + qinbeaconr2 * l) - pi * qinbeaconr1^2 # Determine the labour intensity for building the beacon's stone cover in_buildqinbeacon <- 1 / (6 * (4/2 + 0.2*4*(l-1) + 0.3/(2*0.3))) # Calculate facade lengths and volumes for stone and rubble construction qinbeaconfacadelength <- s / l qinbeaconfacadedepth <- 0.3 qinbeaconfacadesectionheight <- 1 qinbeaconfacadevolsection <- 1 * qinbeaconfacadedepth * qinbeaconfacadesectionheight qinbeaconfacade_volstones <- mean_facadevol * 1 * qinbeaconfacadesectionheight qinbeaconfacade_volrubble <- qinbeaconfacadevolsection - qinbeaconfacade_volstones # Prepare matrix to store volumes for stones and rubble materialsvolume <- matrix(1:2, nrow = 2, ncol = 1) stonevolume <- rbind( c(qinbeaconfacade_volstones), c(qinbeaconfacade_volrubble) ) rownames(stonevolume) <- c("volstones", "volrubble") colnames(stonevolume) <- c("Yard wall") # Convert the matrix into a table and format for presentation stonevolumetab <- stonevolume rownames(stonevolumetab) <- c("Stone volume (m^3)", "Rubble volume (m^3)") colnames(stonevolumetab) <- c("Qin Beacon Facade") # Display the stone and rubble volume calculations in a formatted table kable(stonevolumetab, digits = 4) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ### Qin earth beacon stone facade labour rate ```{r} # Define beacon facade wall dimensions and construction rate qinbeaconfacadewallheight <- l qinbeaconfacadewallwidth <- 0.3 # Material acquisition calculations qinbeaconfacade_materialstone <- (qinbeaconfacade_volstones + qinbeaconfacade_volstones * in_quarrylost) * qinbeaconfacadewallheight / in_quarrywall qinbeaconfacade_materialrubble <- qinbeaconfacade_volrubble * qinbeaconfacadewallheight / in_quarryrubble qinbeaconfacade_materialtotal <- qinbeaconfacade_materialstone + qinbeaconfacade_materialrubble # Leveling the terrain labour calculation qinbeaconfacade_leveling <- qinbeaconfacadewallwidth / in_leveling # Wall assembly labour calculation qinbeaconfacade_assemblyfacade <- (mean_facadevol / in_buildqinbeacon) * qinbeaconfacadewallheight * 1 qinbeaconfacade_assemblyfill <- qinbeaconfacade_volrubble * qinbeaconfacadewallheight / in_buildrubbleearth qinbeaconfacade_assemplytotal <- qinbeaconfacade_assemblyfacade + qinbeaconfacade_assemblyfill # Calculate the total labour required for the beacon facade wall construction qinbeaconfacade_total <- qinbeaconfacade_materialtotal + qinbeaconfacade_leveling + qinbeaconfacade_assemplytotal # Organize and display the construction labour estimates in a table estimates <- matrix(1:7, nrow = 7, ncol = 1) estimates <- rbind( c(qinbeaconfacade_materialstone), c(qinbeaconfacade_materialrubble), c(qinbeaconfacade_leveling), c(qinbeaconfacade_assemblyfacade), c(qinbeaconfacade_assemblyfill), c(qinbeaconfacade_total) ) rownames(estimates) <- c("Stone quarry (ph/m)", "Rubble quarry (ph/m)", "Leveling (ph/m)", "Assembly facade (ph/m)", "Assembly fill (ph/m)", "Total wall construction (ph/m)") colnames(estimates) <- c("Beacon facade wall") # Present the table with kable styling kable(estimates, digits = 4) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ### Qin beacon stone facade in person-hour ```{r} #| label:Qin beacon stone cover tbl-costs # Calculate the total facade length for all Qin beacons qin_beaconfacade_length <- qinbeaconfacadelength * qinbeacon_earthstone # Calculate the total labor in person-days for building the facade wall across all Qin beacons qinbeacon_beaconfacade <- qinbeacon_earthstone * qin_beaconfacade_length * qinbeaconfacade_total # Display the result in a formatted table results <- matrix(c(qinbeacon_beaconfacade), nrow = 1, ncol = 1) rownames(results) <- c("Qin beacon stone facade") colnames(results) <- c("Person-hours") kable(results, digits = 4) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ## Stone composition for Han beacon facade per 1.1 m\^3 ```{r} #| label: tbl-beacon facade unit # Define section dimensions for the Han beacon facade hanbeaconfacadesectiondepth <- 1.1 hanbeaconfacadesectionheight <- 1 hanbeaconfacadevolsection <- 1 * hanbeaconfacadesectiondepth * hanbeaconfacadesectionheight # Calculate volumes for stones and rubble based on the mean facade volume hanbeaconfacade_volstones <- mean_facadevol * 1 * hanbeaconfacadesectionheight hanbeaconfacade_volrubble <- hanbeaconfacadevolsection - hanbeaconfacade_volstones # Organize volumes into a matrix materialsvolume <- matrix(1:2, nrow = 2, ncol = 1) stonevolume <- rbind( c(hanbeaconfacade_volstones), c(hanbeaconfacade_volrubble) ) rownames(stonevolume) <- c("volstones", "volrubble") colnames(stonevolume) <- c("Han Beacon Facade") # Convert the matrix into a table and apply formatting for presentation stonevolumetab <- stonevolume rownames(stonevolumetab) <- c("Stone volume (m^3)", "Rubble volume (m^3)") colnames(stonevolumetab) <- c("Han beacon facade") # Display the table using kable and apply styling kable(stonevolumetab, digits = 4) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ### Costs estimated for Han beacon facade wall ```{r} # Define facade wall dimensions and volume for the Han beacon hanbeaconfacadewallheight <- 3 hanbeaconfacadewallwidth <- 1.1 in_buildhanbeacon <- 1 / (6 * (4/2 + 0.2*4*(3-1) + 0.3/(2*1.1))) han_beaconvolume <- 7.5 * 6.5 * 3 # Predefined total volume # Material acquisition for stone and rubble hanbeaconfacade_materialstone <- (hanbeaconfacade_volstones + hanbeaconfacade_volstones * in_quarrylost) * hanbeaconfacadewallheight / in_quarrywall hanbeaconfacade_materialrubble <- hanbeaconfacade_volrubble * hanbeaconfacadewallheight / in_quarryrubble hanbeaconfacade_materialtotal <- hanbeaconfacade_materialstone + hanbeaconfacade_materialrubble # Leveling the terrain hanbeaconfacade_leveling <- hanbeaconfacadewallwidth / in_leveling # Wall assembly hanbeaconfacade_assemblyfacade <- (mean_facadevol / in_buildhanbeacon) * hanbeaconfacadewallheight hanbeaconfacade_assemblyfill <- hanbeaconfacade_volrubble * hanbeaconfacadewallheight / in_buildrubbleearth hanbeaconfacade_assemplytotal <- hanbeaconfacade_assemblyfacade + hanbeaconfacade_assemblyfill # Calculate the total labor required hanbeaconfacade_total <- hanbeaconfacade_materialtotal + hanbeaconfacade_leveling + hanbeaconfacade_assemplytotal # Organize and display the construction labor estimates in a table estimates <- matrix(1:7, nrow = 7, ncol = 1) estimates <- rbind( c(hanbeaconfacade_materialstone), c(hanbeaconfacade_materialrubble), c(hanbeaconfacade_leveling), c(hanbeaconfacade_assemblyfacade), c(hanbeaconfacade_assemblyfill), c(hanbeaconfacade_total) ) rownames(estimates) <- c("Stone quarry (ph/m)", "Rubble quarry (ph/m)", "Leveling (ph/m)", "Assembly facade (ph/m)", "Assembly fill (ph/m)", "Total wall construction (ph/m)") colnames(estimates) <- c("Han beacon facade wall") # Present the table with kable styling kable(estimates, digits = 4) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ### Labour cost for Han beacon facade ```{r} #| label: tbl-costs-hanfacade #| tbl-cap: Costs estimated for building beacon facade wall expressed as person-day. # Define the number of beacon stones and facade length for Han north wall and south wall han_n_beaconfacade_length <- 27 * han_n_beacon_earthstone han_s_beaconfacade_length <- 27 * han_s_beacon_earthstone # Calculate the labor required for constructing the facade of the beacon towers han_n_beacon_beaconfacade <- han_n_beacon_earthstone * han_n_beaconfacade_length * hanbeaconfacade_total han_s_beacon_beaconfacade <- han_s_beacon_earthstone * han_s_beaconfacade_length * hanbeaconfacade_total # Organize the labor required into a matrix and calculate person-days for Qin, Han North, and Han South walls qinbeacon_facadelabour <- qinbeacon_beaconfacade / workinghour han_n_beacon_facadelabour <- han_n_beacon_beaconfacade / workinghour han_s_beacon_facadelabour <- han_s_beacon_beaconfacade / workinghour results <- matrix(1:3, nrow = 3, ncol = 1) # Initial placeholder matrix, to be overwritten results <- rbind( c(qinbeacon_facadelabour), c(han_n_beacon_facadelabour), c(han_s_beacon_facadelabour) ) rownames(results) <- c("Qin wall", "Han north wall", "Han south wall") colnames(results) <- c("Beacon facade(Person-days)") # Display the calculated results in a formatted table using kable and apply styling for aesthetics kable(results, digits = 0) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ## Total labour cost for beacon ```{r} #| label: Total person-days for beacon construction # Calculate total person-days for beacon construction, including earthwork and facade labour costs qinbeacon_construction <- qinbeacon_earthcost + (qinbeacon_beaconfacade + qinbeacon_yardcost) / workinghour han_n_beacon_construction <- han_n_beacon_earthcost + (han_n_beacon_beaconfacade / workinghour) han_s_beacon_construction <- han_s_beacon_earthcost + (han_s_beacon_beaconfacade / workinghour) # Organize the calculated total labor for each wall's beacon construction into a matrix results <- matrix(1:3, nrow = 3, ncol = 1) # Initial placeholder matrix, to be overwritten results <- rbind( c(qinbeacon_construction), c(han_n_beacon_construction), c(han_s_beacon_construction) ) rownames(results) <- c("Qin wall labour cost", "Han north wall labour cost", "Han south wall labour cost") colnames(results) <- c("Beacon Construction (Person-days)") # Present the table with kable styling for a clear and concise display of the labor costs kable(results, digits = 0) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ### Total labour cost for wall and beacon construction ```{r} #| label: tbl-results-cost-final # Initialize the matrix with appropriate dimensions results <- matrix(1:9, nrow = 3, ncol = 3) # Calculate the total labor for wall and beacon construction, and their sum qinbeacon_construction <- qinbeacon_earthcost + (qinbeacon_beaconfacade + qinbeacon_yardcost) / workinghour han_n_beacon_construction <- han_n_beacon_earthcost + (han_n_beacon_beaconfacade) / workinghour han_s_beacon_construction <- han_s_beacon_earthcost + (han_s_beacon_beaconfacade) / workinghour qintotal <- qinlabour + qinbeacon_construction han_n_total <- han_n_labour + han_n_beacon_construction han_s_total <- han_s_labour + han_s_beacon_construction #| label: combine the ratio of the total Qin wall # Define total lengths of the walls (not directly used in these calculations) qinlength <- 474267 han_n_length <- 518297 han_s_length <- 519427 # Calculate construction time and labour force required for the Qin wall, adjusting for efficiency and terrain with qinratio qinfort <- 44*10*10*1800/3.1 qinratio <- qintotal/(qintotal+qinfort) # The ratio is set from a calculation of the construction of 44 forts on the frontier. qinconstructtime <- 2 * 180 * qinratio # Adjusted construction time for the Qin wall qinlabourforce <- qinlabour / qinconstructtime # Labour force calculation for Qin wall # Set construction times for Han North and South walls and calculate respective labour forces han_n_constructtime <- 180 # Preset construction time for Han North wall han_n_labourforce <- han_n_labour / han_n_constructtime # Labour force calculation for Han North wall han_s_constructtime <- 180 # Preset construction time for Han South wall han_s_labourforce <- han_s_labour / han_s_constructtime # Labour force calculation for Han South wall # Populate the matrix with calculated values results <- rbind( c(qinlabour, qinbeacon_construction, qintotal), c(han_n_labour, han_n_beacon_construction, han_n_total), c(han_s_labour, han_s_beacon_construction, han_s_total) ) rownames(results) <- c("Qin wall labour cost", "Han north wall labour cost", "Han south wall labour cost") colnames(results) <- c("Wall Construction (Person-days)", "Beacon Construction(Person-days)", "Total(Person-days)") # Display the results in a styled table kable(results, digits = 0) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` # Total Workforce with Supervisory ```{r} #| label: tbl-results-withsupervisory # Initialize the matrix for the results results <- matrix(1:9, nrow = 3, ncol = 3) # Calculate the total labor force qin_labourforce_total <- (qinlabour + qinbeacon_construction) / qinconstructtime han_n_labourforce_total <- (han_n_labour + han_n_beacon_construction) / han_n_constructtime han_s_labourforce_total <- (han_s_labour + han_s_beacon_construction) / han_s_constructtime # Populate the matrix with calculated values results <- rbind( c(qinconstructtime, qin_labourforce_total, qin_labourforce_total * 1.16), c(han_n_constructtime, han_n_labourforce_total, han_n_labourforce_total * 1.16), c(han_s_constructtime, han_s_labourforce_total, han_s_labourforce_total * 1.16) ) rownames(results) <- c("Qin wall labour cost", "Han north wall labour cost", "Han south wall labour cost") colnames(results) <- c("Construct time (days)", "Workforce", "Workforce with supervision") # Display the results in a styled table kable(results, digits = 0) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` # Food Supply for Wall Construction Only ```{r} #| label: tbl-results-remote # Calculate the food cost for the Qin wall construction labor, adjusting by the same cost factor of 0.83 qin_foodcost <- qintotal * 0.83 # Repeat the calculation for the Han North wall han_n_foodcost <- han_n_total * 0.83 # And for the Han South wall han_s_foodcost <- han_s_total * 0.83 # Determine the number of cart transport trips needed for the Qin wall using the calculated food cost qin_cart <- qin_foodcost / 250 # And for the Han North wall han_n_cart <- han_n_foodcost / 250 # And for the Han South wall han_s_cart <- han_s_foodcost / 250 # Calculate the manpower needed for cart transportation for the Qin wall, multiplying by 6 men per cart qin_cartmen <- qin_cart * 6 # Repeat for the Han North wall han_n_cartmen <- han_n_cart * 6 # And for the Han South wall han_s_cartmen <- han_s_cart * 6 # Prepare the results matrix for display results <- matrix(1:9, nrow = 3, ncol = 3) # Populate the matrix with calculated values results <- rbind( c(qin_foodcost, qin_cart, qin_cartmen), c(han_n_foodcost, han_n_cart, han_n_cartmen), c(han_s_foodcost, han_s_cart, han_s_cartmen) ) # Set the row and column names for clarity rownames(results) <- c("Qin wall", "Han north wall", "Han south wall") colnames(results) <- c("Food cost (Dou)", "cart transport trips", "Person on cart (6 men per cart)") # Display the table with kable styling for readability kable(results, digits = 0) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` ## Considering food loss during trips ```{r} # Considering food loss during trips, unit: Dou qin_foodcost_withloss <- qin_foodcost/0.39 han_foodcost_withloss <- (han_n_foodcost + han_s_foodcost)/0.923 qin_cart_trips <- qin_foodcost_withloss/250 han_cart_trips <- han_foodcost_withloss/250 qin_cart_hauler_wall <- qin_cart_trips*6 han_cart_hauler_wall <- han_cart_trips*6 # qin supply distance: 800 km. Round trip doubles the distance. Speed: 25 km/day. # han supply distance: 100 km qin_days <- 800*2/25 han_days <- 100*2/25 qin_carthauler_personday_withloss <- qin_cart_hauler_wall * qin_days han_carthauler_personday_withloss <- han_cart_hauler_wall * han_days # Prepare the results matrix for display results <- matrix(1:2, nrow = 2, ncol = 2) # Populate the matrix with calculated values results <- rbind( c(qin_carthauler_personday_withloss, qin_cart_hauler_wall), c(han_carthauler_personday_withloss, han_cart_hauler_wall) ) # Set the row and column names for clarity rownames(results) <- c("Qin logistical supply (not including immigration)", "Han logistical supply") colnames(results) <- c("Person-days","Logistic Personel") # Display the table with kable styling for readability kable(results, digits = 0) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ``` # Combination of the Qin Immigration and Qin Wall Project ```{r} # Set the number of immigrants immigration <- 350000 # Calculate the total annual food cost in Dou for immigrants, assuming 2 years at a cost adjusted by 0.83 as food cost per person-day immigration_foodcost <- immigration * 360 * 2 * 0.83 # Total food cost considering the loss rate totalfoodcost <- immigration_foodcost + qin_foodcost # Considering the loss during the transportation totalfoodcost_withloss <- totalfoodcost/0.39 # Cart trips total_cart_withloss <- totalfoodcost_withloss / 250 # Total cart haulers total_cart_hauler <- total_cart_withloss * 6 # Total person-days total_persondays <- total_cart_hauler * qin_days # Prepare the results matrix for display results <- matrix(1:2, nrow = 1, ncol = 2) # Populate the matrix with calculated values results <- rbind( c(total_persondays, total_cart_hauler) ) # Set the row and column names for clarity rownames(results) <- c("Qin logistical supply (including immigration)") colnames(results) <- c("Person-days","Logistic Personel") # Display the table with kable styling for readability kable(results, digits = 0) %>% kable_styling(bootstrap_options = c("condensed"), full_width = T, position = "left") ```