--- title: "Topic 8" output: pdf_document: default html_document: default date: "`r Sys.Date()`" --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE) ``` ```{r preamble, message=FALSE} # Load Libraries library(dplyr) library(lubridate) library(readr) library(utils) library(openSkies) library(dotenv) library(httr) library(jsonlite) library(trajr) ``` # Download flights ```{r opensky} time_now <- Sys.time() creds <- getCredentials( client_id = Sys.getenv('OPENSKY_CLIENT_ID'), client_secret = Sys.getenv('OPENSKY_CLIENT_SECRET')) # get departures from Frankfurt airport departures <- getAirportDepartures(airport = "EDDF", startTime = time_now - hours(1), endTime = time_now, credentials = creds ) getFlights <- function(icao, time, creds){ flights <-getAircraftFlights(icao, startTime = time - days(1), endTime = time, credentials = creds ) return(flights) } icao <- departures[[1]][["ICAO24"]] flights <- getFlights(icao,Sys.time(), creds) # TODO map from all available flights to tracks query <- list(icao24= icao, time=as.numeric(flights[[1]][["departure_time"]])) response <-makeAuthenticatedRequest('tracks/all',query, creds) track_data <- fromJSON(content(response, as = "text", encoding = "UTF-8")) if (!is.null(track_data$path) && length(track_data$path) > 0) { route_df <- as.data.frame(track_data$path) colnames(route_df) <- c("time", "lat", "lon", "alt", "heading", "on_ground") message("Loading of route successfull! Points: ", nrow(route_df)) plot(route_df$lon, route_df$lat, type="o", pch=20, col="blue", main=paste("Geographic route of", icao), xlab="Longitude", ylab="Latitude") plot(route_df$time, route_df$alt, type="l", col="red", lwd=2, main=paste("Altitude profile of", icao), xlab="Time (Unix)", ylab="Height (Meter)") } else { print("No path points from api") } ``` # Trajectory Characteristics Analysis ```{r trajectory-analysis} if (exists("route_df") && nrow(route_df) > 1) { # Convert lat/lon to approximate meters (using simple equirectangular projection) # Reference point: first coordinate lat_ref <- route_df$lat[1] lon_ref <- route_df$lon[1] # Convert to meters (approximate) meters_per_deg_lat <- 111320 meters_per_deg_lon <- 111320 * cos(lat_ref * pi / 180) x_meters <- (route_df$lon - lon_ref) * meters_per_deg_lon y_meters <- (route_df$lat - lat_ref) * meters_per_deg_lat time_seconds <- route_df$time - route_df$time[1] # Create trajr trajectory object trj <- TrajFromCoords( data.frame(x = x_meters, y = y_meters, time = time_seconds), xCol = "x", yCol = "y", timeCol = "time" ) # Calculate trajectory characteristics # 1. Duration of travel (seconds) duration <- TrajDuration(trj) # 2. Total path length (meters) path_length <- TrajLength(trj) # 3. Diffusion distance (net displacement - straight line from start to end) diffusion_distance <- TrajDistance(trj) # 4. Straightness index (ratio of net displacement to path length, 0-1) straightness <- TrajStraightness(trj) # 5. Mean travel velocity (meters/second) mean_velocity <- path_length / duration # 6. Fractal dimension (using divider method) # Note: requires sufficient points for accurate estimation fractal_dim <- tryCatch({ # Calculate appropriate step sizes based on trajectory length min_step <- TrajLength(trj) / 100 max_step <- TrajLength(trj) / 2 step_sizes <- exp(seq(log(min_step), log(max_step), length.out = 10)) TrajFractalDimension(trj, stepSizes = step_sizes) }, error = function(e) { message("Could not calculate fractal dimension: ", e$message) NA }) # Create summary data frame trajectory_characteristics <- data.frame( Parameter = c( "Duration of Travel (s)", "Duration of Travel (min)", "Path Length (m)", "Path Length (km)", "Diffusion Distance (m)", "Diffusion Distance (km)", "Straightness Index", "Mean Travel Velocity (m/s)", "Mean Travel Velocity (km/h)", "Fractal Dimension" ), Value = c( round(duration, 2), round(duration / 60, 2), round(path_length, 2), round(path_length / 1000, 2), round(diffusion_distance, 2), round(diffusion_distance / 1000, 2), round(straightness, 4), round(mean_velocity, 2), round(mean_velocity * 3.6, 2), round(fractal_dim, 4) ) ) print(trajectory_characteristics) # Visualize the trajectory using trajr plot(trj, main = paste("Trajectory of", icao)) } else { message("No valid trajectory data available for analysis") } ``` # Statistical Analysis of Multiple Trajectories ```{r multi-trajectory-analysis} # Function to calculate trajectory characteristics for a single flight calculate_trajectory_params <- function(icao24, departure_time, creds) { tryCatch({ query <- list(icao24 = icao24, time = as.numeric(departure_time)) response <- makeAuthenticatedRequest('tracks/all', query, creds) # Check for HTTP errors if (httr::status_code(response) != 200) { return(NULL) } track_data <- fromJSON(content(response, as = "text", encoding = "UTF-8")) if (is.null(track_data$path) || length(track_data$path) < 2) { return(NULL) } route_df <- as.data.frame(track_data$path) colnames(route_df) <- c("time", "lat", "lon", "alt", "heading", "on_ground") if (nrow(route_df) < 3) return(NULL) # Convert to meters lat_ref <- route_df$lat[1] lon_ref <- route_df$lon[1] meters_per_deg_lat <- 111320 meters_per_deg_lon <- 111320 * cos(lat_ref * pi / 180) x_meters <- (route_df$lon - lon_ref) * meters_per_deg_lon y_meters <- (route_df$lat - lat_ref) * meters_per_deg_lat time_seconds <- route_df$time - route_df$time[1] trj <- TrajFromCoords( data.frame(x = x_meters, y = y_meters, time = time_seconds), xCol = "x", yCol = "y", timeCol = "time" ) # Calculate parameters duration <- TrajDuration(trj) path_length <- TrajLength(trj) diffusion_dist <- TrajDistance(trj) straight <- TrajStraightness(trj) mean_vel <- path_length / duration # Fractal dimension fractal <- tryCatch({ min_step <- path_length / 100 max_step <- path_length / 2 if (min_step > 0 && max_step > min_step) { step_sizes <- exp(seq(log(min_step), log(max_step), length.out = 10)) TrajFractalDimension(trj, stepSizes = step_sizes) } else { NA } }, error = function(e) NA) return(data.frame( icao24 = icao24, diffusion_distance_km = diffusion_dist / 1000, straightness = straight, duration_min = duration / 60, mean_velocity_kmh = mean_vel * 3.6, fractal_dimension = fractal )) }, error = function(e) { message("Error processing ", icao24, ": ", e$message) return(NULL) }) } # Collect trajectory data from multiple departures message("Collecting trajectory data from departures...") all_trajectories <- list() # Process available departures (limit to avoid API rate limits) n_departures <- min(length(departures), 20) for (i in 1:n_departures) { dep <- departures[[i]] icao24 <- dep[["ICAO24"]] dep_time <- dep[["departure_time"]] # Use departure time directly from departures list # Skip if no departure time available if (is.null(dep_time)) { message("Skipping ", icao24, ": no departure time") next } params <- calculate_trajectory_params(icao24, dep_time, creds) if (!is.null(params)) { all_trajectories[[length(all_trajectories) + 1]] <- params message("Successfully processed trajectory for ", icao24) } Sys.sleep(0.5) # Rate limiting } # Combine all trajectory data if (length(all_trajectories) > 0) { trajectory_stats_df <- do.call(rbind, all_trajectories) message("Successfully collected ", nrow(trajectory_stats_df), " trajectories") print(trajectory_stats_df) } else { message("No trajectory data collected") } ``` # Basic Statistical Analysis of Trajectory Parameters ```{r statistical-analysis} if (exists("trajectory_stats_df") && nrow(trajectory_stats_df) >= 2) { # Parameters to analyze params_to_analyze <- c("diffusion_distance_km", "straightness", "duration_min", "mean_velocity_kmh", "fractal_dimension") param_labels <- c("Diffusion Distance (km)", "Straightness Index", "Duration (min)", "Mean Velocity (km/h)", "Fractal Dimension") # Function to calculate comprehensive statistics calc_stats <- function(x, param_name) { x <- x[!is.na(x)] if (length(x) < 2) return(NULL) data.frame( Parameter = param_name, N = length(x), Mean = round(mean(x), 4), Variance = round(var(x), 4), Std_Dev = round(sd(x), 4), Min = round(min(x), 4), Q1 = round(quantile(x, 0.25), 4), Median = round(median(x), 4), Q3 = round(quantile(x, 0.75), 4), Max = round(max(x), 4), IQR = round(IQR(x), 4) ) } # Calculate statistics for each parameter stats_list <- list() for (i in seq_along(params_to_analyze)) { param <- params_to_analyze[i] label <- param_labels[i] stats_list[[i]] <- calc_stats(trajectory_stats_df[[param]], label) } # Combine into summary table stats_summary <- do.call(rbind, stats_list[!sapply(stats_list, is.null)]) cat("\n========== STATISTICAL SUMMARY ==========\n\n") print(stats_summary, row.names = FALSE) # Boxplots for each parameter par(mfrow = c(2, 3)) for (i in seq_along(params_to_analyze)) { param <- params_to_analyze[i] label <- param_labels[i] data <- trajectory_stats_df[[param]][!is.na(trajectory_stats_df[[param]])] if (length(data) >= 2) { boxplot(data, main = paste("Boxplot:", label), ylab = label, col = "lightblue", border = "darkblue") # Add mean point points(1, mean(data), pch = 18, col = "red", cex = 1.5) } } par(mfrow = c(1, 1)) # Density plots for each parameter par(mfrow = c(2, 3)) for (i in seq_along(params_to_analyze)) { param <- params_to_analyze[i] label <- param_labels[i] data <- trajectory_stats_df[[param]][!is.na(trajectory_stats_df[[param]])] if (length(data) >= 3) { dens <- density(data, na.rm = TRUE) plot(dens, main = paste("Density:", label), xlab = label, ylab = "Density", col = "darkblue", lwd = 2) polygon(dens, col = rgb(0, 0, 1, 0.3), border = "darkblue") # Add vertical lines for mean and median abline(v = mean(data), col = "red", lwd = 2, lty = 2) abline(v = median(data), col = "green", lwd = 2, lty = 3) legend("topright", legend = c("Mean", "Median"), col = c("red", "green"), lty = c(2, 3), lwd = 2, cex = 0.7) } } par(mfrow = c(1, 1)) # Histogram with density overlay par(mfrow = c(2, 3)) for (i in seq_along(params_to_analyze)) { param <- params_to_analyze[i] label <- param_labels[i] data <- trajectory_stats_df[[param]][!is.na(trajectory_stats_df[[param]])] if (length(data) >= 3) { hist(data, probability = TRUE, main = paste("Histogram:", label), xlab = label, col = "lightgray", border = "darkgray") # Overlay density curve lines(density(data), col = "red", lwd = 2) } } par(mfrow = c(1, 1)) } else { message("Insufficient trajectory data for statistical analysis (need at least 2 trajectories)") } ``` # Interpretation of Results ```{r interpretation} if (exists("trajectory_stats_df") && nrow(trajectory_stats_df) >= 2) { cat("\n========== INTERPRETATION OF TRAJECTORY PARAMETERS ==========\n\n") # Diffusion Distance dd <- trajectory_stats_df$diffusion_distance_km[!is.na(trajectory_stats_df$diffusion_distance_km)] if (length(dd) >= 2) { cat("1. DIFFUSION DISTANCE (Net Displacement):\n") cat(" - Mean:", round(mean(dd), 2), "km\n") cat(" - This represents the straight-line distance from origin to destination.\n") cat(" - High variance (", round(var(dd), 2), ") indicates diverse flight distances.\n\n") } # Straightness st <- trajectory_stats_df$straightness[!is.na(trajectory_stats_df$straightness)] if (length(st) >= 2) { cat("2. STRAIGHTNESS INDEX:\n") cat(" - Mean:", round(mean(st), 4), "(range 0-1, where 1 = perfectly straight)\n") cat(" - Values close to 1 indicate efficient, direct flight paths.\n") cat(" - Lower values suggest deviations due to weather, airspace, or routing.\n\n") } # Duration dur <- trajectory_stats_df$duration_min[!is.na(trajectory_stats_df$duration_min)] if (length(dur) >= 2) { cat("3. DURATION OF TRAVEL:\n") cat(" - Mean:", round(mean(dur), 2), "minutes\n") cat(" - Range:", round(min(dur), 2), "-", round(max(dur), 2), "minutes\n") cat(" - IQR:", round(IQR(dur), 2), "minutes (middle 50% of flights)\n\n") } # Velocity vel <- trajectory_stats_df$mean_velocity_kmh[!is.na(trajectory_stats_df$mean_velocity_kmh)] if (length(vel) >= 2) { cat("4. MEAN TRAVEL VELOCITY:\n") cat(" - Mean:", round(mean(vel), 2), "km/h\n") cat(" - Typical commercial aircraft cruise: 800-900 km/h\n") cat(" - Lower values may include taxi, takeoff, and landing phases.\n\n") } # Fractal Dimension fd <- trajectory_stats_df$fractal_dimension[!is.na(trajectory_stats_df$fractal_dimension)] if (length(fd) >= 2) { cat("5. FRACTAL DIMENSION:\n") cat(" - Mean:", round(mean(fd), 4), "\n") cat(" - Value of 1.0 = perfectly straight line\n") cat(" - Values closer to 2.0 = more complex, space-filling paths\n") cat(" - Aircraft typically show low fractal dimension (efficient paths).\n\n") } cat("========== END OF ANALYSIS ==========\n") } ```