♻️ refactor all logic to main.rmd
This commit is contained in:
305
src/app.Rmd
305
src/app.Rmd
@@ -1,22 +1,13 @@
|
||||
```{r backend, child="./main.Rmd"}
|
||||
source("./main.Rmd")
|
||||
exists("getRouteSummary")
|
||||
```{r backend, include=FALSE}
|
||||
# Load all functions from main.Rmd
|
||||
knitr::purl("./main.Rmd", output = tempfile(), quiet = TRUE) |> source()
|
||||
```
|
||||
|
||||
# Web Interface
|
||||
```{r shiny}
|
||||
# Flight Trajectory Analysis - Shiny GUI Application
|
||||
# This app allows interactive selection of flights and displays trajectory analysis
|
||||
|
||||
library(shiny)
|
||||
library(dplyr)
|
||||
library(lubridate)
|
||||
library(openSkies)
|
||||
library(dotenv)
|
||||
library(httr)
|
||||
library(jsonlite)
|
||||
library(trajr)
|
||||
|
||||
# All core functions are loaded from main.Rmd
|
||||
|
||||
# UI Definition
|
||||
ui <- fluidPage(
|
||||
@@ -132,6 +123,7 @@ server <- function(input, output, session) {
|
||||
status("Loading departures...")
|
||||
|
||||
tryCatch({
|
||||
# Use getCredentials from main.Rmd
|
||||
rv$creds <- getCredentials(
|
||||
client_id = input$client_id,
|
||||
client_secret = input$client_secret
|
||||
@@ -199,28 +191,18 @@ server <- function(input, output, session) {
|
||||
|
||||
rv$current_icao <- icao24
|
||||
|
||||
# Get track data
|
||||
query <- list(icao24 = icao24, time = as.numeric(dep_time))
|
||||
response <- makeAuthenticatedRequest('tracks/all', query, rv$creds)
|
||||
# Use getAircraftTrack from main.Rmd
|
||||
route_df <- getAircraftTrack(icao24, dep_time, rv$creds)
|
||||
|
||||
if (httr::status_code(response) != 200) {
|
||||
status(paste("Track data not available for", icao24, "(HTTP", httr::status_code(response), ")"))
|
||||
return()
|
||||
}
|
||||
|
||||
track_data <- fromJSON(content(response, as = "text", encoding = "UTF-8"))
|
||||
|
||||
if (is.null(track_data$path) || length(track_data$path) < 2) {
|
||||
if (is.null(route_df) || nrow(route_df) < 2) {
|
||||
status(paste("No path data available for", icao24))
|
||||
return()
|
||||
}
|
||||
|
||||
route_df <- as.data.frame(track_data$path)
|
||||
colnames(route_df) <- c("time", "lat", "lon", "alt", "heading", "on_ground")
|
||||
rv$current_route <- route_df
|
||||
|
||||
# Create trajectory object
|
||||
rv$current_trj <- createTrajFromRoute(route_df)
|
||||
# Use getTrajFromRoute from main.Rmd
|
||||
rv$current_trj <- getTrajFromRoute(route_df)
|
||||
status(paste("Successfully analyzed", icao24, "with", nrow(route_df), "points"))
|
||||
|
||||
# Switch to analysis tab
|
||||
@@ -256,15 +238,10 @@ server <- function(input, output, session) {
|
||||
# Characteristics table
|
||||
output$characteristics_table <- renderTable({
|
||||
req(rv$current_trj)
|
||||
|
||||
trj <- rv$current_trj
|
||||
|
||||
data.frame <- calculateRouteCharacteristics(trj)
|
||||
data.frame
|
||||
calculateTrajectoryStats(rv$current_trj, format = "table")
|
||||
})
|
||||
|
||||
# Batch analysis
|
||||
# FIXME use multiple flights from one aircraft instead of random flights of random aircrafts
|
||||
observeEvent(input$batch_analyze, {
|
||||
req(rv$departures, rv$creds)
|
||||
|
||||
@@ -284,47 +261,8 @@ server <- function(input, output, session) {
|
||||
|
||||
if (is.null(dep_time)) next
|
||||
|
||||
params <- tryCatch({
|
||||
query <- list(icao24 = icao24, time = as.numeric(dep_time))
|
||||
response <- makeAuthenticatedRequest('tracks/all', query, rv$creds)
|
||||
|
||||
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) < 3) return(NULL)
|
||||
|
||||
route_df <- as.data.frame(track_data$path)
|
||||
colnames(route_df) <- c("time", "lat", "lon", "alt", "heading", "on_ground")
|
||||
|
||||
trj <- createTrajFromRoute(route_df)
|
||||
|
||||
duration <- TrajDuration(trj)
|
||||
path_length <- TrajLength(trj)
|
||||
diffusion_dist <- TrajDistance(trj)
|
||||
straight <- TrajStraightness(trj)
|
||||
mean_vel <- path_length / duration
|
||||
|
||||
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)
|
||||
|
||||
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) NULL)
|
||||
# Use calculate_trajectory_params from main.Rmd
|
||||
params <- calculate_trajectory_params(icao24, dep_time, rv$creds)
|
||||
|
||||
if (!is.null(params)) {
|
||||
all_trajectories[[length(all_trajectories) + 1]] <- params
|
||||
@@ -347,242 +285,37 @@ server <- function(input, output, session) {
|
||||
})
|
||||
})
|
||||
|
||||
# Statistics summary table
|
||||
# Statistics summary table - use calculateStatsSummary from main.Rmd
|
||||
output$stats_summary_table <- renderTable({
|
||||
req(rv$trajectory_stats_df)
|
||||
calculateStatsSummary(rv$trajectory_stats_df)
|
||||
})
|
||||
|
||||
# Boxplots
|
||||
# Boxplots - use createBoxplots from main.Rmd
|
||||
output$boxplots <- renderPlot({
|
||||
req(rv$trajectory_stats_df)
|
||||
createBoxplots(rv$trajectory_stats_df)
|
||||
})
|
||||
|
||||
# Density plots
|
||||
# Density plots - use createDensityPlots from main.Rmd
|
||||
output$density_plots <- renderPlot({
|
||||
req(rv$trajectory_stats_df)
|
||||
createDensityPlots(rv$trajectory_stats_df)
|
||||
})
|
||||
|
||||
# Histograms
|
||||
# Histograms - use createHistograms from main.Rmd
|
||||
output$histograms <- renderPlot({
|
||||
req(rv$trajectory_stats_df)
|
||||
createHistograms(rv$trajectory_stats_df)
|
||||
})
|
||||
|
||||
# Interpretation text
|
||||
# Interpretation text - use generateInterpretation from main.Rmd
|
||||
output$interpretation_text <- renderText({
|
||||
req(rv$trajectory_stats_df)
|
||||
generateInterpretation(rv$trajectory_stats_df)
|
||||
})
|
||||
}
|
||||
|
||||
# Helper function to get parameter names and labels
|
||||
getTrajectoryParams <- function() {
|
||||
list(
|
||||
params = c("diffusion_distance_km", "straightness", "duration_min",
|
||||
"mean_velocity_kmh", "fractal_dimension"),
|
||||
labels = c("Diffusion Distance (km)", "Straightness", "Duration (min)",
|
||||
"Mean Velocity (km/h)", "Fractal Dimension")
|
||||
)
|
||||
}
|
||||
|
||||
# Calculate statistics summary table
|
||||
calculateStatsSummary <- function(trajectory_stats_df) {
|
||||
p <- getTrajectoryParams()
|
||||
|
||||
stats_list <- lapply(seq_along(p$params), function(i) {
|
||||
x <- trajectory_stats_df[[p$params[i]]]
|
||||
x <- x[!is.na(x)]
|
||||
if (length(x) < 2) return(NULL)
|
||||
|
||||
data.frame(
|
||||
Parameter = p$labels[i],
|
||||
N = length(x),
|
||||
Mean = round(mean(x), 4),
|
||||
Variance = round(var(x), 4),
|
||||
Std_Dev = round(sd(x), 4),
|
||||
Q1 = round(quantile(x, 0.25), 4),
|
||||
Median = round(median(x), 4),
|
||||
Q3 = round(quantile(x, 0.75), 4)
|
||||
)
|
||||
})
|
||||
|
||||
do.call(rbind, stats_list[!sapply(stats_list, is.null)])
|
||||
}
|
||||
|
||||
# Create boxplots for trajectory statistics
|
||||
createBoxplots <- function(trajectory_stats_df) {
|
||||
p <- getTrajectoryParams()
|
||||
|
||||
par(mfrow = c(2, 3))
|
||||
for (i in seq_along(p$params)) {
|
||||
data <- trajectory_stats_df[[p$params[i]]][!is.na(trajectory_stats_df[[p$params[i]]])]
|
||||
if (length(data) >= 2) {
|
||||
boxplot(data, main = p$labels[i], ylab = p$labels[i], col = "lightblue", border = "darkblue")
|
||||
points(1, mean(data), pch = 18, col = "red", cex = 1.5)
|
||||
}
|
||||
}
|
||||
par(mfrow = c(1, 1))
|
||||
}
|
||||
|
||||
# Create density plots for trajectory statistics
|
||||
createDensityPlots <- function(trajectory_stats_df) {
|
||||
p <- getTrajectoryParams()
|
||||
|
||||
par(mfrow = c(2, 3))
|
||||
for (i in seq_along(p$params)) {
|
||||
data <- trajectory_stats_df[[p$params[i]]][!is.na(trajectory_stats_df[[p$params[i]]])]
|
||||
if (length(data) >= 3) {
|
||||
dens <- density(data)
|
||||
plot(dens, main = paste("Density:", p$labels[i]), xlab = p$labels[i], col = "darkblue", lwd = 2)
|
||||
polygon(dens, col = rgb(0, 0, 1, 0.3), border = "darkblue")
|
||||
abline(v = mean(data), col = "red", lwd = 2, lty = 2)
|
||||
abline(v = median(data), col = "green", lwd = 2, lty = 3)
|
||||
}
|
||||
}
|
||||
par(mfrow = c(1, 1))
|
||||
}
|
||||
|
||||
# Create histograms for trajectory statistics
|
||||
createHistograms <- function(trajectory_stats_df) {
|
||||
p <- getTrajectoryParams()
|
||||
|
||||
par(mfrow = c(2, 3))
|
||||
for (i in seq_along(p$params)) {
|
||||
data <- trajectory_stats_df[[p$params[i]]][!is.na(trajectory_stats_df[[p$params[i]]])]
|
||||
if (length(data) >= 3) {
|
||||
hist(data, probability = TRUE, main = paste("Histogram:", p$labels[i]),
|
||||
xlab = p$labels[i], col = "lightgray", border = "darkgray")
|
||||
lines(density(data), col = "red", lwd = 2)
|
||||
}
|
||||
}
|
||||
par(mfrow = c(1, 1))
|
||||
}
|
||||
|
||||
# Generate interpretation text for trajectory statistics
|
||||
generateInterpretation <- function(trajectory_stats_df) {
|
||||
df <- trajectory_stats_df
|
||||
|
||||
text <- "========== INTERPRETATION OF TRAJECTORY PARAMETERS ==========\n\n"
|
||||
|
||||
# Diffusion Distance
|
||||
dd <- df$diffusion_distance_km[!is.na(df$diffusion_distance_km)]
|
||||
if (length(dd) >= 2) {
|
||||
text <- paste0(text, "1. DIFFUSION DISTANCE (Net Displacement):\n")
|
||||
text <- paste0(text, " - Mean: ", round(mean(dd), 2), " km\n")
|
||||
text <- paste0(text, " - Represents straight-line distance from origin to destination.\n")
|
||||
text <- paste0(text, " - Variance: ", round(var(dd), 2), " (indicates diversity in flight distances)\n\n")
|
||||
}
|
||||
|
||||
# Straightness
|
||||
st <- df$straightness[!is.na(df$straightness)]
|
||||
if (length(st) >= 2) {
|
||||
text <- paste0(text, "2. STRAIGHTNESS INDEX:\n")
|
||||
text <- paste0(text, " - Mean: ", round(mean(st), 4), " (range 0-1, where 1 = perfectly straight)\n")
|
||||
text <- paste0(text, " - Values close to 1 indicate efficient, direct flight paths.\n")
|
||||
text <- paste0(text, " - Lower values suggest deviations due to weather, airspace, or routing.\n\n")
|
||||
}
|
||||
|
||||
# Duration
|
||||
dur <- df$duration_min[!is.na(df$duration_min)]
|
||||
if (length(dur) >= 2) {
|
||||
text <- paste0(text, "3. DURATION OF TRAVEL:\n")
|
||||
text <- paste0(text, " - Mean: ", round(mean(dur), 2), " minutes\n")
|
||||
text <- paste0(text, " - Range: ", round(min(dur), 2), " - ", round(max(dur), 2), " minutes\n")
|
||||
text <- paste0(text, " - IQR: ", round(IQR(dur), 2), " minutes (middle 50% of flights)\n\n")
|
||||
}
|
||||
|
||||
# Velocity
|
||||
vel <- df$mean_velocity_kmh[!is.na(df$mean_velocity_kmh)]
|
||||
if (length(vel) >= 2) {
|
||||
text <- paste0(text, "4. MEAN TRAVEL VELOCITY:\n")
|
||||
text <- paste0(text, " - Mean: ", round(mean(vel), 2), " km/h\n")
|
||||
text <- paste0(text, " - Typical commercial aircraft cruise: 800-900 km/h\n")
|
||||
text <- paste0(text, " - Lower values may include taxi, takeoff, and landing phases.\n\n")
|
||||
}
|
||||
|
||||
# Fractal Dimension
|
||||
fd <- df$fractal_dimension[!is.na(df$fractal_dimension)]
|
||||
if (length(fd) >= 2) {
|
||||
text <- paste0(text, "5. FRACTAL DIMENSION:\n")
|
||||
text <- paste0(text, " - Mean: ", round(mean(fd), 4), "\n")
|
||||
text <- paste0(text, " - Value of 1.0 = perfectly straight line\n")
|
||||
text <- paste0(text, " - Values closer to 2.0 = more complex, space-filling paths\n")
|
||||
text <- paste0(text, " - Aircraft typically show low fractal dimension (efficient paths).\n\n")
|
||||
}
|
||||
|
||||
text <- paste0(text, "========== END OF ANALYSIS ==========")
|
||||
|
||||
text
|
||||
}
|
||||
|
||||
createTrajFromRoute <- function(route_df) {
|
||||
tryCatch({
|
||||
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"
|
||||
)
|
||||
|
||||
return(trj)
|
||||
|
||||
}, error = function(e) {
|
||||
status(paste("Error creating trajectory object:", e$message))
|
||||
})
|
||||
}
|
||||
|
||||
calculateRouteCharacteristics <- function(trj) {
|
||||
duration <- TrajDuration(trj)
|
||||
path_length <- TrajLength(trj)
|
||||
diffusion_distance <- TrajDistance(trj)
|
||||
straightness <- TrajStraightness(trj)
|
||||
mean_velocity <- path_length / duration
|
||||
|
||||
fractal_dim <- 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(
|
||||
Parameter = c(
|
||||
"Duration (s)", "Duration (min)",
|
||||
"Path Length (km)",
|
||||
"Duffusion Distance (m)",
|
||||
"Diffusion Distance (km)",
|
||||
"Straightness Index",
|
||||
"Mean Velocity (km/h)",
|
||||
"Fractal Dimension"
|
||||
),
|
||||
Value = c(
|
||||
duration_s = round(duration, 2),
|
||||
duration_min = round(duration / 60, 2),
|
||||
path_length_km = round(path_length / 1000, 2),
|
||||
diffusion_distance_m = round(diffusion_distance, 2),
|
||||
diffusion_distance_km = round(diffusion_distance / 1000, 2),
|
||||
straightness_index = round(straightness, 4),
|
||||
mean_velocity_kmh = round(mean_velocity *3.6, 2),
|
||||
fractal_dimension = round(fractal_dim, 4)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
# Run the application
|
||||
shinyApp(ui = ui, server = server)
|
||||
```
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user